## Automatically generated incremental diff ## From: linux-2.5.69-bk13 ## To: linux-2.5.69-bk14 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.5.69-bk13/Documentation/networking/3c509.txt linux-2.5.69-bk14/Documentation/networking/3c509.txt --- linux-2.5.69-bk13/Documentation/networking/3c509.txt 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/Documentation/networking/3c509.txt 2003-05-20 04:46:40.000000000 -0700 @@ -52,7 +52,7 @@ For example, setting two cards to 10base2/IRQ10 and AUI/IRQ11 is done by using the xcvr and irq module options: - options 3c509 xcvr=3,3 irq=10,11 + options 3c509 xcvr=3,1 irq=10,11 (2) Full-duplex mode diff -urN linux-2.5.69-bk13/Documentation/video4linux/bttv/CARDLIST linux-2.5.69-bk14/Documentation/video4linux/bttv/CARDLIST --- linux-2.5.69-bk13/Documentation/video4linux/bttv/CARDLIST 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/Documentation/video4linux/bttv/CARDLIST 2003-05-20 04:46:40.000000000 -0700 @@ -22,7 +22,7 @@ card=20 - CEI Raffles Card card=21 - Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50 card=22 - Askey CPH050/ Phoebe Tv Master + FM - card=23 - Modular Technology MM205 PCTV, bt878 + card=23 - Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV, bt878 card=24 - Askey CPH05X/06X (bt878) [many vendors] card=25 - Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar card=26 - Hauppauge WinCam newer (bt878) @@ -39,7 +39,7 @@ card=37 - Prolink PixelView PlayTV pro card=38 - Askey CPH06X TView99 card=39 - Pinnacle PCTV Studio/Rave - card=40 - STB TV PCI FM, Gateway P/N 6000704 (bt878) + card=40 - STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100 card=41 - AVerMedia TVPhone 98 card=42 - ProVideo PV951 card=43 - Little OnAir TV @@ -81,11 +81,27 @@ card=79 - DSP Design TCVIDEO card=80 - Hauppauge WinTV PVR card=81 - GV-BCTV5/PCI + card=82 - Osprey 100/150 (878) + card=83 - Osprey 100/150 (848) + card=84 - Osprey 101 (848) + card=85 - Osprey 101/151 + card=86 - Osprey 101/151 w/ svid + card=87 - Osprey 200/201/250/251 + card=88 - Osprey 200/250 + card=89 - Osprey 210/220 + card=90 - Osprey 500 + card=91 - Osprey 540 + card=92 - Osprey 2000 + card=93 - IDS Eagle + card=94 - Pinnacle PCTV Sat + card=95 - Formac ProTV II + card=96 - MachTV + card=97 - Euresys Picolo tuner.o type=0 - Temic PAL (4002 FH5) type=1 - Philips PAL_I (FI1246 and compatibles) - type=2 - Philips NTSC (FI1236 and compatibles) + type=2 - Philips NTSC (FI1236,FM1236 and compatibles) type=3 - Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF) type=4 - NoTuner type=5 - Philips PAL_BG (FI1216 and compatibles) diff -urN linux-2.5.69-bk13/Documentation/video4linux/bttv/Cards linux-2.5.69-bk14/Documentation/video4linux/bttv/Cards --- linux-2.5.69-bk13/Documentation/video4linux/bttv/Cards 2003-05-04 16:53:07.000000000 -0700 +++ linux-2.5.69-bk14/Documentation/video4linux/bttv/Cards 2003-05-20 04:46:40.000000000 -0700 @@ -179,7 +179,8 @@ has not yet been seen (perhaps it was the german name for LR90 [stereo]). These cards are sold by many OEMs too. - FlyVideo A2 = LR90 Rev.F (w/Remote, w/o FM, stereo TV by tda9821) {Germany} + FlyVideo A2 (Elta 8680)= LR90 Rev.F (w/Remote, w/o FM, stereo TV by tda9821) {Germany} + Lifeview 3000 (Elta 8681) as sold by Plus(April 2002), Germany = LR138 w/ saa7134 Typhoon TV card series: @@ -397,6 +398,8 @@ AVerTV98 mit Fernbedienung (BT-878 chip) AVerTV/FM98 (BT-878 chip) + VDOmate (www.averm.com.cn) = M168U ? + Aimslab ------- Video Highway or "Video Highway TR200" (ISA) @@ -413,7 +416,8 @@ LT9306/MD9306 = CPH061 LT9415/MD9415 = LR90 Rev.F or Rev.G MD9592 = Avermedia TVphone98 (PCI_ID=1461:0003), PCB-Rev=M168II-B (w/TDA9873H) - MD9717 = KNC One (Rev D4, saa7134) + MD9717 = KNC One (Rev D4, saa7134, FM1216 MK2 tuner) + MD5044 = KNC One (Rev D4, saa7134, FM1216ME MK3 tuner) Modular Technologies (www.modulartech.com) UK --------------------------------------------- @@ -481,7 +485,8 @@ Studio PCTV Pro (Bt878 stereo w/ FM) Pinnacle PCTV (Bt878, MT2032) Pinnacle PCTV Pro (Bt878, MT2032) - Pinncale PCTV Sat (bt878a, HM1821/1221) + Pinncale PCTV Sat (bt878a, HM1821/1221) ["Conexant CX24110 with CX24108 tuner, aka HM1221/HM1811"] + Pinnacle PCTV Sat XE M(J)PEG capture and playback: DC1+ (ISA) @@ -654,9 +659,9 @@ WinTV PVR 450 US models - 990 WinTV-PVR-350 (249USD) - 980 WinTV-PVR-250 (149USD) - 880 WinTV-PVR-PCI (199USD) + 990 WinTV-PVR-350 (249USD) (iTVC15 chipset + radio) + 980 WinTV-PVR-250 (149USD) (iTVC15 chipset) + 880 WinTV-PVR-PCI (199USD) (KFIR chipset + bt878) 881 WinTV-PVR-USB 190 WinTV-GO 191 WinTV-GO-FM @@ -697,11 +702,40 @@ 566 WinTV USB (UK) 573 WinTV USB FM 429 Impact VCB (bt848) - 600 USB Libe (Video-In 1x Comp, 1xSVHS) + 600 USB Live (Video-In 1x Comp, 1xSVHS) 542 WinTV Nova 717 WinTV DVB-S 909 Nova-t PCI 893 Nova-t USB (Duplicate entry) + 802 MyTV + 804 MyView + 809 MyVideo + 872 MyTV2Go FM + + + 546 WinTV Nova-S CI + 543 WinTV Nova + 907 Nova-S USB + 908 Nova-T USB + 717 WinTV Nexus-S + 157 DEC3000-s Standalone + USB + + Spain + 685 WinTV-Go + 690 WinTV-PrimioFM + 416 WinTV-PCI Nicam Estereo + 677 WinTV-PCI-FM + 699 WinTV-Theater + 683 WinTV-USB + 678 WinTV-USB-FM + 983 WinTV-PVR-250 + 883 WinTV-PVR-PCI + 993 WinTV-PVR-350 + 893 WinTV-PVR-USB + 728 WinTV-DVB-C PCI + 832 MyTV2Go + 869 MyTV2Go-FM + 805 MyVideo (USB) Matrix-Vision @@ -713,6 +747,7 @@ Conceptronic (.net) ------------ TVCON FM, TV card w/ FM = CPH05x + TVCON = CPH06x BestData -------- @@ -747,11 +782,22 @@ Kworld (www.kworld.com.tw) -------------------------- - KWORLD KW-TV878RF-Pro TV Capture with FM Radio - KWORLD KW-TV878R-Pro TV - KWORLD KW-TVL878RF (low profile) - KWORLD KW-TV878R TV Capture (No FM Radio) - KWORLD KW-TV878RF TV + PC TV Station + KWORLD KW-TV878R TV (no radio) + KWORLD KW-TV878RF TV (w/ radio) + + KWORLD KW-TVL878RF (low profile) + + KWORLD KW-TV713XRF (saa7134) + + + MPEG TV Station (same cards as above plus WinDVR Software MPEG en/decoder) + KWORLD KW-TV878R -Pro TV (no Radio) + KWORLD KW-TV878RF-Pro TV (w/ Radio) + KWORLD KW-TV878R -Ultra TV (no Radio) + KWORLD KW-TV878RF-Ultra TV (w/ Radio) + + JTT/ Justy Corp.http://www.justy.co.jp/ (www.jtt.com.jp website down) --------------------------------------------------------------------- @@ -793,7 +839,7 @@ TV-FM =KNC1 saa7134 Standard PCI (DVB-S) = Technotrend Budget Standard PCI (DVB-S) w/ CI - Satelco Hoghend PCI (DVB-S) = Technotrend Premium + Satelco Highend PCI (DVB-S) = Technotrend Premium Sensoray www.sensoray.com @@ -879,3 +925,37 @@ Mercury www.kobian.com (UK and FR) LR50 LR138RBG-Rx == LR138 + +TEC sound (package and manuals don't have any other manufacturer info) TecSound + Though educated googling found: www.techmakers.com + TV-Mate = Zoltrix VP-8482 + +Lorenzen www.lorenzen.de +-------- + SL DVB-S PCI = Technotrend Budget PCI (su1278 or bsru version) + +Origo (.uk) www.origo2000.com + PC TV Card = LR50 + +I/O Magic www.iomagic.com +--------- + PC PVR - Desktop TV Personal Video Recorder DR-PCTV100 = Pinnacle ROB2D-51009464 4.0 + Cyberlink PowerVCR II + +Arowana +------- + TV-Karte / Poso Power TV (?) = Zoltrix VP-8482 (?) + +iTVC15 boards: +------------- +kuroutoshikou.com ITVC15 +yuan.com MPG160 PCI TV (Internal PCI MPEG2 encoder card plus TV-tuner) + +Asus www.asuscom.com + Asus TV Tuner Card 880 NTSC (low profile, cx23880) + Asus TV (saa7134) + +Hoontech +-------- +http://www.hoontech.com/korean/download/down_driver_list03.html + HART Vision 848 (H-ART Vision 848) + HART Vision 878 (H-Art Vision 878) diff -urN linux-2.5.69-bk13/Documentation/video4linux/bttv/Sound-FAQ linux-2.5.69-bk14/Documentation/video4linux/bttv/Sound-FAQ --- linux-2.5.69-bk13/Documentation/video4linux/bttv/Sound-FAQ 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/Documentation/video4linux/bttv/Sound-FAQ 2003-05-20 04:46:40.000000000 -0700 @@ -79,10 +79,11 @@ What you have to do is figure out the correct values for gpiomask and the audiomux array. If you have Windows and the drivers four your card installed, you might to check out if you can read these registers -values used by the windows driver. A tool to do this is available from -ftp://telepresence.dmem.strath.ac.uk/pub/bt848/winutil, but it does'nt -work with bt878 boards according to some reports I received. Another -one is available from http://www.kki.net.pl/~borgx/bTV.html. +values used by the windows driver. A tool to do this is available +from ftp://telepresence.dmem.strath.ac.uk/pub/bt848/winutil, but it +does'nt work with bt878 boards according to some reports I received. +Another one with bt878 suport is available from +http://btwincap.sourceforge.net/Files/btspy2.00.zip You might also dig around in the *.ini files of the Windows applications. You can have a look at the board to see which of the gpio pins are diff -urN linux-2.5.69-bk13/Makefile linux-2.5.69-bk14/Makefile --- linux-2.5.69-bk13/Makefile 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/Makefile 2003-05-20 04:46:40.000000000 -0700 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 69 -EXTRAVERSION = -bk13 +EXTRAVERSION = -bk14 # *DOCUMENTATION* # To see a list of typical targets execute "make help" diff -urN linux-2.5.69-bk13/arch/arm/mach-rpc/dma.c linux-2.5.69-bk14/arch/arm/mach-rpc/dma.c --- linux-2.5.69-bk13/arch/arm/mach-rpc/dma.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/arm/mach-rpc/dma.c 2003-05-20 04:46:40.000000000 -0700 @@ -57,7 +57,7 @@ if (end > PAGE_SIZE) end = PAGE_SIZE; - if (offset + (int) TRANSFER_SIZE > end) + if (offset + TRANSFER_SIZE >= end) flags |= DMA_END_L; sg->length = end - TRANSFER_SIZE; @@ -95,27 +95,31 @@ if (!(status & DMA_ST_INT)) return IRQ_HANDLED; - if (status & DMA_ST_OFL && !dma->sg) - break; - - iomd_get_next_sg(&dma->cur_sg, dma); + if ((dma->state ^ status) & DMA_ST_AB) + iomd_get_next_sg(&dma->cur_sg, dma); switch (status & (DMA_ST_OFL | DMA_ST_AB)) { case DMA_ST_OFL: /* OIA */ case DMA_ST_AB: /* .IB */ iomd_writel(dma->cur_sg.dma_address, base + CURA); iomd_writel(dma->cur_sg.length, base + ENDA); + dma->state = DMA_ST_AB; break; case DMA_ST_OFL | DMA_ST_AB: /* OIB */ case 0: /* .IA */ iomd_writel(dma->cur_sg.dma_address, base + CURB); iomd_writel(dma->cur_sg.length, base + ENDB); + dma->state = 0; break; } + + if (status & DMA_ST_OFL && + dma->cur_sg.length == (DMA_END_S|DMA_END_L)) + break; } while (1); - iomd_writeb(0, dma->dma_base + CR); + dma->state = ~DMA_ST_AB; disable_irq(irq); return IRQ_HANDLED; @@ -152,6 +156,7 @@ } iomd_writeb(DMA_CR_C, dma_base + CR); + dma->state = DMA_ST_AB; } if (dma->dma_mode == DMA_MODE_READ) @@ -165,13 +170,11 @@ { unsigned long dma_base = dma->dma_base; unsigned long flags; - unsigned int ctrl; local_irq_save(flags); - ctrl = iomd_readb(dma_base + CR); - if (ctrl & DMA_CR_E) + if (dma->state != ~DMA_ST_AB) disable_irq(dma->dma_irq); - iomd_writeb(ctrl & ~DMA_CR_E, dma_base + CR); + iomd_writeb(0, dma_base + CR); local_irq_restore(flags); } diff -urN linux-2.5.69-bk13/arch/i386/kernel/Makefile linux-2.5.69-bk14/arch/i386/kernel/Makefile --- linux-2.5.69-bk13/arch/i386/kernel/Makefile 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/i386/kernel/Makefile 2003-05-20 04:46:40.000000000 -0700 @@ -17,7 +17,8 @@ obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_MICROCODE) += microcode.o -obj-$(CONFIG_APM) += apm.o suspend.o +obj-$(CONFIG_PM) += suspend.o +obj-$(CONFIG_APM) += apm.o obj-$(CONFIG_X86_SMP) += smp.o smpboot.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-$(CONFIG_X86_MPPARSE) += mpparse.o diff -urN linux-2.5.69-bk13/arch/i386/kernel/process.c linux-2.5.69-bk14/arch/i386/kernel/process.c --- linux-2.5.69-bk13/arch/i386/kernel/process.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/i386/kernel/process.c 2003-05-20 04:46:40.000000000 -0700 @@ -212,7 +212,6 @@ */ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { - struct task_struct *p; struct pt_regs regs; memset(®s, 0, sizeof(regs)); @@ -228,8 +227,7 @@ regs.eflags = 0x286; /* Ok, create the new process.. */ - p = do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); } /* @@ -518,15 +516,11 @@ asmlinkage int sys_fork(struct pt_regs regs) { - struct task_struct *p; - - p = do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL); } asmlinkage int sys_clone(struct pt_regs regs) { - struct task_struct *p; unsigned long clone_flags; unsigned long newsp; int __user *parent_tidptr, *child_tidptr; @@ -537,8 +531,7 @@ child_tidptr = (int __user *)regs.edi; if (!newsp) newsp = regs.esp; - p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, parent_tidptr, child_tidptr); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, parent_tidptr, child_tidptr); } /* @@ -553,10 +546,7 @@ */ asmlinkage int sys_vfork(struct pt_regs regs) { - struct task_struct *p; - - p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL); } /* diff -urN linux-2.5.69-bk13/arch/i386/kernel/smpboot.c linux-2.5.69-bk14/arch/i386/kernel/smpboot.c --- linux-2.5.69-bk13/arch/i386/kernel/smpboot.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/i386/kernel/smpboot.c 2003-05-20 04:46:40.000000000 -0700 @@ -493,7 +493,7 @@ * don't care about the eip and regs settings since * we'll never reschedule the forked task. */ - return do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); + return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); } #ifdef CONFIG_NUMA @@ -793,6 +793,7 @@ idle = fork_by_hand(); if (IS_ERR(idle)) panic("failed fork for CPU %d", cpu); + wake_up_forked_process(idle); /* * We remove it from the pidhash and the runqueue @@ -935,7 +936,7 @@ static void __init smp_boot_cpus(unsigned int max_cpus) { - int apicid, cpu, bit; + int apicid, cpu, bit, kicked; /* * Setup boot CPU information @@ -1018,7 +1019,8 @@ */ Dprintk("CPU present map: %lx\n", phys_cpu_present_map); - for (bit = 0; bit < NR_CPUS; bit++) { + kicked = 1; + for (bit = 0; kicked < NR_CPUS && bit < BITS_PER_LONG; bit++) { apicid = cpu_present_to_apicid(bit); /* * Don't even attempt to start the boot CPU! @@ -1034,6 +1036,8 @@ if (do_boot_cpu(apicid)) printk("CPU #%d not responding - cannot use it.\n", apicid); + else + ++kicked; } /* diff -urN linux-2.5.69-bk13/arch/i386/kernel/suspend.c linux-2.5.69-bk14/arch/i386/kernel/suspend.c --- linux-2.5.69-bk13/arch/i386/kernel/suspend.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/i386/kernel/suspend.c 2003-05-20 04:46:40.000000000 -0700 @@ -130,3 +130,6 @@ } } + +EXPORT_SYMBOL(save_processor_state); +EXPORT_SYMBOL(restore_processor_state); diff -urN linux-2.5.69-bk13/arch/i386/kernel/suspend_asm.S linux-2.5.69-bk14/arch/i386/kernel/suspend_asm.S --- linux-2.5.69-bk13/arch/i386/kernel/suspend_asm.S 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/i386/kernel/suspend_asm.S 2003-05-20 04:46:40.000000000 -0700 @@ -7,6 +7,12 @@ #include .data + .align 4 + .globl saved_context_eax, saved_context_ebx + .globl saved_context_ecx, saved_context_edx + .globl saved_context_esp, saved_context_ebp + .globl saved_context_esi, saved_context_edi + .globl saved_context_eflags saved_context_eax: .long 0 saved_context_ebx: diff -urN linux-2.5.69-bk13/arch/i386/mach-voyager/voyager_smp.c linux-2.5.69-bk14/arch/i386/mach-voyager/voyager_smp.c --- linux-2.5.69-bk13/arch/i386/mach-voyager/voyager_smp.c 2003-05-04 16:53:13.000000000 -0700 +++ linux-2.5.69-bk14/arch/i386/mach-voyager/voyager_smp.c 2003-05-20 04:46:40.000000000 -0700 @@ -247,7 +247,7 @@ /* This is for the new dynamic CPU boot code */ volatile unsigned long cpu_callin_map = 0; -unsigned long cpu_callout_map = 0; +volatile unsigned long cpu_callout_map = 0; /* The per processor IRQ masks (these are usually kept in sync) */ static __u16 vic_irq_mask[NR_CPUS] __cacheline_aligned; @@ -531,7 +531,7 @@ struct pt_regs regs; /* don't care about the eip and regs settings since we'll * never reschedule the forked task. */ - return do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); + return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); } diff -urN linux-2.5.69-bk13/arch/sparc/Kconfig linux-2.5.69-bk14/arch/sparc/Kconfig --- linux-2.5.69-bk13/arch/sparc/Kconfig 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/Kconfig 2003-05-20 04:46:40.000000000 -0700 @@ -1000,6 +1000,13 @@ If you say Y here, various routines which may sleep will become very noisy if they are called with a spinlock held. +config DEBUG_BUGVERBOSE + bool "Verbose BUG() reporting (adds 70K)" + help + Say Y here to make BUG() panics output the file name and line number + of the BUG call as well as the EIP and oops trace. This aids + debugging but costs about 70-100K of memory. + endmenu source "security/Kconfig" diff -urN linux-2.5.69-bk13/arch/sparc/defconfig linux-2.5.69-bk14/arch/sparc/defconfig --- linux-2.5.69-bk13/arch/sparc/defconfig 2003-05-04 16:53:36.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/defconfig 2003-05-20 04:46:40.000000000 -0700 @@ -2,7 +2,6 @@ # Automatically generated make config: don't edit # CONFIG_MMU=y -CONFIG_SWAP=y CONFIG_UID16=y CONFIG_HIGHMEM=y CONFIG_GENERIC_ISA_DMA=y @@ -15,10 +14,11 @@ # # General setup # -CONFIG_NET=y +CONFIG_SWAP=y CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +CONFIG_LOG_BUF_SHIFT=14 # # Loadable module support @@ -26,6 +26,8 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set CONFIG_KMOD=y # @@ -45,6 +47,7 @@ CONFIG_SUN_PM=y # CONFIG_SUN4 is not set CONFIG_PCI=y +# CONFIG_PCI_LEGACY_PROC is not set # CONFIG_PCI_NAMES is not set CONFIG_SUN_OPENPROMFS=m CONFIG_KCORE_ELF=y @@ -59,15 +62,17 @@ # CONFIG_PARPORT is not set # -# Console drivers +# Graphics support # -# CONFIG_PROM_CONSOLE is not set -CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB is not set # -# Frame-buffer support +# Console display driver support # -# CONFIG_FB is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +# CONFIG_PROM_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y # # Memory Technology Devices (MTD) @@ -130,7 +135,32 @@ # # SCSI support # -# CONFIG_SCSI is not set +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CDrom) +# +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_SR_EXTRA_DEVS=2 +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +CONFIG_SCSI_SUNESP=y +CONFIG_SCSI_QLOGICPTI=m # # Fibre Channel support @@ -138,6 +168,11 @@ # CONFIG_FC4 is not set # +# Networking support +# +CONFIG_NET=y + +# # Networking options # CONFIG_PACKET=y @@ -389,6 +424,7 @@ # USB support # # CONFIG_USB is not set +# CONFIG_USB_GADGET is not set # # Bluetooth support @@ -407,6 +443,7 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_SPINLOCK=y # CONFIG_DEBUG_SPINLOCK_SLEEP is not set +CONFIG_DEBUG_BUGVERBOSE=y # # Security options diff -urN linux-2.5.69-bk13/arch/sparc/kernel/asm-offsets.c linux-2.5.69-bk14/arch/sparc/kernel/asm-offsets.c --- linux-2.5.69-bk13/arch/sparc/kernel/asm-offsets.c 2003-05-04 16:53:33.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/asm-offsets.c 2003-05-20 04:46:40.000000000 -0700 @@ -22,8 +22,6 @@ int foo(void) { DEFINE(AOFF_task_thread, offsetof(struct task_struct, thread)); - DEFINE(AOFF_task_ptrace, offsetof(struct task_struct, ptrace)); - DEFINE(AOFF_task_blocked, offsetof(struct task_struct, blocked)); BLANK(); /* XXX This is the stuff for sclow.S, kill it. */ DEFINE(AOFF_task_pid, offsetof(struct task_struct, pid)); diff -urN linux-2.5.69-bk13/arch/sparc/kernel/entry.S linux-2.5.69-bk14/arch/sparc/kernel/entry.S --- linux-2.5.69-bk13/arch/sparc/kernel/entry.S 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/entry.S 2003-05-20 04:46:40.000000000 -0700 @@ -359,11 +359,11 @@ andcc %o1, %o4, %g0 be,a smp4m_ticker cmp %l7, 14 - cmp %l7, 13 + sethi %hi(0x40000000), %o2 add %o5, %o3, %o5 - bne,a 1f - sethi %hi(0x40000000), %o2 - sethi %hi(0x20000000), %o2 + andcc %o1, %o2, %g0 + be,a 1f + sethi %hi(0x20000000), %o2 1: st %o2, [%o5 + 0x4] WRITE_PAUSE @@ -374,7 +374,8 @@ WRITE_PAUSE wr %l4, PSR_ET, %psr WRITE_PAUSE - cmp %l7, 13 + srl %o2, (16+14), %o2 + tst %o2 bne 2f nop call C_LABEL(smp_reschedule_irq) @@ -1245,9 +1246,8 @@ call C_LABEL(do_ptrace) add %sp, STACKFRAME_SZ, %o0 - ld [%curptr + TI_TASK], %l5 - ld [%l5 + AOFF_task_ptrace], %l5 - andcc %l5, 0x02, %g0 + ld [%curptr + TI_FLAGS], %l5 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 be 1f nop @@ -1296,9 +1296,8 @@ call C_LABEL(do_sigpause) add %sp, STACKFRAME_SZ, %o1 - ld [%curptr + TI_TASK], %l5 - ld [%l5 + AOFF_task_ptrace], %l5 - andcc %l5, 0x02, %g0 + ld [%curptr + TI_FLAGS], %l5 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 be 1f nop @@ -1315,9 +1314,8 @@ call C_LABEL(do_sigsuspend) add %sp, STACKFRAME_SZ, %o0 - ld [%curptr + TI_TASK], %l5 - ld [%l5 + AOFF_task_ptrace], %l5 - andcc %l5, 0x02, %g0 + ld [%curptr + TI_FLAGS], %l5 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 be 1f nop @@ -1335,9 +1333,8 @@ call C_LABEL(do_rt_sigsuspend) add %sp, STACKFRAME_SZ, %o2 - ld [%curptr + TI_TASK], %l5 - ld [%l5 + AOFF_task_ptrace], %l5 - andcc %l5, 0x02, %g0 + ld [%curptr + TI_FLAGS], %l5 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 be 1f nop @@ -1354,9 +1351,8 @@ call C_LABEL(do_sigreturn) add %sp, STACKFRAME_SZ, %o0 - ld [%curptr + TI_TASK], %l5 - ld [%l5 + AOFF_task_ptrace], %l5 - andcc %l5, 0x02, %g0 + ld [%curptr + TI_FLAGS], %l5 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 be 1f nop @@ -1375,9 +1371,8 @@ call C_LABEL(do_rt_sigreturn) add %sp, STACKFRAME_SZ, %o0 - ld [%curptr + TI_TASK], %l5 - ld [%l5 + AOFF_task_ptrace], %l5 - andcc %l5, 0x02, %g0 + ld [%curptr + TI_FLAGS], %l5 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 be 1f nop @@ -1511,10 +1506,9 @@ mov %i1, %o1 mov %i2, %o2 - ld [%curptr + TI_TASK], %l5 - ld [%l5 + AOFF_task_ptrace], %l5 + ld [%curptr + TI_FLAGS], %l5 mov %i3, %o3 - andcc %l5, 0x02, %g0 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 mov %i4, %o4 bne linux_syscall_trace mov %i0, %l5 @@ -1526,13 +1520,12 @@ .globl C_LABEL(ret_sys_call) C_LABEL(ret_sys_call): - ld [%curptr + TI_TASK], %l6 - ld [%l6 + AOFF_task_ptrace], %l6 + ld [%curptr + TI_FLAGS], %l6 cmp %o0, -ENOIOCTLCMD ld [%sp + STACKFRAME_SZ + PT_PSR], %g3 set PSR_C, %g2 bgeu 1f - andcc %l6, 0x02, %l6 + andcc %l6, _TIF_SYSCALL_TRACE, %g0 /* System call success, clear Carry condition code. */ andn %g3, %g2, %g3 diff -urN linux-2.5.69-bk13/arch/sparc/kernel/process.c linux-2.5.69-bk14/arch/sparc/kernel/process.c --- linux-2.5.69-bk13/arch/sparc/kernel/process.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/process.c 2003-05-20 04:46:40.000000000 -0700 @@ -403,7 +403,6 @@ { unsigned long parent_tid_ptr = 0; unsigned long child_tid_ptr = 0; - struct task_struct *p; clone_flags &= ~CLONE_IDLETASK; @@ -411,11 +410,10 @@ parent_tid_ptr = regs->u_regs[UREG_G2]; child_tid_ptr = regs->u_regs[UREG_G3]; } - p = do_fork(clone_flags, stack_start, - regs, stack_size, - (int *) parent_tid_ptr, - (int *) child_tid_ptr); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + return do_fork(clone_flags, stack_start, + regs, stack_size, + (int *) parent_tid_ptr, + (int *) child_tid_ptr); } /* Copy a Sparc thread. The fork() return value conventions diff -urN linux-2.5.69-bk13/arch/sparc/kernel/ptrace.c linux-2.5.69-bk14/arch/sparc/kernel/ptrace.c --- linux-2.5.69-bk13/arch/sparc/kernel/ptrace.c 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/ptrace.c 2003-05-20 04:46:40.000000000 -0700 @@ -521,7 +521,7 @@ addr = 1; case PTRACE_CONT: { /* restart after signal. */ - if ((unsigned long) data > _NSIG) { + if (data > _NSIG) { pt_error_return(regs, EIO); goto out_tsk; } @@ -545,11 +545,10 @@ child->exit_code = data; #ifdef DEBUG_PTRACE - printk("CONT: %s [%d]: set exit_code = %x %x %x\n", child->comm, - child->pid, child->exit_code, + printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", + child->comm, child->pid, child->exit_code, child->thread.kregs->pc, child->thread.kregs->npc); - #endif wake_up_process(child); pt_succ_return(regs, 0); diff -urN linux-2.5.69-bk13/arch/sparc/kernel/sclow.S linux-2.5.69-bk14/arch/sparc/kernel/sclow.S --- linux-2.5.69-bk13/arch/sparc/kernel/sclow.S 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/sclow.S 2003-05-20 04:46:40.000000000 -0700 @@ -67,27 +67,6 @@ mov 256, %i0 CC_AND_RETT - .globl LABEL(sunossblock) -LABEL(sunossblock): - LOAD_CURRENT(l4, l5) - ld [%l4 + TI_TASK], %l4 - set -65793, %l5 - and %i0, %l5, %l5 - ld [%l4 + AOFF_task_blocked], %i0 - or %i0, %l5, %l5 - st %l5, [%l4 + AOFF_task_blocked] - CC_AND_RETT - - .globl LABEL(sunossmask) -LABEL(sunossmask): - LOAD_CURRENT(l4, l5) - ld [%l4 + TI_TASK], %l4 - set -65793, %l5 - and %i0, %l5, %l5 - ld [%l4 + AOFF_task_blocked], %i0 - st %l5, [%l4 + AOFF_task_blocked] - CC_AND_RETT - .globl LABEL(getpagesize) LABEL(getpagesize): set PAGE_SIZE, %i0 diff -urN linux-2.5.69-bk13/arch/sparc/kernel/smp.c linux-2.5.69-bk14/arch/sparc/kernel/smp.c --- linux-2.5.69-bk13/arch/sparc/kernel/smp.c 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/smp.c 2003-05-20 04:46:40.000000000 -0700 @@ -133,6 +133,16 @@ smp4d_boot_cpus(); } +void smp_send_reschedule(int cpu) +{ + smp_message_pass (cpu, MSG_RESCHEDULE, 0, 0); +} + +void smp_send_stop(void) +{ + smp_message_pass (MSG_ALL_BUT_SELF, MSG_STOP_CPU, 0, 0); +} + void smp_flush_cache_all(void) { xc0((smpfunc_t) BTFIXUP_CALL(local_flush_cache_all)); diff -urN linux-2.5.69-bk13/arch/sparc/kernel/sparc_ksyms.c linux-2.5.69-bk14/arch/sparc/kernel/sparc_ksyms.c --- linux-2.5.69-bk13/arch/sparc/kernel/sparc_ksyms.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/sparc_ksyms.c 2003-05-20 04:46:40.000000000 -0700 @@ -53,6 +53,7 @@ #endif #include #include +#include extern spinlock_t rtc_lock; @@ -312,5 +313,9 @@ EXPORT_SYMBOL_DOT(div); EXPORT_SYMBOL_DOT(udiv); +#ifdef CONFIG_DEBUG_BUGVERBOSE +EXPORT_SYMBOL(do_BUG); +#endif + /* Sun Power Management Idle Handler */ EXPORT_SYMBOL(pm_idle); diff -urN linux-2.5.69-bk13/arch/sparc/kernel/traps.c linux-2.5.69-bk14/arch/sparc/kernel/traps.c --- linux-2.5.69-bk13/arch/sparc/kernel/traps.c 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc/kernel/traps.c 2003-05-20 04:46:40.000000000 -0700 @@ -463,6 +463,14 @@ send_sig_info(SIGFPE, &info, current); } +#ifdef CONFIG_DEBUG_BUGVERBOSE +void do_BUG(const char *file, int line) +{ + // bust_spinlocks(1); XXX Not in our original BUG() + printk("kernel BUG at %s:%d!\n", file, line); +} +#endif + /* Since we have our mappings set up, on multiprocessors we can spin them * up here so that timer interrupts work during initialization. */ diff -urN linux-2.5.69-bk13/arch/sparc64/kernel/process.c linux-2.5.69-bk14/arch/sparc64/kernel/process.c --- linux-2.5.69-bk13/arch/sparc64/kernel/process.c 2003-05-04 16:53:13.000000000 -0700 +++ linux-2.5.69-bk14/arch/sparc64/kernel/process.c 2003-05-20 04:46:40.000000000 -0700 @@ -568,7 +568,6 @@ struct pt_regs *regs, unsigned long stack_size) { - struct task_struct *p; unsigned long parent_tid_ptr = 0; unsigned long child_tid_ptr = 0; @@ -583,12 +582,10 @@ } } - p = do_fork(clone_flags, stack_start, - regs, stack_size, - (int *) parent_tid_ptr, - (int *) child_tid_ptr); - - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + return do_fork(clone_flags, stack_start, + regs, stack_size, + (int *) parent_tid_ptr, + (int *) child_tid_ptr); } /* Copy a Sparc thread. The fork() return value conventions diff -urN linux-2.5.69-bk13/crypto/Kconfig linux-2.5.69-bk14/crypto/Kconfig --- linux-2.5.69-bk13/crypto/Kconfig 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/crypto/Kconfig 2003-05-20 04:46:40.000000000 -0700 @@ -138,7 +138,7 @@ config CRYPTO_DEFLATE tristate "Deflate compression algorithm" depends on CRYPTO - default y if INET_IPCOMP=y || INET_IPCOMP=m + default y if INET_IPCOMP=y || INET_IPCOMP=m || INET6_IPCOMP=y || INET6_IPCOMP=m help This is the Deflate algorithm (RFC1951), specified for use in IPSec with the IPCOMP protocol (RFC3173, RFC2394). diff -urN linux-2.5.69-bk13/drivers/acorn/char/i2c.c linux-2.5.69-bk14/drivers/acorn/char/i2c.c --- linux-2.5.69-bk13/drivers/acorn/char/i2c.c 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/char/i2c.c 2003-05-20 04:46:40.000000000 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -306,7 +307,7 @@ .id = I2C_HW_B_IOC, .algo_data = &ioc_data, .client_register = ioc_client_reg, - .client_unregister = ioc_client_unreg + .client_unregister = ioc_client_unreg, .dev = { .name = "IOC/IOMD", }, diff -urN linux-2.5.69-bk13/drivers/acorn/char/pcf8583.c linux-2.5.69-bk14/drivers/acorn/char/pcf8583.c --- linux-2.5.69-bk13/drivers/acorn/char/pcf8583.c 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/char/pcf8583.c 2003-05-20 04:46:40.000000000 -0700 @@ -34,7 +34,7 @@ .force = ignore, }; -#define DAT(x) ((unsigned int)(x->data)) +#define DAT(x) ((unsigned int)(x->dev.driver_data)) static int pcf8583_attach(struct i2c_adapter *adap, int addr, unsigned short flags, @@ -51,13 +51,13 @@ if (!c) return -ENOMEM; - strcpy(c->name, "PCF8583"); + strcpy(c->dev.name, "PCF8583"); c->id = pcf8583_driver.id; c->flags = 0; c->addr = addr; c->adapter = adap; c->driver = &pcf8583_driver; - c->data = NULL; + c->dev.driver_data = NULL; if (i2c_transfer(c->adapter, msgs, 2) == 2) DAT(c) = buf[0]; diff -urN linux-2.5.69-bk13/drivers/acorn/net/Kconfig linux-2.5.69-bk14/drivers/acorn/net/Kconfig --- linux-2.5.69-bk13/drivers/acorn/net/Kconfig 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/net/Kconfig 1969-12-31 16:00:00.000000000 -0800 @@ -1,25 +0,0 @@ -# -# Acorn Network device configuration -# These are for Acorn's Expansion card network interfaces -# -config ARM_ETHER1 - tristate "Acorn Ether1 support" - depends on NET_ETHERNET && ARM && ARCH_ACORN - help - If you have an Acorn system with one of these (AKA25) network cards, - you should say Y to this option if you wish to use it with Linux. - -config ARM_ETHER3 - tristate "Acorn/ANT Ether3 support" - depends on NET_ETHERNET && ARM && ARCH_ACORN - help - If you have an Acorn system with one of these network cards, you - should say Y to this option if you wish to use it with Linux. - -config ARM_ETHERH - tristate "I-cubed EtherH/ANT EtherM support" - depends on NET_ETHERNET && ARM && ARCH_ACORN - help - If you have an Acorn system with one of these network cards, you - should say Y to this option if you wish to use it with Linux. - diff -urN linux-2.5.69-bk13/drivers/acorn/net/Makefile linux-2.5.69-bk14/drivers/acorn/net/Makefile --- linux-2.5.69-bk13/drivers/acorn/net/Makefile 2003-05-04 16:53:32.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/net/Makefile 1969-12-31 16:00:00.000000000 -0800 @@ -1,8 +0,0 @@ -# File: drivers/acorn/net/Makefile -# -# Makefile for the Acorn ethercard network device drivers -# - -obj-$(CONFIG_ARM_ETHERH) += etherh.o -obj-$(CONFIG_ARM_ETHER3) += ether3.o -obj-$(CONFIG_ARM_ETHER1) += ether1.o diff -urN linux-2.5.69-bk13/drivers/acorn/net/ether1.c linux-2.5.69-bk14/drivers/acorn/net/ether1.c --- linux-2.5.69-bk13/drivers/acorn/net/ether1.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/net/ether1.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,1105 +0,0 @@ -/* - * linux/drivers/acorn/net/ether1.c - * - * Copyright (C) 1996-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Acorn ether1 driver (82586 chip) for Acorn machines - * - * We basically keep two queues in the cards memory - one for transmit - * and one for receive. Each has a head and a tail. The head is where - * we/the chip adds packets to be transmitted/received, and the tail - * is where the transmitter has got to/where the receiver will stop. - * Both of these queues are circular, and since the chip is running - * all the time, we have to be careful when we modify the pointers etc - * so that the buffer memory contents is valid all the time. - * - * Change log: - * 1.00 RMK Released - * 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now. - * 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready - * 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt. - * Should prevent lockup. - * 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong. - * TDR now only reports failure when chip reports non-zero - * TDR time-distance. - * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1 - * 1.06 RMK 10/02/2000 Updated for 2.3.43 - * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define __ETHER1_C -#include "ether1.h" - -static unsigned int net_debug = NET_DEBUG; - -#define BUFFER_SIZE 0x10000 -#define TX_AREA_START 0x00100 -#define TX_AREA_END 0x05000 -#define RX_AREA_START 0x05000 -#define RX_AREA_END 0x0fc00 - -static int ether1_open(struct net_device *dev); -static int ether1_sendpacket(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t ether1_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int ether1_close(struct net_device *dev); -static struct net_device_stats *ether1_getstats(struct net_device *dev); -static void ether1_setmulticastlist(struct net_device *dev); -static void ether1_timeout(struct net_device *dev); - -/* ------------------------------------------------------------------------- */ - -static char version[] __initdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; - -#define BUS_16 16 -#define BUS_8 8 - -/* ------------------------------------------------------------------------- */ - -#define DISABLEIRQS 1 -#define NORMALIRQS 0 - -#define ether1_inw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs) -#define ether1_outw(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs) - -static inline unsigned short -ether1_inw_p (struct net_device *dev, int addr, int svflgs) -{ - unsigned long flags; - unsigned short ret; - - if (svflgs) - local_irq_save (flags); - - outb (addr >> 12, REG_PAGE); - ret = inw (ETHER1_RAM + ((addr & 4095) >> 1)); - if (svflgs) - local_irq_restore (flags); - return ret; -} - -static inline void -ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs) -{ - unsigned long flags; - - if (svflgs) - local_irq_save (flags); - - outb (addr >> 12, REG_PAGE); - outw (val, ETHER1_RAM + ((addr & 4095) >> 1)); - if (svflgs) - local_irq_restore (flags); -} - -/* - * Some inline assembler to allow fast transfers on to/off of the card. - * Since this driver depends on some features presented by the ARM - * specific architecture, and that you can't configure this driver - * without specifiing ARM mode, this is not a problem. - * - * This routine is essentially an optimised memcpy from the card's - * onboard RAM to kernel memory. - */ -static void -ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) -{ - unsigned int page, thislen, offset, addr; - - offset = start & 4095; - page = start >> 12; - addr = ioaddr(ETHER1_RAM + (offset >> 1)); - - if (offset + length > 4096) - thislen = 4096 - offset; - else - thislen = length; - - do { - int used; - - outb(page, REG_PAGE); - length -= thislen; - - __asm__ __volatile__( - "subs %3, %3, #2 - bmi 2f -1: ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bmi 2f - ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bmi 2f - ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bmi 2f - ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bpl 1b -2: adds %3, %3, #1 - ldreqb %0, [%1] - streqb %0, [%2]" - : "=&r" (used), "=&r" (data) - : "r" (addr), "r" (thislen), "1" (data)); - - addr = ioaddr(ETHER1_RAM); - - thislen = length; - if (thislen > 4096) - thislen = 4096; - page++; - } while (thislen); -} - -static void -ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) -{ - unsigned int page, thislen, offset, addr; - - offset = start & 4095; - page = start >> 12; - addr = ioaddr(ETHER1_RAM + (offset >> 1)); - - if (offset + length > 4096) - thislen = 4096 - offset; - else - thislen = length; - - do { - int used; - - outb(page, REG_PAGE); - length -= thislen; - - __asm__ __volatile__( - "subs %3, %3, #2 - bmi 2f -1: ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bmi 2f - ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bmi 2f - ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bmi 2f - ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bpl 1b -2: adds %3, %3, #1 - ldreqb %0, [%2] - streqb %0, [%1]" - : "=&r" (used), "=&r" (data) - : "r" (addr), "r" (thislen), "1" (data)); - - addr = ioaddr(ETHER1_RAM); - - thislen = length; - if (thislen > 4096) - thislen = 4096; - page++; - } while (thislen); -} - -static int __init -ether1_ramtest(struct net_device *dev, unsigned char byte) -{ - unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); - int i, ret = BUFFER_SIZE; - int max_errors = 15; - int bad = -1; - int bad_start = 0; - - if (!buffer) - return 1; - - memset (buffer, byte, BUFFER_SIZE); - ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE); - memset (buffer, byte ^ 0xff, BUFFER_SIZE); - ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE); - - for (i = 0; i < BUFFER_SIZE; i++) { - if (buffer[i] != byte) { - if (max_errors >= 0 && bad != buffer[i]) { - if (bad != -1) - printk ("\n"); - printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X", - dev->name, buffer[i], byte, i); - ret = -ENODEV; - max_errors --; - bad = buffer[i]; - bad_start = i; - } - } else { - if (bad != -1) { - if (bad_start == i - 1) - printk ("\n"); - else - printk (" - 0x%04X\n", i - 1); - bad = -1; - } - } - } - - if (bad != -1) - printk (" - 0x%04X\n", BUFFER_SIZE); - kfree (buffer); - - return ret; -} - -static int -ether1_reset (struct net_device *dev) -{ - outb (CTRL_RST|CTRL_ACK, REG_CONTROL); - return BUS_16; -} - -static int __init -ether1_init_2(struct net_device *dev) -{ - int i; - dev->mem_start = 0; - - i = ether1_ramtest (dev, 0x5a); - - if (i > 0) - i = ether1_ramtest (dev, 0x1e); - - if (i <= 0) - return -ENODEV; - - dev->mem_end = i; - return 0; -} - -/* - * These are the structures that are loaded into the ether RAM card to - * initialise the 82586 - */ - -/* at 0x0100 */ -#define NOP_ADDR (TX_AREA_START) -#define NOP_SIZE (0x06) -static nop_t init_nop = { - 0, - CMD_NOP, - NOP_ADDR -}; - -/* at 0x003a */ -#define TDR_ADDR (0x003a) -#define TDR_SIZE (0x08) -static tdr_t init_tdr = { - 0, - CMD_TDR | CMD_INTR, - NOP_ADDR, - 0 -}; - -/* at 0x002e */ -#define MC_ADDR (0x002e) -#define MC_SIZE (0x0c) -static mc_t init_mc = { - 0, - CMD_SETMULTICAST, - TDR_ADDR, - 0, - { { 0, } } -}; - -/* at 0x0022 */ -#define SA_ADDR (0x0022) -#define SA_SIZE (0x0c) -static sa_t init_sa = { - 0, - CMD_SETADDRESS, - MC_ADDR, - { 0, } -}; - -/* at 0x0010 */ -#define CFG_ADDR (0x0010) -#define CFG_SIZE (0x12) -static cfg_t init_cfg = { - 0, - CMD_CONFIG, - SA_ADDR, - 8, - 8, - CFG8_SRDY, - CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6), - 0, - 0x60, - 0, - CFG13_RETRY(15) | CFG13_SLOTH(2), - 0, -}; - -/* at 0x0000 */ -#define SCB_ADDR (0x0000) -#define SCB_SIZE (0x10) -static scb_t init_scb = { - 0, - SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX, - CFG_ADDR, - RX_AREA_START, - 0, - 0, - 0, - 0 -}; - -/* at 0xffee */ -#define ISCP_ADDR (0xffee) -#define ISCP_SIZE (0x08) -static iscp_t init_iscp = { - 1, - SCB_ADDR, - 0x0000, - 0x0000 -}; - -/* at 0xfff6 */ -#define SCP_ADDR (0xfff6) -#define SCP_SIZE (0x0a) -static scp_t init_scp = { - SCP_SY_16BBUS, - { 0, 0 }, - ISCP_ADDR, - 0 -}; - -#define RFD_SIZE (0x16) -static rfd_t init_rfd = { - 0, - 0, - 0, - 0, - { 0, }, - { 0, }, - 0 -}; - -#define RBD_SIZE (0x0a) -static rbd_t init_rbd = { - 0, - 0, - 0, - 0, - ETH_FRAME_LEN + 8 -}; - -#define TX_SIZE (0x08) -#define TBD_SIZE (0x08) - -static int -ether1_init_for_open (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int i, status, addr, next, next2; - int failures = 0; - - outb (CTRL_RST|CTRL_ACK, REG_CONTROL); - - for (i = 0; i < 6; i++) - init_sa.sa_addr[i] = dev->dev_addr[i]; - - /* load data structures into ether1 RAM */ - ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE); - ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE); - ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE); - ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE); - ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE); - ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE); - ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE); - ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE); - - if (ether1_inw (dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) { - printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n", - dev->name); - return 1; - } - - /* - * setup circularly linked list of { rfd, rbd, buffer }, with - * all rfds circularly linked, rbds circularly linked. - * First rfd is linked to scp, first rbd is linked to first - * rfd. Last rbd has a suspend command. - */ - addr = RX_AREA_START; - do { - next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; - next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; - - if (next2 >= RX_AREA_END) { - next = RX_AREA_START; - init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND; - priv->rx_tail = addr; - } else - init_rfd.rfd_command = 0; - if (addr == RX_AREA_START) - init_rfd.rfd_rbdoffset = addr + RFD_SIZE; - else - init_rfd.rfd_rbdoffset = 0; - init_rfd.rfd_link = next; - init_rbd.rbd_link = next + RFD_SIZE; - init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE; - - ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE); - ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE); - addr = next; - } while (next2 < RX_AREA_END); - - priv->tx_link = NOP_ADDR; - priv->tx_head = NOP_ADDR + NOP_SIZE; - priv->tx_tail = TDR_ADDR; - priv->rx_head = RX_AREA_START; - - /* release reset & give 586 a prod */ - priv->resetting = 1; - priv->initialising = 1; - outb (CTRL_RST, REG_CONTROL); - outb (0, REG_CONTROL); - outb (CTRL_CA, REG_CONTROL); - - /* 586 should now unset iscp.busy */ - i = jiffies + HZ/2; - while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) { - if (time_after(jiffies, i)) { - printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name); - return 1; - } - } - - /* check status of commands that we issued */ - i += HZ/10; - while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - failures += 1; - } - - i += HZ/10; - while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - failures += 1; - } - - i += HZ/10; - while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - failures += 1; - } - - i += HZ; - while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - } else { - status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS); - if (status & TDR_XCVRPROB) - printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name); - else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) { -#ifdef FANCY - printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name, - status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10, - (status & TDR_TIME) % 10); -#else - printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name, - status & TDR_SHORT ? "short" : "open", (status & TDR_TIME)); -#endif - } - } - - if (failures) - ether1_reset (dev); - return failures ? 1 : 0; -} - -/* ------------------------------------------------------------------------- */ - -static int -ether1_txalloc (struct net_device *dev, int size) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int start, tail; - - size = (size + 1) & ~1; - tail = priv->tx_tail; - - if (priv->tx_head + size > TX_AREA_END) { - if (tail > priv->tx_head) - return -1; - start = TX_AREA_START; - if (start + size > tail) - return -1; - priv->tx_head = start + size; - } else { - if (priv->tx_head < tail && (priv->tx_head + size) > tail) - return -1; - start = priv->tx_head; - priv->tx_head += size; - } - - return start; -} - -static int -ether1_open (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - - if (!is_valid_ether_addr(dev->dev_addr)) { - printk(KERN_WARNING "%s: invalid ethernet MAC address\n", - dev->name); - return -EINVAL; - } - - if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev)) - return -EAGAIN; - - memset (&priv->stats, 0, sizeof (struct net_device_stats)); - - if (ether1_init_for_open (dev)) { - free_irq (dev->irq, dev); - return -EAGAIN; - } - - netif_start_queue(dev); - - return 0; -} - -static void -ether1_timeout(struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - - printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n", - dev->name); - printk(KERN_WARNING "%s: resetting device\n", dev->name); - - ether1_reset (dev); - - if (ether1_init_for_open (dev)) - printk (KERN_ERR "%s: unable to restart interface\n", dev->name); - - priv->stats.tx_errors++; - netif_wake_queue(dev); -} - -static int -ether1_sendpacket (struct sk_buff *skb, struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; - int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr; - unsigned long flags; - tx_t tx; - tbd_t tbd; - nop_t nop; - - if (priv->restart) { - printk(KERN_WARNING "%s: resetting device\n", dev->name); - - ether1_reset(dev); - - if (ether1_init_for_open(dev)) - printk(KERN_ERR "%s: unable to restart interface\n", dev->name); - else - priv->restart = 0; - } - - /* - * insert packet followed by a nop - */ - txaddr = ether1_txalloc (dev, TX_SIZE); - tbdaddr = ether1_txalloc (dev, TBD_SIZE); - dataddr = ether1_txalloc (dev, len); - nopaddr = ether1_txalloc (dev, NOP_SIZE); - - tx.tx_status = 0; - tx.tx_command = CMD_TX | CMD_INTR; - tx.tx_link = nopaddr; - tx.tx_tbdoffset = tbdaddr; - tbd.tbd_opts = TBD_EOL | len; - tbd.tbd_link = I82586_NULL; - tbd.tbd_bufl = dataddr; - tbd.tbd_bufh = 0; - nop.nop_status = 0; - nop.nop_command = CMD_NOP; - nop.nop_link = nopaddr; - - local_irq_save(flags); - ether1_writebuffer (dev, &tx, txaddr, TX_SIZE); - ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE); - ether1_writebuffer (dev, skb->data, dataddr, len); - ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE); - tmp = priv->tx_link; - priv->tx_link = nopaddr; - - /* now reset the previous nop pointer */ - ether1_outw (dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS); - - local_irq_restore(flags); - - /* handle transmit */ - dev->trans_start = jiffies; - - /* check to see if we have room for a full sized ether frame */ - tmp = priv->tx_head; - tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); - priv->tx_head = tmp; - dev_kfree_skb (skb); - - if (tst == -1) - netif_stop_queue(dev); - - return 0; -} - -static void -ether1_xmit_done (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - nop_t nop; - int caddr, tst; - - caddr = priv->tx_tail; - -again: - ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); - - switch (nop.nop_command & CMD_MASK) { - case CMD_TDR: - /* special case */ - if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) - != (unsigned short)I82586_NULL) { - ether1_outw(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, - scb_command, NORMALIRQS); - outb (CTRL_CA, REG_CONTROL); - } - priv->tx_tail = NOP_ADDR; - return; - - case CMD_NOP: - if (nop.nop_link == caddr) { - if (priv->initialising == 0) - printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name); - else - priv->initialising = 0; - return; - } - if (caddr == nop.nop_link) - return; - caddr = nop.nop_link; - goto again; - - case CMD_TX: - if (nop.nop_status & STAT_COMPLETE) - break; - printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name); - priv->restart = 1; - return; - - default: - printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name, - nop.nop_command & CMD_MASK, caddr); - priv->restart = 1; - return; - } - - while (nop.nop_status & STAT_COMPLETE) { - if (nop.nop_status & STAT_OK) { - priv->stats.tx_packets ++; - priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS); - } else { - priv->stats.tx_errors ++; - - if (nop.nop_status & STAT_COLLAFTERTX) - priv->stats.collisions ++; - if (nop.nop_status & STAT_NOCARRIER) - priv->stats.tx_carrier_errors ++; - if (nop.nop_status & STAT_TXLOSTCTS) - printk (KERN_WARNING "%s: cts lost\n", dev->name); - if (nop.nop_status & STAT_TXSLOWDMA) - priv->stats.tx_fifo_errors ++; - if (nop.nop_status & STAT_COLLEXCESSIVE) - priv->stats.collisions += 16; - } - - if (nop.nop_link == caddr) { - printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name); - break; - } - - caddr = nop.nop_link; - ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); - if ((nop.nop_command & CMD_MASK) != CMD_NOP) { - printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name); - break; - } - - if (caddr == nop.nop_link) - break; - - caddr = nop.nop_link; - ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); - if ((nop.nop_command & CMD_MASK) != CMD_TX) { - printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name); - break; - } - } - priv->tx_tail = caddr; - - caddr = priv->tx_head; - tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); - priv->tx_head = caddr; - if (tst != -1) - netif_wake_queue(dev); -} - -static void -ether1_recv_done (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int status; - int nexttail, rbdaddr; - rbd_t rbd; - - do { - status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status, NORMALIRQS); - if ((status & RFD_COMPLETE) == 0) - break; - - rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS); - ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE); - - if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) { - int length = rbd.rbd_status & RBD_ACNT; - struct sk_buff *skb; - - length = (length + 1) & ~1; - skb = dev_alloc_skb (length + 2); - - if (skb) { - skb->dev = dev; - skb_reserve (skb, 2); - - ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); - - skb->protocol = eth_type_trans (skb, dev); - netif_rx (skb); - priv->stats.rx_packets ++; - } else - priv->stats.rx_dropped ++; - } else { - printk(KERN_WARNING "%s: %s\n", dev->name, - (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid"); - priv->stats.rx_dropped ++; - } - - nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link, NORMALIRQS); - /* nexttail should be rx_head */ - if (nexttail != priv->rx_head) - printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n", - dev->name, nexttail, priv->rx_head); - ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS); - ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command, NORMALIRQS); - ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status, NORMALIRQS); - ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS); - - priv->rx_tail = nexttail; - priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link, NORMALIRQS); - } while (1); -} - -static irqreturn_t -ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int status; - - status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS); - - if (status) { - ether1_outw(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX), - SCB_ADDR, scb_t, scb_command, NORMALIRQS); - outb (CTRL_CA | CTRL_ACK, REG_CONTROL); - if (status & SCB_STCX) { - ether1_xmit_done (dev); - } - if (status & SCB_STCNA) { - if (priv->resetting == 0) - printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name); - else - priv->resetting += 1; - if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) - != (unsigned short)I82586_NULL) { - ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS); - outb (CTRL_CA, REG_CONTROL); - } - if (priv->resetting == 2) - priv->resetting = 0; - } - if (status & SCB_STFR) { - ether1_recv_done (dev); - } - if (status & SCB_STRNR) { - if (ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) { - printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name); - ether1_outw (dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS); - outb (CTRL_CA, REG_CONTROL); - priv->stats.rx_dropped ++; /* we suspended due to lack of buffer space */ - } else - printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS)); - printk (KERN_WARNING "RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, - NORMALIRQS)); - } - } else - outb (CTRL_ACK, REG_CONTROL); - - return IRQ_HANDLED; -} - -static int -ether1_close (struct net_device *dev) -{ - ether1_reset (dev); - - free_irq(dev->irq, dev); - - return 0; -} - -static struct net_device_stats * -ether1_getstats (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - return &priv->stats; -} - -/* - * Set or clear the multicast filter for this adaptor. - * num_addrs == -1 Promiscuous mode, receive all packets. - * num_addrs == 0 Normal mode, clear multicast list. - * num_addrs > 0 Multicast mode, receive normal and MC packets, and do - * best-effort filtering. - */ -static void -ether1_setmulticastlist (struct net_device *dev) -{ -} - -/* ------------------------------------------------------------------------- */ - -static void __init ether1_banner(void) -{ - static unsigned int version_printed = 0; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -static int __devinit -ether1_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct net_device *dev; - struct ether1_priv *priv; - int i, ret = 0; - - ether1_banner(); - - dev = init_etherdev(NULL, sizeof(struct ether1_priv)); - if (!dev) { - ret = -ENOMEM; - goto out; - } - - SET_MODULE_OWNER(dev); - - dev->base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); - dev->irq = ec->irq; - - /* - * these will not fail - the nature of the bus ensures this - */ - request_region(dev->base_addr, 16, dev->name); - request_region(dev->base_addr + 0x800, 4096, dev->name); - - priv = (struct ether1_priv *)dev->priv; - if ((priv->bus_type = ether1_reset(dev)) == 0) { - ret = -ENODEV; - goto release; - } - - printk(KERN_INFO "%s: ether1 in slot %d, ", - dev->name, ec->slot_no); - - for (i = 0; i < 6; i++) { - dev->dev_addr[i] = inb(IDPROM_ADDRESS + i); - printk ("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - } - - if (ether1_init_2(dev)) { - ret = -ENODEV; - goto release; - } - - dev->open = ether1_open; - dev->stop = ether1_close; - dev->hard_start_xmit = ether1_sendpacket; - dev->get_stats = ether1_getstats; - dev->set_multicast_list = ether1_setmulticastlist; - dev->tx_timeout = ether1_timeout; - dev->watchdog_timeo = 5 * HZ / 100; - - ecard_set_drvdata(ec, dev); - return 0; - -release: - release_region(dev->base_addr, 16); - release_region(dev->base_addr + 0x800, 4096); - unregister_netdev(dev); - kfree(dev); -out: - return ret; -} - -static void __devexit ether1_remove(struct expansion_card *ec) -{ - struct net_device *dev = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - - unregister_netdev(dev); - - release_region(dev->base_addr, 16); - release_region(dev->base_addr + 0x800, 4096); - kfree(dev); -} - -static const struct ecard_id ether1_ids[] = { - { MANU_ACORN, PROD_ACORN_ETHER1 }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver ether1_driver = { - .probe = ether1_probe, - .remove = __devexit_p(ether1_remove), - .id_table = ether1_ids, - .drv = { - .name = "ether1", - }, -}; - -static int __init ether1_init(void) -{ - return ecard_register_driver(ðer1_driver); -} - -static void __exit ether1_exit(void) -{ - ecard_remove_driver(ðer1_driver); -} - -module_init(ether1_init); -module_exit(ether1_exit); - -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/net/ether1.h linux-2.5.69-bk14/drivers/acorn/net/ether1.h --- linux-2.5.69-bk13/drivers/acorn/net/ether1.h 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/net/ether1.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,278 +0,0 @@ -/* - * linux/drivers/acorn/net/ether1.h - * - * Copyright (C) 1996 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Network driver for Acorn Ether1 cards. - */ - -#ifndef _LINUX_ether1_H -#define _LINUX_ether1_H - -#ifdef __ETHER1_C -/* use 0 for production, 1 for verification, >2 for debug */ -#ifndef NET_DEBUG -#define NET_DEBUG 0 -#endif - -/* Page register */ -#define REG_PAGE (dev->base_addr + 0x00) - -/* Control register */ -#define REG_CONTROL (dev->base_addr + 0x01) -#define CTRL_RST 0x01 -#define CTRL_LOOPBACK 0x02 -#define CTRL_CA 0x04 -#define CTRL_ACK 0x08 - -#define ETHER1_RAM (dev->base_addr + 0x800) - -/* HW address */ -#define IDPROM_ADDRESS (dev->base_addr + 0x09) - -struct ether1_priv { - struct net_device_stats stats; - unsigned int tx_link; - unsigned int tx_head; - volatile unsigned int tx_tail; - volatile unsigned int rx_head; - volatile unsigned int rx_tail; - unsigned char bus_type; - unsigned char resetting; - unsigned char initialising : 1; - unsigned char restart : 1; -}; - -#define I82586_NULL (-1) - -typedef struct { /* tdr */ - unsigned short tdr_status; - unsigned short tdr_command; - unsigned short tdr_link; - unsigned short tdr_result; -#define TDR_TIME (0x7ff) -#define TDR_SHORT (1 << 12) -#define TDR_OPEN (1 << 13) -#define TDR_XCVRPROB (1 << 14) -#define TDR_LNKOK (1 << 15) -} tdr_t; - -typedef struct { /* transmit */ - unsigned short tx_status; - unsigned short tx_command; - unsigned short tx_link; - unsigned short tx_tbdoffset; -} tx_t; - -typedef struct { /* tbd */ - unsigned short tbd_opts; -#define TBD_CNT (0x3fff) -#define TBD_EOL (1 << 15) - unsigned short tbd_link; - unsigned short tbd_bufl; - unsigned short tbd_bufh; -} tbd_t; - -typedef struct { /* rfd */ - unsigned short rfd_status; -#define RFD_NOEOF (1 << 6) -#define RFD_FRAMESHORT (1 << 7) -#define RFD_DMAOVRN (1 << 8) -#define RFD_NORESOURCES (1 << 9) -#define RFD_ALIGNERROR (1 << 10) -#define RFD_CRCERROR (1 << 11) -#define RFD_OK (1 << 13) -#define RFD_FDCONSUMED (1 << 14) -#define RFD_COMPLETE (1 << 15) - unsigned short rfd_command; -#define RFD_CMDSUSPEND (1 << 14) -#define RFD_CMDEL (1 << 15) - unsigned short rfd_link; - unsigned short rfd_rbdoffset; - unsigned char rfd_dest[6]; - unsigned char rfd_src[6]; - unsigned short rfd_len; -} rfd_t; - -typedef struct { /* rbd */ - unsigned short rbd_status; -#define RBD_ACNT (0x3fff) -#define RBD_ACNTVALID (1 << 14) -#define RBD_EOF (1 << 15) - unsigned short rbd_link; - unsigned short rbd_bufl; - unsigned short rbd_bufh; - unsigned short rbd_len; -} rbd_t; - -typedef struct { /* nop */ - unsigned short nop_status; - unsigned short nop_command; - unsigned short nop_link; -} nop_t; - -typedef struct { /* set multicast */ - unsigned short mc_status; - unsigned short mc_command; - unsigned short mc_link; - unsigned short mc_cnt; - unsigned char mc_addrs[1][6]; -} mc_t; - -typedef struct { /* set address */ - unsigned short sa_status; - unsigned short sa_command; - unsigned short sa_link; - unsigned char sa_addr[6]; -} sa_t; - -typedef struct { /* config command */ - unsigned short cfg_status; - unsigned short cfg_command; - unsigned short cfg_link; - unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */ - unsigned char cfg_fifolim; /* FIFO threshold */ - unsigned char cfg_byte8; -#define CFG8_SRDY (1 << 6) -#define CFG8_SAVEBADF (1 << 7) - unsigned char cfg_byte9; -#define CFG9_ADDRLEN(x) (x) -#define CFG9_ADDRLENBUF (1 << 3) -#define CFG9_PREAMB2 (0 << 4) -#define CFG9_PREAMB4 (1 << 4) -#define CFG9_PREAMB8 (2 << 4) -#define CFG9_PREAMB16 (3 << 4) -#define CFG9_ILOOPBACK (1 << 6) -#define CFG9_ELOOPBACK (1 << 7) - unsigned char cfg_byte10; -#define CFG10_LINPRI(x) (x) -#define CFG10_ACR(x) (x << 4) -#define CFG10_BOFMET (1 << 7) - unsigned char cfg_ifs; - unsigned char cfg_slotl; - unsigned char cfg_byte13; -#define CFG13_SLOTH(x) (x) -#define CFG13_RETRY(x) (x << 4) - unsigned char cfg_byte14; -#define CFG14_PROMISC (1 << 0) -#define CFG14_DISBRD (1 << 1) -#define CFG14_MANCH (1 << 2) -#define CFG14_TNCRS (1 << 3) -#define CFG14_NOCRC (1 << 4) -#define CFG14_CRC16 (1 << 5) -#define CFG14_BTSTF (1 << 6) -#define CFG14_FLGPAD (1 << 7) - unsigned char cfg_byte15; -#define CFG15_CSTF(x) (x) -#define CFG15_ICSS (1 << 3) -#define CFG15_CDTF(x) (x << 4) -#define CFG15_ICDS (1 << 7) - unsigned short cfg_minfrmlen; -} cfg_t; - -typedef struct { /* scb */ - unsigned short scb_status; /* status of 82586 */ -#define SCB_STRXMASK (7 << 4) /* Receive unit status */ -#define SCB_STRXIDLE (0 << 4) /* Idle */ -#define SCB_STRXSUSP (1 << 4) /* Suspended */ -#define SCB_STRXNRES (2 << 4) /* No resources */ -#define SCB_STRXRDY (4 << 4) /* Ready */ -#define SCB_STCUMASK (7 << 8) /* Command unit status */ -#define SCB_STCUIDLE (0 << 8) /* Idle */ -#define SCB_STCUSUSP (1 << 8) /* Suspended */ -#define SCB_STCUACTV (2 << 8) /* Active */ -#define SCB_STRNR (1 << 12) /* Receive unit not ready */ -#define SCB_STCNA (1 << 13) /* Command unit not ready */ -#define SCB_STFR (1 << 14) /* Frame received */ -#define SCB_STCX (1 << 15) /* Command completed */ - unsigned short scb_command; /* Next command */ -#define SCB_CMDRXSTART (1 << 4) /* Start (at rfa_offset) */ -#define SCB_CMDRXRESUME (2 << 4) /* Resume reception */ -#define SCB_CMDRXSUSPEND (3 << 4) /* Suspend reception */ -#define SCB_CMDRXABORT (4 << 4) /* Abort reception */ -#define SCB_CMDCUCSTART (1 << 8) /* Start (at cbl_offset) */ -#define SCB_CMDCUCRESUME (2 << 8) /* Resume execution */ -#define SCB_CMDCUCSUSPEND (3 << 8) /* Suspend execution */ -#define SCB_CMDCUCABORT (4 << 8) /* Abort execution */ -#define SCB_CMDACKRNR (1 << 12) /* Ack RU not ready */ -#define SCB_CMDACKCNA (1 << 13) /* Ack CU not ready */ -#define SCB_CMDACKFR (1 << 14) /* Ack Frame received */ -#define SCB_CMDACKCX (1 << 15) /* Ack Command complete */ - unsigned short scb_cbl_offset; /* Offset of first command unit */ - unsigned short scb_rfa_offset; /* Offset of first receive frame area */ - unsigned short scb_crc_errors; /* Properly aligned frame with CRC error*/ - unsigned short scb_aln_errors; /* Misaligned frames */ - unsigned short scb_rsc_errors; /* Frames lost due to no space */ - unsigned short scb_ovn_errors; /* Frames lost due to slow bus */ -} scb_t; - -typedef struct { /* iscp */ - unsigned short iscp_busy; /* set by CPU before CA */ - unsigned short iscp_offset; /* offset of SCB */ - unsigned short iscp_basel; /* base of SCB */ - unsigned short iscp_baseh; -} iscp_t; - - /* this address must be 0xfff6 */ -typedef struct { /* scp */ - unsigned short scp_sysbus; /* bus size */ -#define SCP_SY_16BBUS 0x00 -#define SCP_SY_8BBUS 0x01 - unsigned short scp_junk[2]; /* junk */ - unsigned short scp_iscpl; /* lower 16 bits of iscp */ - unsigned short scp_iscph; /* upper 16 bits of iscp */ -} scp_t; - -/* commands */ -#define CMD_NOP 0 -#define CMD_SETADDRESS 1 -#define CMD_CONFIG 2 -#define CMD_SETMULTICAST 3 -#define CMD_TX 4 -#define CMD_TDR 5 -#define CMD_DUMP 6 -#define CMD_DIAGNOSE 7 - -#define CMD_MASK 7 - -#define CMD_INTR (1 << 13) -#define CMD_SUSP (1 << 14) -#define CMD_EOL (1 << 15) - -#define STAT_COLLISIONS (15) -#define STAT_COLLEXCESSIVE (1 << 5) -#define STAT_COLLAFTERTX (1 << 6) -#define STAT_TXDEFERRED (1 << 7) -#define STAT_TXSLOWDMA (1 << 8) -#define STAT_TXLOSTCTS (1 << 9) -#define STAT_NOCARRIER (1 << 10) -#define STAT_FAIL (1 << 11) -#define STAT_ABORTED (1 << 12) -#define STAT_OK (1 << 13) -#define STAT_BUSY (1 << 14) -#define STAT_COMPLETE (1 << 15) -#endif -#endif - -/* - * Ether1 card definitions: - * - * FAST accesses: - * +0 Page register - * 16 pages - * +4 Control - * '1' = reset - * '2' = loopback - * '4' = CA - * '8' = int ack - * - * RAM at address + 0x2000 - * Pod. Prod id = 3 - * Words after ID block [base + 8 words] - * +0 pcb issue (0x0c and 0xf3 invalid) - * +1 - +6 eth hw address - */ diff -urN linux-2.5.69-bk13/drivers/acorn/net/ether3.c linux-2.5.69-bk14/drivers/acorn/net/ether3.c --- linux-2.5.69-bk13/drivers/acorn/net/ether3.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/net/ether3.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,947 +0,0 @@ -/* - * linux/drivers/acorn/net/ether3.c - * - * Copyright (C) 1995-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card - * for Acorn machines - * - * By Russell King, with some suggestions from borris@ant.co.uk - * - * Changelog: - * 1.04 RMK 29/02/1996 Won't pass packets that are from our ethernet - * address up to the higher levels - they're - * silently ignored. I/F can now be put into - * multicast mode. Receiver routine optimised. - * 1.05 RMK 30/02/1996 Now claims interrupt at open when part of - * the kernel rather than when a module. - * 1.06 RMK 02/03/1996 Various code cleanups - * 1.07 RMK 13/10/1996 Optimised interrupt routine and transmit - * routines. - * 1.08 RMK 14/10/1996 Fixed problem with too many packets, - * prevented the kernel message about dropped - * packets appearing too many times a second. - * Now does not disable all IRQs, only the IRQ - * used by this card. - * 1.09 RMK 10/11/1996 Only enables TX irq when buffer space is low, - * but we still service the TX queue if we get a - * RX interrupt. - * 1.10 RMK 15/07/1997 Fixed autoprobing of NQ8004. - * 1.11 RMK 16/11/1997 Fixed autoprobing of NQ8005A. - * 1.12 RMK 31/12/1997 Removed reference to dev_tint for Linux 2.1. - * RMK 27/06/1998 Changed asm/delay.h to linux/delay.h. - * 1.13 RMK 29/06/1998 Fixed problem with transmission of packets. - * Chip seems to have a bug in, whereby if the - * packet starts two bytes from the end of the - * buffer, it corrupts the receiver chain, and - * never updates the transmit status correctly. - * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. - * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy - * hardware. - * 1.16 RMK 10/02/2000 Updated for 2.3.43 - * 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static char version[] __initdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; - -#include "ether3.h" - -static unsigned int net_debug = NET_DEBUG; - -static void ether3_setmulticastlist(struct net_device *dev); -static int ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt); -static void ether3_tx(struct net_device *dev, struct dev_priv *priv); -static int ether3_open (struct net_device *dev); -static int ether3_sendpacket (struct sk_buff *skb, struct net_device *dev); -static irqreturn_t ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs); -static int ether3_close (struct net_device *dev); -static struct net_device_stats *ether3_getstats (struct net_device *dev); -static void ether3_setmulticastlist (struct net_device *dev); -static void ether3_timeout(struct net_device *dev); - -#define BUS_16 2 -#define BUS_8 1 -#define BUS_UNKNOWN 0 - -/* --------------------------------------------------------------------------- */ - -typedef enum { - buffer_write, - buffer_read -} buffer_rw_t; - -/* - * ether3 read/write. Slow things down a bit... - * The SEEQ8005 doesn't like us writing to its registers - * too quickly. - */ -static inline void ether3_outb(int v, const int r) -{ - outb(v, r); - udelay(1); -} - -static inline void ether3_outw(int v, const int r) -{ - outw(v, r); - udelay(1); -} -#define ether3_inb(r) ({ unsigned int __v = inb((r)); udelay(1); __v; }) -#define ether3_inw(r) ({ unsigned int __v = inw((r)); udelay(1); __v; }) - -static int -ether3_setbuffer(struct net_device *dev, buffer_rw_t read, int start) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int timeout = 1000; - - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); - ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); - - while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) { - if (!timeout--) { - printk("%s: setbuffer broken\n", dev->name); - priv->broken = 1; - return 1; - } - udelay(1); - } - - if (read == buffer_read) { - ether3_outw(start, REG_DMAADDR); - ether3_outw(priv->regs.command | CMD_FIFOREAD, REG_COMMAND); - } else { - ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); - ether3_outw(start, REG_DMAADDR); - } - return 0; -} - -/* - * write data to the buffer memory - */ -#define ether3_writebuffer(dev,data,length) \ - outsw(REG_BUFWIN, (data), (length) >> 1) - -#define ether3_writeword(dev,data) \ - outw((data), REG_BUFWIN) - -#define ether3_writelong(dev,data) { \ - unsigned long reg_bufwin = REG_BUFWIN; \ - outw((data), reg_bufwin); \ - outw((data) >> 16, reg_bufwin); \ -} - -/* - * read data from the buffer memory - */ -#define ether3_readbuffer(dev,data,length) \ - insw(REG_BUFWIN, (data), (length) >> 1) - -#define ether3_readword(dev) \ - inw(REG_BUFWIN) - -#define ether3_readlong(dev) \ - inw(REG_BUFWIN) | (inw(REG_BUFWIN) << 16) - -/* - * Switch LED off... - */ -static void -ether3_ledoff(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct dev_priv *priv = (struct dev_priv *)dev->priv; - ether3_outw(priv->regs.config2 |= CFG2_CTRLO, REG_CONFIG2); -} - -/* - * switch LED on... - */ -static inline void -ether3_ledon(struct net_device *dev, struct dev_priv *priv) -{ - del_timer(&priv->timer); - priv->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */ - priv->timer.data = (unsigned long)dev; - priv->timer.function = ether3_ledoff; - add_timer(&priv->timer); - if (priv->regs.config2 & CFG2_CTRLO) - ether3_outw(priv->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2); -} - -/* - * Read the ethernet address string from the on board rom. - * This is an ascii string!!! - */ -static int __init -ether3_addr(char *addr, struct expansion_card *ec) -{ - struct in_chunk_dir cd; - char *s; - - if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { - int i; - for (i = 0; i<6; i++) { - addr[i] = simple_strtoul(s + 1, &s, 0x10); - if (*s != (i==5?')' : ':' )) - break; - } - if (i == 6) - return 0; - } - /* I wonder if we should even let the user continue in this case - * - no, it would be better to disable the device - */ - printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n"); - return -ENODEV; -} - -/* --------------------------------------------------------------------------- */ - -static int __init -ether3_ramtest(struct net_device *dev, unsigned char byte) -{ - unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL); - int i,ret = 0; - int max_errors = 4; - int bad = -1; - - if (!buffer) - return 1; - - memset(buffer, byte, RX_END); - ether3_setbuffer(dev, buffer_write, 0); - ether3_writebuffer(dev, buffer, TX_END); - ether3_setbuffer(dev, buffer_write, RX_START); - ether3_writebuffer(dev, buffer + RX_START, RX_LEN); - memset(buffer, byte ^ 0xff, RX_END); - ether3_setbuffer(dev, buffer_read, 0); - ether3_readbuffer(dev, buffer, TX_END); - ether3_setbuffer(dev, buffer_read, RX_START); - ether3_readbuffer(dev, buffer + RX_START, RX_LEN); - - for (i = 0; i < RX_END; i++) { - if (buffer[i] != byte) { - if (max_errors > 0 && bad != buffer[i]) { - printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X", - dev->name, buffer[i], byte, i); - ret = 2; - max_errors--; - bad = i; - } - } else { - if (bad != -1) { - if (bad != i - 1) - printk(" - 0x%04X\n", i - 1); - printk("\n"); - bad = -1; - } - } - } - if (bad != -1) - printk(" - 0xffff\n"); - kfree(buffer); - - return ret; -} - -/* ------------------------------------------------------------------------------- */ - -static int __init -ether3_init_2(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int i; - - priv->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8; - priv->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC; - priv->regs.command = 0; - - /* - * Set up our hardware address - */ - ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); - for (i = 0; i < 6; i++) - ether3_outb(dev->dev_addr[i], REG_BUFWIN); - - if (dev->flags & IFF_PROMISC) - priv->regs.config1 |= CFG1_RECVPROMISC; - else if (dev->flags & IFF_MULTICAST) - priv->regs.config1 |= CFG1_RECVSPECBRMULTI; - else - priv->regs.config1 |= CFG1_RECVSPECBROAD; - - /* - * There is a problem with the NQ8005 in that it occasionally loses the - * last two bytes. To get round this problem, we receive the CRC as - * well. That way, if we do lose the last two, then it doesn't matter. - */ - ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); - ether3_outw((TX_END>>8) - 1, REG_BUFWIN); - ether3_outw(priv->rx_head, REG_RECVPTR); - ether3_outw(0, REG_TRANSMITPTR); - ether3_outw(priv->rx_head >> 8, REG_RECVEND); - ether3_outw(priv->regs.config2, REG_CONFIG2); - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); - ether3_outw(priv->regs.command, REG_COMMAND); - - i = ether3_ramtest(dev, 0x5A); - if(i) - return i; - i = ether3_ramtest(dev, 0x1E); - if(i) - return i; - - ether3_setbuffer(dev, buffer_write, 0); - ether3_writelong(dev, 0); - return 0; -} - -static void -ether3_init_for_open(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int i; - - memset(&priv->stats, 0, sizeof(struct net_device_stats)); - - /* Reset the chip */ - ether3_outw(CFG2_RESET, REG_CONFIG2); - udelay(4); - - priv->regs.command = 0; - ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); - while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); - - ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); - for (i = 0; i < 6; i++) - ether3_outb(dev->dev_addr[i], REG_BUFWIN); - - priv->tx_head = 0; - priv->tx_tail = 0; - priv->regs.config2 |= CFG2_CTRLO; - priv->rx_head = RX_START; - - ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); - ether3_outw((TX_END>>8) - 1, REG_BUFWIN); - ether3_outw(priv->rx_head, REG_RECVPTR); - ether3_outw(priv->rx_head >> 8, REG_RECVEND); - ether3_outw(0, REG_TRANSMITPTR); - ether3_outw(priv->regs.config2, REG_CONFIG2); - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); - - ether3_setbuffer(dev, buffer_write, 0); - ether3_writelong(dev, 0); - - priv->regs.command = CMD_ENINTRX | CMD_ENINTTX; - ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); -} - -static inline int -ether3_probe_bus_8(struct net_device *dev, int val) -{ - int write_low, write_high, read_low, read_high; - - write_low = val & 255; - write_high = val >> 8; - - printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low); - - ether3_outb(write_low, REG_RECVPTR); - ether3_outb(write_high, REG_RECVPTR + 1); - - read_low = ether3_inb(REG_RECVPTR); - read_high = ether3_inb(REG_RECVPTR + 1); - - printk(", read8 [%02X:%02X]\n", read_high, read_low); - - return read_low == write_low && read_high == write_high; -} - -static inline int -ether3_probe_bus_16(struct net_device *dev, int val) -{ - int read_val; - - ether3_outw(val, REG_RECVPTR); - read_val = ether3_inw(REG_RECVPTR); - - printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val); - - return read_val == val; -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ -static int -ether3_open(struct net_device *dev) -{ - if (!is_valid_ether_addr(dev->dev_addr)) { - printk(KERN_WARNING "%s: invalid ethernet MAC address\n", - dev->name); - return -EINVAL; - } - - if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) - return -EAGAIN; - - ether3_init_for_open(dev); - - netif_start_queue(dev); - - return 0; -} - -/* - * The inverse routine to ether3_open(). - */ -static int -ether3_close(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - - netif_stop_queue(dev); - - disable_irq(dev->irq); - - ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); - priv->regs.command = 0; - while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); - ether3_outb(0x80, REG_CONFIG2 + 1); - ether3_outw(0, REG_COMMAND); - - free_irq(dev->irq, dev); - - return 0; -} - -/* - * Get the current statistics. This may be called with the card open or - * closed. - */ -static struct net_device_stats *ether3_getstats(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - return &priv->stats; -} - -/* - * Set or clear promiscuous/multicast mode filter for this adaptor. - * - * We don't attempt any packet filtering. The card may have a SEEQ 8004 - * in which does not have the other ethernet address registers present... - */ -static void ether3_setmulticastlist(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - - priv->regs.config1 &= ~CFG1_RECVPROMISC; - - if (dev->flags & IFF_PROMISC) { - /* promiscuous mode */ - priv->regs.config1 |= CFG1_RECVPROMISC; - } else if (dev->flags & IFF_ALLMULTI) { - priv->regs.config1 |= CFG1_RECVSPECBRMULTI; - } else - priv->regs.config1 |= CFG1_RECVSPECBROAD; - - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); -} - -static void -ether3_timeout(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - - del_timer(&priv->timer); - - local_irq_save(flags); - printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name); - printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name, - ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); - printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name, - ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR)); - printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name, - priv->tx_head, priv->tx_tail); - ether3_setbuffer(dev, buffer_read, priv->tx_tail); - printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); - local_irq_restore(flags); - - priv->regs.config2 |= CFG2_CTRLO; - priv->stats.tx_errors += 1; - ether3_outw(priv->regs.config2, REG_CONFIG2); - priv->tx_head = priv->tx_tail = 0; - - netif_wake_queue(dev); -} - -/* - * Transmit a packet - */ -static int -ether3_sendpacket(struct sk_buff *skb, struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned int ptr, next_ptr; - - length = (length + 1) & ~1; - - if (priv->broken) { - dev_kfree_skb(skb); - priv->stats.tx_dropped ++; - netif_start_queue(dev); - return 0; - } - - next_ptr = (priv->tx_head + 1) & 15; - - local_irq_save(flags); - - if (priv->tx_tail == next_ptr) { - local_irq_restore(flags); - return 1; /* unable to queue */ - } - - dev->trans_start = jiffies; - ptr = 0x600 * priv->tx_head; - priv->tx_head = next_ptr; - next_ptr *= 0x600; - -#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS) - - ether3_setbuffer(dev, buffer_write, next_ptr); - ether3_writelong(dev, 0); - ether3_setbuffer(dev, buffer_write, ptr); - ether3_writelong(dev, 0); - ether3_writebuffer(dev, skb->data, length); - ether3_writeword(dev, htons(next_ptr)); - ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16); - ether3_setbuffer(dev, buffer_write, ptr); - ether3_writeword(dev, htons((ptr + length + 4))); - ether3_writeword(dev, TXHDR_FLAGS >> 16); - ether3_ledon(dev, priv); - - if (!(ether3_inw(REG_STATUS) & STAT_TXON)) { - ether3_outw(ptr, REG_TRANSMITPTR); - ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND); - } - - next_ptr = (priv->tx_head + 1) & 15; - local_irq_restore(flags); - - dev_kfree_skb(skb); - - if (priv->tx_tail == next_ptr) - netif_stop_queue(dev); - - return 0; -} - -static irqreturn_t -ether3_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct dev_priv *priv; - unsigned int status, handled = IRQ_NONE; - -#if NET_DEBUG > 1 - if(net_debug & DEBUG_INT) - printk("eth3irq: %d ", irq); -#endif - - priv = (struct dev_priv *)dev->priv; - - status = ether3_inw(REG_STATUS); - - if (status & STAT_INTRX) { - ether3_outw(CMD_ACKINTRX | priv->regs.command, REG_COMMAND); - ether3_rx(dev, priv, 12); - handled = IRQ_HANDLED; - } - - if (status & STAT_INTTX) { - ether3_outw(CMD_ACKINTTX | priv->regs.command, REG_COMMAND); - ether3_tx(dev, priv); - handled = IRQ_HANDLED; - } - -#if NET_DEBUG > 1 - if(net_debug & DEBUG_INT) - printk("done\n"); -#endif - return handled; -} - -/* - * If we have a good packet(s), get it/them out of the buffers. - */ -static int -ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt) -{ - unsigned int next_ptr = priv->rx_head, received = 0; - ether3_ledon(dev, priv); - - do { - unsigned int this_ptr, status; - unsigned char addrs[16]; - - /* - * read the first 16 bytes from the buffer. - * This contains the status bytes etc and ethernet addresses, - * and we also check the source ethernet address to see if - * it originated from us. - */ - { - unsigned int temp_ptr; - ether3_setbuffer(dev, buffer_read, next_ptr); - temp_ptr = ether3_readword(dev); - status = ether3_readword(dev); - if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) != - (RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr) - break; - - this_ptr = next_ptr + 4; - next_ptr = ntohs(temp_ptr); - } - ether3_setbuffer(dev, buffer_read, this_ptr); - ether3_readbuffer(dev, addrs+2, 12); - -if (next_ptr < RX_START || next_ptr >= RX_END) { - int i; - printk("%s: bad next pointer @%04X: ", dev->name, priv->rx_head); - printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8); - for (i = 2; i < 14; i++) - printk("%02X ", addrs[i]); - printk("\n"); - next_ptr = priv->rx_head; - break; -} - /* - * ignore our own packets... - */ - if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) && - !(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) { - maxcnt ++; /* compensate for loopedback packet */ - ether3_outw(next_ptr >> 8, REG_RECVEND); - } else - if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) { - unsigned int length = next_ptr - this_ptr; - struct sk_buff *skb; - - if (next_ptr <= this_ptr) - length += RX_END - RX_START; - - skb = dev_alloc_skb(length + 2); - if (skb) { - unsigned char *buf; - - skb->dev = dev; - skb_reserve(skb, 2); - buf = skb_put(skb, length); - ether3_readbuffer(dev, buf + 12, length - 12); - ether3_outw(next_ptr >> 8, REG_RECVEND); - *(unsigned short *)(buf + 0) = *(unsigned short *)(addrs + 2); - *(unsigned long *)(buf + 2) = *(unsigned long *)(addrs + 4); - *(unsigned long *)(buf + 6) = *(unsigned long *)(addrs + 8); - *(unsigned short *)(buf + 10) = *(unsigned short *)(addrs + 12); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - received ++; - } else - goto dropping; - } else { - struct net_device_stats *stats = &priv->stats; - ether3_outw(next_ptr >> 8, REG_RECVEND); - if (status & RXSTAT_OVERSIZE) stats->rx_over_errors ++; - if (status & RXSTAT_CRCERROR) stats->rx_crc_errors ++; - if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++; - if (status & RXSTAT_SHORTPACKET) stats->rx_length_errors ++; - stats->rx_errors++; - } - } - while (-- maxcnt); - -done: - priv->stats.rx_packets += received; - priv->rx_head = next_ptr; - /* - * If rx went off line, then that means that the buffer may be full. We - * have dropped at least one packet. - */ - if (!(ether3_inw(REG_STATUS) & STAT_RXON)) { - priv->stats.rx_dropped ++; - ether3_outw(next_ptr, REG_RECVPTR); - ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); - } - - return maxcnt; - -dropping:{ - static unsigned long last_warned; - - ether3_outw(next_ptr >> 8, REG_RECVEND); - /* - * Don't print this message too many times... - */ - if (time_after(jiffies, last_warned + 10 * HZ)) { - last_warned = jiffies; - printk("%s: memory squeeze, dropping packet.\n", dev->name); - } - priv->stats.rx_dropped ++; - goto done; - } -} - -/* - * Update stats for the transmitted packet(s) - */ -static void -ether3_tx(struct net_device *dev, struct dev_priv *priv) -{ - unsigned int tx_tail = priv->tx_tail; - int max_work = 14; - - do { - unsigned long status; - - /* - * Read the packet header - */ - ether3_setbuffer(dev, buffer_read, tx_tail * 0x600); - status = ether3_readlong(dev); - - /* - * Check to see if this packet has been transmitted - */ - if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) != - (TXSTAT_DONE | TXHDR_TRANSMIT)) - break; - - /* - * Update errors - */ - if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS))) - priv->stats.tx_packets++; - else { - priv->stats.tx_errors ++; - if (status & TXSTAT_16COLLISIONS) priv->stats.collisions += 16; - if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++; - } - - tx_tail = (tx_tail + 1) & 15; - } while (--max_work); - - if (priv->tx_tail != tx_tail) { - priv->tx_tail = tx_tail; - netif_wake_queue(dev); - } -} - -static void __init ether3_banner(void) -{ - static unsigned version_printed = 0; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -static const char * __init -ether3_get_dev(struct net_device *dev, struct expansion_card *ec) -{ - const char *name = "ether3"; - - dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); - dev->irq = ec->irq; - - if (ec->cid.manufacturer == MANU_ANT && - ec->cid.product == PROD_ANT_ETHERB) { - dev->base_addr += 0x200; - name = "etherb"; - } - - ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); - ec->irqmask = 0xf0; - - ether3_addr(dev->dev_addr, ec); - - return name; -} - -static int __devinit -ether3_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct net_device *dev; - struct dev_priv *priv; - const char *name; - int i, bus_type, ret; - - ether3_banner(); - - dev = init_etherdev(NULL, sizeof(struct dev_priv)); - if (!dev) { - ret = -ENOMEM; - goto out; - } - - SET_MODULE_OWNER(dev); - - name = ether3_get_dev(dev, ec); - if (!name) { - ret = -ENODEV; - goto free; - } - - /* - * this will not fail - the nature of the bus ensures this - */ - if (!request_region(dev->base_addr, 128, dev->name)) { - ret = -EBUSY; - goto free; - } - - priv = (struct dev_priv *) dev->priv; - init_timer(&priv->timer); - - /* Reset card... - */ - ether3_outb(0x80, REG_CONFIG2 + 1); - bus_type = BUS_UNKNOWN; - udelay(4); - - /* Test using Receive Pointer (16-bit register) to find out - * how the ether3 is connected to the bus... - */ - if (ether3_probe_bus_8(dev, 0x100) && - ether3_probe_bus_8(dev, 0x201)) - bus_type = BUS_8; - - if (bus_type == BUS_UNKNOWN && - ether3_probe_bus_16(dev, 0x101) && - ether3_probe_bus_16(dev, 0x201)) - bus_type = BUS_16; - - switch (bus_type) { - case BUS_UNKNOWN: - printk(KERN_ERR "%s: unable to identify bus width\n", dev->name); - ret = -ENODEV; - goto failed; - - case BUS_8: - printk(KERN_ERR "%s: %s found, but is an unsupported " - "8-bit card\n", dev->name, name); - ret = -ENODEV; - goto failed; - - default: - break; - } - - printk("%s: %s in slot %d, ", dev->name, name, ec->slot_no); - for (i = 0; i < 6; i++) - printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - - if (ether3_init_2(dev)) { - ret = -ENODEV; - goto failed; - } - - dev->open = ether3_open; - dev->stop = ether3_close; - dev->hard_start_xmit = ether3_sendpacket; - dev->get_stats = ether3_getstats; - dev->set_multicast_list = ether3_setmulticastlist; - dev->tx_timeout = ether3_timeout; - dev->watchdog_timeo = 5 * HZ / 100; - - ecard_set_drvdata(ec, dev); - return 0; - -failed: - release_region(dev->base_addr, 128); -free: - unregister_netdev(dev); - kfree(dev); -out: - return ret; -} - -static void __devexit ether3_remove(struct expansion_card *ec) -{ - struct net_device *dev = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - - unregister_netdev(dev); - release_region(dev->base_addr, 128); - kfree(dev); -} - -static const struct ecard_id ether3_ids[] = { - { MANU_ANT2, PROD_ANT_ETHER3 }, - { MANU_ANT, PROD_ANT_ETHER3 }, - { MANU_ANT, PROD_ANT_ETHERB }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver ether3_driver = { - .probe = ether3_probe, - .remove = __devexit_p(ether3_remove), - .id_table = ether3_ids, - .drv = { - .name = "ether3", - }, -}; - -static int __init ether3_init(void) -{ - return ecard_register_driver(ðer3_driver); -} - -static void __exit ether3_exit(void) -{ - ecard_remove_driver(ðer3_driver); -} - -module_init(ether3_init); -module_exit(ether3_exit); - -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/net/ether3.h linux-2.5.69-bk14/drivers/acorn/net/ether3.h --- linux-2.5.69-bk13/drivers/acorn/net/ether3.h 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/net/ether3.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,168 +0,0 @@ -/* - * linux/drivers/acorn/net/ether3.h - * - * Copyright (C) 1995-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * network driver for Acorn/ANT Ether3 cards - */ - -#ifndef _LINUX_ether3_H -#define _LINUX_ether3_H - -/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ -#define DEBUG_TX 2 -#define DEBUG_RX 4 -#define DEBUG_INT 8 -#define DEBUG_IC 16 -#ifndef NET_DEBUG -#define NET_DEBUG 0 -#endif - -/* Command register definitions & bits */ -#define REG_COMMAND (dev->base_addr + 0x00) -#define CMD_ENINTDMA 0x0001 -#define CMD_ENINTRX 0x0002 -#define CMD_ENINTTX 0x0004 -#define CMD_ENINTBUFWIN 0x0008 -#define CMD_ACKINTDMA 0x0010 -#define CMD_ACKINTRX 0x0020 -#define CMD_ACKINTTX 0x0040 -#define CMD_ACKINTBUFWIN 0x0080 -#define CMD_DMAON 0x0100 -#define CMD_RXON 0x0200 -#define CMD_TXON 0x0400 -#define CMD_DMAOFF 0x0800 -#define CMD_RXOFF 0x1000 -#define CMD_TXOFF 0x2000 -#define CMD_FIFOREAD 0x4000 -#define CMD_FIFOWRITE 0x8000 - -/* status register */ -#define REG_STATUS (dev->base_addr + 0x00) -#define STAT_ENINTSTAT 0x0001 -#define STAT_ENINTRX 0x0002 -#define STAT_ENINTTX 0x0004 -#define STAT_ENINTBUFWIN 0x0008 -#define STAT_INTDMA 0x0010 -#define STAT_INTRX 0x0020 -#define STAT_INTTX 0x0040 -#define STAT_INTBUFWIN 0x0080 -#define STAT_DMAON 0x0100 -#define STAT_RXON 0x0200 -#define STAT_TXON 0x0400 -#define STAT_FIFOFULL 0x2000 -#define STAT_FIFOEMPTY 0x4000 -#define STAT_FIFODIR 0x8000 - -/* configuration register 1 */ -#define REG_CONFIG1 (dev->base_addr + 0x10) -#define CFG1_BUFSELSTAT0 0x0000 -#define CFG1_BUFSELSTAT1 0x0001 -#define CFG1_BUFSELSTAT2 0x0002 -#define CFG1_BUFSELSTAT3 0x0003 -#define CFG1_BUFSELSTAT4 0x0004 -#define CFG1_BUFSELSTAT5 0x0005 -#define CFG1_ADDRPROM 0x0006 -#define CFG1_TRANSEND 0x0007 -#define CFG1_LOCBUFMEM 0x0008 -#define CFG1_INTVECTOR 0x0009 -#define CFG1_RECVSPECONLY 0x0000 -#define CFG1_RECVSPECBROAD 0x4000 -#define CFG1_RECVSPECBRMULTI 0x8000 -#define CFG1_RECVPROMISC 0xC000 - -/* The following aren't in 8004 */ -#define CFG1_DMABURSTCONT 0x0000 -#define CFG1_DMABURST800NS 0x0010 -#define CFG1_DMABURST1600NS 0x0020 -#define CFG1_DMABURST3200NS 0x0030 -#define CFG1_DMABURST1 0x0000 -#define CFG1_DMABURST4 0x0040 -#define CFG1_DMABURST8 0x0080 -#define CFG1_DMABURST16 0x00C0 -#define CFG1_RECVCOMPSTAT0 0x0100 -#define CFG1_RECVCOMPSTAT1 0x0200 -#define CFG1_RECVCOMPSTAT2 0x0400 -#define CFG1_RECVCOMPSTAT3 0x0800 -#define CFG1_RECVCOMPSTAT4 0x1000 -#define CFG1_RECVCOMPSTAT5 0x2000 - -/* configuration register 2 */ -#define REG_CONFIG2 (dev->base_addr + 0x20) -#define CFG2_BYTESWAP 0x0001 -#define CFG2_ERRENCRC 0x0008 -#define CFG2_ERRENDRIBBLE 0x0010 -#define CFG2_ERRSHORTFRAME 0x0020 -#define CFG2_SLOTSELECT 0x0040 -#define CFG2_PREAMSELECT 0x0080 -#define CFG2_ADDRLENGTH 0x0100 -#define CFG2_RECVCRC 0x0200 -#define CFG2_XMITNOCRC 0x0400 -#define CFG2_LOOPBACK 0x0800 -#define CFG2_CTRLO 0x1000 -#define CFG2_RESET 0x8000 - -#define REG_RECVEND (dev->base_addr + 0x30) - -#define REG_BUFWIN (dev->base_addr + 0x40) - -#define REG_RECVPTR (dev->base_addr + 0x50) - -#define REG_TRANSMITPTR (dev->base_addr + 0x60) - -#define REG_DMAADDR (dev->base_addr + 0x70) - -/* - * Cards transmit/receive headers - */ -#define TX_NEXT (0xffff) -#define TXHDR_ENBABBLEINT (1 << 16) -#define TXHDR_ENCOLLISIONINT (1 << 17) -#define TXHDR_EN16COLLISION (1 << 18) -#define TXHDR_ENSUCCESS (1 << 19) -#define TXHDR_DATAFOLLOWS (1 << 21) -#define TXHDR_CHAINCONTINUE (1 << 22) -#define TXHDR_TRANSMIT (1 << 23) -#define TXSTAT_BABBLED (1 << 24) -#define TXSTAT_COLLISION (1 << 25) -#define TXSTAT_16COLLISIONS (1 << 26) -#define TXSTAT_DONE (1 << 31) - -#define RX_NEXT (0xffff) -#define RXHDR_CHAINCONTINUE (1 << 6) -#define RXHDR_RECEIVE (1 << 7) -#define RXSTAT_OVERSIZE (1 << 8) -#define RXSTAT_CRCERROR (1 << 9) -#define RXSTAT_DRIBBLEERROR (1 << 10) -#define RXSTAT_SHORTPACKET (1 << 11) -#define RXSTAT_DONE (1 << 15) - - -#define TX_START 0x0000 -#define TX_END 0x6000 -#define RX_START 0x6000 -#define RX_LEN 0xA000 -#define RX_END 0x10000 -/* must be a power of 2 and greater than MAX_TX_BUFFERED */ -#define MAX_TXED 16 -#define MAX_TX_BUFFERED 10 - -struct dev_priv { - struct { - unsigned int command; - unsigned int config1; - unsigned int config2; - } regs; - unsigned char tx_head; /* buffer nr to insert next packet */ - unsigned char tx_tail; /* buffer nr of transmitting packet */ - unsigned int rx_head; /* address to fetch next packet from */ - struct net_device_stats stats; - struct timer_list timer; - int broken; /* 0 = ok, 1 = something went wrong */ -}; - -#endif diff -urN linux-2.5.69-bk13/drivers/acorn/net/etherh.c linux-2.5.69-bk14/drivers/acorn/net/etherh.c --- linux-2.5.69-bk13/drivers/acorn/net/etherh.c 2003-05-04 16:53:40.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/net/etherh.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,763 +0,0 @@ -/* - * linux/drivers/acorn/net/etherh.c - * - * Copyright (C) 2000-2002 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * NS8390 I-cubed EtherH and ANT EtherM specific driver - * Thanks to I-Cubed for information on their cards. - * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton - * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan) - * EtherM integration re-engineered by Russell King. - * - * Changelog: - * 08-12-1996 RMK 1.00 Created - * RMK 1.03 Added support for EtherLan500 cards - * 23-11-1997 RMK 1.04 Added media autodetection - * 16-04-1998 RMK 1.05 Improved media autodetection - * 10-02-2000 RMK 1.06 Updated for 2.3.43 - * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8 - * 12-10-1999 CK/TEW EtherM driver first release - * 21-12-2000 TTC EtherH/EtherM integration - * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver. - * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../net/8390.h" - -#define NET_DEBUG 0 -#define DEBUG_INIT 2 - -static unsigned int net_debug = NET_DEBUG; - -struct etherh_priv { - struct ei_device eidev; - unsigned int id; - unsigned int ctrl_port; - unsigned int ctrl; -}; - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("EtherH/EtherM driver"); -MODULE_LICENSE("GPL"); - -static char version[] __initdata = - "EtherH/EtherM Driver (c) 2002 Russell King v1.09\n"; - -#define ETHERH500_DATAPORT 0x200 /* MEMC */ -#define ETHERH500_NS8390 0x000 /* MEMC */ -#define ETHERH500_CTRLPORT 0x200 /* IOC */ - -#define ETHERH600_DATAPORT 16 /* MEMC */ -#define ETHERH600_NS8390 0x200 /* MEMC */ -#define ETHERH600_CTRLPORT 0x080 /* MEMC */ - -#define ETHERH_CP_IE 1 -#define ETHERH_CP_IF 2 -#define ETHERH_CP_HEARTBEAT 2 - -#define ETHERH_TX_START_PAGE 1 -#define ETHERH_STOP_PAGE 127 - -/* - * These came from CK/TEW - */ -#define ETHERM_DATAPORT 0x080 /* MEMC */ -#define ETHERM_NS8390 0x200 /* MEMC */ -#define ETHERM_CTRLPORT 0x08f /* MEMC */ - -#define ETHERM_TX_START_PAGE 64 -#define ETHERM_STOP_PAGE 127 - -/* ------------------------------------------------------------------------ */ - -static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned int mask) -{ - eh->ctrl |= mask; - outb(eh->ctrl, eh->ctrl_port); -} - -static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned int mask) -{ - eh->ctrl &= ~mask; - outb(eh->ctrl, eh->ctrl_port); -} - -static inline unsigned int etherh_get_stat(struct etherh_priv *eh) -{ - return inb(eh->ctrl_port); -} - - - - -static void etherh_irq_enable(ecard_t *ec, int irqnr) -{ - struct etherh_priv *eh = ec->irq_data; - - etherh_set_ctrl(eh, ETHERH_CP_IE); -} - -static void etherh_irq_disable(ecard_t *ec, int irqnr) -{ - struct etherh_priv *eh = ec->irq_data; - - etherh_clr_ctrl(eh, ETHERH_CP_IE); -} - -static expansioncard_ops_t etherh_ops = { - .irqenable = etherh_irq_enable, - .irqdisable = etherh_irq_disable, -}; - - - - -static void -etherh_setif(struct net_device *dev) -{ - struct etherh_priv *eh = (struct etherh_priv *)dev->priv; - struct ei_device *ei_local = &eh->eidev; - unsigned long addr, flags; - - local_irq_save(flags); - - /* set the interface type */ - switch (eh->id) { - case PROD_I3_ETHERLAN600: - case PROD_I3_ETHERLAN600A: - addr = dev->base_addr + EN0_RCNTHI; - - switch (dev->if_port) { - case IF_PORT_10BASE2: - outb((inb(addr) & 0xf8) | 1, addr); - break; - case IF_PORT_10BASET: - outb((inb(addr) & 0xf8), addr); - break; - } - break; - - case PROD_I3_ETHERLAN500: - switch (dev->if_port) { - case IF_PORT_10BASE2: - etherh_clr_ctrl(eh, ETHERH_CP_IF); - break; - - case IF_PORT_10BASET: - etherh_set_ctrl(eh, ETHERH_CP_IF); - break; - } - break; - - default: - break; - } - - local_irq_restore(flags); -} - -static int -etherh_getifstat(struct net_device *dev) -{ - struct etherh_priv *eh = (struct etherh_priv *)dev->priv; - struct ei_device *ei_local = &eh->eidev; - int stat = 0; - - switch (eh->id) { - case PROD_I3_ETHERLAN600: - case PROD_I3_ETHERLAN600A: - switch (dev->if_port) { - case IF_PORT_10BASE2: - stat = 1; - break; - case IF_PORT_10BASET: - stat = inb(dev->base_addr+EN0_RCNTHI) & 4; - break; - } - break; - - case PROD_I3_ETHERLAN500: - switch (dev->if_port) { - case IF_PORT_10BASE2: - stat = 1; - break; - case IF_PORT_10BASET: - stat = etherh_get_stat(eh) & ETHERH_CP_HEARTBEAT; - break; - } - break; - - default: - stat = 0; - break; - } - - return stat != 0; -} - -/* - * Configure the interface. Note that we ignore the other - * parts of ifmap, since its mostly meaningless for this driver. - */ -static int etherh_set_config(struct net_device *dev, struct ifmap *map) -{ - switch (map->port) { - case IF_PORT_10BASE2: - case IF_PORT_10BASET: - /* - * If the user explicitly sets the interface - * media type, turn off automedia detection. - */ - dev->flags &= ~IFF_AUTOMEDIA; - dev->if_port = map->port; - break; - - default: - return -EINVAL; - } - - etherh_setif(dev); - - return 0; -} - -/* - * Reset the 8390 (hard reset). Note that we can't actually do this. - */ -static void -etherh_reset(struct net_device *dev) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, dev->base_addr); - - /* - * See if we need to change the interface type. - * Note that we use 'interface_num' as a flag - * to indicate that we need to change the media. - */ - if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) { - ei_local->interface_num = 0; - - if (dev->if_port == IF_PORT_10BASET) - dev->if_port = IF_PORT_10BASE2; - else - dev->if_port = IF_PORT_10BASET; - - etherh_setif(dev); - } -} - -/* - * Write a block of data out to the 8390 - */ -static void -etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - unsigned int addr, dma_addr; - unsigned long dma_start; - - if (ei_local->dmaing) { - printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " - " DMAstat %d irqlock %d\n", dev->name, - ei_local->dmaing, ei_local->irqlock); - return; - } - - /* - * Make sure we have a round number of bytes if we're in word mode. - */ - if (count & 1 && ei_local->word16) - count++; - - ei_local->dmaing = 1; - - addr = dev->base_addr; - dma_addr = dev->mem_start; - - count = (count + 1) & ~1; - outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); - - outb (0x42, addr + EN0_RCNTLO); - outb (0x00, addr + EN0_RCNTHI); - outb (0x42, addr + EN0_RSARLO); - outb (0x00, addr + EN0_RSARHI); - outb (E8390_RREAD | E8390_START, addr + E8390_CMD); - - udelay (1); - - outb (ENISR_RDC, addr + EN0_ISR); - outb (count, addr + EN0_RCNTLO); - outb (count >> 8, addr + EN0_RCNTHI); - outb (0, addr + EN0_RSARLO); - outb (start_page, addr + EN0_RSARHI); - outb (E8390_RWRITE | E8390_START, addr + E8390_CMD); - - if (ei_local->word16) - outsw (dma_addr, buf, count >> 1); - else - outsb (dma_addr, buf, count); - - dma_start = jiffies; - - while ((inb (addr + EN0_ISR) & ENISR_RDC) == 0) - if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ - printk(KERN_ERR "%s: timeout waiting for TX RDC\n", - dev->name); - etherh_reset (dev); - NS8390_init (dev, 1); - break; - } - - outb (ENISR_RDC, addr + EN0_ISR); - ei_local->dmaing = 0; -} - -/* - * Read a block of data from the 8390 - */ -static void -etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - unsigned int addr, dma_addr; - unsigned char *buf; - - if (ei_local->dmaing) { - printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " - " DMAstat %d irqlock %d\n", dev->name, - ei_local->dmaing, ei_local->irqlock); - return; - } - - ei_local->dmaing = 1; - - addr = dev->base_addr; - dma_addr = dev->mem_start; - - buf = skb->data; - outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); - outb (count, addr + EN0_RCNTLO); - outb (count >> 8, addr + EN0_RCNTHI); - outb (ring_offset, addr + EN0_RSARLO); - outb (ring_offset >> 8, addr + EN0_RSARHI); - outb (E8390_RREAD | E8390_START, addr + E8390_CMD); - - if (ei_local->word16) { - insw (dma_addr, buf, count >> 1); - if (count & 1) - buf[count - 1] = inb (dma_addr); - } else - insb (dma_addr, buf, count); - - outb (ENISR_RDC, addr + EN0_ISR); - ei_local->dmaing = 0; -} - -/* - * Read a header from the 8390 - */ -static void -etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - unsigned int addr, dma_addr; - - if (ei_local->dmaing) { - printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: " - " DMAstat %d irqlock %d\n", dev->name, - ei_local->dmaing, ei_local->irqlock); - return; - } - - ei_local->dmaing = 1; - - addr = dev->base_addr; - dma_addr = dev->mem_start; - - outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); - outb (sizeof (*hdr), addr + EN0_RCNTLO); - outb (0, addr + EN0_RCNTHI); - outb (0, addr + EN0_RSARLO); - outb (ring_page, addr + EN0_RSARHI); - outb (E8390_RREAD | E8390_START, addr + E8390_CMD); - - if (ei_local->word16) - insw (dma_addr, hdr, sizeof (*hdr) >> 1); - else - insb (dma_addr, hdr, sizeof (*hdr)); - - outb (ENISR_RDC, addr + EN0_ISR); - ei_local->dmaing = 0; -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ -static int -etherh_open(struct net_device *dev) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - - if (!is_valid_ether_addr(dev->dev_addr)) { - printk(KERN_WARNING "%s: invalid ethernet MAC address\n", - dev->name); - return -EINVAL; - } - - if (request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)) - return -EAGAIN; - - /* - * Make sure that we aren't going to change the - * media type on the next reset - we are about to - * do automedia manually now. - */ - ei_local->interface_num = 0; - - /* - * If we are doing automedia detection, do it now. - * This is more reliable than the 8390's detection. - */ - if (dev->flags & IFF_AUTOMEDIA) { - dev->if_port = IF_PORT_10BASET; - etherh_setif(dev); - mdelay(1); - if (!etherh_getifstat(dev)) { - dev->if_port = IF_PORT_10BASE2; - etherh_setif(dev); - } - } else - etherh_setif(dev); - - etherh_reset(dev); - ei_open(dev); - - return 0; -} - -/* - * The inverse routine to etherh_open(). - */ -static int -etherh_close(struct net_device *dev) -{ - ei_close (dev); - free_irq (dev->irq, dev); - return 0; -} - -/* - * Initialisation - */ - -static void __init etherh_banner(void) -{ - static int version_printed; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -/* - * Read the ethernet address string from the on board rom. - * This is an ascii string... - */ -static int __init etherh_addr(char *addr, struct expansion_card *ec) -{ - struct in_chunk_dir cd; - char *s; - - if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { - int i; - for (i = 0; i < 6; i++) { - addr[i] = simple_strtoul(s + 1, &s, 0x10); - if (*s != (i == 5? ')' : ':')) - break; - } - if (i == 6) - return 0; - } - return -ENODEV; -} - -/* - * Create an ethernet address from the system serial number. - */ -static int __init etherm_addr(char *addr) -{ - unsigned int serial; - - if (system_serial_low == 0 && system_serial_high == 0) - return -ENODEV; - - serial = system_serial_low | system_serial_high; - - addr[0] = 0; - addr[1] = 0; - addr[2] = 0xa4; - addr[3] = 0x10 + (serial >> 24); - addr[4] = serial >> 16; - addr[5] = serial >> 8; - return 0; -} - -static u32 etherh_regoffsets[16]; -static u32 etherm_regoffsets[16]; - -static int __init -etherh_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct ei_device *ei_local; - struct net_device *dev; - struct etherh_priv *eh; - const char *dev_type; - int i, size, ret; - - etherh_banner(); - - dev = init_etherdev(NULL, sizeof(struct etherh_priv)); - if (!dev) { - ret = -ENOMEM; - goto out; - } - - /* - * init_etherdev allocs and zeros dev->priv - */ - eh = dev->priv; - - spin_lock_init(&eh->eidev.page_lock); - - SET_MODULE_OWNER(dev); - - dev->open = etherh_open; - dev->stop = etherh_close; - dev->set_config = etherh_set_config; - dev->irq = ec->irq; - dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); - - /* - * IRQ and control port handling - */ - if (ec->irq != 11) { - ec->ops = ðerh_ops; - ec->irq_data = eh; - } - eh->ctrl = 0; - eh->id = ec->cid.product; - - switch (ec->cid.product) { - case PROD_ANT_ETHERM: - etherm_addr(dev->dev_addr); - dev->base_addr += ETHERM_NS8390; - dev->mem_start = dev->base_addr + ETHERM_DATAPORT; - eh->ctrl_port = dev->base_addr + ETHERM_CTRLPORT; - break; - - case PROD_I3_ETHERLAN500: - etherh_addr(dev->dev_addr, ec); - dev->base_addr += ETHERH500_NS8390; - dev->mem_start = dev->base_addr + ETHERH500_DATAPORT; - eh->ctrl_port = ecard_address (ec, ECARD_IOC, ECARD_FAST) - + ETHERH500_CTRLPORT; - break; - - case PROD_I3_ETHERLAN600: - case PROD_I3_ETHERLAN600A: - etherh_addr(dev->dev_addr, ec); - dev->base_addr += ETHERH600_NS8390; - dev->mem_start = dev->base_addr + ETHERH600_DATAPORT; - eh->ctrl_port = dev->base_addr + ETHERH600_CTRLPORT; - break; - - default: - printk(KERN_ERR "%s: unknown card type %x\n", - dev->name, ec->cid.product); - ret = -ENODEV; - goto free; - } - - size = 16; - if (ec->cid.product == PROD_ANT_ETHERM) - size <<= 3; - - if (!request_region(dev->base_addr, size, dev->name)) { - ret = -EBUSY; - goto free; - } - - if (ethdev_init(dev)) { - ret = -ENODEV; - goto release; - } - - /* - * If we're in the NIC slot, make sure the IRQ is enabled - */ - if (dev->irq == 11) - etherh_set_ctrl(eh, ETHERH_CP_IE); - - /* - * Unfortunately, ethdev_init eventually calls - * ether_setup, which re-writes dev->flags. - */ - switch (ec->cid.product) { - case PROD_ANT_ETHERM: - dev_type = "ANT EtherM"; - dev->if_port = IF_PORT_UNKNOWN; - break; - - case PROD_I3_ETHERLAN500: - dev_type = "i3 EtherH 500"; - dev->if_port = IF_PORT_UNKNOWN; - break; - - case PROD_I3_ETHERLAN600: - dev_type = "i3 EtherH 600"; - dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; - dev->if_port = IF_PORT_10BASET; - break; - - case PROD_I3_ETHERLAN600A: - dev_type = "i3 EtherH 600A"; - dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; - dev->if_port = IF_PORT_10BASET; - break; - - default: - dev_type = "unknown"; - break; - } - - printk(KERN_INFO "%s: %s in slot %d, ", - dev->name, dev_type, ec->slot_no); - - for (i = 0; i < 6; i++) - printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - - ei_local = (struct ei_device *) dev->priv; - if (ec->cid.product == PROD_ANT_ETHERM) { - ei_local->tx_start_page = ETHERM_TX_START_PAGE; - ei_local->stop_page = ETHERM_STOP_PAGE; - ei_local->reg_offset = etherm_regoffsets; - } else { - ei_local->tx_start_page = ETHERH_TX_START_PAGE; - ei_local->stop_page = ETHERH_STOP_PAGE; - ei_local->reg_offset = etherh_regoffsets; - } - - ei_local->name = dev->name; - ei_local->word16 = 1; - ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES; - ei_local->reset_8390 = etherh_reset; - ei_local->block_input = etherh_block_input; - ei_local->block_output = etherh_block_output; - ei_local->get_8390_hdr = etherh_get_header; - ei_local->interface_num = 0; - - etherh_reset(dev); - NS8390_init(dev, 0); - - ecard_set_drvdata(ec, dev); - - return 0; - -release: - release_region(dev->base_addr, 16); -free: - unregister_netdev(dev); - kfree(dev->priv); - kfree(dev); -out: - return ret; -} - -static void __devexit etherh_remove(struct expansion_card *ec) -{ - struct net_device *dev = ecard_get_drvdata(ec); - int size = 16; - - ecard_set_drvdata(ec, NULL); - - unregister_netdev(dev); - if (ec->cid.product == PROD_ANT_ETHERM) - size <<= 3; - release_region(dev->base_addr, size); - kfree(dev); - - ec->ops = NULL; - kfree(ec->irq_data); -} - -static const struct ecard_id etherh_ids[] = { - { MANU_ANT, PROD_ANT_ETHERM }, - { MANU_I3, PROD_I3_ETHERLAN500 }, - { MANU_I3, PROD_I3_ETHERLAN600 }, - { MANU_I3, PROD_I3_ETHERLAN600A }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver etherh_driver = { - .probe = etherh_probe, - .remove = __devexit_p(etherh_remove), - .id_table = etherh_ids, - .drv = { - .name = "etherh", - }, -}; - -static int __init etherh_init(void) -{ - int i; - - for (i = 0; i < 16; i++) { - etherh_regoffsets[i] = i; - etherm_regoffsets[i] = i << 3; - } - - return ecard_register_driver(ðerh_driver); -} - -static void __exit etherh_exit(void) -{ - ecard_remove_driver(ðerh_driver); -} - -module_init(etherh_init); -module_exit(etherh_exit); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/Kconfig linux-2.5.69-bk14/drivers/acorn/scsi/Kconfig --- linux-2.5.69-bk13/drivers/acorn/scsi/Kconfig 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/Kconfig 1969-12-31 16:00:00.000000000 -0800 @@ -1,89 +0,0 @@ -# -# SCSI driver configuration for Acorn -# -config SCSI_ACORNSCSI_3 - tristate "Acorn SCSI card (aka30) support" - depends on ARCH_ACORN && SCSI - help - This enables support for the Acorn SCSI card (aka30). If you have an - Acorn system with one of these, say Y. If unsure, say N. - -config SCSI_ACORNSCSI_TAGGED_QUEUE - bool "Support SCSI 2 Tagged queueing" - depends on SCSI_ACORNSCSI_3 - help - Say Y here to enable tagged queuing support on the Acorn SCSI card. - - This is a feature of SCSI-2 which improves performance: the host - adapter can send several SCSI commands to a device's queue even if - previous commands haven't finished yet. Some SCSI devices don't - implement this properly, so the safe answer is N. - -config SCSI_ACORNSCSI_SYNC - bool "Support SCSI 2 Synchronous Transfers" - depends on SCSI_ACORNSCSI_3 - help - Say Y here to enable synchronous transfer negotiation with all - targets on the Acorn SCSI card. - - In general, this improves performance; however some SCSI devices - don't implement it properly, so the safe answer is N. - -config SCSI_ARXESCSI - tristate "ARXE SCSI support" - depends on ARCH_ACORN && SCSI - help - Around 1991, Arxe Systems Limited released a high density floppy - disc interface for the Acorn Archimedes range, to allow the use of - HD discs from the then new A5000 on earlier models. This interface - was either sold on its own or with an integral SCSI controller. - Technical details on this NCR53c94-based device are available at - - Say Y here to compile in support for the SCSI controller. - -config SCSI_CUMANA_2 - tristate "CumanaSCSI II support" - depends on ARCH_ACORN && SCSI - help - This enables support for the Cumana SCSI II card. If you have an - Acorn system with one of these, say Y. If unsure, say N. - -config SCSI_EESOXSCSI - tristate "EESOX support" - depends on ARCH_ACORN && SCSI - help - This enables support for the EESOX SCSI card. If you have an Acorn - system with one of these, say Y, otherwise say N. - -config SCSI_POWERTECSCSI - tristate "PowerTec support" - depends on ARCH_ACORN && SCSI - help - This enables support for the Powertec SCSI card on Acorn systems. If - you have one of these, say Y. If unsure, say N. - -comment "The following drivers are not fully supported" - depends on ARCH_ACORN && EXPERIMENTAL - -config SCSI_CUMANA_1 - tristate "CumanaSCSI I support (EXPERIMENTAL)" - depends on ARCH_ACORN && EXPERIMENTAL && SCSI - help - This enables support for the Cumana SCSI I card. If you have an - Acorn system with one of these, say Y. If unsure, say N. - -config SCSI_ECOSCSI - tristate "EcoScsi support (EXPERIMENTAL)" - depends on ARCH_ACORN && EXPERIMENTAL && (ARCH_ARC || ARCH_A5K) && SCSI - help - This enables support for the EcoSCSI card -- a small card that sits - in the Econet socket. If you have an Acorn system with one of these, - say Y. If unsure, say N. - -config SCSI_OAK1 - tristate "Oak SCSI support (EXPERIMENTAL)" - depends on ARCH_ACORN && EXPERIMENTAL && SCSI - help - This enables support for the Oak SCSI card. If you have an Acorn - system with one of these, say Y. If unsure, say N. - diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/Makefile linux-2.5.69-bk14/drivers/acorn/scsi/Makefile --- linux-2.5.69-bk13/drivers/acorn/scsi/Makefile 2003-05-04 16:52:49.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/Makefile 1969-12-31 16:00:00.000000000 -0800 @@ -1,14 +0,0 @@ -# -# Makefile for drivers/acorn/scsi -# - -acornscsi_mod-objs := acornscsi.o acornscsi-io.o - -obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_ARXESCSI) += arxescsi.o fas216.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_CUMANA_1) += cumana_1.o -obj-$(CONFIG_SCSI_CUMANA_2) += cumana_2.o fas216.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_ECOSCSI) += ecoscsi.o -obj-$(CONFIG_SCSI_OAK1) += oak.o -obj-$(CONFIG_SCSI_POWERTECSCSI) += powertec.o fas216.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_EESOXSCSI) += eesox.o fas216.o queue.o msgqueue.o diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/acornscsi-io.S linux-2.5.69-bk14/drivers/acorn/scsi/acornscsi-io.S --- linux-2.5.69-bk13/drivers/acorn/scsi/acornscsi-io.S 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/acornscsi-io.S 1969-12-31 16:00:00.000000000 -0800 @@ -1,145 +0,0 @@ -/* - * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include - -#include -#include - -#if (IO_BASE == (PCIO_BASE & 0xff000000)) -#define ADDR(off,reg) \ - tst off, $0x80000000 ;\ - mov reg, $IO_BASE ;\ - orreq reg, reg, $(PCIO_BASE & 0x00ff0000) -#else -#define ADDR(off,reg) \ - tst off, $0x80000000 ;\ - movne reg, $IO_BASE ;\ - moveq reg, $(PCIO_BASE & 0xff000000) ;\ - orreq reg, reg, $(PCIO_BASE & 0x00ff0000) -#endif - -@ Purpose: transfer a block of data from the acorn scsi card to memory -@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) -@ Returns: nothing - - .align -ENTRY(__acornscsi_in) - stmfd sp!, {r4 - r7, lr} - bic r0, r0, #3 - mov lr, #0xff - orr lr, lr, #0xff00 -acornscsi_in16lp: - subs r2, r2, #16 - bmi acornscsi_in8 - ldmia r0!, {r3, r4, r5, r6} - and r3, r3, lr - orr r3, r3, r4, lsl #16 - and r4, r5, lr - orr r4, r4, r6, lsl #16 - ldmia r0!, {r5, r6, r7, ip} - and r5, r5, lr - orr r5, r5, r6, lsl #16 - and r6, r7, lr - orr r6, r6, ip, lsl #16 - stmia r1!, {r3 - r6} - bne acornscsi_in16lp - LOADREGS(fd, sp!, {r4 - r7, pc}) - -acornscsi_in8: adds r2, r2, #8 - bmi acornscsi_in4 - ldmia r0!, {r3, r4, r5, r6} - and r3, r3, lr - orr r3, r3, r4, lsl #16 - and r4, r5, lr - orr r4, r4, r6, lsl #16 - stmia r1!, {r3 - r4} - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r2, r2, #8 - -acornscsi_in4: adds r2, r2, #4 - bmi acornscsi_in2 - ldmia r0!, {r3, r4} - and r3, r3, lr - orr r3, r3, r4, lsl #16 - str r3, [r1], #4 - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r2, r2, #4 - -acornscsi_in2: adds r2, r2, #2 - ldr r3, [r0], #4 - and r3, r3, lr - strb r3, [r1], #1 - mov r3, r3, lsr #8 - strplb r3, [r1], #1 - LOADREGS(fd, sp!, {r4 - r7, pc}) - -@ Purpose: transfer a block of data from memory to the acorn scsi card -@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) -@ Returns: nothing - -ENTRY(__acornscsi_out) - stmfd sp!, {r4 - r6, lr} - bic r0, r0, #3 -acornscsi_out16lp: - subs r2, r2, #16 - bmi acornscsi_out8 - ldmia r1!, {r4, r6, ip, lr} - mov r3, r4, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, r4, lsr #16 - orr r4, r4, r4, lsl #16 - mov r5, r6, lsl #16 - orr r5, r5, r5, lsr #16 - mov r6, r6, lsr #16 - orr r6, r6, r6, lsl #16 - stmia r0!, {r3, r4, r5, r6} - mov r3, ip, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, ip, lsr #16 - orr r4, r4, r4, lsl #16 - mov ip, lr, lsl #16 - orr ip, ip, ip, lsr #16 - mov lr, lr, lsr #16 - orr lr, lr, lr, lsl #16 - stmia r0!, {r3, r4, ip, lr} - bne acornscsi_out16lp - LOADREGS(fd, sp!, {r4 - r6, pc}) - -acornscsi_out8: adds r2, r2, #8 - bmi acornscsi_out4 - ldmia r1!, {r4, r6} - mov r3, r4, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, r4, lsr #16 - orr r4, r4, r4, lsl #16 - mov r5, r6, lsl #16 - orr r5, r5, r5, lsr #16 - mov r6, r6, lsr #16 - orr r6, r6, r6, lsl #16 - stmia r0!, {r3, r4, r5, r6} - LOADREGS(eqfd, sp!, {r4 - r6, pc}) - - sub r2, r2, #8 -acornscsi_out4: adds r2, r2, #4 - bmi acornscsi_out2 - ldr r4, [r1], #4 - mov r3, r4, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, r4, lsr #16 - orr r4, r4, r4, lsl #16 - stmia r0!, {r3, r4} - LOADREGS(eqfd, sp!, {r4 - r6, pc}) - - sub r2, r2, #4 -acornscsi_out2: adds r2, r2, #2 - ldr r3, [r1], #2 - strb r3, [r0], #1 - mov r3, r3, lsr #8 - strplb r3, [r0], #1 - LOADREGS(fd, sp!, {r4 - r6, pc}) - diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/acornscsi.c linux-2.5.69-bk14/drivers/acorn/scsi/acornscsi.c --- linux-2.5.69-bk13/drivers/acorn/scsi/acornscsi.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/acornscsi.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,3097 +0,0 @@ -/* - * linux/drivers/acorn/scsi/acornscsi.c - * - * Acorn SCSI 3 driver - * By R.M.King. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Abandoned using the Select and Transfer command since there were - * some nasty races between our software and the target devices that - * were not easy to solve, and the device errata had a lot of entries - * for this command, some of them quite nasty... - * - * Changelog: - * 26-Sep-1997 RMK Re-jigged to use the queue module. - * Re-coded state machine to be based on driver - * state not scsi state. Should be easier to debug. - * Added acornscsi_release to clean up properly. - * Updated proc/scsi reporting. - * 05-Oct-1997 RMK Implemented writing to SCSI devices. - * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/ - * reconnect race condition causing a warning message. - * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. - * 15-Oct-1997 RMK Improved handling of commands. - * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. - * 13-Dec-1998 RMK Better abort code and command handling. Extra state - * transitions added to allow dodgy devices to work. - */ -#define DEBUG_NO_WRITE 1 -#define DEBUG_QUEUES 2 -#define DEBUG_DMA 4 -#define DEBUG_ABORT 8 -#define DEBUG_DISCON 16 -#define DEBUG_CONNECT 32 -#define DEBUG_PHASES 64 -#define DEBUG_WRITE 128 -#define DEBUG_LINK 256 -#define DEBUG_MESSAGES 512 -#define DEBUG_RESET 1024 -#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ - DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ - DEBUG_DMA|DEBUG_QUEUES) - -/* DRIVER CONFIGURATION - * - * SCSI-II Tagged queue support. - * - * I don't have any SCSI devices that support it, so it is totally untested - * (except to make sure that it doesn't interfere with any non-tagging - * devices). It is not fully implemented either - what happens when a - * tagging device reconnects??? - * - * You can tell if you have a device that supports tagged queueing my - * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported - * as '2 TAG'. - * - * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config - * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug, - * comment out the undef. - */ -#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE -/* - * SCSI-II Linked command support. - * - * The higher level code doesn't support linked commands yet, and so the option - * is undef'd here. - */ -#undef CONFIG_SCSI_ACORNSCSI_LINK -/* - * SCSI-II Synchronous transfer support. - * - * Tried and tested... - * - * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max) - * SDTR_PERIOD - period of REQ signal (min=125, max=1020) - * DEFAULT_PERIOD - default REQ period. - */ -#define SDTR_SIZE 12 -#define SDTR_PERIOD 125 -#define DEFAULT_PERIOD 500 - -/* - * Debugging information - * - * DEBUG - bit mask from list above - * DEBUG_TARGET - is defined to the target number if you want to debug - * a specific target. [only recon/write/dma]. - */ -#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE) -/* only allow writing to SCSI device 0 */ -#define NO_WRITE 0xFE -/*#define DEBUG_TARGET 2*/ -/* - * Select timeout time (in 10ms units) - * - * This is the timeout used between the start of selection and the WD33C93 - * chip deciding that the device isn't responding. - */ -#define TIMEOUT_TIME 10 -/* - * Define this if you want to have verbose explaination of SCSI - * status/messages. - */ -#undef CONFIG_ACORNSCSI_CONSTANTS -/* - * Define this if you want to use the on board DMAC [don't remove this option] - * If not set, then use PIO mode (not currently supported). - */ -#define USE_DMAC - -/* - * ==================================================================================== - */ - -#ifdef DEBUG_TARGET -#define DBG(cmd,xxx...) \ - if (cmd->device->id == DEBUG_TARGET) { \ - xxx; \ - } -#else -#define DBG(cmd,xxx...) xxx -#endif - -#ifndef STRINGIFY -#define STRINGIFY(x) #x -#endif -#define STRx(x) STRINGIFY(x) -#define NO_WRITE_STR STRx(NO_WRITE) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "acornscsi.h" -#include "msgqueue.h" -#include "scsi.h" - -#include - -#define VER_MAJOR 2 -#define VER_MINOR 0 -#define VER_PATCH 6 - -#ifndef ABORT_TAG -#define ABORT_TAG 0xd -#else -#error "Yippee! ABORT TAG is now defined! Remove this error!" -#endif - -#ifdef CONFIG_SCSI_ACORNSCSI_LINK -#error SCSI2 LINKed commands not supported (yet)! -#endif - -#ifdef USE_DMAC -/* - * DMAC setup parameters - */ -#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP) -#define INIT_DEVCON1 (DEVCON1_BHLD) -#define DMAC_READ (MODECON_READ) -#define DMAC_WRITE (MODECON_WRITE) -#define INIT_SBICDMA (CTRL_DMABURST) - -#define scsi_xferred have_data_in - -/* - * Size of on-board DMA buffer - */ -#define DMAC_BUFFER_SIZE 65536 -#endif - -#define STATUS_BUFFER_TO_PRINT 24 - -unsigned int sdtr_period = SDTR_PERIOD; -unsigned int sdtr_size = SDTR_SIZE; - -static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); -static int acornscsi_reconnect_finish(AS_Host *host); -static void acornscsi_dma_cleanup(AS_Host *host); -static void acornscsi_abortcmd(AS_Host *host, unsigned char tag); - -/* ==================================================================================== - * Miscellaneous - */ - -static inline void -sbic_arm_write(unsigned int io_port, int reg, int value) -{ - __raw_writeb(reg, io_port); - __raw_writeb(value, io_port + 4); -} - -#define sbic_arm_writenext(io,val) \ - __raw_writeb((val), (io) + 4) - -static inline -int sbic_arm_read(unsigned int io_port, int reg) -{ - if(reg == ASR) - return __raw_readl(io_port) & 255; - __raw_writeb(reg, io_port); - return __raw_readl(io_port + 4) & 255; -} - -#define sbic_arm_readnext(io) \ - __raw_readb((io) + 4) - -#ifdef USE_DMAC -#define dmac_read(io_port,reg) \ - inb((io_port) + (reg)) - -#define dmac_write(io_port,reg,value) \ - ({ outb((value), (io_port) + (reg)); }) - -#define dmac_clearintr(io_port) \ - ({ outb(0, (io_port)); }) - -static inline -unsigned int dmac_address(unsigned int io_port) -{ - return dmac_read(io_port, TXADRHI) << 16 | - dmac_read(io_port, TXADRMD) << 8 | - dmac_read(io_port, TXADRLO); -} - -static -void acornscsi_dumpdma(AS_Host *host, char *where) -{ - unsigned int mode, addr, len; - - mode = dmac_read(host->dma.io_port, MODECON); - addr = dmac_address(host->dma.io_port); - len = dmac_read(host->dma.io_port, TXCNTHI) << 8 | - dmac_read(host->dma.io_port, TXCNTLO); - - printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", - host->host->host_no, where, - mode, addr, (len + 1) & 0xffff, - dmac_read(host->dma.io_port, MASKREG)); - - printk("DMA @%06x, ", host->dma.start_addr); - printk("BH @%p +%04x, ", host->scsi.SCp.ptr, - host->scsi.SCp.this_residual); - printk("DT @+%04x ST @+%04x", host->dma.transferred, - host->scsi.SCp.scsi_xferred); - printk("\n"); -} -#endif - -static -unsigned long acornscsi_sbic_xfcount(AS_Host *host) -{ - unsigned long length; - - length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16; - length |= sbic_arm_readnext(host->scsi.io_port) << 8; - length |= sbic_arm_readnext(host->scsi.io_port); - - return length; -} - -static int -acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg) -{ - int asr; - - do { - asr = sbic_arm_read(host->scsi.io_port, ASR); - - if ((asr & stat_mask) == stat) - return 0; - - udelay(1); - } while (--timeout); - - printk("scsi%d: timeout while %s\n", host->host->host_no, msg); - - return -1; -} - -static -int acornscsi_sbic_issuecmd(AS_Host *host, int command) -{ - if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) - return -1; - - sbic_arm_write(host->scsi.io_port, CMND, command); - - return 0; -} - -static void -acornscsi_csdelay(unsigned int cs) -{ - unsigned long target_jiffies, flags; - - target_jiffies = jiffies + 1 + cs * HZ / 100; - - local_save_flags(flags); - local_irq_enable(); - - while (time_before(jiffies, target_jiffies)) barrier(); - - local_irq_restore(flags); -} - -static -void acornscsi_resetcard(AS_Host *host) -{ - unsigned int i, timeout; - - /* assert reset line */ - host->card.page_reg = 0x80; - outb(host->card.page_reg, host->card.io_page); - - /* wait 3 cs. SCSI standard says 25ms. */ - acornscsi_csdelay(3); - - host->card.page_reg = 0; - outb(host->card.page_reg, host->card.io_page); - - /* - * Should get a reset from the card - */ - timeout = 1000; - do { - if (inb(host->card.io_intr) & 8) - break; - udelay(1); - } while (--timeout); - - if (timeout == 0) - printk("scsi%d: timeout while resetting card\n", - host->host->host_no); - - sbic_arm_read(host->scsi.io_port, ASR); - sbic_arm_read(host->scsi.io_port, SSR); - - /* setup sbic - WD33C93A */ - sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); - - /* - * Command should cause a reset interrupt - */ - timeout = 1000; - do { - if (inb(host->card.io_intr) & 8) - break; - udelay(1); - } while (--timeout); - - if (timeout == 0) - printk("scsi%d: timeout while resetting card\n", - host->host->host_no); - - sbic_arm_read(host->scsi.io_port, ASR); - if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01) - printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", - host->host->host_no); - - sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - - host->card.page_reg = 0x40; - outb(host->card.page_reg, host->card.io_page); - - /* setup dmac - uPC71071 */ - dmac_write(host->dma.io_port, INIT, 0); -#ifdef USE_DMAC - dmac_write(host->dma.io_port, INIT, INIT_8BIT); - dmac_write(host->dma.io_port, CHANNEL, CHANNEL_0); - dmac_write(host->dma.io_port, DEVCON0, INIT_DEVCON0); - dmac_write(host->dma.io_port, DEVCON1, INIT_DEVCON1); -#endif - - host->SCpnt = NULL; - host->scsi.phase = PHASE_IDLE; - host->scsi.disconnectable = 0; - - memset(host->busyluns, 0, sizeof(host->busyluns)); - - for (i = 0; i < 8; i++) { - host->device[i].sync_state = SYNC_NEGOCIATE; - host->device[i].disconnect_ok = 1; - } - - /* wait 25 cs. SCSI standard says 250ms. */ - acornscsi_csdelay(25); -} - -/*============================================================================================= - * Utility routines (eg. debug) - */ -#ifdef CONFIG_ACORNSCSI_CONSTANTS -static char *acornscsi_interrupttype[] = { - "rst", "suc", "p/a", "3", - "term", "5", "6", "7", - "serv", "9", "a", "b", - "c", "d", "e", "f" -}; - -static signed char acornscsi_map[] = { - 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; - -static char *acornscsi_interruptcode[] = { - /* 0 */ - "reset - normal mode", /* 00 */ - "reset - advanced mode", /* 01 */ - - /* 2 */ - "sel", /* 11 */ - "sel+xfer", /* 16 */ - "data-out", /* 18 */ - "data-in", /* 19 */ - "cmd", /* 1A */ - "stat", /* 1B */ - "??-out", /* 1C */ - "??-in", /* 1D */ - "msg-out", /* 1E */ - "msg-in", /* 1F */ - - /* 12 */ - "/ACK asserted", /* 20 */ - "save-data-ptr", /* 21 */ - "{re}sel", /* 22 */ - - /* 15 */ - "inv cmd", /* 40 */ - "unexpected disconnect", /* 41 */ - "sel timeout", /* 42 */ - "P err", /* 43 */ - "P err+ATN", /* 44 */ - "bad status byte", /* 47 */ - - /* 21 */ - "resel, no id", /* 80 */ - "resel", /* 81 */ - "discon", /* 85 */ -}; - -static -void print_scsi_status(unsigned int ssr) -{ - if (acornscsi_map[ssr] != -1) - printk("%s:%s", - acornscsi_interrupttype[(ssr >> 4)], - acornscsi_interruptcode[acornscsi_map[ssr]]); - else - printk("%X:%X", ssr >> 4, ssr & 0x0f); -} -#endif - -static -void print_sbic_status(int asr, int ssr, int cmdphase) -{ -#ifdef CONFIG_ACORNSCSI_CONSTANTS - printk("sbic: %c%c%c%c%c%c ", - asr & ASR_INT ? 'I' : 'i', - asr & ASR_LCI ? 'L' : 'l', - asr & ASR_BSY ? 'B' : 'b', - asr & ASR_CIP ? 'C' : 'c', - asr & ASR_PE ? 'P' : 'p', - asr & ASR_DBR ? 'D' : 'd'); - printk("scsi: "); - print_scsi_status(ssr); - printk(" ph %02X\n", cmdphase); -#else - printk("sbic: %02X scsi: %X:%X ph: %02X\n", - asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase); -#endif -} - -static void -acornscsi_dumplogline(AS_Host *host, int target, int line) -{ - unsigned long prev; - signed int ptr; - - ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; - if (ptr < 0) - ptr += STATUS_BUFFER_SIZE; - - printk("%c: %3s:", target == 8 ? 'H' : '0' + target, - line == 0 ? "ph" : line == 1 ? "ssr" : "int"); - - prev = host->status[target][ptr].when; - - for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { - unsigned long time_diff; - - if (!host->status[target][ptr].when) - continue; - - switch (line) { - case 0: - printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', - host->status[target][ptr].ph); - break; - - case 1: - printk(" %02X", host->status[target][ptr].ssr); - break; - - case 2: - time_diff = host->status[target][ptr].when - prev; - prev = host->status[target][ptr].when; - if (time_diff == 0) - printk("==^"); - else if (time_diff >= 100) - printk(" "); - else - printk(" %02ld", time_diff); - break; - } - } - - printk("\n"); -} - -static -void acornscsi_dumplog(AS_Host *host, int target) -{ - do { - acornscsi_dumplogline(host, target, 0); - acornscsi_dumplogline(host, target, 1); - acornscsi_dumplogline(host, target, 2); - - if (target == 8) - break; - - target = 8; - } while (1); -} - -static -char acornscsi_target(AS_Host *host) -{ - if (host->SCpnt) - return '0' + host->SCpnt->device->id; - return 'H'; -} - -/* - * Prototype: cmdtype_t acornscsi_cmdtype(int command) - * Purpose : differentiate READ from WRITE from other commands - * Params : command - command to interpret - * Returns : CMD_READ - command reads data, - * CMD_WRITE - command writes data, - * CMD_MISC - everything else - */ -static inline -cmdtype_t acornscsi_cmdtype(int command) -{ - switch (command) { - case WRITE_6: case WRITE_10: case WRITE_12: - return CMD_WRITE; - case READ_6: case READ_10: case READ_12: - return CMD_READ; - default: - return CMD_MISC; - } -} - -/* - * Prototype: int acornscsi_datadirection(int command) - * Purpose : differentiate between commands that have a DATA IN phase - * and a DATA OUT phase - * Params : command - command to interpret - * Returns : DATADIR_OUT - data out phase expected - * DATADIR_IN - data in phase expected - */ -static -datadir_t acornscsi_datadirection(int command) -{ - switch (command) { - case CHANGE_DEFINITION: case COMPARE: case COPY: - case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: - case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: - case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: - case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: - case WRITE_6: case WRITE_10: case WRITE_VERIFY: - case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME: - case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: - case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW: - case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea: - return DATADIR_OUT; - default: - return DATADIR_IN; - } -} - -/* - * Purpose : provide values for synchronous transfers with 33C93. - * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting - * Modified by Russell King for 8MHz WD33C93A - */ -static struct sync_xfer_tbl { - unsigned int period_ns; - unsigned char reg_value; -} sync_xfer_table[] = { - { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 }, - { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 }, - { 874, 0x70 }, { 999, 0x00 }, { 0, 0 } -}; - -/* - * Prototype: int acornscsi_getperiod(unsigned char syncxfer) - * Purpose : period for the synchronous transfer setting - * Params : syncxfer SYNCXFER register value - * Returns : period in ns. - */ -static -int acornscsi_getperiod(unsigned char syncxfer) -{ - int i; - - syncxfer &= 0xf0; - if (syncxfer == 0x10) - syncxfer = 0; - - for (i = 1; sync_xfer_table[i].period_ns; i++) - if (syncxfer == sync_xfer_table[i].reg_value) - return sync_xfer_table[i].period_ns; - return 0; -} - -/* - * Prototype: int round_period(unsigned int period) - * Purpose : return index into above table for a required REQ period - * Params : period - time (ns) for REQ - * Returns : table index - * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting - */ -static inline -int round_period(unsigned int period) -{ - int i; - - for (i = 1; sync_xfer_table[i].period_ns; i++) { - if ((period <= sync_xfer_table[i].period_ns) && - (period > sync_xfer_table[i - 1].period_ns)) - return i; - } - return 7; -} - -/* - * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) - * Purpose : calculate value for 33c93s SYNC register - * Params : period - time (ns) for REQ - * offset - offset in bytes between REQ/ACK - * Returns : value for SYNC register - * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting - */ -static -unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) -{ - return sync_xfer_table[round_period(period)].reg_value | - ((offset < SDTR_SIZE) ? offset : SDTR_SIZE); -} - -/* ==================================================================================== - * Command functions - */ -/* - * Function: acornscsi_kick(AS_Host *host) - * Purpose : kick next command to interface - * Params : host - host to send command to - * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING - * Notes : interrupts are always disabled! - */ -static -intr_ret_t acornscsi_kick(AS_Host *host) -{ - int from_queue = 0; - Scsi_Cmnd *SCpnt; - - /* first check to see if a command is waiting to be executed */ - SCpnt = host->origSCpnt; - host->origSCpnt = NULL; - - /* retrieve next command */ - if (!SCpnt) { - SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); - if (!SCpnt) - return INTR_IDLE; - - from_queue = 1; - } - - if (host->scsi.disconnectable && host->SCpnt) { - queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); - host->scsi.disconnectable = 0; -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", - host->host->host_no, acornscsi_target(host))); -#endif - host->SCpnt = NULL; - } - - /* - * If we have an interrupt pending, then we may have been reselected. - * In this case, we don't want to write to the registers - */ - if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { - sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->device->id); - sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN); - } - - /* - * claim host busy - all of these must happen atomically wrt - * our interrupt routine. Failure means command loss. - */ - host->scsi.phase = PHASE_CONNECTING; - host->SCpnt = SCpnt; - host->scsi.SCp = SCpnt->SCp; - host->dma.xfer_setup = 0; - host->dma.xfer_required = 0; - host->dma.xfer_done = 0; - -#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) - DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", - host->host->host_no, '0' + SCpnt->device->id, - SCpnt->cmnd[0])); -#endif - - if (from_queue) { -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - /* - * tagged queueing - allocate a new tag to this command - */ - if (SCpnt->device->tagged_queue) { - SCpnt->device->current_tag += 1; - if (SCpnt->device->current_tag == 0) - SCpnt->device->current_tag = 1; - SCpnt->tag = SCpnt->device->current_tag; - } else -#endif - set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); - - host->stats.removes += 1; - - switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { - case CMD_WRITE: - host->stats.writes += 1; - break; - case CMD_READ: - host->stats.reads += 1; - break; - case CMD_MISC: - host->stats.miscs += 1; - break; - } - } - - return INTR_PROCESSING; -} - -/* - * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) - * Purpose : complete processing for command - * Params : host - interface that completed - * result - driver byte of result - */ -static -void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) -{ - Scsi_Cmnd *SCpnt = *SCpntp; - - /* clean up */ - sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - - host->stats.fins += 1; - - if (SCpnt) { - *SCpntp = NULL; - - acornscsi_dma_cleanup(host); - - SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; - - /* - * In theory, this should not happen. In practice, it seems to. - * Only trigger an error if the device attempts to report all happy - * but with untransferred buffers... If we don't do something, then - * data loss will occur. Should we check SCpnt->underflow here? - * It doesn't appear to be set to something meaningful by the higher - * levels all the time. - */ - if (result == DID_OK) { - int xfer_warn = 0; - - if (SCpnt->underflow == 0) { - if (host->scsi.SCp.ptr && - acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) - xfer_warn = 1; - } else { - if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || - host->scsi.SCp.scsi_xferred != host->dma.transferred) - xfer_warn = 1; - } - - /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) - * Targets which break data transfers into multiple - * connections shall end each successful connection - * (except possibly the last) with a SAVE DATA - * POINTER - DISCONNECT message sequence. - * - * This makes it difficult to ensure that a transfer has - * completed. If we reach the end of a transfer during - * the command, then we can only have finished the transfer. - * therefore, if we seem to have some data remaining, this - * is not a problem. - */ - if (host->dma.xfer_done) - xfer_warn = 0; - - if (xfer_warn) { - switch (status_byte(SCpnt->result)) { - case CHECK_CONDITION: - case COMMAND_TERMINATED: - case BUSY: - case QUEUE_FULL: - case RESERVATION_CONFLICT: - break; - - default: - printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", - host->host->host_no, SCpnt->result); - print_command(SCpnt->cmnd); - acornscsi_dumpdma(host, "done"); - acornscsi_dumplog(host, SCpnt->device->id); - SCpnt->result &= 0xffff; - SCpnt->result |= DID_ERROR << 16; - } - } - } - - if (!SCpnt->scsi_done) - panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); - - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); - - SCpnt->scsi_done(SCpnt); - } else - printk("scsi%d: null command in acornscsi_done", host->host->host_no); - - host->scsi.phase = PHASE_IDLE; -} - -/* ==================================================================================== - * DMA routines - */ -/* - * Purpose : update SCSI Data Pointer - * Notes : this will only be one SG entry or less - */ -static -void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length) -{ - SCp->ptr += length; - SCp->this_residual -= length; - - if (SCp->this_residual == 0 && next_SCp(SCp) == 0) - host->dma.xfer_done = 1; -} - -/* - * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, - * unsigned int start_addr, unsigned int length) - * Purpose : read data from DMA RAM - * Params : host - host to transfer from - * ptr - DRAM address - * start_addr - host mem address - * length - number of bytes to transfer - * Notes : this will only be one SG entry or less - */ -static -void acornscsi_data_read(AS_Host *host, char *ptr, - unsigned int start_addr, unsigned int length) -{ - extern void __acornscsi_in(int port, char *buf, int len); - unsigned int page, offset, len = length; - - page = (start_addr >> 12); - offset = start_addr & ((1 << 12) - 1); - - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - - while (len > 0) { - unsigned int this_len; - - if (len + offset > (1 << 12)) - this_len = (1 << 12) - offset; - else - this_len = len; - - __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len); - - offset += this_len; - ptr += this_len; - len -= this_len; - - if (offset == (1 << 12)) { - offset = 0; - page ++; - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - } - } - outb(host->card.page_reg, host->card.io_page); -} - -/* - * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, - * unsigned int start_addr, unsigned int length) - * Purpose : write data to DMA RAM - * Params : host - host to transfer from - * ptr - DRAM address - * start_addr - host mem address - * length - number of bytes to transfer - * Notes : this will only be one SG entry or less - */ -static -void acornscsi_data_write(AS_Host *host, char *ptr, - unsigned int start_addr, unsigned int length) -{ - extern void __acornscsi_out(int port, char *buf, int len); - unsigned int page, offset, len = length; - - page = (start_addr >> 12); - offset = start_addr & ((1 << 12) - 1); - - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - - while (len > 0) { - unsigned int this_len; - - if (len + offset > (1 << 12)) - this_len = (1 << 12) - offset; - else - this_len = len; - - __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len); - - offset += this_len; - ptr += this_len; - len -= this_len; - - if (offset == (1 << 12)) { - offset = 0; - page ++; - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - } - } - outb(host->card.page_reg, host->card.io_page); -} - -/* ========================================================================================= - * On-board DMA routines - */ -#ifdef USE_DMAC -/* - * Prototype: void acornscsi_dmastop(AS_Host *host) - * Purpose : stop all DMA - * Params : host - host on which to stop DMA - * Notes : This is called when leaving DATA IN/OUT phase, - * or when interface is RESET - */ -static inline -void acornscsi_dma_stop(AS_Host *host) -{ - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr(host->dma.io_intr_clear); - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "stop")); -#endif -} - -/* - * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) - * Purpose : setup DMA controller for data transfer - * Params : host - host to setup - * direction - data transfer direction - * Notes : This is called when entering DATA I/O phase, not - * while we're in a DATA I/O phase - */ -static -void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) -{ - unsigned int address, length, mode; - - host->dma.direction = direction; - - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - - if (direction == DMA_OUT) { -#if (DEBUG & DEBUG_NO_WRITE) - if (NO_WRITE & (1 << host->SCpnt->device->id)) { - printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", - host->host->host_no, acornscsi_target(host)); - return; - } -#endif - mode = DMAC_WRITE; - } else - mode = DMAC_READ; - - /* - * Allocate some buffer space, limited to half the buffer size - */ - length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); - if (length) { - host->dma.start_addr = address = host->dma.free_addr; - host->dma.free_addr = (host->dma.free_addr + length) & - (DMAC_BUFFER_SIZE - 1); - - /* - * Transfer data to DMA memory - */ - if (direction == DMA_OUT) - acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, - length); - - length -= 1; - dmac_write(host->dma.io_port, TXCNTLO, length); - dmac_write(host->dma.io_port, TXCNTHI, length >> 8); - dmac_write(host->dma.io_port, TXADRLO, address); - dmac_write(host->dma.io_port, TXADRMD, address >> 8); - dmac_write(host->dma.io_port, TXADRHI, 0); - dmac_write(host->dma.io_port, MODECON, mode); - dmac_write(host->dma.io_port, MASKREG, MASK_OFF); - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "strt")); -#endif - host->dma.xfer_setup = 1; - } -} - -/* - * Function: void acornscsi_dma_cleanup(AS_Host *host) - * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct - * Params : host - host to finish - * Notes : This is called when a command is: - * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT - * : This must not return until all transfers are completed. - */ -static -void acornscsi_dma_cleanup(AS_Host *host) -{ - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr(host->dma.io_intr_clear); - - /* - * Check for a pending transfer - */ - if (host->dma.xfer_required) { - host->dma.xfer_required = 0; - if (host->dma.direction == DMA_IN) - acornscsi_data_read(host, host->dma.xfer_ptr, - host->dma.xfer_start, host->dma.xfer_length); - } - - /* - * Has a transfer been setup? - */ - if (host->dma.xfer_setup) { - unsigned int transferred; - - host->dma.xfer_setup = 0; - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi")); -#endif - - /* - * Calculate number of bytes transferred from DMA. - */ - transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; - host->dma.transferred += transferred; - - if (host->dma.direction == DMA_IN) - acornscsi_data_read(host, host->scsi.SCp.ptr, - host->dma.start_addr, transferred); - - /* - * Update SCSI pointers - */ - acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo")); -#endif - } -} - -/* - * Function: void acornscsi_dmacintr(AS_Host *host) - * Purpose : handle interrupts from DMAC device - * Params : host - host to process - * Notes : If reading, we schedule the read to main memory & - * allow the transfer to continue. - * : If writing, we fill the onboard DMA memory from main - * memory. - * : Called whenever DMAC finished it's current transfer. - */ -static -void acornscsi_dma_intr(AS_Host *host) -{ - unsigned int address, length, transferred; - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "inti")); -#endif - - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr(host->dma.io_intr_clear); - - /* - * Calculate amount transferred via DMA - */ - transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; - host->dma.transferred += transferred; - - /* - * Schedule DMA transfer off board - */ - if (host->dma.direction == DMA_IN) { - host->dma.xfer_start = host->dma.start_addr; - host->dma.xfer_length = transferred; - host->dma.xfer_ptr = host->scsi.SCp.ptr; - host->dma.xfer_required = 1; - } - - acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); - - /* - * Allocate some buffer space, limited to half the on-board RAM size - */ - length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); - if (length) { - host->dma.start_addr = address = host->dma.free_addr; - host->dma.free_addr = (host->dma.free_addr + length) & - (DMAC_BUFFER_SIZE - 1); - - /* - * Transfer data to DMA memory - */ - if (host->dma.direction == DMA_OUT) - acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, - length); - - length -= 1; - dmac_write(host->dma.io_port, TXCNTLO, length); - dmac_write(host->dma.io_port, TXCNTHI, length >> 8); - dmac_write(host->dma.io_port, TXADRLO, address); - dmac_write(host->dma.io_port, TXADRMD, address >> 8); - dmac_write(host->dma.io_port, TXADRHI, 0); - dmac_write(host->dma.io_port, MASKREG, MASK_OFF); - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "into")); -#endif - } else { - host->dma.xfer_setup = 0; -#if 0 - /* - * If the interface still wants more, then this is an error. - * We give it another byte, but we also attempt to raise an - * attention condition. We continue giving one byte until - * the device recognises the attention. - */ - if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) { - acornscsi_abortcmd(host, host->SCpnt->tag); - - dmac_write(host->dma.io_port, TXCNTLO, 0); - dmac_write(host->dma.io_port, TXCNTHI, 0); - dmac_write(host->dma.io_port, TXADRLO, 0); - dmac_write(host->dma.io_port, TXADRMD, 0); - dmac_write(host->dma.io_port, TXADRHI, 0); - dmac_write(host->dma.io_port, MASKREG, MASK_OFF); - } -#endif - } -} - -/* - * Function: void acornscsi_dma_xfer(AS_Host *host) - * Purpose : transfer data between AcornSCSI and memory - * Params : host - host to process - */ -static -void acornscsi_dma_xfer(AS_Host *host) -{ - host->dma.xfer_required = 0; - - if (host->dma.direction == DMA_IN) - acornscsi_data_read(host, host->dma.xfer_ptr, - host->dma.xfer_start, host->dma.xfer_length); -} - -/* - * Function: void acornscsi_dma_adjust(AS_Host *host) - * Purpose : adjust DMA pointers & count for bytes transferred to - * SBIC but not SCSI bus. - * Params : host - host to adjust DMA count for - */ -static -void acornscsi_dma_adjust(AS_Host *host) -{ - if (host->dma.xfer_setup) { - signed long transferred; -#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma(host, "adji")); -#endif - /* - * Calculate correct DMA address - DMA is ahead of SCSI bus while - * writing. - * host->scsi.SCp.scsi_xferred is the number of bytes - * actually transferred to/from the SCSI bus. - * host->dma.transferred is the number of bytes transferred - * over DMA since host->dma.start_addr was last set. - * - * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred - * - host->dma.transferred - */ - transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; - if (transferred < 0) - printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", - host->host->host_no, acornscsi_target(host), transferred); - else if (transferred == 0) - host->dma.xfer_setup = 0; - else { - transferred += host->dma.start_addr; - dmac_write(host->dma.io_port, TXADRLO, transferred); - dmac_write(host->dma.io_port, TXADRMD, transferred >> 8); - dmac_write(host->dma.io_port, TXADRHI, transferred >> 16); -#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo")); -#endif - } - } -} -#endif - -/* ========================================================================================= - * Data I/O - */ -static int -acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout) -{ - unsigned int asr, timeout = max_timeout; - int my_ptr = *ptr; - - while (my_ptr < len) { - asr = sbic_arm_read(host->scsi.io_port, ASR); - - if (asr & ASR_DBR) { - timeout = max_timeout; - - sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]); - } else if (asr & ASR_INT) - break; - else if (--timeout == 0) - break; - udelay(1); - } - - *ptr = my_ptr; - - return (timeout == 0) ? -1 : 0; -} - -/* - * Function: void acornscsi_sendcommand(AS_Host *host) - * Purpose : send a command to a target - * Params : host - host which is connected to target - */ -static void -acornscsi_sendcommand(AS_Host *host) -{ - Scsi_Cmnd *SCpnt = host->SCpnt; - - sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext(host->scsi.io_port, 0); - sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); - - acornscsi_sbic_issuecmd(host, CMND_XFERINFO); - - if (acornscsi_write_pio(host, SCpnt->cmnd, - (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) - printk("scsi%d: timeout while sending command\n", host->host->host_no); - - host->scsi.phase = PHASE_COMMAND; -} - -static -void acornscsi_sendmessage(AS_Host *host) -{ - unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); - unsigned int msgnr; - struct message *msg; - -#if (DEBUG & DEBUG_MESSAGES) - printk("scsi%d.%c: sending message ", - host->host->host_no, acornscsi_target(host)); -#endif - - switch (message_length) { - case 0: - acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); - - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1"); - - sbic_arm_write(host->scsi.io_port, DATA, NOP); - - host->scsi.last_message = NOP; -#if (DEBUG & DEBUG_MESSAGES) - printk("NOP"); -#endif - break; - - case 1: - acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); - msg = msgqueue_getmsg(&host->scsi.msgs, 0); - - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2"); - - sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]); - - host->scsi.last_message = msg->msg[0]; -#if (DEBUG & DEBUG_MESSAGES) - print_msg(msg->msg); -#endif - break; - - default: - /* - * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14) - * 'When a target sends this (MESSAGE_REJECT) message, it - * shall change to MESSAGE IN phase and send this message - * prior to requesting additional message bytes from the - * initiator. This provides an interlock so that the - * initiator can determine which message byte is rejected. - */ - sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext(host->scsi.io_port, 0); - sbic_arm_writenext(host->scsi.io_port, message_length); - acornscsi_sbic_issuecmd(host, CMND_XFERINFO); - - msgnr = 0; - while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) { - unsigned int i; -#if (DEBUG & DEBUG_MESSAGES) - print_msg(msg); -#endif - i = 0; - if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000)) - printk("scsi%d: timeout while sending message\n", host->host->host_no); - - host->scsi.last_message = msg->msg[0]; - if (msg->msg[0] == EXTENDED_MESSAGE) - host->scsi.last_message |= msg->msg[2] << 8; - - if (i != msg->length) - break; - } - break; - } -#if (DEBUG & DEBUG_MESSAGES) - printk("\n"); -#endif -} - -/* - * Function: void acornscsi_readstatusbyte(AS_Host *host) - * Purpose : Read status byte from connected target - * Params : host - host connected to target - */ -static -void acornscsi_readstatusbyte(AS_Host *host) -{ - acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT); - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte"); - host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA); -} - -/* - * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host) - * Purpose : Read one message byte from connected target - * Params : host - host connected to target - */ -static -unsigned char acornscsi_readmessagebyte(AS_Host *host) -{ - unsigned char message; - - acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); - - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte"); - - message = sbic_arm_read(host->scsi.io_port, DATA); - - /* wait for MSGIN-XFER-PAUSED */ - acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte"); - - sbic_arm_read(host->scsi.io_port, SSR); - - return message; -} - -/* - * Function: void acornscsi_message(AS_Host *host) - * Purpose : Read complete message from connected target & action message - * Params : host - host connected to target - */ -static -void acornscsi_message(AS_Host *host) -{ - unsigned char message[16]; - unsigned int msgidx = 0, msglen = 1; - - do { - message[msgidx] = acornscsi_readmessagebyte(host); - - switch (msgidx) { - case 0: - if (message[0] == EXTENDED_MESSAGE || - (message[0] >= 0x20 && message[0] <= 0x2f)) - msglen = 2; - break; - - case 1: - if (message[0] == EXTENDED_MESSAGE) - msglen += message[msgidx]; - break; - } - msgidx += 1; - if (msgidx < msglen) { - acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); - - /* wait for next msg-in */ - acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack"); - sbic_arm_read(host->scsi.io_port, SSR); - } - } while (msgidx < msglen); - -#if (DEBUG & DEBUG_MESSAGES) - printk("scsi%d.%c: message in: ", - host->host->host_no, acornscsi_target(host)); - print_msg(message); - printk("\n"); -#endif - - if (host->scsi.phase == PHASE_RECONNECTED) { - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) - * 'Whenever a target reconnects to an initiator to continue - * a tagged I/O process, the SIMPLE QUEUE TAG message shall - * be sent immediately following the IDENTIFY message...' - */ - if (message[0] == SIMPLE_QUEUE_TAG) - host->scsi.reconnected.tag = message[1]; - if (acornscsi_reconnect_finish(host)) - host->scsi.phase = PHASE_MSGIN; - } - - switch (message[0]) { - case ABORT: - case ABORT_TAG: - case COMMAND_COMPLETE: - if (host->scsi.phase != PHASE_STATUSIN) { - printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n", - host->host->host_no, acornscsi_target(host)); - acornscsi_dumplog(host, host->SCpnt->device->id); - } - host->scsi.phase = PHASE_DONE; - host->scsi.SCp.Message = message[0]; - break; - - case SAVE_POINTERS: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20) - * 'The SAVE DATA POINTER message is sent from a target to - * direct the initiator to copy the active data pointer to - * the saved data pointer for the current I/O process. - */ - acornscsi_dma_cleanup(host); - host->SCpnt->SCp = host->scsi.SCp; - host->SCpnt->SCp.sent_command = 0; - host->scsi.phase = PHASE_MSGIN; - break; - - case RESTORE_POINTERS: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19) - * 'The RESTORE POINTERS message is sent from a target to - * direct the initiator to copy the most recently saved - * command, data, and status pointers for the I/O process - * to the corresponding active pointers. The command and - * status pointers shall be restored to the beginning of - * the present command and status areas.' - */ - acornscsi_dma_cleanup(host); - host->scsi.SCp = host->SCpnt->SCp; - host->scsi.phase = PHASE_MSGIN; - break; - - case DISCONNECT: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2) - * 'On those occasions when an error or exception condition occurs - * and the target elects to repeat the information transfer, the - * target may repeat the transfer either issuing a RESTORE POINTERS - * message or by disconnecting without issuing a SAVE POINTERS - * message. When reconnection is completed, the most recent - * saved pointer values are restored.' - */ - acornscsi_dma_cleanup(host); - host->scsi.phase = PHASE_DISCONNECT; - break; - - case MESSAGE_REJECT: -#if 0 /* this isn't needed any more */ - /* - * If we were negociating sync transfer, we don't yet know if - * this REJECT is for the sync transfer or for the tagged queue/wide - * transfer. Re-initiate sync transfer negociation now, and if - * we got a REJECT in response to SDTR, then it'll be set to DONE. - */ - if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) - host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE; -#endif - - /* - * If we have any messages waiting to go out, then assert ATN now - */ - if (msgqueue_msglength(&host->scsi.msgs)) - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - - switch (host->scsi.last_message) { -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - case HEAD_OF_QUEUE_TAG: - case ORDERED_QUEUE_TAG: - case SIMPLE_QUEUE_TAG: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) - * If a target does not implement tagged queuing and a queue tag - * message is received, it shall respond with a MESSAGE REJECT - * message and accept the I/O process as if it were untagged. - */ - printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", - host->host->host_no, acornscsi_target(host)); - host->SCpnt->device->tagged_queue = 0; - set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns); - break; -#endif - case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): - /* - * Target can't handle synchronous transfers - */ - printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", - host->host->host_no, acornscsi_target(host)); - host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA; - host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS; - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); - break; - - default: - break; - } - break; - - case QUEUE_FULL: - /* TODO: target queue is full */ - break; - - case SIMPLE_QUEUE_TAG: - /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */ - printk("scsi%d.%c: reconnect queue tag %02X\n", - host->host->host_no, acornscsi_target(host), - message[1]); - break; - - case EXTENDED_MESSAGE: - switch (message[2]) { -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - case EXTENDED_SDTR: - if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) { - /* - * We requested synchronous transfers. This isn't quite right... - * We can only say if this succeeded if we proceed on to execute the - * command from this message. If we get a MESSAGE PARITY ERROR, - * and the target retries fail, then we fallback to asynchronous mode - */ - host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED; - printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", - host->host->host_no, acornscsi_target(host), - message[4], message[3] * 4); - host->device[host->SCpnt->device->id].sync_xfer = - calc_sync_xfer(message[3] * 4, message[4]); - } else { - unsigned char period, length; - /* - * Target requested synchronous transfers. The agreement is only - * to be in operation AFTER the target leaves message out phase. - */ - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - period = max_t(unsigned int, message[3], sdtr_period / 4); - length = min_t(unsigned int, message[4], sdtr_size); - msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, - EXTENDED_SDTR, period, length); - host->device[host->SCpnt->device->id].sync_xfer = - calc_sync_xfer(period * 4, length); - } - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); - break; -#else - /* We do not accept synchronous transfers. Respond with a - * MESSAGE_REJECT. - */ -#endif - - case EXTENDED_WDTR: - /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT - * to a wide data transfer request. - */ - default: - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - msgqueue_flush(&host->scsi.msgs); - msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); - break; - } - break; - -#ifdef CONFIG_SCSI_ACORNSCSI_LINK - case LINKED_CMD_COMPLETE: - case LINKED_FLG_CMD_COMPLETE: - /* - * We don't support linked commands yet - */ - if (0) { -#if (DEBUG & DEBUG_LINK) - printk("scsi%d.%c: lun %d tag %d linked command complete\n", - host->host->host_no, acornscsi_target(host), host->SCpnt->tag); -#endif - /* - * A linked command should only terminate with one of these messages - * if there are more linked commands available. - */ - if (!host->SCpnt->next_link) { - printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", - instance->host_no, acornscsi_target(host), host->SCpnt->tag); - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); - } else { - Scsi_Cmnd *SCpnt = host->SCpnt; - - acornscsi_dma_cleanup(host); - - host->SCpnt = host->SCpnt->next_link; - host->SCpnt->tag = SCpnt->tag; - SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status; - SCpnt->done(SCpnt); - - /* initialise host->SCpnt->SCp */ - } - break; - } -#endif - - default: /* reject message */ - printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", - host->host->host_no, acornscsi_target(host), - message[0]); - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - msgqueue_flush(&host->scsi.msgs); - msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); - host->scsi.phase = PHASE_MSGIN; - break; - } - acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); -} - -/* - * Function: int acornscsi_buildmessages(AS_Host *host) - * Purpose : build the connection messages for a host - * Params : host - host to add messages to - */ -static -void acornscsi_buildmessages(AS_Host *host) -{ -#if 0 - /* does the device need resetting? */ - if (cmd_reset) { - msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET); - return; - } -#endif - - msgqueue_addmsg(&host->scsi.msgs, 1, - IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok, - host->SCpnt->device->lun)); - -#if 0 - /* does the device need the current command aborted */ - if (cmd_aborted) { - acornscsi_abortcmd(host->SCpnt->tag); - return; - } -#endif - -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - if (host->SCpnt->tag) { - unsigned int tag_type; - - if (host->SCpnt->cmnd[0] == REQUEST_SENSE || - host->SCpnt->cmnd[0] == TEST_UNIT_READY || - host->SCpnt->cmnd[0] == INQUIRY) - tag_type = HEAD_OF_QUEUE_TAG; - else - tag_type = SIMPLE_QUEUE_TAG; - msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); - } -#endif - -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) { - host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST; - msgqueue_addmsg(&host->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - sdtr_period / 4, sdtr_size); - } -#endif -} - -/* - * Function: int acornscsi_starttransfer(AS_Host *host) - * Purpose : transfer data to/from connected target - * Params : host - host to which target is connected - * Returns : 0 if failure - */ -static -int acornscsi_starttransfer(AS_Host *host) -{ - int residual; - - if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) { - printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", - host->host->host_no, acornscsi_target(host)); - return 0; - } - - residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred; - - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); - sbic_arm_writenext(host->scsi.io_port, residual >> 16); - sbic_arm_writenext(host->scsi.io_port, residual >> 8); - sbic_arm_writenext(host->scsi.io_port, residual); - acornscsi_sbic_issuecmd(host, CMND_XFERINFO); - return 1; -} - -/* ========================================================================================= - * Connection & Disconnection - */ -/* - * Function : acornscsi_reconnect(AS_Host *host) - * Purpose : reconnect a previously disconnected command - * Params : host - host specific data - * Remarks : SCSI spec says: - * 'The set of active pointers is restored from the set - * of saved pointers upon reconnection of the I/O process' - */ -static -int acornscsi_reconnect(AS_Host *host) -{ - unsigned int target, lun, ok = 0; - - target = sbic_arm_read(host->scsi.io_port, SOURCEID); - - if (!(target & 8)) - printk(KERN_ERR "scsi%d: invalid source id after reselection " - "- device fault?\n", - host->host->host_no); - - target &= 7; - - if (host->SCpnt && !host->scsi.disconnectable) { - printk(KERN_ERR "scsi%d.%d: reconnected while command in " - "progress to target %d?\n", - host->host->host_no, target, host->SCpnt->device->id); - host->SCpnt = NULL; - } - - lun = sbic_arm_read(host->scsi.io_port, DATA) & 7; - - host->scsi.reconnected.target = target; - host->scsi.reconnected.lun = lun; - host->scsi.reconnected.tag = 0; - - if (host->scsi.disconnectable && host->SCpnt && - host->SCpnt->device->id == target && host->SCpnt->device->lun == lun) - ok = 1; - - if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) - ok = 1; - - ADD_STATUS(target, 0x81, host->scsi.phase, 0); - - if (ok) { - host->scsi.phase = PHASE_RECONNECTED; - } else { - /* this doesn't seem to work */ - printk(KERN_ERR "scsi%d.%c: reselected with no command " - "to reconnect with\n", - host->host->host_no, '0' + target); - acornscsi_dumplog(host, target); - acornscsi_abortcmd(host, 0); - if (host->SCpnt) { - queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); - host->SCpnt = NULL; - } - } - acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); - return !ok; -} - -/* - * Function: int acornscsi_reconect_finish(AS_Host *host) - * Purpose : finish reconnecting a command - * Params : host - host to complete - * Returns : 0 if failed - */ -static -int acornscsi_reconnect_finish(AS_Host *host) -{ - if (host->scsi.disconnectable && host->SCpnt) { - host->scsi.disconnectable = 0; - if (host->SCpnt->device->id == host->scsi.reconnected.target && - host->SCpnt->device->lun == host->scsi.reconnected.lun && - host->SCpnt->tag == host->scsi.reconnected.tag) { -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: reconnected", - host->host->host_no, acornscsi_target(host))); -#endif - } else { - queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: had to move command " - "to disconnected queue\n", - host->host->host_no, acornscsi_target(host))); -#endif - host->SCpnt = NULL; - } - } - if (!host->SCpnt) { - host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, - host->scsi.reconnected.target, - host->scsi.reconnected.lun, - host->scsi.reconnected.tag); -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: had to get command", - host->host->host_no, acornscsi_target(host))); -#endif - } - - if (!host->SCpnt) - acornscsi_abortcmd(host, host->scsi.reconnected.tag); - else { - /* - * Restore data pointer from SAVED pointers. - */ - host->scsi.SCp = host->SCpnt->SCp; -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk(", data pointers: [%p, %X]", - host->scsi.SCp.ptr, host->scsi.SCp.this_residual); -#endif - } -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk("\n"); -#endif - - host->dma.transferred = host->scsi.SCp.scsi_xferred; - - return host->SCpnt != NULL; -} - -/* - * Function: void acornscsi_disconnect_unexpected(AS_Host *host) - * Purpose : handle an unexpected disconnect - * Params : host - host on which disconnect occurred - */ -static -void acornscsi_disconnect_unexpected(AS_Host *host) -{ - printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", - host->host->host_no, acornscsi_target(host)); -#if (DEBUG & DEBUG_ABORT) - acornscsi_dumplog(host, 8); -#endif - - acornscsi_done(host, &host->SCpnt, DID_ERROR); -} - -/* - * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) - * Purpose : abort a currently executing command - * Params : host - host with connected command to abort - * tag - tag to abort - */ -static -void acornscsi_abortcmd(AS_Host *host, unsigned char tag) -{ - host->scsi.phase = PHASE_ABORTED; - sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN); - - msgqueue_flush(&host->scsi.msgs); -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - if (tag) - msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); - else -#endif - msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); -} - -/* ========================================================================================== - * Interrupt routines. - */ -/* - * Function: int acornscsi_sbicintr(AS_Host *host) - * Purpose : handle interrupts from SCSI device - * Params : host - host to process - * Returns : INTR_PROCESS if expecting another SBIC interrupt - * INTR_IDLE if no interrupt - * INTR_NEXT_COMMAND if we have finished processing the command - */ -static -intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq) -{ - unsigned int asr, ssr; - - asr = sbic_arm_read(host->scsi.io_port, ASR); - if (!(asr & ASR_INT)) - return INTR_IDLE; - - ssr = sbic_arm_read(host->scsi.io_port, SSR); - -#if (DEBUG & DEBUG_PHASES) - print_sbic_status(asr, ssr, host->scsi.phase); -#endif - - ADD_STATUS(8, ssr, host->scsi.phase, in_irq); - - if (host->SCpnt && !host->scsi.disconnectable) - ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); - - switch (ssr) { - case 0x00: /* reset state - not advanced */ - printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", - host->host->host_no); - /* setup sbic - WD33C93A */ - sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); - return INTR_IDLE; - - case 0x01: /* reset state - advanced */ - sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - msgqueue_flush(&host->scsi.msgs); - return INTR_IDLE; - - case 0x41: /* unexpected disconnect aborted command */ - acornscsi_disconnect_unexpected(host); - return INTR_NEXT_COMMAND; - } - - switch (host->scsi.phase) { - case PHASE_CONNECTING: /* STATE: command removed from issue queue */ - switch (ssr) { - case 0x11: /* -> PHASE_CONNECTED */ - /* BUS FREE -> SELECTION */ - host->scsi.phase = PHASE_CONNECTED; - msgqueue_flush(&host->scsi.msgs); - host->dma.transferred = host->scsi.SCp.scsi_xferred; - /* 33C93 gives next interrupt indicating bus phase */ - asr = sbic_arm_read(host->scsi.io_port, ASR); - if (!(asr & ASR_INT)) - break; - ssr = sbic_arm_read(host->scsi.io_port, SSR); - ADD_STATUS(8, ssr, host->scsi.phase, 1); - ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1); - goto connected; - - case 0x42: /* select timed out */ - /* -> PHASE_IDLE */ - acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); - return INTR_NEXT_COMMAND; - - case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ - /* BUS FREE -> RESELECTION */ - host->origSCpnt = host->SCpnt; - host->SCpnt = NULL; - msgqueue_flush(&host->scsi.msgs); - acornscsi_reconnect(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - acornscsi_abortcmd(host, host->SCpnt->tag); - } - return INTR_PROCESSING; - - connected: - case PHASE_CONNECTED: /* STATE: device selected ok */ - switch (ssr) { -#ifdef NONSTANDARD - case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - /* SELECTION -> COMMAND */ - acornscsi_sendcommand(host); - break; - - case 0x8b: /* -> PHASE_STATUS */ - /* SELECTION -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; -#endif - - case 0x8e: /* -> PHASE_MSGOUT */ - /* SELECTION ->MESSAGE OUT */ - host->scsi.phase = PHASE_MSGOUT; - acornscsi_buildmessages(host); - acornscsi_sendmessage(host); - break; - - /* these should not happen */ - case 0x85: /* target disconnected */ - acornscsi_done(host, &host->SCpnt, DID_ERROR); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - acornscsi_abortcmd(host, host->SCpnt->tag); - } - return INTR_PROCESSING; - - case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ - /* - * SCSI standard says that MESSAGE OUT phases can be followed by a - * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase - */ - switch (ssr) { - case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - /* MESSAGE OUT -> COMMAND */ - acornscsi_sendcommand(host); - break; - - case 0x8b: /* -> PHASE_STATUS */ - case 0x1b: /* -> PHASE_STATUS */ - /* MESSAGE OUT -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x8e: /* -> PHASE_MSGOUT */ - /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - /* MESSAGE OUT -> MESSAGE IN */ - acornscsi_message(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_COMMAND: /* STATE: connected & command sent */ - switch (ssr) { - case 0x18: /* -> PHASE_DATAOUT */ - /* COMMAND -> DATA OUT */ - if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd(host, host->SCpnt->tag); - acornscsi_dma_setup(host, DMA_OUT); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAOUT; - return INTR_IDLE; - - case 0x19: /* -> PHASE_DATAIN */ - /* COMMAND -> DATA IN */ - if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd(host, host->SCpnt->tag); - acornscsi_dma_setup(host, DMA_IN); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAIN; - return INTR_IDLE; - - case 0x1b: /* -> PHASE_STATUS */ - /* COMMAND -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - /* COMMAND -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - /* COMMAND -> MESSAGE IN */ - acornscsi_message(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */ - if (ssr == 0x85) { /* -> PHASE_IDLE */ - host->scsi.disconnectable = 1; - host->scsi.reconnected.tag = 0; - host->scsi.phase = PHASE_IDLE; - host->stats.disconnects += 1; - } else { - printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_NEXT_COMMAND; - - case PHASE_IDLE: /* STATE: disconnected */ - if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ - acornscsi_reconnect(host); - else { - printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */ - /* - * Command reconnected - if MESGIN, get message - it may be - * the tag. If not, get command out of disconnected queue - */ - /* - * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, - * reconnect I_T_L command - */ - if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) - return INTR_IDLE; - ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); - switch (ssr) { - case 0x88: /* data out phase */ - /* -> PHASE_DATAOUT */ - /* MESSAGE IN -> DATA OUT */ - acornscsi_dma_setup(host, DMA_OUT); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAOUT; - return INTR_IDLE; - - case 0x89: /* data in phase */ - /* -> PHASE_DATAIN */ - /* MESSAGE IN -> DATA IN */ - acornscsi_dma_setup(host, DMA_IN); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAIN; - return INTR_IDLE; - - case 0x8a: /* command out */ - /* MESSAGE IN -> COMMAND */ - acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - break; - - case 0x8b: /* status in */ - /* -> PHASE_STATUSIN */ - /* MESSAGE IN -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x8e: /* message out */ - /* -> PHASE_MSGOUT */ - /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x8f: /* message in */ - acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DATAIN: /* STATE: transferred data in */ - /* - * This is simple - if we disconnect then the DMA address & count is - * correct. - */ - switch (ssr) { - case 0x19: /* -> PHASE_DATAIN */ - case 0x89: /* -> PHASE_DATAIN */ - acornscsi_abortcmd(host, host->SCpnt->tag); - return INTR_IDLE; - - case 0x1b: /* -> PHASE_STATUSIN */ - case 0x4b: /* -> PHASE_STATUSIN */ - case 0x8b: /* -> PHASE_STATUSIN */ - /* DATA IN -> STATUS */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x4e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* DATA IN -> MESSAGE OUT */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_sendmessage(host); - break; - - case 0x1f: /* message in */ - case 0x4f: /* message in */ - case 0x8f: /* message in */ - /* DATA IN -> MESSAGE IN */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DATAOUT: /* STATE: transferred data out */ - /* - * This is more complicated - if we disconnect, the DMA could be 12 - * bytes ahead of us. We need to correct this. - */ - switch (ssr) { - case 0x18: /* -> PHASE_DATAOUT */ - case 0x88: /* -> PHASE_DATAOUT */ - acornscsi_abortcmd(host, host->SCpnt->tag); - return INTR_IDLE; - - case 0x1b: /* -> PHASE_STATUSIN */ - case 0x4b: /* -> PHASE_STATUSIN */ - case 0x8b: /* -> PHASE_STATUSIN */ - /* DATA OUT -> STATUS */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_dma_adjust(host); - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x4e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* DATA OUT -> MESSAGE OUT */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_dma_adjust(host); - acornscsi_sendmessage(host); - break; - - case 0x1f: /* message in */ - case 0x4f: /* message in */ - case 0x8f: /* message in */ - /* DATA OUT -> MESSAGE IN */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_dma_adjust(host); - acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_STATUSIN: /* STATE: status in complete */ - switch (ssr) { - case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ - case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ - /* STATUS -> MESSAGE IN */ - acornscsi_message(host); - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* STATUS -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_MSGIN: /* STATE: message in */ - switch (ssr) { - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x4e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ - case 0x2f: - case 0x4f: - case 0x8f: - acornscsi_message(host); - break; - - case 0x85: - printk("scsi%d.%c: strange message in disconnection\n", - host->host->host_no, acornscsi_target(host)); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - acornscsi_done(host, &host->SCpnt, DID_ERROR); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DONE: /* STATE: received status & message */ - switch (ssr) { - case 0x85: /* -> PHASE_IDLE */ - acornscsi_done(host, &host->SCpnt, DID_OK); - return INTR_NEXT_COMMAND; - - case 0x1e: - case 0x8e: - acornscsi_sendmessage(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_ABORTED: - switch (ssr) { - case 0x85: - if (host->SCpnt) - acornscsi_done(host, &host->SCpnt, DID_ABORT); - else { - clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun, - host->busyluns); - host->scsi.phase = PHASE_IDLE; - } - return INTR_NEXT_COMMAND; - - case 0x1e: - case 0x2e: - case 0x4e: - case 0x8e: - acornscsi_sendmessage(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - default: - printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; -} - -/* - * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) - * Purpose : handle interrupts from Acorn SCSI card - * Params : irq - interrupt number - * dev_id - device specific data (AS_Host structure) - * regs - processor registers when interrupt occurred - */ -static irqreturn_t -acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - AS_Host *host = (AS_Host *)dev_id; - intr_ret_t ret; - int iostatus; - int in_irq = 0; - - do { - ret = INTR_IDLE; - - iostatus = inb(host->card.io_intr); - - if (iostatus & 2) { - acornscsi_dma_intr(host); - iostatus = inb(host->card.io_intr); - } - - if (iostatus & 8) - ret = acornscsi_sbicintr(host, in_irq); - - /* - * If we have a transfer pending, start it. - * Only start it if the interface has already started transferring - * it's data - */ - if (host->dma.xfer_required) - acornscsi_dma_xfer(host); - - if (ret == INTR_NEXT_COMMAND) - ret = acornscsi_kick(host); - - in_irq = 1; - } while (ret != INTR_IDLE); - - return IRQ_HANDLED; -} - -/*============================================================================================= - * Interfaces between interrupt handler and rest of scsi code - */ - -/* - * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) - * Purpose : queues a SCSI command - * Params : cmd - SCSI command - * done - function called on completion, with pointer to command descriptor - * Returns : 0, or < 0 on error. - */ -int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) -{ - AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; - - if (!done) { - /* there should be some way of rejecting errors like this without panicing... */ - panic("scsi%d: queuecommand called with NULL done function [cmd=%p]", - host->host->host_no, SCpnt); - return -EINVAL; - } - -#if (DEBUG & DEBUG_NO_WRITE) - if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) { - printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", - host->host->host_no, '0' + SCpnt->device->id); - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 0; - } -#endif - - SCpnt->scsi_done = done; - SCpnt->host_scribble = NULL; - SCpnt->result = 0; - SCpnt->tag = 0; - SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]); - SCpnt->SCp.sent_command = 0; - SCpnt->SCp.scsi_xferred = 0; - - init_SCp(SCpnt); - - host->stats.queues += 1; - - { - unsigned long flags; - - if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) { - SCpnt->result = DID_ERROR << 16; - done(SCpnt); - return 0; - } - local_irq_save(flags); - if (host->scsi.phase == PHASE_IDLE) - acornscsi_kick(host); - local_irq_restore(flags); - } - return 0; -} - -/* - * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) - * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 - * Params : SCpntp1 - pointer to command to return - * SCpntp2 - pointer to command to check - * result - result to pass back to mid-level done function - * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. - */ -static inline -void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) -{ - Scsi_Cmnd *SCpnt = *SCpntp1; - - if (SCpnt) { - *SCpntp1 = NULL; - - SCpnt->result = result; - SCpnt->scsi_done(SCpnt); - } - - if (SCpnt == *SCpntp2) - *SCpntp2 = NULL; -} - -enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; - -/* - * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort a command on this host - * Params : SCpnt - command to abort - * Returns : our abort status - */ -static enum res_abort -acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) -{ - enum res_abort res = res_not_running; - - if (queue_remove_cmd(&host->queues.issue, SCpnt)) { - /* - * The command was on the issue queue, and has not been - * issued yet. We can remove the command from the queue, - * and acknowledge the abort. Neither the devices nor the - * interface know about the command. - */ -//#if (DEBUG & DEBUG_ABORT) - printk("on issue queue "); -//#endif - res = res_success; - } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) { - /* - * The command was on the disconnected queue. Simply - * acknowledge the abort condition, and when the target - * reconnects, we will give it an ABORT message. The - * target should then disconnect, and we will clear - * the busylun bit. - */ -//#if (DEBUG & DEBUG_ABORT) - printk("on disconnected queue "); -//#endif - res = res_success; - } else if (host->SCpnt == SCpnt) { - unsigned long flags; - -//#if (DEBUG & DEBUG_ABORT) - printk("executing "); -//#endif - - local_irq_save(flags); - switch (host->scsi.phase) { - /* - * If the interface is idle, and the command is 'disconnectable', - * then it is the same as on the disconnected queue. We simply - * remove all traces of the command. When the target reconnects, - * we will give it an ABORT message since the command could not - * be found. When the target finally disconnects, we will clear - * the busylun bit. - */ - case PHASE_IDLE: - if (host->scsi.disconnectable) { - host->scsi.disconnectable = 0; - host->SCpnt = NULL; - res = res_success; - } - break; - - /* - * If the command has connected and done nothing further, - * simply force a disconnect. We also need to clear the - * busylun bit. - */ - case PHASE_CONNECTED: - sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT); - host->SCpnt = NULL; - res = res_success_clear; - break; - - default: - acornscsi_abortcmd(host, host->SCpnt->tag); - res = res_snooze; - } - local_irq_restore(flags); - } else if (host->origSCpnt == SCpnt) { - /* - * The command will be executed next, but a command - * is currently using the interface. This is similar to - * being on the issue queue, except the busylun bit has - * been set. - */ - host->origSCpnt = NULL; -//#if (DEBUG & DEBUG_ABORT) - printk("waiting for execution "); -//#endif - res = res_success_clear; - } else - printk("unknown "); - - return res; -} - -/* - * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort a command on this host - * Params : SCpnt - command to abort - * Returns : one of SCSI_ABORT_ macros - */ -int acornscsi_abort(Scsi_Cmnd *SCpnt) -{ - AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata; - int result; - - host->stats.aborts += 1; - -#if (DEBUG & DEBUG_ABORT) - { - int asr, ssr; - asr = sbic_arm_read(host->scsi.io_port, ASR); - ssr = sbic_arm_read(host->scsi.io_port, SSR); - - printk(KERN_WARNING "acornscsi_abort: "); - print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog(host, SCpnt->device->id); - } -#endif - - printk("scsi%d: ", host->host->host_no); - - switch (acornscsi_do_abort(host, SCpnt)) { - /* - * We managed to find the command and cleared it out. - * We do not expect the command to be executing on the - * target, but we have set the busylun bit. - */ - case res_success_clear: -//#if (DEBUG & DEBUG_ABORT) - printk("clear "); -//#endif - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); - - /* - * We found the command, and cleared it out. Either - * the command is still known to be executing on the - * target, or the busylun bit is not set. - */ - case res_success: -//#if (DEBUG & DEBUG_ABORT) - printk("success\n"); -//#endif - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done(SCpnt); - result = SCSI_ABORT_SUCCESS; - break; - - /* - * We did find the command, but unfortunately we couldn't - * unhook it from ourselves. Wait some more, and if it - * still doesn't complete, reset the interface. - */ - case res_snooze: -//#if (DEBUG & DEBUG_ABORT) - printk("snooze\n"); -//#endif - result = SCSI_ABORT_SNOOZE; - break; - - /* - * The command could not be found (either because it completed, - * or it got dropped. - */ - default: - case res_not_running: - acornscsi_dumplog(host, SCpnt->device->id); -#if (DEBUG & DEBUG_ABORT) - result = SCSI_ABORT_SNOOZE; -#else - result = SCSI_ABORT_NOT_RUNNING; -#endif -//#if (DEBUG & DEBUG_ABORT) - printk("not running\n"); -//#endif - break; - } - - return result; -} - -/* - * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) - * Purpose : reset a command on this host/reset this host - * Params : SCpnt - command causing reset - * result - what type of reset to perform - * Returns : one of SCSI_RESET_ macros - */ -int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) -{ - AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; - Scsi_Cmnd *SCptr; - - host->stats.resets += 1; - -#if (DEBUG & DEBUG_RESET) - { - int asr, ssr; - - asr = sbic_arm_read(host->scsi.io_port, ASR); - ssr = sbic_arm_read(host->scsi.io_port, SSR); - - printk(KERN_WARNING "acornscsi_reset: "); - print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog(host, SCpnt->device->id); - } -#endif - - acornscsi_dma_stop(host); - - SCptr = host->SCpnt; - - /* - * do hard reset. This resets all devices on this host, and so we - * must set the reset status on all commands. - */ - acornscsi_resetcard(host); - - /* - * report reset on commands current connected/disconnected - */ - acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET); - - while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL) - acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET); - - if (SCpnt) { - SCpnt->result = DID_RESET << 16; - SCpnt->scsi_done(SCpnt); - } - - return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS; -} - -/*============================================================================================== - * initialisation & miscellaneous support - */ - -/* - * Function: char *acornscsi_info(struct Scsi_Host *host) - * Purpose : return a string describing this interface - * Params : host - host to give information on - * Returns : a constant string - */ -const -char *acornscsi_info(struct Scsi_Host *host) -{ - static char string[100], *p; - - p = string; - - p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d" -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - " SYNC" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - " TAG" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_LINK - " LINK" -#endif -#if (DEBUG & DEBUG_NO_WRITE) - " NOWRITE ("NO_WRITE_STR")" -#endif - , host->hostt->name, host->io_port, host->irq, - VER_MAJOR, VER_MINOR, VER_PATCH); - return string; -} - -int acornscsi_proc_info(char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - int pos, begin = 0, devidx; - struct Scsi_Host *instance; - Scsi_Device *scd; - AS_Host *host; - char *p = buffer; - - instance = scsi_host_hn_get(host_no); - - if (inout == 1 || !instance) - return -EINVAL; - - host = (AS_Host *)instance->hostdata; - - p += sprintf(p, "AcornSCSI driver v%d.%d.%d" -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - " SYNC" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - " TAG" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_LINK - " LINK" -#endif -#if (DEBUG & DEBUG_NO_WRITE) - " NOWRITE ("NO_WRITE_STR")" -#endif - "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH); - - p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", - host->scsi.io_port, host->scsi.irq); -#ifdef USE_DMAC - p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", - host->dma.io_port, host->scsi.irq); -#endif - - p += sprintf(p, "Statistics:\n" - "Queued commands: %-10u Issued commands: %-10u\n" - "Done commands : %-10u Reads : %-10u\n" - "Writes : %-10u Others : %-10u\n" - "Disconnects : %-10u Aborts : %-10u\n" - "Resets : %-10u\n\nLast phases:", - host->stats.queues, host->stats.removes, - host->stats.fins, host->stats.reads, - host->stats.writes, host->stats.miscs, - host->stats.disconnects, host->stats.aborts, - host->stats.resets); - - for (devidx = 0; devidx < 9; devidx ++) { - unsigned int statptr, prev; - - p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); - statptr = host->status_ptr[devidx] - 10; - - if ((signed int)statptr < 0) - statptr += STATUS_BUFFER_SIZE; - - prev = host->status[devidx][statptr].when; - - for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) { - if (host->status[devidx][statptr].when) { - p += sprintf(p, "%c%02X:%02X+%2ld", - host->status[devidx][statptr].irq ? '-' : ' ', - host->status[devidx][statptr].ph, - host->status[devidx][statptr].ssr, - (host->status[devidx][statptr].when - prev) < 100 ? - (host->status[devidx][statptr].when - prev) : 99); - prev = host->status[devidx][statptr].when; - } - } - } - - pos = p - buffer; - - *start = buffer + (offset - begin); - pos -= offset - begin; - - if (pos > length) - pos = length; - - return pos; -} - -static Scsi_Host_Template acornscsi_template = { - .module = THIS_MODULE, - .proc_info = acornscsi_proc_info, - .name = "AcornSCSI", - .info = acornscsi_info, - .queuecommand = acornscsi_queuecmd, -#warning fixme - .abort = acornscsi_abort, - .reset = acornscsi_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .unchecked_isa_dma = 0, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "acornscsi", -}; - -static int __devinit -acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - AS_Host *ashost; - int ret = -ENOMEM; - - host = scsi_register(&acornscsi_template, sizeof(AS_Host)); - if (!host) - goto out; - - ashost = (AS_Host *)host->hostdata; - - host->io_port = ecard_address(ec, ECARD_MEMC, 0); - host->irq = ec->irq; - - ashost->host = host; - ashost->scsi.io_port = ioaddr(host->io_port + 0x800); - ashost->scsi.irq = host->irq; - ashost->card.io_intr = POD_SPACE(host->io_port) + 0x800; - ashost->card.io_page = POD_SPACE(host->io_port) + 0xc00; - ashost->card.io_ram = ioaddr(host->io_port); - ashost->dma.io_port = host->io_port + 0xc00; - ashost->dma.io_intr_clear = POD_SPACE(host->io_port) + 0x800; - - ec->irqaddr = (char *)ioaddr(ashost->card.io_intr); - ec->irqmask = 0x0a; - - ret = -EBUSY; - if (!request_region(host->io_port + 0x800, 2, "acornscsi(sbic)")) - goto err_1; - if (!request_region(ashost->card.io_intr, 1, "acornscsi(intr)")) - goto err_2; - if (!request_region(ashost->card.io_page, 1, "acornscsi(page)")) - goto err_3; -#ifdef USE_DMAC - if (!request_region(ashost->dma.io_port, 256, "acornscsi(dmac)")) - goto err_4; -#endif - if (!request_region(host->io_port, 2048, "acornscsi(ram)")) - goto err_5; - - ret = request_irq(host->irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", ashost); - if (ret) { - printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n", - host->host_no, ashost->scsi.irq, ret); - goto err_6; - } - - memset(&ashost->stats, 0, sizeof (ashost->stats)); - queue_initialise(&ashost->queues.issue); - queue_initialise(&ashost->queues.disconnected); - msgqueue_initialise(&ashost->scsi.msgs); - - acornscsi_resetcard(ashost); - - ret = scsi_add_host(host, &ec->dev); - if (ret == 0) - goto out; - - free_irq(host->irq, ashost); - err_6: - release_region(host->io_port, 2048); - err_5: -#ifdef USE_DMAC - release_region(ashost->dma.io_port, 256); -#endif - err_4: - release_region(ashost->card.io_page, 1); - err_3: - release_region(ashost->card.io_intr, 1); - err_2: - release_region(host->io_port + 0x800, 2); - err_1: - scsi_unregister(host); - out: - return ret; -} - -static void __devexit acornscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - AS_Host *ashost = (AS_Host *)host->hostdata; - - ecard_set_drvdata(ec, NULL); - scsi_remove_host(host); - - /* - * Put card into RESET state - */ - outb(0x80, ashost->card.io_page); - - free_irq(host->irq, ashost); - - release_region(host->io_port + 0x800, 2); - release_region(ashost->card.io_intr, 1); - release_region(ashost->card.io_page, 1); - release_region(ashost->dma.io_port, 256); - release_region(host->io_port, 2048); - - msgqueue_free(&ashost->scsi.msgs); - queue_free(&ashost->queues.disconnected); - queue_free(&ashost->queues.issue); -} - -static const struct ecard_id acornscsi_cids[] = { - { MANU_ACORN, PROD_ACORN_SCSI }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver acornscsi_driver = { - .probe = acornscsi_probe, - .remove = __devexit_p(acornscsi_remove), - .id_table = acornscsi_cids, - .drv = { - .name = "acornscsi", - }, -}; - -static int __init acornscsi_init(void) -{ - return ecard_register_driver(&acornscsi_driver); -} - -static void __exit acornscsi_exit(void) -{ - ecard_remove_driver(&acornscsi_driver); -} - -module_init(acornscsi_init); -module_exit(acornscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("AcornSCSI driver"); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/acornscsi.h linux-2.5.69-bk14/drivers/acorn/scsi/acornscsi.h --- linux-2.5.69-bk13/drivers/acorn/scsi/acornscsi.h 2003-05-04 16:53:13.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/acornscsi.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,359 +0,0 @@ -/* - * linux/drivers/acorn/scsi/acornscsi.h - * - * Copyright (C) 1997 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Acorn SCSI driver - */ -#ifndef ACORNSCSI_H -#define ACORNSCSI_H - -/* SBIC registers */ -#define OWNID 0 -#define OWNID_FS1 (1<<7) -#define OWNID_FS2 (1<<6) -#define OWNID_EHP (1<<4) -#define OWNID_EAF (1<<3) - -#define CTRL 1 -#define CTRL_DMAMODE (1<<7) -#define CTRL_DMADBAMODE (1<<6) -#define CTRL_DMABURST (1<<5) -#define CTRL_DMAPOLLED 0 -#define CTRL_HHP (1<<4) -#define CTRL_EDI (1<<3) -#define CTRL_IDI (1<<2) -#define CTRL_HA (1<<1) -#define CTRL_HSP (1<<0) - -#define TIMEOUT 2 -#define TOTSECTS 3 -#define TOTHEADS 4 -#define TOTCYLH 5 -#define TOTCYLL 6 -#define LOGADDRH 7 -#define LOGADDRM2 8 -#define LOGADDRM1 9 -#define LOGADDRL 10 -#define SECTORNUM 11 -#define HEADNUM 12 -#define CYLH 13 -#define CYLL 14 -#define TARGETLUN 15 -#define TARGETLUN_TLV (1<<7) -#define TARGETLUN_DOK (1<<6) - -#define CMNDPHASE 16 -#define SYNCHTRANSFER 17 -#define SYNCHTRANSFER_OF0 0x00 -#define SYNCHTRANSFER_OF1 0x01 -#define SYNCHTRANSFER_OF2 0x02 -#define SYNCHTRANSFER_OF3 0x03 -#define SYNCHTRANSFER_OF4 0x04 -#define SYNCHTRANSFER_OF5 0x05 -#define SYNCHTRANSFER_OF6 0x06 -#define SYNCHTRANSFER_OF7 0x07 -#define SYNCHTRANSFER_OF8 0x08 -#define SYNCHTRANSFER_OF9 0x09 -#define SYNCHTRANSFER_OF10 0x0A -#define SYNCHTRANSFER_OF11 0x0B -#define SYNCHTRANSFER_OF12 0x0C -#define SYNCHTRANSFER_8DBA 0x00 -#define SYNCHTRANSFER_2DBA 0x20 -#define SYNCHTRANSFER_3DBA 0x30 -#define SYNCHTRANSFER_4DBA 0x40 -#define SYNCHTRANSFER_5DBA 0x50 -#define SYNCHTRANSFER_6DBA 0x60 -#define SYNCHTRANSFER_7DBA 0x70 - -#define TRANSCNTH 18 -#define TRANSCNTM 19 -#define TRANSCNTL 20 -#define DESTID 21 -#define DESTID_SCC (1<<7) -#define DESTID_DPD (1<<6) - -#define SOURCEID 22 -#define SOURCEID_ER (1<<7) -#define SOURCEID_ES (1<<6) -#define SOURCEID_DSP (1<<5) -#define SOURCEID_SIV (1<<4) - -#define SSR 23 -#define CMND 24 -#define CMND_RESET 0x00 -#define CMND_ABORT 0x01 -#define CMND_ASSERTATN 0x02 -#define CMND_NEGATEACK 0x03 -#define CMND_DISCONNECT 0x04 -#define CMND_RESELECT 0x05 -#define CMND_SELWITHATN 0x06 -#define CMND_SELECT 0x07 -#define CMND_SELECTATNTRANSFER 0x08 -#define CMND_SELECTTRANSFER 0x09 -#define CMND_RESELECTRXDATA 0x0A -#define CMND_RESELECTTXDATA 0x0B -#define CMND_WAITFORSELRECV 0x0C -#define CMND_SENDSTATCMD 0x0D -#define CMND_SENDDISCONNECT 0x0E -#define CMND_SETIDI 0x0F -#define CMND_RECEIVECMD 0x10 -#define CMND_RECEIVEDTA 0x11 -#define CMND_RECEIVEMSG 0x12 -#define CMND_RECEIVEUSP 0x13 -#define CMND_SENDCMD 0x14 -#define CMND_SENDDATA 0x15 -#define CMND_SENDMSG 0x16 -#define CMND_SENDUSP 0x17 -#define CMND_TRANSLATEADDR 0x18 -#define CMND_XFERINFO 0x20 -#define CMND_SBT (1<<7) - -#define DATA 25 -#define ASR 26 -#define ASR_INT (1<<7) -#define ASR_LCI (1<<6) -#define ASR_BSY (1<<5) -#define ASR_CIP (1<<4) -#define ASR_PE (1<<1) -#define ASR_DBR (1<<0) - -/* DMAC registers */ -#define INIT 0x00 -#define INIT_8BIT (1) - -#define CHANNEL 0x80 -#define CHANNEL_0 0x00 -#define CHANNEL_1 0x01 -#define CHANNEL_2 0x02 -#define CHANNEL_3 0x03 - -#define TXCNTLO 0x01 -#define TXCNTHI 0x81 -#define TXADRLO 0x02 -#define TXADRMD 0x82 -#define TXADRHI 0x03 - -#define DEVCON0 0x04 -#define DEVCON0_AKL (1<<7) -#define DEVCON0_RQL (1<<6) -#define DEVCON0_EXW (1<<5) -#define DEVCON0_ROT (1<<4) -#define DEVCON0_CMP (1<<3) -#define DEVCON0_DDMA (1<<2) -#define DEVCON0_AHLD (1<<1) -#define DEVCON0_MTM (1<<0) - -#define DEVCON1 0x84 -#define DEVCON1_WEV (1<<1) -#define DEVCON1_BHLD (1<<0) - -#define MODECON 0x05 -#define MODECON_WOED 0x01 -#define MODECON_VERIFY 0x00 -#define MODECON_READ 0x04 -#define MODECON_WRITE 0x08 -#define MODECON_AUTOINIT 0x10 -#define MODECON_ADDRDIR 0x20 -#define MODECON_DEMAND 0x00 -#define MODECON_SINGLE 0x40 -#define MODECON_BLOCK 0x80 -#define MODECON_CASCADE 0xC0 - -#define STATUS 0x85 -#define STATUS_TC0 (1<<0) -#define STATUS_RQ0 (1<<4) - -#define TEMPLO 0x06 -#define TEMPHI 0x86 -#define REQREG 0x07 -#define MASKREG 0x87 -#define MASKREG_M0 0x01 -#define MASKREG_M1 0x02 -#define MASKREG_M2 0x04 -#define MASKREG_M3 0x08 - -/* miscellaneous internal variables */ - -#define POD_SPACE(x) ((x) + 0xd0000) -#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0) -#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1) - -/* - * SCSI driver phases - */ -typedef enum { - PHASE_IDLE, /* we're not planning on doing anything */ - PHASE_CONNECTING, /* connecting to a target */ - PHASE_CONNECTED, /* connected to a target */ - PHASE_MSGOUT, /* message out to device */ - PHASE_RECONNECTED, /* reconnected */ - PHASE_COMMANDPAUSED, /* command partly sent */ - PHASE_COMMAND, /* command all sent */ - PHASE_DATAOUT, /* data out to device */ - PHASE_DATAIN, /* data in from device */ - PHASE_STATUSIN, /* status in from device */ - PHASE_MSGIN, /* message in from device */ - PHASE_DONE, /* finished */ - PHASE_ABORTED, /* aborted */ - PHASE_DISCONNECT, /* disconnecting */ -} phase_t; - -/* - * After interrupt, what to do now - */ -typedef enum { - INTR_IDLE, /* not expecting another IRQ */ - INTR_NEXT_COMMAND, /* start next command */ - INTR_PROCESSING, /* interrupt routine still processing */ -} intr_ret_t; - -/* - * DMA direction - */ -typedef enum { - DMA_OUT, /* DMA from memory to chip */ - DMA_IN /* DMA from chip to memory */ -} dmadir_t; - -/* - * Synchronous transfer state - */ -typedef enum { /* Synchronous transfer state */ - SYNC_ASYNCHRONOUS, /* don't negociate synchronous transfers*/ - SYNC_NEGOCIATE, /* start negociation */ - SYNC_SENT_REQUEST, /* sent SDTR message */ - SYNC_COMPLETED, /* received SDTR reply */ -} syncxfer_t; - -/* - * Command type - */ -typedef enum { /* command type */ - CMD_READ, /* READ_6, READ_10, READ_12 */ - CMD_WRITE, /* WRITE_6, WRITE_10, WRITE_12 */ - CMD_MISC, /* Others */ -} cmdtype_t; - -/* - * Data phase direction - */ -typedef enum { /* Data direction */ - DATADIR_IN, /* Data in phase expected */ - DATADIR_OUT /* Data out phase expected */ -} datadir_t; - -#include "queue.h" -#include "msgqueue.h" - -#define STATUS_BUFFER_SIZE 32 -/* - * This is used to dump the previous states of the SBIC - */ -struct status_entry { - unsigned long when; - unsigned char ssr; - unsigned char ph; - unsigned char irq; - unsigned char unused; -}; - -#define ADD_STATUS(_q,_ssr,_ph,_irq) \ -({ \ - host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \ - host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \ - host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \ - host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \ - host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \ -}) - -/* - * AcornSCSI host specific data - */ -typedef struct acornscsi_hostdata { - /* miscellaneous */ - struct Scsi_Host *host; /* host */ - Scsi_Cmnd *SCpnt; /* currently processing command */ - Scsi_Cmnd *origSCpnt; /* original connecting command */ - - /* driver information */ - struct { - unsigned int io_port; /* base address of WD33C93 */ - unsigned int irq; /* interrupt */ - phase_t phase; /* current phase */ - - struct { - unsigned char target; /* reconnected target */ - unsigned char lun; /* reconnected lun */ - unsigned char tag; /* reconnected tag */ - } reconnected; - - Scsi_Pointer SCp; /* current commands data pointer */ - - MsgQueue_t msgs; - - unsigned short last_message; /* last message to be sent */ - unsigned char disconnectable:1; /* this command can be disconnected */ - unsigned char interrupt:1; /* interrupt active */ - } scsi; - - /* statistics information */ - struct { - unsigned int queues; - unsigned int removes; - unsigned int fins; - unsigned int reads; - unsigned int writes; - unsigned int miscs; - unsigned int disconnects; - unsigned int aborts; - unsigned int resets; - } stats; - - /* queue handling */ - struct { - Queue_t issue; /* issue queue */ - Queue_t disconnected; /* disconnected command queue */ - } queues; - - /* per-device info */ - struct { - unsigned char sync_xfer; /* synchronous transfer (SBIC value) */ - syncxfer_t sync_state; /* sync xfer negociation state */ - unsigned char disconnect_ok:1; /* device can disconnect */ - } device[8]; - unsigned long busyluns[64 / sizeof(unsigned long)];/* array of bits indicating LUNs busy */ - - /* DMA info */ - struct { - unsigned int io_port; /* base address of DMA controller */ - unsigned int io_intr_clear; /* address of DMA interrupt clear */ - unsigned int free_addr; /* next free address */ - unsigned int start_addr; /* start address of current transfer */ - dmadir_t direction; /* dma direction */ - unsigned int transferred; /* number of bytes transferred */ - unsigned int xfer_start; /* scheduled DMA transfer start */ - unsigned int xfer_length; /* scheduled DMA transfer length */ - char *xfer_ptr; /* pointer to area */ - unsigned char xfer_required:1; /* set if we need to transfer something */ - unsigned char xfer_setup:1; /* set if DMA is setup */ - unsigned char xfer_done:1; /* set if DMA reached end of BH list */ - } dma; - - /* card info */ - struct { - unsigned int io_intr; /* base address of interrupt id reg */ - unsigned int io_page; /* base address of page reg */ - unsigned int io_ram; /* base address of RAM access */ - unsigned char page_reg; /* current setting of page reg */ - } card; - - unsigned char status_ptr[9]; - struct status_entry status[9][STATUS_BUFFER_SIZE]; -} AS_Host; - -#endif /* ACORNSCSI_H */ diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/arxescsi.c linux-2.5.69-bk14/drivers/acorn/scsi/arxescsi.c --- linux-2.5.69-bk13/drivers/acorn/scsi/arxescsi.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/arxescsi.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,420 +0,0 @@ -/* - * linux/arch/arm/drivers/scsi/arxescsi.c - * - * Copyright (C) 1997-2000 Russell King, Stefan Hanske - * - * This driver is based on experimentation. Hence, it may have made - * assumptions about the particular card that I have available, and - * may not be reliable! - * - * Changelog: - * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c - * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 - * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. - * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card - * enabled writing - * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing - * (arxescsi_pseudo_dma_write) - * 02-04-2000 RMK 0.1.1 Updated for new error handling code. - * 22-10-2000 SH Updated for new registering scheme. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" - -struct arxescsi_info { - FAS216_Info info; - struct expansion_card *ec; -}; - -#define DMADATA_OFFSET (0x200) - -#define DMASTAT_OFFSET (0x600) -#define DMASTAT_DRQ (1 << 0) - -#define CSTATUS_IRQ (1 << 0) - -#define VERSION "1.10 (23/01/2003 2.5.57)" - -/* - * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : 0 if we should not set CMD_WITHDMA for transfer info command - */ -static fasdmatype_t -arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - /* - * We don't do real DMA - */ - return fasdma_pseudo; -} - -static void arxescsi_pseudo_dma_write(unsigned char *addr, unsigned char *base) -{ - __asm__ __volatile__( - " stmdb sp!, {r0-r12}\n" - " mov r3, %0\n" - " mov r1, %1\n" - " add r2, r1, #512\n" - " mov r4, #256\n" - ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n" - " mov r5, r6, lsl #16\n" - " mov r7, r8, lsl #16\n" - ".loop_2: ldrb r0, [r1, #1536]\n" - " tst r0, #1\n" - " beq .loop_2\n" - " stmia r2, {r5-r8}\n\t" - " mov r9, r10, lsl #16\n" - " mov r11, r12, lsl #16\n" - ".loop_3: ldrb r0, [r1, #1536]\n" - " tst r0, #1\n" - " beq .loop_3\n" - " stmia r2, {r9-r12}\n" - " subs r4, r4, #16\n" - " bne .loop_1\n" - " ldmia sp!, {r0-r12}\n" - : - : "r" (addr), "r" (base)); -} - -/* - * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) - * Purpose : handles pseudo DMA - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * transfer - minimum number of bytes we expect to transfer - */ -static void -arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, int transfer) -{ - struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; - unsigned int length, error = 0; - unsigned char *base = info->info.scsi.io_base; - unsigned char *addr; - - length = SCp->this_residual; - addr = SCp->ptr; - - if (direction == DMA_OUT) { - unsigned int word; - while (length > 256) { - if (readb(base + 0x80) & STAT_INT) { - error = 1; - break; - } - arxescsi_pseudo_dma_write(addr, base); - addr += 256; - length -= 256; - } - - if (!error) - while (length > 0) { - if (readb(base + 0x80) & STAT_INT) - break; - - if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) - continue; - - word = *addr | *(addr + 1) << 8; - - writew(word, base + DMADATA_OFFSET); - if (length > 1) { - addr += 2; - length -= 2; - } else { - addr += 1; - length -= 1; - } - } - } - else { - if (transfer && (transfer & 255)) { - while (length >= 256) { - if (readb(base + 0x80) & STAT_INT) { - error = 1; - break; - } - - if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) - continue; - - readsw(base + DMADATA_OFFSET, addr, 256 >> 1); - addr += 256; - length -= 256; - } - } - - if (!(error)) - while (length > 0) { - unsigned long word; - - if (readb(base + 0x80) & STAT_INT) - break; - - if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) - continue; - - word = readw(base + DMADATA_OFFSET); - *addr++ = word; - if (--length > 0) { - *addr++ = word >> 8; - length --; - } - } - } -} - -/* - * Function: int arxescsi_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - /* - * no DMA to stop - */ -} - -/* - * Function: const char *arxescsi_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -static const char *arxescsi_info(struct Scsi_Host *host) -{ - struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION); - - return string; -} - -/* - * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -static int -arxescsi_proc_info(char *buffer, char **start, off_t offset, int length, - int host_no, int inout) -{ - int pos, begin; - struct Scsi_Host *host; - struct arxescsi_info *info; - Scsi_Device *scd; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - info = (struct arxescsi_info *)host->hostdata; - if (inout == 1) - return -EINVAL; - - begin = 0; - pos = sprintf(buffer, "ARXE 16-bit SCSI driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += fas216_print_stats(&info->info, buffer + pos); - - pos += sprintf (buffer+pos, "\nAttached devices:\n"); - - list_for_each_entry(scd, &host->my_devices, siblings) { - pos += fas216_print_device(&info->info, scd, buffer + pos); - - if (pos + begin < offset) { - begin += pos; - pos = 0; - } - if (pos + begin > offset + length) - break; - } - - *start = buffer + (offset - begin); - pos -= offset - begin; - if (pos > length) - pos = length; - - return pos; -} - -static Scsi_Host_Template arxescsi_template = { - .proc_info = arxescsi_proc_info, - .name = "ARXE SCSI card", - .info = arxescsi_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - .can_queue = 0, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "arxescsi", -}; - -static int __devinit -arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct arxescsi_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - if (!request_mem_region(resbase, reslen, "arxescsi")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&arxescsi_template, sizeof(struct arxescsi_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = NO_IRQ; - host->dma_channel = NO_DMA; - - info = (struct arxescsi_info *)host->hostdata; - info->ec = ec; - - info->info.scsi.io_base = base + 0x2000; - info->info.scsi.irq = host->irq; - info->info.scsi.io_shift = 5; - info->info.ifcfg.clockrate = 24; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 0; - info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 0; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; - info->info.dma.setup = arxescsi_dma_setup; - info->info.dma.pseudo = arxescsi_dma_pseudo; - info->info.dma.stop = arxescsi_dma_stop; - - ec->irqaddr = base; - ec->irqmask = CSTATUS_IRQ; - - ret = fas216_init(host); - if (ret) - goto out_unregister; - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - fas216_release(host); - out_unregister: - scsi_unregister(host); - out_unmap: - iounmap(base); - out_region: - release_mem_region(resbase, reslen); - out: - return ret; -} - -static void __devexit arxescsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id arxescsi_cids[] = { - { MANU_ARXE, PROD_ARXE_SCSI }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver arxescsi_driver = { - .probe = arxescsi_probe, - .remove = __devexit_p(arxescsi_remove), - .id_table = arxescsi_cids, - .drv = { - .name = "arxescsi", - }, -}; - -static int __init init_arxe_scsi_driver(void) -{ - return ecard_register_driver(&arxescsi_driver); -} - -static void __exit exit_arxe_scsi_driver(void) -{ - ecard_remove_driver(&arxescsi_driver); -} - -module_init(init_arxe_scsi_driver); -module_exit(exit_arxe_scsi_driver); - -MODULE_AUTHOR("Stefan Hanske"); -MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines"); -MODULE_LICENSE("GPL"); - diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/cumana_1.c linux-2.5.69-bk14/drivers/acorn/scsi/cumana_1.c --- linux-2.5.69-bk13/drivers/acorn/scsi/cumana_1.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/cumana_1.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,355 +0,0 @@ -/* - * Generic Generic NCR5380 driver - * - * Copyright 1995-2002, Russell King - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" - -#include - -#define AUTOSENSE -#define PSEUDO_DMA - -#define CUMANASCSI_PUBLIC_RELEASE 1 - -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance -#define NCR5380_read(reg) cumanascsi_read(_instance, reg) -#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value) -#define NCR5380_intr cumanascsi_intr -#define NCR5380_queue_command cumanascsi_queue_command -#define NCR5380_proc_info cumanascsi_proc_info - -int NCR5380_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -#define BOARD_NORMAL 0 -#define BOARD_NCR53C400 1 - -#include "../../scsi/NCR5380.h" - -void cumanascsi_setup(char *str, int *ints) -{ -} - -const char *cumanascsi_info(struct Scsi_Host *spnt) -{ - return ""; -} - -#ifdef NOT_EFFICIENT -#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) -#define STAT(p) inb((p)+1) -#define IN(p) inb((p)) -#define OUT(v,p) outb((v), (p)) -#else -#define CTRL(p,v) (p[-2308] = (*ctrl = (v))) -#define STAT(p) (p[4]) -#define IN(p) (*(p)) -#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p))) -#define OUT(v,p) (*(p) = (v)) -#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v)) -#endif -#define L(v) (((v)<<16)|((v) & 0x0000ffff)) -#define H(v) (((v)>>16)|((v) & 0xffff0000)) - -static inline int -NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len) -{ - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - int oldctrl = *ctrl; - unsigned long *laddr; -#ifdef NOT_EFFICIENT - int iobase = instance->io_port; - int dma_io = iobase & ~(0x3C0000>>2); -#else - volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); - volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); -#endif - - if(!len) return 0; - - CTRL(iobase, 0x02); - laddr = (unsigned long *)addr; - while(len >= 32) - { - int status; - unsigned long v; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(!(status & 0x40)) - continue; - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - len -= 32; - if(len == 0) - break; - } - - addr = (unsigned char *)laddr; - CTRL(iobase, 0x12); - while(len > 0) - { - int status; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - OUT(*addr++, dma_io); - if(--len == 0) - break; - } - - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - OUT(*addr++, dma_io); - if(--len == 0) - break; - } - } -end: - CTRL(iobase, oldctrl|0x40); - return len; -} - -static inline int -NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len) -{ - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - int oldctrl = *ctrl; - unsigned long *laddr; -#ifdef NOT_EFFICIENT - int iobase = instance->io_port; - int dma_io = iobase & ~(0x3C0000>>2); -#else - volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); - volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); -#endif - - if(!len) return 0; - - CTRL(iobase, 0x00); - laddr = (unsigned long *)addr; - while(len >= 32) - { - int status; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(!(status & 0x40)) - continue; - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - len -= 32; - if(len == 0) - break; - } - - addr = (unsigned char *)laddr; - CTRL(iobase, 0x10); - while(len > 0) - { - int status; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - *addr++ = IN(dma_io); - if(--len == 0) - break; - } - - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - *addr++ = IN(dma_io); - if(--len == 0) - break; - } - } -end: - CTRL(iobase, oldctrl|0x40); - return len; -} - -#undef STAT -#undef CTRL -#undef IN -#undef OUT - -#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) - -static char cumanascsi_read(struct Scsi_Host *instance, int reg) -{ - unsigned int iobase = instance->io_port; - int i; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - - CTRL(iobase, 0); - i = inb(iobase + 64 + reg); - CTRL(iobase, 0x40); - - return i; -} - -static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value) -{ - int iobase = instance->io_port; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - - CTRL(iobase, 0); - outb(value, iobase + 64 + reg); - CTRL(iobase, 0x40); -} - -#undef CTRL - -#include "../../scsi/NCR5380.c" - -static Scsi_Host_Template cumanascsi_template = { - .module = THIS_MODULE, - .name = "Cumana 16-bit SCSI", - .info = cumanascsi_info, - .queuecommand = cumanascsi_queue_command, - .eh_abort_handler = NCR5380_abort, - .eh_device_reset_handler= NCR5380_device_reset, - .eh_bus_reset_handler = NCR5380_bus_reset, - .eh_host_reset_handler = NCR5380_host_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .unchecked_isa_dma = 0, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "CumanaSCSI-1", -}; - -static int __devinit -cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - int ret = -ENOMEM; - - host = scsi_register(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); - if (!host) - goto out; - - host->io_port = ecard_address(ec, ECARD_IOC, ECARD_SLOW) + 0x800; - host->irq = ec->irq; - - NCR5380_init(host, 0); - - host->n_io_port = 255; - if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) { - ret = -EBUSY; - goto out_free; - } - - ((struct NCR5380_hostdata *)host->hostdata)->ctrl = 0; - outb(0x00, host->io_port - 577); - - ret = request_irq(host->irq, cumanascsi_intr, SA_INTERRUPT, - "CumanaSCSI-1", host); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_release; - } - - printk("scsi%d: at port 0x%08lx irq %d", - host->host_no, host->io_port, host->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - - ret = scsi_add_host(host, &ec->dev); - if (ret == 0) - goto out; - - free_irq(host->irq, host); - out_release: - release_region(host->io_port, host->n_io_port); - out_free: - scsi_unregister(host); - out: - return ret; -} - -static void __devexit cumanascsi1_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - - scsi_remove_host(host); - free_irq(host->irq, host); - release_region(host->io_port, host->n_io_port); - scsi_unregister(host); -} - -static const struct ecard_id cumanascsi1_cids[] = { - { MANU_CUMANA, PROD_CUMANA_SCSI_1 }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver cumanascsi1_driver = { - .probe = cumanascsi1_probe, - .remove = __devexit_p(cumanascsi1_remove), - .id_table = cumanascsi1_cids, - .drv = { - .name = "cumanascsi1", - }, -}; - -static int __init cumanascsi_init(void) -{ - return ecard_register_driver(&cumanascsi1_driver); -} - -static void __exit cumanascsi_exit(void) -{ - ecard_remove_driver(&cumanascsi1_driver); -} - -module_init(cumanascsi_init); -module_exit(cumanascsi_exit); - -MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines"); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/cumana_2.c linux-2.5.69-bk14/drivers/acorn/scsi/cumana_2.c --- linux-2.5.69-bk13/drivers/acorn/scsi/cumana_2.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/cumana_2.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,576 +0,0 @@ -/* - * linux/drivers/acorn/scsi/cumana_2.c - * - * Copyright (C) 1997-2002 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 30-08-1997 RMK 0.0.0 Created, READONLY version. - * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. - * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. - * 02-05-1998 RMK 0.0.2 Updated & added DMA support. - * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. - * 02-04-2000 RMK 0.0.4 Updated for new error handling code. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -#include - -#define CUMANASCSI2_STATUS (0x0000) -#define STATUS_INT (1 << 0) -#define STATUS_DRQ (1 << 1) -#define STATUS_LATCHED (1 << 3) - -#define CUMANASCSI2_ALATCH (0x0014) -#define ALATCH_ENA_INT (3) -#define ALATCH_DIS_INT (2) -#define ALATCH_ENA_TERM (5) -#define ALATCH_DIS_TERM (4) -#define ALATCH_ENA_BIT32 (11) -#define ALATCH_DIS_BIT32 (10) -#define ALATCH_ENA_DMA (13) -#define ALATCH_DIS_DMA (12) -#define ALATCH_DMA_OUT (15) -#define ALATCH_DMA_IN (14) - -#define CUMANASCSI2_PSEUDODMA (0x0200) - -#define CUMANASCSI2_FAS216_OFFSET (0x0300) -#define CUMANASCSI2_FAS216_SHIFT 2 - -/* - * Version - */ -#define VERSION "1.00 (13/11/2002 2.5.47)" - -/* - * Use term=0,1,0,0,0 to turn terminators on/off - */ -static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -#define NR_SG 256 - -struct cumanascsi2_info { - FAS216_Info info; - struct expansion_card *ec; - - void *status; /* card status register */ - void *alatch; /* Control register */ - unsigned int terms; /* Terminator state */ - void *dmaarea; /* Pseudo DMA area */ - struct scatterlist sg[NR_SG]; /* Scatter DMA list */ -}; - -#define CSTATUS_IRQ (1 << 0) -#define CSTATUS_DRQ (1 << 1) - -/* Prototype: void cumanascsi_2_irqenable(ec, irqnr) - * Purpose : Enable interrupts on Cumana SCSI 2 card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr) -{ - writeb(ALATCH_ENA_INT, ec->irq_data); -} - -/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr) - * Purpose : Disable interrupts on Cumana SCSI 2 card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr) -{ - writeb(ALATCH_DIS_INT, ec->irq_data); -} - -static const expansioncard_ops_t cumanascsi_2_ops = { - .irqenable = cumanascsi_2_irqenable, - .irqdisable = cumanascsi_2_irqdisable, -}; - -/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off) - * Purpose : Turn the Cumana SCSI 2 terminators on or off - * Params : host - card to turn on/off - * : on_off - !0 to turn on, 0 to turn off - */ -static void -cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - - if (on_off) { - info->terms = 1; - writeb(ALATCH_ENA_TERM, info->alatch); - } else { - info->terms = 0; - writeb(ALATCH_DIS_TERM, info->alatch); - } -} - -/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs) - * Purpose : handle interrupts from Cumana SCSI 2 card - * Params : irq - interrupt number - * dev_id - user-defined (Scsi_Host structure) - * regs - processor registers at interrupt - */ -static irqreturn_t -cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct cumanascsi2_info *info = dev_id; - - return fas216_intr(&info->info); -} - -/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : type of transfer to be performed - */ -static fasdmatype_t -cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - struct device *dev = scsi_get_device(host); - int dmach = host->dma_channel; - - writeb(ALATCH_DIS_DMA, info->alatch); - - if (dmach != NO_DMA && - (min_type == fasdma_real_all || SCp->this_residual >= 512)) { - int bufs, map_dir, dma_dir, alatch_dir; - - bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); - - if (direction == DMA_OUT) - map_dir = DMA_TO_DEVICE, - dma_dir = DMA_MODE_WRITE, - alatch_dir = ALATCH_DMA_OUT; - else - map_dir = DMA_FROM_DEVICE, - dma_dir = DMA_MODE_READ, - alatch_dir = ALATCH_DMA_IN; - - dma_map_sg(dev, info->sg, bufs + 1, map_dir); - - disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); - writeb(alatch_dir, info->alatch); - set_dma_mode(dmach, dma_dir); - enable_dma(dmach); - writeb(ALATCH_ENA_DMA, info->alatch); - writeb(ALATCH_DIS_BIT32, info->alatch); - return fasdma_real_all; - } - - /* - * If we're not doing DMA, - * we'll do pseudo DMA - */ - return fasdma_pio; -} - -/* - * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer) - * Purpose : handles pseudo DMA - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * transfer - minimum number of bytes we expect to transfer - */ -static void -cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, int transfer) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - unsigned int length; - unsigned char *addr; - - length = SCp->this_residual; - addr = SCp->ptr; - - if (direction == DMA_OUT) -#if 0 - while (length > 1) { - unsigned long word; - unsigned int status = readb(info->status); - - if (status & STATUS_INT) - goto end; - - if (!(status & STATUS_DRQ)) - continue; - - word = *addr | *(addr + 1) << 8; - writew(word, info->dmaarea); - addr += 2; - length -= 2; - } -#else - printk ("PSEUDO_OUT???\n"); -#endif - else { - if (transfer && (transfer & 255)) { - while (length >= 256) { - unsigned int status = readb(info->status); - - if (status & STATUS_INT) - return; - - if (!(status & STATUS_DRQ)) - continue; - - readsw(info->dmaarea, addr, 256 >> 1); - addr += 256; - length -= 256; - } - } - - while (length > 0) { - unsigned long word; - unsigned int status = readb(info->status); - - if (status & STATUS_INT) - return; - - if (!(status & STATUS_DRQ)) - continue; - - word = readw(info->dmaarea); - *addr++ = word; - if (--length > 0) { - *addr++ = word >> 8; - length --; - } - } - } -} - -/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void -cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - if (host->dma_channel != NO_DMA) { - writeb(ALATCH_DIS_DMA, info->alatch); - disable_dma(host->dma_channel); - } -} - -/* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -const char *cumanascsi_2_info(struct Scsi_Host *host) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s terminators o%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION, info->terms ? "n" : "ff"); - - return string; -} - -/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) - * Purpose : Set a driver specific function - * Params : host - host to setup - * : buffer - buffer containing string describing operation - * : length - length of string - * Returns : -EINVAL, or 0 - */ -static int -cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) -{ - int ret = length; - - if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) { - buffer += 11; - length -= 11; - - if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { - if (buffer[5] == '1') - cumanascsi_2_terminator_ctl(host, 1); - else if (buffer[5] == '0') - cumanascsi_2_terminator_ctl(host, 0); - else - ret = -EINVAL; - } else - ret = -EINVAL; - } else - ret = -EINVAL; - - return ret; -} - -/* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - int pos, begin; - struct Scsi_Host *host; - struct cumanascsi2_info *info; - Scsi_Device *scd; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - if (inout == 1) - return cumanascsi_2_set_proc_info(host, buffer, length); - - info = (struct cumanascsi2_info *)host->hostdata; - - begin = 0; - pos = sprintf(buffer, "Cumana SCSI II driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += sprintf(buffer + pos, "Term : o%s\n", - info->terms ? "n" : "ff"); - - pos += fas216_print_stats(&info->info, buffer + pos); - - pos += sprintf(buffer+pos, "\nAttached devices:\n"); - - *start = buffer + (offset - begin); - pos -= offset - begin; - if (pos > length) - pos = length; - - return pos; -} - -static Scsi_Host_Template cumanascsi2_template = { - .module = THIS_MODULE, - .proc_info = cumanascsi_2_proc_info, - .name = "Cumana SCSI II", - .info = cumanascsi_2_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - .can_queue = 1, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "cumanascsi2", -}; - -static int __devinit -cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct cumanascsi2_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - if (!request_mem_region(resbase, reslen, "cumanascsi2")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&cumanascsi2_template, - sizeof(struct cumanascsi2_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = ec->irq; - host->dma_channel = ec->dma; - - ecard_set_drvdata(ec, host); - - info = (struct cumanascsi2_info *)host->hostdata; - info->ec = ec; - info->dmaarea = base + CUMANASCSI2_PSEUDODMA; - info->status = base + CUMANASCSI2_STATUS; - info->alatch = base + CUMANASCSI2_ALATCH; - - ec->irqaddr = info->status; - ec->irqmask = STATUS_INT; - ec->irq_data = base + CUMANASCSI2_ALATCH; - ec->ops = &cumanascsi_2_ops; - - cumanascsi_2_terminator_ctl(host, term[ec->slot_no]); - - info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET; - info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT; - info->info.scsi.irq = host->irq; - info->info.ifcfg.clockrate = 40; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 7; - info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 1; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; - info->info.dma.setup = cumanascsi_2_dma_setup; - info->info.dma.pseudo = cumanascsi_2_dma_pseudo; - info->info.dma.stop = cumanascsi_2_dma_stop; - - ret = fas216_init(host); - if (ret) - goto out_free; - - ret = request_irq(host->irq, cumanascsi_2_intr, - SA_INTERRUPT, "cumanascsi2", info); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_release; - } - - if (host->dma_channel != NO_DMA) { - if (request_dma(host->dma_channel, "cumanascsi2")) { - printk("scsi%d: DMA%d not free, using PIO\n", - host->host_no, host->dma_channel); - host->dma_channel = NO_DMA; - } else { - set_dma_speed(host->dma_channel, 180); - info->info.ifcfg.capabilities |= FASCAP_DMA; - } - } - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, host); - - out_release: - fas216_release(host); - - out_free: - scsi_unregister(host); - - out_unmap: - iounmap(base); - - out_region: - release_mem_region(resbase, reslen); - - out: - return ret; -} - -static void __devexit cumanascsi2_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, info); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id cumanascsi2_cids[] = { - { MANU_CUMANA, PROD_CUMANA_SCSI_2 }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver cumanascsi2_driver = { - .probe = cumanascsi2_probe, - .remove = __devexit_p(cumanascsi2_remove), - .id_table = cumanascsi2_cids, - .drv = { - .name = "cumanascsi2", - }, -}; - -static int __init cumanascsi2_init(void) -{ - return ecard_register_driver(&cumanascsi2_driver); -} - -static void __exit cumanascsi2_exit(void) -{ - ecard_remove_driver(&cumanascsi2_driver); -} - -module_init(cumanascsi2_init); -module_exit(cumanascsi2_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/ecoscsi.c linux-2.5.69-bk14/drivers/acorn/scsi/ecoscsi.c --- linux-2.5.69-bk13/drivers/acorn/scsi/ecoscsi.c 2003-05-04 16:53:42.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/ecoscsi.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,283 +0,0 @@ -#define AUTOSENSE -/* #define PSEUDO_DMA */ - -/* - * EcoSCSI Generic NCR5380 driver - * - * Copyright 1995, Russell King - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 - */ - -/* - * Options : - * - * PARITY - enable parity checking. Not supported. - * - * SCSI2 - enable support for SCSI-II tagged queueing. Untested. - * - * USLEEP - enable support for devices that don't disconnect. Untested. - */ - -/* - * $Log: ecoscsi.c,v $ - * Revision 1.2 1998/03/08 05:49:47 davem - * Merge to 2.1.89 - * - * Revision 1.1 1998/02/23 02:45:24 davem - * Merge to 2.1.88 - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" - -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance - -#define NCR5380_read(reg) ecoscsi_read(_instance, reg) -#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value) - -#define NCR5380_intr ecoscsi_intr -#define NCR5380_queue_command ecoscsi_queue_command -#define NCR5380_proc_info ecoscsi_proc_info - -#include "../../scsi/NCR5380.h" - -#define ECOSCSI_PUBLIC_RELEASE 1 - -static char ecoscsi_read(struct Scsi_Host *instance, int reg) -{ - int iobase = instance->io_port; - outb(reg | 8, iobase); - return inb(iobase + 1); -} - -static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value) -{ - int iobase = instance->io_port; - outb(reg | 8, iobase); - outb(value, iobase + 1); -} - -/* - * Function : ecoscsi_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -void ecoscsi_setup(char *str, int *ints) { -} - -/* - * Function : int ecoscsi_detect(Scsi_Host_Template * tpnt) - * - * Purpose : initializes ecoscsi NCR5380 driver based on the - * command line / compile time port and irq definitions. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -int ecoscsi_detect(Scsi_Host_Template * tpnt) -{ - struct Scsi_Host *host; - - tpnt->proc_name = "ecoscsi"; - - host = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if (!host) - return 0; - - host->io_port = 0x80ce8000; - host->n_io_port = 144; - host->irq = IRQ_NONE; - - if ( !(request_region(host->io_port, host->n_io_port, "ecoscsi")) ) - goto unregister_scsi; - - ecoscsi_write (host, MODE_REG, 0x20); /* Is it really SCSI? */ - if (ecoscsi_read (host, MODE_REG) != 0x20) /* Write to a reg. */ - goto release_reg; - - ecoscsi_write( host, MODE_REG, 0x00 ); /* it back. */ - if (ecoscsi_read (host, MODE_REG) != 0x00) - goto release_reg; - - NCR5380_init(host, 0); - - printk("scsi%d: at port 0x%08lx irqs disabled", host->host_no, host->io_port); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, ECOSCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - - return 1; - -release_reg: - release_region(host->io_port, host->n_io_port); -unregister_scsi: - scsi_unregister(host); - return 0; -} - -int ecoscsi_release (struct Scsi_Host *shpnt) -{ - if (shpnt->irq != IRQ_NONE) - free_irq (shpnt->irq, NULL); - if (shpnt->io_port) - release_region (shpnt->io_port, shpnt->n_io_port); - return 0; -} - -const char * ecoscsi_info (struct Scsi_Host *spnt) -{ - return ""; -} - -#if 0 -#define STAT(p) inw(p + 144) - -static inline int NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, - int len) -{ - int iobase = host->io_port; -printk("writing %p len %d\n",addr, len); - if(!len) return -1; - - while(1) - { - int status; - while(((status = STAT(iobase)) & 0x100)==0); - } -} - -static inline int NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, - int len) -{ - int iobase = host->io_port; - int iobase2= host->io_port + 0x100; - unsigned char *start = addr; - int s; -printk("reading %p len %d\n",addr, len); - outb(inb(iobase + 128), iobase + 135); - while(len > 0) - { - int status,b,i, timeout; - timeout = 0x07FFFFFF; - while(((status = STAT(iobase)) & 0x100)==0) - { - timeout--; - if(status & 0x200 || !timeout) - { - printk("status = %p\n",status); - outb(0, iobase + 135); - return 1; - } - } - if(len >= 128) - { - for(i=0; i<64; i++) - { - b = inw(iobase + 136); - *addr++ = b; - *addr++ = b>>8; - } - len -= 128; - } - else - { - b = inw(iobase + 136); - *addr ++ = b; - len -= 1; - if(len) - *addr ++ = b>>8; - len -= 1; - } - } - outb(0, iobase + 135); - printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]); - return 1; -} -#endif -#undef STAT - -int NCR5380_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -#define BOARD_NORMAL 0 -#define BOARD_NCR53C400 1 - -#include "../../scsi/NCR5380.c" - -static Scsi_Host_Template ecoscsi_template = { - .module = THIS_MODULE, - .name = "Serial Port EcoSCSI NCR5380", - .detect = ecoscsi_detect, - .release = ecoscsi_release, - .info = ecoscsi_info, - .queuecommand = ecoscsi_queue_command, - .eh_abort_handler = NCR5380_abort, - .eh_device_reset_handler= NCR5380_device_reset, - .eh_bus_reset_handler = NCR5380_bus_reset, - .eh_host_reset_handler = NCR5380_host_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING -}; - -static int __init ecoscsi_init(void) -{ - scsi_register_host(&ecoscsi_template); - if (ecoscsi_template.present) - return 0; - - scsi_unregister_host(&ecoscsi_template); - return -ENODEV; -} - -static void __exit ecoscsi_exit(void) -{ - scsi_unregister_host(&ecoscsi_template); -} - -module_init(ecoscsi_init); -module_exit(ecoscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Econet-SCSI driver for Acorn machines"); -MODULE_LICENSE("GPL"); - diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/eesox.c linux-2.5.69-bk14/drivers/acorn/scsi/eesox.c --- linux-2.5.69-bk13/drivers/acorn/scsi/eesox.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/eesox.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,685 +0,0 @@ -/* - * linux/drivers/acorn/scsi/eesox.c - * - * Copyright (C) 1997-2003 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This driver is based on experimentation. Hence, it may have made - * assumptions about the particular card that I have available, and - * may not be reliable! - * - * Changelog: - * 01-10-1997 RMK Created, READONLY version - * 15-02-1998 RMK READ/WRITE version - * added DMA support and hardware definitions - * 14-03-1998 RMK Updated DMA support - * Added terminator control - * 15-04-1998 RMK Only do PIO if FAS216 will allow it. - * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new - * error handling code. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -#include - -#define EESOX_FAS216_OFFSET 0x3000 -#define EESOX_FAS216_SHIFT 5 - -#define EESOX_DMASTAT 0x2800 -#define EESOX_STAT_INTR 0x01 -#define EESOX_STAT_DMA 0x02 - -#define EESOX_CONTROL 0x2800 -#define EESOX_INTR_ENABLE 0x04 -#define EESOX_TERM_ENABLE 0x02 -#define EESOX_RESET 0x01 - -#define EESOX_DMADATA 0x3800 - -#define VERSION "1.10 (17/01/2003 2.5.59)" - -/* - * Use term=0,1,0,0,0 to turn terminators on/off - */ -static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -#define NR_SG 256 - -struct eesoxscsi_info { - FAS216_Info info; - struct expansion_card *ec; - - void *ctl_port; - unsigned int control; - struct scatterlist sg[NR_SG]; /* Scatter DMA list */ -}; - -/* Prototype: void eesoxscsi_irqenable(ec, irqnr) - * Purpose : Enable interrupts on EESOX SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -eesoxscsi_irqenable(struct expansion_card *ec, int irqnr) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; - - info->control |= EESOX_INTR_ENABLE; - - writeb(info->control, info->ctl_port); -} - -/* Prototype: void eesoxscsi_irqdisable(ec, irqnr) - * Purpose : Disable interrupts on EESOX SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; - - info->control &= ~EESOX_INTR_ENABLE; - - writeb(info->control, info->ctl_port); -} - -static const expansioncard_ops_t eesoxscsi_ops = { - .irqenable = eesoxscsi_irqenable, - .irqdisable = eesoxscsi_irqdisable, -}; - -/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off) - * Purpose : Turn the EESOX SCSI terminators on or off - * Params : host - card to turn on/off - * : on_off - !0 to turn on, 0 to turn off - */ -static void -eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - unsigned long flags; - - spin_lock_irqsave(host->host_lock, flags); - if (on_off) - info->control |= EESOX_TERM_ENABLE; - else - info->control &= ~EESOX_TERM_ENABLE; - - writeb(info->control, info->ctl_port); - spin_unlock_irqrestore(host->host_lock, flags); -} - -/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs) - * Purpose : handle interrupts from EESOX SCSI card - * Params : irq - interrupt number - * dev_id - user-defined (Scsi_Host structure) - * regs - processor registers at interrupt - */ -static irqreturn_t -eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct eesoxscsi_info *info = dev_id; - - return fas216_intr(&info->info); -} - -/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : type of transfer to be performed - */ -static fasdmatype_t -eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - struct device *dev = scsi_get_device(host); - int dmach = host->dma_channel; - - if (dmach != NO_DMA && - (min_type == fasdma_real_all || SCp->this_residual >= 512)) { - int bufs, map_dir, dma_dir; - - bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); - - if (direction == DMA_OUT) - map_dir = DMA_TO_DEVICE, - dma_dir = DMA_MODE_WRITE; - else - map_dir = DMA_FROM_DEVICE, - dma_dir = DMA_MODE_READ; - - dma_map_sg(dev, info->sg, bufs + 1, map_dir); - - disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); - set_dma_mode(dmach, dma_dir); - enable_dma(dmach); - return fasdma_real_all; - } - /* - * We don't do DMA, we only do slow PIO - * - * Some day, we will do Pseudo DMA - */ - return fasdma_pseudo; -} - -static void eesoxscsi_buffer_in(void *buf, int length, void *base) -{ - const void *reg_fas = base + EESOX_FAS216_OFFSET; - const void *reg_dmastat = base + EESOX_DMASTAT; - const void *reg_dmadata = base + EESOX_DMADATA; - const register unsigned long mask = 0xffff; - - do { - unsigned int status; - - /* - * Interrupt request? - */ - status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); - if (status & STAT_INT) - break; - - /* - * DMA request active? - */ - status = readb(reg_dmastat); - if (!(status & EESOX_STAT_DMA)) - continue; - - /* - * Get number of bytes in FIFO - */ - status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; - if (status > 16) - status = 16; - if (status > length) - status = length; - - /* - * Align buffer. - */ - if (((u32)buf) & 2 && status >= 2) { - *((u16 *)buf)++ = readl(reg_dmadata); - status -= 2; - length -= 2; - } - - if (status >= 8) { - unsigned long l1, l2; - - l1 = readl(reg_dmadata) & mask; - l1 |= readl(reg_dmadata) << 16; - l2 = readl(reg_dmadata) & mask; - l2 |= readl(reg_dmadata) << 16; - *((u32 *)buf)++ = l1; - *((u32 *)buf)++ = l2; - length -= 8; - continue; - } - - if (status >= 4) { - unsigned long l1; - - l1 = readl(reg_dmadata) & mask; - l1 |= readl(reg_dmadata) << 16; - - *((u32 *)buf)++ = l1; - length -= 4; - continue; - } - - if (status >= 2) { - *((u16 *)buf)++ = readl(reg_dmadata); - length -= 2; - } - } while (length); -} - -static void eesoxscsi_buffer_out(void *buf, int length, void *base) -{ - const void *reg_fas = base + EESOX_FAS216_OFFSET; - const void *reg_dmastat = base + EESOX_DMASTAT; - const void *reg_dmadata = base + EESOX_DMADATA; - - do { - unsigned int status; - - /* - * Interrupt request? - */ - status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); - if (status & STAT_INT) - break; - - /* - * DMA request active? - */ - status = readb(reg_dmastat); - if (!(status & EESOX_STAT_DMA)) - continue; - - /* - * Get number of bytes in FIFO - */ - status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; - if (status > 16) - status = 16; - status = 16 - status; - if (status > length) - status = length; - status &= ~1; - - /* - * Align buffer. - */ - if (((u32)buf) & 2 && status >= 2) { - writel(*((u16 *)buf)++ << 16, reg_dmadata); - status -= 2; - length -= 2; - } - - if (status >= 8) { - unsigned long l1, l2; - - l1 = *((u32 *)buf)++; - l2 = *((u32 *)buf)++; - - writel(l1 << 16, reg_dmadata); - writel(l1, reg_dmadata); - writel(l2 << 16, reg_dmadata); - writel(l2, reg_dmadata); - length -= 8; - continue; - } - - if (status >= 4) { - unsigned long l1; - - l1 = *((u32 *)buf)++; - - writel(l1 << 16, reg_dmadata); - writel(l1, reg_dmadata); - length -= 4; - continue; - } - - if (status >= 2) { - writel(*((u16 *)buf)++ << 16, reg_dmadata); - length -= 2; - } - } while (length); -} - -static void -eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t dir, int transfer_size) -{ - void *base = (void *)host->base; - if (dir == DMA_IN) { - eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, base); - } else { - eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, base); - } -} - -/* Prototype: int eesoxscsi_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void -eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - if (host->dma_channel != NO_DMA) - disable_dma(host->dma_channel); -} - -/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -const char *eesoxscsi_info(struct Scsi_Host *host) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s terminators o%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff"); - - return string; -} - -/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) - * Purpose : Set a driver specific function - * Params : host - host to setup - * : buffer - buffer containing string describing operation - * : length - length of string - * Returns : -EINVAL, or 0 - */ -static int -eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) -{ - int ret = length; - - if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) { - buffer += 9; - length -= 9; - - if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { - if (buffer[5] == '1') - eesoxscsi_terminator_ctl(host, 1); - else if (buffer[5] == '0') - eesoxscsi_terminator_ctl(host, 0); - else - ret = -EINVAL; - } else - ret = -EINVAL; - } else - ret = -EINVAL; - - return ret; -} - -/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - int pos, begin; - struct Scsi_Host *host; - struct eesoxscsi_info *info; - Scsi_Device *scd; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - if (inout == 1) - return eesoxscsi_set_proc_info(host, buffer, length); - - info = (struct eesoxscsi_info *)host->hostdata; - - begin = 0; - pos = sprintf(buffer, "EESOX SCSI driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += sprintf(buffer + pos, "Term : o%s\n", - info->control & EESOX_TERM_ENABLE ? "n" : "ff"); - - pos += fas216_print_stats(&info->info, buffer + pos); - - *start = buffer + (offset - begin); - pos -= offset - begin; - if (pos > length) - pos = length; - - return pos; -} - -static ssize_t eesoxscsi_show_term(struct device *dev, char *buf) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - - return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0); -} - -static ssize_t eesoxscsi_store_term(struct device *dev, const char *buf, size_t len) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - unsigned long flags; - - if (len > 1) { - spin_lock_irqsave(host->host_lock, flags); - if (buf[0] != '0') { - info->control |= EESOX_TERM_ENABLE; - } else { - info->control &= ~EESOX_TERM_ENABLE; - } - writeb(info->control, info->ctl_port); - spin_unlock_irqrestore(host->host_lock, flags); - } - - return len; -} - -static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, - eesoxscsi_show_term, eesoxscsi_store_term); - -static Scsi_Host_Template eesox_template = { - .module = THIS_MODULE, - .proc_info = eesoxscsi_proc_info, - .name = "EESOX SCSI", - .info = eesoxscsi_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - .can_queue = 1, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "eesox", -}; - -static int __devinit -eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct eesoxscsi_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - if (!request_mem_region(resbase, reslen, "eesoxscsi")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&eesox_template, - sizeof(struct eesoxscsi_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = ec->irq; - host->dma_channel = ec->dma; - - ecard_set_drvdata(ec, host); - - info = (struct eesoxscsi_info *)host->hostdata; - info->ec = ec; - info->ctl_port = base + EESOX_CONTROL; - info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0; - writeb(info->control, info->ctl_port); - - ec->irqaddr = base + EESOX_DMASTAT; - ec->irqmask = EESOX_STAT_INTR; - ec->irq_data = info; - ec->ops = &eesoxscsi_ops; - - info->info.scsi.io_base = base + EESOX_FAS216_OFFSET; - info->info.scsi.io_shift = EESOX_FAS216_SHIFT; - info->info.scsi.irq = host->irq; - info->info.ifcfg.clockrate = 40; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 7; - info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 1; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; - info->info.dma.setup = eesoxscsi_dma_setup; - info->info.dma.pseudo = eesoxscsi_dma_pseudo; - info->info.dma.stop = eesoxscsi_dma_stop; - - device_create_file(&ec->dev, &dev_attr_bus_term); - - ret = fas216_init(host); - if (ret) - goto out_free; - - ret = request_irq(host->irq, eesoxscsi_intr, 0, "eesoxscsi", info); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_remove; - } - - if (host->dma_channel != NO_DMA) { - if (request_dma(host->dma_channel, "eesox")) { - printk("scsi%d: DMA%d not free, DMA disabled\n", - host->host_no, host->dma_channel); - host->dma_channel = NO_DMA; - } else { - set_dma_speed(host->dma_channel, 180); - info->info.ifcfg.capabilities |= FASCAP_DMA; - info->info.ifcfg.cntl3 |= CNTL3_BS8; - } - } - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, host); - - out_remove: - fas216_remove(host); - - out_free: - device_remove_file(&ec->dev, &dev_attr_bus_term); - scsi_unregister(host); - - out_unmap: - iounmap(base); - - out_region: - release_mem_region(resbase, reslen); - - out: - return ret; -} - -static void __devexit eesoxscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, info); - - device_remove_file(&ec->dev, &dev_attr_bus_term); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id eesoxscsi_cids[] = { - { MANU_EESOX, PROD_EESOX_SCSI2 }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver eesoxscsi_driver = { - .probe = eesoxscsi_probe, - .remove = __devexit_p(eesoxscsi_remove), - .id_table = eesoxscsi_cids, - .drv = { - .name = "eesoxscsi", - }, -}; - -static int __init eesox_init(void) -{ - return ecard_register_driver(&eesoxscsi_driver); -} - -static void __exit eesox_exit(void) -{ - ecard_remove_driver(&eesoxscsi_driver); -} - -module_init(eesox_init); -module_exit(eesox_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); - diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/fas216.c linux-2.5.69-bk14/drivers/acorn/scsi/fas216.c --- linux-2.5.69-bk13/drivers/acorn/scsi/fas216.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/fas216.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,3231 +0,0 @@ -/* - * linux/drivers/acorn/scsi/fas216.c - * - * Copyright (C) 1997-2003 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and - * other sources, including: - * the AMD Am53CF94 data sheet - * the AMD Am53C94 data sheet - * - * This is a generic driver. To use it, have a look at cumana_2.c. You - * should define your own structure that overlays FAS216_Info, eg: - * struct my_host_data { - * FAS216_Info info; - * ... my host specific data ... - * }; - * - * Changelog: - * 30-08-1997 RMK Created - * 14-09-1997 RMK Started disconnect support - * 08-02-1998 RMK Corrected real DMA support - * 15-02-1998 RMK Started sync xfer support - * 06-04-1998 RMK Tightened conditions for printing incomplete - * transfers - * 02-05-1998 RMK Added extra checks in fas216_reset - * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns - * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT - * 02-04-2000 RMK Converted to use the new error handling, and - * automatically request sense data upon check - * condition status from targets. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -/* NOTE: SCSI2 Synchronous transfers *require* DMA according to - * the data sheet. This restriction is crazy, especially when - * you only want to send 16 bytes! What were the guys who - * designed this chip on at that time? Did they read the SCSI2 - * spec at all? The following sections are taken from the SCSI2 - * standard (s2r10) concerning this: - * - * > IMPLEMENTORS NOTES: - * > (1) Re-negotiation at every selection is not recommended, since a - * > significant performance impact is likely. - * - * > The implied synchronous agreement shall remain in effect until a BUS DEVICE - * > RESET message is received, until a hard reset condition occurs, or until one - * > of the two SCSI devices elects to modify the agreement. The default data - * > transfer mode is asynchronous data transfer mode. The default data transfer - * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard - * > reset condition. - * - * In total, this means that once you have elected to use synchronous - * transfers, you must always use DMA. - * - * I was thinking that this was a good chip until I found this restriction ;( - */ -#define SCSI2_SYNC -#undef SCSI2_WIDE -#undef SCSI2_TAG - -#undef DEBUG_CONNECT -#undef DEBUG_MESSAGES - -#undef CHECK_STRUCTURE - -#define LOG_CONNECT (1 << 0) -#define LOG_BUSSERVICE (1 << 1) -#define LOG_FUNCTIONDONE (1 << 2) -#define LOG_MESSAGES (1 << 3) -#define LOG_BUFFER (1 << 4) -#define LOG_ERROR (1 << 8) - -static int level_mask = LOG_ERROR; - -static int __init fas216_log_setup(char *str) -{ - char *s; - - level_mask = 0; - - while ((s = strsep(&str, ",")) != NULL) { - switch (s[0]) { - case 'a': - if (strcmp(s, "all") == 0) - level_mask |= -1; - break; - case 'b': - if (strncmp(s, "bus", 3) == 0) - level_mask |= LOG_BUSSERVICE; - if (strncmp(s, "buf", 3) == 0) - level_mask |= LOG_BUFFER; - break; - case 'c': - level_mask |= LOG_CONNECT; - break; - case 'e': - level_mask |= LOG_ERROR; - break; - case 'm': - level_mask |= LOG_MESSAGES; - break; - case 'n': - if (strcmp(s, "none") == 0) - level_mask = 0; - break; - case 's': - level_mask |= LOG_FUNCTIONDONE; - break; - } - } - return 1; -} - -__setup("fas216_logging=", fas216_log_setup); - -static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg) -{ - unsigned int off = reg << info->scsi.io_shift; - if (info->scsi.io_base) - return readb(info->scsi.io_base + off); - else - return inb(info->scsi.io_port + off); -} - -static inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val) -{ - unsigned int off = reg << info->scsi.io_shift; - if (info->scsi.io_base) - writeb(val, info->scsi.io_base + off); - else - outb(val, info->scsi.io_port + off); -} - -static void fas216_dumpstate(FAS216_Info *info) -{ - unsigned char is, stat, inst; - - is = fas216_readb(info, REG_IS); - stat = fas216_readb(info, REG_STAT); - inst = fas216_readb(info, REG_INST); - - printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X" - " INST=%02X IS=%02X CFIS=%02X", - fas216_readb(info, REG_CTCL), - fas216_readb(info, REG_CTCM), - fas216_readb(info, REG_CMD), stat, inst, is, - fas216_readb(info, REG_CFIS)); - printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n", - fas216_readb(info, REG_CNTL1), - fas216_readb(info, REG_CNTL2), - fas216_readb(info, REG_CNTL3), - fas216_readb(info, REG_CTCH)); -} - -static void print_SCp(Scsi_Pointer *SCp, const char *prefix, const char *suffix) -{ - printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s", - prefix, SCp->ptr, SCp->this_residual, SCp->buffer, - SCp->buffers_residual, suffix); -} - -static void fas216_dumpinfo(FAS216_Info *info) -{ - static int used = 0; - int i; - - if (used++) - return; - - printk("FAS216_Info=\n"); - printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n", - info->magic_start, info->host, info->SCpnt, - info->origSCpnt); - printk(" scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n", - info->scsi.io_port, info->scsi.io_shift, info->scsi.irq, - info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2], - info->scsi.cfg[3]); - printk(" type=%p phase=%X reconnected={ target=%d lun=%d tag=%d }\n", - info->scsi.type, info->scsi.phase, - info->scsi.reconnected.target, - info->scsi.reconnected.lun, info->scsi.reconnected.tag); - print_SCp(&info->scsi.SCp, " SCp={ ", " }\n"); - printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", - info->scsi.async_stp, - info->scsi.disconnectable, info->scsi.aborting); - printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" - " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n", - info->stats.queues, info->stats.removes, info->stats.fins, - info->stats.reads, info->stats.writes, info->stats.miscs, - info->stats.disconnects, info->stats.aborts, info->stats.bus_resets, - info->stats.host_resets); - printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n", - info->ifcfg.clockrate, info->ifcfg.select_timeout, - info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); - for (i = 0; i < 8; i++) { - printk(" busyluns[%d]=%08lx dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", - i, info->busyluns[i], i, - info->device[i].disconnect_ok, info->device[i].stp, - info->device[i].sof, info->device[i].sync_state); - } - printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", - info->dma.transfer_type, info->dma.setup, - info->dma.pseudo, info->dma.stop); - printk(" internal_done=%X magic_end=%lX }\n", - info->internal_done, info->magic_end); -} - -#ifdef CHECK_STRUCTURE -static void __fas216_checkmagic(FAS216_Info *info, const char *func) -{ - int corruption = 0; - if (info->magic_start != MAGIC) { - printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n"); - corruption++; - } - if (info->magic_end != MAGIC) { - printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n"); - corruption++; - } - if (corruption) { - fas216_dumpinfo(info); - panic("scsi memory space corrupted in %s", func); - } -} -#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__) -#else -#define fas216_checkmagic(info) -#endif - -static const char *fas216_bus_phase(int stat) -{ - static const char *phases[] = { - "DATA OUT", "DATA IN", - "COMMAND", "STATUS", - "MISC OUT", "MISC IN", - "MESG OUT", "MESG IN" - }; - - return phases[stat & STAT_BUSMASK]; -} - -static const char *fas216_drv_phase(FAS216_Info *info) -{ - switch (info->scsi.phase) { - case PHASE_IDLE: return "idle"; - case PHASE_SELECTION: return "selection"; - case PHASE_COMMAND: return "command"; - case PHASE_RECONNECTED: return "reconnected"; - case PHASE_DATAOUT: return "data out"; - case PHASE_DATAIN: return "data in"; - case PHASE_MSGIN: return "message in"; - case PHASE_MSGIN_DISCONNECT: return "disconnect"; - case PHASE_MSGOUT_EXPECT: return "expect message out"; - case PHASE_MSGOUT: return "message out"; - case PHASE_STATUS: return "status"; - case PHASE_DONE: return "done"; - default: return "???"; - } -} - -static char fas216_target(FAS216_Info *info) -{ - if (info->SCpnt) - return '0' + info->SCpnt->device->id; - else - return 'H'; -} - -static void -fas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap) -{ - static char buf[1024]; - - vsnprintf(buf, sizeof(buf), fmt, ap); - printk("scsi%d.%c: %s", info->host->host_no, target, buf); -} - -static void -fas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...) -{ - va_list args; - - if (level != 0 && !(level & level_mask)) - return; - - va_start(args, fmt); - fas216_do_log(info, '0' + SCpnt->device->id, fmt, args); - va_end(args); - - printk(" CDB: "); - print_command(SCpnt->cmnd); -} - -static void -fas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...) -{ - va_list args; - - if (level != 0 && !(level & level_mask)) - return; - - if (target < 0) - target = 'H'; - else - target += '0'; - - va_start(args, fmt); - fas216_do_log(info, target, fmt, args); - va_end(args); - - printk("\n"); -} - -static void fas216_log(FAS216_Info *info, int level, char *fmt, ...) -{ - va_list args; - - if (level != 0 && !(level & level_mask)) - return; - - va_start(args, fmt); - fas216_do_log(info, fas216_target(info), fmt, args); - va_end(args); - - printk("\n"); -} - -#define PH_SIZE 32 - -static struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE]; -static int ph_ptr; - -static void add_debug_list(int stat, int ssr, int isr, int ph) -{ - ph_list[ph_ptr].stat = stat; - ph_list[ph_ptr].ssr = ssr; - ph_list[ph_ptr].isr = isr; - ph_list[ph_ptr].ph = ph; - - ph_ptr = (ph_ptr + 1) & (PH_SIZE-1); -} - -static struct { int command; void *from; } cmd_list[8]; -static int cmd_ptr; - -static void fas216_cmd(FAS216_Info *info, unsigned int command) -{ - cmd_list[cmd_ptr].command = command; - cmd_list[cmd_ptr].from = __builtin_return_address(0); - - cmd_ptr = (cmd_ptr + 1) & 7; - - fas216_writeb(info, REG_CMD, command); -} - -static void print_debug_list(void) -{ - int i; - - i = ph_ptr; - - printk(KERN_ERR "SCSI IRQ trail\n"); - do { - printk(" %02x:%02x:%02x:%1x", - ph_list[i].stat, ph_list[i].ssr, - ph_list[i].isr, ph_list[i].ph); - i = (i + 1) & (PH_SIZE - 1); - if (((i ^ ph_ptr) & 7) == 0) - printk("\n"); - } while (i != ph_ptr); - if ((i ^ ph_ptr) & 7) - printk("\n"); - - i = cmd_ptr; - printk(KERN_ERR "FAS216 commands: "); - do { - printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from); - i = (i + 1) & 7; - } while (i != cmd_ptr); - printk("\n"); -} - -static void fas216_done(FAS216_Info *info, unsigned int result); - -/** - * fas216_clockrate - calculate clock conversion factor - * @clock: clock speed in MHz - * - * Calculate correct value to be written into clock conversion factor - * register. Returns CLKF_ value. - */ -static int fas216_clockrate(int clock) -{ - if (clock <= 10 || clock > 40) { - printk(KERN_CRIT - "fas216: invalid clock rate: check your driver!\n"); - clock = -1; - } else - clock = ((clock - 1) / 5 + 1) & 7; - - return clock; -} - -/** - * fas216_get_last_msg - retrive last message from the list - * @info: interface to search - * @pos: current fifo position - * - * Retrieve a last message from the list, using position in fifo. - */ -static inline unsigned short -fas216_get_last_msg(FAS216_Info *info, int pos) -{ - unsigned short packed_msg = NOP; - struct message *msg; - int msgnr = 0; - - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - if (pos >= msg->fifo) - break; - } - - if (msg) { - if (msg->msg[0] == EXTENDED_MESSAGE) - packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; - else - packed_msg = msg->msg[0]; - } - - fas216_log(info, LOG_MESSAGES, - "Message: %04x found at position %02x\n", packed_msg, pos); - - return packed_msg; -} - -/** - * fas216_syncperiod - calculate STP register value - * @info: state structure for interface connected to device - * @ns: period in ns (between subsequent bytes) - * - * Calculate value to be loaded into the STP register for a given period - * in ns. Returns a value suitable for REG_STP. - */ -static int fas216_syncperiod(FAS216_Info *info, int ns) -{ - int value = (info->ifcfg.clockrate * ns) / 1000; - - fas216_checkmagic(info); - - if (value < 4) - value = 4; - else if (value > 35) - value = 35; - - return value & 31; -} - -/** - * fas216_set_sync - setup FAS216 chip for specified transfer period. - * @info: state structure for interface connected to device - * @target: target - * - * Correctly setup FAS216 chip for specified transfer period. - * Notes : we need to switch the chip out of FASTSCSI mode if we have - * a transfer period >= 200ns - otherwise the chip will violate - * the SCSI timings. - */ -static void fas216_set_sync(FAS216_Info *info, int target) -{ - unsigned int cntl3; - - fas216_writeb(info, REG_SOF, info->device[target].sof); - fas216_writeb(info, REG_STP, info->device[target].stp); - - cntl3 = info->scsi.cfg[2]; - if (info->device[target].period >= (200 / 4)) - cntl3 = cntl3 & ~CNTL3_FASTSCSI; - - fas216_writeb(info, REG_CNTL3, cntl3); -} - -/* Synchronous transfer support - * - * Note: The SCSI II r10 spec says (5.6.12): - * - * (2) Due to historical problems with early host adapters that could - * not accept an SDTR message, some targets may not initiate synchronous - * negotiation after a power cycle as required by this standard. Host - * adapters that support synchronous mode may avoid the ensuing failure - * modes when the target is independently power cycled by initiating a - * synchronous negotiation on each REQUEST SENSE and INQUIRY command. - * This approach increases the SCSI bus overhead and is not recommended - * for new implementations. The correct method is to respond to an - * SDTR message with a MESSAGE REJECT message if the either the - * initiator or target devices does not support synchronous transfers - * or does not want to negotiate for synchronous transfers at the time. - * Using the correct method assures compatibility with wide data - * transfers and future enhancements. - * - * We will always initiate a synchronous transfer negotiation request on - * every INQUIRY or REQUEST SENSE message, unless the target itself has - * at some point performed a synchronous transfer negotiation request, or - * we have synchronous transfers disabled for this device. - */ - -/** - * fas216_handlesync - Handle a synchronous transfer message - * @info: state structure for interface - * @msg: message from target - * - * Handle a synchronous transfer message from the target - */ -static void fas216_handlesync(FAS216_Info *info, char *msg) -{ - struct fas216_device *dev = &info->device[info->SCpnt->device->id]; - enum { sync, async, none, reject } res = none; - -#ifdef SCSI2_SYNC - switch (msg[0]) { - case MESSAGE_REJECT: - /* Synchronous transfer request failed. - * Note: SCSI II r10: - * - * SCSI devices that are capable of synchronous - * data transfers shall not respond to an SDTR - * message with a MESSAGE REJECT message. - * - * Hence, if we get this condition, we disable - * negotiation for this device. - */ - if (dev->sync_state == neg_inprogress) { - dev->sync_state = neg_invalid; - res = async; - } - break; - - case EXTENDED_MESSAGE: - switch (dev->sync_state) { - /* We don't accept synchronous transfer requests. - * Respond with a MESSAGE_REJECT to prevent a - * synchronous transfer agreement from being reached. - */ - case neg_invalid: - res = reject; - break; - - /* We were not negotiating a synchronous transfer, - * but the device sent us a negotiation request. - * Honour the request by sending back a SDTR - * message containing our capability, limited by - * the targets capability. - */ - default: - fas216_cmd(info, CMD_SETATN); - if (msg[4] > info->ifcfg.sync_max_depth) - msg[4] = info->ifcfg.sync_max_depth; - if (msg[3] < 1000 / info->ifcfg.clockrate) - msg[3] = 1000 / info->ifcfg.clockrate; - - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - msg[3], msg[4]); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - - /* This is wrong. The agreement is not in effect - * until this message is accepted by the device - */ - dev->sync_state = neg_targcomplete; - res = sync; - break; - - /* We initiated the synchronous transfer negotiation, - * and have successfully received a response from the - * target. The synchronous transfer agreement has been - * reached. Note: if the values returned are out of our - * bounds, we must reject the message. - */ - case neg_inprogress: - res = reject; - if (msg[4] <= info->ifcfg.sync_max_depth && - msg[3] >= 1000 / info->ifcfg.clockrate) { - dev->sync_state = neg_complete; - res = sync; - } - break; - } - } -#else - res = reject; -#endif - - switch (res) { - case sync: - dev->period = msg[3]; - dev->sof = msg[4]; - dev->stp = fas216_syncperiod(info, msg[3] * 4); - fas216_set_sync(info, info->SCpnt->device->id); - break; - - case reject: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - - case async: - dev->period = info->ifcfg.asyncperiod / 4; - dev->sof = 0; - dev->stp = info->scsi.async_stp; - fas216_set_sync(info, info->SCpnt->device->id); - break; - - case none: - break; - } -} - -/** - * fas216_handlewide - Handle a wide transfer message - * @info: state structure for interface - * @msg: message from target - * - * Handle a wide transfer message from the target - */ -static void fas216_handlewide(FAS216_Info *info, char *msg) -{ - struct fas216_device *dev = &info->device[info->SCpnt->device->id]; - enum { wide, bit8, none, reject } res = none; - -#ifdef SCSI2_WIDE - switch (msg[0]) { - case MESSAGE_REJECT: - /* Wide transfer request failed. - * Note: SCSI II r10: - * - * SCSI devices that are capable of wide - * data transfers shall not respond to a - * WDTR message with a MESSAGE REJECT message. - * - * Hence, if we get this condition, we never - * reattempt negotiation for this device. - */ - if (dev->wide_state == neg_inprogress) { - dev->wide_state = neg_invalid; - res = bit8; - } - break; - - case EXTENDED_MESSAGE: - switch (dev->wide_state) { - /* We don't accept wide data transfer requests. - * Respond with a MESSAGE REJECT to prevent a - * wide data transfer agreement from being reached. - */ - case neg_invalid: - res = reject; - break; - - /* We were not negotiating a wide data transfer, - * but the device sent is a negotiation request. - * Honour the request by sending back a WDTR - * message containing our capability, limited by - * the targets capability. - */ - default: - fas216_cmd(info, CMD_SETATN); - if (msg[3] > info->ifcfg.wide_max_size) - msg[3] = info->ifcfg.wide_max_size; - - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 4, - EXTENDED_MESSAGE, 2, EXTENDED_WDTR, - msg[3]); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - res = wide; - break; - - /* We initiated the wide data transfer negotiation, - * and have successfully received a response from the - * target. The synchronous transfer agreement has been - * reached. Note: if the values returned are out of our - * bounds, we must reject the message. - */ - case neg_inprogress: - res = reject; - if (msg[3] <= info->ifcfg.wide_max_size) { - dev->wide_state = neg_complete; - res = wide; - } - break; - } - } -#else - res = reject; -#endif - - switch (res) { - case wide: - dev->wide_xfer = msg[3]; - break; - - case reject: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - - case bit8: - dev->wide_xfer = 0; - break; - - case none: - break; - } -} - -/** - * fas216_updateptrs - update data pointers after transfer suspended/paused - * @info: interface's local pointer to update - * @bytes_transferred: number of bytes transferred - * - * Update data pointers after transfer suspended/paused - */ -static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) -{ - Scsi_Pointer *SCp = &info->scsi.SCp; - - fas216_checkmagic(info); - - BUG_ON(bytes_transferred < 0); - - info->SCpnt->request_bufflen -= bytes_transferred; - - while (bytes_transferred != 0) { - if (SCp->this_residual > bytes_transferred) - break; - /* - * We have used up this buffer. Move on to the - * next buffer. - */ - bytes_transferred -= SCp->this_residual; - if (!next_SCp(SCp) && bytes_transferred) { - printk(KERN_WARNING "scsi%d.%c: out of buffers\n", - info->host->host_no, '0' + info->SCpnt->device->id); - return; - } - } - - SCp->this_residual -= bytes_transferred; - if (SCp->this_residual) - SCp->ptr += bytes_transferred; - else - SCp->ptr = NULL; -} - -/** - * fas216_pio - transfer data off of/on to card using programmed IO - * @info: interface to transfer data to/from - * @direction: direction to transfer data (DMA_OUT/DMA_IN) - * - * Transfer data off of/on to card using programmed IO. - * Notes: this is incredibly slow. - */ -static void fas216_pio(FAS216_Info *info, fasdmadir_t direction) -{ - Scsi_Pointer *SCp = &info->scsi.SCp; - - fas216_checkmagic(info); - - if (direction == DMA_OUT) - fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp)); - else - put_next_SCp_byte(SCp, fas216_readb(info, REG_FF)); - - if (SCp->this_residual == 0) - next_SCp(SCp); -} - -static void fas216_set_stc(FAS216_Info *info, unsigned int length) -{ - fas216_writeb(info, REG_STCL, length); - fas216_writeb(info, REG_STCM, length >> 8); - fas216_writeb(info, REG_STCH, length >> 16); -} - -static unsigned int fas216_get_ctc(FAS216_Info *info) -{ - return fas216_readb(info, REG_CTCL) + - (fas216_readb(info, REG_CTCM) << 8) + - (fas216_readb(info, REG_CTCH) << 16); -} - -/** - * fas216_cleanuptransfer - clean up after a transfer has completed. - * @info: interface to clean up - * - * Update the data pointers according to the number of bytes transferred - * on the SCSI bus. - */ -static void fas216_cleanuptransfer(FAS216_Info *info) -{ - unsigned long total, residual, fifo; - fasdmatype_t dmatype = info->dma.transfer_type; - - info->dma.transfer_type = fasdma_none; - - /* - * PIO transfers do not need to be cleaned up. - */ - if (dmatype == fasdma_pio || dmatype == fasdma_none) - return; - - if (dmatype == fasdma_real_all) - total = info->SCpnt->request_bufflen; - else - total = info->scsi.SCp.this_residual; - - residual = fas216_get_ctc(info); - - fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - - fas216_log(info, LOG_BUFFER, "cleaning up from previous " - "transfer: length 0x%06x, residual 0x%x, fifo %d", - total, residual, fifo); - - /* - * If we were performing Data-Out, the transfer counter - * counts down each time a byte is transferred by the - * host to the FIFO. This means we must include the - * bytes left in the FIFO from the transfer counter. - */ - if (info->scsi.phase == PHASE_DATAOUT) - residual += fifo; - - fas216_updateptrs(info, total - residual); -} - -/** - * fas216_transfer - Perform a DMA/PIO transfer off of/on to card - * @info: interface from which device disconnected from - * - * Start a DMA/PIO transfer off of/on to card - */ -static void fas216_transfer(FAS216_Info *info) -{ - fasdmadir_t direction; - fasdmatype_t dmatype; - - fas216_log(info, LOG_BUFFER, - "starttransfer: buffer %p length 0x%06x reqlen 0x%06x", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual, - info->SCpnt->request_bufflen); - - if (!info->scsi.SCp.ptr) { - fas216_log(info, LOG_ERROR, "null buffer passed to " - "fas216_starttransfer"); - print_SCp(&info->scsi.SCp, "SCp: ", "\n"); - print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n"); - return; - } - - /* - * If we have a synchronous transfer agreement in effect, we must - * use DMA mode. If we are using asynchronous transfers, we may - * use DMA mode or PIO mode. - */ - if (info->device[info->SCpnt->device->id].sof) - dmatype = fasdma_real_all; - else - dmatype = fasdma_pio; - - if (info->scsi.phase == PHASE_DATAOUT) - direction = DMA_OUT; - else - direction = DMA_IN; - - if (info->dma.setup) - dmatype = info->dma.setup(info->host, &info->scsi.SCp, - direction, dmatype); - info->dma.transfer_type = dmatype; - - if (dmatype == fasdma_real_all) - fas216_set_stc(info, info->SCpnt->request_bufflen); - else - fas216_set_stc(info, info->scsi.SCp.this_residual); - - switch (dmatype) { - case fasdma_pio: - fas216_log(info, LOG_BUFFER, "PIO transfer"); - fas216_writeb(info, REG_SOF, 0); - fas216_writeb(info, REG_STP, info->scsi.async_stp); - fas216_cmd(info, CMD_TRANSFERINFO); - fas216_pio(info, direction); - break; - - case fasdma_pseudo: - fas216_log(info, LOG_BUFFER, "pseudo transfer"); - fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); - info->dma.pseudo(info->host, &info->scsi.SCp, - direction, info->SCpnt->transfersize); - break; - - case fasdma_real_block: - fas216_log(info, LOG_BUFFER, "block dma transfer"); - fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); - break; - - case fasdma_real_all: - fas216_log(info, LOG_BUFFER, "total dma transfer"); - fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); - break; - - default: - fas216_log(info, LOG_BUFFER | LOG_ERROR, - "invalid FAS216 DMA type"); - break; - } -} - -/** - * fas216_stoptransfer - Stop a DMA transfer onto / off of the card - * @info: interface from which device disconnected from - * - * Called when we switch away from DATA IN or DATA OUT phases. - */ -static void fas216_stoptransfer(FAS216_Info *info) -{ - fas216_checkmagic(info); - - if (info->dma.transfer_type == fasdma_real_all || - info->dma.transfer_type == fasdma_real_block) - info->dma.stop(info->host, &info->scsi.SCp); - - fas216_cleanuptransfer(info); - - if (info->scsi.phase == PHASE_DATAIN) { - unsigned int fifo; - - /* - * If we were performing Data-In, then the FIFO counter - * contains the number of bytes not transferred via DMA - * from the on-board FIFO. Read them manually. - */ - fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - while (fifo && info->scsi.SCp.ptr) { - *info->scsi.SCp.ptr = fas216_readb(info, REG_FF); - fas216_updateptrs(info, 1); - fifo--; - } - } else { - /* - * After a Data-Out phase, there may be unsent - * bytes left in the FIFO. Flush them out. - */ - fas216_cmd(info, CMD_FLUSHFIFO); - } -} - -static void fas216_aborttransfer(FAS216_Info *info) -{ - fas216_checkmagic(info); - - if (info->dma.transfer_type == fasdma_real_all || - info->dma.transfer_type == fasdma_real_block) - info->dma.stop(info->host, &info->scsi.SCp); - - info->dma.transfer_type = fasdma_none; - fas216_cmd(info, CMD_FLUSHFIFO); -} - -/** - * fas216_disconnected_intr - handle device disconnection - * @info: interface from which device disconnected from - * - * Handle device disconnection - */ -static void fas216_disconnect_intr(FAS216_Info *info) -{ - fas216_checkmagic(info); - - fas216_log(info, LOG_CONNECT, "disconnect phase=%02x", - info->scsi.phase); - - msgqueue_flush(&info->scsi.msgs); - - fas216_cmd(info, CMD_ENABLESEL); - - switch (info->scsi.phase) { - case PHASE_SELECTION: /* while selecting - no target */ - case PHASE_SELSTEPS: - fas216_done(info, DID_NO_CONNECT); - break; - - case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ - info->scsi.disconnectable = 1; - info->scsi.reconnected.tag = 0; - info->scsi.phase = PHASE_IDLE; - info->stats.disconnects += 1; - break; - - case PHASE_DONE: /* at end of command - complete */ - fas216_done(info, DID_OK); - break; - - case PHASE_MSGOUT: /* message out - possible ABORT message */ - if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { - info->scsi.aborting = 0; - fas216_done(info, DID_ABORT); - break; - } - - default: /* huh? */ - printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n", - info->host->host_no, fas216_target(info), fas216_drv_phase(info)); - print_debug_list(); - fas216_stoptransfer(info); - fas216_done(info, DID_ERROR); - break; - } -} - -/** - * fas216_reselected_intr - start reconnection of a device - * @info: interface which was reselected - * - * Start reconnection of a device - */ -static void -fas216_reselected_intr(FAS216_Info *info) -{ - unsigned char target, identify_msg, ok; - - fas216_checkmagic(info); - - if ((info->scsi.phase == PHASE_SELECTION || - info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) { - Scsi_Cmnd *SCpnt = info->SCpnt; - - info->origSCpnt = SCpnt; - info->SCpnt = NULL; - - if (info->device[SCpnt->device->id].wide_state == neg_inprogress) - info->device[SCpnt->device->id].wide_state = neg_wait; - if (info->device[SCpnt->device->id].sync_state == neg_inprogress) - info->device[SCpnt->device->id].sync_state = neg_wait; - } - - fas216_log(info, LOG_CONNECT, "reconnect phase=%02X", info->scsi.phase); - - if ((fas216_readb(info, REG_CFIS) & CFIS_CF) != 2) { - printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", - info->host->host_no); - goto initiator_error; - } - - target = fas216_readb(info, REG_FF); - identify_msg = fas216_readb(info, REG_FF); - - ok = 1; - if (!(target & (1 << info->host->this_id))) { - printk(KERN_ERR "scsi%d.H: invalid host id on reselect\n", info->host->host_no); - ok = 0; - } - - if (!(identify_msg & 0x80)) { - printk(KERN_ERR "scsi%d.H: no IDENTIFY message on reselect, got msg %02X\n", - info->host->host_no, identify_msg); - ok = 0; - } - - if (!ok) { - /* - * Something went wrong - send an initiator error to - * the target. - */ - goto initiator_error; - } - - target &= ~(1 << info->host->this_id); - target = ffs(target) - 1; - - identify_msg &= 7; - info->scsi.reconnected.target = target; - info->scsi.reconnected.lun = identify_msg; - info->scsi.reconnected.tag = 0; - - /* set up for synchronous transfers */ - fas216_set_sync(info, target); - - ok = 0; - if (info->scsi.disconnectable && info->SCpnt && - info->SCpnt->device->id == target && info->SCpnt->device->lun == identify_msg) - ok = 1; - - if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg)) - ok = 1; - - msgqueue_flush(&info->scsi.msgs); - if (ok) { - info->scsi.phase = PHASE_RECONNECTED; - fas216_writeb(info, REG_SDID, target); - } else { - /* - * Our command structure not found - abort the - * command on the target. Since we have no - * record of this command, we can't send - * an INITIATOR DETECTED ERROR message. - */ - fas216_cmd(info, CMD_SETATN); - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - } - - fas216_cmd(info, CMD_MSGACCEPTED); - return; - - initiator_error: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - fas216_cmd(info, CMD_MSGACCEPTED); -} - -/** - * fas216_finish_reconnect - finish reconnection sequence for device - * @info: interface which caused function done interrupt - * - * Finish reconnection sequence for device - */ -static void -fas216_finish_reconnect(FAS216_Info *info) -{ - fas216_checkmagic(info); - - fas216_log(info, LOG_CONNECT, "Connected: %1x %1x %02x, reconnected: %1x %1x %02x", - info->SCpnt->device->id, info->SCpnt->device->lun, info->SCpnt->tag, - info->scsi.reconnected.target, info->scsi.reconnected.lun, - info->scsi.reconnected.tag); - - if (info->scsi.disconnectable && info->SCpnt) { - info->scsi.disconnectable = 0; - if (info->SCpnt->device->id == info->scsi.reconnected.target && - info->SCpnt->device->lun == info->scsi.reconnected.lun && - info->SCpnt->tag == info->scsi.reconnected.tag) { - fas216_log(info, LOG_CONNECT, "reconnected"); - } else { - queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); - fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue"); - info->SCpnt = NULL; - } - } - if (!info->SCpnt) { - info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected, - info->scsi.reconnected.target, - info->scsi.reconnected.lun, - info->scsi.reconnected.tag); - fas216_log(info, LOG_CONNECT, "had to get command"); - } - if (!info->SCpnt) { - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - -#if 0 - if (info->scsi.reconnected.tag) - msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag); - else -#endif - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - - info->scsi.phase = PHASE_MSGOUT_EXPECT; - info->scsi.aborting = 1; - } else { - /* - * Restore data pointer from SAVED data pointer - */ - info->scsi.SCp = info->SCpnt->SCp; - fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - } -} - -static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen) -{ - int i; - - switch (message[0]) { - case COMMAND_COMPLETE: - if (msglen != 1) - goto unrecognised; - - printk(KERN_ERR "scsi%d.%c: command complete with no " - "status in MESSAGE_IN?\n", - info->host->host_no, fas216_target(info)); - break; - - case SAVE_POINTERS: - if (msglen != 1) - goto unrecognised; - - /* - * Save current data pointer to SAVED data pointer - * SCSI II standard says that we must not acknowledge - * this until we have really saved pointers. - * NOTE: we DO NOT save the command nor status pointers - * as required by the SCSI II standard. These always - * point to the start of their respective areas. - */ - info->SCpnt->SCp = info->scsi.SCp; - info->SCpnt->SCp.sent_command = 0; - fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, - "save data pointers: [%p, %X]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - break; - - case RESTORE_POINTERS: - if (msglen != 1) - goto unrecognised; - - /* - * Restore current data pointer from SAVED data pointer - */ - info->scsi.SCp = info->SCpnt->SCp; - fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, - "restore data pointers: [%p, 0x%x]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - break; - - case DISCONNECT: - if (msglen != 1) - goto unrecognised; - - info->scsi.phase = PHASE_MSGIN_DISCONNECT; - break; - - case MESSAGE_REJECT: - if (msglen != 1) - goto unrecognised; - - switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) { - case EXTENDED_MESSAGE | EXTENDED_SDTR << 8: - fas216_handlesync(info, message); - break; - - case EXTENDED_MESSAGE | EXTENDED_WDTR << 8: - fas216_handlewide(info, message); - break; - - default: - fas216_log(info, 0, "reject, last message 0x%04x", - fas216_get_last_msg(info, info->scsi.msgin_fifo)); - } - break; - - case NOP: - break; - - case SIMPLE_QUEUE_TAG: - if (msglen < 2) - goto unrecognised; - - /* handled above - print a warning since this is untested */ - fas216_log(info, 0, "reconnect queue tag 0x%02x", message[1]); - break; - - case EXTENDED_MESSAGE: - if (msglen < 3) - goto unrecognised; - - switch (message[2]) { - case EXTENDED_SDTR: /* Sync transfer negotiation request/reply */ - fas216_handlesync(info, message); - break; - - case EXTENDED_WDTR: /* Wide transfer negotiation request/reply */ - fas216_handlewide(info, message); - break; - - default: - goto unrecognised; - } - break; - - default: - goto unrecognised; - } - return; - -unrecognised: - fas216_log(info, 0, "unrecognised message, rejecting"); - printk("scsi%d.%c: message was", info->host->host_no, fas216_target(info)); - for (i = 0; i < msglen; i++) - printk("%s%02X", i & 31 ? " " : "\n ", message[i]); - printk("\n"); - - /* - * Something strange seems to be happening here - - * I can't use SETATN since the chip gives me an - * invalid command interrupt when I do. Weird. - */ -fas216_cmd(info, CMD_NOP); -fas216_dumpstate(info); - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; -fas216_dumpstate(info); -} - -static int fas216_wait_cmd(FAS216_Info *info, int cmd) -{ - int tout; - int stat; - - fas216_cmd(info, cmd); - - for (tout = 1000; tout; tout -= 1) { - stat = fas216_readb(info, REG_STAT); - if (stat & (STAT_INT|STAT_PARITYERROR)) - break; - udelay(1); - } - - return stat; -} - -static int fas216_get_msg_byte(FAS216_Info *info) -{ - unsigned int stat = fas216_wait_cmd(info, CMD_MSGACCEPTED); - - if ((stat & STAT_INT) == 0) - goto timedout; - - if ((stat & STAT_BUSMASK) != STAT_MESGIN) - goto unexpected_phase_change; - - fas216_readb(info, REG_INST); - - stat = fas216_wait_cmd(info, CMD_TRANSFERINFO); - - if ((stat & STAT_INT) == 0) - goto timedout; - - if (stat & STAT_PARITYERROR) - goto parity_error; - - if ((stat & STAT_BUSMASK) != STAT_MESGIN) - goto unexpected_phase_change; - - fas216_readb(info, REG_INST); - - return fas216_readb(info, REG_FF); - -timedout: - fas216_log(info, LOG_ERROR, "timed out waiting for message byte"); - return -1; - -unexpected_phase_change: - fas216_log(info, LOG_ERROR, "unexpected phase change: status = %02x", stat); - return -2; - -parity_error: - fas216_log(info, LOG_ERROR, "parity error during message in phase"); - return -3; -} - -/** - * fas216_message - handle a function done interrupt from FAS216 chip - * @info: interface which caused function done interrupt - * - * Handle a function done interrupt from FAS216 chip - */ -static void fas216_message(FAS216_Info *info) -{ - unsigned char *message = info->scsi.message; - unsigned int msglen = 1; - int msgbyte = 0; - - fas216_checkmagic(info); - - message[0] = fas216_readb(info, REG_FF); - - if (message[0] == EXTENDED_MESSAGE) { - msgbyte = fas216_get_msg_byte(info); - - if (msgbyte >= 0) { - message[1] = msgbyte; - - for (msglen = 2; msglen < message[1] + 2; msglen++) { - msgbyte = fas216_get_msg_byte(info); - - if (msgbyte >= 0) - message[msglen] = msgbyte; - else - break; - } - } - } - - if (msgbyte == -3) - goto parity_error; - -#ifdef DEBUG_MESSAGES - { - int i; - - printk("scsi%d.%c: message in: ", - info->host->host_no, fas216_target(info)); - for (i = 0; i < msglen; i++) - printk("%02X ", message[i]); - printk("\n"); - } -#endif - - if (info->scsi.phase == PHASE_RECONNECTED) { - if (message[0] == SIMPLE_QUEUE_TAG) - info->scsi.reconnected.tag = message[1]; - fas216_finish_reconnect(info); - info->scsi.phase = PHASE_MSGIN; - } - - fas216_parse_message(info, message, msglen); - fas216_cmd(info, CMD_MSGACCEPTED); - return; - -parity_error: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MSG_PARITY_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - fas216_cmd(info, CMD_MSGACCEPTED); - return; -} - -/** - * fas216_send_command - send command after all message bytes have been sent - * @info: interface which caused bus service - * - * Send a command to a target after all message bytes have been sent - */ -static void fas216_send_command(FAS216_Info *info) -{ - int i; - - fas216_checkmagic(info); - - fas216_cmd(info, CMD_NOP|CMD_WITHDMA); - fas216_cmd(info, CMD_FLUSHFIFO); - - /* load command */ - for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++) - fas216_writeb(info, REG_FF, info->SCpnt->cmnd[i]); - - fas216_cmd(info, CMD_TRANSFERINFO); - - info->scsi.phase = PHASE_COMMAND; -} - -/** - * fas216_send_messageout - handle bus service to send a message - * @info: interface which caused bus service - * - * Handle bus service to send a message. - * Note: We do not allow the device to change the data direction! - */ -static void fas216_send_messageout(FAS216_Info *info, int start) -{ - unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); - - fas216_checkmagic(info); - - fas216_cmd(info, CMD_FLUSHFIFO); - - if (tot_msglen) { - struct message *msg; - int msgnr = 0; - - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - int i; - - for (i = start; i < msg->length; i++) - fas216_writeb(info, REG_FF, msg->msg[i]); - - msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); - start = 0; - } - } else - fas216_writeb(info, REG_FF, NOP); - - fas216_cmd(info, CMD_TRANSFERINFO); - - info->scsi.phase = PHASE_MSGOUT; -} - -/** - * fas216_busservice_intr - handle bus service interrupt from FAS216 chip - * @info: interface which caused bus service interrupt - * @stat: Status register contents - * @ssr: SCSI Status register contents - * - * Handle a bus service interrupt from FAS216 chip - */ -static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) -{ - fas216_checkmagic(info); - - fas216_log(info, LOG_BUSSERVICE, - "bus service: stat=%02x ssr=%02x phase=%02x", - stat, ssr, info->scsi.phase); - - switch (info->scsi.phase) { - case PHASE_SELECTION: - if ((ssr & IS_BITS) != IS_MSGBYTESENT) - goto bad_is; - break; - - case PHASE_SELSTEPS: - switch (ssr & IS_BITS) { - case IS_SELARB: - case IS_MSGBYTESENT: - goto bad_is; - - case IS_NOTCOMMAND: - case IS_EARLYPHASE: - if ((stat & STAT_BUSMASK) == STAT_MESGIN) - break; - goto bad_is; - - case IS_COMPLETE: - break; - } - - default: - break; - } - - fas216_cmd(info, CMD_NOP); - -#define STATE(st,ph) ((ph) << 3 | (st)) - /* This table describes the legal SCSI state transitions, - * as described by the SCSI II spec. - */ - switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { - /* Reselmsgin -> Data In */ - case STATE(STAT_DATAIN, PHASE_RECONNECTED): - fas216_finish_reconnect(info); - case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ - case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ - case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ - case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */ - info->scsi.phase = PHASE_DATAIN; - fas216_transfer(info); - return; - - case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */ - case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */ - fas216_cleanuptransfer(info); - fas216_transfer(info); - return; - - /* Reselmsgin -> Data Out */ - case STATE(STAT_DATAOUT, PHASE_RECONNECTED): - fas216_finish_reconnect(info); - case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ - case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ - case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ - case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */ - fas216_cmd(info, CMD_FLUSHFIFO); - info->scsi.phase = PHASE_DATAOUT; - fas216_transfer(info); - return; - - /* Reselmsgin -> Status */ - case STATE(STAT_STATUS, PHASE_RECONNECTED): - fas216_finish_reconnect(info); - goto status; - case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ - case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ - fas216_stoptransfer(info); - case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ - case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ - case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ - case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ - status: - fas216_cmd(info, CMD_INITCMDCOMPLETE); - info->scsi.phase = PHASE_STATUS; - return; - - case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ - case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ - fas216_stoptransfer(info); - case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */ - case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ - case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ - info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - fas216_cmd(info, CMD_FLUSHFIFO); - fas216_cmd(info, CMD_TRANSFERINFO); - info->scsi.phase = PHASE_MSGIN; - return; - - /* Reselmsgin -> Message In */ - case STATE(STAT_MESGIN, PHASE_RECONNECTED): - case STATE(STAT_MESGIN, PHASE_MSGIN): - info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - fas216_cmd(info, CMD_TRANSFERINFO); - return; - - /* Reselmsgin -> Command */ - case STATE(STAT_COMMAND, PHASE_RECONNECTED): - fas216_finish_reconnect(info); - case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ - case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ - fas216_send_command(info); - info->scsi.phase = PHASE_COMMAND; - return; - - - /* - * Selection -> Message Out - */ - case STATE(STAT_MESGOUT, PHASE_SELECTION): - fas216_send_messageout(info, 1); - return; - - /* - * Message Out -> Message Out - */ - case STATE(STAT_MESGOUT, PHASE_SELSTEPS): - case STATE(STAT_MESGOUT, PHASE_MSGOUT): - /* - * If we get another message out phase, this usually - * means some parity error occurred. Resend complete - * set of messages. If we have more than one byte to - * send, we need to assert ATN again. - */ - if (info->device[info->SCpnt->device->id].parity_check) { - /* - * We were testing... good, the device - * supports parity checking. - */ - info->device[info->SCpnt->device->id].parity_check = 0; - info->device[info->SCpnt->device->id].parity_enabled = 1; - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - } - - if (msgqueue_msglength(&info->scsi.msgs) > 1) - fas216_cmd(info, CMD_SETATN); - /*FALLTHROUGH*/ - - /* - * Any -> Message Out - */ - case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT): - fas216_send_messageout(info, 0); - return; - - /* Error recovery rules. - * These either attempt to abort or retry the operation. - * TODO: we need more of these - */ - case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ - /* error - we've sent out all the command bytes - * we have. - * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS - * to include the command bytes sent for this to work - * correctly. - */ - printk(KERN_ERR "scsi%d.%c: " - "target trying to receive more command bytes\n", - info->host->host_no, fas216_target(info)); - fas216_cmd(info, CMD_SETATN); - fas216_set_stc(info, 15); - fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - return; - } - - if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { - printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - msgqueue_flush(&info->scsi.msgs); - fas216_cmd(info, CMD_SETATN); - msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - info->scsi.aborting = 1; - fas216_cmd(info, CMD_TRANSFERINFO); - return; - } - printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat), - fas216_drv_phase(info)); - print_debug_list(); - return; - -bad_is: - fas216_log(info, 0, "bus service at step %d?", ssr & IS_BITS); - fas216_dumpstate(info); - print_debug_list(); - - fas216_done(info, DID_ERROR); -} - -/** - * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip - * @info: interface which caused function done interrupt - * @stat: Status register contents - * @ssr: SCSI Status register contents - * - * Handle a function done interrupt from FAS216 chip - */ -static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) -{ - unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF; - unsigned int status, message; - - fas216_checkmagic(info); - - fas216_log(info, LOG_FUNCTIONDONE, - "function done: stat=%02x ssr=%02x phase=%02x", - stat, ssr, info->scsi.phase); - - switch (info->scsi.phase) { - case PHASE_STATUS: /* status phase - read status and msg */ - if (fifo_len != 2) { - fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len); - } - status = fas216_readb(info, REG_FF); - message = fas216_readb(info, REG_FF); - info->scsi.SCp.Message = message; - info->scsi.SCp.Status = status; - info->scsi.phase = PHASE_DONE; - fas216_cmd(info, CMD_MSGACCEPTED); - break; - - case PHASE_IDLE: /* reselected? */ - case PHASE_MSGIN: /* message in phase */ - case PHASE_RECONNECTED: /* reconnected command */ - if ((stat & STAT_BUSMASK) == STAT_MESGIN) { - info->scsi.msgin_fifo = fifo_len; - fas216_message(info); - break; - } - - default: - fas216_log(info, 0, "internal phase %s for function done?" - " What do I do with this?", - fas216_target(info), fas216_drv_phase(info)); - } -} - -static void fas216_bus_reset(FAS216_Info *info) -{ - neg_t sync_state, wide_state; - int i; - - msgqueue_flush(&info->scsi.msgs); - - info->scsi.reconnected.target = 0; - info->scsi.reconnected.lun = 0; - info->scsi.reconnected.tag = 0; - - wide_state = neg_invalid; - sync_state = neg_invalid; - -#ifdef SCSI2_WIDE - if (info->ifcfg.wide_max_size != 0) - wide_state = neg_wait; -#endif -#ifdef SCSI2_SYNC - if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA)) - sync_state = neg_wait; -#endif - - info->scsi.phase = PHASE_IDLE; - info->SCpnt = NULL; /* bug! */ - memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp)); - - for (i = 0; i < 8; i++) { - info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; - info->device[i].sync_state = sync_state; - info->device[i].wide_state = wide_state; - info->device[i].period = info->ifcfg.asyncperiod / 4; - info->device[i].stp = info->scsi.async_stp; - info->device[i].sof = 0; - info->device[i].wide_xfer = 0; - } - - info->rst_bus_status = 1; - wake_up(&info->eh_wait); -} - -/** - * fas216_intr - handle interrupts to progress a command - * @info: interface to service - * - * Handle interrupts from the interface to progress a command - */ -irqreturn_t fas216_intr(FAS216_Info *info) -{ - unsigned char isr, ssr, stat; - int handled = IRQ_NONE; - - fas216_checkmagic(info); - - stat = fas216_readb(info, REG_STAT); - ssr = fas216_readb(info, REG_IS); - isr = fas216_readb(info, REG_INST); - - add_debug_list(stat, ssr, isr, info->scsi.phase); - - if (stat & STAT_INT) { - if (isr & INST_BUSRESET) { - fas216_log(info, 0, "bus reset detected"); - fas216_bus_reset(info); - scsi_report_bus_reset(info->host, 0); - } else if (isr & INST_ILLEGALCMD) { - fas216_log(info, LOG_ERROR, "illegal command given\n"); - fas216_dumpstate(info); - print_debug_list(); - } else if (isr & INST_DISCONNECT) - fas216_disconnect_intr(info); - else if (isr & INST_RESELECTED) /* reselected */ - fas216_reselected_intr(info); - else if (isr & INST_BUSSERVICE) /* bus service request */ - fas216_busservice_intr(info, stat, ssr); - else if (isr & INST_FUNCDONE) /* function done */ - fas216_funcdone_intr(info, stat, ssr); - else - fas216_log(info, 0, "unknown interrupt received:" - " phase %s isr %02X ssr %02X stat %02X", - fas216_drv_phase(info), isr, ssr, stat); - handled = IRQ_HANDLED; - } - return handled; -} - -static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - int tot_msglen; - - /* following what the ESP driver says */ - fas216_set_stc(info, 0); - fas216_cmd(info, CMD_NOP | CMD_WITHDMA); - - /* flush FIFO */ - fas216_cmd(info, CMD_FLUSHFIFO); - - /* load bus-id and timeout */ - fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); - fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); - - /* synchronous transfers */ - fas216_set_sync(info, SCpnt->device->id); - - tot_msglen = msgqueue_msglength(&info->scsi.msgs); - -#ifdef DEBUG_MESSAGES - { - struct message *msg; - int msgnr = 0, i; - - printk("scsi%d.%c: message out: ", - info->host->host_no, '0' + SCpnt->device->id); - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - printk("{ "); - for (i = 0; i < msg->length; i++) - printk("%02x ", msg->msg[i]); - printk("} "); - } - printk("\n"); - } -#endif - - if (tot_msglen == 1 || tot_msglen == 3) { - /* - * We have an easy message length to send... - */ - struct message *msg; - int msgnr = 0, i; - - info->scsi.phase = PHASE_SELSTEPS; - - /* load message bytes */ - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - for (i = 0; i < msg->length; i++) - fas216_writeb(info, REG_FF, msg->msg[i]); - msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); - } - - /* load command */ - for (i = 0; i < SCpnt->cmd_len; i++) - fas216_writeb(info, REG_FF, SCpnt->cmnd[i]); - - if (tot_msglen == 1) - fas216_cmd(info, CMD_SELECTATN); - else - fas216_cmd(info, CMD_SELECTATN3); - } else { - /* - * We have an unusual number of message bytes to send. - * Load first byte into fifo, and issue SELECT with ATN and - * stop steps. - */ - struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); - - fas216_writeb(info, REG_FF, msg->msg[0]); - msg->fifo = 1; - - fas216_cmd(info, CMD_SELECTATNSTOP); - } -} - -/* - * Decide whether we need to perform a parity test on this device. - * Can also be used to force parity error conditions during initial - * information transfer phase (message out) for test purposes. - */ -static int parity_test(FAS216_Info *info, int target) -{ -#if 0 - if (target == 3) { - info->device[3].parity_check = 0; - return 1; - } -#endif - return info->device[target].parity_check; -} - -static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - int disconnect_ok; - - /* - * claim host busy - */ - info->scsi.phase = PHASE_SELECTION; - info->scsi.SCp = SCpnt->SCp; - info->SCpnt = SCpnt; - info->dma.transfer_type = fasdma_none; - - if (parity_test(info, SCpnt->device->id)) - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE); - else - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - - /* - * Don't allow request sense commands to disconnect. - */ - disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE && - info->device[SCpnt->device->id].disconnect_ok; - - /* - * build outgoing message bytes - */ - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->device->lun)); - - /* - * add tag message if required - */ - if (SCpnt->tag) - msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); - - do { -#ifdef SCSI2_WIDE - if (info->device[SCpnt->device->id].wide_state == neg_wait) { - info->device[SCpnt->device->id].wide_state = neg_inprogress; - msgqueue_addmsg(&info->scsi.msgs, 4, - EXTENDED_MESSAGE, 2, EXTENDED_WDTR, - info->ifcfg.wide_max_size); - break; - } -#endif -#ifdef SCSI2_SYNC - if ((info->device[SCpnt->device->id].sync_state == neg_wait || - info->device[SCpnt->device->id].sync_state == neg_complete) && - (SCpnt->cmnd[0] == REQUEST_SENSE || - SCpnt->cmnd[0] == INQUIRY)) { - info->device[SCpnt->device->id].sync_state = neg_inprogress; - msgqueue_addmsg(&info->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - 1000 / info->ifcfg.clockrate, - info->ifcfg.sync_max_depth); - break; - } -#endif - } while (0); - - __fas216_start_command(info, SCpnt); -} - -static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ -#ifdef SCSI2_TAG - /* - * tagged queuing - allocate a new tag to this command - */ - if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && - SCpnt->cmnd[0] != INQUIRY) { - SCpnt->device->current_tag += 1; - if (SCpnt->device->current_tag == 0) - SCpnt->device->current_tag = 1; - SCpnt->tag = SCpnt->device->current_tag; - } else -#endif - set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); - - info->stats.removes += 1; - switch (SCpnt->cmnd[0]) { - case WRITE_6: - case WRITE_10: - case WRITE_12: - info->stats.writes += 1; - break; - case READ_6: - case READ_10: - case READ_12: - info->stats.reads += 1; - break; - default: - info->stats.miscs += 1; - break; - } -} - -static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - struct message *msg; - - /* - * claim host busy - */ - info->scsi.phase = PHASE_SELECTION; - info->scsi.SCp = SCpnt->SCp; - info->SCpnt = SCpnt; - info->dma.transfer_type = fasdma_none; - - fas216_log(info, LOG_ERROR, "sending bus device reset"); - - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET); - - /* following what the ESP driver says */ - fas216_set_stc(info, 0); - fas216_cmd(info, CMD_NOP | CMD_WITHDMA); - - /* flush FIFO */ - fas216_cmd(info, CMD_FLUSHFIFO); - - /* load bus-id and timeout */ - fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); - fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); - - /* synchronous transfers */ - fas216_set_sync(info, SCpnt->device->id); - - msg = msgqueue_getmsg(&info->scsi.msgs, 0); - - fas216_writeb(info, REG_FF, BUS_DEVICE_RESET); - msg->fifo = 1; - - fas216_cmd(info, CMD_SELECTATNSTOP); -} - -/** - * fas216_kick - kick a command to the interface - * @info: our host interface to kick - * - * Kick a command to the interface, interface should be idle. - * Notes: Interrupts are always disabled! - */ -static void fas216_kick(FAS216_Info *info) -{ - Scsi_Cmnd *SCpnt = NULL; -#define TYPE_OTHER 0 -#define TYPE_RESET 1 -#define TYPE_QUEUE 2 - int where_from = TYPE_OTHER; - - fas216_checkmagic(info); - - /* - * Obtain the next command to process. - */ - do { - if (info->rstSCpnt) { - SCpnt = info->rstSCpnt; - /* don't remove it */ - where_from = TYPE_RESET; - break; - } - - if (info->reqSCpnt) { - SCpnt = info->reqSCpnt; - info->reqSCpnt = NULL; - break; - } - - if (info->origSCpnt) { - SCpnt = info->origSCpnt; - info->origSCpnt = NULL; - break; - } - - /* retrieve next command */ - if (!SCpnt) { - SCpnt = queue_remove_exclude(&info->queues.issue, - info->busyluns); - where_from = TYPE_QUEUE; - break; - } - } while (0); - - if (!SCpnt) /* no command pending - just exit */ - return; - - if (info->scsi.disconnectable && info->SCpnt) { - fas216_log(info, LOG_CONNECT, - "moved command for %d to disconnected queue", - info->SCpnt->device->id); - queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); - info->scsi.disconnectable = 0; - info->SCpnt = NULL; - } - - fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt, - "starting"); - - switch (where_from) { - case TYPE_QUEUE: - fas216_allocate_tag(info, SCpnt); - case TYPE_OTHER: - fas216_start_command(info, SCpnt); - break; - case TYPE_RESET: - fas216_do_bus_device_reset(info, SCpnt); - break; - } - - fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - - /* - * should now get either DISCONNECT or - * (FUNCTION DONE with BUS SERVICE) interrupt - */ -} - -/* - * Clean up from issuing a BUS DEVICE RESET message to a device. - */ -static void -fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) -{ - fas216_log(info, LOG_ERROR, "fas216 device reset complete"); - - info->rstSCpnt = NULL; - info->rst_dev_status = 1; - wake_up(&info->eh_wait); -} - -/** - * fas216_rq_sns_done - Finish processing automatic request sense command - * @info: interface that completed - * @SCpnt: command that completed - * @result: driver byte of result - * - * Finish processing automatic request sense command - */ -static void -fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) -{ - fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, - "request sense complete, result=0x%04x%02x%02x", - result, SCpnt->SCp.Message, SCpnt->SCp.Status); - - if (result != DID_OK || SCpnt->SCp.Status != GOOD) - /* - * Something went wrong. Make sure that we don't - * have valid data in the sense buffer that could - * confuse the higher levels. - */ - memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); -//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id); -//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); } - /* - * Note that we don't set SCpnt->result, since that should - * reflect the status of the command that we were asked by - * the upper layers to process. This would have been set - * correctly by fas216_std_done. - */ - SCpnt->scsi_done(SCpnt); -} - -/** - * fas216_std_done - finish processing of standard command - * @info: interface that completed - * @SCpnt: command that completed - * @result: driver byte of result - * - * Finish processing of standard command - */ -static void -fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) -{ - info->stats.fins += 1; - - SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | - info->scsi.SCp.Status; - - fas216_log_command(info, LOG_CONNECT, SCpnt, - "command complete, result=0x%08x", SCpnt->result); - - /* - * If the driver detected an error, we're all done. - */ - if (host_byte(SCpnt->result) != DID_OK || - msg_byte(SCpnt->result) != COMMAND_COMPLETE) - goto done; - - /* - * If the command returned CHECK_CONDITION or COMMAND_TERMINATED - * status, request the sense information. - */ - if (status_byte(SCpnt->result) == CHECK_CONDITION || - status_byte(SCpnt->result) == COMMAND_TERMINATED) - goto request_sense; - - /* - * If the command did not complete with GOOD status, - * we are all done here. - */ - if (status_byte(SCpnt->result) != GOOD) - goto done; - - /* - * We have successfully completed a command. Make sure that - * we do not have any buffers left to transfer. The world - * is not perfect, and we seem to occasionally hit this. - * It can be indicative of a buggy driver, target or the upper - * levels of the SCSI code. - */ - if (info->scsi.SCp.ptr) { - switch (SCpnt->cmnd[0]) { - case INQUIRY: - case START_STOP: - case MODE_SENSE: - break; - - default: - printk(KERN_ERR "scsi%d.%c: incomplete data transfer " - "detected: res=%08X ptr=%p len=%X CDB: ", - info->host->host_no, '0' + SCpnt->device->id, - SCpnt->result, info->scsi.SCp.ptr, - info->scsi.SCp.this_residual); - print_command(SCpnt->cmnd); - SCpnt->result &= ~(255 << 16); - SCpnt->result |= DID_BAD_TARGET << 16; - goto request_sense; - } - } - -done: - if (SCpnt->scsi_done) { - SCpnt->scsi_done(SCpnt); - return; - } - - panic("scsi%d.H: null scsi_done function in fas216_done", - info->host->host_no); - - -request_sense: - if (SCpnt->cmnd[0] == REQUEST_SENSE) - goto done; - - fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, - "requesting sense"); - memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd)); - SCpnt->cmnd[0] = REQUEST_SENSE; - SCpnt->cmnd[1] = SCpnt->device->lun << 5; - SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->SCp.buffer = NULL; - SCpnt->SCp.buffers_residual = 0; - SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; - SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); - SCpnt->SCp.Message = 0; - SCpnt->SCp.Status = 0; - SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); - SCpnt->sc_data_direction = SCSI_DATA_READ; - SCpnt->use_sg = 0; - SCpnt->tag = 0; - SCpnt->host_scribble = (void *)fas216_rq_sns_done; - - /* - * Place this command into the high priority "request - * sense" slot. This will be the very next command - * executed, unless a target connects to us. - */ - if (info->reqSCpnt) - printk(KERN_WARNING "scsi%d.%c: loosing request command\n", - info->host->host_no, '0' + SCpnt->device->id); - info->reqSCpnt = SCpnt; -} - -/** - * fas216_done - complete processing for current command - * @info: interface that completed - * @result: driver byte of result - * - * Complete processing for current command - */ -static void fas216_done(FAS216_Info *info, unsigned int result) -{ - void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int); - Scsi_Cmnd *SCpnt; - unsigned long flags; - - fas216_checkmagic(info); - - if (!info->SCpnt) - goto no_command; - - SCpnt = info->SCpnt; - info->SCpnt = NULL; - info->scsi.phase = PHASE_IDLE; - - if (info->scsi.aborting) { - fas216_log(info, 0, "uncaught abort - returning DID_ABORT"); - result = DID_ABORT; - info->scsi.aborting = 0; - } - - /* - * Sanity check the completion - if we have zero bytes left - * to transfer, we should not have a valid pointer. - */ - if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { - printk("scsi%d.%c: zero bytes left to transfer, but " - "buffer pointer still valid: ptr=%p len=%08x CDB: ", - info->host->host_no, '0' + SCpnt->device->id, - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - info->scsi.SCp.ptr = NULL; - print_command(SCpnt->cmnd); - } - - /* - * Clear down this command as completed. If we need to request - * the sense information, fas216_kick will re-assert the busy - * status. - */ - info->device[SCpnt->device->id].parity_check = 0; - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); - - fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble; - fn(info, SCpnt, result); - - if (info->scsi.irq != NO_IRQ) { - spin_lock_irqsave(&info->host_lock, flags); - if (info->scsi.phase == PHASE_IDLE) - fas216_kick(info); - spin_unlock_irqrestore(&info->host_lock, flags); - } - return; - -no_command: - panic("scsi%d.H: null command in fas216_done", - info->host->host_no); -} - -/** - * fas216_queue_command - queue a command for adapter to process. - * @SCpnt: Command to queue - * @done: done function to call once command is complete - * - * Queue a command for adapter to process. - * Returns: 0 on success, else error. - * Notes: io_request_lock is held, interrupts are disabled. - */ -int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - int result; - - fas216_checkmagic(info); - - fas216_log_command(info, LOG_CONNECT, SCpnt, - "received command (%p)", SCpnt); - - SCpnt->scsi_done = done; - SCpnt->host_scribble = (void *)fas216_std_done; - SCpnt->result = 0; - - init_SCp(SCpnt); - - info->stats.queues += 1; - SCpnt->tag = 0; - - spin_lock(&info->host_lock); - - /* - * Add command into execute queue and let it complete under - * whatever scheme we're using. - */ - result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt); - - /* - * If we successfully added the command, - * kick the interface to get it moving. - */ - if (result == 0 && info->scsi.phase == PHASE_IDLE) - fas216_kick(info); - spin_unlock(&info->host_lock); - - fas216_log_target(info, LOG_CONNECT, -1, "queue %s", - result ? "failure" : "success"); - - return result; -} - -/** - * fas216_internal_done - trigger restart of a waiting thread in fas216_command - * @SCpnt: Command to wake - * - * Trigger restart of a waiting thread in fas216_command - */ -static void fas216_internal_done(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - - fas216_checkmagic(info); - - info->internal_done = 1; -} - -/** - * fas216_command - queue a command for adapter to process. - * @SCpnt: Command to queue - * - * Queue a command for adapter to process. - * Returns: scsi result code. - * Notes: io_request_lock is held, interrupts are disabled. - */ -int fas216_command(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - - fas216_checkmagic(info); - - /* - * We should only be using this if we don't have an interrupt. - * Provide some "incentive" to use the queueing code. - */ - if (info->scsi.irq != NO_IRQ) - BUG(); - - info->internal_done = 0; - fas216_queue_command(SCpnt, fas216_internal_done); - - /* - * This wastes time, since we can't return until the command is - * complete. We can't sleep either since we may get re-entered! - * However, we must re-enable interrupts, or else we'll be - * waiting forever. - */ - spin_unlock_irq(info->host->host_lock); - - while (!info->internal_done) { - /* - * If we don't have an IRQ, then we must poll the card for - * it's interrupt, and use that to call this driver's - * interrupt routine. That way, we keep the command - * progressing. Maybe we can add some inteligence here - * and go to sleep if we know that the device is going - * to be some time (eg, disconnected). - */ - if (fas216_readb(info, REG_STAT) & STAT_INT) { - spin_lock_irq(info->host->host_lock); - fas216_intr(info); - spin_unlock_irq(info->host->host_lock); - } - } - - spin_lock_irq(info->host->host_lock); - - return SCpnt->result; -} - -/* - * Error handler timeout function. Indicate that we timed out, - * and wake up any error handler process so it can continue. - */ -static void fas216_eh_timer(unsigned long data) -{ - FAS216_Info *info = (FAS216_Info *)data; - - fas216_log(info, LOG_ERROR, "error handling timed out\n"); - - del_timer(&info->eh_timer); - - if (info->rst_bus_status == 0) - info->rst_bus_status = -1; - if (info->rst_dev_status == 0) - info->rst_dev_status = -1; - - wake_up(&info->eh_wait); -} - -enum res_find { - res_failed, /* not found */ - res_success, /* command on issue queue */ - res_hw_abort /* command on disconnected dev */ -}; - -/** - * fas216_do_abort - decide how to abort a command - * @SCpnt: command to abort - * - * Decide how to abort a command. - * Returns: abort status - */ -static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - enum res_find res = res_failed; - - if (queue_remove_cmd(&info->queues.issue, SCpnt)) { - /* - * The command was on the issue queue, and has not been - * issued yet. We can remove the command from the queue, - * and acknowledge the abort. Neither the device nor the - * interface know about the command. - */ - printk("on issue queue "); - - res = res_success; - } else if (queue_remove_cmd(&info->queues.disconnected, SCpnt)) { - /* - * The command was on the disconnected queue. We must - * reconnect with the device if possible, and send it - * an abort message. - */ - printk("on disconnected queue "); - - res = res_hw_abort; - } else if (info->SCpnt == SCpnt) { - printk("executing "); - - switch (info->scsi.phase) { - /* - * If the interface is idle, and the command is 'disconnectable', - * then it is the same as on the disconnected queue. - */ - case PHASE_IDLE: - if (info->scsi.disconnectable) { - info->scsi.disconnectable = 0; - info->SCpnt = NULL; - res = res_hw_abort; - } - break; - - default: - break; - } - } else if (info->origSCpnt == SCpnt) { - /* - * The command will be executed next, but a command - * is currently using the interface. This is similar to - * being on the issue queue, except the busylun bit has - * been set. - */ - info->origSCpnt = NULL; - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); - printk("waiting for execution "); - res = res_success; - } else - printk("unknown "); - - return res; -} - -/** - * fas216_eh_abort - abort this command - * @SCpnt: command to abort - * - * Abort this command. - * Returns: FAILED if unable to abort - * Notes: io_request_lock is taken, and irqs are disabled - */ -int fas216_eh_abort(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - int result = FAILED; - - fas216_checkmagic(info); - - info->stats.aborts += 1; - - printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no); - print_command(SCpnt->data_cmnd); - - print_debug_list(); - fas216_dumpstate(info); - - printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt); - - switch (fas216_find_command(info, SCpnt)) { - /* - * We found the command, and cleared it out. Either - * the command is still known to be executing on the - * target, or the busylun bit is not set. - */ - case res_success: - printk("success\n"); - result = SUCCESS; - break; - - /* - * We need to reconnect to the target and send it an - * ABORT or ABORT_TAG message. We can only do this - * if the bus is free. - */ - case res_hw_abort: - - - /* - * We are unable to abort the command for some reason. - */ - default: - case res_failed: - printk("failed\n"); - break; - } - - return result; -} - -/** - * fas216_eh_device_reset - Reset the device associated with this command - * @SCpnt: command specifing device to reset - * - * Reset the device associated with this command. - * Returns: FAILED if unable to reset. - * Notes: We won't be re-entered, so we'll only have one device - * reset on the go at one time. - */ -int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - unsigned long flags; - int i, res = FAILED, target = SCpnt->device->id; - - fas216_log(info, LOG_ERROR, "device reset for target %d", target); - - spin_lock_irqsave(&info->host_lock, flags); - - do { - /* - * If we are currently connected to a device, and - * it is the device we want to reset, there is - * nothing we can do here. Chances are it is stuck, - * and we need a bus reset. - */ - if (info->SCpnt && !info->scsi.disconnectable && - info->SCpnt->device->id == SCpnt->device->id) - break; - - /* - * We're going to be resetting this device. Remove - * all pending commands from the driver. By doing - * so, we guarantee that we won't touch the command - * structures except to process the reset request. - */ - queue_remove_all_target(&info->queues.issue, target); - queue_remove_all_target(&info->queues.disconnected, target); - if (info->origSCpnt && info->origSCpnt->device->id == target) - info->origSCpnt = NULL; - if (info->reqSCpnt && info->reqSCpnt->device->id == target) - info->reqSCpnt = NULL; - for (i = 0; i < 8; i++) - clear_bit(target * 8 + i, info->busyluns); - - /* - * Hijack this SCSI command structure to send - * a bus device reset message to this device. - */ - SCpnt->host_scribble = (void *)fas216_devicereset_done; - - info->rst_dev_status = 0; - info->rstSCpnt = SCpnt; - - if (info->scsi.phase == PHASE_IDLE) - fas216_kick(info); - - mod_timer(&info->eh_timer, 30 * HZ); - spin_unlock_irqrestore(&info->host_lock, flags); - - /* - * Wait up to 30 seconds for the reset to complete. - */ - wait_event(info->eh_wait, info->rst_dev_status); - - del_timer_sync(&info->eh_timer); - spin_lock_irqsave(&info->host_lock, flags); - info->rstSCpnt = NULL; - - if (info->rst_dev_status == 1) - res = SUCCESS; - } while (0); - - SCpnt->host_scribble = NULL; - spin_unlock_irqrestore(&info->host_lock, flags); - - fas216_log(info, LOG_ERROR, "device reset complete: %s\n", - res == SUCCESS ? "success" : "failed"); - - return res; -} - -/** - * fas216_eh_bus_reset - Reset the bus associated with the command - * @SCpnt: command specifing bus to reset - * - * Reset the bus associated with the command. - * Returns: FAILED if unable to reset. - * Notes: Further commands are blocked. - */ -int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - unsigned long flags; - Scsi_Device *SDpnt; - - fas216_checkmagic(info); - fas216_log(info, LOG_ERROR, "resetting bus"); - - info->stats.bus_resets += 1; - - spin_lock_irqsave(&info->host_lock, flags); - - /* - * Stop all activity on this interface. - */ - fas216_aborttransfer(info); - fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); - - /* - * Clear any pending interrupts. - */ - while (fas216_readb(info, REG_STAT) & STAT_INT) - fas216_readb(info, REG_INST); - - info->rst_bus_status = 0; - - /* - * For each attached hard-reset device, clear out - * all command structures. Leave the running - * command in place. - */ - list_for_each_entry(SDpnt, &info->host->my_devices, siblings) { - int i; - - if (SDpnt->soft_reset) - continue; - - queue_remove_all_target(&info->queues.issue, SDpnt->id); - queue_remove_all_target(&info->queues.disconnected, SDpnt->id); - if (info->origSCpnt && info->origSCpnt->device->id == SDpnt->id) - info->origSCpnt = NULL; - if (info->reqSCpnt && info->reqSCpnt->device->id == SDpnt->id) - info->reqSCpnt = NULL; - info->SCpnt = NULL; - - for (i = 0; i < 8; i++) - clear_bit(SDpnt->id * 8 + i, info->busyluns); - } - - /* - * Reset the SCSI bus. Device cleanup happens in - * the interrupt handler. - */ - fas216_cmd(info, CMD_RESETSCSI); - - mod_timer(&info->eh_timer, jiffies + HZ); - spin_unlock_irqrestore(&info->host_lock, flags); - - /* - * Wait one second for the interrupt. - */ - wait_event(info->eh_wait, info->rst_bus_status); - del_timer_sync(&info->eh_timer); - - fas216_log(info, LOG_ERROR, "bus reset complete: %s\n", - info->rst_bus_status == 1 ? "success" : "failed"); - - return info->rst_bus_status == 1 ? SUCCESS : FAILED; -} - -/** - * fas216_init_chip - Initialise FAS216 state after reset - * @info: state structure for interface - * - * Initialise FAS216 state after reset - */ -static void fas216_init_chip(FAS216_Info *info) -{ - fas216_writeb(info, REG_CLKF, fas216_clockrate(info->ifcfg.clockrate)); - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]); - fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); - fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); - fas216_writeb(info, REG_SOF, 0); - fas216_writeb(info, REG_STP, info->scsi.async_stp); - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); -} - -/** - * fas216_eh_host_reset - Reset the host associated with this command - * @SCpnt: command specifing host to reset - * - * Reset the host associated with this command. - * Returns: FAILED if unable to reset. - * Notes: io_request_lock is taken, and irqs are disabled - */ -int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - - fas216_checkmagic(info); - - printk("scsi%d.%c: %s: resetting host\n", - info->host->host_no, '0' + SCpnt->device->id, __FUNCTION__); - - /* - * Reset the SCSI chip. - */ - fas216_cmd(info, CMD_RESETCHIP); - - /* - * Ugly ugly ugly! - * We need to release the host_lock and enable - * IRQs if we sleep, but we must relock and disable - * IRQs after the sleep. - */ - spin_unlock_irq(info->host->host_lock); - scsi_sleep(50 * HZ/100); - spin_lock_irq(info->host->host_lock); - - /* - * Release the SCSI reset. - */ - fas216_cmd(info, CMD_NOP); - - fas216_init_chip(info); - - return SUCCESS; -} - -#define TYPE_UNKNOWN 0 -#define TYPE_NCR53C90 1 -#define TYPE_NCR53C90A 2 -#define TYPE_NCR53C9x 3 -#define TYPE_Am53CF94 4 -#define TYPE_EmFAS216 5 -#define TYPE_QLFAS216 6 - -static char *chip_types[] = { - "unknown", - "NS NCR53C90", - "NS NCR53C90A", - "NS NCR53C9x", - "AMD Am53CF94", - "Emulex FAS216", - "QLogic FAS216" -}; - -static int fas216_detect_type(FAS216_Info *info) -{ - int family, rev; - - /* - * Reset the chip. - */ - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - udelay(50); - fas216_writeb(info, REG_CMD, CMD_NOP); - - /* - * Check to see if control reg 2 is present. - */ - fas216_writeb(info, REG_CNTL3, 0); - fas216_writeb(info, REG_CNTL2, CNTL2_S2FE); - - /* - * If we are unable to read back control reg 2 - * correctly, it is not present, and we have a - * NCR53C90. - */ - if ((fas216_readb(info, REG_CNTL2) & (~0xe0)) != CNTL2_S2FE) - return TYPE_NCR53C90; - - /* - * Now, check control register 3 - */ - fas216_writeb(info, REG_CNTL2, 0); - fas216_writeb(info, REG_CNTL3, 0); - fas216_writeb(info, REG_CNTL3, 5); - - /* - * If we are unable to read the register back - * correctly, we have a NCR53C90A - */ - if (fas216_readb(info, REG_CNTL3) != 5) - return TYPE_NCR53C90A; - - /* - * Now read the ID from the chip. - */ - fas216_writeb(info, REG_CNTL3, 0); - - fas216_writeb(info, REG_CNTL3, CNTL3_ADIDCHK); - fas216_writeb(info, REG_CNTL3, 0); - - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - udelay(50); - fas216_writeb(info, REG_CMD, CMD_WITHDMA | CMD_NOP); - - fas216_writeb(info, REG_CNTL2, CNTL2_ENF); - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - udelay(50); - fas216_writeb(info, REG_CMD, CMD_NOP); - - rev = fas216_readb(info, REG_ID); - family = rev >> 3; - rev &= 7; - - switch (family) { - case 0x01: - if (rev == 4) - return TYPE_Am53CF94; - break; - - case 0x02: - switch (rev) { - case 2: - return TYPE_EmFAS216; - case 3: - return TYPE_QLFAS216; - } - break; - - default: - break; - } - printk("family %x rev %x\n", family, rev); - return TYPE_NCR53C9x; -} - -/** - * fas216_reset_state - Initialise driver internal state - * @info: state to initialise - * - * Initialise driver internal state - */ -static void fas216_reset_state(FAS216_Info *info) -{ - int i; - - fas216_checkmagic(info); - - fas216_bus_reset(info); - - /* - * Clear out all stale info in our state structure - */ - memset(info->busyluns, 0, sizeof(info->busyluns)); - info->scsi.disconnectable = 0; - info->scsi.aborting = 0; - - for (i = 0; i < 8; i++) { - info->device[i].parity_enabled = 0; - info->device[i].parity_check = 1; - } - - /* - * Drain all commands on disconnected queue - */ - while (queue_remove(&info->queues.disconnected) != NULL); - - /* - * Remove executing commands. - */ - info->SCpnt = NULL; - info->reqSCpnt = NULL; - info->rstSCpnt = NULL; - info->origSCpnt = NULL; -} - -/** - * fas216_init - initialise FAS/NCR/AMD SCSI structures. - * @host: a driver-specific filled-out structure - * - * Initialise FAS/NCR/AMD SCSI structures. - * Returns: 0 on success - */ -int fas216_init(struct Scsi_Host *host) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - - info->magic_start = MAGIC; - info->magic_end = MAGIC; - info->host = host; - info->scsi.cfg[0] = host->this_id | CNTL1_PERE; - info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE; - info->scsi.cfg[2] = info->ifcfg.cntl3 | - CNTL3_ADIDCHK | CNTL3_G2CB | CNTL3_LBTM; - info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); - - info->rst_dev_status = -1; - info->rst_bus_status = -1; - init_waitqueue_head(&info->eh_wait); - init_timer(&info->eh_timer); - info->eh_timer.data = (unsigned long)info; - info->eh_timer.function = fas216_eh_timer; - - spin_lock_init(&info->host_lock); - - memset(&info->stats, 0, sizeof(info->stats)); - - msgqueue_initialise(&info->scsi.msgs); - - if (!queue_initialise(&info->queues.issue)) - return -ENOMEM; - - if (!queue_initialise(&info->queues.disconnected)) { - queue_free(&info->queues.issue); - return -ENOMEM; - } - - return 0; -} - -/** - * fas216_add - initialise FAS/NCR/AMD SCSI ic. - * @host: a driver-specific filled-out structure - * @dev: parent device - * - * Initialise FAS/NCR/AMD SCSI ic. - * Returns: 0 on success - */ -int fas216_add(struct Scsi_Host *host, struct device *dev) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - int type, ret; - - fas216_reset_state(info); - type = fas216_detect_type(info); - info->scsi.type = chip_types[type]; - - udelay(300); - - /* - * Initialise the chip correctly. - */ - fas216_init_chip(info); - - /* - * Reset the SCSI bus. We don't want to see - * the resulting reset interrupt, so mask it - * out. - */ - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_DISR); - fas216_writeb(info, REG_CMD, CMD_RESETSCSI); - - /* - * scsi standard says wait 250ms - */ - spin_unlock_irq(info->host->host_lock); - scsi_sleep(100*HZ/100); - spin_lock_irq(info->host->host_lock); - - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - fas216_readb(info, REG_INST); - - fas216_checkmagic(info); - - ret = scsi_add_host(host, dev); - if (ret) - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - - return ret; -} - -void fas216_remove(struct Scsi_Host *host) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - - fas216_checkmagic(info); - scsi_remove_host(host); - - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); -} - -/** - * fas216_release - release all resources for FAS/NCR/AMD SCSI ic. - * @host: a driver-specific filled-out structure - * - * release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. - */ -void fas216_release(struct Scsi_Host *host) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - - queue_free(&info->queues.disconnected); - queue_free(&info->queues.issue); -} - -int fas216_print_host(FAS216_Info *info, char *buffer) -{ - return sprintf(buffer, - "\n" - "Chip : %s\n" - " Address: 0x%08lx\n" - " IRQ : %d\n" - " DMA : %d\n", - info->scsi.type, info->host->io_port, - info->host->irq, info->host->dma_channel); -} - -int fas216_print_stats(FAS216_Info *info, char *buffer) -{ - char *p = buffer; - - p += sprintf(p, "\n" - "Command Statistics:\n" - " Queued : %u\n" - " Issued : %u\n" - " Completed : %u\n" - " Reads : %u\n" - " Writes : %u\n" - " Others : %u\n" - " Disconnects: %u\n" - " Aborts : %u\n" - " Bus resets : %u\n" - " Host resets: %u\n", - info->stats.queues, info->stats.removes, - info->stats.fins, info->stats.reads, - info->stats.writes, info->stats.miscs, - info->stats.disconnects, info->stats.aborts, - info->stats.bus_resets, info->stats.host_resets); - - return p - buffer; -} - -int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer) -{ - struct fas216_device *dev = &info->device[scd->id]; - int len = 0; - char *p; - - proc_print_scsidevice(scd, buffer, &len, 0); - p = buffer + len; - - p += sprintf(p, " Extensions: "); - - if (scd->tagged_supported) - p += sprintf(p, "TAG %sabled [%d] ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); - - p += sprintf(p, "%s\n", dev->parity_enabled ? "parity" : ""); - - p += sprintf(p, " Transfers : %d-bit ", - 8 << dev->wide_xfer); - - if (dev->sof) - p += sprintf(p, "sync offset %d, %d ns\n", - dev->sof, dev->period * 4); - else - p += sprintf(p, "async\n"); - - return p - buffer; -} - -EXPORT_SYMBOL(fas216_init); -EXPORT_SYMBOL(fas216_add); -EXPORT_SYMBOL(fas216_queue_command); -EXPORT_SYMBOL(fas216_command); -EXPORT_SYMBOL(fas216_intr); -EXPORT_SYMBOL(fas216_remove); -EXPORT_SYMBOL(fas216_release); -EXPORT_SYMBOL(fas216_eh_abort); -EXPORT_SYMBOL(fas216_eh_device_reset); -EXPORT_SYMBOL(fas216_eh_bus_reset); -EXPORT_SYMBOL(fas216_eh_host_reset); -EXPORT_SYMBOL(fas216_print_host); -EXPORT_SYMBOL(fas216_print_stats); -EXPORT_SYMBOL(fas216_print_device); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core"); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/fas216.h linux-2.5.69-bk14/drivers/acorn/scsi/fas216.h --- linux-2.5.69-bk13/drivers/acorn/scsi/fas216.h 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/fas216.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,401 +0,0 @@ -/* - * linux/drivers/acorn/scsi/fas216.h - * - * Copyright (C) 1997-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * FAS216 generic driver - */ -#ifndef FAS216_H -#define FAS216_H - -#ifndef NO_IRQ -#define NO_IRQ 255 -#endif - -#include "queue.h" -#include "msgqueue.h" - -/* FAS register definitions */ - -/* transfer count low */ -#define REG_CTCL (0) -#define REG_STCL (0) - -/* transfer count medium */ -#define REG_CTCM (1) -#define REG_STCM (1) - -/* fifo data */ -#define REG_FF (2) - -/* command */ -#define REG_CMD (3) -#define CMD_NOP 0x00 -#define CMD_FLUSHFIFO 0x01 -#define CMD_RESETCHIP 0x02 -#define CMD_RESETSCSI 0x03 - -#define CMD_TRANSFERINFO 0x10 -#define CMD_INITCMDCOMPLETE 0x11 -#define CMD_MSGACCEPTED 0x12 -#define CMD_PADBYTES 0x18 -#define CMD_SETATN 0x1a -#define CMD_RSETATN 0x1b - -#define CMD_SELECTWOATN 0x41 -#define CMD_SELECTATN 0x42 -#define CMD_SELECTATNSTOP 0x43 -#define CMD_ENABLESEL 0x44 -#define CMD_DISABLESEL 0x45 -#define CMD_SELECTATN3 0x46 -#define CMD_RESEL3 0x47 - -#define CMD_WITHDMA 0x80 - -/* status register (read) */ -#define REG_STAT (4) -#define STAT_IO (1 << 0) /* IO phase */ -#define STAT_CD (1 << 1) /* CD phase */ -#define STAT_MSG (1 << 2) /* MSG phase */ -#define STAT_TRANSFERDONE (1 << 3) /* Transfer completed */ -#define STAT_TRANSFERCNTZ (1 << 4) /* Transfer counter is zero */ -#define STAT_PARITYERROR (1 << 5) /* Parity error */ -#define STAT_REALBAD (1 << 6) /* Something bad */ -#define STAT_INT (1 << 7) /* Interrupt */ - -#define STAT_BUSMASK (STAT_MSG|STAT_CD|STAT_IO) -#define STAT_DATAOUT (0) /* Data out */ -#define STAT_DATAIN (STAT_IO) /* Data in */ -#define STAT_COMMAND (STAT_CD) /* Command out */ -#define STAT_STATUS (STAT_CD|STAT_IO) /* Status In */ -#define STAT_MESGOUT (STAT_MSG|STAT_CD) /* Message out */ -#define STAT_MESGIN (STAT_MSG|STAT_CD|STAT_IO) /* Message In */ - -/* bus ID for select / reselect */ -#define REG_SDID (4) -#define BUSID(target) ((target) & 7) - -/* Interrupt status register (read) */ -#define REG_INST (5) -#define INST_SELWOATN (1 << 0) /* Select w/o ATN */ -#define INST_SELATN (1 << 1) /* Select w/ATN */ -#define INST_RESELECTED (1 << 2) /* Reselected */ -#define INST_FUNCDONE (1 << 3) /* Function done */ -#define INST_BUSSERVICE (1 << 4) /* Bus service */ -#define INST_DISCONNECT (1 << 5) /* Disconnect */ -#define INST_ILLEGALCMD (1 << 6) /* Illegal command */ -#define INST_BUSRESET (1 << 7) /* SCSI Bus reset */ - -/* Timeout register (write) */ -#define REG_STIM (5) - -/* Sequence step register (read) */ -#define REG_IS (6) -#define IS_BITS 0x07 -#define IS_SELARB 0x00 /* Select & Arb ok */ -#define IS_MSGBYTESENT 0x01 /* One byte message sent*/ -#define IS_NOTCOMMAND 0x02 /* Not in command state */ -#define IS_EARLYPHASE 0x03 /* Early phase change */ -#define IS_COMPLETE 0x04 /* Command ok */ -#define IS_SOF 0x08 /* Sync off flag */ - -/* Transfer period step (write) */ -#define REG_STP (6) - -/* Synchronous Offset (write) */ -#define REG_SOF (7) - -/* Fifo state register (read) */ -#define REG_CFIS (7) -#define CFIS_CF 0x1f /* Num bytes in FIFO */ -#define CFIS_IS 0xe0 /* Step */ - -/* config register 1 */ -#define REG_CNTL1 (8) -#define CNTL1_CID (7 << 0) /* Chip ID */ -#define CNTL1_STE (1 << 3) /* Self test enable */ -#define CNTL1_PERE (1 << 4) /* Parity enable reporting en. */ -#define CNTL1_PTE (1 << 5) /* Parity test enable */ -#define CNTL1_DISR (1 << 6) /* Disable Irq on SCSI reset */ -#define CNTL1_ETM (1 << 7) /* Extended Timing Mode */ - -/* Clock conversion factor (read) */ -#define REG_CLKF (9) -#define CLKF_F37MHZ 0x00 /* 35.01 - 40 MHz */ -#define CLKF_F10MHZ 0x02 /* 10 MHz */ -#define CLKF_F12MHZ 0x03 /* 10.01 - 15 MHz */ -#define CLKF_F17MHZ 0x04 /* 15.01 - 20 MHz */ -#define CLKF_F22MHZ 0x05 /* 20.01 - 25 MHz */ -#define CLKF_F27MHZ 0x06 /* 25.01 - 30 MHz */ -#define CLKF_F32MHZ 0x07 /* 30.01 - 35 MHz */ - -/* Chip test register (write) */ -#define REG_FTM (10) -#define TEST_FTM 0x01 /* Force target mode */ -#define TEST_FIM 0x02 /* Force initiator mode */ -#define TEST_FHI 0x04 /* Force high impedance mode */ - -/* Configuration register 2 (read/write) */ -#define REG_CNTL2 (11) -#define CNTL2_PGDP (1 << 0) /* Pass Th/Generate Data Parity */ -#define CNTL2_PGRP (1 << 1) /* Pass Th/Generate Reg Parity */ -#define CNTL2_ACDPE (1 << 2) /* Abort on Cmd/Data Parity Err */ -#define CNTL2_S2FE (1 << 3) /* SCSI2 Features Enable */ -#define CNTL2_TSDR (1 << 4) /* Tristate DREQ */ -#define CNTL2_SBO (1 << 5) /* Select Byte Order */ -#define CNTL2_ENF (1 << 6) /* Enable features */ -#define CNTL2_DAE (1 << 7) /* Data Alignment Enable */ - -/* Configuration register 3 (read/write) */ -#define REG_CNTL3 (12) -#define CNTL3_BS8 (1 << 0) /* Burst size 8 */ -#define CNTL3_MDM (1 << 1) /* Modify DMA mode */ -#define CNTL3_LBTM (1 << 2) /* Last Byte Transfer mode */ -#define CNTL3_FASTCLK (1 << 3) /* Fast SCSI clocking */ -#define CNTL3_FASTSCSI (1 << 4) /* Fast SCSI */ -#define CNTL3_G2CB (1 << 5) /* Group2 SCSI support */ -#define CNTL3_QTAG (1 << 6) /* Enable 3 byte msgs */ -#define CNTL3_ADIDCHK (1 << 7) /* Additional ID check */ - -/* High transfer count (read/write) */ -#define REG_CTCH (14) -#define REG_STCH (14) - -/* ID register (read only) */ -#define REG_ID (14) - -/* Data alignment */ -#define REG_DAL (15) - -typedef enum { - PHASE_IDLE, /* we're not planning on doing anything */ - PHASE_SELECTION, /* selecting a device */ - PHASE_SELSTEPS, /* selection with command steps */ - PHASE_COMMAND, /* command sent */ - PHASE_MESSAGESENT, /* selected, and we're sending cmd */ - PHASE_RECONNECTED, /* reconnected */ - PHASE_DATAOUT, /* data out to device */ - PHASE_DATAIN, /* data in from device */ - PHASE_MSGIN, /* message in from device */ - PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */ - PHASE_MSGOUT, /* after message out phase */ - PHASE_MSGOUT_EXPECT, /* expecting message out */ - PHASE_STATUS, /* status from device */ - PHASE_DONE /* Command complete */ -} phase_t; - -typedef enum { - DMA_OUT, /* DMA from memory to chip */ - DMA_IN /* DMA from chip to memory */ -} fasdmadir_t; - -typedef enum { - fasdma_none, /* No dma */ - fasdma_pio, /* PIO mode */ - fasdma_pseudo, /* Pseudo DMA */ - fasdma_real_block, /* Real DMA, on block by block basis */ - fasdma_real_all /* Real DMA, on request by request */ -} fasdmatype_t; - -typedef enum { - neg_wait, /* Negociate with device */ - neg_inprogress, /* Negociation sent */ - neg_complete, /* Negociation complete */ - neg_targcomplete, /* Target completed negociation */ - neg_invalid /* Negociation not supported */ -} neg_t; - -#define MAGIC 0x441296bdUL -#define NR_MSGS 8 - -#define FASCAP_DMA (1 << 0) -#define FASCAP_PSEUDODMA (1 << 1) - -typedef struct { - unsigned long magic_start; - spinlock_t host_lock; - struct Scsi_Host *host; /* host */ - Scsi_Cmnd *SCpnt; /* currently processing command */ - Scsi_Cmnd *origSCpnt; /* original connecting command */ - Scsi_Cmnd *reqSCpnt; /* request sense command */ - Scsi_Cmnd *rstSCpnt; /* reset command */ - Scsi_Cmnd *pending_SCpnt[8]; /* per-device pending commands */ - int next_pending; /* next pending device */ - - /* - * Error recovery - */ - wait_queue_head_t eh_wait; - struct timer_list eh_timer; - unsigned int rst_dev_status; - unsigned int rst_bus_status; - - /* driver information */ - struct { - phase_t phase; /* current phase */ - void *io_base; /* iomem base of FAS216 */ - unsigned int io_port; /* base address of FAS216 */ - unsigned int io_shift; /* shift to adjust reg offsets by */ - unsigned char cfg[4]; /* configuration registers */ - const char *type; /* chip type */ - unsigned int irq; /* interrupt */ - - struct { - unsigned char target; /* reconnected target */ - unsigned char lun; /* reconnected lun */ - unsigned char tag; /* reconnected tag */ - } reconnected; - - Scsi_Pointer SCp; /* current commands data pointer */ - - MsgQueue_t msgs; /* message queue for connected device */ - - unsigned int async_stp; /* Async transfer STP value */ - unsigned char msgin_fifo; /* bytes in fifo at time of message in */ - unsigned char message[256]; /* last message received from device */ - - unsigned char disconnectable:1; /* this command can be disconnected */ - unsigned char aborting:1; /* aborting command */ - } scsi; - - /* statistics information */ - struct { - unsigned int queues; - unsigned int removes; - unsigned int fins; - unsigned int reads; - unsigned int writes; - unsigned int miscs; - unsigned int disconnects; - unsigned int aborts; - unsigned int bus_resets; - unsigned int host_resets; - } stats; - - /* configuration information */ - struct { - unsigned char clockrate; /* clock rate of FAS device (MHz) */ - unsigned char select_timeout; /* timeout (R5) */ - unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */ - unsigned char wide_max_size; /* Maximum wide transfer size */ - unsigned char cntl3; /* Control Reg 3 */ - unsigned int asyncperiod; /* Async transfer period (ns) */ - unsigned int capabilities; /* driver capabilities */ - unsigned int disconnect_ok:1; /* Disconnects allowed? */ - } ifcfg; - - /* queue handling */ - struct { - Queue_t issue; /* issue queue */ - Queue_t disconnected; /* disconnected command queue */ - } queues; - - /* per-device info */ - struct fas216_device { - unsigned char disconnect_ok:1; /* device can disconnect */ - unsigned char parity_enabled:1; /* parity checking enabled */ - unsigned char parity_check:1; /* need to check parity checking */ - unsigned char period; /* sync xfer period in (*4ns) */ - unsigned char stp; /* synchronous transfer period */ - unsigned char sof; /* synchronous offset register */ - unsigned char wide_xfer; /* currently negociated wide transfer */ - neg_t sync_state; /* synchronous transfer mode */ - neg_t wide_state; /* wide transfer mode */ - } device[8]; - unsigned long busyluns[64/sizeof(unsigned long)];/* array of bits indicating LUNs busy */ - - /* dma */ - struct { - fasdmatype_t transfer_type; /* current type of DMA transfer */ - fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma); - void (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer); - void (*stop) (struct Scsi_Host *host, Scsi_Pointer *SCp); - } dma; - - /* miscellaneous */ - int internal_done; /* flag to indicate request done */ - - unsigned long magic_end; -} FAS216_Info; - -/* Function: int fas216_init (struct Scsi_Host *instance) - * Purpose : initialise FAS/NCR/AMD SCSI structures. - * Params : instance - a driver-specific filled-out structure - * Returns : 0 on success - */ -extern int fas216_init (struct Scsi_Host *instance); - -/* Function: int fas216_add (struct Scsi_Host *instance, struct device *dev) - * Purpose : initialise FAS/NCR/AMD SCSI ic. - * Params : instance - a driver-specific filled-out structure - * Returns : 0 on success - */ -extern int fas216_add (struct Scsi_Host *instance, struct device *dev); - -/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) - * Purpose : queue a command for adapter to process. - * Params : SCpnt - Command to queue - * done - done function to call once command is complete - * Returns : 0 - success, else error - */ -extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); - -/* Function: int fas216_command (Scsi_Cmnd *SCpnt) - * Purpose : queue a command for adapter to process. - * Params : SCpnt - Command to queue - * Returns : scsi result code - */ -extern int fas216_command (Scsi_Cmnd *); - -/* Function: irqreturn_t fas216_intr (FAS216_Info *info) - * Purpose : handle interrupts from the interface to progress a command - * Params : info - interface to service - */ -extern irqreturn_t fas216_intr (FAS216_Info *info); - -extern void fas216_remove (struct Scsi_Host *instance); - -/* Function: void fas216_release (struct Scsi_Host *instance) - * Purpose : release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. - * Params : instance - a driver-specific filled-out structure - * Returns : 0 on success - */ -extern void fas216_release (struct Scsi_Host *instance); - -extern int fas216_print_host(FAS216_Info *info, char *buffer); -extern int fas216_print_stats(FAS216_Info *info, char *buffer); -extern int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer); - -/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort this command - * Params : SCpnt - command to abort - * Returns : FAILED if unable to abort - */ -extern int fas216_eh_abort(Scsi_Cmnd *SCpnt); - -/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the device associated with this command - * Params : SCpnt - command specifing device to reset - * Returns : FAILED if unable to reset - */ -extern int fas216_eh_device_reset(Scsi_Cmnd *SCpnt); - -/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the complete bus associated with this command - * Params : SCpnt - command specifing bus to reset - * Returns : FAILED if unable to reset - */ -extern int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt); - -/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the host associated with this command - * Params : SCpnt - command specifing host to reset - * Returns : FAILED if unable to reset - */ -extern int fas216_eh_host_reset(Scsi_Cmnd *SCpnt); - -#endif /* FAS216_H */ diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/msgqueue.c linux-2.5.69-bk14/drivers/acorn/scsi/msgqueue.c --- linux-2.5.69-bk13/drivers/acorn/scsi/msgqueue.c 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/msgqueue.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,171 +0,0 @@ -/* - * linux/drivers/acorn/scsi/msgqueue.c - * - * Copyright (C) 1997-1998 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * message queue handling - */ -#include -#include -#include -#include - -#include "msgqueue.h" - -/* - * Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) - * Purpose : Allocate a message queue entry - * Params : msgq - message queue to claim entry for - * Returns : message queue entry or NULL. - */ -static struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) -{ - struct msgqueue_entry *mq; - - if ((mq = msgq->free) != NULL) - msgq->free = mq->next; - - return mq; -} - -/* - * Function: void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) - * Purpose : free a message queue entry - * Params : msgq - message queue to free entry from - * mq - message queue entry to free - */ -static void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) -{ - if (mq) { - mq->next = msgq->free; - msgq->free = mq; - } -} - -/* - * Function: void msgqueue_initialise(MsgQueue_t *msgq) - * Purpose : initialise a message queue - * Params : msgq - queue to initialise - */ -void msgqueue_initialise(MsgQueue_t *msgq) -{ - int i; - - msgq->qe = NULL; - msgq->free = &msgq->entries[0]; - - for (i = 0; i < NR_MESSAGES; i++) - msgq->entries[i].next = &msgq->entries[i + 1]; - - msgq->entries[NR_MESSAGES - 1].next = NULL; -} - - -/* - * Function: void msgqueue_free(MsgQueue_t *msgq) - * Purpose : free a queue - * Params : msgq - queue to free - */ -void msgqueue_free(MsgQueue_t *msgq) -{ -} - -/* - * Function: int msgqueue_msglength(MsgQueue_t *msgq) - * Purpose : calculate the total length of all messages on the message queue - * Params : msgq - queue to examine - * Returns : number of bytes of messages in queue - */ -int msgqueue_msglength(MsgQueue_t *msgq) -{ - struct msgqueue_entry *mq = msgq->qe; - int length = 0; - - for (mq = msgq->qe; mq; mq = mq->next) - length += mq->msg.length; - - return length; -} - -/* - * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) - * Purpose : return a message - * Params : msgq - queue to obtain message from - * : msgno - message number - * Returns : pointer to message string, or NULL - */ -struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) -{ - struct msgqueue_entry *mq; - - for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--); - - return mq ? &mq->msg : NULL; -} - -/* - * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) - * Purpose : add a message onto a message queue - * Params : msgq - queue to add message on - * length - length of message - * ... - message bytes - * Returns : != 0 if successful - */ -int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) -{ - struct msgqueue_entry *mq = mqe_alloc(msgq); - va_list ap; - - if (mq) { - struct msgqueue_entry **mqp; - int i; - - va_start(ap, length); - for (i = 0; i < length; i++) - mq->msg.msg[i] = va_arg(ap, unsigned int); - va_end(ap); - - mq->msg.length = length; - mq->msg.fifo = 0; - mq->next = NULL; - - mqp = &msgq->qe; - while (*mqp) - mqp = &(*mqp)->next; - - *mqp = mq; - } - - return mq != NULL; -} - -/* - * Function: void msgqueue_flush(MsgQueue_t *msgq) - * Purpose : flush all messages from message queue - * Params : msgq - queue to flush - */ -void msgqueue_flush(MsgQueue_t *msgq) -{ - struct msgqueue_entry *mq, *mqnext; - - for (mq = msgq->qe; mq; mq = mqnext) { - mqnext = mq->next; - mqe_free(msgq, mq); - } - msgq->qe = NULL; -} - -EXPORT_SYMBOL(msgqueue_initialise); -EXPORT_SYMBOL(msgqueue_free); -EXPORT_SYMBOL(msgqueue_msglength); -EXPORT_SYMBOL(msgqueue_getmsg); -EXPORT_SYMBOL(msgqueue_addmsg); -EXPORT_SYMBOL(msgqueue_flush); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("SCSI message queue handling"); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/msgqueue.h linux-2.5.69-bk14/drivers/acorn/scsi/msgqueue.h --- linux-2.5.69-bk13/drivers/acorn/scsi/msgqueue.h 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/msgqueue.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,82 +0,0 @@ -/* - * linux/drivers/acorn/scsi/msgqueue.h - * - * Copyright (C) 1997 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * message queue handling - */ -#ifndef MSGQUEUE_H -#define MSGQUEUE_H - -struct message { - char msg[8]; - int length; - int fifo; -}; - -struct msgqueue_entry { - struct message msg; - struct msgqueue_entry *next; -}; - -#define NR_MESSAGES 4 - -typedef struct { - struct msgqueue_entry *qe; - struct msgqueue_entry *free; - struct msgqueue_entry entries[NR_MESSAGES]; -} MsgQueue_t; - -/* - * Function: void msgqueue_initialise(MsgQueue_t *msgq) - * Purpose : initialise a message queue - * Params : msgq - queue to initialise - */ -extern void msgqueue_initialise(MsgQueue_t *msgq); - -/* - * Function: void msgqueue_free(MsgQueue_t *msgq) - * Purpose : free a queue - * Params : msgq - queue to free - */ -extern void msgqueue_free(MsgQueue_t *msgq); - -/* - * Function: int msgqueue_msglength(MsgQueue_t *msgq) - * Purpose : calculate the total length of all messages on the message queue - * Params : msgq - queue to examine - * Returns : number of bytes of messages in queue - */ -extern int msgqueue_msglength(MsgQueue_t *msgq); - -/* - * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) - * Purpose : return a message & its length - * Params : msgq - queue to obtain message from - * : msgno - message number - * Returns : pointer to message string, or NULL - */ -extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno); - -/* - * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) - * Purpose : add a message onto a message queue - * Params : msgq - queue to add message on - * length - length of message - * ... - message bytes - * Returns : != 0 if successful - */ -extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...); - -/* - * Function: void msgqueue_flush(MsgQueue_t *msgq) - * Purpose : flush all messages from message queue - * Params : msgq - queue to flush - */ -extern void msgqueue_flush(MsgQueue_t *msgq); - -#endif diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/oak.c linux-2.5.69-bk14/drivers/acorn/scsi/oak.c --- linux-2.5.69-bk13/drivers/acorn/scsi/oak.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/oak.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,215 +0,0 @@ -/* - * Oak Generic NCR5380 driver - * - * Copyright 1995-2002, Russell King - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" - -#define AUTOSENSE -/*#define PSEUDO_DMA*/ - -#define OAKSCSI_PUBLIC_RELEASE 1 - -#define NCR5380_read(reg) oakscsi_read(_instance, reg) -#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value) -#define NCR5380_intr oakscsi_intr -#define NCR5380_queue_command oakscsi_queue_command -#define NCR5380_proc_info oakscsi_proc_info - -int NCR5380_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance - -#define BOARD_NORMAL 0 -#define BOARD_NCR53C400 1 - -#include "../../scsi/NCR5380.h" - -#undef START_DMA_INITIATOR_RECEIVE_REG -#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128) - -const char * oakscsi_info (struct Scsi_Host *spnt) -{ - return ""; -} - -#define STAT(p) inw(p + 144) -extern void inswb(int from, void *to, int len); - -static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, - int len) -{ - int iobase = instance->io_port; -printk("writing %p len %d\n",addr, len); - if(!len) return -1; - - while(1) - { - int status; - while(((status = STAT(iobase)) & 0x100)==0); - } -} - -static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, - int len) -{ - int iobase = instance->io_port; -printk("reading %p len %d\n", addr, len); - while(len > 0) - { - int status, timeout; - unsigned long b; - - timeout = 0x01FFFFFF; - - while(((status = STAT(iobase)) & 0x100)==0) - { - timeout--; - if(status & 0x200 || !timeout) - { - printk("status = %08X\n",status); - return 1; - } - } - if(len >= 128) - { - inswb(iobase + 136, addr, 128); - addr += 128; - len -= 128; - } - else - { - b = (unsigned long) inw(iobase + 136); - *addr ++ = b; - len -= 1; - if(len) - *addr ++ = b>>8; - len -= 1; - } - } - return 0; -} - -#define oakscsi_read(instance,reg) (inb((instance)->io_port + (reg))) -#define oakscsi_write(instance,reg,val) (outb((val), (instance)->io_port + (reg))) - -#undef STAT - -#include "../../scsi/NCR5380.c" - -static Scsi_Host_Template oakscsi_template = { - .module = THIS_MODULE, - .proc_info = oakscsi_proc_info, - .name = "Oak 16-bit SCSI", - .info = oakscsi_info, - .queuecommand = oakscsi_queue_command, - .eh_abort_handler = NCR5380_abort, - .eh_device_reset_handler= NCR5380_device_reset, - .eh_bus_reset_handler = NCR5380_bus_reset, - .eh_host_reset_handler = NCR5380_host_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "oakscsi", -}; - -static int __devinit -oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - int ret = -ENOMEM; - - host = scsi_register(&oakscsi_template, sizeof(struct NCR5380_hostdata)); - if (!host) - goto out; - - host->io_port = ecard_address(ec, ECARD_MEMC, 0); - host->irq = IRQ_NONE; - host->n_io_port = 255; - - ret = -EBUSY; - if (!request_region (host->io_port, host->n_io_port, "Oak SCSI")) - goto unreg; - - NCR5380_init(host, 0); - - printk("scsi%d: at port 0x%08lx irqs disabled", - host->host_no, host->io_port); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - - ret = scsi_add_host(host, &ec->dev); - if (ret == 0) - goto out; - - release_region(host->io_port, host->n_io_port); - unreg: - scsi_unregister(host); - out: - return ret; -} - -static void __devexit oakscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - scsi_remove_host(host); - - release_region(host->io_port, host->n_io_port); - scsi_unregister(host); -} - -static const struct ecard_id oakscsi_cids[] = { - { MANU_OAK, PROD_OAK_SCSI }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver oakscsi_driver = { - .probe = oakscsi_probe, - .remove = __devexit_p(oakscsi_remove), - .id_table = oakscsi_cids, - .drv = { - .name = "oakscsi", - }, -}; - -static int __init oakscsi_init(void) -{ - return ecard_register_driver(&oakscsi_driver); -} - -static void __exit oakscsi_exit(void) -{ - ecard_remove_driver(&oakscsi_driver); -} - -module_init(oakscsi_init); -module_exit(oakscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Oak SCSI driver"); -MODULE_LICENSE("GPL"); - diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/powertec.c linux-2.5.69-bk14/drivers/acorn/scsi/powertec.c --- linux-2.5.69-bk13/drivers/acorn/scsi/powertec.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/powertec.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,499 +0,0 @@ -/* - * linux/drivers/acorn/scsi/powertec.c - * - * Copyright (C) 1997-2003 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -#include - -#define POWERTEC_FAS216_OFFSET 0x3000 -#define POWERTEC_FAS216_SHIFT 6 - -#define POWERTEC_INTR_STATUS 0x2000 -#define POWERTEC_INTR_BIT 0x80 - -#define POWERTEC_RESET_CONTROL 0x1018 -#define POWERTEC_RESET_BIT 1 - -#define POWERTEC_TERM_CONTROL 0x2018 -#define POWERTEC_TERM_ENABLE 1 - -#define POWERTEC_INTR_CONTROL 0x101c -#define POWERTEC_INTR_ENABLE 1 -#define POWERTEC_INTR_DISABLE 0 - -#define VERSION "1.10 (19/01/2003 2.5.59)" - -/* - * Use term=0,1,0,0,0 to turn terminators on/off. - * One entry per slot. - */ -static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -#define NR_SG 256 - -struct powertec_info { - FAS216_Info info; - struct expansion_card *ec; - void *term_port; - unsigned int term_ctl; - struct scatterlist sg[NR_SG]; -}; - -/* Prototype: void powertecscsi_irqenable(ec, irqnr) - * Purpose : Enable interrupts on Powertec SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -powertecscsi_irqenable(struct expansion_card *ec, int irqnr) -{ - writeb(POWERTEC_INTR_ENABLE, ec->irq_data); -} - -/* Prototype: void powertecscsi_irqdisable(ec, irqnr) - * Purpose : Disable interrupts on Powertec SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -powertecscsi_irqdisable(struct expansion_card *ec, int irqnr) -{ - writeb(POWERTEC_INTR_DISABLE, ec->irq_data); -} - -static const expansioncard_ops_t powertecscsi_ops = { - .irqenable = powertecscsi_irqenable, - .irqdisable = powertecscsi_irqdisable, -}; - -/* Prototype: void powertecscsi_terminator_ctl(host, on_off) - * Purpose : Turn the Powertec SCSI terminators on or off - * Params : host - card to turn on/off - * : on_off - !0 to turn on, 0 to turn off - */ -static void -powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off) -{ - struct powertec_info *info = (struct powertec_info *)host->hostdata; - - info->term_ctl = on_off ? POWERTEC_TERM_ENABLE : 0; - writeb(info->term_ctl, info->term_port); -} - -/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs) - * Purpose : handle interrupts from Powertec SCSI card - * Params : irq - interrupt number - * dev_id - user-defined (Scsi_Host structure) - * regs - processor registers at interrupt - */ -static irqreturn_t -powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct powertec_info *info = dev_id; - - return fas216_intr(&info->info); -} - -/* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : type of transfer to be performed - */ -static fasdmatype_t -powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - struct powertec_info *info = (struct powertec_info *)host->hostdata; - struct device *dev = scsi_get_device(host); - int dmach = host->dma_channel; - - if (info->info.ifcfg.capabilities & FASCAP_DMA && - min_type == fasdma_real_all) { - int bufs, map_dir, dma_dir; - - bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); - - if (direction == DMA_OUT) - map_dir = DMA_TO_DEVICE, - dma_dir = DMA_MODE_WRITE; - else - map_dir = DMA_FROM_DEVICE, - dma_dir = DMA_MODE_READ; - - dma_map_sg(dev, info->sg, bufs + 1, map_dir); - - disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); - set_dma_mode(dmach, dma_dir); - enable_dma(dmach); - return fasdma_real_all; - } - - /* - * If we're not doing DMA, - * we'll do slow PIO - */ - return fasdma_pio; -} - -/* Prototype: int powertecscsi_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void -powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - if (host->dma_channel != NO_DMA) - disable_dma(host->dma_channel); -} - -/* Prototype: const char *powertecscsi_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -const char *powertecscsi_info(struct Scsi_Host *host) -{ - struct powertec_info *info = (struct powertec_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s terminators o%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION, info->term_ctl ? "n" : "ff"); - - return string; -} - -/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) - * Purpose : Set a driver specific function - * Params : host - host to setup - * : buffer - buffer containing string describing operation - * : length - length of string - * Returns : -EINVAL, or 0 - */ -static int -powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) -{ - int ret = length; - - if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) { - buffer += 12; - length -= 12; - - if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { - if (buffer[5] == '1') - powertecscsi_terminator_ctl(host, 1); - else if (buffer[5] == '0') - powertecscsi_terminator_ctl(host, 0); - else - ret = -EINVAL; - } else - ret = -EINVAL; - } else - ret = -EINVAL; - - return ret; -} - -/* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -int powertecscsi_proc_info(char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - int pos, begin; - struct Scsi_Host *host; - struct powertec_info *info; - Scsi_Device *scd; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - if (inout == 1) - return powertecscsi_set_proc_info(host, buffer, length); - - info = (struct powertec_info *)host->hostdata; - - begin = 0; - pos = sprintf(buffer, "PowerTec SCSI driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += sprintf(buffer + pos, "Term : o%s\n", - info->term_ctl ? "n" : "ff"); - - pos += fas216_print_stats(&info->info, buffer + pos); - - pos += sprintf(buffer+pos, "\nAttached devices:\n"); - - list_for_each_entry(scd, &host->my_devices, siblings) { - pos += fas216_print_device(&info->info, scd, buffer + pos); - - if (pos + begin < offset) { - begin += pos; - pos = 0; - } - if (pos + begin > offset + length) - break; - } - - *start = buffer + (offset - begin); - pos -= offset - begin; - if (pos > length) - pos = length; - - return pos; -} - -static ssize_t powertecscsi_show_term(struct device *dev, char *buf) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct powertec_info *info = (struct powertec_info *)host->hostdata; - - return sprintf(buf, "%d\n", info->term_ctl ? 1 : 0); -} - -static ssize_t -powertecscsi_store_term(struct device *dev, const char *buf, size_t len) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - - if (len > 1) - powertecscsi_terminator_ctl(host, buf[0] != '0'); - - return len; -} - -static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, - powertecscsi_show_term, powertecscsi_store_term); - -static Scsi_Host_Template powertecscsi_template = { - .module = THIS_MODULE, - .proc_info = powertecscsi_proc_info, - .name = "PowerTec SCSI", - .info = powertecscsi_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - - .can_queue = 8, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = ENABLE_CLUSTERING, - .proc_name = "powertec", -}; - -static int __devinit -powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct powertec_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - if (!request_mem_region(resbase, reslen, "powertecscsi")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&powertecscsi_template, - sizeof (struct powertec_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = ec->irq; - host->dma_channel = ec->dma; - - ec->irqaddr = base + POWERTEC_INTR_STATUS; - ec->irqmask = POWERTEC_INTR_BIT; - ec->irq_data = base + POWERTEC_INTR_CONTROL; - ec->ops = &powertecscsi_ops; - - ecard_set_drvdata(ec, host); - - info = (struct powertec_info *)host->hostdata; - info->term_port = base + POWERTEC_TERM_CONTROL; - powertecscsi_terminator_ctl(host, term[ec->slot_no]); - - device_create_file(&ec->dev, &dev_attr_bus_term); - - info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET; - info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT; - info->info.scsi.irq = host->irq; - info->info.ifcfg.clockrate = 40; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 7; - info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 1; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = 0; - info->info.dma.setup = powertecscsi_dma_setup; - info->info.dma.pseudo = NULL; - info->info.dma.stop = powertecscsi_dma_stop; - - ret = fas216_init(host); - if (ret) - goto out_free; - - ret = request_irq(host->irq, powertecscsi_intr, - SA_INTERRUPT, "powertec", info); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_release; - } - - if (host->dma_channel != NO_DMA) { - if (request_dma(host->dma_channel, "powertec")) { - printk("scsi%d: DMA%d not free, using PIO\n", - host->host_no, host->dma_channel); - host->dma_channel = NO_DMA; - } else { - set_dma_speed(host->dma_channel, 180); - info->info.ifcfg.capabilities |= FASCAP_DMA; - } - } - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, host); - - out_release: - fas216_release(host); - - out_free: - device_remove_file(&ec->dev, &dev_attr_bus_term); - scsi_unregister(host); - - out_unmap: - iounmap(base); - - out_region: - release_mem_region(resbase, reslen); - - out: - return ret; -} - -static void __devexit powertecscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct powertecscsi_info *info = (struct powertecscsi_info *)host->hostdata; - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - device_remove_file(&ec->dev, &dev_attr_bus_term); - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, info); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id powertecscsi_cids[] = { - { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver powertecscsi_driver = { - .probe = powertecscsi_probe, - .remove = __devexit_p(powertecscsi_remove), - .id_table = powertecscsi_cids, - .drv = { - .name = "powertecscsi", - }, -}; - -static int __init powertecscsi_init(void) -{ - return ecard_register_driver(&powertecscsi_driver); -} - -static void __exit powertecscsi_exit(void) -{ - ecard_remove_driver(&powertecscsi_driver); -} - -module_init(powertecscsi_init); -module_exit(powertecscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Powertec SCSI driver"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/queue.c linux-2.5.69-bk14/drivers/acorn/scsi/queue.c --- linux-2.5.69-bk13/drivers/acorn/scsi/queue.c 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/queue.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,319 +0,0 @@ -/* - * linux/drivers/acorn/scsi/queue.c: queue handling primitives - * - * Copyright (C) 1997-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 15-Sep-1997 RMK Created. - * 11-Oct-1997 RMK Corrected problem with queue_remove_exclude - * not updating internal linked list properly - * (was causing commands to go missing). - * 30-Aug-2000 RMK Use Linux list handling and spinlocks - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" - -#define DEBUG - -typedef struct queue_entry { - struct list_head list; - Scsi_Cmnd *SCpnt; -#ifdef DEBUG - unsigned long magic; -#endif -} QE_t; - -#ifdef DEBUG -#define QUEUE_MAGIC_FREE 0xf7e1c9a3 -#define QUEUE_MAGIC_USED 0xf7e1cc33 - -#define SET_MAGIC(q,m) ((q)->magic = (m)) -#define BAD_MAGIC(q,m) ((q)->magic != (m)) -#else -#define SET_MAGIC(q,m) do { } while (0) -#define BAD_MAGIC(q,m) (0) -#endif - -#include "queue.h" - -#define NR_QE 32 - -/* - * Function: void queue_initialise (Queue_t *queue) - * Purpose : initialise a queue - * Params : queue - queue to initialise - */ -int queue_initialise (Queue_t *queue) -{ - unsigned int nqueues = NR_QE; - QE_t *q; - - spin_lock_init(&queue->queue_lock); - INIT_LIST_HEAD(&queue->head); - INIT_LIST_HEAD(&queue->free); - - /* - * If life was easier, then SCpnt would have a - * host-available list head, and we wouldn't - * need to keep free lists or allocate this - * memory. - */ - queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL); - if (q) { - for (; nqueues; q++, nqueues--) { - SET_MAGIC(q, QUEUE_MAGIC_FREE); - q->SCpnt = NULL; - list_add(&q->list, &queue->free); - } - } - - return queue->alloc != NULL; -} - -/* - * Function: void queue_free (Queue_t *queue) - * Purpose : free a queue - * Params : queue - queue to free - */ -void queue_free (Queue_t *queue) -{ - if (!list_empty(&queue->head)) - printk(KERN_WARNING "freeing non-empty queue %p\n", queue); - if (queue->alloc) - kfree(queue->alloc); -} - - -/* - * Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) - * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head. - * Params : queue - destination queue - * SCpnt - command to add - * head - add command to head of queue - * Returns : 0 on error, !0 on success - */ -int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) -{ - unsigned long flags; - struct list_head *l; - QE_t *q; - int ret = 0; - - spin_lock_irqsave(&queue->queue_lock, flags); - if (list_empty(&queue->free)) - goto empty; - - l = queue->free.next; - list_del(l); - - q = list_entry(l, QE_t, list); - if (BAD_MAGIC(q, QUEUE_MAGIC_FREE)) - BUG(); - - SET_MAGIC(q, QUEUE_MAGIC_USED); - q->SCpnt = SCpnt; - - if (head) - list_add(l, &queue->head); - else - list_add_tail(l, &queue->head); - - ret = 1; -empty: - spin_unlock_irqrestore(&queue->queue_lock, flags); - return ret; -} - -static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) -{ - QE_t *q; - - /* - * Move the entry from the "used" list onto the "free" list - */ - list_del(ent); - q = list_entry(ent, QE_t, list); - if (BAD_MAGIC(q, QUEUE_MAGIC_USED)) - BUG(); - - SET_MAGIC(q, QUEUE_MAGIC_FREE); - list_add(ent, &queue->free); - - return q->SCpnt; -} - -/* - * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude) - * Purpose : remove a SCSI command from a queue - * Params : queue - queue to remove command from - * exclude - bit array of target&lun which is busy - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) -{ - unsigned long flags; - struct list_head *l; - Scsi_Cmnd *SCpnt = NULL; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) { - SCpnt = __queue_remove(queue, l); - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return SCpnt; -} - -/* - * Function: Scsi_Cmnd *queue_remove (queue) - * Purpose : removes first SCSI command from a queue - * Params : queue - queue to remove command from - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -Scsi_Cmnd *queue_remove(Queue_t *queue) -{ - unsigned long flags; - Scsi_Cmnd *SCpnt = NULL; - - spin_lock_irqsave(&queue->queue_lock, flags); - if (!list_empty(&queue->head)) - SCpnt = __queue_remove(queue, queue->head.next); - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return SCpnt; -} - -/* - * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) - * Purpose : remove a SCSI command from the queue for a specified target/lun/tag - * Params : queue - queue to remove command from - * target - target that we want - * lun - lun on device - * tag - tag on device - * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements - */ -Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag) -{ - unsigned long flags; - struct list_head *l; - Scsi_Cmnd *SCpnt = NULL; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun && - q->SCpnt->tag == tag) { - SCpnt = __queue_remove(queue, l); - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return SCpnt; -} - -/* - * Function: queue_remove_all_target(queue, target) - * Purpose : remove all SCSI commands from the queue for a specified target - * Params : queue - queue to remove command from - * target - target device id - * Returns : nothing - */ -void queue_remove_all_target(Queue_t *queue, int target) -{ - unsigned long flags; - struct list_head *l; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt->device->id == target) - __queue_remove(queue, l); - } - spin_unlock_irqrestore(&queue->queue_lock, flags); -} - -/* - * Function: int queue_probetgtlun (queue, target, lun) - * Purpose : check to see if we have a command in the queue for the specified - * target/lun. - * Params : queue - queue to look in - * target - target we want to probe - * lun - lun on target - * Returns : 0 if not found, != 0 if found - */ -int queue_probetgtlun (Queue_t *queue, int target, int lun) -{ - unsigned long flags; - struct list_head *l; - int found = 0; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) { - found = 1; - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return found; -} - -/* - * Function: int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) - * Purpose : remove a specific command from the queues - * Params : queue - queue to look in - * SCpnt - command to find - * Returns : 0 if not found - */ -int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) -{ - unsigned long flags; - struct list_head *l; - int found = 0; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt == SCpnt) { - __queue_remove(queue, l); - found = 1; - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return found; -} - -EXPORT_SYMBOL(queue_initialise); -EXPORT_SYMBOL(queue_free); -EXPORT_SYMBOL(__queue_add); -EXPORT_SYMBOL(queue_remove); -EXPORT_SYMBOL(queue_remove_exclude); -EXPORT_SYMBOL(queue_remove_tgtluntag); -EXPORT_SYMBOL(queue_remove_cmd); -EXPORT_SYMBOL(queue_remove_all_target); -EXPORT_SYMBOL(queue_probetgtlun); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("SCSI command queueing"); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/queue.h linux-2.5.69-bk14/drivers/acorn/scsi/queue.h --- linux-2.5.69-bk13/drivers/acorn/scsi/queue.h 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/queue.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,105 +0,0 @@ -/* - * linux/drivers/acorn/scsi/queue.h: queue handling - * - * Copyright (C) 1997 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef QUEUE_H -#define QUEUE_H - -typedef struct { - struct list_head head; - struct list_head free; - spinlock_t queue_lock; - void *alloc; /* start of allocated mem */ -} Queue_t; - -/* - * Function: void queue_initialise (Queue_t *queue) - * Purpose : initialise a queue - * Params : queue - queue to initialise - */ -extern int queue_initialise (Queue_t *queue); - -/* - * Function: void queue_free (Queue_t *queue) - * Purpose : free a queue - * Params : queue - queue to free - */ -extern void queue_free (Queue_t *queue); - -/* - * Function: Scsi_Cmnd *queue_remove (queue) - * Purpose : removes first SCSI command from a queue - * Params : queue - queue to remove command from - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -extern Scsi_Cmnd *queue_remove (Queue_t *queue); - -/* - * Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude) - * Purpose : remove a SCSI command from a queue - * Params : queue - queue to remove command from - * exclude - array of busy LUNs - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned long *exclude); - -#define queue_add_cmd_ordered(queue,SCpnt) \ - __queue_add(queue,SCpnt,(SCpnt)->cmnd[0] == REQUEST_SENSE) -#define queue_add_cmd_tail(queue,SCpnt) \ - __queue_add(queue,SCpnt,0) -/* - * Function: int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) - * Purpose : Add a new command onto a queue - * Params : queue - destination queue - * SCpnt - command to add - * head - add command to head of queue - * Returns : 0 on error, !0 on success - */ -extern int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head); - -/* - * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) - * Purpose : remove a SCSI command from the queue for a specified target/lun/tag - * Params : queue - queue to remove command from - * target - target that we want - * lun - lun on device - * tag - tag on device - * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements - */ -extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag); - -/* - * Function: queue_remove_all_target(queue, target) - * Purpose : remove all SCSI commands from the queue for a specified target - * Params : queue - queue to remove command from - * target - target device id - * Returns : nothing - */ -extern void queue_remove_all_target(Queue_t *queue, int target); - -/* - * Function: int queue_probetgtlun (queue, target, lun) - * Purpose : check to see if we have a command in the queue for the specified - * target/lun. - * Params : queue - queue to look in - * target - target we want to probe - * lun - lun on target - * Returns : 0 if not found, != 0 if found - */ -extern int queue_probetgtlun (Queue_t *queue, int target, int lun); - -/* - * Function: int queue_remove_cmd (Queue_t *queue, Scsi_Cmnd *SCpnt) - * Purpose : remove a specific command from the queues - * Params : queue - queue to look in - * SCpnt - command to find - * Returns : 0 if not found - */ -int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt); - -#endif /* QUEUE_H */ diff -urN linux-2.5.69-bk13/drivers/acorn/scsi/scsi.h linux-2.5.69-bk14/drivers/acorn/scsi/scsi.h --- linux-2.5.69-bk13/drivers/acorn/scsi/scsi.h 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/drivers/acorn/scsi/scsi.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,115 +0,0 @@ -/* - * linux/drivers/acorn/scsi/scsi.h - * - * Copyright (C) 2002 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Commonly used scsi driver functions. - */ - -#define BELT_AND_BRACES - -/* - * The scatter-gather list handling. This contains all - * the yucky stuff that needs to be fixed properly. - */ -static inline int copy_SCp_to_sg(struct scatterlist *sg, Scsi_Pointer *SCp, int max) -{ - int bufs = SCp->buffers_residual; - - BUG_ON(bufs + 1 > max); - - sg->page = virt_to_page(SCp->ptr); - sg->offset = ((unsigned int)SCp->ptr) & ~PAGE_MASK; - sg->length = SCp->this_residual; - - if (bufs) - memcpy(sg + 1, SCp->buffer + 1, - sizeof(struct scatterlist) * bufs); - return bufs + 1; -} - -static inline int next_SCp(Scsi_Pointer *SCp) -{ - int ret = SCp->buffers_residual; - if (ret) { - SCp->buffer++; - SCp->buffers_residual--; - SCp->ptr = (char *) - (page_address(SCp->buffer->page) + - SCp->buffer->offset); - SCp->this_residual = SCp->buffer->length; - } else { - SCp->ptr = NULL; - SCp->this_residual = 0; - } - return ret; -} - -static inline unsigned char get_next_SCp_byte(Scsi_Pointer *SCp) -{ - char c = *SCp->ptr; - - SCp->ptr += 1; - SCp->this_residual -= 1; - - return c; -} - -static inline void put_next_SCp_byte(Scsi_Pointer *SCp, unsigned char c) -{ - *SCp->ptr = c; - SCp->ptr += 1; - SCp->this_residual -= 1; -} - -static inline void init_SCp(Scsi_Cmnd *SCpnt) -{ - memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer)); - - if (SCpnt->use_sg) { - unsigned long len = 0; - int buf; - - SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; - SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; - SCpnt->SCp.ptr = (char *) - (page_address(SCpnt->SCp.buffer->page) + - SCpnt->SCp.buffer->offset); - SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; - -#ifdef BELT_AND_BRACES - /* - * Calculate correct buffer length. Some commands - * come in with the wrong request_bufflen. - */ - for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++) - len += SCpnt->SCp.buffer[buf].length; - - if (SCpnt->request_bufflen != len) - printk(KERN_WARNING "scsi%d.%c: bad request buffer " - "length %d, should be %ld\n", SCpnt->device->host->host_no, - '0' + SCpnt->device->id, SCpnt->request_bufflen, len); - SCpnt->request_bufflen = len; -#endif - } else { - SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer; - SCpnt->SCp.this_residual = SCpnt->request_bufflen; - } - - /* - * If the upper SCSI layers pass a buffer, but zero length, - * we aren't interested in the buffer pointer. - */ - if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) { -#if 0 //def BELT_AND_BRACES - printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for " - "command ", SCpnt->host->host_no, '0' + SCpnt->target); - print_command(SCpnt->cmnd); -#endif - SCpnt->SCp.ptr = NULL; - } -} diff -urN linux-2.5.69-bk13/drivers/block/DAC960.c linux-2.5.69-bk14/drivers/block/DAC960.c --- linux-2.5.69-bk13/drivers/block/DAC960.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/block/DAC960.c 2003-05-20 04:46:40.000000000 -0700 @@ -102,7 +102,7 @@ int drive_nr = (int)disk->private_data; struct hd_geometry g, *loc = (struct hd_geometry *)arg; - if (file->f_flags & O_NONBLOCK) + if (file && (file->f_flags & O_NONBLOCK)) return DAC960_UserIOCTL(inode, file, cmd, arg); if (cmd != HDIO_GETGEO || !loc) diff -urN linux-2.5.69-bk13/drivers/char/agp/Kconfig linux-2.5.69-bk14/drivers/char/agp/Kconfig --- linux-2.5.69-bk13/drivers/char/agp/Kconfig 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/Kconfig 2003-05-20 04:46:41.000000000 -0700 @@ -29,7 +29,7 @@ tristate "ALI chipset support" depends on AGP && X86 && !X86_64 ---help--- - This option gives you AGP support for the GLX component of the + This option gives you AGP support for the GLX component of XFree86 4.x on the following ALi chipsets. The supported chipsets include M1541, M1621, M1631, M1632, M1641,M1647,and M1651. For the ALi-chipset question, ALi suggests you refer to @@ -47,25 +47,27 @@ tristate "AMD Irongate, 761, and 762 chipset support" depends on AGP && X86 && !X86_64 help - This option gives you AGP support for the GLX component of the + This option gives you AGP support for the GLX component of XFree86 4.x on AMD Irongate, 761, and 762 chipsets. You should say Y here if you use XFree86 3.3.6 or 4.x and want to use GLX or DRI. If unsure, say N. config AGP_AMD_8151 - tristate "AMD 8151 chipset support" + tristate "AMD Opteron/Athlon64 on-CPU GART support" depends on AGP && X86 default GART_IOMMU help - Say Y here to support the AMD 8151 AGP bridge and the builtin - GART on the AMD Athlon64/Opteron ("Hammer") CPUs. + This option gives you AGP support for the GLX component of + XFree86 4.x using the on-CPU AGP bridge of the AMD Athlon64/Opteron CPUs. + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N config AGP_INTEL tristate "Intel 440LX/BX/GX, I8xx and E7x05 chipset support" depends on AGP && X86 && !X86_64 help - This option gives you AGP support for the GLX component of the + This option gives you AGP support for the GLX component of XFree86 4.x on Intel 440LX/BX/GX, 815, 820, 830, 840, 845, 850, 860 E7205 and E7505 chipsets and full support for the 810, 815, 830M, 845G, 852GM, 855GM and 865G integrated graphics chipsets. @@ -78,7 +80,7 @@ tristate "NVIDIA nForce/nForce2 chipset support" depends on AGP && X86 && !X86_64 help - This option gives you AGP support for the GLX component of the + This option gives you AGP support for the GLX component of XFree86 4.x on the following NVIDIA chipsets. The supported chipsets include nForce and nForce2 @@ -86,9 +88,8 @@ tristate "SiS chipset support" depends on AGP && X86 && !X86_64 help - This option gives you AGP support for the GLX component of the "soon - to be released" XFree86 4.x on Silicon Integrated Systems [SiS] - chipsets. + This option gives you AGP support for the GLX component of + XFree86 4.x on Silicon Integrated Systems [SiS] chipsets. Note that 5591/5592 AGP chipsets are NOT supported. @@ -106,7 +107,7 @@ tristate "VIA chipset support" depends on AGP && X86 && !X86_64 help - This option gives you AGP support for the GLX component of the + This option gives you AGP support for the GLX component of XFree86 4.x on VIA MPV3/Apollo Pro chipsets. You should say Y here if you use XFree86 3.3.6 or 4.x and want to @@ -131,3 +132,10 @@ depends on AGP && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL) default AGP +config AGP_UNINORTH + tristate "Apple UniNorth AGP support" + depends on AGP && ALL_PPC + help + This option gives you AGP support for Apple machines with a + UniNorth bridge. + diff -urN linux-2.5.69-bk13/drivers/char/agp/Makefile linux-2.5.69-bk14/drivers/char/agp/Makefile --- linux-2.5.69-bk13/drivers/char/agp/Makefile 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/Makefile 2003-05-20 04:46:41.000000000 -0700 @@ -11,5 +11,6 @@ obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o obj-$(CONFIG_AGP_SIS) += sis-agp.o obj-$(CONFIG_AGP_SWORKS) += sworks-agp.o +obj-$(CONFIG_AGP_UNINORTH) += uninorth-agp.o obj-$(CONFIG_AGP_VIA) += via-agp.o diff -urN linux-2.5.69-bk13/drivers/char/agp/agp.h linux-2.5.69-bk14/drivers/char/agp/agp.h --- linux-2.5.69-bk13/drivers/char/agp/agp.h 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/agp.h 2003-05-20 04:46:41.000000000 -0700 @@ -113,8 +113,6 @@ void (*free_by_type)(agp_memory *); void *(*agp_alloc_page)(void); void (*agp_destroy_page)(void *); - int (*suspend)(void); - void (*resume)(void); }; struct agp_bridge_data { @@ -384,16 +382,14 @@ void agp_generic_free_by_type(agp_memory * curr); void *agp_generic_alloc_page(void); void agp_generic_destroy_page(void *addr); -int agp_generic_suspend(void); -void agp_generic_resume(void); void agp_free_key(int key); int agp_num_entries(void); u32 agp_collect_device_status(u32 mode, u32 command); void agp_device_command(u32 command, int agp_v3); -int agp_3_0_enable(struct agp_bridge_data *bridge, u32 mode); -int agp_3_5_enable(struct agp_bridge_data *bridge, u32 mode); +int agp_3_5_enable(struct agp_bridge_data *bridge); void global_cache_flush(void); void get_agp_version(struct agp_bridge_data *bridge); +unsigned long agp_generic_mask_memory(unsigned long addr, int type); /* Standard agp registers */ #define AGPSTAT 0x4 diff -urN linux-2.5.69-bk13/drivers/char/agp/ali-agp.c linux-2.5.69-bk14/drivers/char/agp/ali-agp.c --- linux-2.5.69-bk13/drivers/char/agp/ali-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/ali-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -114,12 +114,6 @@ return 0; } -static unsigned long ali_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - - return addr | agp_bridge->driver->masks[0].mask; -} static void m1541_cache_flush(void) { @@ -180,10 +174,6 @@ /* Setup function */ -static struct gatt_mask ali_generic_masks[] = -{ - {.mask = 0x00000000, .type = 0} -}; static struct aper_size_info_32 ali_generic_sizes[7] = { @@ -198,7 +188,6 @@ struct agp_bridge_driver ali_generic_bridge = { .owner = THIS_MODULE, - .masks = ali_generic_masks, .aperture_sizes = ali_generic_sizes, .size_type = U32_APER_SIZE, .num_aperture_sizes = 7, @@ -206,7 +195,8 @@ .fetch_size = ali_fetch_size, .cleanup = ali_cleanup, .tlb_flush = ali_tlbflush, - .mask_memory = ali_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -217,13 +207,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = ali_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver ali_m1541_bridge = { .owner = THIS_MODULE, - .masks = ali_generic_masks, .aperture_sizes = ali_generic_sizes, .size_type = U32_APER_SIZE, .num_aperture_sizes = 7, @@ -231,7 +218,8 @@ .fetch_size = ali_fetch_size, .cleanup = ali_cleanup, .tlb_flush = ali_tlbflush, - .mask_memory = ali_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, .agp_enable = agp_generic_enable, .cache_flush = m1541_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -242,8 +230,6 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = m1541_alloc_page, .agp_destroy_page = m1541_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; @@ -391,7 +377,7 @@ MODULE_DEVICE_TABLE(pci, agp_ali_pci_table); -static struct __initdata pci_driver agp_ali_pci_driver = { +static struct pci_driver agp_ali_pci_driver = { .name = "agpgart-ali", .id_table = agp_ali_pci_table, .probe = agp_ali_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/alpha-agp.c linux-2.5.69-bk14/drivers/char/agp/alpha-agp.c --- linux-2.5.69-bk13/drivers/char/agp/alpha-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/alpha-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -38,10 +38,6 @@ { 0, 0, 0 }, /* filled in by alpha_core_agp_setup */ }; -static struct gatt_mask alpha_core_agp_masks[] = { - { .mask = 0, .type = 0 }, -}; - struct vm_operations_struct alpha_core_agp_vm_ops = { .nopage = alpha_core_agp_vm_nopage, }; @@ -78,12 +74,6 @@ alpha_mv.mv_pci_tbi(agp->hose, 0, -1); } -static unsigned long alpha_core_agp_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - return addr | agp_bridge->driver->masks[0].mask; -} - static void alpha_core_agp_enable(u32 mode) { alpha_agp_info *agp = agp_bridge->dev_private_data; @@ -127,7 +117,6 @@ struct agp_bridge_driver alpha_core_agp_driver = { .owner = THIS_MODULE, - .masks = alpha_core_agp_masks, .aperture_sizes = aper_size, .current_size = aper_size, /* only one entry */ .size_type = FIXED_APER_SIZE, @@ -136,7 +125,8 @@ .fetch_size = alpha_core_agp_fetch_size, .cleanup = alpha_core_agp_cleanup, .tlb_flush = alpha_core_agp_tlbflush, - .mask_memory = alpha_core_agp_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, .agp_enable = alpha_core_agp_enable, .cache_flush = global_cache_flush, .create_gatt_table = alpha_core_agp_nop, diff -urN linux-2.5.69-bk13/drivers/char/agp/amd-k7-agp.c linux-2.5.69-bk14/drivers/char/agp/amd-k7-agp.c --- linux-2.5.69-bk13/drivers/char/agp/amd-k7-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/amd-k7-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -29,9 +29,9 @@ int i; page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); - if (page_map->real == NULL) { + if (page_map->real == NULL) return -ENOMEM; - } + SetPageReserved(virt_to_page(page_map->real)); global_cache_flush(); page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), @@ -44,9 +44,8 @@ } global_cache_flush(); - for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { + for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) page_map->remapped[i] = agp_bridge->scratch_page; - } return 0; } @@ -65,12 +64,11 @@ struct amd_page_map *entry; tables = amd_irongate_private.gatt_pages; - for(i = 0; i < amd_irongate_private.num_tables; i++) { + for (i = 0; i < amd_irongate_private.num_tables; i++) { entry = tables[i]; if (entry != NULL) { - if (entry->real != NULL) { + if (entry->real != NULL) amd_free_page_map(entry); - } kfree(entry); } } @@ -87,25 +85,27 @@ tables = kmalloc((nr_tables + 1) * sizeof(struct amd_page_map *), GFP_KERNEL); - if (tables == NULL) { + if (tables == NULL) return -ENOMEM; - } - memset(tables, 0, sizeof(struct amd_page_map *) * (nr_tables + 1)); + + memset (tables, 0, sizeof(struct amd_page_map *) * (nr_tables + 1)); for (i = 0; i < nr_tables; i++) { entry = kmalloc(sizeof(struct amd_page_map), GFP_KERNEL); if (entry == NULL) { retval = -ENOMEM; break; } - memset(entry, 0, sizeof(struct amd_page_map)); + memset (entry, 0, sizeof(struct amd_page_map)); tables[i] = entry; retval = amd_create_page_map(entry); - if (retval != 0) break; + if (retval != 0) + break; } amd_irongate_private.num_tables = nr_tables; amd_irongate_private.gatt_pages = tables; - if (retval != 0) amd_free_gatt_pages(); + if (retval != 0) + amd_free_gatt_pages(); return retval; } @@ -132,9 +132,8 @@ value = A_SIZE_LVL2(agp_bridge->current_size); retval = amd_create_page_map(&page_dir); - if (retval != 0) { + if (retval != 0) return retval; - } retval = amd_create_gatt_pages(value->num_entries / 1024); if (retval != 0) { @@ -156,7 +155,7 @@ agp_bridge->gart_bus_addr = addr; /* Calculate the agp offset */ - for(i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { + for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { page_dir.remapped[GET_PAGE_DIR_OFF(addr)] = virt_to_phys(amd_irongate_private.gatt_pages[i]->real); page_dir.remapped[GET_PAGE_DIR_OFF(addr)] |= 0x00000001; @@ -266,20 +265,12 @@ * entries. */ -static void amd_irongate_tlbflush(agp_memory * temp) +static void amd_irongate_tlbflush(agp_memory *temp) { OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); } -static unsigned long amd_irongate_mask_memory(unsigned long addr, int type) -{ - /* Only type 0 is supported by the irongate */ - - return addr | agp_bridge->driver->masks[0].mask; -} - -static int amd_insert_memory(agp_memory * mem, - off_t pg_start, int type) +static int amd_insert_memory(agp_memory * mem, off_t pg_start, int type) { int i, j, num_entries; unsigned long *cur_gatt; @@ -287,12 +278,11 @@ num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; - if (type != 0 || mem->type != 0) { + if (type != 0 || mem->type != 0) return -EINVAL; - } - if ((pg_start + mem->page_count) > num_entries) { + + if ((pg_start + mem->page_count) > num_entries) return -EINVAL; - } j = pg_start; while (j < (pg_start + mem->page_count)) { @@ -312,22 +302,21 @@ addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); cur_gatt[GET_GATT_OFF(addr)] = - amd_irongate_mask_memory(mem->memory[i], mem->type); + agp_generic_mask_memory(mem->memory[i], mem->type); } amd_irongate_tlbflush(mem); return 0; } -static int amd_remove_memory(agp_memory * mem, off_t pg_start, - int type) +static int amd_remove_memory(agp_memory *mem, off_t pg_start, int type) { int i; unsigned long *cur_gatt; unsigned long addr; - if (type != 0 || mem->type != 0) { + if (type != 0 || mem->type != 0) return -EINVAL; - } + for (i = pg_start; i < (mem->page_count + pg_start); i++) { addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); @@ -352,12 +341,11 @@ static struct gatt_mask amd_irongate_masks[] = { - {.mask = 0x00000001, .type = 0} + {.mask = 1, .type = 0} }; struct agp_bridge_driver amd_irongate_driver = { .owner = THIS_MODULE, - .masks = amd_irongate_masks, .aperture_sizes = amd_irongate_sizes, .size_type = LVL2_APER_SIZE, .num_aperture_sizes = 7, @@ -365,7 +353,8 @@ .fetch_size = amd_irongate_fetch_size, .cleanup = amd_irongate_cleanup, .tlb_flush = amd_irongate_tlbflush, - .mask_memory = amd_irongate_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = amd_irongate_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = amd_create_gatt_table, @@ -376,8 +365,6 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_device_ids amd_agp_device_ids[] __initdata = @@ -469,7 +456,7 @@ MODULE_DEVICE_TABLE(pci, agp_amdk7_pci_table); -static struct __initdata pci_driver agp_amdk7_pci_driver = { +static struct pci_driver agp_amdk7_pci_driver = { .name = "agpgart-amdk7", .id_table = agp_amdk7_pci_table, .probe = agp_amdk7_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/amd-k8-agp.c linux-2.5.69-bk14/drivers/char/agp/amd-k8-agp.c --- linux-2.5.69-bk13/drivers/char/agp/amd-k8-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/amd-k8-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -212,20 +212,13 @@ } -static unsigned long amd_8151_mask_memory(unsigned long addr, int type) -{ - return addr | agp_bridge->driver->masks[0].mask; -} - - static struct gatt_mask amd_8151_masks[] = { - {.mask = 0x00000001, .type = 0} + { .mask = 1, .type = 0 } }; struct agp_bridge_driver amd_8151_driver = { .owner = THIS_MODULE, - .masks = amd_8151_masks, .aperture_sizes = amd_8151_sizes, .size_type = U32_APER_SIZE, .num_aperture_sizes = 7, @@ -233,7 +226,8 @@ .fetch_size = amd_x86_64_fetch_size, .cleanup = amd_8151_cleanup, .tlb_flush = amd_x86_64_tlbflush, - .mask_memory = amd_8151_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = amd_8151_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -244,8 +238,6 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; static int __init agp_amdk8_probe(struct pci_dev *pdev, @@ -268,9 +260,11 @@ if (!bridge) return -ENOMEM; - /* Assume here we have an 8151. (Later this assumption will be fixed). */ - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); - switch (rev_id) { + if (pdev->vendor == PCI_VENDOR_ID_AMD && + pdev->device == PCI_DEVICE_ID_AMD_8151_0) { + + pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); + switch (rev_id) { case 0x01: revstring="A0"; break; case 0x02: revstring="A1"; @@ -283,15 +277,16 @@ break; default: revstring="??"; break; - } - printk ("Detected AMD 8151 AGP Bridge rev %s", revstring); - /* - * Work around errata. - * Chips before B2 stepping incorrectly reporting v3.5 - */ - if (rev_id < 0x13) { - bridge->major_version = 3; - bridge->minor_version = 0; + } + printk ("Detected AMD 8151 AGP Bridge rev %s", revstring); + /* + * Work around errata. + * Chips before B2 stepping incorrectly reporting v3.5 + */ + if (rev_id < 0x13) { + bridge->major_version = 3; + bridge->minor_version = 0; + } } bridge->driver = &amd_8151_driver; @@ -338,12 +333,20 @@ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, + { + .class = (PCI_CLASS_BRIDGE_HOST << 8), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_VIA, + .device = PCI_DEVICE_ID_VIA_K8T400M_0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, { } }; MODULE_DEVICE_TABLE(pci, agp_amdk8_pci_table); -static struct __initdata pci_driver agp_amdk8_pci_driver = { +static struct pci_driver agp_amdk8_pci_driver = { .name = "agpgart-amd-k8", .id_table = agp_amdk8_pci_table, .probe = agp_amdk8_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/backend.c linux-2.5.69-bk14/drivers/char/agp/backend.c --- linux-2.5.69-bk13/drivers/char/agp/backend.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/backend.c 2003-05-20 04:46:41.000000000 -0700 @@ -210,19 +210,6 @@ phys_to_virt(bridge->scratch_page_real)); } -static int agp_power(struct pm_dev *dev, pm_request_t rq, void *data) -{ - switch(rq) { - case PM_SUSPEND: - return agp_bridge->driver->suspend(); - case PM_RESUME: - agp_bridge->driver->resume(); - return 0; - } - return 0; -} - - static const drm_agp_t drm_agp = { &agp_free_memory, &agp_allocate_memory, @@ -253,34 +240,39 @@ int error; if (!bridge->dev) { - printk(KERN_DEBUG PFX "Erk, registering with no pci_dev!\n"); + printk (KERN_DEBUG PFX "Erk, registering with no pci_dev!\n"); return -EINVAL; } if (agp_count) { - printk(KERN_DEBUG PFX + printk (KERN_INFO PFX "Only one agpgart device currently supported.\n"); return -ENODEV; } /* Grab reference on the chipset driver. */ - if (!try_module_get(bridge->driver->owner)) + if (!try_module_get(bridge->driver->owner)) { + printk (KERN_INFO PFX "Couldn't lock chipset driver.\n"); return -EINVAL; + } bridge->type = SUPPORTED; error = agp_backend_initialize(agp_bridge); - if (error) + if (error) { + printk (KERN_INFO PFX "agp_backend_initialize() failed.\n"); goto err_out; + } error = agp_frontend_initialize(); - if (error) + if (error) { + printk (KERN_INFO PFX "agp_frontend_initialize() failed.\n"); goto frontend_err; + } /* FIXME: What to do with this? */ inter_module_register("drm_agp", THIS_MODULE, &drm_agp); - pm_register(PM_PCI_DEV, PM_PCI_ID(bridge->dev), agp_power); agp_count++; return 0; @@ -297,7 +289,6 @@ void agp_remove_bridge(struct agp_bridge_data *bridge) { bridge->type = NOT_SUPPORTED; - pm_unregister_all(agp_power); agp_frontend_cleanup(); agp_backend_cleanup(bridge); inter_module_unregister("drm_agp"); diff -urN linux-2.5.69-bk13/drivers/char/agp/generic.c linux-2.5.69-bk14/drivers/char/agp/generic.c --- linux-2.5.69-bk13/drivers/char/agp/generic.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/generic.c 2003-05-20 04:46:41.000000000 -0700 @@ -419,7 +419,35 @@ if (!((*cmd & AGPSTAT_FW) && (*tmp & AGPSTAT_FW) && (*mode & AGPSTAT_FW))) *cmd &= ~AGPSTAT_FW; - /* Set speed. */ + /* + * Set speed. + * Check for invalid speeds. This can happen when applications + * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware + */ + if (*mode & AGPSTAT_MODE_3_0) { + /* + * Caller hasn't a clue what its doing. We are in 3.0 mode, + * have been passed a 3.0 mode, but with 2.x speed bits set. + * AGP2.x 4x -> AGP3.0 4x. + */ + if (*mode & AGPSTAT2_4X) { + printk (KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n", + current->comm, *mode); + *mode &= ~AGPSTAT2_4X; + *mode |= AGPSTAT3_4X; + } + } else { + /* + * The caller doesn't know what they are doing. We are in 3.0 mode, + * but have been passed an AGP 2.x mode. + * Convert AGP 1x,2x,4x -> AGP 3.0 4x. + */ + printk (KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n", + current->comm, *mode); + *mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X); + *mode |= AGPSTAT3_4X; + } + if (!((*cmd & AGPSTAT3_8X) && (*tmp & AGPSTAT3_8X) && (*mode & AGPSTAT3_8X))) *cmd &= ~AGPSTAT3_8X; @@ -428,9 +456,9 @@ /* Clear out unwanted bits. */ if (*cmd & AGPSTAT3_8X) - *cmd *= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); + *cmd = ~(AGPSTAT3_4X | AGPSTAT3_RSVD); if (*cmd & AGPSTAT3_4X) - *cmd *= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); + *cmd = ~(AGPSTAT3_8X | AGPSTAT3_RSVD); } //FIXME: This doesn't smell right. @@ -539,7 +567,7 @@ if (agp3 & AGPSTAT_MODE_3_0) { /* If we have 3.5, we can do the isoch stuff. */ if (agp_bridge->minor_version >= 5) - agp_3_5_enable(agp_bridge, mode); + agp_3_5_enable(agp_bridge); agp_device_command(command, TRUE); return; } else { @@ -671,20 +699,6 @@ } EXPORT_SYMBOL(agp_generic_create_gatt_table); -int agp_generic_suspend(void) -{ - return 0; -} -EXPORT_SYMBOL(agp_generic_suspend); - - -void agp_generic_resume(void) -{ - return; -} -EXPORT_SYMBOL(agp_generic_resume); - - int agp_generic_free_gatt_table(void) { int page_order; @@ -922,3 +936,13 @@ } EXPORT_SYMBOL(global_cache_flush); +unsigned long agp_generic_mask_memory(unsigned long addr, int type) +{ + /* memory type is ignored in the generic routine */ + if (agp_bridge->driver->masks) + return addr | agp_bridge->driver->masks[0].mask; + else + return addr; +} +EXPORT_SYMBOL(agp_generic_mask_memory); + diff -urN linux-2.5.69-bk13/drivers/char/agp/hp-agp.c linux-2.5.69-bk14/drivers/char/agp/hp-agp.c --- linux-2.5.69-bk13/drivers/char/agp/hp-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/hp-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -330,13 +330,13 @@ struct agp_bridge_driver hp_zx1_driver = { .owner = THIS_MODULE, - .masks = hp_zx1_masks, .size_type = FIXED_APER_SIZE, .configure = hp_zx1_configure, .fetch_size = hp_zx1_fetch_size, .cleanup = hp_zx1_cleanup, .tlb_flush = hp_zx1_tlbflush, .mask_memory = hp_zx1_mask_memory, + .masks = hp_zx1_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = hp_zx1_create_gatt_table, @@ -400,7 +400,7 @@ MODULE_DEVICE_TABLE(pci, agp_hp_pci_table); -static struct __initdata pci_driver agp_hp_pci_driver = { +static struct pci_driver agp_hp_pci_driver = { .name = "agpgart-hp", .id_table = agp_hp_pci_table, .probe = agp_hp_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/i460-agp.c linux-2.5.69-bk14/drivers/char/agp/i460-agp.c --- linux-2.5.69-bk13/drivers/char/agp/i460-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/i460-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -525,7 +525,6 @@ struct agp_bridge_driver intel_i460_driver = { .owner = THIS_MODULE, - .masks = i460_masks, .aperture_sizes = i460_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 3, @@ -534,6 +533,7 @@ .cleanup = i460_cleanup, .tlb_flush = i460_tlb_flush, .mask_memory = i460_mask_memory, + .masks = i460_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = i460_create_gatt_table, @@ -551,8 +551,6 @@ #endif .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, .cant_use_aperture = 1, }; @@ -600,7 +598,7 @@ MODULE_DEVICE_TABLE(pci, agp_intel_i460_pci_table); -static struct __initdata pci_driver agp_intel_i460_pci_driver = { +static struct pci_driver agp_intel_i460_pci_driver = { .name = "agpgart-intel-i460", .id_table = agp_intel_i460_pci_table, .probe = agp_intel_i460_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/intel-agp.c linux-2.5.69-bk14/drivers/char/agp/intel-agp.c --- linux-2.5.69-bk13/drivers/char/agp/intel-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/intel-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -782,11 +782,6 @@ return 0; } -static void intel_845_resume(void) -{ - intel_845_configure(); -} - static int intel_850_configure(void) { u32 temp; @@ -902,17 +897,6 @@ return 0; } -static unsigned long intel_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - return addr | agp_bridge->driver->masks[0].mask; -} - -static void intel_resume(void) -{ - intel_configure(); -} - /* Setup function */ static struct gatt_mask intel_generic_masks[] = { @@ -957,7 +941,6 @@ struct agp_bridge_driver intel_generic_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_generic_sizes, .size_type = U16_APER_SIZE, .num_aperture_sizes = 7, @@ -965,7 +948,8 @@ .fetch_size = intel_fetch_size, .cleanup = intel_cleanup, .tlb_flush = intel_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -976,13 +960,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = intel_resume, }; struct agp_bridge_driver intel_810_driver = { .owner = THIS_MODULE, - .masks = intel_i810_masks, .aperture_sizes = intel_i810_sizes, .size_type = FIXED_APER_SIZE, .num_aperture_sizes = 2, @@ -992,6 +973,7 @@ .cleanup = intel_i810_cleanup, .tlb_flush = intel_i810_tlbflush, .mask_memory = intel_i810_mask_memory, + .masks = intel_i810_masks, .agp_enable = intel_i810_agp_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1002,14 +984,11 @@ .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_815_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_815_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 2, @@ -1017,7 +996,8 @@ .fetch_size = intel_815_fetch_size, .cleanup = intel_8xx_cleanup, .tlb_flush = intel_8xx_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1028,13 +1008,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_830_driver = { .owner = THIS_MODULE, - .masks = intel_i810_masks, .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, .num_aperture_sizes = 2, @@ -1044,6 +1021,7 @@ .cleanup = intel_i830_cleanup, .tlb_flush = intel_i810_tlbflush, .mask_memory = intel_i810_mask_memory, + .masks = intel_i810_masks, .agp_enable = intel_i810_agp_enable, .cache_flush = global_cache_flush, .create_gatt_table = intel_i830_create_gatt_table, @@ -1054,14 +1032,11 @@ .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_820_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -1069,7 +1044,8 @@ .fetch_size = intel_8xx_fetch_size, .cleanup = intel_820_cleanup, .tlb_flush = intel_820_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1080,13 +1056,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_830mp_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_830mp_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 4, @@ -1094,7 +1067,8 @@ .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, .tlb_flush = intel_8xx_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1105,13 +1079,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_840_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -1119,7 +1090,8 @@ .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, .tlb_flush = intel_8xx_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1130,13 +1102,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_845_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -1144,7 +1113,8 @@ .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, .tlb_flush = intel_8xx_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1155,13 +1125,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = intel_845_resume, }; struct agp_bridge_driver intel_850_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -1169,7 +1136,8 @@ .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, .tlb_flush = intel_8xx_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1180,13 +1148,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_860_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -1194,7 +1159,8 @@ .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, .tlb_flush = intel_8xx_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1205,13 +1171,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver intel_7505_driver = { .owner = THIS_MODULE, - .masks = intel_generic_masks, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -1219,7 +1182,8 @@ .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, .tlb_flush = intel_8xx_tlbflush, - .mask_memory = intel_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = intel_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -1230,8 +1194,6 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; static int find_i810(u16 device, const char *name) @@ -1436,6 +1398,23 @@ agp_put_bridge(bridge); } +static int agp_intel_suspend(struct pci_dev *dev, u32 state) +{ + return 0; +} + +static int agp_intel_resume(struct pci_dev *pdev) +{ + struct agp_bridge_data *bridge = pci_get_drvdata(pdev); + + if (bridge->driver == &intel_generic_driver) + intel_configure(); + else if (bridge->driver == &intel_845_driver) + intel_845_configure(); + + return 0; +} + static struct pci_device_id agp_intel_pci_table[] __initdata = { { .class = (PCI_CLASS_BRIDGE_HOST << 8), @@ -1450,11 +1429,13 @@ MODULE_DEVICE_TABLE(pci, agp_intel_pci_table); -static struct __initdata pci_driver agp_intel_pci_driver = { +static struct pci_driver agp_intel_pci_driver = { .name = "agpgart-intel", .id_table = agp_intel_pci_table, .probe = agp_intel_probe, .remove = agp_intel_remove, + .suspend = agp_intel_suspend, + .resume = agp_intel_resume, }; /* intel_agp_init() must not be declared static for explicit diff -urN linux-2.5.69-bk13/drivers/char/agp/isoch.c linux-2.5.69-bk14/drivers/char/agp/isoch.c --- linux-2.5.69-bk13/drivers/char/agp/isoch.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/isoch.c 2003-05-20 04:46:41.000000000 -0700 @@ -314,7 +314,7 @@ * Fully configure and enable an AGP 3.0 host bridge and all the devices * lying behind it. */ -int agp_3_5_enable(struct agp_bridge_data *bridge, u32 mode) +int agp_3_5_enable(struct agp_bridge_data *bridge) { struct pci_dev *td = bridge->dev, *dev; u8 mcapndx; @@ -448,6 +448,8 @@ printk(KERN_INFO PFX "Something bad happened setting " "up isochronous xfers. Falling back to " "non-isochronous xfer mode.\n"); + } else { + goto free_and_exit; } } agp_3_5_nonisochronous_node_enable(bridge, dev_list, ndevs); diff -urN linux-2.5.69-bk13/drivers/char/agp/nvidia-agp.c linux-2.5.69-bk14/drivers/char/agp/nvidia-agp.c --- linux-2.5.69-bk13/drivers/char/agp/nvidia-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/nvidia-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -140,13 +140,12 @@ } -static unsigned long nvidia_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - return addr | agp_bridge->driver->masks[0].mask; -} - -#if 0 +/* + * Note we can't use the generic routines, even though they are 99% the same. + * Aperture sizes <64M still requires a full 64k GART directory, but + * only use the portion of the TLB entries that correspond to the apertures + * alignment inside the surrounding 64M block. + */ extern int agp_memory_reserved; static int nvidia_insert_memory(agp_memory * mem, off_t pg_start, int type) @@ -172,10 +171,11 @@ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) agp_bridge->gatt_table[nvidia_private.pg_offset + j] = mem->memory[i]; - agp_bridge->tlb_flush(mem); + agp_bridge->driver->tlb_flush(mem); return 0; } + static int nvidia_remove_memory(agp_memory * mem, off_t pg_start, int type) { int i; @@ -188,10 +188,9 @@ (unsigned long) agp_bridge->scratch_page; } - agp_bridge->tlb_flush(mem); + agp_bridge->driver->tlb_flush(mem); return 0; } -#endif static void nvidia_tlbflush(agp_memory * mem) @@ -238,13 +237,12 @@ static struct gatt_mask nvidia_generic_masks[] = { - {0x00000001, 0} + { .mask = 1, .type = 0} }; struct agp_bridge_driver nvidia_driver = { .owner = THIS_MODULE, - .masks = nvidia_generic_masks, .aperture_sizes = nvidia_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 5, @@ -252,19 +250,18 @@ .fetch_size = nvidia_fetch_size, .cleanup = nvidia_cleanup, .tlb_flush = nvidia_tlbflush, - .mask_memory = nvidia_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = nvidia_generic_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, .free_gatt_table = agp_generic_free_gatt_table, - .insert_memory = agp_generic_insert_memory, - .remove_memory = agp_generic_remove_memory, + .insert_memory = nvidia_insert_memory, + .remove_memory = nvidia_remove_memory, .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; static int __init agp_nvidia_probe(struct pci_dev *pdev, @@ -354,7 +351,7 @@ MODULE_DEVICE_TABLE(pci, agp_nvidia_pci_table); -static struct __initdata pci_driver agp_nvidia_pci_driver = { +static struct pci_driver agp_nvidia_pci_driver = { .name = "agpgart-nvidia", .id_table = agp_nvidia_pci_table, .probe = agp_nvidia_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/sis-agp.c linux-2.5.69-bk14/drivers/char/agp/sis-agp.c --- linux-2.5.69-bk13/drivers/char/agp/sis-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/sis-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -63,13 +63,6 @@ (previous_size->size_value & ~(0x03))); } -static unsigned long sis_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - - return addr | agp_bridge->driver->masks[0].mask; -} - static struct aper_size_info_8 sis_generic_sizes[7] = { {256, 65536, 6, 99}, @@ -81,14 +74,8 @@ {4, 1024, 0, 3} }; -static struct gatt_mask sis_generic_masks[] = -{ - {.mask = 0x00000000, .type = 0} -}; - struct agp_bridge_driver sis_driver = { .owner = THIS_MODULE, - .masks = sis_generic_masks, .aperture_sizes = sis_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -96,7 +83,8 @@ .fetch_size = sis_fetch_size, .cleanup = sis_cleanup, .tlb_flush = sis_tlbflush, - .mask_memory = sis_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -107,8 +95,6 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_device_ids sis_agp_device_ids[] __initdata = @@ -240,7 +226,7 @@ MODULE_DEVICE_TABLE(pci, agp_sis_pci_table); -static struct __initdata pci_driver agp_sis_pci_driver = { +static struct pci_driver agp_sis_pci_driver = { .name = "agpgart-sis", .id_table = agp_sis_pci_table, .probe = agp_sis_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/sworks-agp.c linux-2.5.69-bk14/drivers/char/agp/sworks-agp.c --- linux-2.5.69-bk13/drivers/char/agp/sworks-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/sworks-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -308,13 +308,6 @@ iounmap((void *) serverworks_private.registers); } -static unsigned long serverworks_mask_memory(unsigned long addr, int type) -{ - /* Only type 0 is supported by the serverworks chipsets */ - - return addr | agp_bridge->driver->masks[0].mask; -} - static int serverworks_insert_memory(agp_memory * mem, off_t pg_start, int type) { @@ -383,7 +376,7 @@ static struct gatt_mask serverworks_masks[] = { - {.mask = 0x00000001, .type = 0} + {.mask = 1, .type = 0} }; static struct aper_size_info_lvl2 serverworks_sizes[7] = @@ -421,7 +414,6 @@ struct agp_bridge_driver sworks_driver = { .owner = THIS_MODULE, - .masks = serverworks_masks, .aperture_sizes = serverworks_sizes, .size_type = LVL2_APER_SIZE, .num_aperture_sizes = 7, @@ -429,7 +421,8 @@ .fetch_size = serverworks_fetch_size, .cleanup = serverworks_cleanup, .tlb_flush = serverworks_tlbflush, - .mask_memory = serverworks_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = serverworks_masks, .agp_enable = serverworks_agp_enable, .cache_flush = global_cache_flush, .create_gatt_table = serverworks_create_gatt_table, @@ -440,8 +433,6 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; static int __init agp_serverworks_probe(struct pci_dev *pdev, @@ -532,7 +523,7 @@ MODULE_DEVICE_TABLE(pci, agp_serverworks_pci_table); -static struct __initdata pci_driver agp_serverworks_pci_driver = { +static struct pci_driver agp_serverworks_pci_driver = { .name = "agpgart-serverworks", .id_table = agp_serverworks_pci_table, .probe = agp_serverworks_probe, diff -urN linux-2.5.69-bk13/drivers/char/agp/uninorth-agp.c linux-2.5.69-bk14/drivers/char/agp/uninorth-agp.c --- linux-2.5.69-bk13/drivers/char/agp/uninorth-agp.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/char/agp/uninorth-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -0,0 +1,398 @@ +/* + * UniNorth AGPGART routines. + */ +#include +#include +#include +#include +#include +#include +#include +#include "agp.h" + +static int agp_try_unsupported __initdata = 0; + +static int uninorth_fetch_size(void) +{ + int i; + u32 temp; + struct aper_size_info_32 *values; + + pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_BASE, &temp); + temp &= ~(0xfffff000); + values = A_SIZE_32(agp_bridge->driver->aperture_sizes); + + for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge->previous_size = + agp_bridge->current_size = (void *) (values + i); + agp_bridge->aperture_size_idx = i; + return values[i].size; + } + } + + agp_bridge->previous_size = + agp_bridge->current_size = (void *) (values + 1); + agp_bridge->aperture_size_idx = 1; + return values[1].size; + + return 0; +} + +static void uninorth_tlbflush(agp_memory * mem) +{ + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL); + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE); + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_2xRESET); + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE); +} + +static void uninorth_cleanup(void) +{ + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL); + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + 0); + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_2xRESET); + pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, + 0); +} + +static int uninorth_configure(void) +{ + struct aper_size_info_32 *current_size; + + current_size = A_SIZE_32(agp_bridge->current_size); + + printk(KERN_INFO PFX "configuring for size idx: %d\n", + current_size->size_value); + + /* aperture size and gatt addr */ + pci_write_config_dword(agp_bridge->dev, + UNI_N_CFG_GART_BASE, + (agp_bridge->gatt_bus_addr & 0xfffff000) + | current_size->size_value); + + /* HACK ALERT + * UniNorth seem to be buggy enough not to handle properly when + * the AGP aperture isn't mapped at bus physical address 0 + */ + agp_bridge->gart_bus_addr = 0; + pci_write_config_dword(agp_bridge->dev, + UNI_N_CFG_AGP_BASE, agp_bridge->gart_bus_addr); + + return 0; +} + +static int uninorth_insert_memory(agp_memory * mem, off_t pg_start, int type) +{ + int i, j, num_entries; + void *temp; + + temp = agp_bridge->current_size; + num_entries = A_SIZE_32(temp)->num_entries; + + if (type != 0 || mem->type != 0) + /* We know nothing of memory types */ + return -EINVAL; + if ((pg_start + mem->page_count) > num_entries) + return -EINVAL; + + j = pg_start; + + while (j < (pg_start + mem->page_count)) { + if (!PGE_EMPTY(agp_bridge, agp_bridge->gatt_table[j])) + return -EBUSY; + j++; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + agp_bridge->gatt_table[j] = cpu_to_le32((mem->memory[i] & 0xfffff000) | 0x00000001UL); + flush_dcache_range((unsigned long)__va(mem->memory[i]), + (unsigned long)__va(mem->memory[i])+0x1000); + } + (void)in_le32((volatile u32*)&agp_bridge->gatt_table[pg_start]); + mb(); + flush_dcache_range((unsigned long)&agp_bridge->gatt_table[pg_start], + (unsigned long)&agp_bridge->gatt_table[pg_start + mem->page_count]); + + uninorth_tlbflush(mem); + return 0; +} + +static void uninorth_agp_enable(u32 mode) +{ + u32 command, scratch; + int timeout; + + pci_read_config_dword(agp_bridge->dev, + agp_bridge->capndx + PCI_AGP_STATUS, + &command); + + command = agp_collect_device_status(mode, command); + command |= 0x100; + + uninorth_tlbflush(NULL); + + timeout = 0; + do { + pci_write_config_dword(agp_bridge->dev, + agp_bridge->capndx + PCI_AGP_COMMAND, + command); + pci_read_config_dword(agp_bridge->dev, + agp_bridge->capndx + PCI_AGP_COMMAND, + &scratch); + } while ((scratch & 0x100) == 0 && ++timeout < 1000); + if ((scratch & 0x100) == 0) + printk(KERN_ERR PFX "failed to write UniNorth AGP command reg\n"); + + agp_device_command(command, 0); + + uninorth_tlbflush(NULL); +} + +static int uninorth_create_gatt_table(void) +{ + char *table; + char *table_end; + int size; + int page_order; + int num_entries; + int i; + void *temp; + struct page *page; + + /* We can't handle 2 level gatt's */ + if (agp_bridge->driver->size_type == LVL2_APER_SIZE) + return -EINVAL; + + table = NULL; + i = agp_bridge->aperture_size_idx; + temp = agp_bridge->current_size; + size = page_order = num_entries = 0; + + do { + size = A_SIZE_32(temp)->size; + page_order = A_SIZE_32(temp)->page_order; + num_entries = A_SIZE_32(temp)->num_entries; + + table = (char *) __get_free_pages(GFP_KERNEL, page_order); + + if (table == NULL) { + i++; + agp_bridge->current_size = A_IDX32(agp_bridge); + } else { + agp_bridge->aperture_size_idx = i; + } + } while (!table && (i < agp_bridge->driver->num_aperture_sizes)); + + if (table == NULL) + return -ENOMEM; + + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + SetPageReserved(page); + + agp_bridge->gatt_table_real = (u32 *) table; + agp_bridge->gatt_table = (u32 *)table; + agp_bridge->gatt_bus_addr = virt_to_phys(table); + + for (i = 0; i < num_entries; i++) { + agp_bridge->gatt_table[i] = + (unsigned long) agp_bridge->scratch_page; + } + + flush_dcache_range((unsigned long)table, (unsigned long)table_end); + + return 0; +} + +static int uninorth_free_gatt_table(void) +{ + int page_order; + char *table, *table_end; + void *temp; + struct page *page; + + temp = agp_bridge->current_size; + page_order = A_SIZE_32(temp)->page_order; + + /* Do not worry about freeing memory, because if this is + * called, then all agp memory is deallocated and removed + * from the table. + */ + + table = (char *) agp_bridge->gatt_table_real; + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + ClearPageReserved(page); + + free_pages((unsigned long) agp_bridge->gatt_table_real, page_order); + + return 0; +} + +void null_cache_flush(void) +{ + mb(); +} + +/* Setup function */ + +static struct aper_size_info_32 uninorth_sizes[7] = +{ +#if 0 /* Not sure uninorth supports that high aperture sizes */ + {256, 65536, 6, 64}, + {128, 32768, 5, 32}, + {64, 16384, 4, 16}, +#endif + {32, 8192, 3, 8}, + {16, 4096, 2, 4}, + {8, 2048, 1, 2}, + {4, 1024, 0, 1} +}; + +struct agp_bridge_driver uninorth_agp_driver = { + .owner = THIS_MODULE, + .aperture_sizes = (void *)uninorth_sizes, + .size_type = U32_APER_SIZE, + .num_aperture_sizes = 4, + .configure = uninorth_configure, + .fetch_size = uninorth_fetch_size, + .cleanup = uninorth_cleanup, + .tlb_flush = uninorth_tlbflush, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, + .cache_flush = null_cache_flush, + .agp_enable = uninorth_agp_enable, + .create_gatt_table = uninorth_create_gatt_table, + .free_gatt_table = uninorth_free_gatt_table, + .insert_memory = uninorth_insert_memory, + .remove_memory = agp_generic_remove_memory, + .alloc_by_type = agp_generic_alloc_by_type, + .free_by_type = agp_generic_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_destroy_page = agp_generic_destroy_page, + .suspend = agp_generic_suspend, + .resume = agp_generic_resume, + .cant_use_aperture = 1, +}; + +struct agp_device_ids uninorth_agp_device_ids[] __initdata = { + { + .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP, + .chipset_name = "UniNorth", + }, + { + .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP_P, + .chipset_name = "UniNorth/Pangea", + }, + { + .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP15, + .chipset_name = "UniNorth 1.5", + }, + { + .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP2, + .chipset_name = "UniNorth 2", + }, +}; + +static int __init agp_uninorth_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct agp_device_ids *devs = uninorth_agp_device_ids; + struct agp_bridge_data *bridge; + u8 cap_ptr; + int j; + + cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); + if (cap_ptr == 0) + return -ENODEV; + + /* probe for known chipsets */ + for (j = 0; devs[j].chipset_name != NULL; ++j) { + if (pdev->device == devs[j].device_id) { + printk(KERN_INFO PFX "Detected Apple %s chipset\n", + devs[j].chipset_name); + goto found; + } + } + + if (!agp_try_unsupported) { + printk(KERN_ERR PFX "Unsupported Apple chipset" + " (device id: %04x).\n", pdev->device); + printk(KERN_ERR PFX "You might want to try" + " agp_try_unsupported=1\n"); + return -ENODEV; + } + printk(KERN_ERR PFX "Trying generic Uninorth routines" + " for device id %04x\n", pdev->device); + + found: + bridge = agp_alloc_bridge(); + if (!bridge) + return -ENOMEM; + + bridge->driver = &uninorth_agp_driver; + bridge->dev = pdev; + bridge->capndx = cap_ptr; + + /* Fill in the mode register */ + pci_read_config_dword(pdev, cap_ptr+PCI_AGP_STATUS, &bridge->mode); + + pci_set_drvdata(pdev, bridge); + return agp_add_bridge(bridge); +} + +static void __devexit agp_uninorth_remove(struct pci_dev *pdev) +{ + struct agp_bridge_data *bridge = pci_get_drvdata(pdev); + + agp_remove_bridge(bridge); + agp_put_bridge(bridge); +} + +static struct pci_device_id agp_uninorth_pci_table[] __initdata = { + { + .class = (PCI_CLASS_BRIDGE_HOST << 8), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_APPLE, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { } +}; + +MODULE_DEVICE_TABLE(pci, agp_uninorth_pci_table); + +static struct pci_driver agp_uninorth_pci_driver = { + .name = "agpgart-uninorth", + .id_table = agp_uninorth_pci_table, + .probe = agp_uninorth_probe, + .remove = agp_uninorth_remove, +}; + +static int __init agp_uninorth_init(void) +{ + return pci_module_init(&agp_uninorth_pci_driver); +} + +static void __exit agp_uninorth_cleanup(void) +{ + pci_unregister_driver(&agp_uninorth_pci_driver); +} + +module_init(agp_uninorth_init); +module_exit(agp_uninorth_cleanup); + +MODULE_PARM(agp_try_unsupported, "1i"); +MODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/char/agp/via-agp.c linux-2.5.69-bk14/drivers/char/agp/via-agp.c --- linux-2.5.69-bk13/drivers/char/agp/via-agp.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/agp/via-agp.c 2003-05-20 04:46:41.000000000 -0700 @@ -75,14 +75,6 @@ } -static unsigned long via_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - - return addr | agp_bridge->driver->masks[0].mask; -} - - static struct aper_size_info_8 via_generic_sizes[7] = { {256, 65536, 6, 0}, @@ -95,12 +87,6 @@ }; -static struct gatt_mask via_generic_masks[] = -{ - {.mask = 0x00000000, .type = 0} -}; - - static int via_fetch_size_agp3(void) { int i; @@ -176,7 +162,6 @@ struct agp_bridge_driver via_agp3_driver = { .owner = THIS_MODULE, - .masks = via_generic_masks, .aperture_sizes = via_generic_agp3_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 10, @@ -184,7 +169,8 @@ .fetch_size = via_fetch_size_agp3, .cleanup = via_cleanup_agp3, .tlb_flush = via_tlbflush_agp3, - .mask_memory = via_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -195,13 +181,10 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; struct agp_bridge_driver via_driver = { .owner = THIS_MODULE, - .masks = via_generic_masks, .aperture_sizes = via_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, @@ -209,7 +192,8 @@ .fetch_size = via_fetch_size, .cleanup = via_cleanup, .tlb_flush = via_tlbflush, - .mask_memory = via_mask_memory, + .mask_memory = agp_generic_mask_memory, + .masks = NULL, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, @@ -220,8 +204,6 @@ .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_destroy_page = agp_generic_destroy_page, - .suspend = agp_generic_suspend, - .resume = agp_generic_resume, }; static struct agp_device_ids via_agp_device_ids[] __initdata = @@ -402,6 +384,7 @@ bridge->dev = pdev; bridge->capndx = cap_ptr; + bridge->driver = &via_driver; switch (pdev->device) { case PCI_DEVICE_ID_VIA_8367_0: @@ -427,7 +410,6 @@ } /*FALLTHROUGH*/ default: - bridge->driver = &via_driver; break; } @@ -466,7 +448,7 @@ MODULE_DEVICE_TABLE(pci, agp_via_pci_table); -static struct __initdata pci_driver agp_via_pci_driver = { +static struct pci_driver agp_via_pci_driver = { .name = "agpgart-via", .id_table = agp_via_pci_table, .probe = agp_via_probe, diff -urN linux-2.5.69-bk13/drivers/char/nwbutton.h linux-2.5.69-bk14/drivers/char/nwbutton.h --- linux-2.5.69-bk13/drivers/char/nwbutton.h 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/drivers/char/nwbutton.h 2003-05-20 04:46:41.000000000 -0700 @@ -25,7 +25,7 @@ /* Function prototypes: */ static void button_sequence_finished (unsigned long parameters); -static void button_handler (int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t button_handler (int irq, void *dev_id, struct pt_regs *regs); static int button_read (struct file *filp, char *buffer, size_t count, loff_t *ppos); int button_init (void); diff -urN linux-2.5.69-bk13/drivers/i2c/busses/Kconfig linux-2.5.69-bk14/drivers/i2c/busses/Kconfig --- linux-2.5.69-bk13/drivers/i2c/busses/Kconfig 2003-05-04 16:53:40.000000000 -0700 +++ linux-2.5.69-bk14/drivers/i2c/busses/Kconfig 2003-05-20 04:46:42.000000000 -0700 @@ -117,6 +117,31 @@ http://www.lm-sensors.nu +config I2C_SIS96X + tristate " SiS 96x" + depends on I2C && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the SiS + 96x SMBus (a subset of I2C) interfaces. Specifically, the following + chipsets are supported: + 645/961 + 645DX/961 + 645DX/962 + 648/961 + 650/961 + 735 + + This can also be built as a module which can be inserted and removed + while the kernel is running. If you want to compile it as a module, + say M here and read . + + The module will be called i2c-sis96x. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + + config I2C_VIAPRO tristate " VIA 82C596/82C686/823x" depends on I2C && PCI && EXPERIMENTAL diff -urN linux-2.5.69-bk13/drivers/i2c/busses/Makefile linux-2.5.69-bk14/drivers/i2c/busses/Makefile --- linux-2.5.69-bk13/drivers/i2c/busses/Makefile 2003-05-04 16:53:01.000000000 -0700 +++ linux-2.5.69-bk14/drivers/i2c/busses/Makefile 2003-05-20 04:46:42.000000000 -0700 @@ -8,4 +8,5 @@ obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISA) += i2c-isa.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o +obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o diff -urN linux-2.5.69-bk13/drivers/i2c/busses/i2c-piix4.c linux-2.5.69-bk14/drivers/i2c/busses/i2c-piix4.c --- linux-2.5.69-bk13/drivers/i2c/busses/i2c-piix4.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/i2c/busses/i2c-piix4.c 2003-05-20 04:46:42.000000000 -0700 @@ -269,7 +269,7 @@ if (temp & 0x04) { result = -1; - dev_err(&piix4_adapter.dev, "Error: no response!\n"); + dev_dbg(&piix4_adapter.dev, "Error: no response!\n"); } if (inb_p(SMBHSTSTS) != 0x00) @@ -467,7 +467,7 @@ static struct pci_driver piix4_driver = { - .name = "piix4 smbus", + .name = "piix4-smbus", .id_table = piix4_ids, .probe = piix4_probe, .remove = __devexit_p(piix4_remove), diff -urN linux-2.5.69-bk13/drivers/i2c/busses/i2c-sis96x.c linux-2.5.69-bk14/drivers/i2c/busses/i2c-sis96x.c --- linux-2.5.69-bk13/drivers/i2c/busses/i2c-sis96x.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/i2c/busses/i2c-sis96x.c 2003-05-20 04:46:42.000000000 -0700 @@ -0,0 +1,376 @@ +/* + sis96x.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 2003 Mark M. Hoffman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This module must be considered BETA unless and until + the chipset manufacturer releases a datasheet. + The register definitions are based on the SiS630. + + This module relies on quirk_sis_96x_smbus (drivers/pci/quirks.c) + for just about every machine for which users have reported. + If this module isn't detecting your 96x south bridge, have a + look there. + + We assume there can only be one SiS96x with one SMBus interface. +*/ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + HISTORY: + 2003-05-11 1.0.0 Updated from lm_sensors project for kernel 2.5 + (was i2c-sis645.c from lm_sensors 2.7.0) +*/ +#define SIS96x_VERSION "1.0.0" + +/* SiS96x SMBus PCI device ID */ +#define PCI_DEVICE_ID_SI_SMBUS 0x16 + +/* base address register in PCI config space */ +#define SIS96x_BAR 0x04 + +/* SiS96x SMBus registers */ +#define SMB_STS 0x00 +#define SMB_EN 0x01 +#define SMB_CNT 0x02 +#define SMB_HOST_CNT 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCOUNT 0x06 +#define SMB_COUNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV_ADDR 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_SAA 0x13 + +/* register count for request_region */ +#define SMB_IOSIZE 0x20 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SiS96x SMBus constants */ +#define SIS96x_QUICK 0x00 +#define SIS96x_BYTE 0x01 +#define SIS96x_BYTE_DATA 0x02 +#define SIS96x_WORD_DATA 0x03 +#define SIS96x_PROC_CALL 0x04 +#define SIS96x_BLOCK_DATA 0x05 + +static struct i2c_adapter sis96x_adapter; +static u16 sis96x_smbus_base = 0; + +static inline u8 sis96x_read(u8 reg) +{ + return inb(sis96x_smbus_base + reg) ; +} + +static inline void sis96x_write(u8 reg, u8 data) +{ + outb(data, sis96x_smbus_base + reg) ; +} + +/* Internally used pause function */ +static void sis96x_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Execute a SMBus transaction. + int size is from SIS96x_QUICK to SIS96x_BLOCK_DATA + */ +static int sis96x_transaction(int size) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&sis96x_adapter.dev, "SMBus transaction %d\n", size); + + /* Make sure the SMBus host is ready to start transmitting */ + if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { + + dev_dbg(&sis96x_adapter.dev, "SMBus busy (0x%02x). " + "Resetting...\n", temp); + + /* kill the transaction */ + sis96x_write(SMB_HOST_CNT, 0x20); + + /* check it again */ + if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { + dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp); + return -1; + } else { + dev_dbg(&sis96x_adapter.dev, "Successful\n"); + } + } + + /* Turn off timeout interrupts, set fast host clock */ + sis96x_write(SMB_CNT, 0x20); + + /* clear all (sticky) status flags */ + temp = sis96x_read(SMB_STS); + sis96x_write(SMB_STS, temp & 0x1e); + + /* start the transaction by setting bit 4 and size bits */ + sis96x_write(SMB_HOST_CNT, 0x10 | (size & 0x07)); + + /* We will always wait for a fraction of a second! */ + do { + sis96x_do_pause(1); + temp = sis96x_read(SMB_STS); + } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp); + result = -1; + } + + /* device error - probably missing ACK */ + if (temp & 0x02) { + dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n"); + result = -1; + } + + /* bus collision */ + if (temp & 0x04) { + dev_dbg(&sis96x_adapter.dev, "Bus collision!\n"); + result = -1; + } + + /* Finish up by resetting the bus */ + sis96x_write(SMB_STS, temp); + if ((temp = sis96x_read(SMB_STS))) { + dev_dbg(&sis96x_adapter.dev, "Failed reset at " + "end of transaction! (0x%02x)\n", temp); + } + + return result; +} + +/* Return -1 on error. */ +static s32 sis96x_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + + switch (size) { + case I2C_SMBUS_QUICK: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS96x_QUICK; + break; + + case I2C_SMBUS_BYTE: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis96x_write(SMB_CMD, command); + size = SIS96x_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis96x_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis96x_write(SMB_BYTE, data->byte); + size = SIS96x_BYTE_DATA; + break; + + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis96x_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis96x_write(SMB_BYTE, data->word & 0xff); + sis96x_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? + SIS96x_PROC_CALL : SIS96x_WORD_DATA); + break; + + case I2C_SMBUS_BLOCK_DATA: + /* TO DO: */ + dev_info(&adap->dev, "SMBus block not implemented!\n"); + return -1; + break; + + default: + dev_info(&adap->dev, "Unsupported I2C size\n"); + return -1; + break; + } + + if (sis96x_transaction(size)) + return -1; + + if ((size != SIS96x_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS96x_QUICK))) + return 0; + + switch (size) { + case SIS96x_BYTE: + case SIS96x_BYTE_DATA: + data->byte = sis96x_read(SMB_BYTE); + break; + + case SIS96x_WORD_DATA: + case SIS96x_PROC_CALL: + data->word = sis96x_read(SMB_BYTE) + + (sis96x_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +static u32 sis96x_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = sis96x_access, + .functionality = sis96x_func, +}; + +static struct i2c_adapter sis96x_adapter = { + .owner = THIS_MODULE, + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS96X, + .class = I2C_ADAP_CLASS_SMBUS, + .algo = &smbus_algorithm, + .dev = { + .name ="unset", + }, +}; + +static struct pci_device_id sis96x_ids[] __devinitdata = { + + { + .vendor = PCI_VENDOR_ID_SI, + .device = PCI_DEVICE_ID_SI_SMBUS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + + { 0, } +}; + +static int __devinit sis96x_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u16 ww = 0; + int retval; + + if (sis96x_smbus_base) { + dev_err(&dev->dev, "Only one device supported.\n"); + return -EBUSY; + } + + pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); + if (PCI_CLASS_SERIAL_SMBUS != ww) { + dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww); + return -ENODEV; + } + + sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR); + if (!sis96x_smbus_base) { + dev_err(&dev->dev, "SiS96x SMBus base address " + "not initialized!\n"); + return -EINVAL; + } + dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n", + sis96x_smbus_base); + + /* Everything is happy, let's grab the memory and set things up. */ + if (!request_region(sis96x_smbus_base, SMB_IOSIZE, "sis96x-smbus")) { + dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x " + "already in use!\n", sis96x_smbus_base, + sis96x_smbus_base + SMB_IOSIZE - 1); + + sis96x_smbus_base = 0; + return -EINVAL; + } + + /* set up the driverfs linkage to our parent device */ + sis96x_adapter.dev.parent = &dev->dev; + + snprintf(sis96x_adapter.dev.name, DEVICE_NAME_SIZE, + "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base); + + if ((retval = i2c_add_adapter(&sis96x_adapter))) { + dev_err(&dev->dev, "Couldn't register adapter!\n"); + release_region(sis96x_smbus_base, SMB_IOSIZE); + sis96x_smbus_base = 0; + } + + return retval; +} + +static void __devexit sis96x_remove(struct pci_dev *dev) +{ + if (sis96x_smbus_base) { + i2c_del_adapter(&sis96x_adapter); + release_region(sis96x_smbus_base, SMB_IOSIZE); + sis96x_smbus_base = 0; + } +} + +static struct pci_driver sis96x_driver = { + .name = "sis96x smbus", + .id_table = sis96x_ids, + .probe = sis96x_probe, + .remove = __devexit_p(sis96x_remove), +}; + +static int __init i2c_sis96x_init(void) +{ + printk(KERN_INFO "i2c-sis96x version %s\n", SIS96x_VERSION); + return pci_module_init(&sis96x_driver); +} + +static void __exit i2c_sis96x_exit(void) +{ + pci_unregister_driver(&sis96x_driver); +} + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("SiS96x SMBus driver"); +MODULE_LICENSE("GPL"); + +/* Register initialization functions using helper macros */ +module_init(i2c_sis96x_init); +module_exit(i2c_sis96x_exit); + diff -urN linux-2.5.69-bk13/drivers/i2c/chips/it87.c linux-2.5.69-bk14/drivers/i2c/chips/it87.c --- linux-2.5.69-bk13/drivers/i2c/chips/it87.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/i2c/chips/it87.c 2003-05-20 04:46:42.000000000 -0700 @@ -630,7 +630,6 @@ } } } - memset (new_client, 0x00, sizeof(struct i2c_client) + sizeof(struct it87_data)); /* OK. For now, we presume we have a valid client. We now create the client structure, even though we cannot fill it completely yet. diff -urN linux-2.5.69-bk13/drivers/i2c/i2c-dev.c linux-2.5.69-bk14/drivers/i2c/i2c-dev.c --- linux-2.5.69-bk13/drivers/i2c/i2c-dev.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/i2c/i2c-dev.c 2003-05-20 04:46:42.000000000 -0700 @@ -3,6 +3,7 @@ Copyright (C) 1995-97 Simon G. Vogl Copyright (C) 1998-99 Frodo Looijaard + Copyright (C) 2003 Greg Kroah-Hartman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,8 +29,6 @@ /* The devfs code is contributed by Philipp Matthias Hahn */ -/* $Id: i2c-dev.c,v 1.53 2003/01/21 08:08:16 kmalkki Exp $ */ - /* If you want debugging uncomment: */ /* #define DEBUG 1 */ @@ -44,54 +43,84 @@ #include #include -/* struct file_operations changed too often in the 2.1 series for nice code */ - -static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, - loff_t *offset); -static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, - loff_t *offset); - -static int i2cdev_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static int i2cdev_open (struct inode *inode, struct file *file); - -static int i2cdev_release (struct inode *inode, struct file *file); - -static int i2cdev_attach_adapter(struct i2c_adapter *adap); -static int i2cdev_detach_adapter(struct i2c_adapter *adap); -static int i2cdev_detach_client(struct i2c_client *client); -static int i2cdev_command(struct i2c_client *client, unsigned int cmd, - void *arg); +static struct i2c_client i2cdev_client_template; -static struct file_operations i2cdev_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = i2cdev_read, - .write = i2cdev_write, - .ioctl = i2cdev_ioctl, - .open = i2cdev_open, - .release = i2cdev_release, +struct i2c_dev { + int minor; + struct i2c_adapter *adap; + struct class_device class_dev; }; +#define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev) -static struct i2c_driver i2cdev_driver = { - .owner = THIS_MODULE, - .name = "dev driver", - .id = I2C_DRIVERID_I2CDEV, - .flags = I2C_DF_NOTIFY, - .attach_adapter = i2cdev_attach_adapter, - .detach_adapter = i2cdev_detach_adapter, - .detach_client = i2cdev_detach_client, - .command = i2cdev_command, -}; +#define I2C_MINORS 256 +static struct i2c_dev *i2c_dev_array[I2C_MINORS]; +static spinlock_t i2c_dev_array_lock = SPIN_LOCK_UNLOCKED; -static struct i2c_client i2cdev_client_template = { - .dev = { - .name = "I2C /dev entry", - }, - .id = 1, - .addr = -1, - .driver = &i2cdev_driver, -}; +struct i2c_dev *i2c_dev_get_by_minor(unsigned index) +{ + struct i2c_dev *i2c_dev; + + spin_lock(&i2c_dev_array_lock); + i2c_dev = i2c_dev_array[index]; + spin_unlock(&i2c_dev_array_lock); + return i2c_dev; +} + +struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap) +{ + struct i2c_dev *i2c_dev = NULL; + int i; + + spin_lock(&i2c_dev_array_lock); + for (i = 0; i < I2C_MINORS; ++i) { + if ((i2c_dev_array[i]) && + (i2c_dev_array[i]->adap == adap)) { + i2c_dev = i2c_dev_array[i]; + break; + } + } + spin_unlock(&i2c_dev_array_lock); + return i2c_dev; +} + +static struct i2c_dev *get_free_i2c_dev(void) +{ + struct i2c_dev *i2c_dev; + unsigned int i; + + i2c_dev = kmalloc(sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return ERR_PTR(-ENOMEM); + memset(i2c_dev, 0x00, sizeof(*i2c_dev)); + + spin_lock(&i2c_dev_array_lock); + for (i = 0; i < I2C_MINORS; ++i) { + if (i2c_dev_array[i]) + continue; + i2c_dev->minor = i; + i2c_dev_array[i] = i2c_dev; + spin_unlock(&i2c_dev_array_lock); + return i2c_dev; + } + spin_unlock(&i2c_dev_array_lock); + kfree(i2c_dev); + return ERR_PTR(-ENODEV); +} + +static void return_i2c_dev(struct i2c_dev *i2c_dev) +{ + spin_lock(&i2c_dev_array_lock); + i2c_dev_array[i2c_dev->minor] = NULL; + spin_unlock(&i2c_dev_array_lock); + kfree(i2c_dev); +} + +static ssize_t show_dev(struct class_device *class_dev, char *buf) +{ + struct i2c_dev *i2c_dev = to_i2c_dev(class_dev); + return sprintf(buf, "%04x\n", MKDEV(I2C_MAJOR, i2c_dev->minor)); +} +static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset) @@ -104,7 +133,6 @@ if (count > 8192) count = 8192; - /* copy user space data to kernel space. */ tmp = kmalloc(count,GFP_KERNEL); if (tmp==NULL) return -ENOMEM; @@ -129,7 +157,6 @@ if (count > 8192) count = 8192; - /* copy user space data to kernel space. */ tmp = kmalloc(count,GFP_KERNEL); if (tmp==NULL) return -ENOMEM; @@ -157,7 +184,7 @@ int i,datasize,res; unsigned long funcs; - pr_debug("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + dev_dbg(&client->dev, "i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", minor(inode->i_rdev),cmd, arg); switch ( cmd ) { @@ -242,13 +269,11 @@ rdwr_arg.nmsgs); } while(i-- > 0) { - if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) - { + if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) { if(copy_to_user( rdwr_arg.msgs[i].buf, rdwr_pa[i].buf, - rdwr_pa[i].len)) - { + rdwr_pa[i].len)) { res = -EFAULT; } } @@ -340,9 +365,14 @@ unsigned int minor = minor(inode->i_rdev); struct i2c_client *client; struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; - adap = i2c_get_adapter(minor); - if (NULL == adap) + i2c_dev = i2c_dev_get_by_minor(minor); + if (!i2c_dev) + return -ENODEV; + + adap = i2c_get_adapter(i2c_dev->adap->nr); + if (!adap) return -ENODEV; client = kmalloc(sizeof(*client), GFP_KERNEL); @@ -370,29 +400,68 @@ return 0; } -int i2cdev_attach_adapter(struct i2c_adapter *adap) +static struct file_operations i2cdev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = i2cdev_read, + .write = i2cdev_write, + .ioctl = i2cdev_ioctl, + .open = i2cdev_open, + .release = i2cdev_release, +}; + +static struct class i2c_dev_class = { + .name = "i2c-dev", +}; + +static int i2cdev_attach_adapter(struct i2c_adapter *adap) { - int i; + struct i2c_dev *i2c_dev; + int retval; - i = i2c_adapter_id(adap); - devfs_mk_cdev(MKDEV(I2C_MAJOR, i), - S_IFCHR|S_IRUSR|S_IWUSR, "i2c/%d", i); - dev_dbg(&adap->dev, "Registered as minor %d\n", i); + i2c_dev = get_free_i2c_dev(); + if (IS_ERR(i2c_dev)) + return PTR_ERR(i2c_dev); + + devfs_mk_cdev(MKDEV(I2C_MAJOR, i2c_dev->minor), + S_IFCHR|S_IRUSR|S_IWUSR, "i2c/%d", i2c_dev->minor); + dev_dbg(&adap->dev, "Registered as minor %d\n", i2c_dev->minor); + + /* register this i2c device with the driver core */ + i2c_dev->adap = adap; + if (adap->dev.parent == &legacy_bus) + i2c_dev->class_dev.dev = &adap->dev; + else + i2c_dev->class_dev.dev = adap->dev.parent; + i2c_dev->class_dev.class = &i2c_dev_class; + snprintf(i2c_dev->class_dev.class_id, BUS_ID_SIZE, "i2c-%d", i2c_dev->minor); + retval = class_device_register(&i2c_dev->class_dev); + if (retval) + goto error; + class_device_create_file(&i2c_dev->class_dev, &class_device_attr_dev); return 0; +error: + return_i2c_dev(i2c_dev); + return retval; } -int i2cdev_detach_adapter(struct i2c_adapter *adap) +static int i2cdev_detach_adapter(struct i2c_adapter *adap) { - int i; + struct i2c_dev *i2c_dev; - i = i2c_adapter_id(adap); + i2c_dev = i2c_dev_get_by_adapter(adap); + if (!i2c_dev) + return -ENODEV; + + class_device_unregister(&i2c_dev->class_dev); + devfs_remove("i2c/%d", i2c_dev->minor); + return_i2c_dev(i2c_dev); - devfs_remove("i2c/%d", i); dev_dbg(&adap->dev, "Adapter unregistered\n"); return 0; } -int i2cdev_detach_client(struct i2c_client *client) +static int i2cdev_detach_client(struct i2c_client *client) { return 0; } @@ -403,7 +472,27 @@ return -1; } -int __init i2c_dev_init(void) +static struct i2c_driver i2cdev_driver = { + .owner = THIS_MODULE, + .name = "dev driver", + .id = I2C_DRIVERID_I2CDEV, + .flags = I2C_DF_NOTIFY, + .attach_adapter = i2cdev_attach_adapter, + .detach_adapter = i2cdev_detach_adapter, + .detach_client = i2cdev_detach_client, + .command = i2cdev_command, +}; + +static struct i2c_client i2cdev_client_template = { + .dev = { + .name = "I2C /dev entry", + }, + .id = 1, + .addr = -1, + .driver = &i2cdev_driver, +}; + +static int __init i2c_dev_init(void) { int res; @@ -416,6 +505,7 @@ return -EIO; } devfs_mk_dir("i2c"); + class_register(&i2c_dev_class); if ((res = i2c_add_driver(&i2cdev_driver))) { printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n"); devfs_remove("i2c"); @@ -428,11 +518,13 @@ static void __exit i2c_dev_exit(void) { i2c_del_driver(&i2cdev_driver); + class_unregister(&i2c_dev_class); devfs_remove("i2c"); unregister_chrdev(I2C_MAJOR,"i2c"); } -MODULE_AUTHOR("Frodo Looijaard and Simon G. Vogl "); +MODULE_AUTHOR("Frodo Looijaard and " + "Simon G. Vogl "); MODULE_DESCRIPTION("I2C /dev entries driver"); MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/ide/arm/icside.c linux-2.5.69-bk14/drivers/ide/arm/icside.c --- linux-2.5.69-bk13/drivers/ide/arm/icside.c 2003-05-04 16:53:28.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ide/arm/icside.c 2003-05-20 04:46:42.000000000 -0700 @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include @@ -27,12 +27,7 @@ #include #include -/* - * Maximum number of interfaces per card - */ -#define MAX_IFS 2 - -#define ICS_IDENT_OFFSET 0x8a0 +#define ICS_IDENT_OFFSET 0x2280 #define ICS_ARCIN_V5_INTRSTAT 0x000 #define ICS_ARCIN_V5_INTROFFSET 0x001 @@ -77,14 +72,19 @@ struct icside_state { unsigned int channel; unsigned int enabled; - unsigned int irq_port; + unsigned long irq_port; + unsigned long slot_port; + unsigned int type; + /* parent device... until the IDE core gets one of its own */ + struct device *dev; + ide_hwif_t *hwif[2]; }; -typedef enum { - ics_if_unknown, - ics_if_arcin_v5, - ics_if_arcin_v6 -} iftype_t; +#define ICS_TYPE_A3IN 0 +#define ICS_TYPE_A3USER 1 +#define ICS_TYPE_V6 3 +#define ICS_TYPE_V5 15 +#define ICS_TYPE_NOTYPE ((unsigned int)-1) /* ---------------- Version 5 PCB Support Functions --------------------- */ /* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) @@ -92,8 +92,10 @@ */ static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) { - unsigned int memc_port = (unsigned int)ec->irq_data; - outb(0, memc_port + ICS_ARCIN_V5_INTROFFSET); + struct icside_state *state = ec->irq_data; + unsigned int base = state->irq_port; + + outb(0, base + ICS_ARCIN_V5_INTROFFSET); } /* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) @@ -101,17 +103,15 @@ */ static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) { - unsigned int memc_port = (unsigned int)ec->irq_data; - inb(memc_port + ICS_ARCIN_V5_INTROFFSET); + struct icside_state *state = ec->irq_data; + unsigned int base = state->irq_port; + + inb(base + ICS_ARCIN_V5_INTROFFSET); } static const expansioncard_ops_t icside_ops_arcin_v5 = { - icside_irqenable_arcin_v5, - icside_irqdisable_arcin_v5, - NULL, - NULL, - NULL, - NULL + .irqenable = icside_irqenable_arcin_v5, + .irqdisable = icside_irqdisable_arcin_v5, }; @@ -163,65 +163,11 @@ } static const expansioncard_ops_t icside_ops_arcin_v6 = { - icside_irqenable_arcin_v6, - icside_irqdisable_arcin_v6, - icside_irqpending_arcin_v6, - NULL, - NULL, - NULL + .irqenable = icside_irqenable_arcin_v6, + .irqdisable = icside_irqdisable_arcin_v6, + .irqpending = icside_irqpending_arcin_v6, }; -/* Prototype: icside_identifyif (struct expansion_card *ec) - * Purpose : identify IDE interface type - * Notes : checks the description string - */ -static iftype_t __init icside_identifyif (struct expansion_card *ec) -{ - unsigned int addr; - iftype_t iftype; - int id = 0; - - iftype = ics_if_unknown; - - addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; - - id = inb(addr) & 1; - id |= (inb(addr + 1) & 1) << 1; - id |= (inb(addr + 2) & 1) << 2; - id |= (inb(addr + 3) & 1) << 3; - - switch (id) { - case 0: /* A3IN */ - printk("icside: A3IN unsupported\n"); - break; - - case 1: /* A3USER */ - printk("icside: A3USER unsupported\n"); - break; - - case 3: /* ARCIN V6 */ - printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v6; - break; - - case 15:/* ARCIN V5 (no id) */ - printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v5; - break; - - default:/* we don't know - complain very loudly */ - printk("icside: ***********************************\n"); - printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); - printk("icside: ***********************************\n"); - printk("icside: please report this to linux@arm.linux.org.uk\n"); - printk("icside: defaulting to ARCIN V5\n"); - iftype = ics_if_arcin_v5; - break; - } - - return iftype; -} - /* * Handle routing of interrupts. This is called before * we write the command to the drive. @@ -229,7 +175,7 @@ static void icside_maskproc(ide_drive_t *drive, int mask) { ide_hwif_t *hwif = HWIF(drive); - struct icside_state *state = hwif->hw.priv; + struct icside_state *state = hwif->hwif_data; unsigned long flags; local_irq_save(flags); @@ -271,6 +217,7 @@ static void ide_build_sglist(ide_drive_t *drive, struct request *rq) { ide_hwif_t *hwif = drive->hwif; + struct icside_state *state = hwif->hwif_data; struct scatterlist *sg = hwif->sg_table; int nents; @@ -280,9 +227,9 @@ ide_task_t *args = rq->special; if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) - hwif->sg_dma_direction = PCI_DMA_TODEVICE; + hwif->sg_dma_direction = DMA_TO_DEVICE; else - hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + hwif->sg_dma_direction = DMA_FROM_DEVICE; memset(sg, 0, sizeof(*sg)); sg->page = virt_to_page(rq->buffer); @@ -293,12 +240,12 @@ nents = blk_rq_map_sg(&drive->queue, rq, sg); if (rq_data_dir(rq) == READ) - hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + hwif->sg_dma_direction = DMA_FROM_DEVICE; else - hwif->sg_dma_direction = PCI_DMA_TODEVICE; + hwif->sg_dma_direction = DMA_TO_DEVICE; } - nents = pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); + nents = dma_map_sg(state->dev, sg, nents, hwif->sg_dma_direction); hwif->sg_nents = nents; } @@ -484,7 +431,7 @@ int xfer_mode = XFER_PIO_2; int on; - if (!id || !(id->capability & 1) || !hwif->autodma) + if (!(id->capability & 1) || !hwif->autodma) goto out; /* @@ -500,13 +447,7 @@ * Enable DMA on any drive that has multiword DMA */ if (id->field_valid & 2) { - if (id->dma_mword & 4) { - xfer_mode = XFER_MW_DMA_2; - } else if (id->dma_mword & 2) { - xfer_mode = XFER_MW_DMA_1; - } else if (id->dma_mword & 1) { - xfer_mode = XFER_MW_DMA_0; - } + xfer_mode = ide_dma_speed(drive, 0); goto out; } @@ -531,13 +472,14 @@ static int icside_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + struct icside_state *state = hwif->hwif_data; drive->waiting_for_dma = 0; disable_dma(hwif->hw.dma); /* Teardown mappings after DMA has completed. */ - pci_unmap_sg(NULL, hwif->sg_table, hwif->sg_nents, + dma_unmap_sg(state->dev, hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); hwif->sg_dma_active = 0; @@ -713,7 +655,7 @@ static int icside_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - struct icside_state *state = hwif->hw.priv; + struct icside_state *state = hwif->hwif_data; return inb(state->irq_port + (hwif->channel ? @@ -748,7 +690,7 @@ return 1; } -static int icside_setup_dma(ide_hwif_t *hwif) +static int icside_dma_init(ide_hwif_t *hwif) { int autodma = 0; @@ -763,6 +705,10 @@ if (!hwif->sg_table) goto failed; + hwif->atapi_dma = 1; + hwif->mwdma_mask = 7; /* MW0..2 */ + hwif->swdma_mask = 7; /* SW0..2 */ + hwif->dmatable_cpu = NULL; hwif->dmatable_dma = 0; hwif->speedproc = icside_set_speed; @@ -784,10 +730,10 @@ hwif->ide_dma_timeout = icside_dma_timeout; hwif->ide_dma_lostirq = icside_dma_lostirq; - hwif->drives[0].autodma = autodma; - hwif->drives[1].autodma = autodma; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; - printk(" capable%s\n", autodma ? ", auto-enable" : ""); + printk(" capable%s\n", hwif->autodma ? ", auto-enable" : ""); return 1; @@ -796,14 +742,16 @@ return 0; } -int ide_release_dma(ide_hwif_t *hwif) +static void icside_dma_exit(ide_hwif_t *hwif) { if (hwif->sg_table) { kfree(hwif->sg_table); hwif->sg_table = NULL; } - return 1; } +#else +#define icside_dma_init(hwif) (0) +#define icside_dma_exit(hwif) do { } while (0) #endif static ide_hwif_t *icside_find_hwif(unsigned long dataport) @@ -829,7 +777,7 @@ } static ide_hwif_t * -icside_setup(unsigned long base, struct cardinfo *info, int irq) +icside_setup(unsigned long base, struct cardinfo *info, struct expansion_card *ec) { unsigned long port = base + info->dataoffset; ide_hwif_t *hwif; @@ -847,42 +795,47 @@ } hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; hwif->io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; - hwif->hw.irq = irq; - hwif->irq = irq; - hwif->hw.dma = NO_DMA; + hwif->hw.irq = ec->irq; + hwif->irq = ec->irq; hwif->noprobe = 0; hwif->chipset = ide_acorn; + hwif->gendev.parent = &ec->dev; } return hwif; } -static int __init icside_register_v5(struct expansion_card *ec) +static int __init +icside_register_v5(struct icside_state *state, struct expansion_card *ec) { unsigned long slot_port; ide_hwif_t *hwif; slot_port = ecard_address(ec, ECARD_MEMC, 0); + state->irq_port = slot_port; + ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT); ec->irqmask = 1; - ec->irq_data = (void *)slot_port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v5; /* * Be on the safe side - disable interrupts */ inb(slot_port + ICS_ARCIN_V5_INTROFFSET); - hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq); + hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec); + + state->hwif[0] = hwif; return hwif ? 0 : -ENODEV; } -static int __init icside_register_v6(struct expansion_card *ec) +static int __init +icside_register_v6(struct icside_state *state, struct expansion_card *ec) { unsigned long slot_port, port; - struct icside_state *state; ide_hwif_t *hwif, *mate; unsigned int sel = 0; @@ -905,89 +858,173 @@ /* * Find and register the interfaces. */ - hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq); - mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq); + hwif = icside_setup(port, &icside_cardinfo_v6_1, ec); + mate = icside_setup(port, &icside_cardinfo_v6_2, ec); if (!hwif || !mate) return -ENODEV; - state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - state->channel = 0; - state->enabled = 0; state->irq_port = port; + state->slot_port = slot_port; + state->hwif[0] = hwif; + state->hwif[1] = mate; - ec->irq_data = state; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v6; hwif->maskproc = icside_maskproc; hwif->channel = 0; - hwif->hw.priv = state; + hwif->hwif_data = state; hwif->mate = mate; hwif->serialized = 1; hwif->config_data = slot_port; hwif->select_data = sel; - hwif->hw.dma = ec->dma; + hwif->hw.dma = ec->dma; mate->maskproc = icside_maskproc; mate->channel = 1; - mate->hw.priv = state; + mate->hwif_data = state; mate->mate = hwif; mate->serialized = 1; mate->config_data = slot_port; mate->select_data = sel | 1; - mate->hw.dma = ec->dma; + mate->hw.dma = ec->dma; -#ifdef CONFIG_BLK_DEV_IDEDMA_ICS if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) { - icside_setup_dma(hwif); - icside_setup_dma(mate); + icside_dma_init(hwif); + icside_dma_init(mate); } -#endif + return 0; } static int __devinit icside_probe(struct expansion_card *ec, const struct ecard_id *id) { - int result; + struct icside_state *state; + void *idmem; + int ret; + + state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto out; + } + + memset(state, 0, sizeof(state)); + state->type = ICS_TYPE_NOTYPE; + state->dev = &ec->dev; + + idmem = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), + ecard_resource_len(ec, ECARD_RES_IOCFAST)); + if (idmem) { + unsigned int type; + + type = readb(idmem + ICS_IDENT_OFFSET) & 1; + type |= (readb(idmem + ICS_IDENT_OFFSET + 4) & 1) << 1; + type |= (readb(idmem + ICS_IDENT_OFFSET + 8) & 1) << 2; + type |= (readb(idmem + ICS_IDENT_OFFSET + 12) & 1) << 3; + iounmap(idmem); + + state->type = type; + } + + switch (state->type) { + case ICS_TYPE_A3IN: + printk(KERN_WARNING "icside: A3IN unsupported\n"); + ret = -ENODEV; + break; - ecard_claim(ec); + case ICS_TYPE_A3USER: + printk(KERN_WARNING "icside: A3USER unsupported\n"); + ret = -ENODEV; + break; - switch (icside_identifyif(ec)) { - case ics_if_arcin_v5: - result = icside_register_v5(ec); + case ICS_TYPE_V5: + ret = icside_register_v5(state, ec); break; - case ics_if_arcin_v6: - result = icside_register_v6(ec); + case ICS_TYPE_V6: + ret = icside_register_v6(state, ec); break; default: - result = -ENODEV; + printk(KERN_WARNING "icside: unknown interface type\n"); + ret = -ENODEV; break; } - if (result) - ecard_release(ec); - /* - * this locks the driver in-core - remove this - * comment and the two lines below when we can - * safely remove interfaces. - */ - else + if (ret == 0) { + ecard_set_drvdata(ec, state); + + /* + * this locks the driver in-core - remove this + * comment and the line below when we can + * safely remove interfaces. + */ MOD_INC_USE_COUNT; + } else { + kfree(state); + } + out: + return ret; +} + +static void __devexit icside_remove(struct expansion_card *ec) +{ + struct icside_state *state = ecard_get_drvdata(ec); - return result; + switch (state->type) { + case ICS_TYPE_V5: + /* FIXME: tell IDE to stop using the interface */ + + /* Disable interrupts */ + inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); + break; + + case ICS_TYPE_V6: + /* FIXME: tell IDE to stop using the interface */ + icside_dma_exit(state->hwif[1]); + icside_dma_exit(state->hwif[0]); + + if (ec->dma != NO_DMA) + free_dma(ec->dma); + + /* Disable interrupts */ + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + + /* Reset the ROM pointer/EASI selection */ + outb(0, state->slot_port); + break; + } + + ecard_set_drvdata(ec, NULL); + ec->ops = NULL; + ec->irq_data = NULL; + + kfree(state); } -static void __devexit -icside_remove(struct expansion_card *ec) +static void icside_shutdown(struct expansion_card *ec) { - /* need to do more */ - ecard_release(ec); + struct icside_state *state = ecard_get_drvdata(ec); + + switch (state->type) { + case ICS_TYPE_V5: + /* Disable interrupts */ + inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); + break; + + case ICS_TYPE_V6: + /* Disable interrupts */ + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + + /* Reset the ROM pointer/EASI selection */ + outb(0, state->slot_port); + break; + } } static const struct ecard_id icside_ids[] = { @@ -999,6 +1036,7 @@ static struct ecard_driver icside_driver = { .probe = icside_probe, .remove = __devexit_p(icside_remove), + .shutdown = icside_shutdown, .id_table = icside_ids, .drv = { .name = "icside", diff -urN linux-2.5.69-bk13/drivers/ide/arm/rapide.c linux-2.5.69-bk14/drivers/ide/arm/rapide.c --- linux-2.5.69-bk13/drivers/ide/arm/rapide.c 2003-05-04 16:53:37.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ide/arm/rapide.c 2003-05-20 04:46:42.000000000 -0700 @@ -20,8 +20,6 @@ hw_regs_t hw; int i, ret; - ecard_claim(ec); - memset(&hw, 0, sizeof(hw)); for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { @@ -49,7 +47,6 @@ static void __devexit rapide_remove(struct expansion_card *ec) { /* need to do more */ - ecard_release(ec); } static struct ecard_id rapide_ids[] = { diff -urN linux-2.5.69-bk13/drivers/ieee1394/eth1394.c linux-2.5.69-bk14/drivers/ieee1394/eth1394.c --- linux-2.5.69-bk13/drivers/ieee1394/eth1394.c 2003-05-04 16:53:32.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ieee1394/eth1394.c 2003-05-20 04:46:43.000000000 -0700 @@ -79,7 +79,7 @@ printk(KERN_ERR fmt, ## args) static char version[] __devinitdata = - "$Rev: 918 $ Ben Collins "; + "$Rev: 931 $ Ben Collins "; /* Our ieee1394 highlevel driver */ #define ETHER1394_DRIVER_NAME "ether1394" diff -urN linux-2.5.69-bk13/drivers/ieee1394/iso.c linux-2.5.69-bk14/drivers/ieee1394/iso.c --- linux-2.5.69-bk13/drivers/ieee1394/iso.c 2003-05-04 16:53:37.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ieee1394/iso.c 2003-05-20 04:46:43.000000000 -0700 @@ -32,7 +32,6 @@ } dma_region_free(&iso->data_buf); - kfree(iso->infos); kfree(iso); } @@ -70,14 +69,11 @@ /* allocate and write the struct hpsb_iso */ - iso = kmalloc(sizeof(*iso), SLAB_KERNEL); + iso = kmalloc(sizeof(*iso) + buf_packets * sizeof(struct hpsb_iso_packet_info), GFP_KERNEL); if(!iso) return NULL; - /* allocate ringbuffer of packet descriptors */ - iso->infos = kmalloc(buf_packets * sizeof(struct hpsb_iso_packet_info), SLAB_KERNEL); - if(!iso->infos) - return NULL; + iso->infos = (struct hpsb_iso_packet_info *)(iso + 1); iso->type = type; iso->host = host; diff -urN linux-2.5.69-bk13/drivers/ieee1394/iso.h linux-2.5.69-bk14/drivers/ieee1394/iso.h --- linux-2.5.69-bk13/drivers/ieee1394/iso.h 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ieee1394/iso.h 2003-05-20 04:46:43.000000000 -0700 @@ -79,9 +79,6 @@ /* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */ unsigned int buf_size; - /* ringbuffer of packet descriptors in regular kernel memory */ - struct hpsb_iso_packet_info *infos; - /* # of packets in the ringbuffer */ unsigned int buf_packets; @@ -118,6 +115,11 @@ /* cycle at which next packet will be transmitted, -1 if not known */ int xmit_cycle; + + /* ringbuffer of packet descriptors in regular kernel memory + * XXX Keep this last, since we use over-allocated memory from + * this entry to fill this field. */ + struct hpsb_iso_packet_info *infos; }; /* functions available to high-level drivers (e.g. raw1394) */ diff -urN linux-2.5.69-bk13/drivers/ieee1394/ohci1394.c linux-2.5.69-bk14/drivers/ieee1394/ohci1394.c --- linux-2.5.69-bk13/drivers/ieee1394/ohci1394.c 2003-05-04 16:53:40.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ieee1394/ohci1394.c 2003-05-20 04:46:43.000000000 -0700 @@ -164,7 +164,7 @@ printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args) static char version[] __devinitdata = - "$Rev: 921 $ Ben Collins "; + "$Rev: 931 $ Ben Collins "; /* Module Parameters */ static int phys_dma = 1; @@ -2446,7 +2446,7 @@ reg_write(ohci,OHCI1394_PhyReqFilterLoSet, 0x00000000); } - DBGMSG(ohci->id, "PhyReqFilter=%08x%08x\n", + DBGMSG(ohci->id, "PhyReqFilter=%08x%08x", reg_read(ohci,OHCI1394_PhyReqFilterHiSet), reg_read(ohci,OHCI1394_PhyReqFilterLoSet)); @@ -3165,7 +3165,7 @@ struct config_rom_ptr cr; memset(&cr, 0, sizeof(cr)); - memset(ohci->csr_config_rom_cpu, 0, sizeof (ohci->csr_config_rom_cpu)); + memset(ohci->csr_config_rom_cpu, 0, sizeof(*ohci->csr_config_rom_cpu)); cr.data = ohci->csr_config_rom_cpu; diff -urN linux-2.5.69-bk13/drivers/ieee1394/sbp2.c linux-2.5.69-bk14/drivers/ieee1394/sbp2.c --- linux-2.5.69-bk13/drivers/ieee1394/sbp2.c 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ieee1394/sbp2.c 2003-05-20 04:46:43.000000000 -0700 @@ -4,6 +4,8 @@ * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) * jamesg@filanet.com (JSG) * + * Copyright (C) 2003 Ben Collins + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -30,228 +32,10 @@ * You may access any attached SBP-2 storage devices as if they were SCSI * devices (e.g. mount /dev/sda1, fdisk, mkfs, etc.). * - * - * Module Load Options: - * - * max_speed - Force max speed allowed - * (2 = 400mb, 1 = 200mb, 0 = 100mb. default = 2) - * serialize_io - Serialize all I/O coming down from the scsi drivers - * (0 = deserialized, 1 = serialized, default = 0) - * max_sectors, - Change max sectors per I/O supported (default = 255) - * exclusive_login - Set to zero if you'd like to allow multiple hosts the ability - * to log in at the same time. Sbp2 device must support this, - * and you must know what you're doing (default = 1) - * - * (e.g. insmod sbp2 sbp2.serialize_io = 1) - * - * - * Current Support: - * - * The SBP-2 driver is still in an early state, but supports a variety of devices. - * I have read/written many gigabytes of data from/to SBP-2 drives, and have seen - * performance of more than 25 MBytes/s on individual drives (limit of the media - * transfer rate). - * - * - * Following are a sampling of devices that have been tested successfully: - * - * - Western Digital IEEE-1394 hard drives - * - Maxtor IEEE-1394 hard drives - * - VST (SmartDisk) IEEE-1394 hard drives and Zip drives (several flavors) - * - LaCie IEEE-1394 hard drives (several flavors) - * - QPS IEEE-1394 CD-RW/DVD drives and hard drives - * - BusLink IEEE-1394 hard drives - * - Iomega IEEE-1394 Zip/Jazz/Peerless drives - * - ClubMac IEEE-1394 hard drives - * - FirePower IEEE-1394 hard drives - * - EzQuest IEEE-1394 hard drives and CD-RW drives - * - Castlewood/ADS IEEE-1394 ORB drives - * - Evergreen IEEE-1394 hard drives and CD-RW drives - * - Addonics IEEE-1394 CD-RW drives - * - Bellstor IEEE-1394 hard drives and CD-RW drives - * - APDrives IEEE-1394 hard drives - * - Fujitsu IEEE-1394 MO drives - * - Sony IEEE-1394 CD-RW drives - * - Epson IEEE-1394 scanners - * - ADS IEEE-1394 memory stick and compact flash readers - * - SBP-2 bridge-based devices (LSI, Oxford Semiconductor, Indigita bridges) - * - Various other standard IEEE-1394 hard drives and enclosures - * - * - * Performance Issues: - * - * - Make sure you are "not" running fat/fat32 on your attached SBP-2 drives. You'll - * get much better performance formatting the drive ext2 (but you will lose the - * ability to easily move the drive between Windows/Linux). - * - * * Current Issues: * * - Error Handling: SCSI aborts and bus reset requests are handled somewhat * but the code needs additional debugging. - * - * - * History: - * - * 07/25/00 - Initial revision (JSG) - * 08/11/00 - Following changes/bug fixes were made (JSG): - * * Bug fix to SCSI procfs code (still needs to be synched with 2.4 kernel). - * * Bug fix where request sense commands were actually sent on the bus. - * * Changed bus reset/abort code to deal with devices that spin up quite - * slowly (which result in SCSI time-outs). - * * "More" properly pull information from device's config rom, for enumeration - * of SBP-2 devices, and determining SBP-2 register offsets. - * * Change Simplified Direct Access Device type to Direct Access Device type in - * returned inquiry data, in order to make the SCSI stack happy. - * * Modified driver to register with the SCSI stack "before" enumerating any attached - * SBP-2 devices. This means that you'll have to use procfs scsi-add-device or - * some sort of script to discover new SBP-2 devices. - * * Minor re-write of some code and other minor changes. - * 08/28/00 - Following changes/bug fixes were made (JSG): - * * Bug fixes to scatter/gather support (case of one s/g element) - * * Updated direction table for scsi commands (mostly DVD commands) - * * Retries when trying to detect SBP-2 devices (for slow devices) - * * Slightly better error handling (previously none) when commands time-out. - * * Misc. other bug fixes and code reorganization. - * 09/13/00 - Following changes/bug fixes were made (JSG) - * * Moved detection/enumeration code to a kernel thread which is woken up when IEEE-1394 - * bus resets occur. - * * Added code to handle bus resets and hot-plugging while devices are mounted, but full - * hot-plug support is not quite there yet. - * * Now use speed map to determine speed and max payload sizes for ORBs - * * Clean-up of code and reorganization - * 09/19/00 - Added better hot-plug support and other minor changes (JSG) - * 10/15/00 - Fixes for latest 2.4.0 test kernel, minor fix for hot-plug race. (JSG) - * 12/03/00 - Created pool of request packet structures for use in sending out sbp2 command - * and agent reset requests. This removes the kmallocs/kfrees in the critical I/O paths, - * and also deals with some subtle race conditions related to allocating and freeing - * packets. (JSG) - * 12/09/00 - Improved the sbp2 device detection by actually reading the root and unit - * directory (khk@khk.net) - * 12/23/00 - Following changes/enhancements were made (JSG) - * * Only do SCSI to RBC command conversion for Direct Access and Simplified - * Direct Access Devices (this is pulled from the config rom root directory). - * This is needed because doing the conversion for all device types broke the - * Epson scanner. Still looking for a better way of determining when to convert - * commands (for RBC devices). Thanks to khk for helping on this! - * * Added ability to "emulate" physical dma support, for host adapters such as TILynx. - * * Determine max payload and speed by also looking at the host adapter's max_rec field. - * 01/19/01 - Added checks to sbp2 login and made the login time-out longer. Also fixed a compile - * problem for 2.4.0. (JSG) - * 01/24/01 - Fixed problem when individual s/g elements are 64KB or larger. Needed to break - * up these larger elements, since the sbp2 page table element size is only 16 bits. (JSG) - * 01/29/01 - Minor byteswap fix for login response (used for reconnect and log out). - * 03/07/01 - Following changes/enhancements were made (JSG) - * * Changes to allow us to catch the initial scsi bus scan (for detecting sbp2 - * devices when first loading sbp2.o). To disable this, un-define - * SBP2_SUPPORT_INITIAL_BUS_SCAN. - * * Temporary fix to deal with many sbp2 devices that do not support individual - * transfers of greater than 128KB in size. - * * Mode sense conversion from 6 byte to 10 byte versions for CDRW/DVD devices. (Mark Burton) - * * Define allowing support for goofy sbp2 devices that do not support mode - * sense command at all, allowing them to be mounted rw (such as 1394 memory - * stick and compact flash readers). Define SBP2_MODE_SENSE_WRITE_PROTECT_HACK - * if you need this fix. - * 03/29/01 - Major performance enhancements and misc. other changes. Thanks to Daniel Berlin for many of - * changes and suggestions for change: - * * Now use sbp2 doorbell and link commands on the fly (instead of serializing requests) - * * Removed all bit fields in an attempt to run on PPC machines (still needs a little more work) - * * Added large request break-up/linking support for sbp2 chipsets that do not support transfers - * greater than 128KB in size. - * * Bumped up max commands per lun to two, and max total outstanding commands to eight. - * 04/03/01 - Minor clean-up. Write orb pointer directly if no outstanding commands (saves one 1394 bus - * transaction). Added module load options (bus scan, mode sense hack, max speed, serialize_io, - * no_large_transfers). Better bus reset handling while I/O pending. Set serialize_io to 1 by - * default (debugging of deserialized I/O in progress). - * 04/04/01 - Added workaround for PPC Pismo firewire chipset. See #define below. (Daniel Berlin) - * 04/20/01 - Minor clean-up. Allocate more orb structures when running with sbp2 target chipsets with - * 128KB max transfer limit. - * 06/16/01 - Converted DMA interfaces to pci_dma - Ben Collins - * - * - * 11/17/01 - Various bugfixes/cleanups: - * * Remember to logout of device in sbp2_disconnect. - * * If we fail to reconnect to a device after bus reset - * remember to release unit directory, so the ieee1394 - * knows we no longer manage it. - * * Unregister scsi hosts in sbp2_remove_host when a - * hpsb_host goes away. - * * Remove stupid hack in sbp2_remove_host. - * * Switched to "manual" module initialization - * (i.e. not scsi_module.c) and moved sbp2_cleanup - * moved sbp2scsi_release to sbp2_module_ext. The - * release function is called once pr. registered - * scsi host, but sbp2_cleanup should only be called - * upon module unload. Moved much initialization - * from sbp2scsi_detect to sbp2_module_init. - * Kristian Hogsberg - * 01/06/02 - Misc bug fixes/enhancements: (JSG) - * * Enable use_new_eh_code for scsi stuff. - * * Do not write all ones for NULL ORB high/low fields, but - * rather leave reserved areas zeroed (per SBP2 spec). - * * Use newer scsi transfer direction passed down instead of our - * direction table. - * * Bumped login time-out to 20 seconds, as some devices are slow. - * * Fixed a couple scsi unregister bugs on module unload - * 01/13/02 - Fixed compatibility with certain SBP2 devices, such as Iomega - * 1394 devices (Peerless, Jazz). Also a bit of clean-up of the - * driver, thanks to H.J.Lu (hjl@lucon.org). Removed mode_sense_hack - * module load option, as it's been fixed in the 2.4 scsi stack. - * 02/10/02 - Added support for max_sectors, minor fix for inquiry command, make - * up sbp2 device type from inquiry response data if not part of - * device's 1394 unit directory. (JSG) - * 02/18/02 - Code clean-up and enhancements: (JSG) - * * Finish cleaning out hacked code for dealing with broken sbp2 devices - * which do not support requests of 128KB or greater. Now use - * max_sectors scsi host entry to limit transfer sizes. - * * Change status fifo address from a single address to a set of addresses, - * with each sbp2 device having its own status fifo address. This makes - * it easier to match the status write to the sbp2 device instance. - * * Minor change to use lun when logging into sbp2 devices. First step in - * supporting multi-lun devices such as CD/DVD changer devices. - * * Added a new module load option for setting max sectors. For use by folk - * who'd like to bump up the max scsi transfer size supported. - * * Enabled deserialized operation by default, allowing for better performance, - * particularily when running with multiple sbp2 devices. For debugging, - * you may enable serialization through use of the sbp2_serialize_io module - * load option (e.g. insmod sbp2 sbp2_serialize_io=1). - * 02/20/02 - Added a couple additional module load options. - * Needed to bump down max commands per lun because of the !%@&*^# QPS CDRW - * drive I have, which doesn't seem to get along with other sbp2 devices - * (or handle linked commands well). - * 04/21/02 - Added some additional debug capabilities: - * * Able to handle phys dma requests directly, if host controller has phys - * dma disabled (e.g. insmod ohci1394 phys_dma=0). Undefine CONFIG_IEEE1394_SBP2_PHYS_DMA - * if you'd like to disable sbp2 driver from registering for phys address range. - * * New packet dump debug define (CONFIG_IEEE1394_SBP2_PACKET_DUMP) which allows - * dumping of all sbp2 related packets sent and received. Especially effective - * when phys dma is disabled on ohci controller (e.g. insmod ohci1394 phys_dma=0). - * * Added new sbp2 module load option (exclusive_login) for allowing - * non-exclusive login to sbp2 device, for special multi-host applications. - * 04/23/02 - Fix for Sony CD-ROM drives. Only send fetch agent reset to sbp2 device if it - * returns the dead bit in status. Thanks to Chandan (chandan@toad.net) for this one. - * 04/27/02 - Fix sbp2 login problem on SMP systems, enable real spinlocks by default. (JSG) - * 06/09/02 - Don't force 36-byte SCSI inquiry, but leave in a define for badly behaved devices. (JSG) - * 02/04/03 - Fixed a SMP deadlock (don't hold sbp2_command_lock while calling sbp2scsi_complete_command). - * Also save/restore irq flags in sbp2scsi_complete_command - Sancho Dauskardt - * 02/06/03 - Removed spinlock debugging; use kernel stuff instead (sda) - * 02/10/03 - Adopt to new hot-plug aware SCSI inferface (hch@lst.de) - * - */ - - -/* - * Includes */ #include @@ -295,7 +79,7 @@ #include "sbp2.h" static char version[] __devinitdata = - "$Rev: 919 $ James Goodwin "; + "$Rev: 931 $ Ben Collins "; /* * Module load parameter definitions @@ -829,65 +613,81 @@ if (!hi) return -ENODEV; - return sbp2_start_device(hi, ud); + return sbp2_start_ud(hi, ud); } static int sbp2_remove(struct device *dev) { + struct scsi_id_group *scsi_group; + struct list_head *lh, *next; struct unit_directory *ud; struct scsi_id_instance_data *scsi_id; SBP2_DEBUG(__FUNCTION__); ud = container_of(dev, struct unit_directory, device); - scsi_id = ud->device.driver_data; + scsi_group = ud->device.driver_data; ud->device.driver_data = NULL; - if (scsi_id != NULL) { - sbp2_logout_device(scsi_id); - sbp2_remove_device(scsi_id); + list_for_each_safe (lh, next, &scsi_group->scsi_id_list) { + scsi_id = list_entry(lh, struct scsi_id_instance_data, list); + + if (scsi_id != NULL) { + sbp2_logout_device(scsi_id); + sbp2_remove_device(scsi_id); + } } + kfree(scsi_group); + return 0; } static void sbp2_update(struct unit_directory *ud) { - struct scsi_id_instance_data *scsi_id = ud->device.driver_data; - struct sbp2scsi_host_info *hi = scsi_id->hi; + struct sbp2scsi_host_info *hi; + struct scsi_id_group *scsi_group = ud->device.driver_data; + struct list_head *lh, *next; + struct scsi_id_instance_data *scsi_id; unsigned long flags; SBP2_DEBUG("sbp2_update"); - if (sbp2_reconnect_device(scsi_id)) { + list_for_each_safe (lh, next, &scsi_group->scsi_id_list) { + scsi_id = list_entry(lh, struct scsi_id_instance_data, list); + + hi = scsi_id->hi; + + if (sbp2_reconnect_device(scsi_id)) { - /* - * Ok, reconnect has failed. Perhaps we didn't - * reconnect fast enough. Try doing a regular login. - */ - if (sbp2_login_device(scsi_id)) { - /* Login failed too, just remove the device. */ - SBP2_ERR("sbp2_reconnect_device failed!"); - sbp2_remove_device(scsi_id); - return; + /* + * Ok, reconnect has failed. Perhaps we didn't + * reconnect fast enough. Try doing a regular login. + */ + if (sbp2_login_device(scsi_id)) { + /* Login failed too, just remove the device. */ + SBP2_ERR("sbp2_reconnect_device failed!"); + sbp2_remove_device(scsi_id); + return; + } } - } - /* Set max retries to something large on the device. */ - sbp2_set_busy_timeout(scsi_id); + /* Set max retries to something large on the device. */ + sbp2_set_busy_timeout(scsi_id); - /* Do a SBP-2 fetch agent reset. */ - sbp2_agent_reset(scsi_id, 1); + /* Do a SBP-2 fetch agent reset. */ + sbp2_agent_reset(scsi_id, 1); - /* Get the max speed and packet size that we can use. */ - sbp2_max_speed_and_size(scsi_id); + /* Get the max speed and packet size that we can use. */ + sbp2_max_speed_and_size(scsi_id); - /* Complete any pending commands with busy (so they get - * retried) and remove them from our queue - */ - spin_lock_irqsave(&hi->sbp2_command_lock, flags); - sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); - spin_unlock_irqrestore(&hi->sbp2_command_lock, flags); + /* Complete any pending commands with busy (so they get + * retried) and remove them from our queue + */ + spin_lock_irqsave(&hi->sbp2_command_lock, flags); + sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); + spin_unlock_irqrestore(&hi->sbp2_command_lock, flags); + } } /* This functions is called by the sbp2_probe, for each new device. If the @@ -955,31 +755,60 @@ } } +static int sbp2_start_ud(struct sbp2scsi_host_info *hi, struct unit_directory *ud) +{ + struct scsi_id_instance_data *scsi_id; + struct scsi_id_group *scsi_group; + struct list_head *lh, *next; + + SBP2_DEBUG("sbp2_start_ud"); + + scsi_group = kmalloc(sizeof(*scsi_group), GFP_KERNEL); + if (!scsi_group) { + SBP2_ERR ("Could not allocate memory for scsi_group"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&scsi_group->scsi_id_list); + ud->device.driver_data = scsi_group; + sbp2_parse_unit_directory(scsi_group, ud); + + list_for_each_safe (lh, next, &scsi_group->scsi_id_list) { + scsi_id = list_entry(lh, struct scsi_id_instance_data, list); + + scsi_id->ne = ud->ne; + scsi_id->hi = hi; + scsi_id->speed_code = SPEED_100; + scsi_id->max_payload_size = hpsb_speedto_maxrec[SPEED_100]; + atomic_set(&scsi_id->sbp2_login_complete, 0); + INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse); + INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed); + scsi_id->sbp2_command_orb_lock = SPIN_LOCK_UNLOCKED; + + sbp2_start_device(scsi_id); + } + + /* Check to see if any of our devices survived the ordeal */ + if (list_empty(&scsi_group->scsi_id_list)) { + kfree(scsi_group); + return -ENODEV; + } + + return 0; +} + + /* * This function is where we first pull the node unique ids, and then * allocate memory and register a SBP-2 device. */ -static int sbp2_start_device(struct sbp2scsi_host_info *hi, struct unit_directory *ud) +static int sbp2_start_device(struct scsi_id_instance_data *scsi_id) { - struct scsi_id_instance_data *scsi_id = NULL; + struct sbp2scsi_host_info *hi = scsi_id->hi; struct scsi_device *sdev; - struct node_entry *ne; int i; SBP2_DEBUG("sbp2_start_device"); - ne = ud->ne; - - /* - * This really is a "new" device plugged in. Let's allocate memory - * for our scsi id instance data. - */ - scsi_id = (struct scsi_id_instance_data *)kmalloc(sizeof(struct scsi_id_instance_data), - GFP_KERNEL); - if (!scsi_id) - goto alloc_fail_first; - memset(scsi_id, 0, sizeof(struct scsi_id_instance_data)); - - scsi_id->hi = hi; /* Login FIFO DMA */ scsi_id->login_response = @@ -1068,7 +897,9 @@ } kfree(scsi_id); -alloc_fail_first: + + list_del(&scsi_id->list); + SBP2_ERR ("Could not allocate memory for scsi_id"); return -ENOMEM; @@ -1076,31 +907,6 @@ SBP2_DMA_ALLOC("consistent DMA region for login ORB"); /* - * Initialize some of the fields in this structure - */ - scsi_id->ne = ne; - scsi_id->ud = ud; - scsi_id->speed_code = SPEED_100; - scsi_id->max_payload_size = hpsb_speedto_maxrec[SPEED_100]; - ud->device.driver_data = scsi_id; - - atomic_set(&scsi_id->sbp2_login_complete, 0); - - /* - * Initialize structures needed for the command orb pool. - */ - INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse); - INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed); - scsi_id->sbp2_command_orb_lock = SPIN_LOCK_UNLOCKED; - - /* - * Make sure that we've gotten ahold of the sbp2 management agent - * address. Also figure out the command set being used (SCSI or - * RBC). - */ - sbp2_parse_unit_directory(scsi_id); - - /* * Find an empty spot to stick our scsi id instance data. */ for (i = 0; i < hi->scsi_host->max_id; i++) { @@ -1235,6 +1041,8 @@ SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->id); + list_del(&scsi_id->list); + kfree(scsi_id); } @@ -1311,6 +1119,8 @@ scsi_id->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1); if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) { scsi_id->query_logins_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun); + SBP2_DEBUG("sbp2_query_logins: set lun to %d", + ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun)); } SBP2_DEBUG("sbp2_query_logins: lun_misc initialized"); @@ -1423,6 +1233,8 @@ /* Set the lun if we were able to pull it from the device's unit directory */ if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) { scsi_id->login_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun); + SBP2_DEBUG("sbp2_query_logins: set lun to %d", + ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun)); } SBP2_DEBUG("sbp2_login_device: lun_misc initialized"); @@ -1463,7 +1275,8 @@ atomic_set(&scsi_id->sbp2_login_complete, 0); - SBP2_DEBUG("sbp2_login_device: prepared to write"); + SBP2_DEBUG("sbp2_login_device: prepared to write to %08x", + (unsigned int)scsi_id->sbp2_management_agent_addr); hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); SBP2_DEBUG("sbp2_login_device: written"); @@ -1694,50 +1507,50 @@ * directory. Used to determine things like sbp2 management agent offset, * and command set used (SCSI or RBC). */ -static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id) +static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group, + struct unit_directory *ud) { - struct unit_directory *ud; + struct scsi_id_instance_data *scsi_id; + struct list_head *lh; + u64 management_agent_addr; + u32 command_set_spec_id, command_set, unit_characteristics, + firmware_revision, workarounds; int i; SBP2_DEBUG("sbp2_parse_unit_directory"); - /* Initialize some fields, in case an entry does not exist */ - scsi_id->sbp2_device_type_and_lun = SBP2_DEVICE_TYPE_LUN_UNINITIALIZED; - scsi_id->sbp2_management_agent_addr = 0x0; - scsi_id->sbp2_command_set_spec_id = 0x0; - scsi_id->sbp2_command_set = 0x0; - scsi_id->sbp2_unit_characteristics = 0x0; - scsi_id->sbp2_firmware_revision = 0x0; - - ud = scsi_id->ud; + management_agent_addr = 0x0; + command_set_spec_id = 0x0; + command_set = 0x0; + unit_characteristics = 0x0; + firmware_revision = 0x0; /* Handle different fields in the unit directory, based on keys */ for (i = 0; i < ud->length; i++) { switch (CONFIG_ROM_KEY(ud->quadlets[i])) { case SBP2_CSR_OFFSET_KEY: /* Save off the management agent address */ - scsi_id->sbp2_management_agent_addr = + management_agent_addr = CSR_REGISTER_BASE + (CONFIG_ROM_VALUE(ud->quadlets[i]) << 2); SBP2_DEBUG("sbp2_management_agent_addr = %x", - (unsigned int) scsi_id->sbp2_management_agent_addr); + (unsigned int) management_agent_addr); break; case SBP2_COMMAND_SET_SPEC_ID_KEY: /* Command spec organization */ - scsi_id->sbp2_command_set_spec_id + command_set_spec_id = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_command_set_spec_id = %x", - (unsigned int) scsi_id->sbp2_command_set_spec_id); + (unsigned int) command_set_spec_id); break; case SBP2_COMMAND_SET_KEY: /* Command set used by sbp2 device */ - scsi_id->sbp2_command_set - = CONFIG_ROM_VALUE(ud->quadlets[i]); + command_set = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_command_set = %x", - (unsigned int) scsi_id->sbp2_command_set); + (unsigned int) command_set); break; case SBP2_UNIT_CHARACTERISTICS_KEY: @@ -1745,10 +1558,10 @@ * Unit characterisitcs (orb related stuff * that I'm not yet paying attention to) */ - scsi_id->sbp2_unit_characteristics + unit_characteristics = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_unit_characteristics = %x", - (unsigned int) scsi_id->sbp2_unit_characteristics); + (unsigned int) unit_characteristics); break; case SBP2_DEVICE_TYPE_AND_LUN_KEY: @@ -1756,21 +1569,29 @@ * Device type and lun (used for * detemining type of sbp2 device) */ + scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL); + if (!scsi_id) { + SBP2_ERR("Out of memory adding scsi_id, not all LUN's will be added"); + break; + } + memset(scsi_id, 0, sizeof(*scsi_id)); + scsi_id->sbp2_device_type_and_lun = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_device_type_and_lun = %x", (unsigned int) scsi_id->sbp2_device_type_and_lun); + list_add_tail(&scsi_id->list, &scsi_group->scsi_id_list); break; case SBP2_FIRMWARE_REVISION_KEY: /* Firmware revision */ - scsi_id->sbp2_firmware_revision + firmware_revision = CONFIG_ROM_VALUE(ud->quadlets[i]); if (force_inquiry_hack) SBP2_INFO("sbp2_firmware_revision = %x", - (unsigned int) scsi_id->sbp2_firmware_revision); + (unsigned int) firmware_revision); else SBP2_DEBUG("sbp2_firmware_revision = %x", - (unsigned int) scsi_id->sbp2_firmware_revision); + (unsigned int) firmware_revision); break; default: @@ -1780,7 +1601,7 @@ /* This is the start of our broken device checking. We try to hack * around oddities and known defects. */ - scsi_id->workarounds = 0x0; + workarounds = 0x0; /* If the vendor id is 0xa0b8 (Symbios vendor id), then we have a * bridge with 128KB max transfer size limitation. For sanity, we @@ -1791,28 +1612,54 @@ * host gets initialized. That way we can down-force the * max_sectors to account for it. That is not currently * possible. */ - if ((scsi_id->sbp2_firmware_revision & 0xffff00) == + if ((firmware_revision & 0xffff00) == SBP2_128KB_BROKEN_FIRMWARE && (max_sectors * 512) > (128*1024)) { SBP2_WARN("Node " NODE_BUS_FMT ": Bridge only supports 128KB max transfer size.", - NODE_BUS_ARGS(scsi_id->ne->nodeid)); + NODE_BUS_ARGS(ud->ne->nodeid)); SBP2_WARN("WARNING: Current max_sectors setting is larger than 128KB (%d sectors)!", max_sectors); - scsi_id->workarounds |= SBP2_BREAKAGE_128K_MAX_TRANSFER; + workarounds |= SBP2_BREAKAGE_128K_MAX_TRANSFER; } /* Check for a blacklisted set of devices that require us to force * a 36 byte host inquiry. This can be overriden as a module param * (to force all hosts). */ for (i = 0; i < NUM_BROKEN_INQUIRY_DEVS; i++) { - if ((scsi_id->sbp2_firmware_revision & 0xffff00) == + if ((firmware_revision & 0xffff00) == sbp2_broken_inquiry_list[i]) { SBP2_WARN("Node " NODE_BUS_FMT ": Using 36byte inquiry workaround", - NODE_BUS_ARGS(scsi_id->ne->nodeid)); - scsi_id->workarounds |= SBP2_BREAKAGE_INQUIRY_HACK; + NODE_BUS_ARGS(ud->ne->nodeid)); + workarounds |= SBP2_BREAKAGE_INQUIRY_HACK; break; /* No need to continue. */ } } + + /* If our list is empty, add a base scsi_id (happens in a normal + * case where there is no logical_unit_number entry */ + if (list_empty(&scsi_group->scsi_id_list)) { + scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL); + if (!scsi_id) { + SBP2_ERR("Out of memory adding scsi_id"); + return; + } + memset(scsi_id, 0, sizeof(*scsi_id)); + + scsi_id->sbp2_device_type_and_lun = SBP2_DEVICE_TYPE_LUN_UNINITIALIZED; + list_add_tail(&scsi_id->list, &scsi_group->scsi_id_list); + } + + /* Update the generic fields in all the LUN's */ + list_for_each (lh, &scsi_group->scsi_id_list) { + scsi_id = list_entry(lh, struct scsi_id_instance_data, list); + + scsi_id->sbp2_management_agent_addr = management_agent_addr; + scsi_id->sbp2_command_set_spec_id = command_set_spec_id; + scsi_id->sbp2_command_set = command_set; + scsi_id->sbp2_unit_characteristics = unit_characteristics; + scsi_id->sbp2_firmware_revision = firmware_revision; + scsi_id->workarounds = workarounds; + } } /* @@ -3065,7 +2912,7 @@ return (length); } -MODULE_AUTHOR("James Goodwin "); +MODULE_AUTHOR("Ben Collins "); MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/ieee1394/sbp2.h linux-2.5.69-bk14/drivers/ieee1394/sbp2.h --- linux-2.5.69-bk13/drivers/ieee1394/sbp2.h 2003-05-04 16:53:03.000000000 -0700 +++ linux-2.5.69-bk14/drivers/ieee1394/sbp2.h 2003-05-20 04:46:43.000000000 -0700 @@ -384,7 +384,6 @@ /* * Values pulled from the device's unit directory */ - struct unit_directory *ud; u32 sbp2_command_set_spec_id; u32 sbp2_command_set; u32 sbp2_unit_characteristics; @@ -403,6 +402,8 @@ struct list_head sbp2_command_orb_inuse; struct list_head sbp2_command_orb_completed; + struct list_head list; + /* Node entry, as retrieved from NodeMgr entries */ struct node_entry *ne; @@ -413,6 +414,13 @@ u32 workarounds; }; + +/* Describes a per-ud scsi_id group */ +struct scsi_id_group { + struct list_head scsi_id_list; +}; + + /* * Sbp2 host data structure (one per sbp2 host) */ @@ -464,8 +472,9 @@ static int sbp2_remove(struct device *dev); static void sbp2_update(struct unit_directory *ud); -static int sbp2_start_device(struct sbp2scsi_host_info *hi, - struct unit_directory *ud); +static int sbp2_start_ud(struct sbp2scsi_host_info *hi, + struct unit_directory *ud); +static int sbp2_start_device(struct scsi_id_instance_data *scsi_id); static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id); #ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA @@ -499,7 +508,8 @@ static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense_data); static void sbp2_check_sbp2_command(struct scsi_id_instance_data *scsi_id, unchar *cmd); static void sbp2_check_sbp2_response(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *SCpnt); -static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id); +static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group, + struct unit_directory *ud); static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id); static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id); diff -urN linux-2.5.69-bk13/drivers/input/serio/sa1111ps2.c linux-2.5.69-bk14/drivers/input/serio/sa1111ps2.c --- linux-2.5.69-bk13/drivers/input/serio/sa1111ps2.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/input/serio/sa1111ps2.c 2003-05-20 04:46:43.000000000 -0700 @@ -347,7 +347,6 @@ .drv = { .name = "sa1111-ps2", .bus = &sa1111_bus_type, - .devclass = &input_devclass, .probe = ps2_probe, .remove = ps2_remove, .suspend = ps2_suspend, diff -urN linux-2.5.69-bk13/drivers/isdn/hardware/eicon/maintidi.c linux-2.5.69-bk14/drivers/isdn/hardware/eicon/maintidi.c --- linux-2.5.69-bk13/drivers/isdn/hardware/eicon/maintidi.c 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/drivers/isdn/hardware/eicon/maintidi.c 2003-05-20 04:46:43.000000000 -0700 @@ -116,7 +116,7 @@ } pmem += sizeof(*pLib); - memset (pLib, 0x00, sizeof(pLib)); + memset(pLib, 0x00, sizeof(*pLib)); pLib->Adapter = Adapter; diff -urN linux-2.5.69-bk13/drivers/md/dm-ioctl.c linux-2.5.69-bk14/drivers/md/dm-ioctl.c --- linux-2.5.69-bk13/drivers/md/dm-ioctl.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/md/dm-ioctl.c 2003-05-20 04:46:43.000000000 -0700 @@ -1115,7 +1115,6 @@ DM_DRIVER_EMAIL); return 0; - failed: if (misc_deregister(&_dm_misc) < 0) DMERR("misc_deregister failed for control device"); dm_hash_exit(); diff -urN linux-2.5.69-bk13/drivers/media/video/msp3400.c linux-2.5.69-bk14/drivers/media/video/msp3400.c --- linux-2.5.69-bk13/drivers/media/video/msp3400.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/msp3400.c 2003-05-20 04:46:43.000000000 -0700 @@ -45,13 +45,11 @@ #include #include #include -#include #include - -#ifdef CONFIG_SMP -#include #include -#endif +#include +#include + /* kernel_thread */ #define __KERNEL_SYSCALLS__ #include @@ -59,18 +57,13 @@ #include #include "msp3400.h" -/* Addresses to scan */ -static unsigned short normal_i2c[] = {I2C_CLIENT_END}; -static unsigned short normal_i2c_range[] = {0x40,0x40,I2C_CLIENT_END}; -I2C_CLIENT_INSMOD; - /* insmod parameters */ -static int debug = 0; /* debug output */ -static int once = 0; /* no continous stereo monitoring */ -static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), +static int debug = 0; /* debug output */ +static int once = 0; /* no continous stereo monitoring */ +static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), the autoscan seems work well only with FM... */ -static int simple = -1; /* use short programming (>= msp3410 only) */ -static int dolby = 0; +static int simple = -1; /* use short programming (>= msp3410 only) */ +static int dolby = 0; #define DFP_COUNT 0x41 static const int bl_dfp[] = { @@ -79,8 +72,9 @@ }; struct msp3400c { + int rev1,rev2; + int simple; - int nicam; int mode; int norm; int stereo; @@ -107,6 +101,10 @@ struct timer_list wake_stereo; }; +#define HAVE_NICAM(msp) (((msp->rev2>>8) & 0xff) != 00) +#define HAVE_SIMPLE(msp) ((msp->rev1 & 0xff) >= 'D'-'@') +#define HAVE_RADIO(msp) ((msp->rev1 & 0xff) >= 'G'-'@') + #define MSP3400_MAX 4 static struct i2c_client *msps[MSP3400_MAX]; @@ -124,14 +122,25 @@ MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); /* FreeBSD uses this too */ /* ---------------------------------------------------------------------- */ #define I2C_MSP3400C 0x80 +#define I2C_MSP3400C_ALT 0x88 + #define I2C_MSP3400C_DEM 0x10 #define I2C_MSP3400C_DFP 0x12 +/* Addresses to scan */ +static unsigned short normal_i2c[] = { + I2C_MSP3400C >> 1, + I2C_MSP3400C_ALT >> 1, + I2C_CLIENT_END +}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END,I2C_CLIENT_END}; +I2C_CLIENT_INSMOD; + /* ----------------------------------------------------------------------- */ /* functions for talking to the MSP3400C Sound processor */ @@ -354,7 +363,8 @@ if (-1 == scarts[out][in]) return; - dprintk("msp34xx: scart switch: %s => %d\n",scart_names[in],out); + dprintk(KERN_DEBUG + "msp34xx: scart switch: %s => %d\n",scart_names[in],out); msp->acb &= ~scarts[out][SCART_MASK]; msp->acb |= scarts[out][in]; msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, msp->acb); @@ -384,7 +394,8 @@ balance = ((right-left) * 127) / vol; } - dprintk("msp34xx: setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n", + dprintk(KERN_DEBUG + "msp34xx: setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n", muted ? "on" : "off", left, right, val>>8, balance); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ @@ -397,7 +408,7 @@ { int val = ((bass-32768) * 0x60 / 65535) << 8; - dprintk("msp34xx: setbass: %d 0x%02x\n",bass, val>>8); + dprintk(KERN_DEBUG "msp34xx: setbass: %d 0x%02x\n",bass, val>>8); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ } @@ -405,7 +416,7 @@ { int val = ((treble-32768) * 0x60 / 65535) << 8; - dprintk("msp34xx: settreble: %d 0x%02x\n",treble, val>>8); + dprintk(KERN_DEBUG "msp34xx: settreble: %d 0x%02x\n",treble, val>>8); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ } @@ -414,7 +425,7 @@ struct msp3400c *msp = i2c_get_clientdata(client); int i; - dprintk("msp3400: setmode: %d\n",type); + dprintk(KERN_DEBUG "msp3400: setmode: %d\n",type); msp->mode = type; msp->stereo = VIDEO_SOUND_MONO; @@ -460,7 +471,7 @@ msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, msp_init_data[type].dfp_matrix); - if (msp->nicam) { + if (HAVE_NICAM(msp)) { /* nicam prescale */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ } @@ -469,8 +480,15 @@ /* turn on/off nicam + stereo */ static void msp3400c_setstereo(struct i2c_client *client, int mode) { - static char *strmode[] = { "0", "mono", "stereo", "3", - "lang1", "5", "6", "7", "lang2" }; + static char *strmode[16] = { +#if __GNUC__ >= 3 + [ 0 ... 15 ] = "invalid", +#endif + [ VIDEO_SOUND_MONO ] = "mono", + [ VIDEO_SOUND_STEREO ] = "stereo", + [ VIDEO_SOUND_LANG1 ] = "lang1", + [ VIDEO_SOUND_LANG2 ] = "lang2", + }; struct msp3400c *msp = i2c_get_clientdata(client); int nicam=0; /* channel source: FM/AM or nicam */ int src=0; @@ -478,7 +496,7 @@ /* switch demodulator */ switch (msp->mode) { case MSP_MODE_FM_TERRA: - dprintk("msp3400: FM setstereo: %s\n",strmode[mode]); + dprintk(KERN_DEBUG "msp3400: FM setstereo: %s\n",strmode[mode]); msp3400c_setcarrier(client,msp->second,msp->main); switch (mode) { case VIDEO_SOUND_STEREO: @@ -492,7 +510,7 @@ } break; case MSP_MODE_FM_SAT: - dprintk("msp3400: SAT setstereo: %s\n",strmode[mode]); + dprintk(KERN_DEBUG "msp3400: SAT setstereo: %s\n",strmode[mode]); switch (mode) { case VIDEO_SOUND_MONO: msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); @@ -511,24 +529,24 @@ case MSP_MODE_FM_NICAM1: case MSP_MODE_FM_NICAM2: case MSP_MODE_AM_NICAM: - dprintk("msp3400: NICAM setstereo: %s\n",strmode[mode]); + dprintk(KERN_DEBUG "msp3400: NICAM setstereo: %s\n",strmode[mode]); msp3400c_setcarrier(client,msp->second,msp->main); if (msp->nicam_on) nicam=0x0100; break; case MSP_MODE_BTSC: - dprintk("msp3400: BTSC setstereo: %s\n",strmode[mode]); + dprintk(KERN_DEBUG "msp3400: BTSC setstereo: %s\n",strmode[mode]); nicam=0x0300; break; case MSP_MODE_EXTERN: - dprintk("msp3400: extern setstereo: %s\n",strmode[mode]); + dprintk(KERN_DEBUG "msp3400: extern setstereo: %s\n",strmode[mode]); nicam = 0x0200; break; case MSP_MODE_FM_RADIO: - dprintk("msp3400: FM-Radio setstereo: %s\n",strmode[mode]); + dprintk(KERN_DEBUG "msp3400: FM-Radio setstereo: %s\n",strmode[mode]); break; default: - dprintk("msp3400: mono setstereo\n"); + dprintk(KERN_DEBUG "msp3400: mono setstereo\n"); return; } @@ -557,7 +575,8 @@ src = 0x0010 | nicam; break; } - dprintk("msp3400: setstereo final source/matrix = 0x%x\n", src); + dprintk(KERN_DEBUG + "msp3400: setstereo final source/matrix = 0x%x\n", src); if (dolby) { msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); @@ -576,22 +595,22 @@ msp3400c_print_mode(struct msp3400c *msp) { if (msp->main == msp->second) { - printk("msp3400: mono sound carrier: %d.%03d MHz\n", + printk(KERN_DEBUG "msp3400: mono sound carrier: %d.%03d MHz\n", msp->main/910000,(msp->main/910)%1000); } else { - printk("msp3400: main sound carrier: %d.%03d MHz\n", + printk(KERN_DEBUG "msp3400: main sound carrier: %d.%03d MHz\n", msp->main/910000,(msp->main/910)%1000); } if (msp->mode == MSP_MODE_FM_NICAM1 || msp->mode == MSP_MODE_FM_NICAM2) - printk("msp3400: NICAM/FM carrier : %d.%03d MHz\n", + printk(KERN_DEBUG "msp3400: NICAM/FM carrier : %d.%03d MHz\n", msp->second/910000,(msp->second/910)%1000); if (msp->mode == MSP_MODE_AM_NICAM) - printk("msp3400: NICAM/AM carrier : %d.%03d MHz\n", + printk(KERN_DEBUG "msp3400: NICAM/AM carrier : %d.%03d MHz\n", msp->second/910000,(msp->second/910)%1000); if (msp->mode == MSP_MODE_FM_TERRA && msp->main != msp->second) { - printk("msp3400: FM-stereo carrier : %d.%03d MHz\n", + printk(KERN_DEBUG "msp3400: FM-stereo carrier : %d.%03d MHz\n", msp->second/910000,(msp->second/910)%1000); } } @@ -638,8 +657,8 @@ val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); if (val > 32767) val -= 65536; - dprintk("msp34xx: stereo detect register: %d\n",val); - + dprintk(KERN_DEBUG + "msp34xx: stereo detect register: %d\n",val); if (val > 4096) { newstereo = VIDEO_SOUND_STEREO | VIDEO_SOUND_MONO; } else if (val < -4096) { @@ -653,7 +672,9 @@ case MSP_MODE_FM_NICAM2: case MSP_MODE_AM_NICAM: val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); - dprintk("msp34xx: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); + dprintk(KERN_DEBUG + "msp34xx: nicam sync=%d, mode=%d\n", + val & 1, (val & 0x1e) >> 1); if (val & 1) { /* nicam synced */ @@ -685,7 +706,8 @@ break; case MSP_MODE_BTSC: val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); - dprintk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", + dprintk(KERN_DEBUG + "msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", val, (val & 0x0002) ? "no" : "yes", (val & 0x0004) ? "no" : "yes", @@ -699,13 +721,13 @@ } if (newstereo != msp->stereo) { update = 1; - dprintk("msp34xx: watch: stereo %d => %d\n", + dprintk(KERN_DEBUG "msp34xx: watch: stereo %d => %d\n", msp->stereo,newstereo); msp->stereo = newstereo; } if (newnicam != msp->nicam_on) { update = 1; - dprintk("msp34xx: watch: nicam %d => %d\n", + dprintk(KERN_DEBUG "msp34xx: watch: nicam %d => %d\n", msp->nicam_on,newnicam); msp->nicam_on = newnicam; } @@ -734,6 +756,8 @@ msp3400c_setstereo(client,VIDEO_SOUND_STEREO); else if (msp->stereo & VIDEO_SOUND_LANG1) msp3400c_setstereo(client,VIDEO_SOUND_LANG1); + else if (msp->stereo & VIDEO_SOUND_LANG2) + msp3400c_setstereo(client,VIDEO_SOUND_LANG2); else msp3400c_setstereo(client,VIDEO_SOUND_MONO); } @@ -751,17 +775,10 @@ struct CARRIER_DETECT *cd; int count, max1,max2,val1,val2, val,this; -#ifdef CONFIG_SMP lock_kernel(); -#endif - daemonize("msp3400"); - msp->thread = current; - -#ifdef CONFIG_SMP unlock_kernel(); -#endif printk("msp3400: daemon started\n"); if(msp->notify != NULL) @@ -778,10 +795,6 @@ if (msp->rmmod || signal_pending(current)) goto done; - if (VIDEO_MODE_RADIO == msp->norm || - MSP_MODE_EXTERN == msp->mode) - continue; /* nothing to do */ - msp->active = 1; if (msp->watch_stereo) { @@ -798,8 +811,13 @@ restart: if (VIDEO_MODE_RADIO == msp->norm || - MSP_MODE_EXTERN == msp->mode) - continue; /* nothing to do */ + MSP_MODE_EXTERN == msp->mode) { + /* no carrier scan, just unmute */ + printk("msp3400: thread: no carrier scan\n"); + msp3400c_setvolume(client, msp->muted, + msp->left, msp->right); + continue; + } msp->restart = 0; msp3400c_setvolume(client, msp->muted, 0, 0); msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); @@ -884,7 +902,7 @@ msp->nicam_on = 0; msp3400c_setstereo(client, VIDEO_SOUND_MONO); msp->watch_stereo = 1; - } else if (max2 == 1 && msp->nicam) { + } else if (max2 == 1 && HAVE_NICAM(msp)) { /* B/G NICAM */ msp->second = carrier_detect_55[max2].cdo; msp3400c_setmode(client, MSP_MODE_FM_NICAM1); @@ -922,7 +940,7 @@ /* volume prescale for SCART (AM mono input) */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900); msp->watch_stereo = 1; - } else if (max2 == 0 && msp->nicam) { + } else if (max2 == 0 && HAVE_NICAM(msp)) { /* D/K NICAM */ msp->second = carrier_detect_65[max2].cdo; msp3400c_setmode(client, MSP_MODE_FM_NICAM1); @@ -959,7 +977,7 @@ } done: - dprintk("msp3400: thread: exit\n"); + dprintk(KERN_DEBUG "msp3400: thread: exit\n"); msp->active = 0; msp->thread = NULL; @@ -1005,17 +1023,10 @@ struct msp3400c *msp = i2c_get_clientdata(client); int mode,val,i,std; -#ifdef CONFIG_SMP lock_kernel(); -#endif - daemonize("msp3410 [auto]"); - msp->thread = current; - -#ifdef CONFIG_SMP unlock_kernel(); -#endif printk("msp3410: daemon started\n"); if(msp->notify != NULL) @@ -1025,16 +1036,13 @@ if (msp->rmmod) goto done; if (debug > 1) - printk("msp3410: thread: sleep\n"); + printk(KERN_DEBUG "msp3410: thread: sleep\n"); interruptible_sleep_on(&msp->wq); if (debug > 1) - printk("msp3410: thread: wakeup\n"); + printk(KERN_DEBUG "msp3410: thread: wakeup\n"); if (msp->rmmod || signal_pending(current)) goto done; - if (msp->mode == MSP_MODE_EXTERN) - continue; - msp->active = 1; if (msp->watch_stereo) { @@ -1050,8 +1058,13 @@ goto done; restart: - if (msp->mode == MSP_MODE_EXTERN) + if (msp->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + dprintk(KERN_DEBUG "msp3410: thread: no carrier scan\n"); + msp3400c_setvolume(client, msp->muted, + msp->left, msp->right); continue; + } msp->restart = 0; del_timer(&msp->wake_stereo); msp->watch_stereo = 0; @@ -1073,7 +1086,7 @@ mode = 0x0003; std = 1; break; - case VIDEO_MODE_RADIO: + case VIDEO_MODE_RADIO: mode = 0x0003; std = 0x0040; break; @@ -1090,7 +1103,7 @@ for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == std) break; - printk("msp3410: setting mode: %s (0x%04x)\n", + printk(KERN_DEBUG "msp3410: setting mode: %s (0x%04x)\n", modelist[i].name ? modelist[i].name : "unknown",std); } @@ -1111,13 +1124,13 @@ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); if (val < 0x07ff) break; - dprintk("msp3410: detection still in progress\n"); + dprintk(KERN_DEBUG "msp3410: detection still in progress\n"); } } for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == val) break; - dprintk("msp3410: current mode: %s (0x%04x)\n", + dprintk(KERN_DEBUG "msp3410: current mode: %s (0x%04x)\n", modelist[i].name ? modelist[i].name : "unknown", val); msp->main = modelist[i].main; @@ -1125,7 +1138,8 @@ if (amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { /* autodetection has failed, let backup */ - dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n", + dprintk(KERN_DEBUG "msp3410: autodetection failed," + " switching to backup mode: %s (0x%04x)\n", modelist[8].name ? modelist[8].name : "unknown",val); val = 0x0009; msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); @@ -1170,11 +1184,24 @@ msp->stereo = VIDEO_SOUND_STEREO; msp->nicam_on = 0; msp->watch_stereo = 0; + /* not needed in theory if HAVE_RADIO(), but + short programming enables carrier mute */ + msp3400c_setmode(client,MSP_MODE_FM_RADIO); + msp3400c_setcarrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); /* scart routing */ msp3400c_set_scart(client,SCART_IN2,0); +#if 0 + /* radio from SCART_IN2 */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220); +#else + /* msp34xx does radio decoding */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0020); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0020); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0020); +#endif break; case 0x0003: msp->mode = MSP_MODE_FM_TERRA; @@ -1197,7 +1224,7 @@ } done: - dprintk("msp3410: thread: exit\n"); + dprintk(KERN_DEBUG "msp3410: thread: exit\n"); msp->active = 0; msp->thread = NULL; @@ -1225,11 +1252,9 @@ static struct i2c_client client_template = { - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, - .dev = { - .name = "(unset)", - }, + I2C_DEVNAME("(unset)"), + .flags = I2C_CLIENT_ALLOW_USE, + .driver = &driver, }; static int msp_attach(struct i2c_adapter *adap, int addr, int kind) @@ -1237,7 +1262,7 @@ DECLARE_MUTEX_LOCKED(sem); struct msp3400c *msp; struct i2c_client *c; - int rev1,rev2,i; + int i; client_template.adapter = adap; client_template.addr = addr; @@ -1275,10 +1300,10 @@ return -1; } - rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e); - if (-1 != rev1) - rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f); - if ((-1 == rev1) || (0 == rev1 && 0 == rev2)) { + msp->rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e); + if (-1 != msp->rev1) + msp->rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f); + if ((-1 == msp->rev1) || (0 == msp->rev1 && 0 == msp->rev2)) { kfree(msp); kfree(c); printk("msp3400: error while reading chip version\n"); @@ -1292,13 +1317,12 @@ msp3400c_setvolume(c,msp->muted,msp->left,msp->right); snprintf(c->dev.name, DEVICE_NAME_SIZE, "MSP34%02d%c-%c%d", - (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); - msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; + (msp->rev2>>8)&0xff, (msp->rev1&0xff)+'@', + ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f); if (simple == -1) { /* default mode */ - /* msp->simple = (((rev2>>8)&0xff) == 0) ? 0 : 1; */ - msp->simple = ((rev1&0xff)+'@' > 'C'); + msp->simple = HAVE_SIMPLE(msp); } else { /* use insmod option */ msp->simple = simple; @@ -1310,13 +1334,16 @@ msp->wake_stereo.data = (unsigned long)msp; /* hello world :-) */ - printk(KERN_INFO "msp34xx: init: chip=%s",c->dev.name); - if (msp->nicam) - printk(", has NICAM support"); + printk(KERN_INFO "msp34xx: init: chip=%s",i2c_clientname(c)); + if (HAVE_NICAM(msp)) + printk(" +nicam"); + if (HAVE_SIMPLE(msp)) + printk(" +simple"); + if (HAVE_RADIO(msp)) + printk(" +radio"); printk("\n"); /* startup control thread */ - MOD_INC_USE_COUNT; msp->notify = &sem; kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread, (void *)c, 0); @@ -1340,7 +1367,7 @@ static int msp_detach(struct i2c_client *client) { DECLARE_MUTEX_LOCKED(sem); - struct msp3400c *msp = i2c_get_clientdata(client); + struct msp3400c *msp = i2c_get_clientdata(client); int i; /* shutdown control thread */ @@ -1366,7 +1393,6 @@ i2c_detach_client(client); kfree(msp); kfree(client); - MOD_DEC_USE_COUNT; return 0; } @@ -1379,7 +1405,7 @@ static void msp_wake_thread(struct i2c_client *client) { - struct msp3400c *msp = i2c_get_clientdata(client); + struct msp3400c *msp = i2c_get_clientdata(client); msp3400c_setvolume(client,msp->muted,0,0); msp->watch_stereo=0; @@ -1391,7 +1417,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct msp3400c *msp = i2c_get_clientdata(client); + struct msp3400c *msp = i2c_get_clientdata(client); __u16 *sarg = arg; #if 0 int *iarg = (int*)arg; @@ -1440,7 +1466,7 @@ msp->norm = VIDEO_MODE_RADIO; msp->watch_stereo=0; del_timer(&msp->wake_stereo); - dprintk("msp34xx: switching to radio mode\n"); + dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); if (msp->simple) { /* the thread will do for us */ msp_wake_thread(client); @@ -1459,11 +1485,11 @@ case MSP_SET_DFPREG: { struct msp_dfpreg *r = arg; - int i; + unsigned int i; if (r->reg < 0 || r->reg >= DFP_COUNT) return -EINVAL; - for (i = 0; i < sizeof(bl_dfp)/sizeof(int); i++) + for (i = 0; i < ARRAY_SIZE(bl_dfp); i++) if (r->reg == bl_dfp[i]) return -EINVAL; msp->dfp_regs[r->reg] = r->value; @@ -1527,11 +1553,11 @@ msp3400c_setbass(client,msp->bass); msp3400c_settreble(client,msp->treble); - if (va->mode != 0) { + if (va->mode != 0 && msp->norm != VIDEO_MODE_RADIO) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); - msp->stereo = va->mode; - msp3400c_setstereo(client,va->mode); + msp->stereo = va->mode & 0x0f; + msp3400c_setstereo(client,va->mode & 0x0f); } break; } @@ -1540,7 +1566,6 @@ struct video_channel *vc = arg; dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN\n"); - dprintk("msp34xx: switching to TV mode\n"); msp->norm = vc->norm; break; } diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-cards.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-cards.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-cards.c 2003-05-04 16:53:40.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-cards.c 2003-05-20 04:46:43.000000000 -0700 @@ -29,6 +29,7 @@ static char name_mute[] = "mute"; static char name_radio[] = "Radio"; static char name_tv[] = "Television"; +static char name_tv_mono[] = "TV (mono only)"; static char name_comp1[] = "Composite1"; static char name_comp2[] = "Composite2"; static char name_svideo[] = "S-Video"; @@ -61,6 +62,11 @@ .vmux = 1, .amux = TV, .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, }}, }, [SAA7134_BOARD_FLYVIDEO3000] = { @@ -68,27 +74,39 @@ .name = "LifeView FlyVIDEO3000", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, + .gpiomask = 0xe000, .inputs = {{ .name = name_tv, .vmux = 1, .amux = TV, + .gpio = 0x8000, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, .tv = 1, },{ .name = name_comp1, .vmux = 0, - .amux = LINE1, + .amux = LINE2, + .gpio = 0x4000, },{ .name = name_comp2, .vmux = 3, - .amux = LINE1, + .amux = LINE2, + .gpio = 0x4000, },{ .name = name_svideo, .vmux = 8, - .amux = LINE1, + .amux = LINE2, + .gpio = 0x4000, }}, .radio = { .name = name_radio, .amux = LINE2, + .gpio = 0x2000, }, }, [SAA7134_BOARD_FLYVIDEO2000] = { @@ -96,7 +114,7 @@ .name = "LifeView FlyVIDEO2000", .audio_clock = 0x00200000, .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .gpiomask = 0x6000, + .gpiomask = 0xe000, .inputs = {{ .name = name_tv, .vmux = 1, @@ -122,10 +140,12 @@ .radio = { .name = name_radio, .amux = LINE2, + .gpio = 0x2000, }, .mute = { .name = name_mute, - .amux = LINE1, + .amux = LINE2, + .gpio = 0x8000, }, }, [SAA7134_BOARD_EMPRESS] = { @@ -190,7 +210,7 @@ .tv = 1, },{ /* workaround for problems with normal TV sound */ - .name = "TV (mono only)", + .name = name_tv_mono, .vmux = 1, .amux = LINE2, .tv = 1, @@ -270,6 +290,12 @@ .amux = TV, .tv = 1, },{ + /* workaround for problems with normal TV sound */ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ .name = name_comp1, .vmux = 0, .amux = LINE2, @@ -302,7 +328,7 @@ },{ .name = name_tv, .vmux = 1, - .amux = TV, + .amux = LINE2, .tv = 1, }}, }, @@ -332,7 +358,6 @@ .name = name_radio, .amux = LINE2, }, - }, [SAA7134_BOARD_MD7134] = { .name = "Medion 7134", @@ -343,7 +368,7 @@ .name = name_tv, .vmux = 1, .amux = LINE2, - .tv = 1, + .tv = 1, },{ .name = name_comp1, .vmux = 0, @@ -361,9 +386,67 @@ .name = name_radio, .amux = LINE2, }, - }, + }, + [SAA7134_BOARD_TYPHOON_90031] = { + .name = "Typhoon TV+Radio 90031", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_ELSA] = { + .name = "ELSA EX-VISION 300TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_HITACHI_NTSC, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 4, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_ELSA_500TV] = { + .name = "ELSA EX-VISION 500TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_HITACHI_NTSC, + .inputs = {{ + .name = name_svideo, + .vmux = 7, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 8, + .amux = TV, + .tv = 1, + }}, + }, + }; -const int saa7134_bcount = (sizeof(saa7134_boards)/sizeof(struct saa7134_board)); +const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); /* ------------------------------------------------------------------ */ /* PCI ids + subsystem IDs */ @@ -377,6 +460,12 @@ .driver_data = SAA7134_BOARD_PROTEUS_PRO, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2001, + .driver_data = SAA7134_BOARD_PROTEUS_PRO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = PCI_VENDOR_ID_PHILIPS, .subdevice = 0x6752, @@ -407,11 +496,29 @@ .driver_data = SAA7134_BOARD_FLYVIDEO3000, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x5168, + .subdevice = 0x0138, + .driver_data = SAA7134_BOARD_FLYVIDEO2000, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x16be, .subdevice = 0x0003, .driver_data = SAA7134_BOARD_MD7134, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1048, + .subdevice = 0x226b, + .driver_data = SAA7134_BOARD_ELSA, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1048, + .subdevice = 0x226b, + .driver_data = SAA7134_BOARD_ELSA_500TV, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -435,10 +542,22 @@ .driver_data = SAA7134_BOARD_UNKNOWN, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SAA7134_BOARD_UNKNOWN, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = SAA7134_BOARD_UNKNOWN, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7135, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SAA7134_BOARD_UNKNOWN, },{ /* --- end of list --- */ } @@ -446,6 +565,67 @@ MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl); /* ----------------------------------------------------------- */ +/* flyvideo tweaks */ + +#if 0 +static struct { + char *model; + int tuner_type; +} fly_list[0x20] = { + /* default catch ... */ + [ 0 ... 0x1f ] = { + .model = "UNKNOWN", + .tuner_type = TUNER_ABSENT, + }, + /* ... the ones known so far */ + [ 0x05 ] = { + .model = "PAL-BG", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + }, + [ 0x10 ] = { + .model = "PAL-BG / PAL-DK", + .tuner_type = TUNER_PHILIPS_PAL, + }, + [ 0x15 ] = { + .model = "NTSC", + .tuner_type = TUNER_ABSENT /* FIXME */, + }, +}; +#endif + +static void board_flyvideo(struct saa7134_dev *dev) +{ + u32 value; + int index; + + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0); + value = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); +#if 0 + index = (value & 0x1f00) >> 8; + printk(KERN_INFO "%s: flyvideo: gpio is 0x%x [model=%s,tuner=%d]\n", + dev->name, value, fly_list[index].model, + fly_list[index].tuner_type); + dev->tuner_type = fly_list[index].tuner_type; +#else + printk(KERN_INFO "%s: flyvideo: gpio is 0x%x\n", + dev->name, value); +#endif +} + +/* ----------------------------------------------------------- */ + +int saa7134_board_init(struct saa7134_dev *dev) +{ + switch (dev->board) { + case SAA7134_BOARD_FLYVIDEO2000: + case SAA7134_BOARD_FLYVIDEO3000: + board_flyvideo(dev); + break; + } + return 0; +} + +/* ----------------------------------------------------------- */ /* * Local variables: * c-basic-offset: 8 diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-core.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-core.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-core.c 2003-05-04 16:53:09.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-core.c 2003-05-20 04:46:43.000000000 -0700 @@ -2,7 +2,7 @@ * device driver for philips saa7134 based TV cards * driver core * - * (c) 2001,02 Gerd Knorr [SuSE Labs] + * (c) 2001-03 Gerd Knorr [SuSE Labs] * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -67,6 +67,10 @@ MODULE_PARM(radio_nr,"i"); MODULE_PARM_DESC(radio_nr,"radio device number"); +static unsigned int oss = 0; +MODULE_PARM(oss,"i"); +MODULE_PARM_DESC(oss,"register oss devices (default: no)"); + static unsigned int dsp_nr = -1; MODULE_PARM(dsp_nr,"i"); MODULE_PARM_DESC(dsp_nr,"oss dsp device number"); @@ -75,20 +79,20 @@ MODULE_PARM(mixer_nr,"i"); MODULE_PARM_DESC(mixer_nr,"oss mixer device number"); -static int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = -1}; +static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; MODULE_PARM(tuner,"1-" __stringify(SAA7134_MAXBOARDS) "i"); MODULE_PARM_DESC(tuner,"tuner type"); -static int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = -1}; +static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; MODULE_PARM(card,"1-" __stringify(SAA7134_MAXBOARDS) "i"); MODULE_PARM_DESC(card,"card type"); -static int latency = -1; +static unsigned int latency = UNSET; MODULE_PARM(latency,"i"); MODULE_PARM_DESC(latency,"pci latency timer"); struct list_head saa7134_devlist; -int saa7134_devcount; +unsigned int saa7134_devcount; #define dprintk(fmt, arg...) if (core_debug) \ printk(KERN_DEBUG "%s/core: " fmt, dev->name, ## arg) @@ -102,7 +106,7 @@ "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", "GVBIFMT", "SVBIFMT" }; -#define V4L1_IOCTLS (sizeof(v4l1_ioctls)/sizeof(char*)) +#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) static const char *v4l2_ioctls[] = { "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", @@ -116,7 +120,7 @@ "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", "S_MODULATOR" }; -#define V4L2_IOCTLS (sizeof(v4l2_ioctls)/sizeof(char*)) +#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) static const char *osspcm_ioctls[] = { "RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT", @@ -125,7 +129,7 @@ "GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO", "SETDUPLEX", "GETODELAY" }; -#define OSSPCM_IOCTLS (sizeof(v4l2_ioctls)/sizeof(char*)) +#define OSSPCM_IOCTLS ARRAY_SIZE(v4l2_ioctls) void saa7134_print_ioctl(char *name, unsigned int cmd) { @@ -236,9 +240,9 @@ /* calc max # of buffers from size (must not exceed the 4MB virtual * address space per DMA channel) */ -int saa7134_buffer_count(int size, int count) +int saa7134_buffer_count(unsigned int size, unsigned int count) { - int maxcount; + unsigned int maxcount; maxcount = 1024 / saa7134_buffer_pages(size); if (count > maxcount) @@ -277,11 +281,11 @@ } int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, - struct scatterlist *list, int length, - int startpage) + struct scatterlist *list, unsigned int length, + unsigned int startpage) { - u32 *ptr; - int i,p; + u32 *ptr; + unsigned int i,p; BUG_ON(NULL == pt || NULL == pt->cpu); @@ -319,14 +323,25 @@ struct saa7134_dmaqueue *q, struct saa7134_buf *buf) { + struct saa7134_buf *next = NULL; #if DEBUG_SPINLOCKS BUG_ON(!spin_is_locked(&dev->slock)); #endif dprintk("buffer_queue %p\n",buf); if (NULL == q->curr) { - q->curr = buf; - buf->activate(dev,buf,NULL); + if (!q->need_two) { + q->curr = buf; + buf->activate(dev,buf,NULL); + } else if (list_empty(&q->queue)) { + list_add_tail(&buf->vb.queue,&q->queue); + buf->vb.state = STATE_QUEUED; + } else { + next = list_entry(q->queue.next,struct saa7134_buf, + vb.queue); + q->curr = buf; + buf->activate(dev,buf,next); + } } else { list_add_tail(&buf->vb.queue,&q->queue); buf->vb.state = STATE_QUEUED; @@ -336,7 +351,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, - int state) + unsigned int state) { #if DEBUG_SPINLOCKS BUG_ON(!spin_is_locked(&dev->slock)); @@ -388,6 +403,14 @@ unsigned long flags; spin_lock_irqsave(&dev->slock,flags); + + /* try to reset the hardware (SWRST) */ + saa_writeb(SAA7134_REGION_ENABLE, 0x00); + saa_writeb(SAA7134_REGION_ENABLE, 0x80); + saa_writeb(SAA7134_REGION_ENABLE, 0x00); + + /* flag current buffer as failed, + try to start over with the next one. */ if (q->curr) { dprintk("timeout on %p\n",q->curr); saa7134_buffer_finish(dev,q,STATE_ERROR); @@ -400,7 +423,7 @@ int saa7134_set_dmabits(struct saa7134_dev *dev) { - unsigned long task=0, ctrl=0, irq=0, split = 0; + u32 split, task=0, ctrl=0, irq=0; enum v4l2_field cap = V4L2_FIELD_ANY; enum v4l2_field ov = V4L2_FIELD_ANY; @@ -460,12 +483,12 @@ /* set task conditions + field handling */ if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) { - /* default config -- use full frames: - odd A, even A, odd B, even B, repeat */ + /* default config -- use full frames */ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d); saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d); saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x02); saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x02); + split = 0; } else { /* split fields between tasks */ if (V4L2_FIELD_TOP == cap) { @@ -494,7 +517,7 @@ SAA7134_MAIN_CTRL_TE5 | SAA7134_MAIN_CTRL_TE6, ctrl); - dprintk("dmabits: task=0x%02lx ctrl=0x%02lx irq=0x%lx split=%s\n", + dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n", task, ctrl, irq, split ? "no" : "yes"); return 0; @@ -508,12 +531,12 @@ "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC", "TRIG_ERR", "CONF_ERR", "LOAD_ERR" }; -#define IRQBITS (sizeof(irqbits)/sizeof(char*)) +#define IRQBITS ARRAY_SIZE(irqbits) static void print_irqstatus(struct saa7134_dev *dev, int loop, unsigned long report, unsigned long status) { - int i; + unsigned int i; printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx", dev->name,loop,jiffies,report,status); @@ -536,18 +559,19 @@ { struct saa7134_dev *dev = (struct saa7134_dev*) dev_id; unsigned long report,status; - int loop; + int loop, handled = 0; for (loop = 0; loop < 10; loop++) { report = saa_readl(SAA7134_IRQ_REPORT); status = saa_readl(SAA7134_IRQ_STATUS); - saa_writel(SAA7134_IRQ_REPORT,report); if (0 == report) { if (irq_debug > 1) printk(KERN_DEBUG "%s/irq: no (more) work\n", dev->name); goto out; } + handled = 1; + saa_writel(SAA7134_IRQ_REPORT,report); if (irq_debug) print_irqstatus(dev,loop,report,status); @@ -582,8 +606,9 @@ saa_writel(SAA7134_IRQ1,0); saa_writel(SAA7134_IRQ2,0); } -out: - return IRQ_HANDLED; + + out: + return IRQ_RETVAL(handled); } /* ------------------------------------------------------------------ */ @@ -599,8 +624,14 @@ saa7134_vbi_init(dev); if (card_has_ts(dev)) saa7134_ts_init(dev); - if (card_has_audio(dev)) + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: saa7134_oss_init(dev); + break; + } /* RAM FIFO config */ saa_writel(SAA7134_FIFO_SIZE, 0x08070503); @@ -634,12 +665,15 @@ /* enable peripheral devices */ saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + /* set vertical line numbering start (vbi needs this) */ + saa_writeb(SAA7134_SOURCE_TIMING2, 0x20); + return 0; } static void __devinit must_configure_manually(void) { - int i,p; + unsigned int i,p; printk(KERN_WARNING "saa7134: \n" @@ -675,18 +709,39 @@ return -ENOMEM; memset(dev,0,sizeof(*dev)); - /* pci stuff */ + /* pci init */ dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { err = -EIO; goto fail1; } sprintf(dev->name,"saa%x[%d]",pci_dev->device,saa7134_devcount); - if (-1 != latency) { + + /* pci quirks */ + if (pci_pci_problems) { + if (pci_pci_problems & PCIPCI_TRITON) + printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name); + if (pci_pci_problems & PCIPCI_NATOMA) + printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name); + if (pci_pci_problems & PCIPCI_VIAETBF) + printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name); + if (pci_pci_problems & PCIPCI_VSFX) + printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name); +#ifdef PCIPCI_ALIMAGIK + if (pci_pci_problems & PCIPCI_ALIMAGIK) { + printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", + dev->name); + latency = 0x0A; + } +#endif + } + if (UNSET != latency) { printk(KERN_INFO "%s: setting pci latency timer to %d\n", dev->name,latency); pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); } + + /* print pci info */ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " @@ -710,7 +765,7 @@ dev->board = SAA7134_BOARD_UNKNOWN; } dev->tuner_type = saa7134_boards[dev->board].tuner_type; - if (-1 != tuner[saa7134_devcount]) + if (UNSET != tuner[saa7134_devcount]) dev->tuner_type = tuner[saa7134_devcount]; printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", dev->name,pci_dev->subsystem_vendor, @@ -730,6 +785,13 @@ dev->lmmio = ioremap(pci_resource_start(pci_dev,0), 0x1000); dev->bmmio = (__u8*)dev->lmmio; + /* register i2c bus */ + saa7134_i2c_register(dev); + + /* initialize hardware */ + saa7134_board_init(dev); + saa7134_hwinit(dev); + /* get irq */ err = request_irq(pci_dev->irq, saa7134_irq, SA_SHIRQ | SA_INTERRUPT, dev->name, dev); @@ -739,12 +801,8 @@ goto fail2; } - /* initialize hardware */ - saa7134_hwinit(dev); - - /* register i2c bus + load i2c helpers */ - saa7134_i2c_register(dev); - if (TUNER_ABSENT != card(dev).tuner_type) + /* load i2c helpers */ + if (TUNER_ABSENT != dev->tuner_type) request_module("tuner"); if (saa7134_boards[dev->board].need_tda9887) request_module("tda9887"); @@ -793,19 +851,27 @@ } /* register oss devices */ - if (card_has_audio(dev)) { - dev->oss.minor_dsp = register_sound_dsp(&saa7134_dsp_fops,dsp_nr); - if (dev->oss.minor_dsp < 0) - goto fail7; - printk(KERN_INFO "%s: registered device dsp%d\n", - dev->name,dev->oss.minor_dsp >> 4); - - dev->oss.minor_mixer = - register_sound_mixer(&saa7134_mixer_fops,mixer_nr); - if (dev->oss.minor_mixer < 0) - goto fail8; - printk(KERN_INFO "%s: registered device mixer%d\n", - dev->name,dev->oss.minor_mixer >> 4); + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + if (oss) { + err = dev->oss.minor_dsp = + register_sound_dsp(&saa7134_dsp_fops,dsp_nr); + if (err < 0) { + goto fail7; + } + printk(KERN_INFO "%s: registered device dsp%d\n", + dev->name,dev->oss.minor_dsp >> 4); + + err = dev->oss.minor_mixer = + register_sound_mixer(&saa7134_mixer_fops,mixer_nr); + if (err < 0) + goto fail8; + printk(KERN_INFO "%s: registered device mixer%d\n", + dev->name,dev->oss.minor_mixer >> 4); + } + break; } /* everything worked */ @@ -815,8 +881,14 @@ return 0; fail8: - if (card_has_audio(dev)) - unregister_sound_dsp(dev->oss.minor_dsp); + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + if (oss) + unregister_sound_dsp(dev->oss.minor_dsp); + break; + } fail7: if (card_has_radio(dev)) video_unregister_device(&dev->radio_dev); @@ -828,16 +900,21 @@ fail4: video_unregister_device(&dev->video_dev); fail3: - if (card_has_audio(dev)) + saa7134_i2c_unregister(dev); + free_irq(pci_dev->irq, dev); + fail2: + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: saa7134_oss_fini(dev); + break; + } if (card_has_ts(dev)) saa7134_ts_fini(dev); saa7134_vbi_fini(dev); saa7134_video_fini(dev); saa7134_tvaudio_fini(dev); - saa7134_i2c_unregister(dev); - free_irq(pci_dev->irq, dev); - fail2: release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); fail1: @@ -849,6 +926,13 @@ { struct saa7134_dev *dev = pci_get_drvdata(pci_dev); + /* debugging ... */ + if (irq_debug) { + u32 report = saa_readl(SAA7134_IRQ_REPORT); + u32 status = saa_readl(SAA7134_IRQ_STATUS); + print_irqstatus(dev,42,report,status); + } + /* disable peripheral devices */ saa_writeb(SAA7134_SPECIAL_MODE,0); @@ -858,8 +942,13 @@ saa_writel(SAA7134_MAIN_CTRL,0); /* shutdown subsystems */ - if (card_has_audio(dev)) + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: saa7134_oss_fini(dev); + break; + } if (card_has_ts(dev)) saa7134_ts_fini(dev); saa7134_vbi_fini(dev); @@ -874,9 +963,15 @@ /* unregister */ saa7134_i2c_unregister(dev); - if (card_has_audio(dev)) { - unregister_sound_mixer(dev->oss.minor_mixer); - unregister_sound_dsp(dev->oss.minor_dsp); + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + if (oss) { + unregister_sound_mixer(dev->oss.minor_mixer); + unregister_sound_dsp(dev->oss.minor_dsp); + } + break; } if (card_has_radio(dev)) video_unregister_device(&dev->radio_dev); diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-i2c.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-i2c.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-i2c.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-i2c.c 2003-05-20 04:46:43.000000000 -0700 @@ -153,10 +153,7 @@ status = i2c_get_status(dev); if (!i2c_is_busy(status)) break; - if (need_resched()) - schedule(); - else - udelay(I2C_WAIT_DELAY); + saa_wait(I2C_WAIT_DELAY); } if (I2C_WAIT_RETRY == count) return FALSE; @@ -318,7 +315,7 @@ static int attach_inform(struct i2c_client *client) { struct saa7134_dev *dev = client->adapter->algo_data; - int tuner = card(dev).tuner_type; + int tuner = dev->tuner_type; saa7134_i2c_call_clients(dev,TUNER_SET_TYPE,&tuner); return 0; @@ -334,9 +331,9 @@ static struct i2c_adapter saa7134_adap_template = { .owner = THIS_MODULE, + .class = I2C_ADAP_CLASS_TV_ANALOG, I2C_DEVNAME("saa7134"), .id = I2C_ALGO_SAA7134, - .class = I2C_ADAP_CLASS_TV_ANALOG, .algo = &saa7134_algo, .client_register = attach_inform, }; diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-oss.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-oss.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-oss.c 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-oss.c 2003-05-20 04:46:43.000000000 -0700 @@ -94,8 +94,9 @@ static int dsp_rec_start(struct saa7134_dev *dev) { - int err, fmt, bswap, wswap; - unsigned long control,flags; + int err, bswap, sign; + u32 fmt, control; + unsigned long flags; /* prepare buffer */ if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->oss.dma))) @@ -110,45 +111,60 @@ /* sample format */ switch (dev->oss.afmt) { - case AFMT_U8: fmt = 0x00; break; - case AFMT_S8: fmt = 0x00 | 0x04; break; + case AFMT_U8: + case AFMT_S8: fmt = 0x00; break; case AFMT_U16_LE: - case AFMT_U16_BE: fmt = 0x01; break; + case AFMT_U16_BE: case AFMT_S16_LE: - case AFMT_S16_BE: fmt = 0x01 | 0x04; break; -/* 4front API specs mention these ones, - the (2.4.15) kernel header hasn't them ... */ -#ifdef AFMT_S32_LE - case AFMT_S32_LE: - case AFMT_S32_BE: fmt = 0x02 | 0x04; break; -#endif + case AFMT_S16_BE: fmt = 0x01; break; default: err = -EINVAL; goto fail2; } switch (dev->oss.afmt) { + case AFMT_S8: + case AFMT_S16_LE: + case AFMT_S16_BE: sign = 1; break; + default: sign = 0; break; + } + + switch (dev->oss.afmt) { case AFMT_U16_BE: - case AFMT_S16_BE: bswap = 1; wswap = 0; break; -#ifdef AFMT_S32_LE - case AFMT_S32_BE: bswap = 1; wswap = 1; break; -#endif - default: bswap = 0; wswap = 0; break; + case AFMT_S16_BE: bswap = 1; break; + default: bswap = 0; break; } - if (1 == dev->oss.channels) - fmt |= (1 << 3); - if (2 == dev->oss.channels) - fmt |= (3 << 3); - fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80; - - saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff)); - saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >> 8); - saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 0xff0000) >> 16); - saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); - dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c%c\n", + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + if (1 == dev->oss.channels) + fmt |= (1 << 3); + if (2 == dev->oss.channels) + fmt |= (3 << 3); + if (sign) + fmt |= 0x04; + fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80; + + saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff)); + saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >> 8); + saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 0xff0000) >> 16); + saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + if (1 == dev->oss.channels) + fmt |= (1 << 4); + if (2 == dev->oss.channels) + fmt |= (2 << 4); + if (!sign) + fmt |= 0x04; + saa_writel(0x588 >> 2, dev->oss.blksize); + saa_writel(0x58c >> 2, 0x543210 | (fmt << 24)); + break; + } + dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", dev->oss.afmt, dev->oss.channels, fmt, - bswap ? 'b' : '-', wswap ? 'w' : '-'); + bswap ? 'b' : '-'); /* dma: setup channel 6 (= AUDIO) */ control = SAA7134_RS_CONTROL_BURST_16 | @@ -156,8 +172,6 @@ (dev->oss.pt.dma >> 12); if (bswap) control |= SAA7134_RS_CONTROL_BSWAP; - if (wswap) - control |= SAA7134_RS_CONTROL_WSWAP; saa_writel(SAA7134_RS_BA1(6),0); saa_writel(SAA7134_RS_BA2(6),dev->oss.blksize); saa_writel(SAA7134_RS_PITCH(6),0); @@ -201,7 +215,7 @@ static int dsp_open(struct inode *inode, struct file *file) { - unsigned int minor = minor(inode->i_rdev); + int minor = minor(inode->i_rdev); struct saa7134_dev *h,*dev = NULL; struct list_head *list; int err; @@ -259,7 +273,8 @@ { struct saa7134_dev *dev = file->private_data; DECLARE_WAITQUEUE(wait, current); - int bytes,err,ret = 0; + unsigned int bytes; + int err,ret = 0; add_wait_queue(&dev->oss.wq, &wait); down(&dev->oss.lock); @@ -390,10 +405,6 @@ case AFMT_U16_BE: case AFMT_S16_LE: case AFMT_S16_BE: -#ifdef AFMT_S32_LE - case AFMT_S32_LE: - case AFMT_S32_BE: -#endif down(&dev->oss.lock); dev->oss.afmt = val; if (dev->oss.recording) { @@ -416,11 +427,6 @@ case AFMT_S16_LE: case AFMT_S16_BE: return put_user(16, (int*)arg); -#ifdef AFMT_S32_LE - case AFMT_S32_LE: - case AFMT_S32_BE: - return put_user(20, (int*)arg); -#endif default: return -EINVAL; } @@ -499,14 +505,10 @@ /* ------------------------------------------------------------------ */ static int -mixer_recsrc(struct saa7134_dev *dev, enum saa7134_audio_in src) +mixer_recsrc_7134(struct saa7134_dev *dev) { - static const char *iname[] = { "Oops", "TV", "LINE1", "LINE2" }; int analog_io,rate; - dev->oss.count++; - dev->oss.input = src; - dprintk("mixer input = %s\n",iname[dev->oss.input]); switch (dev->oss.input) { case TV: saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0); @@ -525,27 +527,78 @@ } static int -mixer_level(struct saa7134_dev *dev, enum saa7134_audio_in src, int level) +mixer_recsrc_7133(struct saa7134_dev *dev) { - switch (src) { + u32 value = 0xbbbbbb; + + switch (dev->oss.input) { case TV: - /* nothing */ + value = 0xbbbb10; /* MAIN */ break; case LINE1: - saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, - (100 == level) ? 0x00 : 0x10); + value = 0xbbbb32; /* AUX1 */ break; case LINE2: - saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, - (100 == level) ? 0x00 : 0x20); + value = 0xbbbb54; /* AUX2 */ break; } + saa_dsp_writel(dev, 0x46c >> 2, value); return 0; } +static int +mixer_recsrc(struct saa7134_dev *dev, enum saa7134_audio_in src) +{ + static const char *iname[] = { "Oops", "TV", "LINE1", "LINE2" }; + + dev->oss.count++; + dev->oss.input = src; + dprintk("mixer input = %s\n",iname[dev->oss.input]); + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + mixer_recsrc_7134(dev); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + mixer_recsrc_7133(dev); + break; + } + return 0; +} + +static int +mixer_level(struct saa7134_dev *dev, enum saa7134_audio_in src, int level) +{ + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + switch (src) { + case TV: + /* nothing */ + break; + case LINE1: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, + (100 == level) ? 0x00 : 0x10); + break; + case LINE2: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, + (100 == level) ? 0x00 : 0x20); + break; + } + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + /* nothing */ + break; + } + return 0; +} + +/* ------------------------------------------------------------------ */ + static int mixer_open(struct inode *inode, struct file *file) { - unsigned int minor = minor(inode->i_rdev); + int minor = minor(inode->i_rdev); struct saa7134_dev *h,*dev = NULL; struct list_head *list; @@ -681,13 +734,20 @@ int saa7134_oss_init(struct saa7134_dev *dev) { + /* general */ init_MUTEX(&dev->oss.lock); init_waitqueue_head(&dev->oss.wq); - dev->oss.line1 = 50; - dev->oss.line2 = 50; - mixer_level(dev,LINE1,dev->oss.line1); - mixer_level(dev,LINE2,dev->oss.line2); - + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + saa_writel(0x588 >> 2, 0x00000fff); + saa_writel(0x58c >> 2, 0x00543210); + saa_dsp_writel(dev, 0x46c >> 2, 0xbbbbbb); + break; + } + + /* dsp */ dev->oss.rate = 32000; if (oss_rate) dev->oss.rate = oss_rate; @@ -695,7 +755,13 @@ dev->oss.rate = saa7134_boards[dev->board].i2s_rate; dev->oss.rate = (dev->oss.rate > 40000) ? 48000 : 32000; + /* mixer */ + dev->oss.line1 = 50; + dev->oss.line2 = 50; + mixer_level(dev,LINE1,dev->oss.line1); + mixer_level(dev,LINE2,dev->oss.line2); mixer_recsrc(dev, (dev->oss.rate == 32000) ? TV : LINE2); + return 0; } @@ -710,7 +776,7 @@ int next_blk, reg = 0; spin_lock(&dev->slock); - if (-1 == dev->oss.dma_blk) { + if (UNSET == dev->oss.dma_blk) { dprintk("irq: recording stopped%s\n",""); goto done; } diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-reg.h linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-reg.h --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-reg.h 2003-05-04 16:53:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-reg.h 2003-05-20 04:46:43.000000000 -0700 @@ -9,9 +9,15 @@ #ifndef PCI_DEVICE_ID_PHILIPS_SAA7130 # define PCI_DEVICE_ID_PHILIPS_SAA7130 0x7130 #endif +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7133 +# define PCI_DEVICE_ID_PHILIPS_SAA7133 0x7133 +#endif #ifndef PCI_DEVICE_ID_PHILIPS_SAA7134 # define PCI_DEVICE_ID_PHILIPS_SAA7134 0x7134 #endif +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7135 +# define PCI_DEVICE_ID_PHILIPS_SAA7135 0x7135 +#endif /* ------------------------------------------------------------------ */ /* @@ -329,6 +335,13 @@ /* test modes */ #define SAA7134_SPECIAL_MODE 0x1d0 +/* audio -- saa7133 + saa7135 only */ +#define SAA7135_DSP_RWSTATE 0x580 +#define SAA7135_DSP_RWSTATE_ERR (1 << 3) +#define SAA7135_DSP_RWSTATE_IDA (1 << 2) +#define SAA7135_DSP_RWSTATE_RDB (1 << 1) +#define SAA7135_DSP_RWSTATE_WRR (1 << 0) + /* ------------------------------------------------------------------ */ /* * Local variables: diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-ts.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-ts.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-ts.c 2003-05-04 16:52:49.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-ts.c 2003-05-20 04:46:43.000000000 -0700 @@ -52,8 +52,9 @@ { u32 control; - dprintk("buffer_activate [%p]\n",buf); + dprintk("buffer_activate [%p]",buf); buf->vb.state = STATE_ACTIVE; + buf->top_seen = 0; /* dma: setup channel 5 (= TS) */ control = SAA7134_RS_CONTROL_BURST_16 | @@ -63,11 +64,11 @@ if (NULL == next) next = buf; if (V4L2_FIELD_TOP == buf->vb.field) { - dprintk("[top] buf=%p next=%p",buf,next); + dprintk("- [top] buf=%p next=%p\n",buf,next); saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf)); saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next)); } else { - dprintk("[bottom] buf=%p next=%p",buf,next); + dprintk("- [bottom] buf=%p next=%p\n",buf,next); saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next)); saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf)); } @@ -86,8 +87,11 @@ { struct saa7134_dev *dev = file->private_data; struct saa7134_buf *buf = (struct saa7134_buf *)vb; - int lines, llength, size, err; + unsigned int lines, llength, size; + int err; + dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]); + llength = TS_PACKET_SIZE; lines = TS_NR_PACKETS; @@ -116,7 +120,6 @@ goto oops; } buf->vb.state = STATE_PREPARED; - buf->top_seen = 0; buf->activate = buffer_activate; buf->vb.field = field; return 0; @@ -163,7 +166,7 @@ static int ts_open(struct inode *inode, struct file *file) { - unsigned int minor = minor(inode->i_rdev); + int minor = minor(inode->i_rdev); struct saa7134_dev *h,*dev = NULL; struct list_head *list; int err; @@ -414,6 +417,7 @@ dev->ts_q.timeout.function = saa7134_buffer_timeout; dev->ts_q.timeout.data = (unsigned long)(&dev->ts_q); dev->ts_q.dev = dev; + dev->ts_q.need_two = 1; videobuf_queue_init(&dev->ts.ts, &ts_qops, dev->pci, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_ALTERNATE, @@ -445,7 +449,7 @@ spin_lock(&dev->slock); if (dev->ts_q.curr) { - field = dev->video_q.curr->vb.field; + field = dev->ts_q.curr->vb.field; if (field == V4L2_FIELD_TOP) { if ((status & 0x100000) != 0x100000) goto done; diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-tvaudio.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-tvaudio.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-tvaudio.c 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-tvaudio.c 2003-05-20 04:46:43.000000000 -0700 @@ -37,13 +37,23 @@ MODULE_PARM(audio_debug,"i"); MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]"); +static unsigned int audio_carrier = 0; +MODULE_PARM(audio_carrier,"i"); +MODULE_PARM_DESC(audio_carrier,"audio carrier location"); + #define dprintk(fmt, arg...) if (audio_debug) \ printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg) +#define d2printk(fmt, arg...) if (audio_debug > 1) \ + printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg) #define print_regb(reg) printk("%s: reg 0x%03x [%-16s]: 0x%02x\n", \ dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg))) +#define SCAN_INITIAL_DELAY (HZ) +#define SCAN_SAMPLE_DELAY (HZ/10) + /* ------------------------------------------------------------------ */ +/* saa7134 code */ static struct saa7134_tvaudio tvaudio[] = { { @@ -95,6 +105,12 @@ .carr2 = 5850, .mode = TVAUDIO_NICAM_AM, },{ + .name = "SECAM-D/K", + .std = V4L2_STD_SECAM, + .carr1 = 6500, + .carr2 = -1, + .mode = TVAUDIO_FM_MONO, + },{ .name = "NTSC-M", .std = V4L2_STD_NTSC, .carr1 = 4500, @@ -122,7 +138,7 @@ schedule(); else udelay(10); - + saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff); saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff); saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff); @@ -134,9 +150,9 @@ saa_writeb(SAA7134_FM_DEMATRIX, 0x80); } -static __u32 tvaudio_carr2reg(__u32 carrier) +static u32 tvaudio_carr2reg(u32 carrier) { - __u64 a = carrier; + u64 a = carrier; a <<= 24; do_div(a,12288); @@ -152,17 +168,19 @@ saa_writel(SAA7134_CARRIER2_FREQ0 >> 2, tvaudio_carr2reg(secondary)); } -static void saa7134_tvaudio_do_mute_input(struct saa7134_dev *dev) +static void mute_input_7134(struct saa7134_dev *dev) { - int mute; + unsigned int mute; struct saa7134_input *in; int reg = 0; int mask; /* look what is to do ... */ in = dev->input; - mute = (dev->ctl_mute || dev->automute); - if (!card_has_audio(dev) && card(dev).mute.name) { + mute = (dev->ctl_mute || + (dev->automute && (&card(dev).radio) != in)); + if (PCI_DEVICE_ID_PHILIPS_SAA7130 == dev->pci->device && + card(dev).mute.name) { /* 7130 - we'll mute using some unconnected audio input */ if (mute) in = &card(dev).mute; @@ -171,14 +189,12 @@ dev->hw_input == in) return; -#if 1 dprintk("ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n", dev->ctl_mute,dev->automute,dev->input->name,mute,in->name); -#endif dev->hw_mute = mute; dev->hw_input = in; - if (card_has_audio(dev)) + if (PCI_DEVICE_ID_PHILIPS_SAA7134 == dev->pci->device) /* 7134 mute */ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, mute ? 0xff : 0xbb); @@ -199,25 +215,6 @@ saa7134_track_gpio(dev,in->name); } -void saa7134_tvaudio_setmute(struct saa7134_dev *dev) -{ - saa7134_tvaudio_do_mute_input(dev); -} - -void saa7134_tvaudio_setinput(struct saa7134_dev *dev, - struct saa7134_input *in) -{ - dev->input = in; - saa7134_tvaudio_do_mute_input(dev); -} - -void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level) -{ - saa_writeb(SAA7134_CHANNEL1_LEVEL, level & 0x1f); - saa_writeb(SAA7134_CHANNEL2_LEVEL, level & 0x1f); - saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f); -} - static void tvaudio_setmode(struct saa7134_dev *dev, struct saa7134_tvaudio *audio, char *note) @@ -274,8 +271,77 @@ saa_writel(0x174 >> 2, 0x0001e000); /* FIXME */ } -int saa7134_tvaudio_getstereo(struct saa7134_dev *dev, - struct saa7134_tvaudio *audio) +static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->thread.wq, &wait); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(timeout); + remove_wait_queue(&dev->thread.wq, &wait); + return dev->thread.scan1 != dev->thread.scan2; +} + +static int tvaudio_checkcarrier(struct saa7134_dev *dev, int carrier) +{ + __s32 left,right,value; + + if (audio_debug > 1) { + int i; + dprintk("debug %d:",carrier); + for (i = -150; i <= 150; i += 30) { + tvaudio_setcarrier(dev,carrier+i,carrier+i); + saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) + return -1; + value = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (0 == i) + printk(" # %6d # ",value >> 16); + else + printk(" %6d",value >> 16); + } + printk("\n"); + } + + tvaudio_setcarrier(dev,carrier-90,carrier-90); + saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) + return -1; + left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + + tvaudio_setcarrier(dev,carrier+90,carrier+90); + saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) + return -1; + right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + + left >>= 16; + right >>= 16; + value = left > right ? left - right : right - left; + dprintk("scanning %d.%03d MHz => dc is %5d [%d/%d]\n", + carrier/1000,carrier%1000,value,left,right); + return value; +} + +#if 0 +static void sifdebug_dump_regs(struct saa7134_dev *dev) +{ + print_regb(AUDIO_STATUS); + print_regb(IDENT_SIF); + print_regb(LEVEL_READOUT1); + print_regb(LEVEL_READOUT2); + print_regb(DCXO_IDENT_CTRL); + print_regb(DEMODULATOR); + print_regb(AGC_GAIN_SELECT); + print_regb(MONITOR_SELECT); + print_regb(FM_DEEMPHASIS); + print_regb(FM_DEMATRIX); + print_regb(SIF_SAMPLE_FREQ); + print_regb(ANALOG_IO_SELECT); +} +#endif + +static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio) { __u32 idp,nicam; int retval = -1; @@ -321,62 +387,42 @@ return retval; } -static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) +static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio, + u32 mode) { - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&dev->thread.wq, &wait); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(timeout); - remove_wait_queue(&dev->thread.wq, &wait); - return dev->thread.scan1 != dev->thread.scan2; -} - -static int tvaudio_checkcarrier(struct saa7134_dev *dev, int carrier) -{ - __s32 left,right,value; - - tvaudio_setcarrier(dev,carrier-100,carrier-100); - if (tvaudio_sleep(dev,HZ/10)) - return -1; - left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); - if (tvaudio_sleep(dev,HZ/10)) - return -1; - left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); - - tvaudio_setcarrier(dev,carrier+100,carrier+100); - if (tvaudio_sleep(dev,HZ/10)) - return -1; - right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); - if (tvaudio_sleep(dev,HZ/10)) - return -1; - right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + static char *name[] = { + [ V4L2_TUNER_MODE_MONO ] = "mono", + [ V4L2_TUNER_MODE_STEREO ] = "stereo", + [ V4L2_TUNER_MODE_LANG1 ] = "lang1", + [ V4L2_TUNER_MODE_LANG2 ] = "lang2", + }; + static u32 fm[] = { + [ V4L2_TUNER_MODE_MONO ] = 0x00, /* ch1 */ + [ V4L2_TUNER_MODE_STEREO ] = 0x80, /* auto */ + [ V4L2_TUNER_MODE_LANG1 ] = 0x00, /* ch1 */ + [ V4L2_TUNER_MODE_LANG2 ] = 0x01, /* ch2 */ + }; + u32 reg; - left >>= 16; - right >>= 16; - value = left > right ? left - right : right - left; - dprintk("scanning %d.%03d MHz => dc is %5d [%d/%d]\n", - carrier/1000,carrier%1000,value,left,right); - return value; -} - -#if 0 -static void sifdebug_dump_regs(struct saa7134_dev *dev) -{ - print_regb(AUDIO_STATUS); - print_regb(IDENT_SIF); - print_regb(LEVEL_READOUT1); - print_regb(LEVEL_READOUT2); - print_regb(DCXO_IDENT_CTRL); - print_regb(DEMODULATOR); - print_regb(AGC_GAIN_SELECT); - print_regb(MONITOR_SELECT); - print_regb(FM_DEEMPHASIS); - print_regb(FM_DEMATRIX); - print_regb(SIF_SAMPLE_FREQ); - print_regb(ANALOG_IO_SELECT); + switch (audio->mode) { + case TVAUDIO_FM_MONO: + /* nothing to do ... */ + break; + case TVAUDIO_FM_K_STEREO: + case TVAUDIO_FM_BG_STEREO: + dprintk("setstereo [fm] => %s\n", + name[ mode % ARRAY_SIZE(name) ]); + reg = fm[ mode % ARRAY_SIZE(fm) ]; + saa_writeb(SAA7134_FM_DEMATRIX, reg); + break; + case TVAUDIO_FM_SAT_STEREO: + case TVAUDIO_NICAM_AM: + case TVAUDIO_NICAM_FM: + /* FIXME */ + break; + } + return 0; } -#endif static int tvaudio_thread(void *data) { @@ -388,7 +434,8 @@ struct saa7134_dev *dev = data; const int *carr_scan; int carr_vals[4]; - int i,max,carrier,audio; + unsigned int i, audio; + int max1,max2,carrier,rx,mode; lock_kernel(); daemonize("%s", dev->name); @@ -410,10 +457,10 @@ dev->tvaudio = NULL; tvaudio_init(dev); dev->automute = 1; - saa7134_tvaudio_setmute(dev); + mute_input_7134(dev); /* give the tuner some time */ - if (tvaudio_sleep(dev,HZ/2)) + if (tvaudio_sleep(dev,SCAN_INITIAL_DELAY)) goto restart; /* find the main carrier */ @@ -433,55 +480,280 @@ if (dev->thread.scan1 != dev->thread.scan2) goto restart; } - for (carrier = 0, max = 0, i = 0; i < MAX_SCAN; i++) { + for (carrier = 0, max1 = 0, max2 = 0, i = 0; i < MAX_SCAN; i++) { if (!carr_scan[i]) continue; - if (max < carr_vals[i]) { - max = carr_vals[i]; + if (max1 < carr_vals[i]) { + max2 = max1; + max1 = carr_vals[i]; carrier = carr_scan[i]; + } else if (max2 < carr_vals[i]) { + max2 = carr_vals[i]; } } - if (0 == carrier) { - /* Oops: autoscan didn't work for some reason :-/ */ - printk(KERN_WARNING "%s/audio: oops: audio carrier " - "scan failed\n", dev->name); + + if (0 != carrier && max1 > 2000 && max1 > max2*3) { + /* found good carrier */ + dprintk("found %s main sound carrier @ %d.%03d MHz [%d/%d]\n", + dev->tvnorm->name, carrier/1000, carrier%1000, + max1, max2); + dev->last_carrier = carrier; + } else if (0 != audio_carrier) { + /* no carrier -- try insmod option as fallback */ + carrier = audio_carrier; + printk(KERN_WARNING "%s/audio: audio carrier scan failed, " + "using %d.%03d MHz [insmod option]\n", + dev->name, carrier/1000, carrier%1000); + } else if (0 != dev->last_carrier) { + /* no carrier -- try last detected one as fallback */ + carrier = dev->last_carrier; + printk(KERN_WARNING "%s/audio: audio carrier scan failed, " + "using %d.%03d MHz [last detected]\n", + dev->name, carrier/1000, carrier%1000); } else { - dprintk("found %s main sound carrier @ %d.%03d MHz\n", - dev->tvnorm->name, - carrier/1000,carrier%1000); + /* no carrier + no fallback -- try first in list */ + carrier = carr_scan[0]; + printk(KERN_WARNING "%s/audio: audio carrier scan failed, " + "using %d.%03d MHz [default]\n", + dev->name, carrier/1000, carrier%1000); } tvaudio_setcarrier(dev,carrier,carrier); dev->automute = 0; + saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x00); saa7134_tvaudio_setmute(dev); /* find the exact tv audio norm */ - for (audio = -1, i = 0; i < TVAUDIO; i++) { - if (dev->tvnorm->id != -1 && + for (audio = UNSET, i = 0; i < TVAUDIO; i++) { + if (dev->tvnorm->id != UNSET && dev->tvnorm->id != tvaudio[i].std) continue; if (tvaudio[i].carr1 != carrier) continue; - if (-1 == audio) + if (UNSET == audio) audio = i; tvaudio_setmode(dev,&tvaudio[i],"trying"); if (tvaudio_sleep(dev,HZ)) goto restart; - if (-1 != saa7134_tvaudio_getstereo(dev,&tvaudio[i])) { + if (-1 != tvaudio_getstereo(dev,&tvaudio[i])) { audio = i; break; } } - if (-1 == audio) + saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x30); + if (UNSET == audio) continue; tvaudio_setmode(dev,&tvaudio[audio],"using"); + tvaudio_setstereo(dev,&tvaudio[audio],V4L2_TUNER_MODE_MONO); dev->tvaudio = &tvaudio[audio]; -#if 1 if (tvaudio_sleep(dev,3*HZ)) goto restart; - saa7134_tvaudio_getstereo(dev,&tvaudio[i]); + rx = tvaudio_getstereo(dev,&tvaudio[i]); + mode = saa7134_tvaudio_rx2mode(rx); + tvaudio_setstereo(dev,&tvaudio[audio],mode); + } + + done: + dev->thread.task = NULL; + if(dev->thread.notify != NULL) + up(dev->thread.notify); + return 0; +} + +/* ------------------------------------------------------------------ */ +/* saa7133 / saa7135 code */ + +static char *stdres[0x20] = { + [0x00] = "no standard detected", + [0x01] = "B/G (in progress)", + [0x02] = "D/K (in progress)", + [0x03] = "M (in progress)", + + [0x04] = "B/G A2", + [0x05] = "B/G NICAM", + [0x06] = "D/K A2 (1)", + [0x07] = "D/K A2 (2)", + [0x08] = "D/K A2 (3)", + [0x09] = "D/K NICAM", + [0x0a] = "L NICAM", + [0x0b] = "I NICAM", + + [0x0c] = "M Korea", + [0x0d] = "M BTSC ", + [0x0e] = "M EIAJ", + + [0x0f] = "FM radio / IF 10.7 / 50 deemp", + [0x10] = "FM radio / IF 10.7 / 75 deemp", + [0x11] = "FM radio / IF sel / 50 deemp", + [0x12] = "FM radio / IF sel / 75 deemp", + + [0x13 ... 0x1e ] = "unknown", + [0x1f] = "??? [in progress]", +}; + +#define DSP_RETRY 16 +#define DSP_DELAY 16 + +static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit) +{ + int state, count = DSP_RETRY; + + state = saa_readb(SAA7135_DSP_RWSTATE); + if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { + printk("%s: dsp access error\n",dev->name); + /* FIXME: send ack ... */ + return -EIO; + } + while (0 == (state & bit)) { + if (unlikely(0 == count)) { + printk("%s: dsp access wait timeout [bit=%s]\n", + dev->name, + (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" : + (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" : + (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" : + "???"); + return -EIO; + } + saa_wait(DSP_DELAY); + state = saa_readb(SAA7135_DSP_RWSTATE); + count--; + } + return 0; +} + +#if 0 +static int saa_dsp_readl(struct saa7134_dev *dev, int reg, u32 *value) +{ + int err; + + d2printk("dsp read reg 0x%x\n", reg<<2); + saa_readl(reg); + err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_RDB); + if (err < 0) + return err; + *value = saa_readl(reg); + d2printk("dsp read => 0x%06x\n", *value & 0xffffff); + err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_IDA); + if (err < 0) + return err; + return 0; +} #endif + +int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value) +{ + int err; + + d2printk("dsp write reg 0x%x = 0x%06x\n",reg<<2,value); + err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); + if (err < 0) + return err; + saa_writel(reg,value); + err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); + if (err < 0) + return err; + return 0; +} + +static int getstereo_7133(struct saa7134_dev *dev) +{ + int retval = V4L2_TUNER_SUB_MONO; + u32 value; + + value = saa_readl(0x528 >> 2); + if (value & 0x20) + retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + if (value & 0x40) + retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + return retval; +} + +static int mute_input_7133(struct saa7134_dev *dev) +{ + u32 reg = 0; + + switch (dev->input->amux) { + case TV: reg = 0x02; break; + case LINE1: reg = 0x00; break; + case LINE2: reg = 0x01; break; + } + if (dev->ctl_mute) + reg = 0x07; + saa_writel(0x594 >> 2, reg); + return 0; +} + +static int tvaudio_thread_ddep(void *data) +{ + struct saa7134_dev *dev = data; + u32 value, norms; + + lock_kernel(); + daemonize("%s", dev->name); + dev->thread.task = current; + unlock_kernel(); + if (dev->thread.notify != NULL) + up(dev->thread.notify); + + /* unmute */ + saa_dsp_writel(dev, 0x474 >> 2, 0x00); + saa_dsp_writel(dev, 0x450 >> 2, 0x00); + + for (;;) { + if (dev->thread.exit || signal_pending(current)) + goto done; + interruptible_sleep_on(&dev->thread.wq); + if (dev->thread.exit || signal_pending(current)) + goto done; + + restart: + dev->thread.scan1 = dev->thread.scan2; + dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + + norms = 0; + if (dev->tvnorm->id & V4L2_STD_PAL) + norms |= 0x2c; /* B/G + D/K + I */ + if (dev->tvnorm->id & V4L2_STD_NTSC) + norms |= 0x40; /* M */ + if (dev->tvnorm->id & V4L2_STD_SECAM) + norms |= 0x18; /* L + D/K */ + if (0 == norms) + norms = 0x0000007c; + + /* quick & dirty -- to be fixed up later ... */ + saa_dsp_writel(dev, 0x454 >> 2, 0); + saa_dsp_writel(dev, 0x454 >> 2, norms | 0x80); + saa_dsp_writel(dev, 0x464 >> 2, 0x000000); + saa_dsp_writel(dev, 0x470 >> 2, 0x101010); + + if (tvaudio_sleep(dev,3*HZ)) + goto restart; + value = saa_readl(0x528 >> 2) & 0xffffff; + + dprintk("tvaudio thread status: 0x%x [%s%s%s]\n", + value, stdres[value & 0x1f], + (value & 0x000020) ? ",stereo" : "", + (value & 0x000040) ? ",dual" : ""); + dprintk("detailed status: " + "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", + (value & 0x000080) ? " A2/EIAJ pilot tone " : "", + (value & 0x000100) ? " A2/EIAJ dual " : "", + (value & 0x000200) ? " A2/EIAJ stereo " : "", + (value & 0x000400) ? " A2/EIAJ noise mute " : "", + + (value & 0x000800) ? " BTSC/FM radio pilot " : "", + (value & 0x001000) ? " SAP carrier " : "", + (value & 0x002000) ? " BTSC stereo noise mute " : "", + (value & 0x004000) ? " SAP noise mute " : "", + (value & 0x008000) ? " VDSP " : "", + + (value & 0x010000) ? " NICST " : "", + (value & 0x020000) ? " NICDU " : "", + (value & 0x040000) ? " NICAM muted " : "", + (value & 0x080000) ? " NICAM reserve sound " : "", + + (value & 0x100000) ? " init done " : ""); } done: @@ -492,14 +764,89 @@ } /* ------------------------------------------------------------------ */ +/* common stuff + external entry points */ + +int saa7134_tvaudio_rx2mode(u32 rx) +{ + u32 mode; + + mode = V4L2_TUNER_MODE_MONO; + if (rx & V4L2_TUNER_SUB_STEREO) + mode = V4L2_TUNER_MODE_STEREO; + else if (rx & V4L2_TUNER_SUB_LANG1) + mode = V4L2_TUNER_MODE_LANG1; + else if (rx & V4L2_TUNER_SUB_LANG2) + mode = V4L2_TUNER_MODE_LANG2; + return mode; +} + +void saa7134_tvaudio_setmute(struct saa7134_dev *dev) +{ + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7130: + case PCI_DEVICE_ID_PHILIPS_SAA7134: + mute_input_7134(dev); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + mute_input_7133(dev); + break; + } +} + +void saa7134_tvaudio_setinput(struct saa7134_dev *dev, + struct saa7134_input *in) +{ + dev->input = in; + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7130: + case PCI_DEVICE_ID_PHILIPS_SAA7134: + mute_input_7134(dev); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + mute_input_7133(dev); + break; + } +} + +void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level) +{ + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + saa_writeb(SAA7134_CHANNEL1_LEVEL, level & 0x1f); + saa_writeb(SAA7134_CHANNEL2_LEVEL, level & 0x1f); + saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f); + break; + } +} + +int saa7134_tvaudio_getstereo(struct saa7134_dev *dev) +{ + int retval = V4L2_TUNER_SUB_MONO; + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + if (dev->tvaudio) + retval = tvaudio_getstereo(dev,dev->tvaudio); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + retval = getstereo_7133(dev); + break; + } + return retval; +} int saa7134_tvaudio_init(struct saa7134_dev *dev) { DECLARE_MUTEX_LOCKED(sem); + int (*my_thread)(void *data) = NULL; /* enable I2S audio output */ if (saa7134_boards[dev->board].i2s_rate) { - int rate = (32000 == saa7134_boards[dev->board].i2s_rate) ? 0x01 : 0x03; + int rate = (32000 == saa7134_boards[dev->board].i2s_rate) + ? 0x01 : 0x03; /* set rate */ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate); @@ -512,13 +859,24 @@ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); } - /* start tvaudio thread */ - init_waitqueue_head(&dev->thread.wq); - dev->thread.notify = &sem; - kernel_thread(tvaudio_thread,dev,0); - down(&sem); - dev->thread.notify = NULL; - wake_up_interruptible(&dev->thread.wq); + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + my_thread = tvaudio_thread; + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + my_thread = tvaudio_thread_ddep; + break; + } + if (my_thread) { + /* start tvaudio thread */ + init_waitqueue_head(&dev->thread.wq); + dev->thread.notify = &sem; + kernel_thread(my_thread,dev,0); + down(&sem); + dev->thread.notify = NULL; + wake_up_interruptible(&dev->thread.wq); + } return 0; } @@ -541,8 +899,13 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) { - dev->thread.scan2++; - wake_up_interruptible(&dev->thread.wq); + if (dev->thread.task) { + dev->thread.scan2++; + wake_up_interruptible(&dev->thread.wq); + } else { + dev->automute = 0; + saa7134_tvaudio_setmute(dev); + } return 0; } diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-vbi.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-vbi.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-vbi.c 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-vbi.c 2003-05-20 04:46:43.000000000 -0700 @@ -85,6 +85,7 @@ dprintk("buffer_activate [%p]\n",buf); buf->vb.state = STATE_ACTIVE; + buf->top_seen = 0; task_init(dev,buf,TASK_A); task_init(dev,buf,TASK_B); @@ -119,7 +120,8 @@ struct saa7134_dev *dev = fh->dev; struct saa7134_buf *buf = (struct saa7134_buf *)vb; struct saa7134_tvnorm *norm = dev->tvnorm; - int lines, llength, size, err; + unsigned int lines, llength, size; + int err; lines = norm->vbi_v_stop - norm->vbi_v_start +1; if (lines > VBI_LINE_COUNT) @@ -155,7 +157,6 @@ goto oops; } buf->vb.state = STATE_PREPARED; - buf->top_seen = 0; buf->activate = buffer_activate; buf->vb.field = field; return 0; @@ -166,7 +167,7 @@ } static int -buffer_setup(struct file *file, int *count, int *size) +buffer_setup(struct file *file, unsigned int *count, unsigned int *size) { struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; @@ -241,7 +242,7 @@ if (dev->vbi_q.curr) { dev->vbi_fieldcount++; /* make sure we have seen both fields */ - if ((status & 0x10) == 0x10) { + if ((status & 0x10) == 0x00) { dev->vbi_q.curr->top_seen = 1; goto done; } diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-video.c linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-video.c --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134-video.c 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134-video.c 2003-05-20 04:46:43.000000000 -0700 @@ -2,7 +2,7 @@ * device driver for philips saa7134 based TV cards * video4linux video interface * - * (c) 2001,02 Gerd Knorr [SuSE Labs] + * (c) 2001-03 Gerd Knorr [SuSE Labs] * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,7 +127,7 @@ .vshift = 1, } }; -#define FORMATS (sizeof(formats)/sizeof(struct saa7134_format)) +#define FORMATS ARRAY_SIZE(formats) static struct saa7134_tvnorm tvnorms[] = { { @@ -141,13 +141,15 @@ .chroma_ctrl1 = 0x81, .chroma_gain = 0x2a, .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, .h_start = 0, .h_stop = 719, .video_v_start = 24, .video_v_stop = 311, - .vbi_v_start = 7-3, /* FIXME */ - .vbi_v_stop = 22-3, + .vbi_v_start = 7, + .vbi_v_stop = 22, + .src_timing = 4, },{ .name = "NTSC", .id = V4L2_STD_NTSC, @@ -159,6 +161,7 @@ .chroma_ctrl1 = 0x89, .chroma_gain = 0x2a, .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, .h_start = 0, .h_stop = 719, @@ -166,17 +169,19 @@ .video_v_stop = 22+240, .vbi_v_start = 10, /* FIXME */ .vbi_v_stop = 21, /* FIXME */ + .src_timing = 1, },{ .name = "SECAM", .id = V4L2_STD_SECAM, .width = 720, .height = 576, - .sync_control = 0x58, + .sync_control = 0x18, /* old: 0x58, */ .luma_control = 0x1b, .chroma_ctrl1 = 0xd1, .chroma_gain = 0x80, .chroma_ctrl2 = 0x00, + .vgate_misc = 0x1c, .h_start = 0, .h_stop = 719, @@ -184,6 +189,47 @@ .video_v_stop = 311, .vbi_v_start = 7, .vbi_v_stop = 22, + .src_timing = 4, + },{ + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + .width = 720, + .height = 480, + + .sync_control = 0x59, + .luma_control = 0x40, + .chroma_ctrl1 = 0xb9, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, + + .h_start = 0, + .h_stop = 719, + .video_v_start = 22, + .video_v_stop = 22+240, + .vbi_v_start = 10, /* FIXME */ + .vbi_v_stop = 21, /* FIXME */ + .src_timing = 1, + },{ + .name = "PAL-Nc", + .id = V4L2_STD_PAL_Nc, + .width = 720, + .height = 576, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0xa1, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + + .h_start = 0, + .h_stop = 719, + .video_v_start = 24, + .video_v_stop = 311, + .vbi_v_start = 7, + .vbi_v_stop = 22, + .src_timing = 4, #if 0 },{ .name = "AUTO", @@ -196,6 +242,7 @@ .chroma_ctrl1 = 0x8b, .chroma_gain = 0x00, .chroma_ctrl2 = 0x00, + .vgate_misc = 0x18, .h_start = 0, .h_stop = 719, @@ -203,11 +250,11 @@ .video_v_stop = 311, .vbi_v_start = 7, .vbi_v_stop = 22, + .src_timing = 4, #endif } }; -#define TVNORMS (sizeof(tvnorms)/sizeof(struct saa7134_tvnorm)) - +#define TVNORMS ARRAY_SIZE(tvnorms) #define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1) @@ -298,11 +345,11 @@ .type = V4L2_CTRL_TYPE_INTEGER, } }; -const int CTRLS = (sizeof(video_ctrls)/sizeof(struct v4l2_queryctrl)); +static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls); -static const struct v4l2_queryctrl* ctrl_by_id(int id) +static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id) { - int i; + unsigned int i; for (i = 0; i < CTRLS; i++) if (video_ctrls[i].id == id) @@ -310,9 +357,9 @@ return NULL; } -static struct saa7134_format* format_by_fourcc(int fourcc) +static struct saa7134_format* format_by_fourcc(unsigned int fourcc) { - int i; + unsigned int i; for (i = 0; i < FORMATS; i++) if (formats[i].fourcc == fourcc) @@ -323,7 +370,7 @@ /* ----------------------------------------------------------------------- */ /* resource management */ -static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, int bit) +static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit) { if (fh->resources & bit) /* have it already allocated */ @@ -345,19 +392,19 @@ } static -int res_check(struct saa7134_fh *fh, int bit) +int res_check(struct saa7134_fh *fh, unsigned int bit) { return (fh->resources & bit); } static -int res_locked(struct saa7134_dev *dev, int bit) +int res_locked(struct saa7134_dev *dev, unsigned int bit) { return (dev->resources & bit); } static -void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, int bits) +void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits) { if ((fh->resources & bits) != bits) BUG(); @@ -393,7 +440,8 @@ saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90); saa_writeb(SAA7134_HSYNC_START, 0xeb); saa_writeb(SAA7134_HSYNC_STOP, 0xe0); - + saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing); + saa_writeb(SAA7134_SYNC_CTRL, norm->sync_control); saa_writeb(SAA7134_LUMA_CTRL, luma_control); saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); @@ -410,7 +458,7 @@ saa_writeb(SAA7134_ANALOG_ADC, 0x01); saa_writeb(SAA7134_VGATE_START, 0x11); saa_writeb(SAA7134_VGATE_STOP, 0xfe); - saa_writeb(SAA7134_MISC_VGATE_MSB, 0x18); /* FIXME */ + saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); @@ -454,7 +502,7 @@ { 9, 15, 0, 4, 3 }, { 10, 16, 1, 5, 3 }, }; - static const int count = sizeof(vals)/sizeof(vals[0]); + static const int count = ARRAY_SIZE(vals); int i; for (i = 0; i < count; i++) @@ -532,14 +580,14 @@ /* deinterlace y offsets */ if (interlace) { y_odd = dev->ctl_y_odd; - y_even = dev->ctl_y_even + yscale / 32; + y_even = dev->ctl_y_even; saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd); saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even); saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd); saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even); } else { y_odd = dev->ctl_y_odd; - y_even = dev->ctl_y_even + yscale / 64; + y_even = dev->ctl_y_even; saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd); saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even); saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd); @@ -688,7 +736,8 @@ err = verify_preview(dev,&fh->win); if (0 != err) return err; - + + dev->ovfield = fh->win.field; dprintk("start_preview %dx%d+%d+%d %s field=%s\n", fh->win.w.width,fh->win.w.height, fh->win.w.left,fh->win.w.top, @@ -752,6 +801,7 @@ dprintk("buffer_activate buf=%p\n",buf); buf->vb.state = STATE_ACTIVE; + buf->top_seen = 0; set_size(dev,TASK_A,buf->vb.width,buf->vb.height, V4L2_FIELD_HAS_BOTH(buf->vb.field)); @@ -828,7 +878,8 @@ struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; struct saa7134_buf *buf = (struct saa7134_buf *)vb; - int size,err; + unsigned int size; + int err; /* sanity checks */ if (NULL == fh->fmt) @@ -842,8 +893,8 @@ if (0 != buf->vb.baddr && buf->vb.bsize < size) return -EINVAL; - dprintk("buffer_prepare [size=%dx%d,bytes=%d,fields=%s,%s]\n", - fh->width,fh->height,size,v4l2_field_names[field], + dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n", + vb->i,fh->width,fh->height,size,v4l2_field_names[field], fh->fmt->name); if (buf->vb.width != fh->width || buf->vb.height != fh->height || @@ -872,7 +923,6 @@ goto oops; } buf->vb.state = STATE_PREPARED; - buf->top_seen = 0; buf->activate = buffer_activate; return 0; @@ -1081,7 +1131,7 @@ static int video_open(struct inode *inode, struct file *file) { - unsigned int minor = minor(inode->i_rdev); + int minor = minor(inode->i_rdev); struct saa7134_dev *h,*dev = NULL; struct saa7134_fh *fh; struct list_head *list; @@ -1117,8 +1167,8 @@ fh->radio = radio; fh->type = type; fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); - fh->width = 320; - fh->height = 240; + fh->width = 768; + fh->height = 576; videobuf_queue_init(&fh->cap, &video_qops, dev->pci, &dev->slock, @@ -1138,7 +1188,9 @@ if (fh->radio) { /* switch to radio mode */ + u32 v = 400*16; saa7134_tvaudio_setinput(dev,&card(dev).radio); + saa7134_i2c_call_clients(dev,VIDIOCSFREQ,&v); saa7134_i2c_call_clients(dev,AUDC_SET_RADIO,NULL); } else { /* switch to video/vbi mode */ @@ -1185,7 +1237,7 @@ buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); } else { down(&fh->cap.lock); - if (-1 == fh->cap.read_off) { + if (UNSET == fh->cap.read_off) { /* need to capture a new frame */ if (res_locked(fh->dev,RESOURCE_VIDEO)) { up(&fh->cap.lock); @@ -1263,6 +1315,29 @@ /* ------------------------------------------------------------------ */ +void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f) +{ + struct saa7134_tvnorm *norm = dev->tvnorm; + + f->fmt.vbi.sampling_rate = 6750000 * 4; + f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 64 * 4; + f->fmt.vbi.start[0] = norm->vbi_v_start; + f->fmt.vbi.count[0] = norm->vbi_v_stop - norm->vbi_v_start +1; + f->fmt.vbi.start[1] = norm->video_v_stop + norm->vbi_v_start +1; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */; + +#if 0 + if (V4L2_STD_PAL == norm->id) { + /* FIXME */ + f->fmt.vbi.start[0] += 3; + f->fmt.vbi.start[1] += 3*2; + } +#endif +} + int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_format *f) { @@ -1273,27 +1348,17 @@ f->fmt.pix.height = fh->height; f->fmt.pix.field = fh->cap.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.sizeimage = - (fh->width*fh->height*fh->fmt->depth)/8; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: f->fmt.win = fh->win; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: - { - struct saa7134_tvnorm *norm = fh->dev->tvnorm; - - f->fmt.vbi.sampling_rate = 6750000 * 4; - f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = 64 * 4; - f->fmt.vbi.start[0] = norm->vbi_v_start; - f->fmt.vbi.count[0] = norm->vbi_v_stop - norm->vbi_v_start +1; - f->fmt.vbi.start[1] = norm->video_v_stop + norm->vbi_v_start + 1; - f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; - f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */; + saa7134_vbi_fmt(dev,f); return 0; - } default: return -EINVAL; } @@ -1309,7 +1374,7 @@ { struct saa7134_format *fmt; enum v4l2_field field; - int maxw, maxh; + unsigned int maxw, maxh; fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) @@ -1336,12 +1401,18 @@ } f->fmt.pix.field = field; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = - (fh->width * fh->height * fmt->depth)/8; + f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; } @@ -1350,6 +1421,9 @@ if (0 != err) return err; return 0; + case V4L2_BUF_TYPE_VBI_CAPTURE: + saa7134_vbi_fmt(dev,f); + return 0; default: return -EINVAL; } @@ -1397,6 +1471,9 @@ up(&dev->lock); return 0; break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + saa7134_vbi_fmt(dev,f); + return 0; default: return -EINVAL; } @@ -1442,10 +1519,10 @@ case VIDIOC_ENUMSTD: { struct v4l2_standard *e = arg; - int i; + unsigned int i; i = e->index; - if (i < 0 || i >= TVNORMS) + if (i >= TVNORMS) return -EINVAL; err = v4l2_video_std_construct(e, tvnorms[e->index].id, tvnorms[e->index].name); @@ -1464,7 +1541,7 @@ case VIDIOC_S_STD: { v4l2_std_id *id = arg; - int i; + unsigned int i; for(i = 0; i < TVNORMS; i++) if (*id & tvnorms[i].id) @@ -1489,7 +1566,7 @@ case VIDIOC_ENUMINPUT: { struct v4l2_input *i = arg; - int n; + unsigned int n; n = i->index; if (n >= SAA7134_INPUT_MAX) @@ -1557,22 +1634,8 @@ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; t->rangehigh = 0xffffffffUL; - t->rxsubchans = -1; - if (dev->tvaudio) - t->rxsubchans = saa7134_tvaudio_getstereo - (dev,dev->tvaudio); - if (-1 == t->rxsubchans) - t->rxsubchans = V4L2_TUNER_SUB_MONO; -#if 1 - /* fill audmode -- FIXME: allow manual switching */ - t->audmode = V4L2_TUNER_MODE_MONO; - if (t->rxsubchans & V4L2_TUNER_SUB_STEREO) - t->audmode = V4L2_TUNER_MODE_STEREO; - else if (t->rxsubchans & V4L2_TUNER_SUB_LANG1) - t->audmode = V4L2_TUNER_MODE_LANG1; - else if (t->rxsubchans & V4L2_TUNER_SUB_LANG2) - t->audmode = V4L2_TUNER_MODE_LANG2; -#endif + t->rxsubchans = saa7134_tvaudio_getstereo(dev); + t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans); } if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03)) t->signal = 0xffff; @@ -1655,14 +1718,14 @@ { struct v4l2_fmtdesc *f = arg; enum v4l2_buf_type type; - int index; + unsigned int index; index = f->index; type = f->type; switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (index < 0 || index >= FORMATS) + if (index >= FORMATS) return -EINVAL; if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY && formats[index].planar) @@ -1760,21 +1823,23 @@ { struct video_mbuf *mbuf = arg; struct videobuf_queue *q; - int i; + struct v4l2_requestbuffers req; + unsigned int i; q = saa7134_queue(fh); - down(&q->lock); - err = videobuf_mmap_setup(file,q,gbuffers,gbufsize); - if (err < 0) { - up(&q->lock); + memset(&req,0,sizeof(req)); + req.type = q->type; + req.count = gbuffers; + err = videobuf_reqbufs(file,q,&req); + if (err < 0) return err; - } memset(mbuf,0,sizeof(*mbuf)); - mbuf->frames = gbuffers; - mbuf->size = gbuffers * gbufsize; - for (i = 0; i < gbuffers; i++) - mbuf->offsets[i] = i * gbufsize; - up(&q->lock); + mbuf->frames = req.count; + mbuf->size = 0; + for (i = 0; i < mbuf->frames; i++) { + mbuf->offsets[i] = q->bufs[i]->boff; + mbuf->size += q->bufs[i]->bsize; + } return 0; } case VIDIOC_REQBUFS: @@ -1800,7 +1865,10 @@ case VIDIOC_STREAMOFF: { int res = saa7134_resource(fh); + err = videobuf_streamoff(file,saa7134_queue(fh)); + if (err < 0) + return err; res_free(dev,fh,res); return 0; } @@ -1879,9 +1947,16 @@ strcpy(a->name,"Radio"); return 0; } + case VIDIOC_G_STD: + { + v4l2_std_id *id = arg; + *id = 0; + return 0; + } case VIDIOC_S_AUDIO: case VIDIOC_S_TUNER: case VIDIOC_S_INPUT: + case VIDIOC_S_STD: return 0; case VIDIOC_QUERYCTRL: @@ -2055,7 +2130,7 @@ if (V4L2_FIELD_HAS_BOTH(field)) { /* make sure we have seen both fields */ - if ((status & 0x10) == 0x10) { + if ((status & 0x10) == 0x00) { dev->video_q.curr->top_seen = 1; goto done; } diff -urN linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134.h linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134.h --- linux-2.5.69-bk13/drivers/media/video/saa7134/saa7134.h 2003-05-04 16:53:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/saa7134/saa7134.h 2003-05-20 04:46:43.000000000 -0700 @@ -28,7 +28,7 @@ #include #include -#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,2) +#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,8) #ifndef TRUE # define TRUE (1==1) @@ -36,6 +36,7 @@ #ifndef FALSE # define FALSE (1==0) #endif +#define UNSET (-1U) /* 2.4 / 2.5 driver compatibility stuff */ @@ -67,50 +68,52 @@ struct saa7134_tvnorm { char *name; v4l2_std_id id; - int width; - int height; + unsigned int width; + unsigned int height; /* video decoder */ - int sync_control; - int luma_control; - int chroma_ctrl1; - int chroma_gain; - int chroma_ctrl2; + unsigned int sync_control; + unsigned int luma_control; + unsigned int chroma_ctrl1; + unsigned int chroma_gain; + unsigned int chroma_ctrl2; + unsigned int vgate_misc; /* video scaler */ - int h_start; - int h_stop; - int video_v_start; - int video_v_stop; - int vbi_v_start; - int vbi_v_stop; + unsigned int h_start; + unsigned int h_stop; + unsigned int video_v_start; + unsigned int video_v_stop; + unsigned int vbi_v_start; + unsigned int vbi_v_stop; + unsigned int src_timing; }; struct saa7134_tvaudio { - char *name; - int std; - enum saa7134_tvaudio_mode mode; - int carr1; - int carr2; + char *name; + v4l2_std_id std; + enum saa7134_tvaudio_mode mode; + int carr1; + int carr2; }; struct saa7134_format { - char *name; - int fourcc; - int depth; - int pm; - int vshift; /* vertical downsampling (for planar yuv) */ - int hshift; /* horizontal downsampling (for planar yuv) */ - int bswap:1; - int wswap:1; - int yuv:1; - int planar:1; + char *name; + unsigned int fourcc; + unsigned int depth; + unsigned int pm; + unsigned int vshift; /* vertical downsampling (for planar yuv) */ + unsigned int hshift; /* horizontal downsampling (for planar yuv) */ + unsigned int bswap:1; + unsigned int wswap:1; + unsigned int yuv:1; + unsigned int planar:1; }; /* ----------------------------------------------------------- */ /* card configuration */ -#define SAA7134_BOARD_NOAUTO -1 +#define SAA7134_BOARD_NOAUTO UNSET #define SAA7134_BOARD_UNKNOWN 0 #define SAA7134_BOARD_PROTEUS_PRO 1 #define SAA7134_BOARD_FLYVIDEO3000 2 @@ -124,38 +127,40 @@ #define SAA7134_BOARD_KWORLD 10 #define SAA7134_BOARD_CINERGY600 11 #define SAA7134_BOARD_MD7134 12 +#define SAA7134_BOARD_TYPHOON_90031 13 +#define SAA7134_BOARD_ELSA 14 +#define SAA7134_BOARD_ELSA_500TV 15 #define SAA7134_INPUT_MAX 8 struct saa7134_input { char *name; - int vmux; + unsigned int vmux; enum saa7134_audio_in amux; - int gpio; - int tv:1; + unsigned int gpio; + unsigned int tv:1; }; struct saa7134_board { char *name; - int audio_clock; + unsigned int audio_clock; /* input switching */ - int gpiomask; + unsigned int gpiomask; struct saa7134_input inputs[SAA7134_INPUT_MAX]; struct saa7134_input radio; struct saa7134_input mute; /* peripheral I/O */ - int i2s_rate; - int has_ts; + unsigned int i2s_rate; + unsigned int has_ts; enum saa7134_video_out video_out; /* i2c chip info */ - int tuner_type; - int need_tda9887:1; + unsigned int tuner_type; + unsigned int need_tda9887:1; }; -#define card_has_audio(dev) (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7134) #define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) #define card_has_ts(dev) (saa7134_boards[dev->board].has_ts) #define card(dev) (saa7134_boards[dev->board]) @@ -189,9 +194,9 @@ struct task_struct *task; wait_queue_head_t wq; struct semaphore *notify; - int exit; - int scan1; - int scan2; + unsigned int exit; + unsigned int scan1; + unsigned int scan2; }; /* buffer for one video/vbi/ts frame */ @@ -201,7 +206,7 @@ /* saa7134 specific */ struct saa7134_format *fmt; - int top_seen; + unsigned int top_seen; int (*activate)(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next); @@ -215,22 +220,23 @@ struct saa7134_buf *curr; struct list_head queue; struct timer_list timeout; + unsigned int need_two; }; /* video filehandle status */ struct saa7134_fh { struct saa7134_dev *dev; - int radio; + unsigned int radio; enum v4l2_buf_type type; struct v4l2_window win; struct v4l2_clip clips[8]; - int nclips; - int resources; + unsigned int nclips; + unsigned int resources; /* video capture */ struct saa7134_format *fmt; - int width,height; + unsigned int width,height; struct videobuf_queue cap; struct saa7134_pgtable pt_cap; @@ -241,7 +247,7 @@ /* TS status */ struct saa7134_ts { - int users; + unsigned int users; /* TS capture */ struct videobuf_queue ts; @@ -253,28 +259,28 @@ struct semaphore lock; int minor_mixer; int minor_dsp; - int users_dsp; + unsigned int users_dsp; /* mixer */ enum saa7134_audio_in input; - int count; - int line1; - int line2; + unsigned int count; + unsigned int line1; + unsigned int line2; /* dsp */ - int afmt; - int rate; - int channels; - int recording; - int blocks; - int blksize; - int bufsize; + unsigned int afmt; + unsigned int rate; + unsigned int channels; + unsigned int recording; + unsigned int blocks; + unsigned int blksize; + unsigned int bufsize; struct saa7134_pgtable pt; struct videobuf_dmabuf dma; wait_queue_head_t wq; - int dma_blk; - int read_offset; - int read_count; + unsigned int dma_blk; + unsigned int read_offset; + unsigned int read_count; }; /* global device status */ @@ -284,7 +290,7 @@ spinlock_t slock; /* various device info */ - int resources; + unsigned int resources; struct video_device video_dev; struct video_device ts_dev; struct video_device radio_dev; @@ -300,8 +306,8 @@ __u8 *bmmio; /* config info */ - int board; - int tuner_type; + unsigned int board; + unsigned int tuner_type; /* i2c i/o */ struct i2c_adapter i2c_adap; @@ -311,19 +317,19 @@ /* video overlay */ struct v4l2_framebuffer ovbuf; struct saa7134_format *ovfmt; - int ovenable; + unsigned int ovenable; enum v4l2_field ovfield; /* video+ts+vbi capture */ struct saa7134_dmaqueue video_q; struct saa7134_dmaqueue ts_q; struct saa7134_dmaqueue vbi_q; - int vbi_fieldcount; + unsigned int vbi_fieldcount; /* various v4l controls */ struct saa7134_tvnorm *tvnorm; /* video */ struct saa7134_tvaudio *tvaudio; - int ctl_input; + unsigned int ctl_input; int ctl_bright; int ctl_contrast; int ctl_hue; @@ -337,11 +343,12 @@ int ctl_y_even; /* other global state info */ - int automute; + unsigned int automute; struct saa7134_thread thread; struct saa7134_input *input; struct saa7134_input *hw_input; - int hw_mute; + unsigned int hw_mute; + int last_carrier; }; /* ----------------------------------------------------------- */ @@ -362,12 +369,13 @@ #define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit)) #define saa_clearb(reg,bit) saa_andorb((reg),(bit),0) +#define saa_wait(d) { if (need_resched()) schedule(); else udelay(d);} /* ----------------------------------------------------------- */ /* saa7134-core.c */ extern struct list_head saa7134_devlist; -extern int saa7134_devcount; +extern unsigned int saa7134_devcount; void saa7134_print_ioctl(char *name, unsigned int cmd); void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); @@ -376,18 +384,18 @@ int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt); int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, - struct scatterlist *list, int length, - int startpage); + struct scatterlist *list, unsigned int length, + unsigned int startpage); void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt); -int saa7134_buffer_count(int size, int count); +int saa7134_buffer_count(unsigned int size, unsigned int count); int saa7134_buffer_startpage(struct saa7134_buf *buf); unsigned long saa7134_buffer_base(struct saa7134_buf *buf); int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, struct saa7134_buf *buf); void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, - int state); + unsigned int state); void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q); void saa7134_buffer_timeout(unsigned long data); void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf); @@ -398,9 +406,11 @@ /* saa7134-cards.c */ extern struct saa7134_board saa7134_boards[]; -extern const int saa7134_bcount; +extern const unsigned int saa7134_bcount; extern struct pci_device_id __devinitdata saa7134_pci_tbl[]; +extern int saa7134_board_init(struct saa7134_dev *dev); + /* ----------------------------------------------------------- */ /* saa7134-i2c.c */ @@ -449,17 +459,19 @@ /* ----------------------------------------------------------- */ /* saa7134-tvaudio.c */ +int saa7134_tvaudio_rx2mode(u32 rx); + void saa7134_tvaudio_setmute(struct saa7134_dev *dev); void saa7134_tvaudio_setinput(struct saa7134_dev *dev, struct saa7134_input *in); void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level); -int saa7134_tvaudio_getstereo(struct saa7134_dev *dev, - struct saa7134_tvaudio *audio); +int saa7134_tvaudio_getstereo(struct saa7134_dev *dev); int saa7134_tvaudio_init(struct saa7134_dev *dev); int saa7134_tvaudio_fini(struct saa7134_dev *dev); int saa7134_tvaudio_do_scan(struct saa7134_dev *dev); +int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value); /* ----------------------------------------------------------- */ /* saa7134-oss.c */ diff -urN linux-2.5.69-bk13/drivers/media/video/tda7432.c linux-2.5.69-bk14/drivers/media/video/tda7432.c --- linux-2.5.69-bk13/drivers/media/video/tda7432.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/tda7432.c 2003-05-20 04:46:43.000000000 -0700 @@ -330,8 +330,6 @@ i2c_set_clientdata(client, t); do_tda7432_init(client); - MOD_INC_USE_COUNT; - strncpy(client->dev.name, "TDA7432", DEVICE_NAME_SIZE); printk(KERN_INFO "tda7432: init\n"); i2c_attach_client(client); @@ -347,13 +345,12 @@ static int tda7432_detach(struct i2c_client *client) { - struct tda7432 *t = i2c_get_clientdata(client); + struct tda7432 *t = i2c_get_clientdata(client); do_tda7432_init(client); i2c_detach_client(client); kfree(t); - MOD_DEC_USE_COUNT; return 0; } @@ -525,11 +522,9 @@ static struct i2c_client client_template = { - .id = -1, - .driver = &driver, - .dev = { - .name = "tda7432", - }, + I2C_DEVNAME("tda7432"), + .id = -1, + .driver = &driver, }; static int tda7432_init(void) diff -urN linux-2.5.69-bk13/drivers/media/video/tda9875.c linux-2.5.69-bk14/drivers/media/video/tda9875.c --- linux-2.5.69-bk13/drivers/media/video/tda9875.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/tda9875.c 2003-05-20 04:46:43.000000000 -0700 @@ -161,7 +161,8 @@ struct tda9875 *tda = i2c_get_clientdata(client); unsigned char a; - dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n",tda->lvol,tda->rvol,tda->bass,tda->treble); + dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n", + tda->lvol,tda->rvol,tda->bass,tda->treble); a = tda->lvol & 0xff; @@ -263,8 +264,6 @@ } do_tda9875_init(client); - MOD_INC_USE_COUNT; - strncpy(client->dev.name, "TDA9875", DEVICE_NAME_SIZE); printk(KERN_INFO "tda9875: init\n"); i2c_attach_client(client); @@ -280,13 +279,12 @@ static int tda9875_detach(struct i2c_client *client) { - struct tda9875 *t = i2c_get_clientdata(client); + struct tda9875 *t = i2c_get_clientdata(client); do_tda9875_init(client); i2c_detach_client(client); kfree(t); - MOD_DEC_USE_COUNT; return 0; } @@ -395,11 +393,9 @@ static struct i2c_client client_template = { - .id = -1, - .driver = &driver, - .dev = { - .name = "tda9875", - }, + I2C_DEVNAME("tda9875"), + .id = -1, + .driver = &driver, }; static int tda9875_init(void) diff -urN linux-2.5.69-bk13/drivers/media/video/tda9887.c linux-2.5.69-bk14/drivers/media/video/tda9887.c --- linux-2.5.69-bk13/drivers/media/video/tda9887.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/tda9887.c 2003-05-20 04:46:43.000000000 -0700 @@ -293,8 +293,6 @@ unsigned char *buf = NULL; int rc; - printk("tda9887_configure\n"); - if (t->radio) { dprintk("tda9885/6/7: FM Radio mode\n"); buf = buf_fm_stereo; @@ -358,11 +356,11 @@ return -ENOMEM; memset(t,0,sizeof(*t)); t->client = client_template; - i2c_set_clientdata(&t->client, t); t->pinnacle_id = -1; + t->tvnorm=VIDEO_MODE_PAL; + i2c_set_clientdata(&t->client, t); i2c_attach_client(&t->client); - MOD_INC_USE_COUNT; return 0; } @@ -379,7 +377,6 @@ i2c_detach_client(client); kfree(t); - MOD_DEC_USE_COUNT; return 0; } @@ -404,6 +401,7 @@ int *i = arg; t->pinnacle_id = *i; + tda9887_miro(t); break; } /* --- v4l ioctls --- */ diff -urN linux-2.5.69-bk13/drivers/media/video/tuner.c linux-2.5.69-bk14/drivers/media/video/tuner.c --- linux-2.5.69-bk13/drivers/media/video/tuner.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/tuner.c 2003-05-20 04:46:43.000000000 -0700 @@ -20,13 +20,15 @@ static unsigned short normal_i2c_range[] = {0x60,0x6f,I2C_CLIENT_END}; I2C_CLIENT_INSMOD; +#define UNSET (-1U) + /* insmod options */ -static int debug = 0; -static int type = -1; -static int addr = 0; +static unsigned int debug = 0; +static unsigned int type = UNSET; +static unsigned int addr = 0; static char *pal = "b"; -static int tv_range[2] = { 44, 958 }; -static int radio_range[2] = { 65, 108 }; +static unsigned int tv_range[2] = { 44, 958 }; +static unsigned int radio_range[2] = { 65, 108 }; MODULE_PARM(debug,"i"); MODULE_PARM(type,"i"); MODULE_PARM(addr,"i"); @@ -45,13 +47,16 @@ struct tuner { - int type; /* chip type */ - int freq; /* keep track of the current settings */ - int std; - - int radio; - int mode; /* current norm for multi-norm tuners */ - int xogc; // only for MT2032 + unsigned int type; /* chip type */ + unsigned int freq; /* keep track of the current settings */ + unsigned int std; + + unsigned int radio; + unsigned int mode; /* current norm for multi-norm tuners */ + + // only for MT2032 + unsigned int xogc; + unsigned int radio_if2; }; static struct i2c_driver driver; @@ -217,8 +222,11 @@ 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,623 }, { "LG NTSC (newer TAPC series)", LGINNOTEK, NTSC, 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,732}, + + { "HITACHI V7-J180AT", HITACHI, NTSC, + 16*170.00, 16*450.00, 0x01,0x02,0x00,0x8e,940 }, }; -#define TUNERS (sizeof(tuners)/sizeof(struct tunertype)) +#define TUNERS ARRAY_SIZE(tuners) /* ---------------------------------------------------------------------- */ @@ -381,10 +389,15 @@ return 1; } -static int mt2032_compute_freq(int rfin, int if1, int if2, int spectrum_from, - int spectrum_to, unsigned char *buf, int *ret_sel, int xogc) //all in Hz +static int mt2032_compute_freq(unsigned int rfin, + unsigned int if1, unsigned int if2, + unsigned int spectrum_from, + unsigned int spectrum_to, + unsigned char *buf, + int *ret_sel, + unsigned int xogc) //all in Hz { - int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1, + unsigned int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1, desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq; fref= 5250 *1000; //5.25MHz @@ -513,7 +526,9 @@ } -static void mt2032_set_if_freq(struct i2c_client *c,int rfin, int if1, int if2, int from, int to) +static void mt2032_set_if_freq(struct i2c_client *c, unsigned int rfin, + unsigned int if1, unsigned int if2, + unsigned int from, unsigned int to) { unsigned char buf[21]; int lint_try,ret,sel,lock=0; @@ -568,28 +583,30 @@ } -static void mt2032_set_tv_freq(struct i2c_client *c, int freq, int norm) +static void mt2032_set_tv_freq(struct i2c_client *c, + unsigned int freq, unsigned int norm) { int if2,from,to; // signal bandwidth and picture carrier - if(norm==VIDEO_MODE_NTSC) { + if (norm==VIDEO_MODE_NTSC) { from=40750*1000; to=46750*1000; if2=45750*1000; - } - else { // Pal + } else { + // Pal from=32900*1000; to=39900*1000; if2=38900*1000; } - mt2032_set_if_freq(c,freq* 1000*1000/16, 1090*1000*1000, if2, from, to); + mt2032_set_if_freq(c, freq*62500 /* freq*1000*1000/16 */, + 1090*1000*1000, if2, from, to); } // Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz -static void set_tv_freq(struct i2c_client *c, int freq) +static void set_tv_freq(struct i2c_client *c, unsigned int freq) { u8 config; u16 div; @@ -598,7 +615,7 @@ unsigned char buffer[4]; int rc; - if (t->type == -1) { + if (t->type == UNSET) { printk("tuner: tuner type not set\n"); return; } @@ -720,22 +737,23 @@ } -static void mt2032_set_radio_freq(struct i2c_client *c,int freq) -{ - int if2; - - if2=10700*1000; // 10.7MHz FM intermediate frequency +static void mt2032_set_radio_freq(struct i2c_client *c, unsigned int freq) +{ + struct tuner *t = i2c_get_clientdata(c); + int if2 = t->radio_if2; // per Manual for FM tuning: first if center freq. 1085 MHz - mt2032_set_if_freq(c,freq* 1000*1000/16, 1085*1000*1000,if2,if2,if2); + mt2032_set_if_freq(c, freq*62500 /* freq*1000*1000/16 */, + 1085*1000*1000,if2,if2,if2); } -static void set_radio_freq(struct i2c_client *c, int freq) +static void set_radio_freq(struct i2c_client *c, unsigned int freq) { struct tunertype *tun; struct tuner *t = i2c_get_clientdata(c); unsigned char buffer[4]; - int rc,div; + unsigned div; + int rc; if (freq < radio_range[0]*16 || freq > radio_range[1]*16) { printk("tuner: radio freq (%d.%02d) out of range (%d-%d)\n", @@ -743,7 +761,7 @@ radio_range[0],radio_range[1]); return; } - if (t->type == -1) { + if (t->type == UNSET) { printk("tuner: tuner type not set\n"); return; } @@ -798,20 +816,20 @@ kfree(client); return -ENOMEM; } - i2c_set_clientdata(client, t); memset(t,0,sizeof(struct tuner)); - if (type >= 0 && type < TUNERS) { + i2c_set_clientdata(client, t); + t->type = UNSET; + t->radio_if2 = 10700*1000; // 10.7MHz - FM radio + + if (type < TUNERS) { t->type = type; printk("tuner(bttv): type forced to %d (%s) [insmod]\n",t->type,tuners[t->type].name); strncpy(client->dev.name, tuners[t->type].name, DEVICE_NAME_SIZE); - } else { - t->type = -1; } i2c_attach_client(client); if (t->type == TUNER_MT2032) mt2032_init(client); - MOD_INC_USE_COUNT; return 0; } @@ -835,7 +853,6 @@ i2c_detach_client(client); kfree(t); kfree(client); - MOD_DEC_USE_COUNT; return 0; } @@ -843,20 +860,17 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct tuner *t = i2c_get_clientdata(client); - int *iarg = (int*)arg; -#if 0 - __u16 *sarg = (__u16*)arg; -#endif + unsigned int *iarg = (int*)arg; switch (cmd) { /* --- configuration --- */ case TUNER_SET_TYPE: - if (t->type != -1) { + if (t->type != UNSET) { printk("tuner: type already set (%d)\n",t->type); return 0; } - if (*iarg < 0 || *iarg >= TUNERS) + if (*iarg >= TUNERS) return 0; t->type = *iarg; printk("tuner: type set to %d (%s)\n", @@ -868,6 +882,18 @@ case AUDC_SET_RADIO: t->radio = 1; break; + case AUDC_CONFIG_PINNACLE: + switch (*iarg) { + case 2: + dprintk("tuner: pinnacle pal\n"); + t->radio_if2 = 33300 * 1000; + break; + case 3: + dprintk("tuner: pinnacle ntsc\n"); + t->radio_if2 = 41300 * 1000; + break; + } + break; /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a @@ -913,35 +939,6 @@ va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO); return 0; } - -#if 0 - /* --- old, obsolete interface --- */ - case TUNER_SET_TVFREQ: - dprintk("tuner: tv freq set to %d.%02d\n", - (*iarg)/16,(*iarg)%16*100/16); - set_tv_freq(client,*iarg); - t->radio = 0; - t->freq = *iarg; - break; - - case TUNER_SET_RADIOFREQ: - dprintk("tuner: radio freq set to %d.%02d\n", - (*iarg)/16,(*iarg)%16*100/16); - set_radio_freq(client,*iarg); - t->radio = 1; - t->freq = *iarg; - break; - case TUNER_SET_MODE: - if (t->type != TUNER_PHILIPS_SECAM) { - dprintk("tuner: trying to change mode for other than TUNER_PHILIPS_SECAM\n"); - } else { - int mode=(*sarg==VIDEO_MODE_SECAM)?1:0; - dprintk("tuner: mode set to %d\n", *sarg); - t->mode = mode; - set_tv_freq(client,t->freq); - } - break; -#endif default: /* nothing */ break; @@ -964,9 +961,9 @@ static struct i2c_client client_template = { .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, - .dev = { - .name = "(tuner unset)", + .driver = &driver, + .dev = { + .name = "(tuner unset)", }, }; diff -urN linux-2.5.69-bk13/drivers/media/video/tvaudio.c linux-2.5.69-bk14/drivers/media/video/tvaudio.c --- linux-2.5.69-bk13/drivers/media/video/tvaudio.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/tvaudio.c 2003-05-20 04:46:43.000000000 -0700 @@ -34,19 +34,19 @@ #include "tvaudio.h" - /* ---------------------------------------------------------------------- */ /* insmod args */ MODULE_PARM(debug,"i"); static int debug = 0; /* insmod parameter */ -#define dprintk if (debug) printk - MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr"); MODULE_LICENSE("GPL"); +#define UNSET (-1U) +#define dprintk if (debug) printk + /* ---------------------------------------------------------------------- */ /* our structs */ @@ -161,22 +161,24 @@ unsigned char buffer[2]; if (-1 == subaddr) { - dprintk("%s: chip_write: 0x%x\n", chip->c.dev.name, val); + dprintk("%s: chip_write: 0x%x\n", + i2c_clientname(&chip->c), val); chip->shadow.bytes[1] = val; buffer[0] = val; if (1 != i2c_master_send(&chip->c,buffer,1)) { printk(KERN_WARNING "%s: I/O error (write 0x%x)\n", - chip->c.dev.name, val); + i2c_clientname(&chip->c), val); return -1; } } else { - dprintk("%s: chip_write: reg%d=0x%x\n", chip->c.dev.name, subaddr, val); + dprintk("%s: chip_write: reg%d=0x%x\n", + i2c_clientname(&chip->c), subaddr, val); chip->shadow.bytes[subaddr+1] = val; buffer[0] = subaddr; buffer[1] = val; if (2 != i2c_master_send(&chip->c,buffer,2)) { printk(KERN_WARNING "%s: I/O error (write reg%d=0x%x)\n", - chip->c.dev.name, subaddr, val); + i2c_clientname(&chip->c), subaddr, val); return -1; } } @@ -201,10 +203,10 @@ if (1 != i2c_master_recv(&chip->c,&buffer,1)) { printk(KERN_WARNING "%s: I/O error (read)\n", - chip->c.dev.name); + i2c_clientname(&chip->c)); return -1; } - dprintk("%s: chip_read: 0x%x\n",chip->c.dev.name,buffer); + dprintk("%s: chip_read: 0x%x\n",i2c_clientname(&chip->c),buffer); return buffer; } @@ -220,11 +222,11 @@ if (2 != i2c_transfer(chip->c.adapter,msgs,2)) { printk(KERN_WARNING "%s: I/O error (read2)\n", - chip->c.dev.name); + i2c_clientname(&chip->c)); return -1; } dprintk("%s: chip_read2: reg%d=0x%x\n", - chip->c.dev.name,subaddr,read[0]); + i2c_clientname(&chip->c),subaddr,read[0]); return read[0]; } @@ -237,7 +239,7 @@ /* update our shadow register set; print bytes if (debug > 0) */ dprintk("%s: chip_cmd(%s): reg=%d, data:", - chip->c.dev.name,name,cmd->bytes[0]); + i2c_clientname(&chip->c),name,cmd->bytes[0]); for (i = 1; i < cmd->count; i++) { dprintk(" 0x%x",cmd->bytes[i]); chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; @@ -246,7 +248,7 @@ /* send data to the chip */ if (cmd->count != i2c_master_send(&chip->c,cmd->bytes,cmd->count)) { - printk(KERN_WARNING "%s: I/O error (%s)\n", chip->c.dev.name, name); + printk(KERN_WARNING "%s: I/O error (%s)\n", i2c_clientname(&chip->c), name); return -1; } return 0; @@ -270,22 +272,18 @@ struct CHIPSTATE *chip = data; struct CHIPDESC *desc = chiplist + chip->type; -#ifdef CONFIG_SMP lock_kernel(); -#endif - daemonize("%s", chip->c.dev.name); + daemonize("%s",i2c_clientname(&chip->c)); chip->thread = current; -#ifdef CONFIG_SMP unlock_kernel(); -#endif - dprintk("%s: thread started\n", chip->c.dev.name); + dprintk("%s: thread started\n", i2c_clientname(&chip->c)); if(chip->notify != NULL) up(chip->notify); for (;;) { interruptible_sleep_on(&chip->wq); - dprintk("%s: thread wakeup\n", chip->c.dev.name); + dprintk("%s: thread wakeup\n", i2c_clientname(&chip->c)); if (chip->done || signal_pending(current)) break; @@ -301,7 +299,7 @@ } chip->thread = NULL; - dprintk("%s: thread exiting\n", chip->c.dev.name); + dprintk("%s: thread exiting\n", i2c_clientname(&chip->c)); if(chip->notify != NULL) up(chip->notify); @@ -316,7 +314,7 @@ if (mode == chip->prevmode) return; - dprintk("%s: thread checkmode\n", chip->c.dev.name); + dprintk("%s: thread checkmode\n", i2c_clientname(&chip->c)); chip->prevmode = mode; if (mode & VIDEO_SOUND_STEREO) @@ -764,9 +762,9 @@ static int tda9874a_dic = -1; /* device id. code */ /* insmod options for tda9874a */ -static int tda9874a_SIF = -1; -static int tda9874a_AMSEL = -1; -static int tda9874a_STD = -1; +static unsigned int tda9874a_SIF = UNSET; +static unsigned int tda9874a_AMSEL = UNSET; +static unsigned int tda9874a_STD = UNSET; MODULE_PARM(tda9874a_SIF,"i"); MODULE_PARM(tda9874a_AMSEL,"i"); MODULE_PARM(tda9874a_STD,"i"); @@ -981,7 +979,7 @@ dprintk("tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); if((dic == 0x11)||(dic == 0x07)) { - dprintk("tvaudio: found tda9874%s.\n",(dic == 0x11) ? "a (new)":"h (old)"); + printk("tvaudio: found tda9874%s.\n", (dic == 0x11) ? "a":"h"); tda9874a_dic = dic; /* remember device id. */ return 1; } @@ -990,35 +988,27 @@ static int tda9874a_initialize(struct CHIPSTATE *chip) { - if(tda9874a_SIF != -1) { - if(tda9874a_SIF == 1) - tda9874a_GCONR = 0xc0; /* sound IF input 1 */ - else if(tda9874a_SIF == 2) - tda9874a_GCONR = 0xc1; /* sound IF input 2 */ - else - printk(KERN_WARNING "tda9874a: SIF parameter must be 1 or 2.\n"); - } + if (tda9874a_SIF > 2) + tda9874a_SIF = 1; + if (tda9874a_STD >= 8) + tda9874a_STD = 0; + if(tda9874a_AMSEL > 1) + tda9874a_AMSEL = 0; - if(tda9874a_STD != -1) { - if((tda9874a_STD >= 0)&&(tda9874a_STD <= 8)) { - tda9874a_ESP = tda9874a_STD; - tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; - } else { - printk(KERN_WARNING "tda9874a: STD parameter must be between 0 and 8.\n"); - } - } + if(tda9874a_SIF == 1) + tda9874a_GCONR = 0xc0; /* sound IF input 1 */ + else + tda9874a_GCONR = 0xc1; /* sound IF input 2 */ - if(tda9874a_AMSEL != -1) { - if(tda9874a_AMSEL == 0) - tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ - else if(tda9874a_AMSEL == 1) - tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ - else - printk(KERN_WARNING "tda9874a: AMSEL parameter must be 0 or 1.\n"); - } + tda9874a_ESP = tda9874a_STD; + tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; - tda9874a_setup(chip); + if(tda9874a_AMSEL == 0) + tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ + else + tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ + tda9874a_setup(chip); return 0; } @@ -1132,6 +1122,87 @@ #define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ #define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for TA8874Z */ + +// write 1st byte +#define TA8874Z_LED_STE 0x80 +#define TA8874Z_LED_BIL 0x40 +#define TA8874Z_LED_EXT 0x20 +#define TA8874Z_MONO_SET 0x10 +#define TA8874Z_MUTE 0x08 +#define TA8874Z_F_MONO 0x04 +#define TA8874Z_MODE_SUB 0x02 +#define TA8874Z_MODE_MAIN 0x01 + +// write 2nd byte +//#define TA8874Z_TI 0x80 // test mode +#define TA8874Z_SEPARATION 0x3f +#define TA8874Z_SEPARATION_DEFAULT 0x10 + +// read +#define TA8874Z_B1 0x80 +#define TA8874Z_B0 0x40 +#define TA8874Z_CHAG_FLAG 0x20 + +// B1 B0 +// mono L H +// stereo L L +// BIL H L + +static int ta8874z_getmode(struct CHIPSTATE *chip) +{ + int val, mode; + + val = chip_read(chip); + mode = VIDEO_SOUND_MONO; + if (val & TA8874Z_B1){ + mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + }else if (!(val & TA8874Z_B0)){ + mode |= VIDEO_SOUND_STEREO; + } + //dprintk ("ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); + return mode; +} + +static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; + +static void ta8874z_setmode(struct CHIPSTATE *chip, int mode) +{ + int update = 1; + audiocmd *t = NULL; + dprintk("ta8874z_setmode(): mode: 0x%02x\n", mode); + + switch(mode){ + case VIDEO_SOUND_MONO: + t = &ta8874z_mono; + break; + case VIDEO_SOUND_STEREO: + t = &ta8874z_stereo; + break; + case VIDEO_SOUND_LANG1: + t = &ta8874z_main; + break; + case VIDEO_SOUND_LANG2: + t = &ta8874z_sub; + break; + default: + update = 0; + } + + if(update) + chip_cmd(chip, "TA8874Z", t); +} + +static int ta8874z_checkit(struct CHIPSTATE *chip) +{ + int rc; + rc = chip_read(chip); + return ((rc & 0x1f) == 0x1f) ? 1 : 0; +} /* ---------------------------------------------------------------------- */ /* audio chip descriptions - struct CHIPDESC */ @@ -1143,9 +1214,11 @@ int tda9855 = 1; int tda9873 = 1; int tda9874a = 1; -int tea6300 = 0; +int tea6300 = 0; // address clash with msp34xx int tea6420 = 1; int pic16c54 = 1; +int ta8874z = 0; // address clash with tda9840 + MODULE_PARM(tda8425,"i"); MODULE_PARM(tda9840,"i"); MODULE_PARM(tda9850,"i"); @@ -1155,6 +1228,7 @@ MODULE_PARM(tea6300,"i"); MODULE_PARM(tea6420,"i"); MODULE_PARM(pic16c54,"i"); +MODULE_PARM(ta8874z,"i"); static struct CHIPDESC chiplist[] = { { @@ -1319,7 +1393,23 @@ PIC16C54_MISC_SND_NOTMUTE}, .inputmute = PIC16C54_MISC_SND_MUTE, }, - { name: NULL } /* EOF */ + { + .name = "ta8874z", + .id = -1, + //.id = I2C_DRIVERID_TA8874Z, + .checkit = ta8874z_checkit, + .insmodopt = &ta8874z, + .addr_lo = I2C_TDA9840 >> 1, + .addr_hi = I2C_TDA9840 >> 1, + .registers = 2, + + .getmode = ta8874z_getmode, + .setmode = ta8874z_setmode, + .checkmode = generic_checkmode, + + .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, + }, + { .name = NULL } /* EOF */ }; @@ -1356,19 +1446,18 @@ dprintk("tvaudio: no matching chip description found\n"); return -EIO; } - printk("tvaudio: found %s\n",desc->name); + printk("tvaudio: found %s @ 0x%x\n", desc->name, addr<<1); dprintk("tvaudio: matches:%s%s%s.\n", (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); /* fill required data structures */ - strncpy(chip->c.dev.name, desc->name, DEVICE_NAME_SIZE); + strcpy(i2c_clientname(&chip->c),desc->name); chip->type = desc-chiplist; chip->shadow.count = desc->registers+1; chip->prevmode = -1; /* register */ - MOD_INC_USE_COUNT; i2c_attach_client(&chip->c); /* initialization */ @@ -1430,7 +1519,6 @@ i2c_detach_client(&chip->c); kfree(chip); - MOD_DEC_USE_COUNT; return 0; } @@ -1444,7 +1532,7 @@ struct CHIPSTATE *chip = i2c_get_clientdata(client); struct CHIPDESC *desc = chiplist + chip->type; - dprintk("%s: chip_command 0x%x\n",chip->c.dev.name,cmd); + dprintk("%s: chip_command 0x%x\n",i2c_clientname(&chip->c),cmd); switch (cmd) { case AUDC_SET_INPUT: @@ -1552,11 +1640,9 @@ static struct i2c_client client_template = { - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, - .dev = { - .name = "(unset)", - }, + I2C_DEVNAME("(unset)"), + .flags = I2C_CLIENT_ALLOW_USE, + .driver = &driver, }; static int audiochip_init_module(void) diff -urN linux-2.5.69-bk13/drivers/media/video/tvaudio.h linux-2.5.69-bk14/drivers/media/video/tvaudio.h --- linux-2.5.69-bk13/drivers/media/video/tvaudio.h 2003-05-04 16:52:49.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/tvaudio.h 2003-05-20 04:46:43.000000000 -0700 @@ -3,7 +3,7 @@ */ #define I2C_TDA8425 0x82 -#define I2C_TDA9840 0x84 +#define I2C_TDA9840 0x84 /* also used by TA8874Z */ #define I2C_TDA985x_L 0xb4 /* also used by 9873 */ #define I2C_TDA985x_H 0xb6 #define I2C_TDA9874 0xb0 /* also used by 9875 */ diff -urN linux-2.5.69-bk13/drivers/media/video/tvmixer.c linux-2.5.69-bk14/drivers/media/video/tvmixer.c --- linux-2.5.69-bk13/drivers/media/video/tvmixer.c 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/tvmixer.c 2003-05-20 04:46:43.000000000 -0700 @@ -10,19 +10,16 @@ #include #include #include -#include - #include #include + +#include #include #define DEV_MAX 4 -static int debug = 0; static int devnr = -1; - -MODULE_PARM(debug,"i"); MODULE_PARM(devnr,"i"); MODULE_AUTHOR("Gerd Knorr"); @@ -87,7 +84,7 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "tv card", sizeof(info.id)); - strncpy(info.name, client->dev.name, sizeof(info.name)); + strncpy(info.name, i2c_clientname(client), sizeof(info.name)); info.modify_counter = 42 /* FIXME */; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -96,7 +93,7 @@ if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; strncpy(info.id, "tv card", sizeof(info.id)); - strncpy(info.name, client->dev.name, sizeof(info.name)); + strncpy(info.name, i2c_clientname(client), sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -143,7 +140,7 @@ case MIXER_READ(SOUND_MIXER_VOLUME): left = (min(65536 - va.balance,32768) * va.volume) / 32768; - right = (min(va.balance,32768) * + right = (min(va.balance,(u16)32768) * va.volume) / 32768; ret = v4l_to_mix2(left,right); break; @@ -218,8 +215,8 @@ .name = "tv card mixer driver", .id = I2C_DRIVERID_TVMIXER, .flags = I2C_DF_NOTIFY, - .attach_adapter = tvmixer_adapters, .detach_adapter = tvmixer_adapters, + .attach_adapter = tvmixer_adapters, .detach_client = tvmixer_clients, }; @@ -238,9 +235,6 @@ struct list_head *item; struct i2c_client *client; - if (debug) - printk("tvmixer: adapter %s\n",adap->dev.name); - list_for_each(item,&adap->clients) { client = list_entry(item, struct i2c_client, list); tvmixer_clients(client); @@ -253,16 +247,8 @@ struct video_audio va; int i,minor; - /* TV card ??? */ - if (!(client->adapter->class & I2C_ADAP_CLASS_TV_ANALOG)) { - /* ignore that one */ - if (debug) - printk("tvmixer: %s is not a tv card\n", - client->adapter->dev.name); + if (!(client->adapter->class & I2C_ADAP_CLASS_TV_ANALOG)) return -1; - } - if (debug) - printk("tvmixer: debug: %s\n",client->dev.name); /* unregister ?? */ for (i = 0; i < DEV_MAX; i++) { @@ -271,7 +257,8 @@ unregister_sound_mixer(devices[i].minor); devices[i].dev = NULL; devices[i].minor = -1; - printk("tvmixer: %s unregistered (#1)\n",client->dev.name); + printk("tvmixer: %s unregistered (#1)\n", + i2c_clientname(client)); return 0; } } @@ -286,25 +273,13 @@ } /* audio chip with mixer ??? */ - if (NULL == client->driver->command) { - if (debug) - printk("tvmixer: %s: driver->command is NULL\n", - client->driver->name); + if (NULL == client->driver->command) return -1; - } memset(&va,0,sizeof(va)); - if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) { - if (debug) - printk("tvmixer: %s: VIDIOCGAUDIO failed\n", - client->dev.name); + if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) return -1; - } - if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) { - if (debug) - printk("tvmixer: %s: has no volume control\n", - client->dev.name); + if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) return -1; - } /* everything is fine, register */ if ((minor = register_sound_mixer(&tvmixer_fops,devnr)) < 0) { @@ -342,7 +317,7 @@ if (devices[i].minor != -1) { unregister_sound_mixer(devices[i].minor); printk("tvmixer: %s unregistered (#2)\n", - devices[i].dev->dev.name); + i2c_clientname(devices[i].dev)); } } } diff -urN linux-2.5.69-bk13/drivers/media/video/v4l1-compat.c linux-2.5.69-bk14/drivers/media/video/v4l1-compat.c --- linux-2.5.69-bk13/drivers/media/video/v4l1-compat.c 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/v4l1-compat.c 2003-05-20 04:46:43.000000000 -0700 @@ -127,6 +127,8 @@ return 0; } +/* ----------------------------------------------------------------- */ + static int palette2pixelformat[] = { [VIDEO_PALETTE_GREY] = V4L2_PIX_FMT_GREY, [VIDEO_PALETTE_RGB555] = V4L2_PIX_FMT_RGB555, @@ -145,16 +147,16 @@ [VIDEO_PALETTE_YUV422P] = V4L2_PIX_FMT_YUV422P, }; -static int -palette_to_pixelformat(int palette) +static unsigned int +palette_to_pixelformat(unsigned int palette) { - if (palette < sizeof(palette2pixelformat)/sizeof(int)) + if (palette < ARRAY_SIZE(palette2pixelformat)) return palette2pixelformat[palette]; else return 0; } -static int +static unsigned int pixelformat_to_palette(int pixelformat) { int palette = 0; @@ -199,66 +201,43 @@ return palette; } -/* Do an 'in' (wait for input) select on a single file descriptor */ -/* This stuff plaigarized from linux/fs/select.c */ -#define __FD_IN(fds, n) (fds->in + n) -#define BIT(i) (1UL << ((i)&(__NFDBITS-1))) -#define SET(i,m) (*(m) |= (i)) -extern int do_select(int n, fd_set_bits *fds, long *timeout); - +/* ----------------------------------------------------------------- */ -static int -simple_select(struct file *file) +static int poll_one(struct file *file) { - fd_set_bits fds; - char *bits; - long timeout; - int i, fd, n, ret, size; - - for (i = 0; i < current->files->max_fds; ++i) - if (file == current->files->fd[i]) - break; - if (i == current->files->max_fds) - return -EINVAL; - fd = i; - n = fd + 1; - - timeout = MAX_SCHEDULE_TIMEOUT; - /* - * We need 6 bitmaps (in/out/ex for both incoming and outgoing), - * since we used fdset we need to allocate memory in units of - * long-words. - */ - ret = -ENOMEM; - size = FDS_BYTES(n); - bits = kmalloc(6 * size, GFP_KERNEL); - if (!bits) - goto out_nofds; - fds.in = (unsigned long *) bits; - fds.out = (unsigned long *) (bits + size); - fds.ex = (unsigned long *) (bits + 2*size); - fds.res_in = (unsigned long *) (bits + 3*size); - fds.res_out = (unsigned long *) (bits + 4*size); - fds.res_ex = (unsigned long *) (bits + 5*size); - - /* All zero except our one file descriptor bit, for input */ - memset(bits, 0, 6 * size); - SET(BIT(fd), __FD_IN((&fds), fd / __NFDBITS)); - - ret = do_select(n, &fds, &timeout); - - if (ret < 0) - goto out; - if (!ret) { - ret = -ERESTARTNOHAND; - if (signal_pending(current)) - goto out; - ret = 0; - } -out: - kfree(bits); -out_nofds: - return ret; + int retval = 1; + poll_table *table; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) + poll_table wait_table; + + poll_initwait(&wait_table); + table = &wait_table; +#else + struct poll_wqueues pwq; + + poll_initwait(&pwq); + table = &pwq.pt; +#endif + for (;;) { + int mask; + set_current_state(TASK_INTERRUPTIBLE); + mask = file->f_op->poll(file, table); + if (mask & POLLIN) + break; + table = NULL; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) + poll_freewait(&wait_table); +#else + poll_freewait(&pwq); +#endif + return retval; } static int count_inputs(struct inode *inode, @@ -306,6 +285,7 @@ return 0; } +/* ----------------------------------------------------------------- */ /* * This function is exported. @@ -317,26 +297,36 @@ void *arg, v4l2_kioctl drv) { - int err = -ENOIOCTLCMD; + struct v4l2_capability *cap2 = NULL; + struct v4l2_format *fmt2 = NULL; - switch (cmd) - { + struct v4l2_framebuffer fbuf2; + struct v4l2_input input2; + struct v4l2_tuner tun2; + struct v4l2_standard std2; + struct v4l2_frequency freq2; + struct v4l2_audio aud2; + struct v4l2_queryctrl qctrl2; + struct v4l2_buffer buf2; + v4l2_std_id sid; + int i, err = 0; + + switch (cmd) { case VIDIOCGCAP: /* capability */ { struct video_capability *cap = arg; - struct v4l2_capability cap2; - struct v4l2_framebuffer fbuf2; - + + cap2 = kmalloc(sizeof(*cap2),GFP_KERNEL); memset(cap, 0, sizeof(*cap)); - memset(&cap2, 0, sizeof(cap2)); + memset(cap2, 0, sizeof(*cap2)); memset(&fbuf2, 0, sizeof(fbuf2)); - err = drv(inode, file, VIDIOC_QUERYCAP, &cap2); + err = drv(inode, file, VIDIOC_QUERYCAP, cap2); if (err < 0) { dprintk("VIDIOCGCAP / VIDIOC_QUERYCAP: %d\n",err); break; } - if (cap2.capabilities & V4L2_CAP_VIDEO_OVERLAY) { + if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) { err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); if (err < 0) { dprintk("VIDIOCGCAP / VIDIOC_G_FBUF: %d\n",err); @@ -345,16 +335,16 @@ err = 0; } - memcpy(cap->name, cap2.card, - min(sizeof(cap->name), sizeof(cap2.card))); + memcpy(cap->name, cap2->card, + min(sizeof(cap->name), sizeof(cap2->card))); cap->name[sizeof(cap->name) - 1] = 0; - if (cap2.capabilities & V4L2_CAP_VIDEO_CAPTURE) + if (cap2->capabilities & V4L2_CAP_VIDEO_CAPTURE) cap->type |= VID_TYPE_CAPTURE; - if (cap2.capabilities & V4L2_CAP_TUNER) + if (cap2->capabilities & V4L2_CAP_TUNER) cap->type |= VID_TYPE_TUNER; - if (cap2.capabilities & V4L2_CAP_VBI_CAPTURE) + if (cap2->capabilities & V4L2_CAP_VBI_CAPTURE) cap->type |= VID_TYPE_TELETEXT; - if (cap2.capabilities & V4L2_CAP_VIDEO_OVERLAY) + if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) cap->type |= VID_TYPE_OVERLAY; if (fbuf2.capability & V4L2_FBUF_CAP_LIST_CLIPPING) cap->type |= VID_TYPE_CLIPPING; @@ -370,7 +360,6 @@ case VIDIOCGFBUF: /* get frame buffer */ { struct video_buffer *buffer = arg; - struct v4l2_framebuffer fbuf2; err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); if (err < 0) { @@ -412,7 +401,6 @@ case VIDIOCSFBUF: /* set frame buffer */ { struct video_buffer *buffer = arg; - struct v4l2_framebuffer fbuf2; memset(&fbuf2, 0, sizeof(fbuf2)); fbuf2.base = buffer->base; @@ -444,36 +432,36 @@ case VIDIOCGWIN: /* get window or capture dimensions */ { struct video_window *win = arg; - struct v4l2_format fmt2; + fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); memset(win,0,sizeof(*win)); - memset(&fmt2,0,sizeof(fmt2)); + memset(fmt2,0,sizeof(*fmt2)); - fmt2.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; - err = drv(inode, file, VIDIOC_G_FMT, &fmt2); + fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) dprintk("VIDIOCGWIN / VIDIOC_G_WIN: %d\n",err); if (err == 0) { - win->x = fmt2.fmt.win.w.left; - win->y = fmt2.fmt.win.w.top; - win->width = fmt2.fmt.win.w.width; - win->height = fmt2.fmt.win.w.height; - win->chromakey = fmt2.fmt.win.chromakey; + win->x = fmt2->fmt.win.w.left; + win->y = fmt2->fmt.win.w.top; + win->width = fmt2->fmt.win.w.width; + win->height = fmt2->fmt.win.w.height; + win->chromakey = fmt2->fmt.win.chromakey; win->clips = NULL; win->clipcount = 0; break; } - fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, &fmt2); + fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) { dprintk("VIDIOCGWIN / VIDIOC_G_FMT: %d\n",err); break; } win->x = 0; win->y = 0; - win->width = fmt2.fmt.pix.width; - win->height = fmt2.fmt.pix.height; + win->width = fmt2->fmt.pix.width; + win->height = fmt2->fmt.pix.height; win->chromakey = 0; win->clips = NULL; win->clipcount = 0; @@ -482,37 +470,41 @@ case VIDIOCSWIN: /* set window and/or capture dimensions */ { struct video_window *win = arg; - struct v4l2_format fmt2; + int err1,err2; - memset(&fmt2,0,sizeof(fmt2)); - fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, &fmt2); - if (err < 0) + fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); + memset(fmt2,0,sizeof(*fmt2)); + fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err1 = drv(inode, file, VIDIOC_G_FMT, fmt2); + if (err1 < 0) dprintk("VIDIOCSWIN / VIDIOC_G_FMT: %d\n",err); - if (err == 0) { - fmt2.fmt.pix.width = win->width; - fmt2.fmt.pix.height = win->height; - fmt2.fmt.pix.field = V4L2_FIELD_ANY; - err = drv(inode, file, VIDIOC_S_FMT, &fmt2); + if (err1 == 0) { + fmt2->fmt.pix.width = win->width; + fmt2->fmt.pix.height = win->height; + fmt2->fmt.pix.field = V4L2_FIELD_ANY; + err = drv(inode, file, VIDIOC_S_FMT, fmt2); if (err < 0) dprintk("VIDIOCSWIN / VIDIOC_S_FMT #1: %d\n", err); - win->width = fmt2.fmt.pix.width; - win->height = fmt2.fmt.pix.height; + win->width = fmt2->fmt.pix.width; + win->height = fmt2->fmt.pix.height; } - memset(&fmt2,0,sizeof(fmt2)); - fmt2.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; - fmt2.fmt.win.w.left = win->x; - fmt2.fmt.win.w.top = win->y; - fmt2.fmt.win.w.width = win->width; - fmt2.fmt.win.w.height = win->height; - fmt2.fmt.win.chromakey = win->chromakey; - fmt2.fmt.win.clips = (void *)win->clips; - fmt2.fmt.win.clipcount = win->clipcount; - err = drv(inode, file, VIDIOC_S_FMT, &fmt2); - if (err < 0) + memset(fmt2,0,sizeof(*fmt2)); + fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + fmt2->fmt.win.w.left = win->x; + fmt2->fmt.win.w.top = win->y; + fmt2->fmt.win.w.width = win->width; + fmt2->fmt.win.w.height = win->height; + fmt2->fmt.win.chromakey = win->chromakey; + fmt2->fmt.win.clips = (void *)win->clips; + fmt2->fmt.win.clipcount = win->clipcount; + err2 = drv(inode, file, VIDIOC_S_FMT, fmt2); + if (err2 < 0) dprintk("VIDIOCSWIN / VIDIOC_S_FMT #2: %d\n",err); + + if (err1 != 0 && err2 != 0) + err = err1; break; } case VIDIOCCAPTURE: /* turn on/off preview */ @@ -525,8 +517,6 @@ case VIDIOCGCHAN: /* get input information */ { struct video_channel *chan = arg; - struct v4l2_input input2; - v4l2_std_id sid; memset(&input2,0,sizeof(input2)); input2.index = chan->channel; @@ -568,16 +558,32 @@ case VIDIOCSCHAN: /* set input */ { struct video_channel *chan = arg; - + + sid = 0; err = drv(inode, file, VIDIOC_S_INPUT, &chan->channel); if (err < 0) dprintk("VIDIOCSCHAN / VIDIOC_S_INPUT: %d\n",err); + switch (chan->norm) { + case VIDEO_MODE_PAL: + sid = V4L2_STD_PAL; + break; + case VIDEO_MODE_NTSC: + sid = V4L2_STD_NTSC; + break; + case VIDEO_MODE_SECAM: + sid = V4L2_STD_SECAM; + break; + } + if (0 != sid) { + err = drv(inode, file, VIDIOC_S_STD, &sid); + if (err < 0) + dprintk("VIDIOCSCHAN / VIDIOC_S_STD: %d\n",err); + } break; } case VIDIOCGPICT: /* get tone controls & partial capture format */ { struct video_picture *pict = arg; - struct v4l2_format fmt2; pict->brightness = get_v4l_control(inode, file, V4L2_CID_BRIGHTNESS,drv); @@ -590,25 +596,24 @@ pict->whiteness = get_v4l_control(inode, file, V4L2_CID_WHITENESS, drv); - memset(&fmt2,0,sizeof(fmt2)); - fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, &fmt2); + fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); + memset(fmt2,0,sizeof(*fmt2)); + fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) { dprintk("VIDIOCGPICT / VIDIOC_G_FMT: %d\n",err); break; } #if 0 /* FIXME */ - pict->depth = fmt2.fmt.pix.depth; + pict->depth = fmt2->fmt.pix.depth; #endif pict->palette = pixelformat_to_palette( - fmt2.fmt.pix.pixelformat); + fmt2->fmt.pix.pixelformat); break; } case VIDIOCSPICT: /* set tone controls & partial capture format */ { struct video_picture *pict = arg; - struct v4l2_format fmt2; - struct v4l2_framebuffer fbuf2; set_v4l_control(inode, file, V4L2_CID_BRIGHTNESS, pict->brightness, drv); @@ -621,16 +626,17 @@ set_v4l_control(inode, file, V4L2_CID_WHITENESS, pict->whiteness, drv); - memset(&fmt2,0,sizeof(fmt2)); - fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, &fmt2); + fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); + memset(fmt2,0,sizeof(*fmt2)); + fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) dprintk("VIDIOCSPICT / VIDIOC_G_FMT: %d\n",err); - if (fmt2.fmt.pix.pixelformat != + if (fmt2->fmt.pix.pixelformat != palette_to_pixelformat(pict->palette)) { - fmt2.fmt.pix.pixelformat = palette_to_pixelformat( + fmt2->fmt.pix.pixelformat = palette_to_pixelformat( pict->palette); - err = drv(inode, file, VIDIOC_S_FMT, &fmt2); + err = drv(inode, file, VIDIOC_S_FMT, fmt2); if (err < 0) dprintk("VIDIOCSPICT / VIDIOC_S_FMT: %d\n",err); } @@ -652,8 +658,6 @@ case VIDIOCGTUNER: /* get tuner information */ { struct video_tuner *tun = arg; - struct v4l2_tuner tun2; - v4l2_std_id sid; memset(&tun2,0,sizeof(tun2)); err = drv(inode, file, VIDIOC_G_TUNER, &tun2); @@ -669,6 +673,19 @@ tun->flags = 0; tun->mode = VIDEO_MODE_AUTO; + for (i = 0; i < 64; i++) { + memset(&std2,0,sizeof(std2)); + std2.index = i; + if (0 != drv(inode, file, VIDIOC_ENUMSTD, &std2)) + break; + if (std2.id & V4L2_STD_PAL) + tun->flags |= VIDEO_TUNER_PAL; + if (std2.id & V4L2_STD_NTSC) + tun->flags |= VIDEO_TUNER_NTSC; + if (std2.id & V4L2_STD_SECAM) + tun->flags |= VIDEO_TUNER_SECAM; + } + err = drv(inode, file, VIDIOC_G_STD, &sid); if (err < 0) dprintk("VIDIOCGTUNER / VIDIOC_G_STD: %d\n",err); @@ -688,21 +705,20 @@ tun->signal = tun2.signal; break; } -#if 0 /* FIXME */ case VIDIOCSTUNER: /* select a tuner input */ { - int i; - +#if 0 /* FIXME */ err = drv(inode, file, VIDIOC_S_INPUT, &i); if (err < 0) dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err); +#else + err = 0; +#endif break; } -#endif case VIDIOCGFREQ: /* get frequency */ { int *freq = arg; - struct v4l2_frequency freq2; err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); if (err < 0) @@ -714,7 +730,6 @@ case VIDIOCSFREQ: /* set frequency */ { int *freq = arg; - struct v4l2_frequency freq2; drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); freq2.frequency = *freq; @@ -726,10 +741,6 @@ case VIDIOCGAUDIO: /* get audio properties/controls */ { struct video_audio *aud = arg; - struct v4l2_audio aud2; - struct v4l2_queryctrl qctrl2; - struct v4l2_tuner tun2; - int v; err = drv(inode, file, VIDIOC_G_AUDIO, &aud2); if (err < 0) { @@ -741,34 +752,29 @@ aud->name[sizeof(aud->name) - 1] = 0; aud->audio = aud2.index; aud->flags = 0; - v = get_v4l_control(inode, file, V4L2_CID_AUDIO_VOLUME, drv); - if (v >= 0) - { - aud->volume = v; + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_VOLUME, drv); + if (i >= 0) { + aud->volume = i; aud->flags |= VIDEO_AUDIO_VOLUME; } - v = get_v4l_control(inode, file, V4L2_CID_AUDIO_BASS, drv); - if (v >= 0) - { - aud->bass = v; + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_BASS, drv); + if (i >= 0) { + aud->bass = i; aud->flags |= VIDEO_AUDIO_BASS; } - v = get_v4l_control(inode, file, V4L2_CID_AUDIO_TREBLE, drv); - if (v >= 0) - { - aud->treble = v; + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_TREBLE, drv); + if (i >= 0) { + aud->treble = i; aud->flags |= VIDEO_AUDIO_TREBLE; } - v = get_v4l_control(inode, file, V4L2_CID_AUDIO_BALANCE, drv); - if (v >= 0) - { - aud->balance = v; + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_BALANCE, drv); + if (i >= 0) { + aud->balance = i; aud->flags |= VIDEO_AUDIO_BALANCE; } - v = get_v4l_control(inode, file, V4L2_CID_AUDIO_MUTE, drv); - if (v >= 0) - { - if (v) + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_MUTE, drv); + if (i >= 0) { + if (i) aud->flags |= VIDEO_AUDIO_MUTE; aud->flags |= VIDEO_AUDIO_MUTABLE; } @@ -795,8 +801,6 @@ case VIDIOCSAUDIO: /* set audio controls */ { struct video_audio *aud = arg; - struct v4l2_audio aud2; - struct v4l2_tuner tun2; memset(&aud2,0,sizeof(aud2)); memset(&tun2,0,sizeof(tun2)); @@ -844,83 +848,36 @@ break; } #if 0 - case VIDIOCGMBUF: /* get mmap parameters */ - { - struct video_mbuf *mbuf = arg; - struct v4l2_requestbuffers reqbuf2; - struct v4l2_buffer buf2; - struct v4l2_format fmt2, fmt2o; - struct v4l2_capability cap2; - int i; - - /* Set the format to maximum dimensions */ - if ((err = drv(inode, file, VIDIOC_QUERYCAP, &cap2)) < 0) - break; - fmt2o.type = V4L2_BUF_TYPE_CAPTURE; - if ((err = drv(inode, file, VIDIOC_G_FMT, &fmt2o)) < 0) - break; - fmt2 = fmt2o; - fmt2.fmt.pix.width = cap2.maxwidth; - fmt2.fmt.pix.height = cap2.maxheight; - fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; - if ((err = drv(inode, file, VIDIOC_S_FMT, &fmt2)) < 0) - break; - reqbuf2.count = 2; /* v4l always used two buffers */ - reqbuf2.type = V4L2_BUF_TYPE_CAPTURE | V4L2_BUF_REQ_CONTIG; - err = drv(inode, file, VIDIOC_REQBUFS, &reqbuf2); - if (err < 0 || reqbuf2.count < 2 || reqbuf2.type - != (V4L2_BUF_TYPE_CAPTURE | V4L2_BUF_REQ_CONTIG)) - {/* Driver doesn't support v4l back-compatibility */ - fmt2o.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; - drv(inode, file, VIDIOC_S_FMT, &fmt2o); - reqbuf2.count = 1; - reqbuf2.type = V4L2_BUF_TYPE_CAPTURE; - err = drv(inode, file, VIDIOC_REQBUFS, &reqbuf2); - if (err < 0) - { - err = -EINVAL; - break; - } - printk(KERN_INFO"V4L2: Device \"%s\" doesn't support" - " v4l memory mapping\n", vfl->name); - } - buf2.index = 0; - buf2.type = V4L2_BUF_TYPE_CAPTURE; - err = drv(inode, file, VIDIOC_QUERYBUF, &buf2); - mbuf->size = buf2.length * reqbuf2.count; - mbuf->frames = reqbuf2.count; - memset(mbuf->offsets, 0, sizeof(mbuf->offsets)); - for (i = 0; i < mbuf->frames; ++i) - mbuf->offsets[i] = i * buf2.length; - break; - } + case VIDIOCGMBUF: + /* v4l2 drivers must implement that themself. The + mmap() differences can't be translated fully + transparent, thus there is no point to try that */ #endif case VIDIOCMCAPTURE: /* capture a frame */ { struct video_mmap *mm = arg; - struct v4l2_buffer buf2; - struct v4l2_format fmt2; + fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); memset(&buf2,0,sizeof(buf2)); - memset(&fmt2,0,sizeof(fmt2)); + memset(fmt2,0,sizeof(*fmt2)); - fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, &fmt2); + fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) { dprintk("VIDIOCMCAPTURE / VIDIOC_G_FMT: %d\n",err); break; } - if (mm->width != fmt2.fmt.pix.width || - mm->height != fmt2.fmt.pix.height || + if (mm->width != fmt2->fmt.pix.width || + mm->height != fmt2->fmt.pix.height || palette_to_pixelformat(mm->format) != - fmt2.fmt.pix.pixelformat) + fmt2->fmt.pix.pixelformat) {/* New capture format... */ - fmt2.fmt.pix.width = mm->width; - fmt2.fmt.pix.height = mm->height; - fmt2.fmt.pix.pixelformat = + fmt2->fmt.pix.width = mm->width; + fmt2->fmt.pix.height = mm->height; + fmt2->fmt.pix.pixelformat = palette_to_pixelformat(mm->format); - fmt2.fmt.pix.field = V4L2_FIELD_ANY; - err = drv(inode, file, VIDIOC_S_FMT, &fmt2); + fmt2->fmt.pix.field = V4L2_FIELD_ANY; + err = drv(inode, file, VIDIOC_S_FMT, fmt2); if (err < 0) { dprintk("VIDIOCMCAPTURE / VIDIOC_S_FMT: %d\n",err); break; @@ -946,7 +903,6 @@ case VIDIOCSYNC: /* wait for a frame */ { int *i = arg; - struct v4l2_buffer buf2; buf2.index = *i; buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -967,7 +923,7 @@ (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) == V4L2_BUF_FLAG_QUEUED) { - err = simple_select(file); + err = poll_one(file); if (err < 0 || /* error or sleep was interrupted */ err == 0) /* timeout? Shouldn't occur. */ break; @@ -984,20 +940,80 @@ } while (err == 0 && buf2.index != *i); break; } - case VIDIOCGUNIT: /* get related device minors */ - /* No translation */ - break; - case VIDIOCGCAPTURE: /* */ - /* No translation, yet... */ - printk(KERN_INFO"v4l1-compat: VIDIOCGCAPTURE not implemented." - " Send patches to bdirks@pacbell.net :-)\n"); - break; - case VIDIOCSCAPTURE: /* */ - /* No translation, yet... */ - printk(KERN_INFO"v4l1-compat: VIDIOCSCAPTURE not implemented." - " Send patches to bdirks@pacbell.net :-)\n"); + + case VIDIOCGVBIFMT: /* query VBI data capture format */ + { + struct vbi_format *fmt = arg; + + fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); + memset(fmt2, 0, sizeof(*fmt2)); + fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; + + err = drv(inode, file, VIDIOC_G_FMT, fmt2); + if (err < 0) { + dprintk("VIDIOCGVBIFMT / VIDIOC_G_FMT: %d\n", err); + break; + } + memset(fmt, 0, sizeof(*fmt)); + fmt->samples_per_line = fmt2->fmt.vbi.samples_per_line; + fmt->sampling_rate = fmt2->fmt.vbi.sampling_rate; + fmt->sample_format = VIDEO_PALETTE_RAW; + fmt->start[0] = fmt2->fmt.vbi.start[0]; + fmt->count[0] = fmt2->fmt.vbi.count[0]; + fmt->start[1] = fmt2->fmt.vbi.start[1]; + fmt->count[1] = fmt2->fmt.vbi.count[1]; + fmt->flags = fmt2->fmt.vbi.flags & 0x03; + break; + } + case VIDIOCSVBIFMT: + { + struct vbi_format *fmt = arg; + + fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); + memset(fmt2, 0, sizeof(*fmt2)); + + fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; + fmt2->fmt.vbi.samples_per_line = fmt->samples_per_line; + fmt2->fmt.vbi.sampling_rate = fmt->sampling_rate; + fmt2->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + fmt2->fmt.vbi.start[0] = fmt->start[0]; + fmt2->fmt.vbi.count[0] = fmt->count[0]; + fmt2->fmt.vbi.start[1] = fmt->start[1]; + fmt2->fmt.vbi.count[1] = fmt->count[1]; + fmt2->fmt.vbi.flags = fmt->flags; + err = drv(inode, file, VIDIOC_TRY_FMT, fmt2); + if (err < 0) { + dprintk("VIDIOCSVBIFMT / VIDIOC_TRY_FMT: %d\n", err); + break; + } + + if (fmt2->fmt.vbi.samples_per_line != fmt->samples_per_line || + fmt2->fmt.vbi.sampling_rate != fmt->sampling_rate || + fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY || + fmt2->fmt.vbi.start[0] != fmt->start[0] || + fmt2->fmt.vbi.count[0] != fmt->count[0] || + fmt2->fmt.vbi.start[1] != fmt->start[1] || + fmt2->fmt.vbi.count[1] != fmt->count[1] || + fmt2->fmt.vbi.flags != fmt->flags) { + err = -EINVAL; + break; + } + err = drv(inode, file, VIDIOC_S_FMT, fmt2); + if (err < 0) { + dprintk("VIDIOCSVBIFMT / VIDIOC_S_FMT: %d\n", err); + break; + } + } + + default: + err = -ENOIOCTLCMD; break; } + + if (cap2) + kfree(cap2); + if (fmt2) + kfree(fmt2); return err; } diff -urN linux-2.5.69-bk13/drivers/media/video/video-buf.c linux-2.5.69-bk14/drivers/media/video/video-buf.c --- linux-2.5.69-bk13/drivers/media/video/video-buf.c 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/drivers/media/video/video-buf.c 2003-05-20 04:46:43.000000000 -0700 @@ -362,21 +362,33 @@ { int i; - if (q->reading) + if (q->streaming) { + dprintk(1,"busy: streaming active\n"); return 1; - if (q->streaming) + } + if (q->reading) { + dprintk(1,"busy: pending read #1\n"); return 1; - if (q->read_buf) + } + if (q->read_buf) { + dprintk(1,"busy: pending read #2\n"); return 1; + } for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; - if (q->bufs[i]->map) + if (q->bufs[i]->map) { + dprintk(1,"busy: buffer #%d mapped\n",i); return 1; - if (q->bufs[i]->state == STATE_QUEUED) + } + if (q->bufs[i]->state == STATE_QUEUED) { + dprintk(1,"busy: buffer #%d queued\n",i); return 1; - if (q->bufs[i]->state == STATE_ACTIVE) + } + if (q->bufs[i]->state == STATE_ACTIVE) { + dprintk(1,"busy: buffer #%d avtive\n",i); return 1; + } } return 0; } @@ -569,7 +581,7 @@ if (list_empty(&q->stream)) goto done; buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - retval = videobuf_waiton(buf,1,1); + retval = videobuf_waiton(buf, file->f_flags & O_NONBLOCK, 1); if (retval < 0) goto done; switch (buf->state) { @@ -925,6 +937,9 @@ videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; + + dprintk(2,"vm_open %p [count=%d,vma=%08lx-%08lx]\n",map, + map->count,vma->vm_start,vma->vm_end); map->count++; } @@ -934,6 +949,9 @@ struct videobuf_mapping *map = vma->vm_private_data; int i; + dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]\n",map, + map->count,vma->vm_start,vma->vm_end); + /* down(&fh->lock); FIXME */ map->count--; if (0 == map->count) { @@ -1081,11 +1099,11 @@ q->bufs[i]->map = map; q->bufs[i]->baddr = vma->vm_start + size; } - map->count = 1; - map->start = vma->vm_start; - map->end = vma->vm_end; - map->q = q; - vma->vm_ops = &videobuf_vm_ops; + map->count = 1; + map->start = vma->vm_start; + map->end = vma->vm_end; + map->q = q; + vma->vm_ops = &videobuf_vm_ops; vma->vm_flags |= VM_DONTEXPAND; vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ vma->vm_private_data = map; @@ -1119,6 +1137,7 @@ EXPORT_SYMBOL_GPL(videobuf_queue_cancel); EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); +EXPORT_SYMBOL_GPL(videobuf_next_field); EXPORT_SYMBOL_GPL(videobuf_status); EXPORT_SYMBOL_GPL(videobuf_reqbufs); EXPORT_SYMBOL_GPL(videobuf_querybuf); diff -urN linux-2.5.69-bk13/drivers/net/3c59x.c linux-2.5.69-bk14/drivers/net/3c59x.c --- linux-2.5.69-bk13/drivers/net/3c59x.c 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/3c59x.c 2003-05-20 04:46:44.000000000 -0700 @@ -466,6 +466,9 @@ CH_3C920, CH_3C982A, CH_3C982B, + + CH_905BT4, + CH_920B_EMB_WNM, }; @@ -565,6 +568,11 @@ {"3c982 Hydra Dual Port B", PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_HWCKSM|HAS_NWAY, 128, }, + {"3c905B-T4", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, }, + {"3c920B-EMB-WNM Tornado", + PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, }, + {0,}, /* 0 terminated list. */ }; @@ -611,6 +619,10 @@ { 0x10B7, 0x9201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C920 }, { 0x10B7, 0x1201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C982A }, { 0x10B7, 0x1202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C982B }, + + { 0x10B7, 0x9056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_905BT4 }, + { 0x10B7, 0x9210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_920B_EMB_WNM }, + {0,} /* 0 terminated list. */ }; MODULE_DEVICE_TABLE(pci, vortex_pci_tbl); @@ -2321,7 +2333,6 @@ long ioaddr; int status; int work_done = max_interrupt_work; - int handled; ioaddr = dev->base_addr; @@ -2336,18 +2347,14 @@ if (vortex_debug > 6) printk(KERN_DEBUG "boomerang_interrupt. status=0x%4x\n", status); - if ((status & IntLatch) == 0) { - handled = 0; + if ((status & IntLatch) == 0) goto handler_exit; /* No interrupt: shared IRQs can cause this */ - } if (status == 0xffff) { /* h/w no longer present (hotplug)? */ if (vortex_debug > 1) printk(KERN_DEBUG "boomerang_interrupt(1): status = 0xffff\n"); - handled = 0; goto handler_exit; } - handled = 1; if (status & IntReq) { status |= vp->deferred; @@ -2442,7 +2449,7 @@ dev->name, status); handler_exit: spin_unlock(&vp->lock); - return IRQ_RETVAL(handled); + return IRQ_HANDLED; } static int vortex_rx(struct net_device *dev) diff -urN linux-2.5.69-bk13/drivers/net/Kconfig linux-2.5.69-bk14/drivers/net/Kconfig --- linux-2.5.69-bk13/drivers/net/Kconfig 2003-05-20 04:46:35.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/Kconfig 2003-05-20 04:46:44.000000000 -0700 @@ -221,14 +221,7 @@ or internal device. It is safe to say Y or M here even if your ethernet card lack MII. -config ARM_AM79C961A - bool "ARM EBSA110 AM79C961A support" - depends on NET_ETHERNET && ARM && ARCH_EBSA110 - help - If you wish to compile a kernel for the EBSA-110, then you should - always answer Y to this. - -source "drivers/acorn/net/Kconfig" +source "drivers/net/arm/Kconfig" config MACE tristate "MACE (Power Mac ethernet) support" diff -urN linux-2.5.69-bk13/drivers/net/Makefile linux-2.5.69-bk14/drivers/net/Makefile --- linux-2.5.69-bk13/drivers/net/Makefile 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/Makefile 2003-05-20 04:46:44.000000000 -0700 @@ -77,7 +77,6 @@ obj-$(CONFIG_SMC9194) += smc9194.o obj-$(CONFIG_FEC) += fec.o obj-$(CONFIG_68360_ENET) += 68360enet.o -obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o obj-$(CONFIG_ARM_ETHERH) += 8390.o obj-$(CONFIG_WD80x3) += wd.o 8390.o obj-$(CONFIG_EL2) += 3c503.o 8390.o @@ -179,7 +178,7 @@ # non-drivers/net drivers who want mii lib obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o -obj-$(CONFIG_ARCH_ACORN) += ../acorn/net/ +obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_NET_FC) += fc/ obj-$(CONFIG_DEV_APPLETALK) += appletalk/ obj-$(CONFIG_TR) += tokenring/ diff -urN linux-2.5.69-bk13/drivers/net/am79c961a.c linux-2.5.69-bk14/drivers/net/am79c961a.c --- linux-2.5.69-bk13/drivers/net/am79c961a.c 2003-05-04 16:53:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/am79c961a.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,685 +0,0 @@ -/* - * linux/drivers/net/am79c961.c - * - * by Russell King 1995-2001. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from various things including skeleton.c - * - * This is a special driver for the am79c961A Lance chip used in the - * Intel (formally Digital Equipment Corp) EBSA110 platform. Please - * note that this can not be built as a module (it doesn't make sense). - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define TX_BUFFERS 15 -#define RX_BUFFERS 25 - -#include "am79c961a.h" - -static irqreturn_t -am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs); - -static unsigned int net_debug = NET_DEBUG; - -static const char version[] = - "am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n"; - -/* --------------------------------------------------------------------------- */ - -#ifdef __arm__ -static void write_rreg(u_long base, u_int reg, u_int val) -{ - __asm__("str%?h %1, [%2] @ NET_RAP - str%?h %0, [%2, #-4] @ NET_RDP - " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); -} - -static inline unsigned short read_rreg(u_long base_addr, u_int reg) -{ - unsigned short v; - __asm__("str%?h %1, [%2] @ NET_RAP - ldr%?h %0, [%2, #-4] @ NET_RDP - " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); - return v; -} - -static inline void write_ireg(u_long base, u_int reg, u_int val) -{ - __asm__("str%?h %1, [%2] @ NET_RAP - str%?h %0, [%2, #8] @ NET_IDP - " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); -} - -#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1)) -#define am_readword(dev,off) __raw_readw(ISAMEM_BASE + ((off) << 1)) - -static inline void -am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) -{ - offset = ISAMEM_BASE + (offset << 1); - length = (length + 1) & ~1; - if ((int)buf & 2) { - __asm__ __volatile__("str%?h %2, [%0], #4" - : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); - buf += 2; - length -= 2; - } - while (length > 8) { - unsigned int tmp, tmp2; - __asm__ __volatile__(" - ldm%?ia %1!, {%2, %3} - str%?h %2, [%0], #4 - mov%? %2, %2, lsr #16 - str%?h %2, [%0], #4 - str%?h %3, [%0], #4 - mov%? %3, %3, lsr #16 - str%?h %3, [%0], #4 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) - : "0" (offset), "1" (buf)); - length -= 8; - } - while (length > 0) { - __asm__ __volatile__("str%?h %2, [%0], #4" - : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); - buf += 2; - length -= 2; - } -} - -static inline void -am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) -{ - offset = ISAMEM_BASE + (offset << 1); - length = (length + 1) & ~1; - if ((int)buf & 2) { - unsigned int tmp; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - str%?b %2, [%1], #1 - mov%? %2, %2, lsr #8 - str%?b %2, [%1], #1 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); - length -= 2; - } - while (length > 8) { - unsigned int tmp, tmp2, tmp3; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - ldr%?h %3, [%0], #4 - orr%? %2, %2, %3, lsl #16 - ldr%?h %3, [%0], #4 - ldr%?h %4, [%0], #4 - orr%? %3, %3, %4, lsl #16 - stm%?ia %1!, {%2, %3} - " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) - : "0" (offset), "1" (buf)); - length -= 8; - } - while (length > 0) { - unsigned int tmp; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - str%?b %2, [%1], #1 - mov%? %2, %2, lsr #8 - str%?b %2, [%1], #1 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); - length -= 2; - } -} -#else -#error Not compatible -#endif - -static int -am79c961_ramtest(struct net_device *dev, unsigned int val) -{ - unsigned char *buffer = kmalloc (65536, GFP_KERNEL); - int i, error = 0, errorcount = 0; - - if (!buffer) - return 0; - memset (buffer, val, 65536); - am_writebuffer(dev, 0, buffer, 65536); - memset (buffer, val ^ 255, 65536); - am_readbuffer(dev, 0, buffer, 65536); - for (i = 0; i < 65536; i++) { - if (buffer[i] != val && !error) { - printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i); - error = 1; - errorcount ++; - } else if (error && buffer[i] == val) { - printk ("%05X\n", i); - error = 0; - } - } - if (error) - printk ("10000\n"); - kfree (buffer); - return errorcount; -} - -static void -am79c961_init_for_open(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - unsigned char *p; - u_int hdr_addr, first_free_addr; - int i; - - /* - * Stop the chip. - */ - spin_lock_irqsave(priv->chip_lock, flags); - write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP); - spin_unlock_irqrestore(priv->chip_lock, flags); - - write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */ - write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */ - write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */ - write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */ - - for (i = LADRL; i <= LADRH; i++) - write_rreg (dev->base_addr, i, 0); - - for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2) - write_rreg (dev->base_addr, i, p[0] | (p[1] << 8)); - - i = MODE_PORT_10BT; - if (dev->flags & IFF_PROMISC) - i |= MODE_PROMISC; - - write_rreg (dev->base_addr, MODE, i); - write_rreg (dev->base_addr, POLLINT, 0); - write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS); - write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS); - - first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16; - hdr_addr = 0; - - priv->rxhead = 0; - priv->rxtail = 0; - priv->rxhdr = hdr_addr; - - for (i = 0; i < RX_BUFFERS; i++) { - priv->rxbuffer[i] = first_free_addr; - am_writeword (dev, hdr_addr, first_free_addr); - am_writeword (dev, hdr_addr + 2, RMD_OWN); - am_writeword (dev, hdr_addr + 4, (-1600)); - am_writeword (dev, hdr_addr + 6, 0); - first_free_addr += 1600; - hdr_addr += 8; - } - priv->txhead = 0; - priv->txtail = 0; - priv->txhdr = hdr_addr; - for (i = 0; i < TX_BUFFERS; i++) { - priv->txbuffer[i] = first_free_addr; - am_writeword (dev, hdr_addr, first_free_addr); - am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP); - am_writeword (dev, hdr_addr + 4, 0xf000); - am_writeword (dev, hdr_addr + 6, 0); - first_free_addr += 1600; - hdr_addr += 8; - } - - write_rreg (dev->base_addr, BASERXL, priv->rxhdr); - write_rreg (dev->base_addr, BASERXH, 0); - write_rreg (dev->base_addr, BASETXL, priv->txhdr); - write_rreg (dev->base_addr, BASERXH, 0); - write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO); - write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); -} - -/* - * Open/initialize the board. - */ -static int -am79c961_open(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int ret; - - memset (&priv->stats, 0, sizeof (priv->stats)); - - ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev); - if (ret) - return ret; - - am79c961_init_for_open(dev); - - netif_start_queue(dev); - - return 0; -} - -/* - * The inverse routine to am79c961_open(). - */ -static int -am79c961_close(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - - netif_stop_queue(dev); - - spin_lock_irqsave(priv->chip_lock, flags); - write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); - spin_unlock_irqrestore(priv->chip_lock, flags); - - free_irq (dev->irq, dev); - - return 0; -} - -/* - * Get the current statistics. - */ -static struct net_device_stats *am79c961_getstats (struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - return &priv->stats; -} - -static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash) -{ - if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) { - int idx, bit; - u32 crc; - - crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr); - - idx = crc >> 30; - bit = (crc >> 26) & 15; - - hash[idx] |= 1 << bit; - } -} - -/* - * Set or clear promiscuous/multicast mode filter for this adapter. - */ -static void am79c961_setmulticastlist (struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - unsigned short multi_hash[4], mode; - int i, stopped; - - mode = MODE_PORT_10BT; - - if (dev->flags & IFF_PROMISC) { - mode |= MODE_PROMISC; - } else if (dev->flags & IFF_ALLMULTI) { - memset(multi_hash, 0xff, sizeof(multi_hash)); - } else { - struct dev_mc_list *dmi; - - memset(multi_hash, 0x00, sizeof(multi_hash)); - - for (dmi = dev->mc_list; dmi; dmi = dmi->next) - am79c961_mc_hash(dmi, multi_hash); - } - - spin_lock_irqsave(priv->chip_lock, flags); - - stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; - - if (!stopped) { - /* - * Put the chip into suspend mode - */ - write_rreg(dev->base_addr, CTRL1, CTRL1_SPND); - - /* - * Spin waiting for chip to report suspend mode - */ - while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) { - spin_unlock_irqrestore(priv->chip_lock, flags); - nop(); - spin_lock_irqsave(priv->chip_lock, flags); - } - } - - /* - * Update the multicast hash table - */ - for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++) - write_rreg(dev->base_addr, i + LADRL, multi_hash[i]); - - /* - * Write the mode register - */ - write_rreg(dev->base_addr, MODE, mode); - - if (!stopped) { - /* - * Put the chip back into running mode - */ - write_rreg(dev->base_addr, CTRL1, 0); - } - - spin_unlock_irqrestore(priv->chip_lock, flags); -} - -static void am79c961_timeout(struct net_device *dev) -{ - printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n", - dev->name); - - /* - * ought to do some setup of the tx side here - */ - - netif_wake_queue(dev); -} - -/* - * Transmit a packet - */ -static int -am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned int length = skb->len; - unsigned int hdraddr, bufaddr; - unsigned int head; - unsigned long flags; - - /* FIXME: I thought the 79c961 could do padding - RMK ??? */ - if (length < ETH_ZLEN) { - skb = skb_padto(skb, ETH_ZLEN); - if (skb == NULL) - return 0; - length = ETH_ZLEN; - } - - head = priv->txhead; - hdraddr = priv->txhdr + (head << 3); - bufaddr = priv->txbuffer[head]; - head += 1; - if (head >= TX_BUFFERS) - head = 0; - - am_writebuffer (dev, bufaddr, skb->data, length); - am_writeword (dev, hdraddr + 4, -length); - am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); - priv->txhead = head; - - spin_lock_irqsave(priv->chip_lock, flags); - write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA); - dev->trans_start = jiffies; - spin_unlock_irqrestore(priv->chip_lock, flags); - - /* - * If the next packet is owned by the ethernet device, - * then the tx ring is full and we can't add another - * packet. - */ - if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN) - netif_stop_queue(dev); - - dev_kfree_skb(skb); - - return 0; -} - -/* - * If we have a good packet(s), get it/them out of the buffers. - */ -static void -am79c961_rx(struct net_device *dev, struct dev_priv *priv) -{ - do { - struct sk_buff *skb; - u_int hdraddr; - u_int pktaddr; - u_int status; - int len; - - hdraddr = priv->rxhdr + (priv->rxtail << 3); - pktaddr = priv->rxbuffer[priv->rxtail]; - - status = am_readword (dev, hdraddr + 2); - if (status & RMD_OWN) /* do we own it? */ - break; - - priv->rxtail ++; - if (priv->rxtail >= RX_BUFFERS) - priv->rxtail = 0; - - if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) { - am_writeword (dev, hdraddr + 2, RMD_OWN); - priv->stats.rx_errors ++; - if (status & RMD_ERR) { - if (status & RMD_FRAM) - priv->stats.rx_frame_errors ++; - if (status & RMD_CRC) - priv->stats.rx_crc_errors ++; - } else if (status & RMD_STP) - priv->stats.rx_length_errors ++; - continue; - } - - len = am_readword(dev, hdraddr + 6); - skb = dev_alloc_skb(len + 2); - - if (skb) { - skb->dev = dev; - skb_reserve(skb, 2); - - am_readbuffer(dev, pktaddr, skb_put(skb, len), len); - am_writeword(dev, hdraddr + 2, RMD_OWN); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - priv->stats.rx_bytes += len; - priv->stats.rx_packets ++; - } else { - am_writeword (dev, hdraddr + 2, RMD_OWN); - printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name); - priv->stats.rx_dropped ++; - break; - } - } while (1); -} - -/* - * Update stats for the transmitted packet - */ -static void -am79c961_tx(struct net_device *dev, struct dev_priv *priv) -{ - do { - u_int hdraddr; - u_int status; - - hdraddr = priv->txhdr + (priv->txtail << 3); - status = am_readword (dev, hdraddr + 2); - if (status & TMD_OWN) - break; - - priv->txtail ++; - if (priv->txtail >= TX_BUFFERS) - priv->txtail = 0; - - if (status & TMD_ERR) { - u_int status2; - - priv->stats.tx_errors ++; - - status2 = am_readword (dev, hdraddr + 6); - - /* - * Clear the error byte - */ - am_writeword (dev, hdraddr + 6, 0); - - if (status2 & TST_RTRY) - priv->stats.collisions += 16; - if (status2 & TST_LCOL) - priv->stats.tx_window_errors ++; - if (status2 & TST_LCAR) - priv->stats.tx_carrier_errors ++; - if (status2 & TST_UFLO) - priv->stats.tx_fifo_errors ++; - continue; - } - priv->stats.tx_packets ++; - } while (priv->txtail != priv->txhead); - - netif_wake_queue(dev); -} - -static irqreturn_t -am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct dev_priv *priv = (struct dev_priv *)dev->priv; - u_int status; - int handled = 0; - - status = read_rreg(dev->base_addr, CSR0); - write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA)); - - if (status & CSR0_RINT) { - handled = 1; - am79c961_rx(dev, priv); - } - if (status & CSR0_TINT) { - handled = 1; - am79c961_tx(dev, priv); - } - if (status & CSR0_MISS) { - handled = 1; - priv->stats.rx_dropped ++; - } - return IRQ_RETVAL(handled); -} - -/* - * Initialise the chip. Note that we always expect - * to be entered with interrupts enabled. - */ -static int -am79c961_hw_init(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - - spin_lock_irq(priv->chip_lock); - write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); - spin_unlock_irq(priv->chip_lock); - - am79c961_ramtest(dev, 0x66); - am79c961_ramtest(dev, 0x99); - - return 0; -} - -static void __init am79c961_banner(void) -{ - static unsigned version_printed; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -static int __init am79c961_init(void) -{ - struct net_device *dev; - struct dev_priv *priv; - int i, ret; - - dev = init_etherdev(NULL, sizeof(struct dev_priv)); - ret = -ENOMEM; - if (!dev) - goto out; - - priv = dev->priv; - - /* - * Fixed address and IRQ lines here. - * The PNP initialisation should have been - * done by the ether bootp loader. - */ - dev->base_addr = 0x220; - dev->irq = IRQ_EBSA110_ETHERNET; - - /* - * Reset the device. - */ - inb(dev->base_addr + NET_RESET); - udelay(5); - - /* - * Check the manufacturer part of the - * ether address. - */ - ret = -ENODEV; - if (inb(dev->base_addr) != 0x08 || - inb(dev->base_addr + 2) != 0x00 || - inb(dev->base_addr + 4) != 0x2b) - goto nodev; - - if (!request_region(dev->base_addr, 0x18, dev->name)) - goto nodev; - - am79c961_banner(); - printk(KERN_INFO "%s: ether address ", dev->name); - - /* Retrive and print the ethernet address. */ - for (i = 0; i < 6; i++) { - dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff; - printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); - } - - if (am79c961_hw_init(dev)) - goto release; - - dev->open = am79c961_open; - dev->stop = am79c961_close; - dev->hard_start_xmit = am79c961_sendpacket; - dev->get_stats = am79c961_getstats; - dev->set_multicast_list = am79c961_setmulticastlist; - dev->tx_timeout = am79c961_timeout; - - return 0; - -release: - release_region(dev->base_addr, 0x18); -nodev: - unregister_netdev(dev); - kfree(dev); -out: - return ret; -} - -__initcall(am79c961_init); diff -urN linux-2.5.69-bk13/drivers/net/am79c961a.h linux-2.5.69-bk14/drivers/net/am79c961a.h --- linux-2.5.69-bk13/drivers/net/am79c961a.h 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/am79c961a.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,130 +0,0 @@ -/* - * linux/drivers/net/am79c961.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _LINUX_am79c961a_H -#define _LINUX_am79c961a_H - -/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ -#define DEBUG_TX 2 -#define DEBUG_RX 4 -#define DEBUG_INT 8 -#define DEBUG_IC 16 -#ifndef NET_DEBUG -#define NET_DEBUG 0 -#endif - -#define NET_UID 0 -#define NET_RDP 0x10 -#define NET_RAP 0x12 -#define NET_RESET 0x14 -#define NET_IDP 0x16 - -/* - * RAP registers - */ -#define CSR0 0 -#define CSR0_INIT 0x0001 -#define CSR0_STRT 0x0002 -#define CSR0_STOP 0x0004 -#define CSR0_TDMD 0x0008 -#define CSR0_TXON 0x0010 -#define CSR0_RXON 0x0020 -#define CSR0_IENA 0x0040 -#define CSR0_INTR 0x0080 -#define CSR0_IDON 0x0100 -#define CSR0_TINT 0x0200 -#define CSR0_RINT 0x0400 -#define CSR0_MERR 0x0800 -#define CSR0_MISS 0x1000 -#define CSR0_CERR 0x2000 -#define CSR0_BABL 0x4000 -#define CSR0_ERR 0x8000 - -#define CSR3 3 -#define CSR3_EMBA 0x0008 -#define CSR3_DXMT2PD 0x0010 -#define CSR3_LAPPEN 0x0020 -#define CSR3_DXSUFLO 0x0040 -#define CSR3_IDONM 0x0100 -#define CSR3_TINTM 0x0200 -#define CSR3_RINTM 0x0400 -#define CSR3_MERRM 0x0800 -#define CSR3_MISSM 0x1000 -#define CSR3_BABLM 0x4000 -#define CSR3_MASKALL 0x5F00 - -#define CTRL1 5 -#define CTRL1_SPND 0x0001 - -#define LADRL 8 -#define LADRM1 9 -#define LADRM2 10 -#define LADRH 11 -#define PADRL 12 -#define PADRM 13 -#define PADRH 14 - -#define MODE 15 -#define MODE_DISRX 0x0001 -#define MODE_DISTX 0x0002 -#define MODE_LOOP 0x0004 -#define MODE_DTCRC 0x0008 -#define MODE_COLL 0x0010 -#define MODE_DRETRY 0x0020 -#define MODE_INTLOOP 0x0040 -#define MODE_PORT_AUI 0x0000 -#define MODE_PORT_10BT 0x0080 -#define MODE_DRXPA 0x2000 -#define MODE_DRXBA 0x4000 -#define MODE_PROMISC 0x8000 - -#define BASERXL 24 -#define BASERXH 25 -#define BASETXL 30 -#define BASETXH 31 - -#define POLLINT 47 - -#define SIZERXR 76 -#define SIZETXR 78 - -#define RMD_ENP 0x0100 -#define RMD_STP 0x0200 -#define RMD_CRC 0x0800 -#define RMD_FRAM 0x2000 -#define RMD_ERR 0x4000 -#define RMD_OWN 0x8000 - -#define TMD_ENP 0x0100 -#define TMD_STP 0x0200 -#define TMD_MORE 0x1000 -#define TMD_ERR 0x4000 -#define TMD_OWN 0x8000 - -#define TST_RTRY 0x0400 -#define TST_LCAR 0x0800 -#define TST_LCOL 0x1000 -#define TST_UFLO 0x4000 -#define TST_BUFF 0x8000 - -struct dev_priv { - struct net_device_stats stats; - unsigned long rxbuffer[RX_BUFFERS]; - unsigned long txbuffer[TX_BUFFERS]; - unsigned char txhead; - unsigned char txtail; - unsigned char rxhead; - unsigned char rxtail; - unsigned long rxhdr; - unsigned long txhdr; - spinlock_t chip_lock; -}; - -extern int am79c961_probe (struct net_device *dev); - -#endif diff -urN linux-2.5.69-bk13/drivers/net/arm/Kconfig linux-2.5.69-bk14/drivers/net/arm/Kconfig --- linux-2.5.69-bk13/drivers/net/arm/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/Kconfig 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,45 @@ +# +# Acorn Network device configuration +# These are for Acorn's Expansion card network interfaces +# +config ARM_AM79C961A + bool "ARM EBSA110 AM79C961A support" + depends on NET_ETHERNET && ARM && ARCH_EBSA110 + help + If you wish to compile a kernel for the EBSA-110, then you should + always answer Y to this. + +config ARM_ETHER1 + tristate "Acorn Ether1 support" + depends on NET_ETHERNET && ARM && ARCH_ACORN + help + If you have an Acorn system with one of these (AKA25) network cards, + you should say Y to this option if you wish to use it with Linux. + +config ARM_ETHER3 + tristate "Acorn/ANT Ether3 support" + depends on NET_ETHERNET && ARM && ARCH_ACORN + help + If you have an Acorn system with one of these network cards, you + should say Y to this option if you wish to use it with Linux. + +config ARM_ETHERH + tristate "I-cubed EtherH/ANT EtherM support" + depends on NET_ETHERNET && ARM && ARCH_ACORN + help + If you have an Acorn system with one of these network cards, you + should say Y to this option if you wish to use it with Linux. + +config ARM_ETHER00 + tristate "Altera Ether00 support" + depends on NET_ETHERNET && ARM && ARCH_CAMELOT + help + This is the driver for Altera's ether00 ethernet mac IP core. Say + Y here if you want to build support for this into the kernel. It + is also available as a module (say M here) that can be inserted/ + removed from the kernel at the same time as the PLD is configured. + If this driver is running on an epxa10 development board then it + will generate a suitable hw address based on the board serial + number (MTD support is required for this). Otherwise you will + need to set a suitable hw address using ifconfig. + diff -urN linux-2.5.69-bk13/drivers/net/arm/Makefile linux-2.5.69-bk14/drivers/net/arm/Makefile --- linux-2.5.69-bk13/drivers/net/arm/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/Makefile 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,10 @@ +# File: drivers/net/arm/Makefile +# +# Makefile for the ARM network device drivers +# + +obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o +obj-$(CONFIG_ARM_ETHER00) += ether00.o +obj-$(CONFIG_ARM_ETHERH) += etherh.o +obj-$(CONFIG_ARM_ETHER3) += ether3.o +obj-$(CONFIG_ARM_ETHER1) += ether1.o diff -urN linux-2.5.69-bk13/drivers/net/arm/am79c961a.c linux-2.5.69-bk14/drivers/net/arm/am79c961a.c --- linux-2.5.69-bk13/drivers/net/arm/am79c961a.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/am79c961a.c 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,685 @@ +/* + * linux/drivers/net/am79c961.c + * + * by Russell King 1995-2001. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Derived from various things including skeleton.c + * + * This is a special driver for the am79c961A Lance chip used in the + * Intel (formally Digital Equipment Corp) EBSA110 platform. Please + * note that this can not be built as a module (it doesn't make sense). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TX_BUFFERS 15 +#define RX_BUFFERS 25 + +#include "am79c961a.h" + +static irqreturn_t +am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs); + +static unsigned int net_debug = NET_DEBUG; + +static const char version[] = + "am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n"; + +/* --------------------------------------------------------------------------- */ + +#ifdef __arm__ +static void write_rreg(u_long base, u_int reg, u_int val) +{ + __asm__("str%?h %1, [%2] @ NET_RAP + str%?h %0, [%2, #-4] @ NET_RDP + " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); +} + +static inline unsigned short read_rreg(u_long base_addr, u_int reg) +{ + unsigned short v; + __asm__("str%?h %1, [%2] @ NET_RAP + ldr%?h %0, [%2, #-4] @ NET_RDP + " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); + return v; +} + +static inline void write_ireg(u_long base, u_int reg, u_int val) +{ + __asm__("str%?h %1, [%2] @ NET_RAP + str%?h %0, [%2, #8] @ NET_IDP + " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); +} + +#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1)) +#define am_readword(dev,off) __raw_readw(ISAMEM_BASE + ((off) << 1)) + +static inline void +am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) +{ + offset = ISAMEM_BASE + (offset << 1); + length = (length + 1) & ~1; + if ((int)buf & 2) { + __asm__ __volatile__("str%?h %2, [%0], #4" + : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); + buf += 2; + length -= 2; + } + while (length > 8) { + unsigned int tmp, tmp2; + __asm__ __volatile__(" + ldm%?ia %1!, {%2, %3} + str%?h %2, [%0], #4 + mov%? %2, %2, lsr #16 + str%?h %2, [%0], #4 + str%?h %3, [%0], #4 + mov%? %3, %3, lsr #16 + str%?h %3, [%0], #4 + " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) + : "0" (offset), "1" (buf)); + length -= 8; + } + while (length > 0) { + __asm__ __volatile__("str%?h %2, [%0], #4" + : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); + buf += 2; + length -= 2; + } +} + +static inline void +am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) +{ + offset = ISAMEM_BASE + (offset << 1); + length = (length + 1) & ~1; + if ((int)buf & 2) { + unsigned int tmp; + __asm__ __volatile__(" + ldr%?h %2, [%0], #4 + str%?b %2, [%1], #1 + mov%? %2, %2, lsr #8 + str%?b %2, [%1], #1 + " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); + length -= 2; + } + while (length > 8) { + unsigned int tmp, tmp2, tmp3; + __asm__ __volatile__(" + ldr%?h %2, [%0], #4 + ldr%?h %3, [%0], #4 + orr%? %2, %2, %3, lsl #16 + ldr%?h %3, [%0], #4 + ldr%?h %4, [%0], #4 + orr%? %3, %3, %4, lsl #16 + stm%?ia %1!, {%2, %3} + " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) + : "0" (offset), "1" (buf)); + length -= 8; + } + while (length > 0) { + unsigned int tmp; + __asm__ __volatile__(" + ldr%?h %2, [%0], #4 + str%?b %2, [%1], #1 + mov%? %2, %2, lsr #8 + str%?b %2, [%1], #1 + " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); + length -= 2; + } +} +#else +#error Not compatible +#endif + +static int +am79c961_ramtest(struct net_device *dev, unsigned int val) +{ + unsigned char *buffer = kmalloc (65536, GFP_KERNEL); + int i, error = 0, errorcount = 0; + + if (!buffer) + return 0; + memset (buffer, val, 65536); + am_writebuffer(dev, 0, buffer, 65536); + memset (buffer, val ^ 255, 65536); + am_readbuffer(dev, 0, buffer, 65536); + for (i = 0; i < 65536; i++) { + if (buffer[i] != val && !error) { + printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i); + error = 1; + errorcount ++; + } else if (error && buffer[i] == val) { + printk ("%05X\n", i); + error = 0; + } + } + if (error) + printk ("10000\n"); + kfree (buffer); + return errorcount; +} + +static void +am79c961_init_for_open(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + unsigned char *p; + u_int hdr_addr, first_free_addr; + int i; + + /* + * Stop the chip. + */ + spin_lock_irqsave(priv->chip_lock, flags); + write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP); + spin_unlock_irqrestore(priv->chip_lock, flags); + + write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */ + write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */ + write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */ + write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */ + + for (i = LADRL; i <= LADRH; i++) + write_rreg (dev->base_addr, i, 0); + + for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2) + write_rreg (dev->base_addr, i, p[0] | (p[1] << 8)); + + i = MODE_PORT_10BT; + if (dev->flags & IFF_PROMISC) + i |= MODE_PROMISC; + + write_rreg (dev->base_addr, MODE, i); + write_rreg (dev->base_addr, POLLINT, 0); + write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS); + write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS); + + first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16; + hdr_addr = 0; + + priv->rxhead = 0; + priv->rxtail = 0; + priv->rxhdr = hdr_addr; + + for (i = 0; i < RX_BUFFERS; i++) { + priv->rxbuffer[i] = first_free_addr; + am_writeword (dev, hdr_addr, first_free_addr); + am_writeword (dev, hdr_addr + 2, RMD_OWN); + am_writeword (dev, hdr_addr + 4, (-1600)); + am_writeword (dev, hdr_addr + 6, 0); + first_free_addr += 1600; + hdr_addr += 8; + } + priv->txhead = 0; + priv->txtail = 0; + priv->txhdr = hdr_addr; + for (i = 0; i < TX_BUFFERS; i++) { + priv->txbuffer[i] = first_free_addr; + am_writeword (dev, hdr_addr, first_free_addr); + am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP); + am_writeword (dev, hdr_addr + 4, 0xf000); + am_writeword (dev, hdr_addr + 6, 0); + first_free_addr += 1600; + hdr_addr += 8; + } + + write_rreg (dev->base_addr, BASERXL, priv->rxhdr); + write_rreg (dev->base_addr, BASERXH, 0); + write_rreg (dev->base_addr, BASETXL, priv->txhdr); + write_rreg (dev->base_addr, BASERXH, 0); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO); + write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); +} + +/* + * Open/initialize the board. + */ +static int +am79c961_open(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int ret; + + memset (&priv->stats, 0, sizeof (priv->stats)); + + ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev); + if (ret) + return ret; + + am79c961_init_for_open(dev); + + netif_start_queue(dev); + + return 0; +} + +/* + * The inverse routine to am79c961_open(). + */ +static int +am79c961_close(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + + netif_stop_queue(dev); + + spin_lock_irqsave(priv->chip_lock, flags); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); + spin_unlock_irqrestore(priv->chip_lock, flags); + + free_irq (dev->irq, dev); + + return 0; +} + +/* + * Get the current statistics. + */ +static struct net_device_stats *am79c961_getstats (struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + return &priv->stats; +} + +static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash) +{ + if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) { + int idx, bit; + u32 crc; + + crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr); + + idx = crc >> 30; + bit = (crc >> 26) & 15; + + hash[idx] |= 1 << bit; + } +} + +/* + * Set or clear promiscuous/multicast mode filter for this adapter. + */ +static void am79c961_setmulticastlist (struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + unsigned short multi_hash[4], mode; + int i, stopped; + + mode = MODE_PORT_10BT; + + if (dev->flags & IFF_PROMISC) { + mode |= MODE_PROMISC; + } else if (dev->flags & IFF_ALLMULTI) { + memset(multi_hash, 0xff, sizeof(multi_hash)); + } else { + struct dev_mc_list *dmi; + + memset(multi_hash, 0x00, sizeof(multi_hash)); + + for (dmi = dev->mc_list; dmi; dmi = dmi->next) + am79c961_mc_hash(dmi, multi_hash); + } + + spin_lock_irqsave(priv->chip_lock, flags); + + stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; + + if (!stopped) { + /* + * Put the chip into suspend mode + */ + write_rreg(dev->base_addr, CTRL1, CTRL1_SPND); + + /* + * Spin waiting for chip to report suspend mode + */ + while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) { + spin_unlock_irqrestore(priv->chip_lock, flags); + nop(); + spin_lock_irqsave(priv->chip_lock, flags); + } + } + + /* + * Update the multicast hash table + */ + for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++) + write_rreg(dev->base_addr, i + LADRL, multi_hash[i]); + + /* + * Write the mode register + */ + write_rreg(dev->base_addr, MODE, mode); + + if (!stopped) { + /* + * Put the chip back into running mode + */ + write_rreg(dev->base_addr, CTRL1, 0); + } + + spin_unlock_irqrestore(priv->chip_lock, flags); +} + +static void am79c961_timeout(struct net_device *dev) +{ + printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n", + dev->name); + + /* + * ought to do some setup of the tx side here + */ + + netif_wake_queue(dev); +} + +/* + * Transmit a packet + */ +static int +am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned int length = skb->len; + unsigned int hdraddr, bufaddr; + unsigned int head; + unsigned long flags; + + /* FIXME: I thought the 79c961 could do padding - RMK ??? */ + if (length < ETH_ZLEN) { + skb = skb_padto(skb, ETH_ZLEN); + if (skb == NULL) + return 0; + length = ETH_ZLEN; + } + + head = priv->txhead; + hdraddr = priv->txhdr + (head << 3); + bufaddr = priv->txbuffer[head]; + head += 1; + if (head >= TX_BUFFERS) + head = 0; + + am_writebuffer (dev, bufaddr, skb->data, length); + am_writeword (dev, hdraddr + 4, -length); + am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); + priv->txhead = head; + + spin_lock_irqsave(priv->chip_lock, flags); + write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA); + dev->trans_start = jiffies; + spin_unlock_irqrestore(priv->chip_lock, flags); + + /* + * If the next packet is owned by the ethernet device, + * then the tx ring is full and we can't add another + * packet. + */ + if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN) + netif_stop_queue(dev); + + dev_kfree_skb(skb); + + return 0; +} + +/* + * If we have a good packet(s), get it/them out of the buffers. + */ +static void +am79c961_rx(struct net_device *dev, struct dev_priv *priv) +{ + do { + struct sk_buff *skb; + u_int hdraddr; + u_int pktaddr; + u_int status; + int len; + + hdraddr = priv->rxhdr + (priv->rxtail << 3); + pktaddr = priv->rxbuffer[priv->rxtail]; + + status = am_readword (dev, hdraddr + 2); + if (status & RMD_OWN) /* do we own it? */ + break; + + priv->rxtail ++; + if (priv->rxtail >= RX_BUFFERS) + priv->rxtail = 0; + + if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) { + am_writeword (dev, hdraddr + 2, RMD_OWN); + priv->stats.rx_errors ++; + if (status & RMD_ERR) { + if (status & RMD_FRAM) + priv->stats.rx_frame_errors ++; + if (status & RMD_CRC) + priv->stats.rx_crc_errors ++; + } else if (status & RMD_STP) + priv->stats.rx_length_errors ++; + continue; + } + + len = am_readword(dev, hdraddr + 6); + skb = dev_alloc_skb(len + 2); + + if (skb) { + skb->dev = dev; + skb_reserve(skb, 2); + + am_readbuffer(dev, pktaddr, skb_put(skb, len), len); + am_writeword(dev, hdraddr + 2, RMD_OWN); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + priv->stats.rx_bytes += len; + priv->stats.rx_packets ++; + } else { + am_writeword (dev, hdraddr + 2, RMD_OWN); + printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name); + priv->stats.rx_dropped ++; + break; + } + } while (1); +} + +/* + * Update stats for the transmitted packet + */ +static void +am79c961_tx(struct net_device *dev, struct dev_priv *priv) +{ + do { + u_int hdraddr; + u_int status; + + hdraddr = priv->txhdr + (priv->txtail << 3); + status = am_readword (dev, hdraddr + 2); + if (status & TMD_OWN) + break; + + priv->txtail ++; + if (priv->txtail >= TX_BUFFERS) + priv->txtail = 0; + + if (status & TMD_ERR) { + u_int status2; + + priv->stats.tx_errors ++; + + status2 = am_readword (dev, hdraddr + 6); + + /* + * Clear the error byte + */ + am_writeword (dev, hdraddr + 6, 0); + + if (status2 & TST_RTRY) + priv->stats.collisions += 16; + if (status2 & TST_LCOL) + priv->stats.tx_window_errors ++; + if (status2 & TST_LCAR) + priv->stats.tx_carrier_errors ++; + if (status2 & TST_UFLO) + priv->stats.tx_fifo_errors ++; + continue; + } + priv->stats.tx_packets ++; + } while (priv->txtail != priv->txhead); + + netif_wake_queue(dev); +} + +static irqreturn_t +am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct dev_priv *priv = (struct dev_priv *)dev->priv; + u_int status; + int handled = 0; + + status = read_rreg(dev->base_addr, CSR0); + write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA)); + + if (status & CSR0_RINT) { + handled = 1; + am79c961_rx(dev, priv); + } + if (status & CSR0_TINT) { + handled = 1; + am79c961_tx(dev, priv); + } + if (status & CSR0_MISS) { + handled = 1; + priv->stats.rx_dropped ++; + } + return IRQ_RETVAL(handled); +} + +/* + * Initialise the chip. Note that we always expect + * to be entered with interrupts enabled. + */ +static int +am79c961_hw_init(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + + spin_lock_irq(priv->chip_lock); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); + spin_unlock_irq(priv->chip_lock); + + am79c961_ramtest(dev, 0x66); + am79c961_ramtest(dev, 0x99); + + return 0; +} + +static void __init am79c961_banner(void) +{ + static unsigned version_printed; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static int __init am79c961_init(void) +{ + struct net_device *dev; + struct dev_priv *priv; + int i, ret; + + dev = init_etherdev(NULL, sizeof(struct dev_priv)); + ret = -ENOMEM; + if (!dev) + goto out; + + priv = dev->priv; + + /* + * Fixed address and IRQ lines here. + * The PNP initialisation should have been + * done by the ether bootp loader. + */ + dev->base_addr = 0x220; + dev->irq = IRQ_EBSA110_ETHERNET; + + /* + * Reset the device. + */ + inb(dev->base_addr + NET_RESET); + udelay(5); + + /* + * Check the manufacturer part of the + * ether address. + */ + ret = -ENODEV; + if (inb(dev->base_addr) != 0x08 || + inb(dev->base_addr + 2) != 0x00 || + inb(dev->base_addr + 4) != 0x2b) + goto nodev; + + if (!request_region(dev->base_addr, 0x18, dev->name)) + goto nodev; + + am79c961_banner(); + printk(KERN_INFO "%s: ether address ", dev->name); + + /* Retrive and print the ethernet address. */ + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff; + printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); + } + + if (am79c961_hw_init(dev)) + goto release; + + dev->open = am79c961_open; + dev->stop = am79c961_close; + dev->hard_start_xmit = am79c961_sendpacket; + dev->get_stats = am79c961_getstats; + dev->set_multicast_list = am79c961_setmulticastlist; + dev->tx_timeout = am79c961_timeout; + + return 0; + +release: + release_region(dev->base_addr, 0x18); +nodev: + unregister_netdev(dev); + kfree(dev); +out: + return ret; +} + +__initcall(am79c961_init); diff -urN linux-2.5.69-bk13/drivers/net/arm/am79c961a.h linux-2.5.69-bk14/drivers/net/arm/am79c961a.h --- linux-2.5.69-bk13/drivers/net/arm/am79c961a.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/am79c961a.h 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,130 @@ +/* + * linux/drivers/net/am79c961.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_am79c961a_H +#define _LINUX_am79c961a_H + +/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ +#define DEBUG_TX 2 +#define DEBUG_RX 4 +#define DEBUG_INT 8 +#define DEBUG_IC 16 +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +#define NET_UID 0 +#define NET_RDP 0x10 +#define NET_RAP 0x12 +#define NET_RESET 0x14 +#define NET_IDP 0x16 + +/* + * RAP registers + */ +#define CSR0 0 +#define CSR0_INIT 0x0001 +#define CSR0_STRT 0x0002 +#define CSR0_STOP 0x0004 +#define CSR0_TDMD 0x0008 +#define CSR0_TXON 0x0010 +#define CSR0_RXON 0x0020 +#define CSR0_IENA 0x0040 +#define CSR0_INTR 0x0080 +#define CSR0_IDON 0x0100 +#define CSR0_TINT 0x0200 +#define CSR0_RINT 0x0400 +#define CSR0_MERR 0x0800 +#define CSR0_MISS 0x1000 +#define CSR0_CERR 0x2000 +#define CSR0_BABL 0x4000 +#define CSR0_ERR 0x8000 + +#define CSR3 3 +#define CSR3_EMBA 0x0008 +#define CSR3_DXMT2PD 0x0010 +#define CSR3_LAPPEN 0x0020 +#define CSR3_DXSUFLO 0x0040 +#define CSR3_IDONM 0x0100 +#define CSR3_TINTM 0x0200 +#define CSR3_RINTM 0x0400 +#define CSR3_MERRM 0x0800 +#define CSR3_MISSM 0x1000 +#define CSR3_BABLM 0x4000 +#define CSR3_MASKALL 0x5F00 + +#define CTRL1 5 +#define CTRL1_SPND 0x0001 + +#define LADRL 8 +#define LADRM1 9 +#define LADRM2 10 +#define LADRH 11 +#define PADRL 12 +#define PADRM 13 +#define PADRH 14 + +#define MODE 15 +#define MODE_DISRX 0x0001 +#define MODE_DISTX 0x0002 +#define MODE_LOOP 0x0004 +#define MODE_DTCRC 0x0008 +#define MODE_COLL 0x0010 +#define MODE_DRETRY 0x0020 +#define MODE_INTLOOP 0x0040 +#define MODE_PORT_AUI 0x0000 +#define MODE_PORT_10BT 0x0080 +#define MODE_DRXPA 0x2000 +#define MODE_DRXBA 0x4000 +#define MODE_PROMISC 0x8000 + +#define BASERXL 24 +#define BASERXH 25 +#define BASETXL 30 +#define BASETXH 31 + +#define POLLINT 47 + +#define SIZERXR 76 +#define SIZETXR 78 + +#define RMD_ENP 0x0100 +#define RMD_STP 0x0200 +#define RMD_CRC 0x0800 +#define RMD_FRAM 0x2000 +#define RMD_ERR 0x4000 +#define RMD_OWN 0x8000 + +#define TMD_ENP 0x0100 +#define TMD_STP 0x0200 +#define TMD_MORE 0x1000 +#define TMD_ERR 0x4000 +#define TMD_OWN 0x8000 + +#define TST_RTRY 0x0400 +#define TST_LCAR 0x0800 +#define TST_LCOL 0x1000 +#define TST_UFLO 0x4000 +#define TST_BUFF 0x8000 + +struct dev_priv { + struct net_device_stats stats; + unsigned long rxbuffer[RX_BUFFERS]; + unsigned long txbuffer[TX_BUFFERS]; + unsigned char txhead; + unsigned char txtail; + unsigned char rxhead; + unsigned char rxtail; + unsigned long rxhdr; + unsigned long txhdr; + spinlock_t chip_lock; +}; + +extern int am79c961_probe (struct net_device *dev); + +#endif diff -urN linux-2.5.69-bk13/drivers/net/arm/ether00.c linux-2.5.69-bk14/drivers/net/arm/ether00.c --- linux-2.5.69-bk13/drivers/net/arm/ether00.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/ether00.c 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,1025 @@ +/* + * drivers/net/ether00.c + * + * Copyright (C) 2001 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +MODULE_AUTHOR("Clive Davies"); +MODULE_DESCRIPTION("Altera Ether00 IP core driver"); +MODULE_LICENSE("GPL"); + +#define PKT_BUF_SZ 1540 /* Size of each rx buffer */ +#define ETH_NR 4 /* Number of MACs this driver supports */ + +#define DEBUG(x) + +#define __dma_va(x) (unsigned int)((unsigned int)priv->dma_data+(((unsigned int)(x))&(EXC_SPSRAM_BLOCK0_SIZE-1))) +#define __dma_pa(x) (unsigned int)(EXC_SPSRAM_BLOCK0_BASE+(((unsigned int)(x))-(unsigned int)priv->dma_data)) + +#define ETHER00_BASE 0 +#define ETHER00_TYPE +#define ETHER00_NAME "ether00" +#define MAC_REG_SIZE 0x400 /* size of MAC register area */ + + + +/* typedefs */ + +/* The definition of the driver control structure */ + +#define RX_NUM_BUFF 10 +#define RX_NUM_FDESC 10 +#define TX_NUM_FDESC 10 + +struct tx_fda_ent{ + FDA_DESC fd; + BUF_DESC bd; + BUF_DESC pad; +}; +struct rx_fda_ent{ + FDA_DESC fd; + BUF_DESC bd; + BUF_DESC pad; +}; +struct rx_blist_ent{ + FDA_DESC fd; + BUF_DESC bd; + BUF_DESC pad; +}; +struct net_priv +{ + struct net_device_stats stats; + struct sk_buff* skb; + void* dma_data; + struct rx_blist_ent* rx_blist_vp; + struct rx_fda_ent* rx_fda_ptr; + struct tx_fda_ent* tx_fdalist_vp; + struct tq_struct tq_memupdate; + unsigned char memupdate_scheduled; + unsigned char rx_disabled; + unsigned char queue_stopped; + spinlock_t rx_lock; +}; + +static const char vendor_id[2]={0x07,0xed}; + +#ifdef ETHER00_DEBUG + +/* Dump (most) registers for debugging puposes */ + +static void dump_regs(struct net_device *dev){ + struct net_priv* priv=dev->priv; + unsigned int* i; + + printk("\n RX free descriptor area:\n"); + + for(i=(unsigned int*)priv->rx_fda_ptr; + i<((unsigned int*)(priv->rx_fda_ptr+RX_NUM_FDESC));){ + printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3)); + i+=4; + } + + printk("\n RX buffer list:\n"); + + for(i=(unsigned int*)priv->rx_blist_vp; + i<((unsigned int*)(priv->rx_blist_vp+RX_NUM_BUFF));){ + printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3)); + i+=4; + } + + printk("\n TX frame descriptor list:\n"); + + for(i=(unsigned int*)priv->tx_fdalist_vp; + i<((unsigned int*)(priv->tx_fdalist_vp+TX_NUM_FDESC));){ + printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3)); + i+=4; + } + + printk("\ndma ctl=%#x\n",readw(ETHER_DMA_CTL(dev->base_addr))); + printk("txfrmptr=%#x\n",readw(ETHER_TXFRMPTR(dev->base_addr))); + printk("txthrsh=%#x\n",readw(ETHER_TXTHRSH(dev->base_addr))); + printk("txpollctr=%#x\n",readw(ETHER_TXPOLLCTR(dev->base_addr))); + printk("blfrmptr=%#x\n",readw(ETHER_BLFRMPTR(dev->base_addr))); + printk("rxfragsize=%#x\n",readw(ETHER_RXFRAGSIZE(dev->base_addr))); + printk("tx_int_en=%#x\n",readw(ETHER_INT_EN(dev->base_addr))); + printk("fda_bas=%#x\n",readw(ETHER_FDA_BAS(dev->base_addr))); + printk("fda_lim=%#x\n",readw(ETHER_FDA_LIM(dev->base_addr))); + printk("int_src=%#x\n",readw(ETHER_INT_SRC(dev->base_addr))); + printk("pausecnt=%#x\n",readw(ETHER_PAUSECNT(dev->base_addr))); + printk("rempaucnt=%#x\n",readw(ETHER_REMPAUCNT(dev->base_addr))); + printk("txconfrmstat=%#x\n",readw(ETHER_TXCONFRMSTAT(dev->base_addr))); + printk("mac_ctl=%#x\n",readw(ETHER_MAC_CTL(dev->base_addr))); + printk("arc_ctl=%#x\n",readw(ETHER_ARC_CTL(dev->base_addr))); + printk("tx_ctl=%#x\n",readw(ETHER_TX_CTL(dev->base_addr))); +} +#endif /* ETHER00_DEBUG */ + + +static int ether00_write_phy(struct net_device *dev, short address, short value) +{ + volatile int count = 1024; + writew(value,ETHER_MD_DATA(dev->base_addr)); + writew( ETHER_MD_CA_BUSY_MSK | + ETHER_MD_CA_WR_MSK | + (address & ETHER_MD_CA_ADDR_MSK), + ETHER_MD_CA(dev->base_addr)); + + /* Wait for the command to complete */ + while((readw(ETHER_MD_CA(dev->base_addr)) & ETHER_MD_CA_BUSY_MSK)&&count){ + count--; + } + if (!count){ + printk("Write to phy failed, addr=%#x, data=%#x\n",address, value); + return -EIO; + } + return 0; +} + +static int ether00_read_phy(struct net_device *dev, short address) +{ + volatile int count = 1024; + writew( ETHER_MD_CA_BUSY_MSK | + (address & ETHER_MD_CA_ADDR_MSK), + ETHER_MD_CA(dev->base_addr)); + + /* Wait for the command to complete */ + while((readw(ETHER_MD_CA(dev->base_addr)) & ETHER_MD_CA_BUSY_MSK)&&count){ + count--; + } + if (!count){ + printk(KERN_WARNING "Read from phy timed out\n"); + return -EIO; + } + return readw(ETHER_MD_DATA(dev->base_addr)); +} + +static void ether00_phy_int(int irq_num, void* dev_id, struct pt_regs* regs) +{ + struct net_device* dev=dev_id; + int irq_status; + + irq_status=ether00_read_phy(dev, PHY_IRQ_CONTROL); + + if(irq_status & PHY_IRQ_CONTROL_ANEG_COMP_INT_MSK){ + /* + * Autonegotiation complete on epxa10db. The mac doesn't + * twig if we're in full duplex so we need to check the + * phy status register and configure the mac accordingly + */ + if(ether00_read_phy(dev, PHY_STATUS)&(PHY_STATUS_10T_F_MSK|PHY_STATUS_100_X_F_MSK)){ + int tmp; + tmp=readl(ETHER_MAC_CTL(dev->base_addr)); + writel(tmp|ETHER_MAC_CTL_FULLDUP_MSK,ETHER_MAC_CTL(dev->base_addr)); + } + } + + if(irq_status&PHY_IRQ_CONTROL_LS_CHG_INT_MSK){ + + if(ether00_read_phy(dev, PHY_STATUS)& PHY_STATUS_LINK_MSK){ + /* Link is up */ + netif_carrier_on(dev); + //printk("Carrier on\n"); + }else{ + netif_carrier_off(dev); + //printk("Carrier off\n"); + + } + } + +} + +static void setup_blist_entry(struct sk_buff* skb,struct rx_blist_ent* blist_ent_ptr){ + /* Make the buffer consistent with the cache as the mac is going to write + * directly into it*/ + blist_ent_ptr->fd.FDSystem=(unsigned int)skb; + blist_ent_ptr->bd.BuffData=(char*)__pa(skb->data); + consistent_sync(skb->data,PKT_BUF_SZ,PCI_DMA_FROMDEVICE); + /* align IP on 16 Byte (DMA_CTL set to skip 2 bytes) */ + skb_reserve(skb,2); + blist_ent_ptr->bd.BuffLength=PKT_BUF_SZ-2; + blist_ent_ptr->fd.FDLength=1; + blist_ent_ptr->fd.FDCtl=FDCTL_COWNSFD_MSK; + blist_ent_ptr->bd.BDCtl=BDCTL_COWNSBD_MSK; +} + + +static int ether00_mem_init(struct net_device* dev) +{ + struct net_priv* priv=dev->priv; + struct tx_fda_ent *tx_fd_ptr,*tx_end_ptr; + struct rx_blist_ent* blist_ent_ptr; + int i; + + /* + * Grab a block of on chip SRAM to contain the control stuctures for + * the ethernet MAC. This uncached becuase it needs to be accesses by both + * bus masters (cpu + mac). However, it shouldn't matter too much in terms + * of speed as its on chip memory + */ + priv->dma_data=ioremap_nocache(EXC_SPSRAM_BLOCK0_BASE,EXC_SPSRAM_BLOCK0_SIZE ); + if (!priv->dma_data) + return -ENOMEM; + + priv->rx_fda_ptr=(struct rx_fda_ent*)priv->dma_data; + /* + * Now share it out amongst the Frame descriptors and the buffer list + */ + priv->rx_blist_vp=(struct rx_blist_ent*)((unsigned int)priv->dma_data+RX_NUM_FDESC*sizeof(struct rx_fda_ent)); + + /* + *Initalise the FDA list + */ + /* set ownership to the controller */ + memset(priv->rx_fda_ptr,0x80,RX_NUM_FDESC*sizeof(struct rx_fda_ent)); + + /* + *Initialise the buffer list + */ + blist_ent_ptr=priv->rx_blist_vp; + i=0; + while(blist_ent_ptr<(priv->rx_blist_vp+RX_NUM_BUFF)){ + struct sk_buff *skb; + blist_ent_ptr->fd.FDLength=1; + skb=dev_alloc_skb(PKT_BUF_SZ); + if(skb){ + setup_blist_entry(skb,blist_ent_ptr); + blist_ent_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(blist_ent_ptr+1); + blist_ent_ptr->bd.BDStat=i++; + blist_ent_ptr++; + } + else + { + printk("Failed to initalise buffer list\n"); + } + + } + blist_ent_ptr--; + blist_ent_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(priv->rx_blist_vp); + + priv->tx_fdalist_vp=(struct tx_fda_ent*)(priv->rx_blist_vp+RX_NUM_BUFF); + + /* Initialise the buffers to be a circular list. The mac will then go poll + * the list until it finds a frame ready to transmit */ + tx_end_ptr=priv->tx_fdalist_vp+TX_NUM_FDESC; + for(tx_fd_ptr=priv->tx_fdalist_vp;tx_fd_ptrfd.FDNext=(FDA_DESC*)__dma_pa((tx_fd_ptr+1)); + tx_fd_ptr->fd.FDCtl=1; + tx_fd_ptr->fd.FDStat=0; + tx_fd_ptr->fd.FDLength=1; + + } + /* Change the last FDNext pointer to make a circular list */ + tx_fd_ptr--; + tx_fd_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(priv->tx_fdalist_vp); + + /* Point the device at the chain of Rx and Tx Buffers */ + writel((unsigned int)__dma_pa(priv->rx_fda_ptr),ETHER_FDA_BAS(dev->base_addr)); + writel((RX_NUM_FDESC-1)*sizeof(struct rx_fda_ent),ETHER_FDA_LIM(dev->base_addr)); + writel((unsigned int)__dma_pa(priv->rx_blist_vp),ETHER_BLFRMPTR(dev->base_addr)); + + writel((unsigned int)__dma_pa(priv->tx_fdalist_vp),ETHER_TXFRMPTR(dev->base_addr)); + + return 0; +} + + +void ether00_mem_update(void* dev_id) +{ + struct net_device* dev=dev_id; + struct net_priv* priv=dev->priv; + struct sk_buff* skb; + struct tx_fda_ent *fda_ptr=priv->tx_fdalist_vp; + struct rx_blist_ent* blist_ent_ptr; + unsigned long flags; + + priv->tq_memupdate.sync=0; + //priv->tq_memupdate.list= + priv->memupdate_scheduled=0; + + /* Transmit interrupt */ + while(fda_ptr<(priv->tx_fdalist_vp+TX_NUM_FDESC)){ + if(!(FDCTL_COWNSFD_MSK&fda_ptr->fd.FDCtl) && (ETHER_TX_STAT_COMP_MSK&fda_ptr->fd.FDStat)){ + priv->stats.tx_packets++; + priv->stats.tx_bytes+=fda_ptr->bd.BuffLength; + skb=(struct sk_buff*)fda_ptr->fd.FDSystem; + //printk("%d:txcln:fda=%#x skb=%#x\n",jiffies,fda_ptr,skb); + dev_kfree_skb(skb); + fda_ptr->fd.FDSystem=0; + fda_ptr->fd.FDStat=0; + fda_ptr->fd.FDCtl=0; + } + fda_ptr++; + } + /* Fill in any missing buffers from the received queue */ + spin_lock_irqsave(&priv->rx_lock,flags); + blist_ent_ptr=priv->rx_blist_vp; + while(blist_ent_ptr<(priv->rx_blist_vp+RX_NUM_BUFF)){ + /* fd.FDSystem of 0 indicates we failed to allocate the buffer in the ISR */ + if(!blist_ent_ptr->fd.FDSystem){ + struct sk_buff *skb; + skb=dev_alloc_skb(PKT_BUF_SZ); + blist_ent_ptr->fd.FDSystem=(unsigned int)skb; + if(skb){ + setup_blist_entry(skb,blist_ent_ptr); + } + else + { + break; + } + } + blist_ent_ptr++; + } + spin_unlock_irqrestore(&priv->rx_lock,flags); + if(priv->queue_stopped){ + //printk("%d:cln:start q\n",jiffies); + netif_start_queue(dev); + } + if(priv->rx_disabled){ + //printk("%d:enable_irq\n",jiffies); + priv->rx_disabled=0; + writel(ETHER_RX_CTL_RXEN_MSK,ETHER_RX_CTL(dev->base_addr)); + + } +} + + +static void ether00_int( int irq_num, void* dev_id, struct pt_regs* regs) +{ + struct net_device* dev=dev_id; + struct net_priv* priv=dev->priv; + + unsigned int interruptValue; + + interruptValue=readl(ETHER_INT_SRC(dev->base_addr)); + + //printk("INT_SRC=%x\n",interruptValue); + + if(!(readl(ETHER_INT_SRC(dev->base_addr)) & ETHER_INT_SRC_IRQ_MSK)) + { + return; /* Interrupt wasn't caused by us!! */ + } + + if(readl(ETHER_INT_SRC(dev->base_addr))& + (ETHER_INT_SRC_INTMACRX_MSK | + ETHER_INT_SRC_FDAEX_MSK | + ETHER_INT_SRC_BLEX_MSK)) { + struct rx_blist_ent* blist_ent_ptr; + struct rx_fda_ent* fda_ent_ptr; + struct sk_buff* skb; + + fda_ent_ptr=priv->rx_fda_ptr; + spin_lock(&priv->rx_lock); + while(fda_ent_ptr<(priv->rx_fda_ptr+RX_NUM_FDESC)){ + int result; + + if(!(fda_ent_ptr->fd.FDCtl&FDCTL_COWNSFD_MSK)) + { + /* This frame is ready for processing */ + /*find the corresponding buffer in the bufferlist */ + blist_ent_ptr=priv->rx_blist_vp+fda_ent_ptr->bd.BDStat; + skb=(struct sk_buff*)blist_ent_ptr->fd.FDSystem; + + /* Pass this skb up the stack */ + skb->dev=dev; + skb_put(skb,fda_ent_ptr->fd.FDLength); + skb->protocol=eth_type_trans(skb,dev); + skb->ip_summed=CHECKSUM_UNNECESSARY; + result=netif_rx(skb); + /* Update statistics */ + priv->stats.rx_packets++; + priv->stats.rx_bytes+=fda_ent_ptr->fd.FDLength; + + /* Free the FDA entry */ + fda_ent_ptr->bd.BDStat=0xff; + fda_ent_ptr->fd.FDCtl=FDCTL_COWNSFD_MSK; + + /* Allocate a new skb and point the bd entry to it */ + blist_ent_ptr->fd.FDSystem=0; + skb=dev_alloc_skb(PKT_BUF_SZ); + //printk("allocskb=%#x\n",skb); + if(skb){ + setup_blist_entry(skb,blist_ent_ptr); + + } + else if(!priv->memupdate_scheduled){ + int tmp; + /* There are no buffers at the moment, so schedule */ + /* the background task to sort this out */ + schedule_task(&priv->tq_memupdate); + priv->memupdate_scheduled=1; + printk(KERN_DEBUG "%s:No buffers",dev->name); + /* If this interrupt was due to a lack of buffers then + * we'd better stop the receiver too */ + if(interruptValueÐER_INT_SRC_BLEX_MSK){ + priv->rx_disabled=1; + tmp=readl(ETHER_INT_SRC(dev->base_addr)); + writel(tmp&~ETHER_RX_CTL_RXEN_MSK,ETHER_RX_CTL(dev->base_addr)); + printk(KERN_DEBUG "%s:Halting rx",dev->name); + } + + } + + } + fda_ent_ptr++; + } + spin_unlock(&priv->rx_lock); + + /* Clear the interrupts */ + writel(ETHER_INT_SRC_INTMACRX_MSK | ETHER_INT_SRC_FDAEX_MSK + | ETHER_INT_SRC_BLEX_MSK,ETHER_INT_SRC(dev->base_addr)); + + } + + if(readl(ETHER_INT_SRC(dev->base_addr))ÐER_INT_SRC_INTMACTX_MSK){ + + if(!priv->memupdate_scheduled){ + schedule_task(&priv->tq_memupdate); + priv->memupdate_scheduled=1; + } + /* Clear the interrupt */ + writel(ETHER_INT_SRC_INTMACTX_MSK,ETHER_INT_SRC(dev->base_addr)); + } + + if (readl(ETHER_INT_SRC(dev->base_addr)) & (ETHER_INT_SRC_SWINT_MSK| + ETHER_INT_SRC_INTEARNOT_MSK| + ETHER_INT_SRC_INTLINK_MSK| + ETHER_INT_SRC_INTEXBD_MSK| + ETHER_INT_SRC_INTTXCTLCMP_MSK)) + { + /* + * Not using any of these so they shouldn't happen + * + * In the cased of INTEXBD - if you allocate more + * than 28 decsriptors you may need to think about this + */ + printk("Not using this interrupt\n"); + } + + if (readl(ETHER_INT_SRC(dev->base_addr)) & + (ETHER_INT_SRC_INTSBUS_MSK | + ETHER_INT_SRC_INTNRABT_MSK + |ETHER_INT_SRC_DMPARERR_MSK)) + { + /* + * Hardware errors, we can either ignore them and hope they go away + *or reset the device, I'll try the first for now to see if they happen + */ + printk("Hardware error\n"); + } +} + +static void ether00_setup_ethernet_address(struct net_device* dev) +{ + int tmp; + + dev->addr_len=6; + writew(0,ETHER_ARC_ADR(dev->base_addr)); + writel((dev->dev_addr[0]<<24) | + (dev->dev_addr[1]<<16) | + (dev->dev_addr[2]<<8) | + dev->dev_addr[3], + ETHER_ARC_DATA(dev->base_addr)); + + writew(4,ETHER_ARC_ADR(dev->base_addr)); + tmp=readl(ETHER_ARC_DATA(dev->base_addr)); + tmp&=0xffff; + tmp|=(dev->dev_addr[4]<<24) | (dev->dev_addr[5]<<16); + writel(tmp, ETHER_ARC_DATA(dev->base_addr)); + /* Enable this entry in the ARC */ + + writel(1,ETHER_ARC_ENA(dev->base_addr)); + + return; +} + + +static void ether00_reset(struct net_device *dev) +{ + /* reset the controller */ + writew(ETHER_MAC_CTL_RESET_MSK,ETHER_MAC_CTL(dev->base_addr)); + + /* + * Make sure we're not going to send anything + */ + + writew(ETHER_TX_CTL_TXHALT_MSK,ETHER_TX_CTL(dev->base_addr)); + + /* + * Make sure we're not going to receive anything + */ + writew(ETHER_RX_CTL_RXHALT_MSK,ETHER_RX_CTL(dev->base_addr)); + + /* + * Disable Interrupts for now, and set the burst size to 8 bytes + */ + + writel(ETHER_DMA_CTL_INTMASK_MSK | + ((8 << ETHER_DMA_CTL_DMBURST_OFST) & ETHER_DMA_CTL_DMBURST_MSK) + |(2<base_addr)); + + + /* + * Set TxThrsh - start transmitting a packet after 1514 + * bytes or when a packet is complete, whichever comes first + */ + writew(1514,ETHER_TXTHRSH(dev->base_addr)); + + /* + * Set TxPollCtr. Each cycle is + * 61.44 microseconds with a 33 MHz bus + */ + writew(1,ETHER_TXPOLLCTR(dev->base_addr)); + + /* + * Set Rx_Ctl - Turn off reception and let RxData turn it + * on later + */ + writew(ETHER_RX_CTL_RXHALT_MSK,ETHER_RX_CTL(dev->base_addr)); + +} + + +static void ether00_set_multicast(struct net_device* dev) +{ + int count=dev->mc_count; + + /* Set promiscuous mode if it's asked for. */ + + if (dev->flags&IFF_PROMISC){ + + writew( ETHER_ARC_CTL_COMPEN_MSK | + ETHER_ARC_CTL_BROADACC_MSK | + ETHER_ARC_CTL_GROUPACC_MSK | + ETHER_ARC_CTL_STATIONACC_MSK, + ETHER_ARC_CTL(dev->base_addr)); + return; + } + + /* + * Get all multicast packets if required, or if there are too + * many addresses to fit in hardware + */ + if (dev->flags & IFF_ALLMULTI){ + writew( ETHER_ARC_CTL_COMPEN_MSK | + ETHER_ARC_CTL_GROUPACC_MSK | + ETHER_ARC_CTL_BROADACC_MSK, + ETHER_ARC_CTL(dev->base_addr)); + return; + } + if (dev->mc_count > (ETHER_ARC_SIZE - 1)){ + + printk(KERN_WARNING "Too many multicast addresses for hardware to filter - receiving all multicast packets\n"); + writew( ETHER_ARC_CTL_COMPEN_MSK | + ETHER_ARC_CTL_GROUPACC_MSK | + ETHER_ARC_CTL_BROADACC_MSK, + ETHER_ARC_CTL(dev->base_addr)); + return; + } + + if(dev->mc_count){ + struct dev_mc_list *mc_list_ent=dev->mc_list; + unsigned int temp,i; + DEBUG(printk("mc_count=%d mc_list=%#x\n",dev-> mc_count, dev->mc_list)); + DEBUG(printk("mc addr=%02#x%02x%02x%02x%02x%02x\n", + mc_list_ent->dmi_addr[5], + mc_list_ent->dmi_addr[4], + mc_list_ent->dmi_addr[3], + mc_list_ent->dmi_addr[2], + mc_list_ent->dmi_addr[1], + mc_list_ent->dmi_addr[0]);) + + /* + * The first 6 bytes are the MAC address, so + * don't change them! + */ + writew(4,ETHER_ARC_ADR(dev->base_addr)); + temp=readl(ETHER_ARC_DATA(dev->base_addr)); + temp&=0xffff0000; + + /* Disable the current multicast stuff */ + writel(1,ETHER_ARC_ENA(dev->base_addr)); + + for(;;){ + temp|=mc_list_ent->dmi_addr[1] | + mc_list_ent->dmi_addr[0]<<8; + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + i=readl(ETHER_ARC_ADR(dev->base_addr)); + writew(i+4,ETHER_ARC_ADR(dev->base_addr)); + + temp=mc_list_ent->dmi_addr[5]| + mc_list_ent->dmi_addr[4]<<8 | + mc_list_ent->dmi_addr[3]<<16 | + mc_list_ent->dmi_addr[2]<<24; + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + count--; + if(!mc_list_ent->next || !count){ + break; + } + DEBUG(printk("mc_list_next=%#x\n",mc_list_ent->next);) + mc_list_ent=mc_list_ent->next; + + + i=readl(ETHER_ARC_ADR(dev->base_addr)); + writel(i+4,ETHER_ARC_ADR(dev->base_addr)); + + temp=mc_list_ent->dmi_addr[3]| + mc_list_ent->dmi_addr[2]<<8 | + mc_list_ent->dmi_addr[1]<<16 | + mc_list_ent->dmi_addr[0]<<24; + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + i=readl(ETHER_ARC_ADR(dev->base_addr)); + writel(i+4,ETHER_ARC_ADR(dev->base_addr)); + + temp=mc_list_ent->dmi_addr[4]<<16 | + mc_list_ent->dmi_addr[5]<<24; + + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + count--; + if(!mc_list_ent->next || !count){ + break; + } + mc_list_ent=mc_list_ent->next; + } + + + if(count) + printk(KERN_WARNING "Multicast list size error\n"); + + + writew( ETHER_ARC_CTL_BROADACC_MSK| + ETHER_ARC_CTL_COMPEN_MSK, + ETHER_ARC_CTL(dev->base_addr)); + + } + + /* enable the active ARC enties */ + writew((1<<(count+2))-1,ETHER_ARC_ENA(dev->base_addr)); +} + + +static int ether00_open(struct net_device* dev) +{ + int result,tmp; + struct net_priv* priv; + + if (!is_valid_ether_addr(dev->dev_addr)) + return -EINVAL; + + /* Allocate private memory */ + dev->priv=kmalloc(sizeof(struct net_priv),GFP_KERNEL); + if(!dev->priv) + return -ENOMEM; + memset(dev->priv,0,sizeof(struct net_priv)); + priv=(struct net_priv*)dev->priv; + priv->tq_memupdate.routine=ether00_mem_update; + priv->tq_memupdate.data=(void*) dev; + spin_lock_init(&priv->rx_lock); + + /* Install interrupt handlers */ + result=request_irq(dev->irq,ether00_int,0,"ether00",dev); + if(result) + goto open_err1; + + result=request_irq(2,ether00_phy_int,0,"ether00_phy",dev); + if(result) + goto open_err2; + + ether00_reset(dev); + result=ether00_mem_init(dev); + if(result) + goto open_err3; + + + ether00_setup_ethernet_address(dev); + + ether00_set_multicast(dev); + + result=ether00_write_phy(dev,PHY_CONTROL, PHY_CONTROL_ANEGEN_MSK | PHY_CONTROL_RANEG_MSK); + if(result) + goto open_err4; + result=ether00_write_phy(dev,PHY_IRQ_CONTROL, PHY_IRQ_CONTROL_LS_CHG_IE_MSK | + PHY_IRQ_CONTROL_ANEG_COMP_IE_MSK); + if(result) + goto open_err4; + + /* Start the device enable interrupts */ + writew(ETHER_RX_CTL_RXEN_MSK +// | ETHER_RX_CTL_STRIPCRC_MSK + | ETHER_RX_CTL_ENGOOD_MSK + | ETHER_RX_CTL_ENRXPAR_MSK| ETHER_RX_CTL_ENLONGERR_MSK + | ETHER_RX_CTL_ENOVER_MSK| ETHER_RX_CTL_ENCRCERR_MSK, + ETHER_RX_CTL(dev->base_addr)); + + writew(ETHER_TX_CTL_TXEN_MSK| + ETHER_TX_CTL_ENEXDEFER_MSK| + ETHER_TX_CTL_ENLCARR_MSK| + ETHER_TX_CTL_ENEXCOLL_MSK| + ETHER_TX_CTL_ENLATECOLL_MSK| + ETHER_TX_CTL_ENTXPAR_MSK| + ETHER_TX_CTL_ENCOMP_MSK, + ETHER_TX_CTL(dev->base_addr)); + + tmp=readl(ETHER_DMA_CTL(dev->base_addr)); + writel(tmp&~ETHER_DMA_CTL_INTMASK_MSK,ETHER_DMA_CTL(dev->base_addr)); + + return 0; + + open_err4: + ether00_reset(dev); + open_err3: + free_irq(2,dev); + open_err2: + free_irq(dev->irq,dev); + open_err1: + kfree(dev->priv); + return result; + +} + + +static int ether00_tx(struct sk_buff* skb, struct net_device* dev) +{ + struct net_priv *priv=dev->priv; + struct tx_fda_ent *fda_ptr; + int i; + + + /* + * Find an empty slot in which to stick the frame + */ + fda_ptr=(struct tx_fda_ent*)__dma_va(readl(ETHER_TXFRMPTR(dev->base_addr))); + i=0; + while(ifd.FDStat||(fda_ptr->fd.FDCtl & FDCTL_COWNSFD_MSK)){ + fda_ptr =(struct tx_fda_ent*) __dma_va((struct tx_fda_ent*)fda_ptr->fd.FDNext); + } + else { + break; + } + i++; + } + + /* Write the skb data from the cache*/ + consistent_sync(skb->data,skb->len,PCI_DMA_TODEVICE); + fda_ptr->bd.BuffData=(char*)__pa(skb->data); + fda_ptr->bd.BuffLength=(unsigned short)skb->len; + /* Save the pointer to the skb for freeing later */ + fda_ptr->fd.FDSystem=(unsigned int)skb; + fda_ptr->fd.FDStat=0; + /* Pass ownership of the buffers to the controller */ + fda_ptr->fd.FDCtl=1; + fda_ptr->fd.FDCtl|=FDCTL_COWNSFD_MSK; + + /* If the next buffer in the list is full, stop the queue */ + fda_ptr=(struct tx_fda_ent*)__dma_va(fda_ptr->fd.FDNext); + if ((fda_ptr->fd.FDStat)||(fda_ptr->fd.FDCtl & FDCTL_COWNSFD_MSK)){ + netif_stop_queue(dev); + priv->queue_stopped=1; + } + + return 0; +} + +static struct net_device_stats *ether00_stats(struct net_device* dev) +{ + struct net_priv *priv=dev->priv; + return &priv->stats; +} + + +static int ether00_stop(struct net_device* dev) +{ + struct net_priv *priv=dev->priv; + int tmp; + + /* Stop/disable the device. */ + tmp=readw(ETHER_RX_CTL(dev->base_addr)); + tmp&=~(ETHER_RX_CTL_RXEN_MSK | ETHER_RX_CTL_ENGOOD_MSK); + tmp|=ETHER_RX_CTL_RXHALT_MSK; + writew(tmp,ETHER_RX_CTL(dev->base_addr)); + + tmp=readl(ETHER_TX_CTL(dev->base_addr)); + tmp&=~ETHER_TX_CTL_TXEN_MSK; + tmp|=ETHER_TX_CTL_TXHALT_MSK; + writel(tmp,ETHER_TX_CTL(dev->base_addr)); + + /* Free up system resources */ + free_irq(dev->irq,dev); + free_irq(2,dev); + iounmap(priv->dma_data); + kfree(priv); + + return 0; +} + + +static void ether00_get_ethernet_address(struct net_device* dev) +{ + struct mtd_info *mymtd=NULL; + int i; + size_t retlen; + + /* + * For the Epxa10 dev board (camelot), the ethernet MAC + * address is of the form 00:aa:aa:00:xx:xx where + * 00:aa:aa is the Altera vendor ID and xx:xx is the + * last 2 bytes of the board serial number, as programmed + * into the OTP area of the flash device on EBI1. If this + * isn't an expa10 dev board, or there's no mtd support to + * read the serial number from flash then we'll force the + * use to set their own mac address using ifconfig. + */ + +#ifdef CONFIG_ARCH_CAMELOT +#ifdef CONFIG_MTD + /* get the mtd_info structure for the first mtd device*/ + for(i=0;iname,"EPXA10DB flash")) + break; + } + + if(!mymtd || !mymtd->read_user_prot_reg){ + printk(KERN_WARNING "%s: Failed to read MAC address from flash\n",dev->name); + }else{ + mymtd->read_user_prot_reg(mymtd,2,1,&retlen,&dev->dev_addr[5]); + mymtd->read_user_prot_reg(mymtd,3,1,&retlen,&dev->dev_addr[4]); + dev->dev_addr[3]=0; + dev->dev_addr[2]=vendor_id[1]; + dev->dev_addr[1]=vendor_id[0]; + dev->dev_addr[0]=0; + } +#else + printk(KERN_WARNING "%s: MTD support required to read MAC address from EPXA10 dev board\n", dev->name); +#endif +#endif + + if (!is_valid_ether_addr(dev->dev_addr)) + printk("%s: Invalid ethernet MAC address. Please set using " + "ifconfig\n", dev->name); + +} + +static int ether00_init(struct net_device* dev) +{ + + ether_setup(dev); + + dev->open=ether00_open; + dev->stop=ether00_stop; + dev->set_multicast_list=ether00_set_multicast; + dev->hard_start_xmit=ether00_tx; + dev->get_stats=ether00_stats; + + ether00_get_ethernet_address(dev); + + SET_MODULE_OWNER(dev); + return 0; +} + +/* + * Keep a mapping of dev_info addresses -> port lines to use when + * removing ports dev==NULL indicates unused entry + */ + + +static struct net_device* dev_list[ETH_NR]; + +static int ether00_add_device(struct pldhs_dev_info* dev_info,void* dev_ps_data) +{ + struct net_device *dev; + void *map_addr; + int result; + int i; + + + i=0; + while(dev_list[i]) + i++; + + if(i==ETH_NR){ + printk(KERN_WARNING "ether00: Maximum number of ports reached\n"); + return 0; + } + + + dev=kmalloc(sizeof(struct net_device),GFP_KERNEL); + if(!dev){ + return -ENOMEM; + } + memset(dev,0,sizeof(struct net_device)); + map_addr=ioremap_nocache(dev_info->base_addr,SZ_4K); + if(!map_addr){ + return -ENOMEM; + } + + dev->init=ether00_init; + strcpy(dev->name,"eth%d"); + dev->base_addr=(unsigned int)map_addr; + dev->irq=dev_info->irq; + dev->features=NETIF_F_DYNALLOC | NETIF_F_HW_CSUM; + + if(check_mem_region((unsigned int)map_addr, MAC_REG_SIZE)){ + return -EBUSY; + } + request_mem_region((unsigned int)map_addr, MAC_REG_SIZE, "ether00"); + + result=register_netdev(dev); + if(result){ + printk("Ether00: Error %i registering driver\n",result); + return result; + } + printk("registered ether00 device at %#x\n",dev_info->base_addr); + + dev_list[i]=dev; + + return result; +} + + +static int ether00_remove_devices(void) +{ + int i; + + for(i=0;ibase_addr); + release_mem_region(dev_list[i]->base_addr, MAC_REG_SIZE); + kfree(dev_list[i]); + dev_list[i]=0; + } + } + return 0; +} + +static struct pld_hotswap_ops ether00_pldhs_ops={ + name: ETHER00_NAME, + add_device: ether00_add_device, + remove_devices: ether00_remove_devices, +}; + + +static void __exit ether00_cleanup_module(void) +{ + int result; + result=ether00_remove_devices(); + if(result) + printk(KERN_WARNING "ether00: failed to remove all devices\n"); + + pldhs_unregister_driver(ETHER00_NAME); +} +module_exit(ether00_cleanup_module); + + +static int __init ether00_mod_init(void) +{ + printk("mod init\n"); + return pldhs_register_driver(ðer00_pldhs_ops); + +} + +module_init(ether00_mod_init); + diff -urN linux-2.5.69-bk13/drivers/net/arm/ether1.c linux-2.5.69-bk14/drivers/net/arm/ether1.c --- linux-2.5.69-bk13/drivers/net/arm/ether1.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/ether1.c 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,1106 @@ +/* + * linux/drivers/acorn/net/ether1.c + * + * Copyright (C) 1996-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Acorn ether1 driver (82586 chip) for Acorn machines + * + * We basically keep two queues in the cards memory - one for transmit + * and one for receive. Each has a head and a tail. The head is where + * we/the chip adds packets to be transmitted/received, and the tail + * is where the transmitter has got to/where the receiver will stop. + * Both of these queues are circular, and since the chip is running + * all the time, we have to be careful when we modify the pointers etc + * so that the buffer memory contents is valid all the time. + * + * Change log: + * 1.00 RMK Released + * 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now. + * 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready + * 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt. + * Should prevent lockup. + * 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong. + * TDR now only reports failure when chip reports non-zero + * TDR time-distance. + * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1 + * 1.06 RMK 10/02/2000 Updated for 2.3.43 + * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define __ETHER1_C +#include "ether1.h" + +static unsigned int net_debug = NET_DEBUG; + +#define BUFFER_SIZE 0x10000 +#define TX_AREA_START 0x00100 +#define TX_AREA_END 0x05000 +#define RX_AREA_START 0x05000 +#define RX_AREA_END 0x0fc00 + +static int ether1_open(struct net_device *dev); +static int ether1_sendpacket(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t ether1_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int ether1_close(struct net_device *dev); +static struct net_device_stats *ether1_getstats(struct net_device *dev); +static void ether1_setmulticastlist(struct net_device *dev); +static void ether1_timeout(struct net_device *dev); + +/* ------------------------------------------------------------------------- */ + +static char version[] __initdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; + +#define BUS_16 16 +#define BUS_8 8 + +/* ------------------------------------------------------------------------- */ + +#define DISABLEIRQS 1 +#define NORMALIRQS 0 + +#define ether1_inw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs) +#define ether1_outw(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs) + +static inline unsigned short +ether1_inw_p (struct net_device *dev, int addr, int svflgs) +{ + unsigned long flags; + unsigned short ret; + + if (svflgs) + local_irq_save (flags); + + outb (addr >> 12, REG_PAGE); + ret = inw (ETHER1_RAM + ((addr & 4095) >> 1)); + if (svflgs) + local_irq_restore (flags); + return ret; +} + +static inline void +ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs) +{ + unsigned long flags; + + if (svflgs) + local_irq_save (flags); + + outb (addr >> 12, REG_PAGE); + outw (val, ETHER1_RAM + ((addr & 4095) >> 1)); + if (svflgs) + local_irq_restore (flags); +} + +/* + * Some inline assembler to allow fast transfers on to/off of the card. + * Since this driver depends on some features presented by the ARM + * specific architecture, and that you can't configure this driver + * without specifiing ARM mode, this is not a problem. + * + * This routine is essentially an optimised memcpy from the card's + * onboard RAM to kernel memory. + */ +static void +ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) +{ + unsigned int page, thislen, offset, addr; + + offset = start & 4095; + page = start >> 12; + addr = ioaddr(ETHER1_RAM + (offset >> 1)); + + if (offset + length > 4096) + thislen = 4096 - offset; + else + thislen = length; + + do { + int used; + + outb(page, REG_PAGE); + length -= thislen; + + __asm__ __volatile__( + "subs %3, %3, #2 + bmi 2f +1: ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bmi 2f + ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bmi 2f + ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bmi 2f + ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bpl 1b +2: adds %3, %3, #1 + ldreqb %0, [%1] + streqb %0, [%2]" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + + addr = ioaddr(ETHER1_RAM); + + thislen = length; + if (thislen > 4096) + thislen = 4096; + page++; + } while (thislen); +} + +static void +ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) +{ + unsigned int page, thislen, offset, addr; + + offset = start & 4095; + page = start >> 12; + addr = ioaddr(ETHER1_RAM + (offset >> 1)); + + if (offset + length > 4096) + thislen = 4096 - offset; + else + thislen = length; + + do { + int used; + + outb(page, REG_PAGE); + length -= thislen; + + __asm__ __volatile__( + "subs %3, %3, #2 + bmi 2f +1: ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bmi 2f + ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bmi 2f + ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bmi 2f + ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bpl 1b +2: adds %3, %3, #1 + ldreqb %0, [%2] + streqb %0, [%1]" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + + addr = ioaddr(ETHER1_RAM); + + thislen = length; + if (thislen > 4096) + thislen = 4096; + page++; + } while (thislen); +} + +static int __init +ether1_ramtest(struct net_device *dev, unsigned char byte) +{ + unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); + int i, ret = BUFFER_SIZE; + int max_errors = 15; + int bad = -1; + int bad_start = 0; + + if (!buffer) + return 1; + + memset (buffer, byte, BUFFER_SIZE); + ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE); + memset (buffer, byte ^ 0xff, BUFFER_SIZE); + ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE); + + for (i = 0; i < BUFFER_SIZE; i++) { + if (buffer[i] != byte) { + if (max_errors >= 0 && bad != buffer[i]) { + if (bad != -1) + printk ("\n"); + printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X", + dev->name, buffer[i], byte, i); + ret = -ENODEV; + max_errors --; + bad = buffer[i]; + bad_start = i; + } + } else { + if (bad != -1) { + if (bad_start == i - 1) + printk ("\n"); + else + printk (" - 0x%04X\n", i - 1); + bad = -1; + } + } + } + + if (bad != -1) + printk (" - 0x%04X\n", BUFFER_SIZE); + kfree (buffer); + + return ret; +} + +static int +ether1_reset (struct net_device *dev) +{ + outb (CTRL_RST|CTRL_ACK, REG_CONTROL); + return BUS_16; +} + +static int __init +ether1_init_2(struct net_device *dev) +{ + int i; + dev->mem_start = 0; + + i = ether1_ramtest (dev, 0x5a); + + if (i > 0) + i = ether1_ramtest (dev, 0x1e); + + if (i <= 0) + return -ENODEV; + + dev->mem_end = i; + return 0; +} + +/* + * These are the structures that are loaded into the ether RAM card to + * initialise the 82586 + */ + +/* at 0x0100 */ +#define NOP_ADDR (TX_AREA_START) +#define NOP_SIZE (0x06) +static nop_t init_nop = { + 0, + CMD_NOP, + NOP_ADDR +}; + +/* at 0x003a */ +#define TDR_ADDR (0x003a) +#define TDR_SIZE (0x08) +static tdr_t init_tdr = { + 0, + CMD_TDR | CMD_INTR, + NOP_ADDR, + 0 +}; + +/* at 0x002e */ +#define MC_ADDR (0x002e) +#define MC_SIZE (0x0c) +static mc_t init_mc = { + 0, + CMD_SETMULTICAST, + TDR_ADDR, + 0, + { { 0, } } +}; + +/* at 0x0022 */ +#define SA_ADDR (0x0022) +#define SA_SIZE (0x0c) +static sa_t init_sa = { + 0, + CMD_SETADDRESS, + MC_ADDR, + { 0, } +}; + +/* at 0x0010 */ +#define CFG_ADDR (0x0010) +#define CFG_SIZE (0x12) +static cfg_t init_cfg = { + 0, + CMD_CONFIG, + SA_ADDR, + 8, + 8, + CFG8_SRDY, + CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6), + 0, + 0x60, + 0, + CFG13_RETRY(15) | CFG13_SLOTH(2), + 0, +}; + +/* at 0x0000 */ +#define SCB_ADDR (0x0000) +#define SCB_SIZE (0x10) +static scb_t init_scb = { + 0, + SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX, + CFG_ADDR, + RX_AREA_START, + 0, + 0, + 0, + 0 +}; + +/* at 0xffee */ +#define ISCP_ADDR (0xffee) +#define ISCP_SIZE (0x08) +static iscp_t init_iscp = { + 1, + SCB_ADDR, + 0x0000, + 0x0000 +}; + +/* at 0xfff6 */ +#define SCP_ADDR (0xfff6) +#define SCP_SIZE (0x0a) +static scp_t init_scp = { + SCP_SY_16BBUS, + { 0, 0 }, + ISCP_ADDR, + 0 +}; + +#define RFD_SIZE (0x16) +static rfd_t init_rfd = { + 0, + 0, + 0, + 0, + { 0, }, + { 0, }, + 0 +}; + +#define RBD_SIZE (0x0a) +static rbd_t init_rbd = { + 0, + 0, + 0, + 0, + ETH_FRAME_LEN + 8 +}; + +#define TX_SIZE (0x08) +#define TBD_SIZE (0x08) + +static int +ether1_init_for_open (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int i, status, addr, next, next2; + int failures = 0; + unsigned long timeout; + + outb (CTRL_RST|CTRL_ACK, REG_CONTROL); + + for (i = 0; i < 6; i++) + init_sa.sa_addr[i] = dev->dev_addr[i]; + + /* load data structures into ether1 RAM */ + ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE); + ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE); + ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE); + ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE); + ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE); + ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE); + ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE); + ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE); + + if (ether1_inw (dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) { + printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n", + dev->name); + return 1; + } + + /* + * setup circularly linked list of { rfd, rbd, buffer }, with + * all rfds circularly linked, rbds circularly linked. + * First rfd is linked to scp, first rbd is linked to first + * rfd. Last rbd has a suspend command. + */ + addr = RX_AREA_START; + do { + next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; + next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; + + if (next2 >= RX_AREA_END) { + next = RX_AREA_START; + init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND; + priv->rx_tail = addr; + } else + init_rfd.rfd_command = 0; + if (addr == RX_AREA_START) + init_rfd.rfd_rbdoffset = addr + RFD_SIZE; + else + init_rfd.rfd_rbdoffset = 0; + init_rfd.rfd_link = next; + init_rbd.rbd_link = next + RFD_SIZE; + init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE; + + ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE); + ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE); + addr = next; + } while (next2 < RX_AREA_END); + + priv->tx_link = NOP_ADDR; + priv->tx_head = NOP_ADDR + NOP_SIZE; + priv->tx_tail = TDR_ADDR; + priv->rx_head = RX_AREA_START; + + /* release reset & give 586 a prod */ + priv->resetting = 1; + priv->initialising = 1; + outb (CTRL_RST, REG_CONTROL); + outb (0, REG_CONTROL); + outb (CTRL_CA, REG_CONTROL); + + /* 586 should now unset iscp.busy */ + timeout = jiffies + HZ/2; + while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) { + if (time_after(jiffies, timeout)) { + printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name); + return 1; + } + } + + /* check status of commands that we issued */ + timeout += HZ/10; + while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + timeout += HZ/10; + while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + timeout += HZ/10; + while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + timeout += HZ; + while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + } else { + status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS); + if (status & TDR_XCVRPROB) + printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name); + else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) { +#ifdef FANCY + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name, + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10, + (status & TDR_TIME) % 10); +#else + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name, + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME)); +#endif + } + } + + if (failures) + ether1_reset (dev); + return failures ? 1 : 0; +} + +/* ------------------------------------------------------------------------- */ + +static int +ether1_txalloc (struct net_device *dev, int size) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int start, tail; + + size = (size + 1) & ~1; + tail = priv->tx_tail; + + if (priv->tx_head + size > TX_AREA_END) { + if (tail > priv->tx_head) + return -1; + start = TX_AREA_START; + if (start + size > tail) + return -1; + priv->tx_head = start + size; + } else { + if (priv->tx_head < tail && (priv->tx_head + size) > tail) + return -1; + start = priv->tx_head; + priv->tx_head += size; + } + + return start; +} + +static int +ether1_open (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_WARNING "%s: invalid ethernet MAC address\n", + dev->name); + return -EINVAL; + } + + if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev)) + return -EAGAIN; + + memset (&priv->stats, 0, sizeof (struct net_device_stats)); + + if (ether1_init_for_open (dev)) { + free_irq (dev->irq, dev); + return -EAGAIN; + } + + netif_start_queue(dev); + + return 0; +} + +static void +ether1_timeout(struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + + printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n", + dev->name); + printk(KERN_WARNING "%s: resetting device\n", dev->name); + + ether1_reset (dev); + + if (ether1_init_for_open (dev)) + printk (KERN_ERR "%s: unable to restart interface\n", dev->name); + + priv->stats.tx_errors++; + netif_wake_queue(dev); +} + +static int +ether1_sendpacket (struct sk_buff *skb, struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr; + unsigned long flags; + tx_t tx; + tbd_t tbd; + nop_t nop; + + if (priv->restart) { + printk(KERN_WARNING "%s: resetting device\n", dev->name); + + ether1_reset(dev); + + if (ether1_init_for_open(dev)) + printk(KERN_ERR "%s: unable to restart interface\n", dev->name); + else + priv->restart = 0; + } + + /* + * insert packet followed by a nop + */ + txaddr = ether1_txalloc (dev, TX_SIZE); + tbdaddr = ether1_txalloc (dev, TBD_SIZE); + dataddr = ether1_txalloc (dev, len); + nopaddr = ether1_txalloc (dev, NOP_SIZE); + + tx.tx_status = 0; + tx.tx_command = CMD_TX | CMD_INTR; + tx.tx_link = nopaddr; + tx.tx_tbdoffset = tbdaddr; + tbd.tbd_opts = TBD_EOL | len; + tbd.tbd_link = I82586_NULL; + tbd.tbd_bufl = dataddr; + tbd.tbd_bufh = 0; + nop.nop_status = 0; + nop.nop_command = CMD_NOP; + nop.nop_link = nopaddr; + + local_irq_save(flags); + ether1_writebuffer (dev, &tx, txaddr, TX_SIZE); + ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE); + ether1_writebuffer (dev, skb->data, dataddr, len); + ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE); + tmp = priv->tx_link; + priv->tx_link = nopaddr; + + /* now reset the previous nop pointer */ + ether1_outw (dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS); + + local_irq_restore(flags); + + /* handle transmit */ + dev->trans_start = jiffies; + + /* check to see if we have room for a full sized ether frame */ + tmp = priv->tx_head; + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); + priv->tx_head = tmp; + dev_kfree_skb (skb); + + if (tst == -1) + netif_stop_queue(dev); + + return 0; +} + +static void +ether1_xmit_done (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + nop_t nop; + int caddr, tst; + + caddr = priv->tx_tail; + +again: + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + + switch (nop.nop_command & CMD_MASK) { + case CMD_TDR: + /* special case */ + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) + != (unsigned short)I82586_NULL) { + ether1_outw(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, + scb_command, NORMALIRQS); + outb (CTRL_CA, REG_CONTROL); + } + priv->tx_tail = NOP_ADDR; + return; + + case CMD_NOP: + if (nop.nop_link == caddr) { + if (priv->initialising == 0) + printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name); + else + priv->initialising = 0; + return; + } + if (caddr == nop.nop_link) + return; + caddr = nop.nop_link; + goto again; + + case CMD_TX: + if (nop.nop_status & STAT_COMPLETE) + break; + printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name); + priv->restart = 1; + return; + + default: + printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name, + nop.nop_command & CMD_MASK, caddr); + priv->restart = 1; + return; + } + + while (nop.nop_status & STAT_COMPLETE) { + if (nop.nop_status & STAT_OK) { + priv->stats.tx_packets ++; + priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS); + } else { + priv->stats.tx_errors ++; + + if (nop.nop_status & STAT_COLLAFTERTX) + priv->stats.collisions ++; + if (nop.nop_status & STAT_NOCARRIER) + priv->stats.tx_carrier_errors ++; + if (nop.nop_status & STAT_TXLOSTCTS) + printk (KERN_WARNING "%s: cts lost\n", dev->name); + if (nop.nop_status & STAT_TXSLOWDMA) + priv->stats.tx_fifo_errors ++; + if (nop.nop_status & STAT_COLLEXCESSIVE) + priv->stats.collisions += 16; + } + + if (nop.nop_link == caddr) { + printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name); + break; + } + + caddr = nop.nop_link; + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + if ((nop.nop_command & CMD_MASK) != CMD_NOP) { + printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name); + break; + } + + if (caddr == nop.nop_link) + break; + + caddr = nop.nop_link; + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + if ((nop.nop_command & CMD_MASK) != CMD_TX) { + printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name); + break; + } + } + priv->tx_tail = caddr; + + caddr = priv->tx_head; + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); + priv->tx_head = caddr; + if (tst != -1) + netif_wake_queue(dev); +} + +static void +ether1_recv_done (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int status; + int nexttail, rbdaddr; + rbd_t rbd; + + do { + status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status, NORMALIRQS); + if ((status & RFD_COMPLETE) == 0) + break; + + rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS); + ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE); + + if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) { + int length = rbd.rbd_status & RBD_ACNT; + struct sk_buff *skb; + + length = (length + 1) & ~1; + skb = dev_alloc_skb (length + 2); + + if (skb) { + skb->dev = dev; + skb_reserve (skb, 2); + + ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); + + skb->protocol = eth_type_trans (skb, dev); + netif_rx (skb); + priv->stats.rx_packets ++; + } else + priv->stats.rx_dropped ++; + } else { + printk(KERN_WARNING "%s: %s\n", dev->name, + (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid"); + priv->stats.rx_dropped ++; + } + + nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link, NORMALIRQS); + /* nexttail should be rx_head */ + if (nexttail != priv->rx_head) + printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n", + dev->name, nexttail, priv->rx_head); + ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS); + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command, NORMALIRQS); + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status, NORMALIRQS); + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS); + + priv->rx_tail = nexttail; + priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link, NORMALIRQS); + } while (1); +} + +static irqreturn_t +ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int status; + + status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS); + + if (status) { + ether1_outw(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX), + SCB_ADDR, scb_t, scb_command, NORMALIRQS); + outb (CTRL_CA | CTRL_ACK, REG_CONTROL); + if (status & SCB_STCX) { + ether1_xmit_done (dev); + } + if (status & SCB_STCNA) { + if (priv->resetting == 0) + printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name); + else + priv->resetting += 1; + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) + != (unsigned short)I82586_NULL) { + ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS); + outb (CTRL_CA, REG_CONTROL); + } + if (priv->resetting == 2) + priv->resetting = 0; + } + if (status & SCB_STFR) { + ether1_recv_done (dev); + } + if (status & SCB_STRNR) { + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) { + printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name); + ether1_outw (dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS); + outb (CTRL_CA, REG_CONTROL); + priv->stats.rx_dropped ++; /* we suspended due to lack of buffer space */ + } else + printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS)); + printk (KERN_WARNING "RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, + NORMALIRQS)); + } + } else + outb (CTRL_ACK, REG_CONTROL); + + return IRQ_HANDLED; +} + +static int +ether1_close (struct net_device *dev) +{ + ether1_reset (dev); + + free_irq(dev->irq, dev); + + return 0; +} + +static struct net_device_stats * +ether1_getstats (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + return &priv->stats; +} + +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets. + * num_addrs == 0 Normal mode, clear multicast list. + * num_addrs > 0 Multicast mode, receive normal and MC packets, and do + * best-effort filtering. + */ +static void +ether1_setmulticastlist (struct net_device *dev) +{ +} + +/* ------------------------------------------------------------------------- */ + +static void __init ether1_banner(void) +{ + static unsigned int version_printed = 0; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static int __devinit +ether1_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct net_device *dev; + struct ether1_priv *priv; + int i, ret = 0; + + ether1_banner(); + + dev = init_etherdev(NULL, sizeof(struct ether1_priv)); + if (!dev) { + ret = -ENOMEM; + goto out; + } + + SET_MODULE_OWNER(dev); + + dev->base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); + dev->irq = ec->irq; + + /* + * these will not fail - the nature of the bus ensures this + */ + request_region(dev->base_addr, 16, dev->name); + request_region(dev->base_addr + 0x800, 4096, dev->name); + + priv = (struct ether1_priv *)dev->priv; + if ((priv->bus_type = ether1_reset(dev)) == 0) { + ret = -ENODEV; + goto release; + } + + printk(KERN_INFO "%s: ether1 in slot %d, ", + dev->name, ec->slot_no); + + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = inb(IDPROM_ADDRESS + i); + printk ("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + } + + if (ether1_init_2(dev)) { + ret = -ENODEV; + goto release; + } + + dev->open = ether1_open; + dev->stop = ether1_close; + dev->hard_start_xmit = ether1_sendpacket; + dev->get_stats = ether1_getstats; + dev->set_multicast_list = ether1_setmulticastlist; + dev->tx_timeout = ether1_timeout; + dev->watchdog_timeo = 5 * HZ / 100; + + ecard_set_drvdata(ec, dev); + return 0; + +release: + release_region(dev->base_addr, 16); + release_region(dev->base_addr + 0x800, 4096); + unregister_netdev(dev); + kfree(dev); +out: + return ret; +} + +static void __devexit ether1_remove(struct expansion_card *ec) +{ + struct net_device *dev = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + unregister_netdev(dev); + + release_region(dev->base_addr, 16); + release_region(dev->base_addr + 0x800, 4096); + kfree(dev); +} + +static const struct ecard_id ether1_ids[] = { + { MANU_ACORN, PROD_ACORN_ETHER1 }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver ether1_driver = { + .probe = ether1_probe, + .remove = __devexit_p(ether1_remove), + .id_table = ether1_ids, + .drv = { + .name = "ether1", + }, +}; + +static int __init ether1_init(void) +{ + return ecard_register_driver(ðer1_driver); +} + +static void __exit ether1_exit(void) +{ + ecard_remove_driver(ðer1_driver); +} + +module_init(ether1_init); +module_exit(ether1_exit); + +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/net/arm/ether1.h linux-2.5.69-bk14/drivers/net/arm/ether1.h --- linux-2.5.69-bk13/drivers/net/arm/ether1.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/ether1.h 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,278 @@ +/* + * linux/drivers/acorn/net/ether1.h + * + * Copyright (C) 1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Network driver for Acorn Ether1 cards. + */ + +#ifndef _LINUX_ether1_H +#define _LINUX_ether1_H + +#ifdef __ETHER1_C +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* Page register */ +#define REG_PAGE (dev->base_addr + 0x00) + +/* Control register */ +#define REG_CONTROL (dev->base_addr + 0x01) +#define CTRL_RST 0x01 +#define CTRL_LOOPBACK 0x02 +#define CTRL_CA 0x04 +#define CTRL_ACK 0x08 + +#define ETHER1_RAM (dev->base_addr + 0x800) + +/* HW address */ +#define IDPROM_ADDRESS (dev->base_addr + 0x09) + +struct ether1_priv { + struct net_device_stats stats; + unsigned int tx_link; + unsigned int tx_head; + volatile unsigned int tx_tail; + volatile unsigned int rx_head; + volatile unsigned int rx_tail; + unsigned char bus_type; + unsigned char resetting; + unsigned char initialising : 1; + unsigned char restart : 1; +}; + +#define I82586_NULL (-1) + +typedef struct { /* tdr */ + unsigned short tdr_status; + unsigned short tdr_command; + unsigned short tdr_link; + unsigned short tdr_result; +#define TDR_TIME (0x7ff) +#define TDR_SHORT (1 << 12) +#define TDR_OPEN (1 << 13) +#define TDR_XCVRPROB (1 << 14) +#define TDR_LNKOK (1 << 15) +} tdr_t; + +typedef struct { /* transmit */ + unsigned short tx_status; + unsigned short tx_command; + unsigned short tx_link; + unsigned short tx_tbdoffset; +} tx_t; + +typedef struct { /* tbd */ + unsigned short tbd_opts; +#define TBD_CNT (0x3fff) +#define TBD_EOL (1 << 15) + unsigned short tbd_link; + unsigned short tbd_bufl; + unsigned short tbd_bufh; +} tbd_t; + +typedef struct { /* rfd */ + unsigned short rfd_status; +#define RFD_NOEOF (1 << 6) +#define RFD_FRAMESHORT (1 << 7) +#define RFD_DMAOVRN (1 << 8) +#define RFD_NORESOURCES (1 << 9) +#define RFD_ALIGNERROR (1 << 10) +#define RFD_CRCERROR (1 << 11) +#define RFD_OK (1 << 13) +#define RFD_FDCONSUMED (1 << 14) +#define RFD_COMPLETE (1 << 15) + unsigned short rfd_command; +#define RFD_CMDSUSPEND (1 << 14) +#define RFD_CMDEL (1 << 15) + unsigned short rfd_link; + unsigned short rfd_rbdoffset; + unsigned char rfd_dest[6]; + unsigned char rfd_src[6]; + unsigned short rfd_len; +} rfd_t; + +typedef struct { /* rbd */ + unsigned short rbd_status; +#define RBD_ACNT (0x3fff) +#define RBD_ACNTVALID (1 << 14) +#define RBD_EOF (1 << 15) + unsigned short rbd_link; + unsigned short rbd_bufl; + unsigned short rbd_bufh; + unsigned short rbd_len; +} rbd_t; + +typedef struct { /* nop */ + unsigned short nop_status; + unsigned short nop_command; + unsigned short nop_link; +} nop_t; + +typedef struct { /* set multicast */ + unsigned short mc_status; + unsigned short mc_command; + unsigned short mc_link; + unsigned short mc_cnt; + unsigned char mc_addrs[1][6]; +} mc_t; + +typedef struct { /* set address */ + unsigned short sa_status; + unsigned short sa_command; + unsigned short sa_link; + unsigned char sa_addr[6]; +} sa_t; + +typedef struct { /* config command */ + unsigned short cfg_status; + unsigned short cfg_command; + unsigned short cfg_link; + unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */ + unsigned char cfg_fifolim; /* FIFO threshold */ + unsigned char cfg_byte8; +#define CFG8_SRDY (1 << 6) +#define CFG8_SAVEBADF (1 << 7) + unsigned char cfg_byte9; +#define CFG9_ADDRLEN(x) (x) +#define CFG9_ADDRLENBUF (1 << 3) +#define CFG9_PREAMB2 (0 << 4) +#define CFG9_PREAMB4 (1 << 4) +#define CFG9_PREAMB8 (2 << 4) +#define CFG9_PREAMB16 (3 << 4) +#define CFG9_ILOOPBACK (1 << 6) +#define CFG9_ELOOPBACK (1 << 7) + unsigned char cfg_byte10; +#define CFG10_LINPRI(x) (x) +#define CFG10_ACR(x) (x << 4) +#define CFG10_BOFMET (1 << 7) + unsigned char cfg_ifs; + unsigned char cfg_slotl; + unsigned char cfg_byte13; +#define CFG13_SLOTH(x) (x) +#define CFG13_RETRY(x) (x << 4) + unsigned char cfg_byte14; +#define CFG14_PROMISC (1 << 0) +#define CFG14_DISBRD (1 << 1) +#define CFG14_MANCH (1 << 2) +#define CFG14_TNCRS (1 << 3) +#define CFG14_NOCRC (1 << 4) +#define CFG14_CRC16 (1 << 5) +#define CFG14_BTSTF (1 << 6) +#define CFG14_FLGPAD (1 << 7) + unsigned char cfg_byte15; +#define CFG15_CSTF(x) (x) +#define CFG15_ICSS (1 << 3) +#define CFG15_CDTF(x) (x << 4) +#define CFG15_ICDS (1 << 7) + unsigned short cfg_minfrmlen; +} cfg_t; + +typedef struct { /* scb */ + unsigned short scb_status; /* status of 82586 */ +#define SCB_STRXMASK (7 << 4) /* Receive unit status */ +#define SCB_STRXIDLE (0 << 4) /* Idle */ +#define SCB_STRXSUSP (1 << 4) /* Suspended */ +#define SCB_STRXNRES (2 << 4) /* No resources */ +#define SCB_STRXRDY (4 << 4) /* Ready */ +#define SCB_STCUMASK (7 << 8) /* Command unit status */ +#define SCB_STCUIDLE (0 << 8) /* Idle */ +#define SCB_STCUSUSP (1 << 8) /* Suspended */ +#define SCB_STCUACTV (2 << 8) /* Active */ +#define SCB_STRNR (1 << 12) /* Receive unit not ready */ +#define SCB_STCNA (1 << 13) /* Command unit not ready */ +#define SCB_STFR (1 << 14) /* Frame received */ +#define SCB_STCX (1 << 15) /* Command completed */ + unsigned short scb_command; /* Next command */ +#define SCB_CMDRXSTART (1 << 4) /* Start (at rfa_offset) */ +#define SCB_CMDRXRESUME (2 << 4) /* Resume reception */ +#define SCB_CMDRXSUSPEND (3 << 4) /* Suspend reception */ +#define SCB_CMDRXABORT (4 << 4) /* Abort reception */ +#define SCB_CMDCUCSTART (1 << 8) /* Start (at cbl_offset) */ +#define SCB_CMDCUCRESUME (2 << 8) /* Resume execution */ +#define SCB_CMDCUCSUSPEND (3 << 8) /* Suspend execution */ +#define SCB_CMDCUCABORT (4 << 8) /* Abort execution */ +#define SCB_CMDACKRNR (1 << 12) /* Ack RU not ready */ +#define SCB_CMDACKCNA (1 << 13) /* Ack CU not ready */ +#define SCB_CMDACKFR (1 << 14) /* Ack Frame received */ +#define SCB_CMDACKCX (1 << 15) /* Ack Command complete */ + unsigned short scb_cbl_offset; /* Offset of first command unit */ + unsigned short scb_rfa_offset; /* Offset of first receive frame area */ + unsigned short scb_crc_errors; /* Properly aligned frame with CRC error*/ + unsigned short scb_aln_errors; /* Misaligned frames */ + unsigned short scb_rsc_errors; /* Frames lost due to no space */ + unsigned short scb_ovn_errors; /* Frames lost due to slow bus */ +} scb_t; + +typedef struct { /* iscp */ + unsigned short iscp_busy; /* set by CPU before CA */ + unsigned short iscp_offset; /* offset of SCB */ + unsigned short iscp_basel; /* base of SCB */ + unsigned short iscp_baseh; +} iscp_t; + + /* this address must be 0xfff6 */ +typedef struct { /* scp */ + unsigned short scp_sysbus; /* bus size */ +#define SCP_SY_16BBUS 0x00 +#define SCP_SY_8BBUS 0x01 + unsigned short scp_junk[2]; /* junk */ + unsigned short scp_iscpl; /* lower 16 bits of iscp */ + unsigned short scp_iscph; /* upper 16 bits of iscp */ +} scp_t; + +/* commands */ +#define CMD_NOP 0 +#define CMD_SETADDRESS 1 +#define CMD_CONFIG 2 +#define CMD_SETMULTICAST 3 +#define CMD_TX 4 +#define CMD_TDR 5 +#define CMD_DUMP 6 +#define CMD_DIAGNOSE 7 + +#define CMD_MASK 7 + +#define CMD_INTR (1 << 13) +#define CMD_SUSP (1 << 14) +#define CMD_EOL (1 << 15) + +#define STAT_COLLISIONS (15) +#define STAT_COLLEXCESSIVE (1 << 5) +#define STAT_COLLAFTERTX (1 << 6) +#define STAT_TXDEFERRED (1 << 7) +#define STAT_TXSLOWDMA (1 << 8) +#define STAT_TXLOSTCTS (1 << 9) +#define STAT_NOCARRIER (1 << 10) +#define STAT_FAIL (1 << 11) +#define STAT_ABORTED (1 << 12) +#define STAT_OK (1 << 13) +#define STAT_BUSY (1 << 14) +#define STAT_COMPLETE (1 << 15) +#endif +#endif + +/* + * Ether1 card definitions: + * + * FAST accesses: + * +0 Page register + * 16 pages + * +4 Control + * '1' = reset + * '2' = loopback + * '4' = CA + * '8' = int ack + * + * RAM at address + 0x2000 + * Pod. Prod id = 3 + * Words after ID block [base + 8 words] + * +0 pcb issue (0x0c and 0xf3 invalid) + * +1 - +6 eth hw address + */ diff -urN linux-2.5.69-bk13/drivers/net/arm/ether3.c linux-2.5.69-bk14/drivers/net/arm/ether3.c --- linux-2.5.69-bk13/drivers/net/arm/ether3.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/ether3.c 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,947 @@ +/* + * linux/drivers/acorn/net/ether3.c + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card + * for Acorn machines + * + * By Russell King, with some suggestions from borris@ant.co.uk + * + * Changelog: + * 1.04 RMK 29/02/1996 Won't pass packets that are from our ethernet + * address up to the higher levels - they're + * silently ignored. I/F can now be put into + * multicast mode. Receiver routine optimised. + * 1.05 RMK 30/02/1996 Now claims interrupt at open when part of + * the kernel rather than when a module. + * 1.06 RMK 02/03/1996 Various code cleanups + * 1.07 RMK 13/10/1996 Optimised interrupt routine and transmit + * routines. + * 1.08 RMK 14/10/1996 Fixed problem with too many packets, + * prevented the kernel message about dropped + * packets appearing too many times a second. + * Now does not disable all IRQs, only the IRQ + * used by this card. + * 1.09 RMK 10/11/1996 Only enables TX irq when buffer space is low, + * but we still service the TX queue if we get a + * RX interrupt. + * 1.10 RMK 15/07/1997 Fixed autoprobing of NQ8004. + * 1.11 RMK 16/11/1997 Fixed autoprobing of NQ8005A. + * 1.12 RMK 31/12/1997 Removed reference to dev_tint for Linux 2.1. + * RMK 27/06/1998 Changed asm/delay.h to linux/delay.h. + * 1.13 RMK 29/06/1998 Fixed problem with transmission of packets. + * Chip seems to have a bug in, whereby if the + * packet starts two bytes from the end of the + * buffer, it corrupts the receiver chain, and + * never updates the transmit status correctly. + * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. + * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy + * hardware. + * 1.16 RMK 10/02/2000 Updated for 2.3.43 + * 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static char version[] __initdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; + +#include "ether3.h" + +static unsigned int net_debug = NET_DEBUG; + +static void ether3_setmulticastlist(struct net_device *dev); +static int ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt); +static void ether3_tx(struct net_device *dev, struct dev_priv *priv); +static int ether3_open (struct net_device *dev); +static int ether3_sendpacket (struct sk_buff *skb, struct net_device *dev); +static irqreturn_t ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs); +static int ether3_close (struct net_device *dev); +static struct net_device_stats *ether3_getstats (struct net_device *dev); +static void ether3_setmulticastlist (struct net_device *dev); +static void ether3_timeout(struct net_device *dev); + +#define BUS_16 2 +#define BUS_8 1 +#define BUS_UNKNOWN 0 + +/* --------------------------------------------------------------------------- */ + +typedef enum { + buffer_write, + buffer_read +} buffer_rw_t; + +/* + * ether3 read/write. Slow things down a bit... + * The SEEQ8005 doesn't like us writing to its registers + * too quickly. + */ +static inline void ether3_outb(int v, const int r) +{ + outb(v, r); + udelay(1); +} + +static inline void ether3_outw(int v, const int r) +{ + outw(v, r); + udelay(1); +} +#define ether3_inb(r) ({ unsigned int __v = inb((r)); udelay(1); __v; }) +#define ether3_inw(r) ({ unsigned int __v = inw((r)); udelay(1); __v; }) + +static int +ether3_setbuffer(struct net_device *dev, buffer_rw_t read, int start) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int timeout = 1000; + + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); + ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); + + while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) { + if (!timeout--) { + printk("%s: setbuffer broken\n", dev->name); + priv->broken = 1; + return 1; + } + udelay(1); + } + + if (read == buffer_read) { + ether3_outw(start, REG_DMAADDR); + ether3_outw(priv->regs.command | CMD_FIFOREAD, REG_COMMAND); + } else { + ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); + ether3_outw(start, REG_DMAADDR); + } + return 0; +} + +/* + * write data to the buffer memory + */ +#define ether3_writebuffer(dev,data,length) \ + outsw(REG_BUFWIN, (data), (length) >> 1) + +#define ether3_writeword(dev,data) \ + outw((data), REG_BUFWIN) + +#define ether3_writelong(dev,data) { \ + unsigned long reg_bufwin = REG_BUFWIN; \ + outw((data), reg_bufwin); \ + outw((data) >> 16, reg_bufwin); \ +} + +/* + * read data from the buffer memory + */ +#define ether3_readbuffer(dev,data,length) \ + insw(REG_BUFWIN, (data), (length) >> 1) + +#define ether3_readword(dev) \ + inw(REG_BUFWIN) + +#define ether3_readlong(dev) \ + inw(REG_BUFWIN) | (inw(REG_BUFWIN) << 16) + +/* + * Switch LED off... + */ +static void +ether3_ledoff(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct dev_priv *priv = (struct dev_priv *)dev->priv; + ether3_outw(priv->regs.config2 |= CFG2_CTRLO, REG_CONFIG2); +} + +/* + * switch LED on... + */ +static inline void +ether3_ledon(struct net_device *dev, struct dev_priv *priv) +{ + del_timer(&priv->timer); + priv->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */ + priv->timer.data = (unsigned long)dev; + priv->timer.function = ether3_ledoff; + add_timer(&priv->timer); + if (priv->regs.config2 & CFG2_CTRLO) + ether3_outw(priv->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2); +} + +/* + * Read the ethernet address string from the on board rom. + * This is an ascii string!!! + */ +static int __init +ether3_addr(char *addr, struct expansion_card *ec) +{ + struct in_chunk_dir cd; + char *s; + + if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { + int i; + for (i = 0; i<6; i++) { + addr[i] = simple_strtoul(s + 1, &s, 0x10); + if (*s != (i==5?')' : ':' )) + break; + } + if (i == 6) + return 0; + } + /* I wonder if we should even let the user continue in this case + * - no, it would be better to disable the device + */ + printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n"); + return -ENODEV; +} + +/* --------------------------------------------------------------------------- */ + +static int __init +ether3_ramtest(struct net_device *dev, unsigned char byte) +{ + unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL); + int i,ret = 0; + int max_errors = 4; + int bad = -1; + + if (!buffer) + return 1; + + memset(buffer, byte, RX_END); + ether3_setbuffer(dev, buffer_write, 0); + ether3_writebuffer(dev, buffer, TX_END); + ether3_setbuffer(dev, buffer_write, RX_START); + ether3_writebuffer(dev, buffer + RX_START, RX_LEN); + memset(buffer, byte ^ 0xff, RX_END); + ether3_setbuffer(dev, buffer_read, 0); + ether3_readbuffer(dev, buffer, TX_END); + ether3_setbuffer(dev, buffer_read, RX_START); + ether3_readbuffer(dev, buffer + RX_START, RX_LEN); + + for (i = 0; i < RX_END; i++) { + if (buffer[i] != byte) { + if (max_errors > 0 && bad != buffer[i]) { + printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X", + dev->name, buffer[i], byte, i); + ret = 2; + max_errors--; + bad = i; + } + } else { + if (bad != -1) { + if (bad != i - 1) + printk(" - 0x%04X\n", i - 1); + printk("\n"); + bad = -1; + } + } + } + if (bad != -1) + printk(" - 0xffff\n"); + kfree(buffer); + + return ret; +} + +/* ------------------------------------------------------------------------------- */ + +static int __init +ether3_init_2(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int i; + + priv->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8; + priv->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC; + priv->regs.command = 0; + + /* + * Set up our hardware address + */ + ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); + for (i = 0; i < 6; i++) + ether3_outb(dev->dev_addr[i], REG_BUFWIN); + + if (dev->flags & IFF_PROMISC) + priv->regs.config1 |= CFG1_RECVPROMISC; + else if (dev->flags & IFF_MULTICAST) + priv->regs.config1 |= CFG1_RECVSPECBRMULTI; + else + priv->regs.config1 |= CFG1_RECVSPECBROAD; + + /* + * There is a problem with the NQ8005 in that it occasionally loses the + * last two bytes. To get round this problem, we receive the CRC as + * well. That way, if we do lose the last two, then it doesn't matter. + */ + ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); + ether3_outw((TX_END>>8) - 1, REG_BUFWIN); + ether3_outw(priv->rx_head, REG_RECVPTR); + ether3_outw(0, REG_TRANSMITPTR); + ether3_outw(priv->rx_head >> 8, REG_RECVEND); + ether3_outw(priv->regs.config2, REG_CONFIG2); + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); + ether3_outw(priv->regs.command, REG_COMMAND); + + i = ether3_ramtest(dev, 0x5A); + if(i) + return i; + i = ether3_ramtest(dev, 0x1E); + if(i) + return i; + + ether3_setbuffer(dev, buffer_write, 0); + ether3_writelong(dev, 0); + return 0; +} + +static void +ether3_init_for_open(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int i; + + memset(&priv->stats, 0, sizeof(struct net_device_stats)); + + /* Reset the chip */ + ether3_outw(CFG2_RESET, REG_CONFIG2); + udelay(4); + + priv->regs.command = 0; + ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); + while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); + + ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); + for (i = 0; i < 6; i++) + ether3_outb(dev->dev_addr[i], REG_BUFWIN); + + priv->tx_head = 0; + priv->tx_tail = 0; + priv->regs.config2 |= CFG2_CTRLO; + priv->rx_head = RX_START; + + ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); + ether3_outw((TX_END>>8) - 1, REG_BUFWIN); + ether3_outw(priv->rx_head, REG_RECVPTR); + ether3_outw(priv->rx_head >> 8, REG_RECVEND); + ether3_outw(0, REG_TRANSMITPTR); + ether3_outw(priv->regs.config2, REG_CONFIG2); + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); + + ether3_setbuffer(dev, buffer_write, 0); + ether3_writelong(dev, 0); + + priv->regs.command = CMD_ENINTRX | CMD_ENINTTX; + ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); +} + +static inline int +ether3_probe_bus_8(struct net_device *dev, int val) +{ + int write_low, write_high, read_low, read_high; + + write_low = val & 255; + write_high = val >> 8; + + printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low); + + ether3_outb(write_low, REG_RECVPTR); + ether3_outb(write_high, REG_RECVPTR + 1); + + read_low = ether3_inb(REG_RECVPTR); + read_high = ether3_inb(REG_RECVPTR + 1); + + printk(", read8 [%02X:%02X]\n", read_high, read_low); + + return read_low == write_low && read_high == write_high; +} + +static inline int +ether3_probe_bus_16(struct net_device *dev, int val) +{ + int read_val; + + ether3_outw(val, REG_RECVPTR); + read_val = ether3_inw(REG_RECVPTR); + + printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val); + + return read_val == val; +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int +ether3_open(struct net_device *dev) +{ + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_WARNING "%s: invalid ethernet MAC address\n", + dev->name); + return -EINVAL; + } + + if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) + return -EAGAIN; + + ether3_init_for_open(dev); + + netif_start_queue(dev); + + return 0; +} + +/* + * The inverse routine to ether3_open(). + */ +static int +ether3_close(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + + netif_stop_queue(dev); + + disable_irq(dev->irq); + + ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); + priv->regs.command = 0; + while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); + ether3_outb(0x80, REG_CONFIG2 + 1); + ether3_outw(0, REG_COMMAND); + + free_irq(dev->irq, dev); + + return 0; +} + +/* + * Get the current statistics. This may be called with the card open or + * closed. + */ +static struct net_device_stats *ether3_getstats(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + return &priv->stats; +} + +/* + * Set or clear promiscuous/multicast mode filter for this adaptor. + * + * We don't attempt any packet filtering. The card may have a SEEQ 8004 + * in which does not have the other ethernet address registers present... + */ +static void ether3_setmulticastlist(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + + priv->regs.config1 &= ~CFG1_RECVPROMISC; + + if (dev->flags & IFF_PROMISC) { + /* promiscuous mode */ + priv->regs.config1 |= CFG1_RECVPROMISC; + } else if (dev->flags & IFF_ALLMULTI) { + priv->regs.config1 |= CFG1_RECVSPECBRMULTI; + } else + priv->regs.config1 |= CFG1_RECVSPECBROAD; + + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); +} + +static void +ether3_timeout(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + + del_timer(&priv->timer); + + local_irq_save(flags); + printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name); + printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name, + ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); + printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name, + ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR)); + printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name, + priv->tx_head, priv->tx_tail); + ether3_setbuffer(dev, buffer_read, priv->tx_tail); + printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); + local_irq_restore(flags); + + priv->regs.config2 |= CFG2_CTRLO; + priv->stats.tx_errors += 1; + ether3_outw(priv->regs.config2, REG_CONFIG2); + priv->tx_head = priv->tx_tail = 0; + + netif_wake_queue(dev); +} + +/* + * Transmit a packet + */ +static int +ether3_sendpacket(struct sk_buff *skb, struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned int ptr, next_ptr; + + length = (length + 1) & ~1; + + if (priv->broken) { + dev_kfree_skb(skb); + priv->stats.tx_dropped ++; + netif_start_queue(dev); + return 0; + } + + next_ptr = (priv->tx_head + 1) & 15; + + local_irq_save(flags); + + if (priv->tx_tail == next_ptr) { + local_irq_restore(flags); + return 1; /* unable to queue */ + } + + dev->trans_start = jiffies; + ptr = 0x600 * priv->tx_head; + priv->tx_head = next_ptr; + next_ptr *= 0x600; + +#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS) + + ether3_setbuffer(dev, buffer_write, next_ptr); + ether3_writelong(dev, 0); + ether3_setbuffer(dev, buffer_write, ptr); + ether3_writelong(dev, 0); + ether3_writebuffer(dev, skb->data, length); + ether3_writeword(dev, htons(next_ptr)); + ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16); + ether3_setbuffer(dev, buffer_write, ptr); + ether3_writeword(dev, htons((ptr + length + 4))); + ether3_writeword(dev, TXHDR_FLAGS >> 16); + ether3_ledon(dev, priv); + + if (!(ether3_inw(REG_STATUS) & STAT_TXON)) { + ether3_outw(ptr, REG_TRANSMITPTR); + ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND); + } + + next_ptr = (priv->tx_head + 1) & 15; + local_irq_restore(flags); + + dev_kfree_skb(skb); + + if (priv->tx_tail == next_ptr) + netif_stop_queue(dev); + + return 0; +} + +static irqreturn_t +ether3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct dev_priv *priv; + unsigned int status, handled = IRQ_NONE; + +#if NET_DEBUG > 1 + if(net_debug & DEBUG_INT) + printk("eth3irq: %d ", irq); +#endif + + priv = (struct dev_priv *)dev->priv; + + status = ether3_inw(REG_STATUS); + + if (status & STAT_INTRX) { + ether3_outw(CMD_ACKINTRX | priv->regs.command, REG_COMMAND); + ether3_rx(dev, priv, 12); + handled = IRQ_HANDLED; + } + + if (status & STAT_INTTX) { + ether3_outw(CMD_ACKINTTX | priv->regs.command, REG_COMMAND); + ether3_tx(dev, priv); + handled = IRQ_HANDLED; + } + +#if NET_DEBUG > 1 + if(net_debug & DEBUG_INT) + printk("done\n"); +#endif + return handled; +} + +/* + * If we have a good packet(s), get it/them out of the buffers. + */ +static int +ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt) +{ + unsigned int next_ptr = priv->rx_head, received = 0; + ether3_ledon(dev, priv); + + do { + unsigned int this_ptr, status; + unsigned char addrs[16]; + + /* + * read the first 16 bytes from the buffer. + * This contains the status bytes etc and ethernet addresses, + * and we also check the source ethernet address to see if + * it originated from us. + */ + { + unsigned int temp_ptr; + ether3_setbuffer(dev, buffer_read, next_ptr); + temp_ptr = ether3_readword(dev); + status = ether3_readword(dev); + if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) != + (RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr) + break; + + this_ptr = next_ptr + 4; + next_ptr = ntohs(temp_ptr); + } + ether3_setbuffer(dev, buffer_read, this_ptr); + ether3_readbuffer(dev, addrs+2, 12); + +if (next_ptr < RX_START || next_ptr >= RX_END) { + int i; + printk("%s: bad next pointer @%04X: ", dev->name, priv->rx_head); + printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8); + for (i = 2; i < 14; i++) + printk("%02X ", addrs[i]); + printk("\n"); + next_ptr = priv->rx_head; + break; +} + /* + * ignore our own packets... + */ + if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) && + !(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) { + maxcnt ++; /* compensate for loopedback packet */ + ether3_outw(next_ptr >> 8, REG_RECVEND); + } else + if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) { + unsigned int length = next_ptr - this_ptr; + struct sk_buff *skb; + + if (next_ptr <= this_ptr) + length += RX_END - RX_START; + + skb = dev_alloc_skb(length + 2); + if (skb) { + unsigned char *buf; + + skb->dev = dev; + skb_reserve(skb, 2); + buf = skb_put(skb, length); + ether3_readbuffer(dev, buf + 12, length - 12); + ether3_outw(next_ptr >> 8, REG_RECVEND); + *(unsigned short *)(buf + 0) = *(unsigned short *)(addrs + 2); + *(unsigned long *)(buf + 2) = *(unsigned long *)(addrs + 4); + *(unsigned long *)(buf + 6) = *(unsigned long *)(addrs + 8); + *(unsigned short *)(buf + 10) = *(unsigned short *)(addrs + 12); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + received ++; + } else + goto dropping; + } else { + struct net_device_stats *stats = &priv->stats; + ether3_outw(next_ptr >> 8, REG_RECVEND); + if (status & RXSTAT_OVERSIZE) stats->rx_over_errors ++; + if (status & RXSTAT_CRCERROR) stats->rx_crc_errors ++; + if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++; + if (status & RXSTAT_SHORTPACKET) stats->rx_length_errors ++; + stats->rx_errors++; + } + } + while (-- maxcnt); + +done: + priv->stats.rx_packets += received; + priv->rx_head = next_ptr; + /* + * If rx went off line, then that means that the buffer may be full. We + * have dropped at least one packet. + */ + if (!(ether3_inw(REG_STATUS) & STAT_RXON)) { + priv->stats.rx_dropped ++; + ether3_outw(next_ptr, REG_RECVPTR); + ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); + } + + return maxcnt; + +dropping:{ + static unsigned long last_warned; + + ether3_outw(next_ptr >> 8, REG_RECVEND); + /* + * Don't print this message too many times... + */ + if (time_after(jiffies, last_warned + 10 * HZ)) { + last_warned = jiffies; + printk("%s: memory squeeze, dropping packet.\n", dev->name); + } + priv->stats.rx_dropped ++; + goto done; + } +} + +/* + * Update stats for the transmitted packet(s) + */ +static void +ether3_tx(struct net_device *dev, struct dev_priv *priv) +{ + unsigned int tx_tail = priv->tx_tail; + int max_work = 14; + + do { + unsigned long status; + + /* + * Read the packet header + */ + ether3_setbuffer(dev, buffer_read, tx_tail * 0x600); + status = ether3_readlong(dev); + + /* + * Check to see if this packet has been transmitted + */ + if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) != + (TXSTAT_DONE | TXHDR_TRANSMIT)) + break; + + /* + * Update errors + */ + if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS))) + priv->stats.tx_packets++; + else { + priv->stats.tx_errors ++; + if (status & TXSTAT_16COLLISIONS) priv->stats.collisions += 16; + if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++; + } + + tx_tail = (tx_tail + 1) & 15; + } while (--max_work); + + if (priv->tx_tail != tx_tail) { + priv->tx_tail = tx_tail; + netif_wake_queue(dev); + } +} + +static void __init ether3_banner(void) +{ + static unsigned version_printed = 0; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static const char * __init +ether3_get_dev(struct net_device *dev, struct expansion_card *ec) +{ + const char *name = "ether3"; + + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + dev->irq = ec->irq; + + if (ec->cid.manufacturer == MANU_ANT && + ec->cid.product == PROD_ANT_ETHERB) { + dev->base_addr += 0x200; + name = "etherb"; + } + + ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); + ec->irqmask = 0xf0; + + ether3_addr(dev->dev_addr, ec); + + return name; +} + +static int __devinit +ether3_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct net_device *dev; + struct dev_priv *priv; + const char *name; + int i, bus_type, ret; + + ether3_banner(); + + dev = init_etherdev(NULL, sizeof(struct dev_priv)); + if (!dev) { + ret = -ENOMEM; + goto out; + } + + SET_MODULE_OWNER(dev); + + name = ether3_get_dev(dev, ec); + if (!name) { + ret = -ENODEV; + goto free; + } + + /* + * this will not fail - the nature of the bus ensures this + */ + if (!request_region(dev->base_addr, 128, dev->name)) { + ret = -EBUSY; + goto free; + } + + priv = (struct dev_priv *) dev->priv; + init_timer(&priv->timer); + + /* Reset card... + */ + ether3_outb(0x80, REG_CONFIG2 + 1); + bus_type = BUS_UNKNOWN; + udelay(4); + + /* Test using Receive Pointer (16-bit register) to find out + * how the ether3 is connected to the bus... + */ + if (ether3_probe_bus_8(dev, 0x100) && + ether3_probe_bus_8(dev, 0x201)) + bus_type = BUS_8; + + if (bus_type == BUS_UNKNOWN && + ether3_probe_bus_16(dev, 0x101) && + ether3_probe_bus_16(dev, 0x201)) + bus_type = BUS_16; + + switch (bus_type) { + case BUS_UNKNOWN: + printk(KERN_ERR "%s: unable to identify bus width\n", dev->name); + ret = -ENODEV; + goto failed; + + case BUS_8: + printk(KERN_ERR "%s: %s found, but is an unsupported " + "8-bit card\n", dev->name, name); + ret = -ENODEV; + goto failed; + + default: + break; + } + + printk("%s: %s in slot %d, ", dev->name, name, ec->slot_no); + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + + if (ether3_init_2(dev)) { + ret = -ENODEV; + goto failed; + } + + dev->open = ether3_open; + dev->stop = ether3_close; + dev->hard_start_xmit = ether3_sendpacket; + dev->get_stats = ether3_getstats; + dev->set_multicast_list = ether3_setmulticastlist; + dev->tx_timeout = ether3_timeout; + dev->watchdog_timeo = 5 * HZ / 100; + + ecard_set_drvdata(ec, dev); + return 0; + +failed: + release_region(dev->base_addr, 128); +free: + unregister_netdev(dev); + kfree(dev); +out: + return ret; +} + +static void __devexit ether3_remove(struct expansion_card *ec) +{ + struct net_device *dev = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + unregister_netdev(dev); + release_region(dev->base_addr, 128); + kfree(dev); +} + +static const struct ecard_id ether3_ids[] = { + { MANU_ANT2, PROD_ANT_ETHER3 }, + { MANU_ANT, PROD_ANT_ETHER3 }, + { MANU_ANT, PROD_ANT_ETHERB }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver ether3_driver = { + .probe = ether3_probe, + .remove = __devexit_p(ether3_remove), + .id_table = ether3_ids, + .drv = { + .name = "ether3", + }, +}; + +static int __init ether3_init(void) +{ + return ecard_register_driver(ðer3_driver); +} + +static void __exit ether3_exit(void) +{ + ecard_remove_driver(ðer3_driver); +} + +module_init(ether3_init); +module_exit(ether3_exit); + +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/net/arm/ether3.h linux-2.5.69-bk14/drivers/net/arm/ether3.h --- linux-2.5.69-bk13/drivers/net/arm/ether3.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/ether3.h 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,168 @@ +/* + * linux/drivers/acorn/net/ether3.h + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * network driver for Acorn/ANT Ether3 cards + */ + +#ifndef _LINUX_ether3_H +#define _LINUX_ether3_H + +/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ +#define DEBUG_TX 2 +#define DEBUG_RX 4 +#define DEBUG_INT 8 +#define DEBUG_IC 16 +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* Command register definitions & bits */ +#define REG_COMMAND (dev->base_addr + 0x00) +#define CMD_ENINTDMA 0x0001 +#define CMD_ENINTRX 0x0002 +#define CMD_ENINTTX 0x0004 +#define CMD_ENINTBUFWIN 0x0008 +#define CMD_ACKINTDMA 0x0010 +#define CMD_ACKINTRX 0x0020 +#define CMD_ACKINTTX 0x0040 +#define CMD_ACKINTBUFWIN 0x0080 +#define CMD_DMAON 0x0100 +#define CMD_RXON 0x0200 +#define CMD_TXON 0x0400 +#define CMD_DMAOFF 0x0800 +#define CMD_RXOFF 0x1000 +#define CMD_TXOFF 0x2000 +#define CMD_FIFOREAD 0x4000 +#define CMD_FIFOWRITE 0x8000 + +/* status register */ +#define REG_STATUS (dev->base_addr + 0x00) +#define STAT_ENINTSTAT 0x0001 +#define STAT_ENINTRX 0x0002 +#define STAT_ENINTTX 0x0004 +#define STAT_ENINTBUFWIN 0x0008 +#define STAT_INTDMA 0x0010 +#define STAT_INTRX 0x0020 +#define STAT_INTTX 0x0040 +#define STAT_INTBUFWIN 0x0080 +#define STAT_DMAON 0x0100 +#define STAT_RXON 0x0200 +#define STAT_TXON 0x0400 +#define STAT_FIFOFULL 0x2000 +#define STAT_FIFOEMPTY 0x4000 +#define STAT_FIFODIR 0x8000 + +/* configuration register 1 */ +#define REG_CONFIG1 (dev->base_addr + 0x10) +#define CFG1_BUFSELSTAT0 0x0000 +#define CFG1_BUFSELSTAT1 0x0001 +#define CFG1_BUFSELSTAT2 0x0002 +#define CFG1_BUFSELSTAT3 0x0003 +#define CFG1_BUFSELSTAT4 0x0004 +#define CFG1_BUFSELSTAT5 0x0005 +#define CFG1_ADDRPROM 0x0006 +#define CFG1_TRANSEND 0x0007 +#define CFG1_LOCBUFMEM 0x0008 +#define CFG1_INTVECTOR 0x0009 +#define CFG1_RECVSPECONLY 0x0000 +#define CFG1_RECVSPECBROAD 0x4000 +#define CFG1_RECVSPECBRMULTI 0x8000 +#define CFG1_RECVPROMISC 0xC000 + +/* The following aren't in 8004 */ +#define CFG1_DMABURSTCONT 0x0000 +#define CFG1_DMABURST800NS 0x0010 +#define CFG1_DMABURST1600NS 0x0020 +#define CFG1_DMABURST3200NS 0x0030 +#define CFG1_DMABURST1 0x0000 +#define CFG1_DMABURST4 0x0040 +#define CFG1_DMABURST8 0x0080 +#define CFG1_DMABURST16 0x00C0 +#define CFG1_RECVCOMPSTAT0 0x0100 +#define CFG1_RECVCOMPSTAT1 0x0200 +#define CFG1_RECVCOMPSTAT2 0x0400 +#define CFG1_RECVCOMPSTAT3 0x0800 +#define CFG1_RECVCOMPSTAT4 0x1000 +#define CFG1_RECVCOMPSTAT5 0x2000 + +/* configuration register 2 */ +#define REG_CONFIG2 (dev->base_addr + 0x20) +#define CFG2_BYTESWAP 0x0001 +#define CFG2_ERRENCRC 0x0008 +#define CFG2_ERRENDRIBBLE 0x0010 +#define CFG2_ERRSHORTFRAME 0x0020 +#define CFG2_SLOTSELECT 0x0040 +#define CFG2_PREAMSELECT 0x0080 +#define CFG2_ADDRLENGTH 0x0100 +#define CFG2_RECVCRC 0x0200 +#define CFG2_XMITNOCRC 0x0400 +#define CFG2_LOOPBACK 0x0800 +#define CFG2_CTRLO 0x1000 +#define CFG2_RESET 0x8000 + +#define REG_RECVEND (dev->base_addr + 0x30) + +#define REG_BUFWIN (dev->base_addr + 0x40) + +#define REG_RECVPTR (dev->base_addr + 0x50) + +#define REG_TRANSMITPTR (dev->base_addr + 0x60) + +#define REG_DMAADDR (dev->base_addr + 0x70) + +/* + * Cards transmit/receive headers + */ +#define TX_NEXT (0xffff) +#define TXHDR_ENBABBLEINT (1 << 16) +#define TXHDR_ENCOLLISIONINT (1 << 17) +#define TXHDR_EN16COLLISION (1 << 18) +#define TXHDR_ENSUCCESS (1 << 19) +#define TXHDR_DATAFOLLOWS (1 << 21) +#define TXHDR_CHAINCONTINUE (1 << 22) +#define TXHDR_TRANSMIT (1 << 23) +#define TXSTAT_BABBLED (1 << 24) +#define TXSTAT_COLLISION (1 << 25) +#define TXSTAT_16COLLISIONS (1 << 26) +#define TXSTAT_DONE (1 << 31) + +#define RX_NEXT (0xffff) +#define RXHDR_CHAINCONTINUE (1 << 6) +#define RXHDR_RECEIVE (1 << 7) +#define RXSTAT_OVERSIZE (1 << 8) +#define RXSTAT_CRCERROR (1 << 9) +#define RXSTAT_DRIBBLEERROR (1 << 10) +#define RXSTAT_SHORTPACKET (1 << 11) +#define RXSTAT_DONE (1 << 15) + + +#define TX_START 0x0000 +#define TX_END 0x6000 +#define RX_START 0x6000 +#define RX_LEN 0xA000 +#define RX_END 0x10000 +/* must be a power of 2 and greater than MAX_TX_BUFFERED */ +#define MAX_TXED 16 +#define MAX_TX_BUFFERED 10 + +struct dev_priv { + struct { + unsigned int command; + unsigned int config1; + unsigned int config2; + } regs; + unsigned char tx_head; /* buffer nr to insert next packet */ + unsigned char tx_tail; /* buffer nr of transmitting packet */ + unsigned int rx_head; /* address to fetch next packet from */ + struct net_device_stats stats; + struct timer_list timer; + int broken; /* 0 = ok, 1 = something went wrong */ +}; + +#endif diff -urN linux-2.5.69-bk13/drivers/net/arm/etherh.c linux-2.5.69-bk14/drivers/net/arm/etherh.c --- linux-2.5.69-bk13/drivers/net/arm/etherh.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/net/arm/etherh.c 2003-05-20 04:46:44.000000000 -0700 @@ -0,0 +1,763 @@ +/* + * linux/drivers/acorn/net/etherh.c + * + * Copyright (C) 2000-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * NS8390 I-cubed EtherH and ANT EtherM specific driver + * Thanks to I-Cubed for information on their cards. + * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton + * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan) + * EtherM integration re-engineered by Russell King. + * + * Changelog: + * 08-12-1996 RMK 1.00 Created + * RMK 1.03 Added support for EtherLan500 cards + * 23-11-1997 RMK 1.04 Added media autodetection + * 16-04-1998 RMK 1.05 Improved media autodetection + * 10-02-2000 RMK 1.06 Updated for 2.3.43 + * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8 + * 12-10-1999 CK/TEW EtherM driver first release + * 21-12-2000 TTC EtherH/EtherM integration + * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver. + * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../8390.h" + +#define NET_DEBUG 0 +#define DEBUG_INIT 2 + +static unsigned int net_debug = NET_DEBUG; + +struct etherh_priv { + struct ei_device eidev; + unsigned int id; + unsigned int ctrl_port; + unsigned int ctrl; +}; + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("EtherH/EtherM driver"); +MODULE_LICENSE("GPL"); + +static char version[] __initdata = + "EtherH/EtherM Driver (c) 2002 Russell King v1.09\n"; + +#define ETHERH500_DATAPORT 0x200 /* MEMC */ +#define ETHERH500_NS8390 0x000 /* MEMC */ +#define ETHERH500_CTRLPORT 0x200 /* IOC */ + +#define ETHERH600_DATAPORT 16 /* MEMC */ +#define ETHERH600_NS8390 0x200 /* MEMC */ +#define ETHERH600_CTRLPORT 0x080 /* MEMC */ + +#define ETHERH_CP_IE 1 +#define ETHERH_CP_IF 2 +#define ETHERH_CP_HEARTBEAT 2 + +#define ETHERH_TX_START_PAGE 1 +#define ETHERH_STOP_PAGE 127 + +/* + * These came from CK/TEW + */ +#define ETHERM_DATAPORT 0x080 /* MEMC */ +#define ETHERM_NS8390 0x200 /* MEMC */ +#define ETHERM_CTRLPORT 0x08f /* MEMC */ + +#define ETHERM_TX_START_PAGE 64 +#define ETHERM_STOP_PAGE 127 + +/* ------------------------------------------------------------------------ */ + +static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned int mask) +{ + eh->ctrl |= mask; + outb(eh->ctrl, eh->ctrl_port); +} + +static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned int mask) +{ + eh->ctrl &= ~mask; + outb(eh->ctrl, eh->ctrl_port); +} + +static inline unsigned int etherh_get_stat(struct etherh_priv *eh) +{ + return inb(eh->ctrl_port); +} + + + + +static void etherh_irq_enable(ecard_t *ec, int irqnr) +{ + struct etherh_priv *eh = ec->irq_data; + + etherh_set_ctrl(eh, ETHERH_CP_IE); +} + +static void etherh_irq_disable(ecard_t *ec, int irqnr) +{ + struct etherh_priv *eh = ec->irq_data; + + etherh_clr_ctrl(eh, ETHERH_CP_IE); +} + +static expansioncard_ops_t etherh_ops = { + .irqenable = etherh_irq_enable, + .irqdisable = etherh_irq_disable, +}; + + + + +static void +etherh_setif(struct net_device *dev) +{ + struct etherh_priv *eh = (struct etherh_priv *)dev->priv; + struct ei_device *ei_local = &eh->eidev; + unsigned long addr, flags; + + local_irq_save(flags); + + /* set the interface type */ + switch (eh->id) { + case PROD_I3_ETHERLAN600: + case PROD_I3_ETHERLAN600A: + addr = dev->base_addr + EN0_RCNTHI; + + switch (dev->if_port) { + case IF_PORT_10BASE2: + outb((inb(addr) & 0xf8) | 1, addr); + break; + case IF_PORT_10BASET: + outb((inb(addr) & 0xf8), addr); + break; + } + break; + + case PROD_I3_ETHERLAN500: + switch (dev->if_port) { + case IF_PORT_10BASE2: + etherh_clr_ctrl(eh, ETHERH_CP_IF); + break; + + case IF_PORT_10BASET: + etherh_set_ctrl(eh, ETHERH_CP_IF); + break; + } + break; + + default: + break; + } + + local_irq_restore(flags); +} + +static int +etherh_getifstat(struct net_device *dev) +{ + struct etherh_priv *eh = (struct etherh_priv *)dev->priv; + struct ei_device *ei_local = &eh->eidev; + int stat = 0; + + switch (eh->id) { + case PROD_I3_ETHERLAN600: + case PROD_I3_ETHERLAN600A: + switch (dev->if_port) { + case IF_PORT_10BASE2: + stat = 1; + break; + case IF_PORT_10BASET: + stat = inb(dev->base_addr+EN0_RCNTHI) & 4; + break; + } + break; + + case PROD_I3_ETHERLAN500: + switch (dev->if_port) { + case IF_PORT_10BASE2: + stat = 1; + break; + case IF_PORT_10BASET: + stat = etherh_get_stat(eh) & ETHERH_CP_HEARTBEAT; + break; + } + break; + + default: + stat = 0; + break; + } + + return stat != 0; +} + +/* + * Configure the interface. Note that we ignore the other + * parts of ifmap, since its mostly meaningless for this driver. + */ +static int etherh_set_config(struct net_device *dev, struct ifmap *map) +{ + switch (map->port) { + case IF_PORT_10BASE2: + case IF_PORT_10BASET: + /* + * If the user explicitly sets the interface + * media type, turn off automedia detection. + */ + dev->flags &= ~IFF_AUTOMEDIA; + dev->if_port = map->port; + break; + + default: + return -EINVAL; + } + + etherh_setif(dev); + + return 0; +} + +/* + * Reset the 8390 (hard reset). Note that we can't actually do this. + */ +static void +etherh_reset(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + + outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, dev->base_addr); + + /* + * See if we need to change the interface type. + * Note that we use 'interface_num' as a flag + * to indicate that we need to change the media. + */ + if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) { + ei_local->interface_num = 0; + + if (dev->if_port == IF_PORT_10BASET) + dev->if_port = IF_PORT_10BASE2; + else + dev->if_port = IF_PORT_10BASET; + + etherh_setif(dev); + } +} + +/* + * Write a block of data out to the 8390 + */ +static void +etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + unsigned int addr, dma_addr; + unsigned long dma_start; + + if (ei_local->dmaing) { + printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " + " DMAstat %d irqlock %d\n", dev->name, + ei_local->dmaing, ei_local->irqlock); + return; + } + + /* + * Make sure we have a round number of bytes if we're in word mode. + */ + if (count & 1 && ei_local->word16) + count++; + + ei_local->dmaing = 1; + + addr = dev->base_addr; + dma_addr = dev->mem_start; + + count = (count + 1) & ~1; + outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); + + outb (0x42, addr + EN0_RCNTLO); + outb (0x00, addr + EN0_RCNTHI); + outb (0x42, addr + EN0_RSARLO); + outb (0x00, addr + EN0_RSARHI); + outb (E8390_RREAD | E8390_START, addr + E8390_CMD); + + udelay (1); + + outb (ENISR_RDC, addr + EN0_ISR); + outb (count, addr + EN0_RCNTLO); + outb (count >> 8, addr + EN0_RCNTHI); + outb (0, addr + EN0_RSARLO); + outb (start_page, addr + EN0_RSARHI); + outb (E8390_RWRITE | E8390_START, addr + E8390_CMD); + + if (ei_local->word16) + outsw (dma_addr, buf, count >> 1); + else + outsb (dma_addr, buf, count); + + dma_start = jiffies; + + while ((inb (addr + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ + printk(KERN_ERR "%s: timeout waiting for TX RDC\n", + dev->name); + etherh_reset (dev); + NS8390_init (dev, 1); + break; + } + + outb (ENISR_RDC, addr + EN0_ISR); + ei_local->dmaing = 0; +} + +/* + * Read a block of data from the 8390 + */ +static void +etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + unsigned int addr, dma_addr; + unsigned char *buf; + + if (ei_local->dmaing) { + printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " + " DMAstat %d irqlock %d\n", dev->name, + ei_local->dmaing, ei_local->irqlock); + return; + } + + ei_local->dmaing = 1; + + addr = dev->base_addr; + dma_addr = dev->mem_start; + + buf = skb->data; + outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); + outb (count, addr + EN0_RCNTLO); + outb (count >> 8, addr + EN0_RCNTHI); + outb (ring_offset, addr + EN0_RSARLO); + outb (ring_offset >> 8, addr + EN0_RSARHI); + outb (E8390_RREAD | E8390_START, addr + E8390_CMD); + + if (ei_local->word16) { + insw (dma_addr, buf, count >> 1); + if (count & 1) + buf[count - 1] = inb (dma_addr); + } else + insb (dma_addr, buf, count); + + outb (ENISR_RDC, addr + EN0_ISR); + ei_local->dmaing = 0; +} + +/* + * Read a header from the 8390 + */ +static void +etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + unsigned int addr, dma_addr; + + if (ei_local->dmaing) { + printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: " + " DMAstat %d irqlock %d\n", dev->name, + ei_local->dmaing, ei_local->irqlock); + return; + } + + ei_local->dmaing = 1; + + addr = dev->base_addr; + dma_addr = dev->mem_start; + + outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); + outb (sizeof (*hdr), addr + EN0_RCNTLO); + outb (0, addr + EN0_RCNTHI); + outb (0, addr + EN0_RSARLO); + outb (ring_page, addr + EN0_RSARHI); + outb (E8390_RREAD | E8390_START, addr + E8390_CMD); + + if (ei_local->word16) + insw (dma_addr, hdr, sizeof (*hdr) >> 1); + else + insb (dma_addr, hdr, sizeof (*hdr)); + + outb (ENISR_RDC, addr + EN0_ISR); + ei_local->dmaing = 0; +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int +etherh_open(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_WARNING "%s: invalid ethernet MAC address\n", + dev->name); + return -EINVAL; + } + + if (request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)) + return -EAGAIN; + + /* + * Make sure that we aren't going to change the + * media type on the next reset - we are about to + * do automedia manually now. + */ + ei_local->interface_num = 0; + + /* + * If we are doing automedia detection, do it now. + * This is more reliable than the 8390's detection. + */ + if (dev->flags & IFF_AUTOMEDIA) { + dev->if_port = IF_PORT_10BASET; + etherh_setif(dev); + mdelay(1); + if (!etherh_getifstat(dev)) { + dev->if_port = IF_PORT_10BASE2; + etherh_setif(dev); + } + } else + etherh_setif(dev); + + etherh_reset(dev); + ei_open(dev); + + return 0; +} + +/* + * The inverse routine to etherh_open(). + */ +static int +etherh_close(struct net_device *dev) +{ + ei_close (dev); + free_irq (dev->irq, dev); + return 0; +} + +/* + * Initialisation + */ + +static void __init etherh_banner(void) +{ + static int version_printed; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +/* + * Read the ethernet address string from the on board rom. + * This is an ascii string... + */ +static int __init etherh_addr(char *addr, struct expansion_card *ec) +{ + struct in_chunk_dir cd; + char *s; + + if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { + int i; + for (i = 0; i < 6; i++) { + addr[i] = simple_strtoul(s + 1, &s, 0x10); + if (*s != (i == 5? ')' : ':')) + break; + } + if (i == 6) + return 0; + } + return -ENODEV; +} + +/* + * Create an ethernet address from the system serial number. + */ +static int __init etherm_addr(char *addr) +{ + unsigned int serial; + + if (system_serial_low == 0 && system_serial_high == 0) + return -ENODEV; + + serial = system_serial_low | system_serial_high; + + addr[0] = 0; + addr[1] = 0; + addr[2] = 0xa4; + addr[3] = 0x10 + (serial >> 24); + addr[4] = serial >> 16; + addr[5] = serial >> 8; + return 0; +} + +static u32 etherh_regoffsets[16]; +static u32 etherm_regoffsets[16]; + +static int __init +etherh_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct ei_device *ei_local; + struct net_device *dev; + struct etherh_priv *eh; + const char *dev_type; + int i, size, ret; + + etherh_banner(); + + dev = init_etherdev(NULL, sizeof(struct etherh_priv)); + if (!dev) { + ret = -ENOMEM; + goto out; + } + + /* + * init_etherdev allocs and zeros dev->priv + */ + eh = dev->priv; + + spin_lock_init(&eh->eidev.page_lock); + + SET_MODULE_OWNER(dev); + + dev->open = etherh_open; + dev->stop = etherh_close; + dev->set_config = etherh_set_config; + dev->irq = ec->irq; + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + + /* + * IRQ and control port handling + */ + if (ec->irq != 11) { + ec->ops = ðerh_ops; + ec->irq_data = eh; + } + eh->ctrl = 0; + eh->id = ec->cid.product; + + switch (ec->cid.product) { + case PROD_ANT_ETHERM: + etherm_addr(dev->dev_addr); + dev->base_addr += ETHERM_NS8390; + dev->mem_start = dev->base_addr + ETHERM_DATAPORT; + eh->ctrl_port = dev->base_addr + ETHERM_CTRLPORT; + break; + + case PROD_I3_ETHERLAN500: + etherh_addr(dev->dev_addr, ec); + dev->base_addr += ETHERH500_NS8390; + dev->mem_start = dev->base_addr + ETHERH500_DATAPORT; + eh->ctrl_port = ecard_address (ec, ECARD_IOC, ECARD_FAST) + + ETHERH500_CTRLPORT; + break; + + case PROD_I3_ETHERLAN600: + case PROD_I3_ETHERLAN600A: + etherh_addr(dev->dev_addr, ec); + dev->base_addr += ETHERH600_NS8390; + dev->mem_start = dev->base_addr + ETHERH600_DATAPORT; + eh->ctrl_port = dev->base_addr + ETHERH600_CTRLPORT; + break; + + default: + printk(KERN_ERR "%s: unknown card type %x\n", + dev->name, ec->cid.product); + ret = -ENODEV; + goto free; + } + + size = 16; + if (ec->cid.product == PROD_ANT_ETHERM) + size <<= 3; + + if (!request_region(dev->base_addr, size, dev->name)) { + ret = -EBUSY; + goto free; + } + + if (ethdev_init(dev)) { + ret = -ENODEV; + goto release; + } + + /* + * If we're in the NIC slot, make sure the IRQ is enabled + */ + if (dev->irq == 11) + etherh_set_ctrl(eh, ETHERH_CP_IE); + + /* + * Unfortunately, ethdev_init eventually calls + * ether_setup, which re-writes dev->flags. + */ + switch (ec->cid.product) { + case PROD_ANT_ETHERM: + dev_type = "ANT EtherM"; + dev->if_port = IF_PORT_UNKNOWN; + break; + + case PROD_I3_ETHERLAN500: + dev_type = "i3 EtherH 500"; + dev->if_port = IF_PORT_UNKNOWN; + break; + + case PROD_I3_ETHERLAN600: + dev_type = "i3 EtherH 600"; + dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; + dev->if_port = IF_PORT_10BASET; + break; + + case PROD_I3_ETHERLAN600A: + dev_type = "i3 EtherH 600A"; + dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; + dev->if_port = IF_PORT_10BASET; + break; + + default: + dev_type = "unknown"; + break; + } + + printk(KERN_INFO "%s: %s in slot %d, ", + dev->name, dev_type, ec->slot_no); + + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + + ei_local = (struct ei_device *) dev->priv; + if (ec->cid.product == PROD_ANT_ETHERM) { + ei_local->tx_start_page = ETHERM_TX_START_PAGE; + ei_local->stop_page = ETHERM_STOP_PAGE; + ei_local->reg_offset = etherm_regoffsets; + } else { + ei_local->tx_start_page = ETHERH_TX_START_PAGE; + ei_local->stop_page = ETHERH_STOP_PAGE; + ei_local->reg_offset = etherh_regoffsets; + } + + ei_local->name = dev->name; + ei_local->word16 = 1; + ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES; + ei_local->reset_8390 = etherh_reset; + ei_local->block_input = etherh_block_input; + ei_local->block_output = etherh_block_output; + ei_local->get_8390_hdr = etherh_get_header; + ei_local->interface_num = 0; + + etherh_reset(dev); + NS8390_init(dev, 0); + + ecard_set_drvdata(ec, dev); + + return 0; + +release: + release_region(dev->base_addr, 16); +free: + unregister_netdev(dev); + kfree(dev->priv); + kfree(dev); +out: + return ret; +} + +static void __devexit etherh_remove(struct expansion_card *ec) +{ + struct net_device *dev = ecard_get_drvdata(ec); + int size = 16; + + ecard_set_drvdata(ec, NULL); + + unregister_netdev(dev); + if (ec->cid.product == PROD_ANT_ETHERM) + size <<= 3; + release_region(dev->base_addr, size); + kfree(dev); + + ec->ops = NULL; + kfree(ec->irq_data); +} + +static const struct ecard_id etherh_ids[] = { + { MANU_ANT, PROD_ANT_ETHERM }, + { MANU_I3, PROD_I3_ETHERLAN500 }, + { MANU_I3, PROD_I3_ETHERLAN600 }, + { MANU_I3, PROD_I3_ETHERLAN600A }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver etherh_driver = { + .probe = etherh_probe, + .remove = __devexit_p(etherh_remove), + .id_table = etherh_ids, + .drv = { + .name = "etherh", + }, +}; + +static int __init etherh_init(void) +{ + int i; + + for (i = 0; i < 16; i++) { + etherh_regoffsets[i] = i; + etherm_regoffsets[i] = i << 3; + } + + return ecard_register_driver(ðerh_driver); +} + +static void __exit etherh_exit(void) +{ + ecard_remove_driver(ðerh_driver); +} + +module_init(etherh_init); +module_exit(etherh_exit); diff -urN linux-2.5.69-bk13/drivers/net/irda/sa1100_ir.c linux-2.5.69-bk14/drivers/net/irda/sa1100_ir.c --- linux-2.5.69-bk13/drivers/net/irda/sa1100_ir.c 2003-05-04 16:53:12.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/irda/sa1100_ir.c 2003-05-20 04:46:44.000000000 -0700 @@ -636,13 +636,14 @@ sa1100_irda_rx_dma_start(si); } -static void sa1100_irda_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t sa1100_irda_irq(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = dev_id; if (IS_FIR(((struct sa1100_irda *)dev->priv))) sa1100_irda_fir_irq(dev); else sa1100_irda_hpsir_irq(dev); + return IRQ_HANDLED; } /* diff -urN linux-2.5.69-bk13/drivers/net/ppp_generic.c linux-2.5.69-bk14/drivers/net/ppp_generic.c --- linux-2.5.69-bk13/drivers/net/ppp_generic.c 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/ppp_generic.c 2003-05-20 04:46:44.000000000 -0700 @@ -803,7 +803,7 @@ S_IFCHR|S_IRUSR|S_IWUSR, "ppp"); } - if (!err) + if (err) printk(KERN_ERR "failed to register PPP device (%d)\n", err); return err; } diff -urN linux-2.5.69-bk13/drivers/net/wan/sdla_chdlc.c linux-2.5.69-bk14/drivers/net/wan/sdla_chdlc.c --- linux-2.5.69-bk13/drivers/net/wan/sdla_chdlc.c 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/wan/sdla_chdlc.c 2003-05-20 04:46:44.000000000 -0700 @@ -1071,7 +1071,7 @@ card->tty=NULL; tty_card_map[card->tty_minor]=NULL; state = &rs_table[card->tty_minor]; - memset(state,0,sizeof(state)); + memset(state, 0, sizeof(*state)); } return; } diff -urN linux-2.5.69-bk13/drivers/net/wireless/airo.c linux-2.5.69-bk14/drivers/net/wireless/airo.c --- linux-2.5.69-bk13/drivers/net/wireless/airo.c 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/drivers/net/wireless/airo.c 2003-05-20 04:46:44.000000000 -0700 @@ -4838,7 +4838,7 @@ readCapabilityRid(local, &cap_rid); dwrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(range)); + memset(range, 0, sizeof(*range)); range->min_nwid = 0x0000; range->max_nwid = 0x0000; range->num_channels = 14; diff -urN linux-2.5.69-bk13/drivers/pci/pci.ids linux-2.5.69-bk14/drivers/pci/pci.ids --- linux-2.5.69-bk13/drivers/pci/pci.ids 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/drivers/pci/pci.ids 2003-05-20 04:46:44.000000000 -0700 @@ -2264,6 +2264,7 @@ 1028 0095 Integrated 3C905C-TX Fast Etherlink for PC Management NIC 10b7 1000 3C905C-TX Fast Etherlink for PC Management NIC 10b7 7000 10/100 Mini PCI Ethernet Adapter + 9210 3C920B-EMB-WNM Integrated Fast Ethernet Controller 9300 3CSOHO100B-TX [910-A01] 9800 3c980-TX [Fast Etherlink XL Server Adapter] 10b7 9800 3c980-TX Fast Etherlink XL Server Adapter diff -urN linux-2.5.69-bk13/drivers/pci/quirks.c linux-2.5.69-bk14/drivers/pci/quirks.c --- linux-2.5.69-bk13/drivers/pci/quirks.c 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/pci/quirks.c 2003-05-20 04:46:44.000000000 -0700 @@ -697,6 +697,37 @@ } /* + * SiS 96x south bridge: BIOS typically hides SMBus device... + */ +static void __init quirk_sis_96x_smbus(struct pci_dev *dev) +{ + u8 val = 0; + printk(KERN_INFO "Enabling SiS 96x SMBus.\n"); + pci_read_config_byte(dev, 0x77, &val); + pci_write_config_byte(dev, 0x77, val & ~0x10); + pci_read_config_byte(dev, 0x77, &val); +} + +/* + * ... This is further complicated by the fact that some SiS96x south + * bridges pretend to be 85C503/5513 instead. In that case see if we + * spotted a compatible north bridge to make sure. + * (pci_find_device doesn't work yet) + */ +static int __devinitdata sis_96x_compatible = 0; + +static void __init quirk_sis_503_smbus(struct pci_dev *dev) +{ + if (sis_96x_compatible) + quirk_sis_96x_smbus(dev); +} + +static void __init quirk_sis_96x_compatible(struct pci_dev *dev) +{ + sis_96x_compatible = 1; +} + +/* * The main table of quirks. */ @@ -729,6 +760,15 @@ { PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, quirk_natoma }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503_smbus }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_645, quirk_sis_96x_compatible }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_646, quirk_sis_96x_compatible }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_648, quirk_sis_96x_compatible }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_650, quirk_sis_96x_compatible }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_651, quirk_sis_96x_compatible }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1647, quirk_alimagik }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1651, quirk_alimagik }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, quirk_vialatency }, diff -urN linux-2.5.69-bk13/drivers/scsi/Kconfig linux-2.5.69-bk14/drivers/scsi/Kconfig --- linux-2.5.69-bk13/drivers/scsi/Kconfig 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/drivers/scsi/Kconfig 2003-05-20 04:46:44.000000000 -0700 @@ -1602,7 +1602,7 @@ whenever you want). If you want to compile it as a module, say M here and read . -source "drivers/acorn/scsi/Kconfig" +source "drivers/scsi/arm/Kconfig" config JAZZ_ESP bool "MIPS JAZZ FAS216 SCSI support" diff -urN linux-2.5.69-bk13/drivers/scsi/Makefile linux-2.5.69-bk14/drivers/scsi/Makefile --- linux-2.5.69-bk13/drivers/scsi/Makefile 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/scsi/Makefile 2003-05-20 04:46:44.000000000 -0700 @@ -114,7 +114,7 @@ obj-$(CONFIG_SCSI_LASI700) += lasi700.o 53c700.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o -obj-$(CONFIG_ARCH_ACORN) += ../acorn/scsi/ +obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_CHR_DEV_ST) += st.o obj-$(CONFIG_CHR_DEV_OSST) += osst.o diff -urN linux-2.5.69-bk13/drivers/scsi/NCR5380.c linux-2.5.69-bk14/drivers/scsi/NCR5380.c --- linux-2.5.69-bk13/drivers/scsi/NCR5380.c 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/drivers/scsi/NCR5380.c 2003-05-20 04:46:44.000000000 -0700 @@ -2471,11 +2471,11 @@ if (cmd->cmnd[0] != REQUEST_SENSE) cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (cmd->SCp.Status != GOOD) + else if (status_byte(cmd->SCp.Status) != GOOD) cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); #ifdef AUTOSENSE - if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) { + if ((cmd->cmnd[0] != REQUEST_SENSE) && (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) { dprintk(NDEBUG_AUTOSENSE, ("scsi%d : performing request sense\n", instance->host_no)); cmd->cmnd[0] = REQUEST_SENSE; cmd->cmnd[1] &= 0xe0; diff -urN linux-2.5.69-bk13/drivers/scsi/arm/Kconfig linux-2.5.69-bk14/drivers/scsi/arm/Kconfig --- linux-2.5.69-bk13/drivers/scsi/arm/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/Kconfig 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,89 @@ +# +# SCSI driver configuration for Acorn +# +config SCSI_ACORNSCSI_3 + tristate "Acorn SCSI card (aka30) support" + depends on ARCH_ACORN && SCSI + help + This enables support for the Acorn SCSI card (aka30). If you have an + Acorn system with one of these, say Y. If unsure, say N. + +config SCSI_ACORNSCSI_TAGGED_QUEUE + bool "Support SCSI 2 Tagged queueing" + depends on SCSI_ACORNSCSI_3 + help + Say Y here to enable tagged queuing support on the Acorn SCSI card. + + This is a feature of SCSI-2 which improves performance: the host + adapter can send several SCSI commands to a device's queue even if + previous commands haven't finished yet. Some SCSI devices don't + implement this properly, so the safe answer is N. + +config SCSI_ACORNSCSI_SYNC + bool "Support SCSI 2 Synchronous Transfers" + depends on SCSI_ACORNSCSI_3 + help + Say Y here to enable synchronous transfer negotiation with all + targets on the Acorn SCSI card. + + In general, this improves performance; however some SCSI devices + don't implement it properly, so the safe answer is N. + +config SCSI_ARXESCSI + tristate "ARXE SCSI support" + depends on ARCH_ACORN && SCSI + help + Around 1991, Arxe Systems Limited released a high density floppy + disc interface for the Acorn Archimedes range, to allow the use of + HD discs from the then new A5000 on earlier models. This interface + was either sold on its own or with an integral SCSI controller. + Technical details on this NCR53c94-based device are available at + + Say Y here to compile in support for the SCSI controller. + +config SCSI_CUMANA_2 + tristate "CumanaSCSI II support" + depends on ARCH_ACORN && SCSI + help + This enables support for the Cumana SCSI II card. If you have an + Acorn system with one of these, say Y. If unsure, say N. + +config SCSI_EESOXSCSI + tristate "EESOX support" + depends on ARCH_ACORN && SCSI + help + This enables support for the EESOX SCSI card. If you have an Acorn + system with one of these, say Y, otherwise say N. + +config SCSI_POWERTECSCSI + tristate "PowerTec support" + depends on ARCH_ACORN && SCSI + help + This enables support for the Powertec SCSI card on Acorn systems. If + you have one of these, say Y. If unsure, say N. + +comment "The following drivers are not fully supported" + depends on ARCH_ACORN && EXPERIMENTAL + +config SCSI_CUMANA_1 + tristate "CumanaSCSI I support (EXPERIMENTAL)" + depends on ARCH_ACORN && EXPERIMENTAL && SCSI + help + This enables support for the Cumana SCSI I card. If you have an + Acorn system with one of these, say Y. If unsure, say N. + +config SCSI_ECOSCSI + tristate "EcoScsi support (EXPERIMENTAL)" + depends on ARCH_ACORN && EXPERIMENTAL && (ARCH_ARC || ARCH_A5K) && SCSI + help + This enables support for the EcoSCSI card -- a small card that sits + in the Econet socket. If you have an Acorn system with one of these, + say Y. If unsure, say N. + +config SCSI_OAK1 + tristate "Oak SCSI support (EXPERIMENTAL)" + depends on ARCH_ACORN && EXPERIMENTAL && SCSI + help + This enables support for the Oak SCSI card. If you have an Acorn + system with one of these, say Y. If unsure, say N. + diff -urN linux-2.5.69-bk13/drivers/scsi/arm/Makefile linux-2.5.69-bk14/drivers/scsi/arm/Makefile --- linux-2.5.69-bk13/drivers/scsi/arm/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/Makefile 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,14 @@ +# +# Makefile for drivers/scsi/arm +# + +acornscsi_mod-objs := acornscsi.o acornscsi-io.o + +obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_ARXESCSI) += arxescsi.o fas216.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_CUMANA_1) += cumana_1.o +obj-$(CONFIG_SCSI_CUMANA_2) += cumana_2.o fas216.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_ECOSCSI) += ecoscsi.o +obj-$(CONFIG_SCSI_OAK1) += oak.o +obj-$(CONFIG_SCSI_POWERTECSCSI) += powertec.o fas216.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_EESOXSCSI) += eesox.o fas216.o queue.o msgqueue.o diff -urN linux-2.5.69-bk13/drivers/scsi/arm/acornscsi-io.S linux-2.5.69-bk14/drivers/scsi/arm/acornscsi-io.S --- linux-2.5.69-bk13/drivers/scsi/arm/acornscsi-io.S 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/acornscsi-io.S 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,145 @@ +/* + * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include + +#include +#include + +#if (IO_BASE == (PCIO_BASE & 0xff000000)) +#define ADDR(off,reg) \ + tst off, $0x80000000 ;\ + mov reg, $IO_BASE ;\ + orreq reg, reg, $(PCIO_BASE & 0x00ff0000) +#else +#define ADDR(off,reg) \ + tst off, $0x80000000 ;\ + movne reg, $IO_BASE ;\ + moveq reg, $(PCIO_BASE & 0xff000000) ;\ + orreq reg, reg, $(PCIO_BASE & 0x00ff0000) +#endif + +@ Purpose: transfer a block of data from the acorn scsi card to memory +@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) +@ Returns: nothing + + .align +ENTRY(__acornscsi_in) + stmfd sp!, {r4 - r7, lr} + bic r0, r0, #3 + mov lr, #0xff + orr lr, lr, #0xff00 +acornscsi_in16lp: + subs r2, r2, #16 + bmi acornscsi_in8 + ldmia r0!, {r3, r4, r5, r6} + and r3, r3, lr + orr r3, r3, r4, lsl #16 + and r4, r5, lr + orr r4, r4, r6, lsl #16 + ldmia r0!, {r5, r6, r7, ip} + and r5, r5, lr + orr r5, r5, r6, lsl #16 + and r6, r7, lr + orr r6, r6, ip, lsl #16 + stmia r1!, {r3 - r6} + bne acornscsi_in16lp + LOADREGS(fd, sp!, {r4 - r7, pc}) + +acornscsi_in8: adds r2, r2, #8 + bmi acornscsi_in4 + ldmia r0!, {r3, r4, r5, r6} + and r3, r3, lr + orr r3, r3, r4, lsl #16 + and r4, r5, lr + orr r4, r4, r6, lsl #16 + stmia r1!, {r3 - r4} + LOADREGS(eqfd, sp!, {r4 - r7, pc}) + sub r2, r2, #8 + +acornscsi_in4: adds r2, r2, #4 + bmi acornscsi_in2 + ldmia r0!, {r3, r4} + and r3, r3, lr + orr r3, r3, r4, lsl #16 + str r3, [r1], #4 + LOADREGS(eqfd, sp!, {r4 - r7, pc}) + sub r2, r2, #4 + +acornscsi_in2: adds r2, r2, #2 + ldr r3, [r0], #4 + and r3, r3, lr + strb r3, [r1], #1 + mov r3, r3, lsr #8 + strplb r3, [r1], #1 + LOADREGS(fd, sp!, {r4 - r7, pc}) + +@ Purpose: transfer a block of data from memory to the acorn scsi card +@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) +@ Returns: nothing + +ENTRY(__acornscsi_out) + stmfd sp!, {r4 - r6, lr} + bic r0, r0, #3 +acornscsi_out16lp: + subs r2, r2, #16 + bmi acornscsi_out8 + ldmia r1!, {r4, r6, ip, lr} + mov r3, r4, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, r4, lsr #16 + orr r4, r4, r4, lsl #16 + mov r5, r6, lsl #16 + orr r5, r5, r5, lsr #16 + mov r6, r6, lsr #16 + orr r6, r6, r6, lsl #16 + stmia r0!, {r3, r4, r5, r6} + mov r3, ip, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, ip, lsr #16 + orr r4, r4, r4, lsl #16 + mov ip, lr, lsl #16 + orr ip, ip, ip, lsr #16 + mov lr, lr, lsr #16 + orr lr, lr, lr, lsl #16 + stmia r0!, {r3, r4, ip, lr} + bne acornscsi_out16lp + LOADREGS(fd, sp!, {r4 - r6, pc}) + +acornscsi_out8: adds r2, r2, #8 + bmi acornscsi_out4 + ldmia r1!, {r4, r6} + mov r3, r4, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, r4, lsr #16 + orr r4, r4, r4, lsl #16 + mov r5, r6, lsl #16 + orr r5, r5, r5, lsr #16 + mov r6, r6, lsr #16 + orr r6, r6, r6, lsl #16 + stmia r0!, {r3, r4, r5, r6} + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + + sub r2, r2, #8 +acornscsi_out4: adds r2, r2, #4 + bmi acornscsi_out2 + ldr r4, [r1], #4 + mov r3, r4, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, r4, lsr #16 + orr r4, r4, r4, lsl #16 + stmia r0!, {r3, r4} + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + + sub r2, r2, #4 +acornscsi_out2: adds r2, r2, #2 + ldr r3, [r1], #2 + strb r3, [r0], #1 + mov r3, r3, lsr #8 + strplb r3, [r0], #1 + LOADREGS(fd, sp!, {r4 - r6, pc}) + diff -urN linux-2.5.69-bk13/drivers/scsi/arm/acornscsi.c linux-2.5.69-bk14/drivers/scsi/arm/acornscsi.c --- linux-2.5.69-bk13/drivers/scsi/arm/acornscsi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/acornscsi.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,3126 @@ +/* + * linux/drivers/acorn/scsi/acornscsi.c + * + * Acorn SCSI 3 driver + * By R.M.King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Abandoned using the Select and Transfer command since there were + * some nasty races between our software and the target devices that + * were not easy to solve, and the device errata had a lot of entries + * for this command, some of them quite nasty... + * + * Changelog: + * 26-Sep-1997 RMK Re-jigged to use the queue module. + * Re-coded state machine to be based on driver + * state not scsi state. Should be easier to debug. + * Added acornscsi_release to clean up properly. + * Updated proc/scsi reporting. + * 05-Oct-1997 RMK Implemented writing to SCSI devices. + * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/ + * reconnect race condition causing a warning message. + * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. + * 15-Oct-1997 RMK Improved handling of commands. + * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. + * 13-Dec-1998 RMK Better abort code and command handling. Extra state + * transitions added to allow dodgy devices to work. + */ +#define DEBUG_NO_WRITE 1 +#define DEBUG_QUEUES 2 +#define DEBUG_DMA 4 +#define DEBUG_ABORT 8 +#define DEBUG_DISCON 16 +#define DEBUG_CONNECT 32 +#define DEBUG_PHASES 64 +#define DEBUG_WRITE 128 +#define DEBUG_LINK 256 +#define DEBUG_MESSAGES 512 +#define DEBUG_RESET 1024 +#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ + DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ + DEBUG_DMA|DEBUG_QUEUES) + +/* DRIVER CONFIGURATION + * + * SCSI-II Tagged queue support. + * + * I don't have any SCSI devices that support it, so it is totally untested + * (except to make sure that it doesn't interfere with any non-tagging + * devices). It is not fully implemented either - what happens when a + * tagging device reconnects??? + * + * You can tell if you have a device that supports tagged queueing my + * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported + * as '2 TAG'. + * + * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config + * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug, + * comment out the undef. + */ +#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE +/* + * SCSI-II Linked command support. + * + * The higher level code doesn't support linked commands yet, and so the option + * is undef'd here. + */ +#undef CONFIG_SCSI_ACORNSCSI_LINK +/* + * SCSI-II Synchronous transfer support. + * + * Tried and tested... + * + * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max) + * SDTR_PERIOD - period of REQ signal (min=125, max=1020) + * DEFAULT_PERIOD - default REQ period. + */ +#define SDTR_SIZE 12 +#define SDTR_PERIOD 125 +#define DEFAULT_PERIOD 500 + +/* + * Debugging information + * + * DEBUG - bit mask from list above + * DEBUG_TARGET - is defined to the target number if you want to debug + * a specific target. [only recon/write/dma]. + */ +#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE) +/* only allow writing to SCSI device 0 */ +#define NO_WRITE 0xFE +/*#define DEBUG_TARGET 2*/ +/* + * Select timeout time (in 10ms units) + * + * This is the timeout used between the start of selection and the WD33C93 + * chip deciding that the device isn't responding. + */ +#define TIMEOUT_TIME 10 +/* + * Define this if you want to have verbose explaination of SCSI + * status/messages. + */ +#undef CONFIG_ACORNSCSI_CONSTANTS +/* + * Define this if you want to use the on board DMAC [don't remove this option] + * If not set, then use PIO mode (not currently supported). + */ +#define USE_DMAC + +/* + * ==================================================================================== + */ + +#ifdef DEBUG_TARGET +#define DBG(cmd,xxx...) \ + if (cmd->device->id == DEBUG_TARGET) { \ + xxx; \ + } +#else +#define DBG(cmd,xxx...) xxx +#endif + +#ifndef STRINGIFY +#define STRINGIFY(x) #x +#endif +#define STRx(x) STRINGIFY(x) +#define NO_WRITE_STR STRx(NO_WRITE) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "acornscsi.h" +#include "msgqueue.h" +#include "scsi.h" + +#include + +#define VER_MAJOR 2 +#define VER_MINOR 0 +#define VER_PATCH 6 + +#ifndef ABORT_TAG +#define ABORT_TAG 0xd +#else +#error "Yippee! ABORT TAG is now defined! Remove this error!" +#endif + +#ifdef CONFIG_SCSI_ACORNSCSI_LINK +#error SCSI2 LINKed commands not supported (yet)! +#endif + +#ifdef USE_DMAC +/* + * DMAC setup parameters + */ +#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP) +#define INIT_DEVCON1 (DEVCON1_BHLD) +#define DMAC_READ (MODECON_READ) +#define DMAC_WRITE (MODECON_WRITE) +#define INIT_SBICDMA (CTRL_DMABURST) + +#define scsi_xferred have_data_in + +/* + * Size of on-board DMA buffer + */ +#define DMAC_BUFFER_SIZE 65536 +#endif + +#define STATUS_BUFFER_TO_PRINT 24 + +unsigned int sdtr_period = SDTR_PERIOD; +unsigned int sdtr_size = SDTR_SIZE; + +static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); +static int acornscsi_reconnect_finish(AS_Host *host); +static void acornscsi_dma_cleanup(AS_Host *host); +static void acornscsi_abortcmd(AS_Host *host, unsigned char tag); + +/* ==================================================================================== + * Miscellaneous + */ + +static inline void +sbic_arm_write(unsigned int io_port, int reg, int value) +{ + __raw_writeb(reg, io_port); + __raw_writeb(value, io_port + 4); +} + +#define sbic_arm_writenext(io,val) \ + __raw_writeb((val), (io) + 4) + +static inline +int sbic_arm_read(unsigned int io_port, int reg) +{ + if(reg == ASR) + return __raw_readl(io_port) & 255; + __raw_writeb(reg, io_port); + return __raw_readl(io_port + 4) & 255; +} + +#define sbic_arm_readnext(io) \ + __raw_readb((io) + 4) + +#ifdef USE_DMAC +#define dmac_read(io_port,reg) \ + inb((io_port) + (reg)) + +#define dmac_write(io_port,reg,value) \ + ({ outb((value), (io_port) + (reg)); }) + +#define dmac_clearintr(io_port) \ + ({ outb(0, (io_port)); }) + +static inline +unsigned int dmac_address(unsigned int io_port) +{ + return dmac_read(io_port, TXADRHI) << 16 | + dmac_read(io_port, TXADRMD) << 8 | + dmac_read(io_port, TXADRLO); +} + +static +void acornscsi_dumpdma(AS_Host *host, char *where) +{ + unsigned int mode, addr, len; + + mode = dmac_read(host->dma.io_port, MODECON); + addr = dmac_address(host->dma.io_port); + len = dmac_read(host->dma.io_port, TXCNTHI) << 8 | + dmac_read(host->dma.io_port, TXCNTLO); + + printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", + host->host->host_no, where, + mode, addr, (len + 1) & 0xffff, + dmac_read(host->dma.io_port, MASKREG)); + + printk("DMA @%06x, ", host->dma.start_addr); + printk("BH @%p +%04x, ", host->scsi.SCp.ptr, + host->scsi.SCp.this_residual); + printk("DT @+%04x ST @+%04x", host->dma.transferred, + host->scsi.SCp.scsi_xferred); + printk("\n"); +} +#endif + +static +unsigned long acornscsi_sbic_xfcount(AS_Host *host) +{ + unsigned long length; + + length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16; + length |= sbic_arm_readnext(host->scsi.io_port) << 8; + length |= sbic_arm_readnext(host->scsi.io_port); + + return length; +} + +static int +acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg) +{ + int asr; + + do { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if ((asr & stat_mask) == stat) + return 0; + + udelay(1); + } while (--timeout); + + printk("scsi%d: timeout while %s\n", host->host->host_no, msg); + + return -1; +} + +static +int acornscsi_sbic_issuecmd(AS_Host *host, int command) +{ + if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) + return -1; + + sbic_arm_write(host->scsi.io_port, CMND, command); + + return 0; +} + +static void +acornscsi_csdelay(unsigned int cs) +{ + unsigned long target_jiffies, flags; + + target_jiffies = jiffies + 1 + cs * HZ / 100; + + local_save_flags(flags); + local_irq_enable(); + + while (time_before(jiffies, target_jiffies)) barrier(); + + local_irq_restore(flags); +} + +static +void acornscsi_resetcard(AS_Host *host) +{ + unsigned int i, timeout; + + /* assert reset line */ + host->card.page_reg = 0x80; + outb(host->card.page_reg, host->card.io_page); + + /* wait 3 cs. SCSI standard says 25ms. */ + acornscsi_csdelay(3); + + host->card.page_reg = 0; + outb(host->card.page_reg, host->card.io_page); + + /* + * Should get a reset from the card + */ + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", + host->host->host_no); + + sbic_arm_read(host->scsi.io_port, ASR); + sbic_arm_read(host->scsi.io_port, SSR); + + /* setup sbic - WD33C93A */ + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); + + /* + * Command should cause a reset interrupt + */ + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", + host->host->host_no); + + sbic_arm_read(host->scsi.io_port, ASR); + if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01) + printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", + host->host->host_no); + + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + + host->card.page_reg = 0x40; + outb(host->card.page_reg, host->card.io_page); + + /* setup dmac - uPC71071 */ + dmac_write(host->dma.io_port, INIT, 0); +#ifdef USE_DMAC + dmac_write(host->dma.io_port, INIT, INIT_8BIT); + dmac_write(host->dma.io_port, CHANNEL, CHANNEL_0); + dmac_write(host->dma.io_port, DEVCON0, INIT_DEVCON0); + dmac_write(host->dma.io_port, DEVCON1, INIT_DEVCON1); +#endif + + host->SCpnt = NULL; + host->scsi.phase = PHASE_IDLE; + host->scsi.disconnectable = 0; + + memset(host->busyluns, 0, sizeof(host->busyluns)); + + for (i = 0; i < 8; i++) { + host->device[i].sync_state = SYNC_NEGOCIATE; + host->device[i].disconnect_ok = 1; + } + + /* wait 25 cs. SCSI standard says 250ms. */ + acornscsi_csdelay(25); +} + +/*============================================================================================= + * Utility routines (eg. debug) + */ +#ifdef CONFIG_ACORNSCSI_CONSTANTS +static char *acornscsi_interrupttype[] = { + "rst", "suc", "p/a", "3", + "term", "5", "6", "7", + "serv", "9", "a", "b", + "c", "d", "e", "f" +}; + +static signed char acornscsi_map[] = { + 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char *acornscsi_interruptcode[] = { + /* 0 */ + "reset - normal mode", /* 00 */ + "reset - advanced mode", /* 01 */ + + /* 2 */ + "sel", /* 11 */ + "sel+xfer", /* 16 */ + "data-out", /* 18 */ + "data-in", /* 19 */ + "cmd", /* 1A */ + "stat", /* 1B */ + "??-out", /* 1C */ + "??-in", /* 1D */ + "msg-out", /* 1E */ + "msg-in", /* 1F */ + + /* 12 */ + "/ACK asserted", /* 20 */ + "save-data-ptr", /* 21 */ + "{re}sel", /* 22 */ + + /* 15 */ + "inv cmd", /* 40 */ + "unexpected disconnect", /* 41 */ + "sel timeout", /* 42 */ + "P err", /* 43 */ + "P err+ATN", /* 44 */ + "bad status byte", /* 47 */ + + /* 21 */ + "resel, no id", /* 80 */ + "resel", /* 81 */ + "discon", /* 85 */ +}; + +static +void print_scsi_status(unsigned int ssr) +{ + if (acornscsi_map[ssr] != -1) + printk("%s:%s", + acornscsi_interrupttype[(ssr >> 4)], + acornscsi_interruptcode[acornscsi_map[ssr]]); + else + printk("%X:%X", ssr >> 4, ssr & 0x0f); +} +#endif + +static +void print_sbic_status(int asr, int ssr, int cmdphase) +{ +#ifdef CONFIG_ACORNSCSI_CONSTANTS + printk("sbic: %c%c%c%c%c%c ", + asr & ASR_INT ? 'I' : 'i', + asr & ASR_LCI ? 'L' : 'l', + asr & ASR_BSY ? 'B' : 'b', + asr & ASR_CIP ? 'C' : 'c', + asr & ASR_PE ? 'P' : 'p', + asr & ASR_DBR ? 'D' : 'd'); + printk("scsi: "); + print_scsi_status(ssr); + printk(" ph %02X\n", cmdphase); +#else + printk("sbic: %02X scsi: %X:%X ph: %02X\n", + asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase); +#endif +} + +static void +acornscsi_dumplogline(AS_Host *host, int target, int line) +{ + unsigned long prev; + signed int ptr; + + ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; + if (ptr < 0) + ptr += STATUS_BUFFER_SIZE; + + printk("%c: %3s:", target == 8 ? 'H' : '0' + target, + line == 0 ? "ph" : line == 1 ? "ssr" : "int"); + + prev = host->status[target][ptr].when; + + for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + unsigned long time_diff; + + if (!host->status[target][ptr].when) + continue; + + switch (line) { + case 0: + printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', + host->status[target][ptr].ph); + break; + + case 1: + printk(" %02X", host->status[target][ptr].ssr); + break; + + case 2: + time_diff = host->status[target][ptr].when - prev; + prev = host->status[target][ptr].when; + if (time_diff == 0) + printk("==^"); + else if (time_diff >= 100) + printk(" "); + else + printk(" %02ld", time_diff); + break; + } + } + + printk("\n"); +} + +static +void acornscsi_dumplog(AS_Host *host, int target) +{ + do { + acornscsi_dumplogline(host, target, 0); + acornscsi_dumplogline(host, target, 1); + acornscsi_dumplogline(host, target, 2); + + if (target == 8) + break; + + target = 8; + } while (1); +} + +static +char acornscsi_target(AS_Host *host) +{ + if (host->SCpnt) + return '0' + host->SCpnt->device->id; + return 'H'; +} + +/* + * Prototype: cmdtype_t acornscsi_cmdtype(int command) + * Purpose : differentiate READ from WRITE from other commands + * Params : command - command to interpret + * Returns : CMD_READ - command reads data, + * CMD_WRITE - command writes data, + * CMD_MISC - everything else + */ +static inline +cmdtype_t acornscsi_cmdtype(int command) +{ + switch (command) { + case WRITE_6: case WRITE_10: case WRITE_12: + return CMD_WRITE; + case READ_6: case READ_10: case READ_12: + return CMD_READ; + default: + return CMD_MISC; + } +} + +/* + * Prototype: int acornscsi_datadirection(int command) + * Purpose : differentiate between commands that have a DATA IN phase + * and a DATA OUT phase + * Params : command - command to interpret + * Returns : DATADIR_OUT - data out phase expected + * DATADIR_IN - data in phase expected + */ +static +datadir_t acornscsi_datadirection(int command) +{ + switch (command) { + case CHANGE_DEFINITION: case COMPARE: case COPY: + case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: + case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: + case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case WRITE_6: case WRITE_10: case WRITE_VERIFY: + case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME: + case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: + case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW: + case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea: + return DATADIR_OUT; + default: + return DATADIR_IN; + } +} + +/* + * Purpose : provide values for synchronous transfers with 33C93. + * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting + * Modified by Russell King for 8MHz WD33C93A + */ +static struct sync_xfer_tbl { + unsigned int period_ns; + unsigned char reg_value; +} sync_xfer_table[] = { + { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 }, + { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 }, + { 874, 0x70 }, { 999, 0x00 }, { 0, 0 } +}; + +/* + * Prototype: int acornscsi_getperiod(unsigned char syncxfer) + * Purpose : period for the synchronous transfer setting + * Params : syncxfer SYNCXFER register value + * Returns : period in ns. + */ +static +int acornscsi_getperiod(unsigned char syncxfer) +{ + int i; + + syncxfer &= 0xf0; + if (syncxfer == 0x10) + syncxfer = 0; + + for (i = 1; sync_xfer_table[i].period_ns; i++) + if (syncxfer == sync_xfer_table[i].reg_value) + return sync_xfer_table[i].period_ns; + return 0; +} + +/* + * Prototype: int round_period(unsigned int period) + * Purpose : return index into above table for a required REQ period + * Params : period - time (ns) for REQ + * Returns : table index + * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting + */ +static inline +int round_period(unsigned int period) +{ + int i; + + for (i = 1; sync_xfer_table[i].period_ns; i++) { + if ((period <= sync_xfer_table[i].period_ns) && + (period > sync_xfer_table[i - 1].period_ns)) + return i; + } + return 7; +} + +/* + * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) + * Purpose : calculate value for 33c93s SYNC register + * Params : period - time (ns) for REQ + * offset - offset in bytes between REQ/ACK + * Returns : value for SYNC register + * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting + */ +static +unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) +{ + return sync_xfer_table[round_period(period)].reg_value | + ((offset < SDTR_SIZE) ? offset : SDTR_SIZE); +} + +/* ==================================================================================== + * Command functions + */ +/* + * Function: acornscsi_kick(AS_Host *host) + * Purpose : kick next command to interface + * Params : host - host to send command to + * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING + * Notes : interrupts are always disabled! + */ +static +intr_ret_t acornscsi_kick(AS_Host *host) +{ + int from_queue = 0; + Scsi_Cmnd *SCpnt; + + /* first check to see if a command is waiting to be executed */ + SCpnt = host->origSCpnt; + host->origSCpnt = NULL; + + /* retrieve next command */ + if (!SCpnt) { + SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); + if (!SCpnt) + return INTR_IDLE; + + from_queue = 1; + } + + if (host->scsi.disconnectable && host->SCpnt) { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); + host->scsi.disconnectable = 0; +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", + host->host->host_no, acornscsi_target(host))); +#endif + host->SCpnt = NULL; + } + + /* + * If we have an interrupt pending, then we may have been reselected. + * In this case, we don't want to write to the registers + */ + if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { + sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->device->id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN); + } + + /* + * claim host busy - all of these must happen atomically wrt + * our interrupt routine. Failure means command loss. + */ + host->scsi.phase = PHASE_CONNECTING; + host->SCpnt = SCpnt; + host->scsi.SCp = SCpnt->SCp; + host->dma.xfer_setup = 0; + host->dma.xfer_required = 0; + host->dma.xfer_done = 0; + +#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) + DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", + host->host->host_no, '0' + SCpnt->device->id, + SCpnt->cmnd[0])); +#endif + + if (from_queue) { +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + /* + * tagged queueing - allocate a new tag to this command + */ + if (SCpnt->device->tagged_queue) { + SCpnt->device->current_tag += 1; + if (SCpnt->device->current_tag == 0) + SCpnt->device->current_tag = 1; + SCpnt->tag = SCpnt->device->current_tag; + } else +#endif + set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + + host->stats.removes += 1; + + switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { + case CMD_WRITE: + host->stats.writes += 1; + break; + case CMD_READ: + host->stats.reads += 1; + break; + case CMD_MISC: + host->stats.miscs += 1; + break; + } + } + + return INTR_PROCESSING; +} + +/* + * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) + * Purpose : complete processing for command + * Params : host - interface that completed + * result - driver byte of result + */ +static +void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) +{ + Scsi_Cmnd *SCpnt = *SCpntp; + + /* clean up */ + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + + host->stats.fins += 1; + + if (SCpnt) { + *SCpntp = NULL; + + acornscsi_dma_cleanup(host); + + SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; + + /* + * In theory, this should not happen. In practice, it seems to. + * Only trigger an error if the device attempts to report all happy + * but with untransferred buffers... If we don't do something, then + * data loss will occur. Should we check SCpnt->underflow here? + * It doesn't appear to be set to something meaningful by the higher + * levels all the time. + */ + if (result == DID_OK) { + int xfer_warn = 0; + + if (SCpnt->underflow == 0) { + if (host->scsi.SCp.ptr && + acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) + xfer_warn = 1; + } else { + if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || + host->scsi.SCp.scsi_xferred != host->dma.transferred) + xfer_warn = 1; + } + + /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) + * Targets which break data transfers into multiple + * connections shall end each successful connection + * (except possibly the last) with a SAVE DATA + * POINTER - DISCONNECT message sequence. + * + * This makes it difficult to ensure that a transfer has + * completed. If we reach the end of a transfer during + * the command, then we can only have finished the transfer. + * therefore, if we seem to have some data remaining, this + * is not a problem. + */ + if (host->dma.xfer_done) + xfer_warn = 0; + + if (xfer_warn) { + switch (status_byte(SCpnt->result)) { + case CHECK_CONDITION: + case COMMAND_TERMINATED: + case BUSY: + case QUEUE_FULL: + case RESERVATION_CONFLICT: + break; + + default: + printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", + host->host->host_no, SCpnt->result); + print_command(SCpnt->cmnd); + acornscsi_dumpdma(host, "done"); + acornscsi_dumplog(host, SCpnt->device->id); + SCpnt->result &= 0xffff; + SCpnt->result |= DID_ERROR << 16; + } + } + } + + if (!SCpnt->scsi_done) + panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); + + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + + SCpnt->scsi_done(SCpnt); + } else + printk("scsi%d: null command in acornscsi_done", host->host->host_no); + + host->scsi.phase = PHASE_IDLE; +} + +/* ==================================================================================== + * DMA routines + */ +/* + * Purpose : update SCSI Data Pointer + * Notes : this will only be one SG entry or less + */ +static +void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length) +{ + SCp->ptr += length; + SCp->this_residual -= length; + + if (SCp->this_residual == 0 && next_SCp(SCp) == 0) + host->dma.xfer_done = 1; +} + +/* + * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, + * unsigned int start_addr, unsigned int length) + * Purpose : read data from DMA RAM + * Params : host - host to transfer from + * ptr - DRAM address + * start_addr - host mem address + * length - number of bytes to transfer + * Notes : this will only be one SG entry or less + */ +static +void acornscsi_data_read(AS_Host *host, char *ptr, + unsigned int start_addr, unsigned int length) +{ + extern void __acornscsi_in(int port, char *buf, int len); + unsigned int page, offset, len = length; + + page = (start_addr >> 12); + offset = start_addr & ((1 << 12) - 1); + + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + + while (len > 0) { + unsigned int this_len; + + if (len + offset > (1 << 12)) + this_len = (1 << 12) - offset; + else + this_len = len; + + __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len); + + offset += this_len; + ptr += this_len; + len -= this_len; + + if (offset == (1 << 12)) { + offset = 0; + page ++; + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + } + } + outb(host->card.page_reg, host->card.io_page); +} + +/* + * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, + * unsigned int start_addr, unsigned int length) + * Purpose : write data to DMA RAM + * Params : host - host to transfer from + * ptr - DRAM address + * start_addr - host mem address + * length - number of bytes to transfer + * Notes : this will only be one SG entry or less + */ +static +void acornscsi_data_write(AS_Host *host, char *ptr, + unsigned int start_addr, unsigned int length) +{ + extern void __acornscsi_out(int port, char *buf, int len); + unsigned int page, offset, len = length; + + page = (start_addr >> 12); + offset = start_addr & ((1 << 12) - 1); + + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + + while (len > 0) { + unsigned int this_len; + + if (len + offset > (1 << 12)) + this_len = (1 << 12) - offset; + else + this_len = len; + + __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len); + + offset += this_len; + ptr += this_len; + len -= this_len; + + if (offset == (1 << 12)) { + offset = 0; + page ++; + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + } + } + outb(host->card.page_reg, host->card.io_page); +} + +/* ========================================================================================= + * On-board DMA routines + */ +#ifdef USE_DMAC +/* + * Prototype: void acornscsi_dmastop(AS_Host *host) + * Purpose : stop all DMA + * Params : host - host on which to stop DMA + * Notes : This is called when leaving DATA IN/OUT phase, + * or when interface is RESET + */ +static inline +void acornscsi_dma_stop(AS_Host *host) +{ + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "stop")); +#endif +} + +/* + * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) + * Purpose : setup DMA controller for data transfer + * Params : host - host to setup + * direction - data transfer direction + * Notes : This is called when entering DATA I/O phase, not + * while we're in a DATA I/O phase + */ +static +void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) +{ + unsigned int address, length, mode; + + host->dma.direction = direction; + + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + + if (direction == DMA_OUT) { +#if (DEBUG & DEBUG_NO_WRITE) + if (NO_WRITE & (1 << host->SCpnt->device->id)) { + printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", + host->host->host_no, acornscsi_target(host)); + return; + } +#endif + mode = DMAC_WRITE; + } else + mode = DMAC_READ; + + /* + * Allocate some buffer space, limited to half the buffer size + */ + length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + if (length) { + host->dma.start_addr = address = host->dma.free_addr; + host->dma.free_addr = (host->dma.free_addr + length) & + (DMAC_BUFFER_SIZE - 1); + + /* + * Transfer data to DMA memory + */ + if (direction == DMA_OUT) + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, + length); + + length -= 1; + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MODECON, mode); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "strt")); +#endif + host->dma.xfer_setup = 1; + } +} + +/* + * Function: void acornscsi_dma_cleanup(AS_Host *host) + * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct + * Params : host - host to finish + * Notes : This is called when a command is: + * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT + * : This must not return until all transfers are completed. + */ +static +void acornscsi_dma_cleanup(AS_Host *host) +{ + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); + + /* + * Check for a pending transfer + */ + if (host->dma.xfer_required) { + host->dma.xfer_required = 0; + if (host->dma.direction == DMA_IN) + acornscsi_data_read(host, host->dma.xfer_ptr, + host->dma.xfer_start, host->dma.xfer_length); + } + + /* + * Has a transfer been setup? + */ + if (host->dma.xfer_setup) { + unsigned int transferred; + + host->dma.xfer_setup = 0; + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi")); +#endif + + /* + * Calculate number of bytes transferred from DMA. + */ + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; + host->dma.transferred += transferred; + + if (host->dma.direction == DMA_IN) + acornscsi_data_read(host, host->scsi.SCp.ptr, + host->dma.start_addr, transferred); + + /* + * Update SCSI pointers + */ + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo")); +#endif + } +} + +/* + * Function: void acornscsi_dmacintr(AS_Host *host) + * Purpose : handle interrupts from DMAC device + * Params : host - host to process + * Notes : If reading, we schedule the read to main memory & + * allow the transfer to continue. + * : If writing, we fill the onboard DMA memory from main + * memory. + * : Called whenever DMAC finished it's current transfer. + */ +static +void acornscsi_dma_intr(AS_Host *host) +{ + unsigned int address, length, transferred; + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "inti")); +#endif + + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); + + /* + * Calculate amount transferred via DMA + */ + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; + host->dma.transferred += transferred; + + /* + * Schedule DMA transfer off board + */ + if (host->dma.direction == DMA_IN) { + host->dma.xfer_start = host->dma.start_addr; + host->dma.xfer_length = transferred; + host->dma.xfer_ptr = host->scsi.SCp.ptr; + host->dma.xfer_required = 1; + } + + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); + + /* + * Allocate some buffer space, limited to half the on-board RAM size + */ + length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + if (length) { + host->dma.start_addr = address = host->dma.free_addr; + host->dma.free_addr = (host->dma.free_addr + length) & + (DMAC_BUFFER_SIZE - 1); + + /* + * Transfer data to DMA memory + */ + if (host->dma.direction == DMA_OUT) + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, + length); + + length -= 1; + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "into")); +#endif + } else { + host->dma.xfer_setup = 0; +#if 0 + /* + * If the interface still wants more, then this is an error. + * We give it another byte, but we also attempt to raise an + * attention condition. We continue giving one byte until + * the device recognises the attention. + */ + if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) { + acornscsi_abortcmd(host, host->SCpnt->tag); + + dmac_write(host->dma.io_port, TXCNTLO, 0); + dmac_write(host->dma.io_port, TXCNTHI, 0); + dmac_write(host->dma.io_port, TXADRLO, 0); + dmac_write(host->dma.io_port, TXADRMD, 0); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); + } +#endif + } +} + +/* + * Function: void acornscsi_dma_xfer(AS_Host *host) + * Purpose : transfer data between AcornSCSI and memory + * Params : host - host to process + */ +static +void acornscsi_dma_xfer(AS_Host *host) +{ + host->dma.xfer_required = 0; + + if (host->dma.direction == DMA_IN) + acornscsi_data_read(host, host->dma.xfer_ptr, + host->dma.xfer_start, host->dma.xfer_length); +} + +/* + * Function: void acornscsi_dma_adjust(AS_Host *host) + * Purpose : adjust DMA pointers & count for bytes transferred to + * SBIC but not SCSI bus. + * Params : host - host to adjust DMA count for + */ +static +void acornscsi_dma_adjust(AS_Host *host) +{ + if (host->dma.xfer_setup) { + signed long transferred; +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) + DBG(host->SCpnt, acornscsi_dumpdma(host, "adji")); +#endif + /* + * Calculate correct DMA address - DMA is ahead of SCSI bus while + * writing. + * host->scsi.SCp.scsi_xferred is the number of bytes + * actually transferred to/from the SCSI bus. + * host->dma.transferred is the number of bytes transferred + * over DMA since host->dma.start_addr was last set. + * + * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred + * - host->dma.transferred + */ + transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; + if (transferred < 0) + printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", + host->host->host_no, acornscsi_target(host), transferred); + else if (transferred == 0) + host->dma.xfer_setup = 0; + else { + transferred += host->dma.start_addr; + dmac_write(host->dma.io_port, TXADRLO, transferred); + dmac_write(host->dma.io_port, TXADRMD, transferred >> 8); + dmac_write(host->dma.io_port, TXADRHI, transferred >> 16); +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) + DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo")); +#endif + } + } +} +#endif + +/* ========================================================================================= + * Data I/O + */ +static int +acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout) +{ + unsigned int asr, timeout = max_timeout; + int my_ptr = *ptr; + + while (my_ptr < len) { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if (asr & ASR_DBR) { + timeout = max_timeout; + + sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]); + } else if (asr & ASR_INT) + break; + else if (--timeout == 0) + break; + udelay(1); + } + + *ptr = my_ptr; + + return (timeout == 0) ? -1 : 0; +} + +/* + * Function: void acornscsi_sendcommand(AS_Host *host) + * Purpose : send a command to a target + * Params : host - host which is connected to target + */ +static void +acornscsi_sendcommand(AS_Host *host) +{ + Scsi_Cmnd *SCpnt = host->SCpnt; + + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); + + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + + if (acornscsi_write_pio(host, SCpnt->cmnd, + (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) + printk("scsi%d: timeout while sending command\n", host->host->host_no); + + host->scsi.phase = PHASE_COMMAND; +} + +static +void acornscsi_sendmessage(AS_Host *host) +{ + unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); + unsigned int msgnr; + struct message *msg; + +#if (DEBUG & DEBUG_MESSAGES) + printk("scsi%d.%c: sending message ", + host->host->host_no, acornscsi_target(host)); +#endif + + switch (message_length) { + case 0: + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1"); + + sbic_arm_write(host->scsi.io_port, DATA, NOP); + + host->scsi.last_message = NOP; +#if (DEBUG & DEBUG_MESSAGES) + printk("NOP"); +#endif + break; + + case 1: + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + msg = msgqueue_getmsg(&host->scsi.msgs, 0); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2"); + + sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]); + + host->scsi.last_message = msg->msg[0]; +#if (DEBUG & DEBUG_MESSAGES) + print_msg(msg->msg); +#endif + break; + + default: + /* + * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14) + * 'When a target sends this (MESSAGE_REJECT) message, it + * shall change to MESSAGE IN phase and send this message + * prior to requesting additional message bytes from the + * initiator. This provides an interlock so that the + * initiator can determine which message byte is rejected. + */ + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, message_length); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + + msgnr = 0; + while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) { + unsigned int i; +#if (DEBUG & DEBUG_MESSAGES) + print_msg(msg); +#endif + i = 0; + if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000)) + printk("scsi%d: timeout while sending message\n", host->host->host_no); + + host->scsi.last_message = msg->msg[0]; + if (msg->msg[0] == EXTENDED_MESSAGE) + host->scsi.last_message |= msg->msg[2] << 8; + + if (i != msg->length) + break; + } + break; + } +#if (DEBUG & DEBUG_MESSAGES) + printk("\n"); +#endif +} + +/* + * Function: void acornscsi_readstatusbyte(AS_Host *host) + * Purpose : Read status byte from connected target + * Params : host - host connected to target + */ +static +void acornscsi_readstatusbyte(AS_Host *host) +{ + acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT); + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte"); + host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA); +} + +/* + * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host) + * Purpose : Read one message byte from connected target + * Params : host - host connected to target + */ +static +unsigned char acornscsi_readmessagebyte(AS_Host *host) +{ + unsigned char message; + + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte"); + + message = sbic_arm_read(host->scsi.io_port, DATA); + + /* wait for MSGIN-XFER-PAUSED */ + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte"); + + sbic_arm_read(host->scsi.io_port, SSR); + + return message; +} + +/* + * Function: void acornscsi_message(AS_Host *host) + * Purpose : Read complete message from connected target & action message + * Params : host - host connected to target + */ +static +void acornscsi_message(AS_Host *host) +{ + unsigned char message[16]; + unsigned int msgidx = 0, msglen = 1; + + do { + message[msgidx] = acornscsi_readmessagebyte(host); + + switch (msgidx) { + case 0: + if (message[0] == EXTENDED_MESSAGE || + (message[0] >= 0x20 && message[0] <= 0x2f)) + msglen = 2; + break; + + case 1: + if (message[0] == EXTENDED_MESSAGE) + msglen += message[msgidx]; + break; + } + msgidx += 1; + if (msgidx < msglen) { + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); + + /* wait for next msg-in */ + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack"); + sbic_arm_read(host->scsi.io_port, SSR); + } + } while (msgidx < msglen); + +#if (DEBUG & DEBUG_MESSAGES) + printk("scsi%d.%c: message in: ", + host->host->host_no, acornscsi_target(host)); + print_msg(message); + printk("\n"); +#endif + + if (host->scsi.phase == PHASE_RECONNECTED) { + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) + * 'Whenever a target reconnects to an initiator to continue + * a tagged I/O process, the SIMPLE QUEUE TAG message shall + * be sent immediately following the IDENTIFY message...' + */ + if (message[0] == SIMPLE_QUEUE_TAG) + host->scsi.reconnected.tag = message[1]; + if (acornscsi_reconnect_finish(host)) + host->scsi.phase = PHASE_MSGIN; + } + + switch (message[0]) { + case ABORT: + case ABORT_TAG: + case COMMAND_COMPLETE: + if (host->scsi.phase != PHASE_STATUSIN) { + printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n", + host->host->host_no, acornscsi_target(host)); + acornscsi_dumplog(host, host->SCpnt->device->id); + } + host->scsi.phase = PHASE_DONE; + host->scsi.SCp.Message = message[0]; + break; + + case SAVE_POINTERS: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20) + * 'The SAVE DATA POINTER message is sent from a target to + * direct the initiator to copy the active data pointer to + * the saved data pointer for the current I/O process. + */ + acornscsi_dma_cleanup(host); + host->SCpnt->SCp = host->scsi.SCp; + host->SCpnt->SCp.sent_command = 0; + host->scsi.phase = PHASE_MSGIN; + break; + + case RESTORE_POINTERS: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19) + * 'The RESTORE POINTERS message is sent from a target to + * direct the initiator to copy the most recently saved + * command, data, and status pointers for the I/O process + * to the corresponding active pointers. The command and + * status pointers shall be restored to the beginning of + * the present command and status areas.' + */ + acornscsi_dma_cleanup(host); + host->scsi.SCp = host->SCpnt->SCp; + host->scsi.phase = PHASE_MSGIN; + break; + + case DISCONNECT: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2) + * 'On those occasions when an error or exception condition occurs + * and the target elects to repeat the information transfer, the + * target may repeat the transfer either issuing a RESTORE POINTERS + * message or by disconnecting without issuing a SAVE POINTERS + * message. When reconnection is completed, the most recent + * saved pointer values are restored.' + */ + acornscsi_dma_cleanup(host); + host->scsi.phase = PHASE_DISCONNECT; + break; + + case MESSAGE_REJECT: +#if 0 /* this isn't needed any more */ + /* + * If we were negociating sync transfer, we don't yet know if + * this REJECT is for the sync transfer or for the tagged queue/wide + * transfer. Re-initiate sync transfer negociation now, and if + * we got a REJECT in response to SDTR, then it'll be set to DONE. + */ + if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) + host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE; +#endif + + /* + * If we have any messages waiting to go out, then assert ATN now + */ + if (msgqueue_msglength(&host->scsi.msgs)) + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + + switch (host->scsi.last_message) { +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + case SIMPLE_QUEUE_TAG: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) + * If a target does not implement tagged queuing and a queue tag + * message is received, it shall respond with a MESSAGE REJECT + * message and accept the I/O process as if it were untagged. + */ + printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", + host->host->host_no, acornscsi_target(host)); + host->SCpnt->device->tagged_queue = 0; + set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns); + break; +#endif + case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): + /* + * Target can't handle synchronous transfers + */ + printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", + host->host->host_no, acornscsi_target(host)); + host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA; + host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS; + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); + break; + + default: + break; + } + break; + + case QUEUE_FULL: + /* TODO: target queue is full */ + break; + + case SIMPLE_QUEUE_TAG: + /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */ + printk("scsi%d.%c: reconnect queue tag %02X\n", + host->host->host_no, acornscsi_target(host), + message[1]); + break; + + case EXTENDED_MESSAGE: + switch (message[2]) { +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + case EXTENDED_SDTR: + if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) { + /* + * We requested synchronous transfers. This isn't quite right... + * We can only say if this succeeded if we proceed on to execute the + * command from this message. If we get a MESSAGE PARITY ERROR, + * and the target retries fail, then we fallback to asynchronous mode + */ + host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED; + printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", + host->host->host_no, acornscsi_target(host), + message[4], message[3] * 4); + host->device[host->SCpnt->device->id].sync_xfer = + calc_sync_xfer(message[3] * 4, message[4]); + } else { + unsigned char period, length; + /* + * Target requested synchronous transfers. The agreement is only + * to be in operation AFTER the target leaves message out phase. + */ + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + period = max_t(unsigned int, message[3], sdtr_period / 4); + length = min_t(unsigned int, message[4], sdtr_size); + msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, + EXTENDED_SDTR, period, length); + host->device[host->SCpnt->device->id].sync_xfer = + calc_sync_xfer(period * 4, length); + } + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); + break; +#else + /* We do not accept synchronous transfers. Respond with a + * MESSAGE_REJECT. + */ +#endif + + case EXTENDED_WDTR: + /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT + * to a wide data transfer request. + */ + default: + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); + break; + } + break; + +#ifdef CONFIG_SCSI_ACORNSCSI_LINK + case LINKED_CMD_COMPLETE: + case LINKED_FLG_CMD_COMPLETE: + /* + * We don't support linked commands yet + */ + if (0) { +#if (DEBUG & DEBUG_LINK) + printk("scsi%d.%c: lun %d tag %d linked command complete\n", + host->host->host_no, acornscsi_target(host), host->SCpnt->tag); +#endif + /* + * A linked command should only terminate with one of these messages + * if there are more linked commands available. + */ + if (!host->SCpnt->next_link) { + printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", + instance->host_no, acornscsi_target(host), host->SCpnt->tag); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); + } else { + Scsi_Cmnd *SCpnt = host->SCpnt; + + acornscsi_dma_cleanup(host); + + host->SCpnt = host->SCpnt->next_link; + host->SCpnt->tag = SCpnt->tag; + SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status; + SCpnt->done(SCpnt); + + /* initialise host->SCpnt->SCp */ + } + break; + } +#endif + + default: /* reject message */ + printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", + host->host->host_no, acornscsi_target(host), + message[0]); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); + host->scsi.phase = PHASE_MSGIN; + break; + } + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); +} + +/* + * Function: int acornscsi_buildmessages(AS_Host *host) + * Purpose : build the connection messages for a host + * Params : host - host to add messages to + */ +static +void acornscsi_buildmessages(AS_Host *host) +{ +#if 0 + /* does the device need resetting? */ + if (cmd_reset) { + msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET); + return; + } +#endif + + msgqueue_addmsg(&host->scsi.msgs, 1, + IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok, + host->SCpnt->device->lun)); + +#if 0 + /* does the device need the current command aborted */ + if (cmd_aborted) { + acornscsi_abortcmd(host->SCpnt->tag); + return; + } +#endif + +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + if (host->SCpnt->tag) { + unsigned int tag_type; + + if (host->SCpnt->cmnd[0] == REQUEST_SENSE || + host->SCpnt->cmnd[0] == TEST_UNIT_READY || + host->SCpnt->cmnd[0] == INQUIRY) + tag_type = HEAD_OF_QUEUE_TAG; + else + tag_type = SIMPLE_QUEUE_TAG; + msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); + } +#endif + +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) { + host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST; + msgqueue_addmsg(&host->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + sdtr_period / 4, sdtr_size); + } +#endif +} + +/* + * Function: int acornscsi_starttransfer(AS_Host *host) + * Purpose : transfer data to/from connected target + * Params : host - host to which target is connected + * Returns : 0 if failure + */ +static +int acornscsi_starttransfer(AS_Host *host) +{ + int residual; + + if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) { + printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", + host->host->host_no, acornscsi_target(host)); + return 0; + } + + residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred; + + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); + sbic_arm_writenext(host->scsi.io_port, residual >> 16); + sbic_arm_writenext(host->scsi.io_port, residual >> 8); + sbic_arm_writenext(host->scsi.io_port, residual); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + return 1; +} + +/* ========================================================================================= + * Connection & Disconnection + */ +/* + * Function : acornscsi_reconnect(AS_Host *host) + * Purpose : reconnect a previously disconnected command + * Params : host - host specific data + * Remarks : SCSI spec says: + * 'The set of active pointers is restored from the set + * of saved pointers upon reconnection of the I/O process' + */ +static +int acornscsi_reconnect(AS_Host *host) +{ + unsigned int target, lun, ok = 0; + + target = sbic_arm_read(host->scsi.io_port, SOURCEID); + + if (!(target & 8)) + printk(KERN_ERR "scsi%d: invalid source id after reselection " + "- device fault?\n", + host->host->host_no); + + target &= 7; + + if (host->SCpnt && !host->scsi.disconnectable) { + printk(KERN_ERR "scsi%d.%d: reconnected while command in " + "progress to target %d?\n", + host->host->host_no, target, host->SCpnt->device->id); + host->SCpnt = NULL; + } + + lun = sbic_arm_read(host->scsi.io_port, DATA) & 7; + + host->scsi.reconnected.target = target; + host->scsi.reconnected.lun = lun; + host->scsi.reconnected.tag = 0; + + if (host->scsi.disconnectable && host->SCpnt && + host->SCpnt->device->id == target && host->SCpnt->device->lun == lun) + ok = 1; + + if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) + ok = 1; + + ADD_STATUS(target, 0x81, host->scsi.phase, 0); + + if (ok) { + host->scsi.phase = PHASE_RECONNECTED; + } else { + /* this doesn't seem to work */ + printk(KERN_ERR "scsi%d.%c: reselected with no command " + "to reconnect with\n", + host->host->host_no, '0' + target); + acornscsi_dumplog(host, target); + acornscsi_abortcmd(host, 0); + if (host->SCpnt) { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); + host->SCpnt = NULL; + } + } + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); + return !ok; +} + +/* + * Function: int acornscsi_reconect_finish(AS_Host *host) + * Purpose : finish reconnecting a command + * Params : host - host to complete + * Returns : 0 if failed + */ +static +int acornscsi_reconnect_finish(AS_Host *host) +{ + if (host->scsi.disconnectable && host->SCpnt) { + host->scsi.disconnectable = 0; + if (host->SCpnt->device->id == host->scsi.reconnected.target && + host->SCpnt->device->lun == host->scsi.reconnected.lun && + host->SCpnt->tag == host->scsi.reconnected.tag) { +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: reconnected", + host->host->host_no, acornscsi_target(host))); +#endif + } else { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: had to move command " + "to disconnected queue\n", + host->host->host_no, acornscsi_target(host))); +#endif + host->SCpnt = NULL; + } + } + if (!host->SCpnt) { + host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, + host->scsi.reconnected.target, + host->scsi.reconnected.lun, + host->scsi.reconnected.tag); +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: had to get command", + host->host->host_no, acornscsi_target(host))); +#endif + } + + if (!host->SCpnt) + acornscsi_abortcmd(host, host->scsi.reconnected.tag); + else { + /* + * Restore data pointer from SAVED pointers. + */ + host->scsi.SCp = host->SCpnt->SCp; +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + printk(", data pointers: [%p, %X]", + host->scsi.SCp.ptr, host->scsi.SCp.this_residual); +#endif + } +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + printk("\n"); +#endif + + host->dma.transferred = host->scsi.SCp.scsi_xferred; + + return host->SCpnt != NULL; +} + +/* + * Function: void acornscsi_disconnect_unexpected(AS_Host *host) + * Purpose : handle an unexpected disconnect + * Params : host - host on which disconnect occurred + */ +static +void acornscsi_disconnect_unexpected(AS_Host *host) +{ + printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", + host->host->host_no, acornscsi_target(host)); +#if (DEBUG & DEBUG_ABORT) + acornscsi_dumplog(host, 8); +#endif + + acornscsi_done(host, &host->SCpnt, DID_ERROR); +} + +/* + * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) + * Purpose : abort a currently executing command + * Params : host - host with connected command to abort + * tag - tag to abort + */ +static +void acornscsi_abortcmd(AS_Host *host, unsigned char tag) +{ + host->scsi.phase = PHASE_ABORTED; + sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN); + + msgqueue_flush(&host->scsi.msgs); +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + if (tag) + msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); + else +#endif + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); +} + +/* ========================================================================================== + * Interrupt routines. + */ +/* + * Function: int acornscsi_sbicintr(AS_Host *host) + * Purpose : handle interrupts from SCSI device + * Params : host - host to process + * Returns : INTR_PROCESS if expecting another SBIC interrupt + * INTR_IDLE if no interrupt + * INTR_NEXT_COMMAND if we have finished processing the command + */ +static +intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq) +{ + unsigned int asr, ssr; + + asr = sbic_arm_read(host->scsi.io_port, ASR); + if (!(asr & ASR_INT)) + return INTR_IDLE; + + ssr = sbic_arm_read(host->scsi.io_port, SSR); + +#if (DEBUG & DEBUG_PHASES) + print_sbic_status(asr, ssr, host->scsi.phase); +#endif + + ADD_STATUS(8, ssr, host->scsi.phase, in_irq); + + if (host->SCpnt && !host->scsi.disconnectable) + ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); + + switch (ssr) { + case 0x00: /* reset state - not advanced */ + printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", + host->host->host_no); + /* setup sbic - WD33C93A */ + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); + return INTR_IDLE; + + case 0x01: /* reset state - advanced */ + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + msgqueue_flush(&host->scsi.msgs); + return INTR_IDLE; + + case 0x41: /* unexpected disconnect aborted command */ + acornscsi_disconnect_unexpected(host); + return INTR_NEXT_COMMAND; + } + + switch (host->scsi.phase) { + case PHASE_CONNECTING: /* STATE: command removed from issue queue */ + switch (ssr) { + case 0x11: /* -> PHASE_CONNECTED */ + /* BUS FREE -> SELECTION */ + host->scsi.phase = PHASE_CONNECTED; + msgqueue_flush(&host->scsi.msgs); + host->dma.transferred = host->scsi.SCp.scsi_xferred; + /* 33C93 gives next interrupt indicating bus phase */ + asr = sbic_arm_read(host->scsi.io_port, ASR); + if (!(asr & ASR_INT)) + break; + ssr = sbic_arm_read(host->scsi.io_port, SSR); + ADD_STATUS(8, ssr, host->scsi.phase, 1); + ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1); + goto connected; + + case 0x42: /* select timed out */ + /* -> PHASE_IDLE */ + acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); + return INTR_NEXT_COMMAND; + + case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ + /* BUS FREE -> RESELECTION */ + host->origSCpnt = host->SCpnt; + host->SCpnt = NULL; + msgqueue_flush(&host->scsi.msgs); + acornscsi_reconnect(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); + } + return INTR_PROCESSING; + + connected: + case PHASE_CONNECTED: /* STATE: device selected ok */ + switch (ssr) { +#ifdef NONSTANDARD + case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + /* SELECTION -> COMMAND */ + acornscsi_sendcommand(host); + break; + + case 0x8b: /* -> PHASE_STATUS */ + /* SELECTION -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; +#endif + + case 0x8e: /* -> PHASE_MSGOUT */ + /* SELECTION ->MESSAGE OUT */ + host->scsi.phase = PHASE_MSGOUT; + acornscsi_buildmessages(host); + acornscsi_sendmessage(host); + break; + + /* these should not happen */ + case 0x85: /* target disconnected */ + acornscsi_done(host, &host->SCpnt, DID_ERROR); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); + } + return INTR_PROCESSING; + + case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ + /* + * SCSI standard says that MESSAGE OUT phases can be followed by a + * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase + */ + switch (ssr) { + case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + /* MESSAGE OUT -> COMMAND */ + acornscsi_sendcommand(host); + break; + + case 0x8b: /* -> PHASE_STATUS */ + case 0x1b: /* -> PHASE_STATUS */ + /* MESSAGE OUT -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x8e: /* -> PHASE_MSGOUT */ + /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + /* MESSAGE OUT -> MESSAGE IN */ + acornscsi_message(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_COMMAND: /* STATE: connected & command sent */ + switch (ssr) { + case 0x18: /* -> PHASE_DATAOUT */ + /* COMMAND -> DATA OUT */ + if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAOUT; + return INTR_IDLE; + + case 0x19: /* -> PHASE_DATAIN */ + /* COMMAND -> DATA IN */ + if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAIN; + return INTR_IDLE; + + case 0x1b: /* -> PHASE_STATUS */ + /* COMMAND -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + /* COMMAND -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + /* COMMAND -> MESSAGE IN */ + acornscsi_message(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */ + if (ssr == 0x85) { /* -> PHASE_IDLE */ + host->scsi.disconnectable = 1; + host->scsi.reconnected.tag = 0; + host->scsi.phase = PHASE_IDLE; + host->stats.disconnects += 1; + } else { + printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_NEXT_COMMAND; + + case PHASE_IDLE: /* STATE: disconnected */ + if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ + acornscsi_reconnect(host); + else { + printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */ + /* + * Command reconnected - if MESGIN, get message - it may be + * the tag. If not, get command out of disconnected queue + */ + /* + * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, + * reconnect I_T_L command + */ + if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) + return INTR_IDLE; + ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); + switch (ssr) { + case 0x88: /* data out phase */ + /* -> PHASE_DATAOUT */ + /* MESSAGE IN -> DATA OUT */ + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAOUT; + return INTR_IDLE; + + case 0x89: /* data in phase */ + /* -> PHASE_DATAIN */ + /* MESSAGE IN -> DATA IN */ + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAIN; + return INTR_IDLE; + + case 0x8a: /* command out */ + /* MESSAGE IN -> COMMAND */ + acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + break; + + case 0x8b: /* status in */ + /* -> PHASE_STATUSIN */ + /* MESSAGE IN -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x8e: /* message out */ + /* -> PHASE_MSGOUT */ + /* MESSAGE IN -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x8f: /* message in */ + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DATAIN: /* STATE: transferred data in */ + /* + * This is simple - if we disconnect then the DMA address & count is + * correct. + */ + switch (ssr) { + case 0x19: /* -> PHASE_DATAIN */ + case 0x89: /* -> PHASE_DATAIN */ + acornscsi_abortcmd(host, host->SCpnt->tag); + return INTR_IDLE; + + case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ + /* DATA IN -> STATUS */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* DATA IN -> MESSAGE OUT */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_sendmessage(host); + break; + + case 0x1f: /* message in */ + case 0x4f: /* message in */ + case 0x8f: /* message in */ + /* DATA IN -> MESSAGE IN */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DATAOUT: /* STATE: transferred data out */ + /* + * This is more complicated - if we disconnect, the DMA could be 12 + * bytes ahead of us. We need to correct this. + */ + switch (ssr) { + case 0x18: /* -> PHASE_DATAOUT */ + case 0x88: /* -> PHASE_DATAOUT */ + acornscsi_abortcmd(host, host->SCpnt->tag); + return INTR_IDLE; + + case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ + /* DATA OUT -> STATUS */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* DATA OUT -> MESSAGE OUT */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_sendmessage(host); + break; + + case 0x1f: /* message in */ + case 0x4f: /* message in */ + case 0x8f: /* message in */ + /* DATA OUT -> MESSAGE IN */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_STATUSIN: /* STATE: status in complete */ + switch (ssr) { + case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + /* STATUS -> MESSAGE IN */ + acornscsi_message(host); + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* STATUS -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_MSGIN: /* STATE: message in */ + switch (ssr) { + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* MESSAGE IN -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + case 0x2f: + case 0x4f: + case 0x8f: + acornscsi_message(host); + break; + + case 0x85: + printk("scsi%d.%c: strange message in disconnection\n", + host->host->host_no, acornscsi_target(host)); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + acornscsi_done(host, &host->SCpnt, DID_ERROR); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DONE: /* STATE: received status & message */ + switch (ssr) { + case 0x85: /* -> PHASE_IDLE */ + acornscsi_done(host, &host->SCpnt, DID_OK); + return INTR_NEXT_COMMAND; + + case 0x1e: + case 0x8e: + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_ABORTED: + switch (ssr) { + case 0x85: + if (host->SCpnt) + acornscsi_done(host, &host->SCpnt, DID_ABORT); + else { + clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun, + host->busyluns); + host->scsi.phase = PHASE_IDLE; + } + return INTR_NEXT_COMMAND; + + case 0x1e: + case 0x2e: + case 0x4e: + case 0x8e: + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + default: + printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; +} + +/* + * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) + * Purpose : handle interrupts from Acorn SCSI card + * Params : irq - interrupt number + * dev_id - device specific data (AS_Host structure) + * regs - processor registers when interrupt occurred + */ +static irqreturn_t +acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + AS_Host *host = (AS_Host *)dev_id; + intr_ret_t ret; + int iostatus; + int in_irq = 0; + + do { + ret = INTR_IDLE; + + iostatus = inb(host->card.io_intr); + + if (iostatus & 2) { + acornscsi_dma_intr(host); + iostatus = inb(host->card.io_intr); + } + + if (iostatus & 8) + ret = acornscsi_sbicintr(host, in_irq); + + /* + * If we have a transfer pending, start it. + * Only start it if the interface has already started transferring + * it's data + */ + if (host->dma.xfer_required) + acornscsi_dma_xfer(host); + + if (ret == INTR_NEXT_COMMAND) + ret = acornscsi_kick(host); + + in_irq = 1; + } while (ret != INTR_IDLE); + + return IRQ_HANDLED; +} + +/*============================================================================================= + * Interfaces between interrupt handler and rest of scsi code + */ + +/* + * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) + * Purpose : queues a SCSI command + * Params : cmd - SCSI command + * done - function called on completion, with pointer to command descriptor + * Returns : 0, or < 0 on error. + */ +int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; + + if (!done) { + /* there should be some way of rejecting errors like this without panicing... */ + panic("scsi%d: queuecommand called with NULL done function [cmd=%p]", + host->host->host_no, SCpnt); + return -EINVAL; + } + +#if (DEBUG & DEBUG_NO_WRITE) + if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) { + printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", + host->host->host_no, '0' + SCpnt->device->id); + SCpnt->result = DID_NO_CONNECT << 16; + done(SCpnt); + return 0; + } +#endif + + SCpnt->scsi_done = done; + SCpnt->host_scribble = NULL; + SCpnt->result = 0; + SCpnt->tag = 0; + SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]); + SCpnt->SCp.sent_command = 0; + SCpnt->SCp.scsi_xferred = 0; + + init_SCp(SCpnt); + + host->stats.queues += 1; + + { + unsigned long flags; + + if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) { + SCpnt->result = DID_ERROR << 16; + done(SCpnt); + return 0; + } + local_irq_save(flags); + if (host->scsi.phase == PHASE_IDLE) + acornscsi_kick(host); + local_irq_restore(flags); + } + return 0; +} + +/* + * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) + * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 + * Params : SCpntp1 - pointer to command to return + * SCpntp2 - pointer to command to check + * result - result to pass back to mid-level done function + * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. + */ +static inline +void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) +{ + Scsi_Cmnd *SCpnt = *SCpntp1; + + if (SCpnt) { + *SCpntp1 = NULL; + + SCpnt->result = result; + SCpnt->scsi_done(SCpnt); + } + + if (SCpnt == *SCpntp2) + *SCpntp2 = NULL; +} + +enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; + +/* + * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : our abort status + */ +static enum res_abort +acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) +{ + enum res_abort res = res_not_running; + + if (queue_remove_cmd(&host->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the devices nor the + * interface know about the command. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on issue queue "); +//#endif + res = res_success; + } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. Simply + * acknowledge the abort condition, and when the target + * reconnects, we will give it an ABORT message. The + * target should then disconnect, and we will clear + * the busylun bit. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on disconnected queue "); +//#endif + res = res_success; + } else if (host->SCpnt == SCpnt) { + unsigned long flags; + +//#if (DEBUG & DEBUG_ABORT) + printk("executing "); +//#endif + + local_irq_save(flags); + switch (host->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. We simply + * remove all traces of the command. When the target reconnects, + * we will give it an ABORT message since the command could not + * be found. When the target finally disconnects, we will clear + * the busylun bit. + */ + case PHASE_IDLE: + if (host->scsi.disconnectable) { + host->scsi.disconnectable = 0; + host->SCpnt = NULL; + res = res_success; + } + break; + + /* + * If the command has connected and done nothing further, + * simply force a disconnect. We also need to clear the + * busylun bit. + */ + case PHASE_CONNECTED: + sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT); + host->SCpnt = NULL; + res = res_success_clear; + break; + + default: + acornscsi_abortcmd(host, host->SCpnt->tag); + res = res_snooze; + } + local_irq_restore(flags); + } else if (host->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + host->origSCpnt = NULL; +//#if (DEBUG & DEBUG_ABORT) + printk("waiting for execution "); +//#endif + res = res_success_clear; + } else + printk("unknown "); + + return res; +} + +/* + * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : one of SCSI_ABORT_ macros + */ +int acornscsi_abort(Scsi_Cmnd *SCpnt) +{ + AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata; + int result; + + host->stats.aborts += 1; + +#if (DEBUG & DEBUG_ABORT) + { + int asr, ssr; + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); + + printk(KERN_WARNING "acornscsi_abort: "); + print_sbic_status(asr, ssr, host->scsi.phase); + acornscsi_dumplog(host, SCpnt->device->id); + } +#endif + + printk("scsi%d: ", host->host->host_no); + + switch (acornscsi_do_abort(host, SCpnt)) { + /* + * We managed to find the command and cleared it out. + * We do not expect the command to be executing on the + * target, but we have set the busylun bit. + */ + case res_success_clear: +//#if (DEBUG & DEBUG_ABORT) + printk("clear "); +//#endif + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: +//#if (DEBUG & DEBUG_ABORT) + printk("success\n"); +//#endif + SCpnt->result = DID_ABORT << 16; + SCpnt->scsi_done(SCpnt); + result = SCSI_ABORT_SUCCESS; + break; + + /* + * We did find the command, but unfortunately we couldn't + * unhook it from ourselves. Wait some more, and if it + * still doesn't complete, reset the interface. + */ + case res_snooze: +//#if (DEBUG & DEBUG_ABORT) + printk("snooze\n"); +//#endif + result = SCSI_ABORT_SNOOZE; + break; + + /* + * The command could not be found (either because it completed, + * or it got dropped. + */ + default: + case res_not_running: + acornscsi_dumplog(host, SCpnt->device->id); +#if (DEBUG & DEBUG_ABORT) + result = SCSI_ABORT_SNOOZE; +#else + result = SCSI_ABORT_NOT_RUNNING; +#endif +//#if (DEBUG & DEBUG_ABORT) + printk("not running\n"); +//#endif + break; + } + + return result; +} + +/* + * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) + * Purpose : reset a command on this host/reset this host + * Params : SCpnt - command causing reset + * result - what type of reset to perform + * Returns : one of SCSI_RESET_ macros + */ +int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +{ + AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; + Scsi_Cmnd *SCptr; + + host->stats.resets += 1; + +#if (DEBUG & DEBUG_RESET) + { + int asr, ssr; + + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); + + printk(KERN_WARNING "acornscsi_reset: "); + print_sbic_status(asr, ssr, host->scsi.phase); + acornscsi_dumplog(host, SCpnt->device->id); + } +#endif + + acornscsi_dma_stop(host); + + SCptr = host->SCpnt; + + /* + * do hard reset. This resets all devices on this host, and so we + * must set the reset status on all commands. + */ + acornscsi_resetcard(host); + + /* + * report reset on commands current connected/disconnected + */ + acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET); + + while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL) + acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET); + + if (SCpnt) { + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + } + + return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS; +} + +/*============================================================================================== + * initialisation & miscellaneous support + */ + +/* + * Function: char *acornscsi_info(struct Scsi_Host *host) + * Purpose : return a string describing this interface + * Params : host - host to give information on + * Returns : a constant string + */ +const +char *acornscsi_info(struct Scsi_Host *host) +{ + static char string[100], *p; + + p = string; + + p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d" +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + " SYNC" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + " TAG" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_LINK + " LINK" +#endif +#if (DEBUG & DEBUG_NO_WRITE) + " NOWRITE ("NO_WRITE_STR")" +#endif + , host->hostt->name, host->io_port, host->irq, + VER_MAJOR, VER_MINOR, VER_PATCH); + return string; +} + +int acornscsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + int pos, begin = 0, devidx; + struct Scsi_Host *instance; + Scsi_Device *scd; + AS_Host *host; + char *p = buffer; + + instance = scsi_host_hn_get(host_no); + + if (inout == 1 || !instance) + return -EINVAL; + + host = (AS_Host *)instance->hostdata; + + p += sprintf(p, "AcornSCSI driver v%d.%d.%d" +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + " SYNC" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + " TAG" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_LINK + " LINK" +#endif +#if (DEBUG & DEBUG_NO_WRITE) + " NOWRITE ("NO_WRITE_STR")" +#endif + "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH); + + p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", + host->scsi.io_port, host->scsi.irq); +#ifdef USE_DMAC + p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", + host->dma.io_port, host->scsi.irq); +#endif + + p += sprintf(p, "Statistics:\n" + "Queued commands: %-10u Issued commands: %-10u\n" + "Done commands : %-10u Reads : %-10u\n" + "Writes : %-10u Others : %-10u\n" + "Disconnects : %-10u Aborts : %-10u\n" + "Resets : %-10u\n\nLast phases:", + host->stats.queues, host->stats.removes, + host->stats.fins, host->stats.reads, + host->stats.writes, host->stats.miscs, + host->stats.disconnects, host->stats.aborts, + host->stats.resets); + + for (devidx = 0; devidx < 9; devidx ++) { + unsigned int statptr, prev; + + p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); + statptr = host->status_ptr[devidx] - 10; + + if ((signed int)statptr < 0) + statptr += STATUS_BUFFER_SIZE; + + prev = host->status[devidx][statptr].when; + + for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + if (host->status[devidx][statptr].when) { + p += sprintf(p, "%c%02X:%02X+%2ld", + host->status[devidx][statptr].irq ? '-' : ' ', + host->status[devidx][statptr].ph, + host->status[devidx][statptr].ssr, + (host->status[devidx][statptr].when - prev) < 100 ? + (host->status[devidx][statptr].when - prev) : 99); + prev = host->status[devidx][statptr].when; + } + } + } + + p += sprintf(p, "\nAttached devices:\n"); + + list_for_each_entry(scd, &instance->my_devices, siblings) { + p += sprintf(p, "Device/Lun TaggedQ Sync\n"); + p += sprintf(p, " %d/%d ", scd->id, scd->lun); + if (scd->tagged_supported) + p += sprintf(p, "%3sabled(%3d) ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + else + p += sprintf(p, "unsupported "); + + if (host->device[scd->id].sync_xfer & 15) + p += sprintf(p, "offset %d, %d ns\n", + host->device[scd->id].sync_xfer & 15, + acornscsi_getperiod(host->device[scd->id].sync_xfer)); + else + p += sprintf(p, "async\n"); + + pos = p - buffer; + if (pos + begin < offset) { + begin += pos; + p = buffer; + } + pos = p - buffer; + if (pos + begin > offset + length) + break; + } + + pos = p - buffer; + + *start = buffer + (offset - begin); + pos -= offset - begin; + + if (pos > length) + pos = length; + + return pos; +} + +static Scsi_Host_Template acornscsi_template = { + .module = THIS_MODULE, + .proc_info = acornscsi_proc_info, + .name = "AcornSCSI", + .info = acornscsi_info, + .queuecommand = acornscsi_queuecmd, +#warning fixme + .abort = acornscsi_abort, + .reset = acornscsi_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .unchecked_isa_dma = 0, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "acornscsi", +}; + +static int __devinit +acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + AS_Host *ashost; + int ret = -ENOMEM; + + host = scsi_register(&acornscsi_template, sizeof(AS_Host)); + if (!host) + goto out; + + ashost = (AS_Host *)host->hostdata; + + host->io_port = ecard_address(ec, ECARD_MEMC, 0); + host->irq = ec->irq; + + ashost->host = host; + ashost->scsi.io_port = ioaddr(host->io_port + 0x800); + ashost->scsi.irq = host->irq; + ashost->card.io_intr = POD_SPACE(host->io_port) + 0x800; + ashost->card.io_page = POD_SPACE(host->io_port) + 0xc00; + ashost->card.io_ram = ioaddr(host->io_port); + ashost->dma.io_port = host->io_port + 0xc00; + ashost->dma.io_intr_clear = POD_SPACE(host->io_port) + 0x800; + + ec->irqaddr = (char *)ioaddr(ashost->card.io_intr); + ec->irqmask = 0x0a; + + ret = -EBUSY; + if (!request_region(host->io_port + 0x800, 2, "acornscsi(sbic)")) + goto err_1; + if (!request_region(ashost->card.io_intr, 1, "acornscsi(intr)")) + goto err_2; + if (!request_region(ashost->card.io_page, 1, "acornscsi(page)")) + goto err_3; +#ifdef USE_DMAC + if (!request_region(ashost->dma.io_port, 256, "acornscsi(dmac)")) + goto err_4; +#endif + if (!request_region(host->io_port, 2048, "acornscsi(ram)")) + goto err_5; + + ret = request_irq(host->irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", ashost); + if (ret) { + printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n", + host->host_no, ashost->scsi.irq, ret); + goto err_6; + } + + memset(&ashost->stats, 0, sizeof (ashost->stats)); + queue_initialise(&ashost->queues.issue); + queue_initialise(&ashost->queues.disconnected); + msgqueue_initialise(&ashost->scsi.msgs); + + acornscsi_resetcard(ashost); + + ret = scsi_add_host(host, &ec->dev); + if (ret == 0) + goto out; + + free_irq(host->irq, ashost); + err_6: + release_region(host->io_port, 2048); + err_5: +#ifdef USE_DMAC + release_region(ashost->dma.io_port, 256); +#endif + err_4: + release_region(ashost->card.io_page, 1); + err_3: + release_region(ashost->card.io_intr, 1); + err_2: + release_region(host->io_port + 0x800, 2); + err_1: + scsi_unregister(host); + out: + return ret; +} + +static void __devexit acornscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + AS_Host *ashost = (AS_Host *)host->hostdata; + + ecard_set_drvdata(ec, NULL); + scsi_remove_host(host); + + /* + * Put card into RESET state + */ + outb(0x80, ashost->card.io_page); + + free_irq(host->irq, ashost); + + release_region(host->io_port + 0x800, 2); + release_region(ashost->card.io_intr, 1); + release_region(ashost->card.io_page, 1); + release_region(ashost->dma.io_port, 256); + release_region(host->io_port, 2048); + + msgqueue_free(&ashost->scsi.msgs); + queue_free(&ashost->queues.disconnected); + queue_free(&ashost->queues.issue); +} + +static const struct ecard_id acornscsi_cids[] = { + { MANU_ACORN, PROD_ACORN_SCSI }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver acornscsi_driver = { + .probe = acornscsi_probe, + .remove = __devexit_p(acornscsi_remove), + .id_table = acornscsi_cids, + .drv = { + .name = "acornscsi", + }, +}; + +static int __init acornscsi_init(void) +{ + return ecard_register_driver(&acornscsi_driver); +} + +static void __exit acornscsi_exit(void) +{ + ecard_remove_driver(&acornscsi_driver); +} + +module_init(acornscsi_init); +module_exit(acornscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("AcornSCSI driver"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/scsi/arm/acornscsi.h linux-2.5.69-bk14/drivers/scsi/arm/acornscsi.h --- linux-2.5.69-bk13/drivers/scsi/arm/acornscsi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/acornscsi.h 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,358 @@ +/* + * linux/drivers/acorn/scsi/acornscsi.h + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Acorn SCSI driver + */ +#ifndef ACORNSCSI_H +#define ACORNSCSI_H + +/* SBIC registers */ +#define OWNID 0 +#define OWNID_FS1 (1<<7) +#define OWNID_FS2 (1<<6) +#define OWNID_EHP (1<<4) +#define OWNID_EAF (1<<3) + +#define CTRL 1 +#define CTRL_DMAMODE (1<<7) +#define CTRL_DMADBAMODE (1<<6) +#define CTRL_DMABURST (1<<5) +#define CTRL_DMAPOLLED 0 +#define CTRL_HHP (1<<4) +#define CTRL_EDI (1<<3) +#define CTRL_IDI (1<<2) +#define CTRL_HA (1<<1) +#define CTRL_HSP (1<<0) + +#define TIMEOUT 2 +#define TOTSECTS 3 +#define TOTHEADS 4 +#define TOTCYLH 5 +#define TOTCYLL 6 +#define LOGADDRH 7 +#define LOGADDRM2 8 +#define LOGADDRM1 9 +#define LOGADDRL 10 +#define SECTORNUM 11 +#define HEADNUM 12 +#define CYLH 13 +#define CYLL 14 +#define TARGETLUN 15 +#define TARGETLUN_TLV (1<<7) +#define TARGETLUN_DOK (1<<6) + +#define CMNDPHASE 16 +#define SYNCHTRANSFER 17 +#define SYNCHTRANSFER_OF0 0x00 +#define SYNCHTRANSFER_OF1 0x01 +#define SYNCHTRANSFER_OF2 0x02 +#define SYNCHTRANSFER_OF3 0x03 +#define SYNCHTRANSFER_OF4 0x04 +#define SYNCHTRANSFER_OF5 0x05 +#define SYNCHTRANSFER_OF6 0x06 +#define SYNCHTRANSFER_OF7 0x07 +#define SYNCHTRANSFER_OF8 0x08 +#define SYNCHTRANSFER_OF9 0x09 +#define SYNCHTRANSFER_OF10 0x0A +#define SYNCHTRANSFER_OF11 0x0B +#define SYNCHTRANSFER_OF12 0x0C +#define SYNCHTRANSFER_8DBA 0x00 +#define SYNCHTRANSFER_2DBA 0x20 +#define SYNCHTRANSFER_3DBA 0x30 +#define SYNCHTRANSFER_4DBA 0x40 +#define SYNCHTRANSFER_5DBA 0x50 +#define SYNCHTRANSFER_6DBA 0x60 +#define SYNCHTRANSFER_7DBA 0x70 + +#define TRANSCNTH 18 +#define TRANSCNTM 19 +#define TRANSCNTL 20 +#define DESTID 21 +#define DESTID_SCC (1<<7) +#define DESTID_DPD (1<<6) + +#define SOURCEID 22 +#define SOURCEID_ER (1<<7) +#define SOURCEID_ES (1<<6) +#define SOURCEID_DSP (1<<5) +#define SOURCEID_SIV (1<<4) + +#define SSR 23 +#define CMND 24 +#define CMND_RESET 0x00 +#define CMND_ABORT 0x01 +#define CMND_ASSERTATN 0x02 +#define CMND_NEGATEACK 0x03 +#define CMND_DISCONNECT 0x04 +#define CMND_RESELECT 0x05 +#define CMND_SELWITHATN 0x06 +#define CMND_SELECT 0x07 +#define CMND_SELECTATNTRANSFER 0x08 +#define CMND_SELECTTRANSFER 0x09 +#define CMND_RESELECTRXDATA 0x0A +#define CMND_RESELECTTXDATA 0x0B +#define CMND_WAITFORSELRECV 0x0C +#define CMND_SENDSTATCMD 0x0D +#define CMND_SENDDISCONNECT 0x0E +#define CMND_SETIDI 0x0F +#define CMND_RECEIVECMD 0x10 +#define CMND_RECEIVEDTA 0x11 +#define CMND_RECEIVEMSG 0x12 +#define CMND_RECEIVEUSP 0x13 +#define CMND_SENDCMD 0x14 +#define CMND_SENDDATA 0x15 +#define CMND_SENDMSG 0x16 +#define CMND_SENDUSP 0x17 +#define CMND_TRANSLATEADDR 0x18 +#define CMND_XFERINFO 0x20 +#define CMND_SBT (1<<7) + +#define DATA 25 +#define ASR 26 +#define ASR_INT (1<<7) +#define ASR_LCI (1<<6) +#define ASR_BSY (1<<5) +#define ASR_CIP (1<<4) +#define ASR_PE (1<<1) +#define ASR_DBR (1<<0) + +/* DMAC registers */ +#define INIT 0x00 +#define INIT_8BIT (1) + +#define CHANNEL 0x80 +#define CHANNEL_0 0x00 +#define CHANNEL_1 0x01 +#define CHANNEL_2 0x02 +#define CHANNEL_3 0x03 + +#define TXCNTLO 0x01 +#define TXCNTHI 0x81 +#define TXADRLO 0x02 +#define TXADRMD 0x82 +#define TXADRHI 0x03 + +#define DEVCON0 0x04 +#define DEVCON0_AKL (1<<7) +#define DEVCON0_RQL (1<<6) +#define DEVCON0_EXW (1<<5) +#define DEVCON0_ROT (1<<4) +#define DEVCON0_CMP (1<<3) +#define DEVCON0_DDMA (1<<2) +#define DEVCON0_AHLD (1<<1) +#define DEVCON0_MTM (1<<0) + +#define DEVCON1 0x84 +#define DEVCON1_WEV (1<<1) +#define DEVCON1_BHLD (1<<0) + +#define MODECON 0x05 +#define MODECON_WOED 0x01 +#define MODECON_VERIFY 0x00 +#define MODECON_READ 0x04 +#define MODECON_WRITE 0x08 +#define MODECON_AUTOINIT 0x10 +#define MODECON_ADDRDIR 0x20 +#define MODECON_DEMAND 0x00 +#define MODECON_SINGLE 0x40 +#define MODECON_BLOCK 0x80 +#define MODECON_CASCADE 0xC0 + +#define STATUS 0x85 +#define STATUS_TC0 (1<<0) +#define STATUS_RQ0 (1<<4) + +#define TEMPLO 0x06 +#define TEMPHI 0x86 +#define REQREG 0x07 +#define MASKREG 0x87 +#define MASKREG_M0 0x01 +#define MASKREG_M1 0x02 +#define MASKREG_M2 0x04 +#define MASKREG_M3 0x08 + +/* miscellaneous internal variables */ + +#define POD_SPACE(x) ((x) + 0xd0000) +#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0) +#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1) + +/* + * SCSI driver phases + */ +typedef enum { + PHASE_IDLE, /* we're not planning on doing anything */ + PHASE_CONNECTING, /* connecting to a target */ + PHASE_CONNECTED, /* connected to a target */ + PHASE_MSGOUT, /* message out to device */ + PHASE_RECONNECTED, /* reconnected */ + PHASE_COMMANDPAUSED, /* command partly sent */ + PHASE_COMMAND, /* command all sent */ + PHASE_DATAOUT, /* data out to device */ + PHASE_DATAIN, /* data in from device */ + PHASE_STATUSIN, /* status in from device */ + PHASE_MSGIN, /* message in from device */ + PHASE_DONE, /* finished */ + PHASE_ABORTED, /* aborted */ + PHASE_DISCONNECT, /* disconnecting */ +} phase_t; + +/* + * After interrupt, what to do now + */ +typedef enum { + INTR_IDLE, /* not expecting another IRQ */ + INTR_NEXT_COMMAND, /* start next command */ + INTR_PROCESSING, /* interrupt routine still processing */ +} intr_ret_t; + +/* + * DMA direction + */ +typedef enum { + DMA_OUT, /* DMA from memory to chip */ + DMA_IN /* DMA from chip to memory */ +} dmadir_t; + +/* + * Synchronous transfer state + */ +typedef enum { /* Synchronous transfer state */ + SYNC_ASYNCHRONOUS, /* don't negociate synchronous transfers*/ + SYNC_NEGOCIATE, /* start negociation */ + SYNC_SENT_REQUEST, /* sent SDTR message */ + SYNC_COMPLETED, /* received SDTR reply */ +} syncxfer_t; + +/* + * Command type + */ +typedef enum { /* command type */ + CMD_READ, /* READ_6, READ_10, READ_12 */ + CMD_WRITE, /* WRITE_6, WRITE_10, WRITE_12 */ + CMD_MISC, /* Others */ +} cmdtype_t; + +/* + * Data phase direction + */ +typedef enum { /* Data direction */ + DATADIR_IN, /* Data in phase expected */ + DATADIR_OUT /* Data out phase expected */ +} datadir_t; + +#include "queue.h" +#include "msgqueue.h" + +#define STATUS_BUFFER_SIZE 32 +/* + * This is used to dump the previous states of the SBIC + */ +struct status_entry { + unsigned long when; + unsigned char ssr; + unsigned char ph; + unsigned char irq; + unsigned char unused; +}; + +#define ADD_STATUS(_q,_ssr,_ph,_irq) \ +({ \ + host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \ + host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \ + host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \ + host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \ + host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \ +}) + +/* + * AcornSCSI host specific data + */ +typedef struct acornscsi_hostdata { + /* miscellaneous */ + struct Scsi_Host *host; /* host */ + Scsi_Cmnd *SCpnt; /* currently processing command */ + Scsi_Cmnd *origSCpnt; /* original connecting command */ + + /* driver information */ + struct { + unsigned int io_port; /* base address of WD33C93 */ + unsigned int irq; /* interrupt */ + phase_t phase; /* current phase */ + + struct { + unsigned char target; /* reconnected target */ + unsigned char lun; /* reconnected lun */ + unsigned char tag; /* reconnected tag */ + } reconnected; + + Scsi_Pointer SCp; /* current commands data pointer */ + + MsgQueue_t msgs; + + unsigned short last_message; /* last message to be sent */ + unsigned char disconnectable:1; /* this command can be disconnected */ + } scsi; + + /* statistics information */ + struct { + unsigned int queues; + unsigned int removes; + unsigned int fins; + unsigned int reads; + unsigned int writes; + unsigned int miscs; + unsigned int disconnects; + unsigned int aborts; + unsigned int resets; + } stats; + + /* queue handling */ + struct { + Queue_t issue; /* issue queue */ + Queue_t disconnected; /* disconnected command queue */ + } queues; + + /* per-device info */ + struct { + unsigned char sync_xfer; /* synchronous transfer (SBIC value) */ + syncxfer_t sync_state; /* sync xfer negociation state */ + unsigned char disconnect_ok:1; /* device can disconnect */ + } device[8]; + unsigned long busyluns[64 / sizeof(unsigned long)];/* array of bits indicating LUNs busy */ + + /* DMA info */ + struct { + unsigned int io_port; /* base address of DMA controller */ + unsigned int io_intr_clear; /* address of DMA interrupt clear */ + unsigned int free_addr; /* next free address */ + unsigned int start_addr; /* start address of current transfer */ + dmadir_t direction; /* dma direction */ + unsigned int transferred; /* number of bytes transferred */ + unsigned int xfer_start; /* scheduled DMA transfer start */ + unsigned int xfer_length; /* scheduled DMA transfer length */ + char *xfer_ptr; /* pointer to area */ + unsigned char xfer_required:1; /* set if we need to transfer something */ + unsigned char xfer_setup:1; /* set if DMA is setup */ + unsigned char xfer_done:1; /* set if DMA reached end of BH list */ + } dma; + + /* card info */ + struct { + unsigned int io_intr; /* base address of interrupt id reg */ + unsigned int io_page; /* base address of page reg */ + unsigned int io_ram; /* base address of RAM access */ + unsigned char page_reg; /* current setting of page reg */ + } card; + + unsigned char status_ptr[9]; + struct status_entry status[9][STATUS_BUFFER_SIZE]; +} AS_Host; + +#endif /* ACORNSCSI_H */ diff -urN linux-2.5.69-bk13/drivers/scsi/arm/arxescsi.c linux-2.5.69-bk14/drivers/scsi/arm/arxescsi.c --- linux-2.5.69-bk13/drivers/scsi/arm/arxescsi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/arxescsi.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,408 @@ +/* + * linux/arch/arm/drivers/scsi/arxescsi.c + * + * Copyright (C) 1997-2000 Russell King, Stefan Hanske + * + * This driver is based on experimentation. Hence, it may have made + * assumptions about the particular card that I have available, and + * may not be reliable! + * + * Changelog: + * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 + * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. + * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card + * enabled writing + * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing + * (arxescsi_pseudo_dma_write) + * 02-04-2000 RMK 0.1.1 Updated for new error handling code. + * 22-10-2000 SH Updated for new registering scheme. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" + +struct arxescsi_info { + FAS216_Info info; + struct expansion_card *ec; +}; + +#define DMADATA_OFFSET (0x200) + +#define DMASTAT_OFFSET (0x600) +#define DMASTAT_DRQ (1 << 0) + +#define CSTATUS_IRQ (1 << 0) + +#define VERSION "1.10 (23/01/2003 2.5.57)" + +/* + * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : 0 if we should not set CMD_WITHDMA for transfer info command + */ +static fasdmatype_t +arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + /* + * We don't do real DMA + */ + return fasdma_pseudo; +} + +static void arxescsi_pseudo_dma_write(unsigned char *addr, unsigned char *base) +{ + __asm__ __volatile__( + " stmdb sp!, {r0-r12}\n" + " mov r3, %0\n" + " mov r1, %1\n" + " add r2, r1, #512\n" + " mov r4, #256\n" + ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n" + " mov r5, r6, lsl #16\n" + " mov r7, r8, lsl #16\n" + ".loop_2: ldrb r0, [r1, #1536]\n" + " tst r0, #1\n" + " beq .loop_2\n" + " stmia r2, {r5-r8}\n\t" + " mov r9, r10, lsl #16\n" + " mov r11, r12, lsl #16\n" + ".loop_3: ldrb r0, [r1, #1536]\n" + " tst r0, #1\n" + " beq .loop_3\n" + " stmia r2, {r9-r12}\n" + " subs r4, r4, #16\n" + " bne .loop_1\n" + " ldmia sp!, {r0-r12}\n" + : + : "r" (addr), "r" (base)); +} + +/* + * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) + * Purpose : handles pseudo DMA + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * transfer - minimum number of bytes we expect to transfer + */ +static void +arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, int transfer) +{ + struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; + unsigned int length, error = 0; + unsigned char *base = info->info.scsi.io_base; + unsigned char *addr; + + length = SCp->this_residual; + addr = SCp->ptr; + + if (direction == DMA_OUT) { + unsigned int word; + while (length > 256) { + if (readb(base + 0x80) & STAT_INT) { + error = 1; + break; + } + arxescsi_pseudo_dma_write(addr, base); + addr += 256; + length -= 256; + } + + if (!error) + while (length > 0) { + if (readb(base + 0x80) & STAT_INT) + break; + + if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) + continue; + + word = *addr | *(addr + 1) << 8; + + writew(word, base + DMADATA_OFFSET); + if (length > 1) { + addr += 2; + length -= 2; + } else { + addr += 1; + length -= 1; + } + } + } + else { + if (transfer && (transfer & 255)) { + while (length >= 256) { + if (readb(base + 0x80) & STAT_INT) { + error = 1; + break; + } + + if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) + continue; + + readsw(base + DMADATA_OFFSET, addr, 256 >> 1); + addr += 256; + length -= 256; + } + } + + if (!(error)) + while (length > 0) { + unsigned long word; + + if (readb(base + 0x80) & STAT_INT) + break; + + if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) + continue; + + word = readw(base + DMADATA_OFFSET); + *addr++ = word; + if (--length > 0) { + *addr++ = word >> 8; + length --; + } + } + } +} + +/* + * Function: int arxescsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + /* + * no DMA to stop + */ +} + +/* + * Function: const char *arxescsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +static const char *arxescsi_info(struct Scsi_Host *host) +{ + struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION); + + return string; +} + +/* + * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +static int +arxescsi_proc_info(char *buffer, char **start, off_t offset, int length, + int host_no, int inout) +{ + struct Scsi_Host *host; + struct arxescsi_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + info = (struct arxescsi_info *)host->hostdata; + if (inout == 1) + return -EINVAL; + + p += sprintf(p, "ARXE 16-bit SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + *start = buffer + offset; + pos = p - buffer - offset; + if (pos > length) + pos = length; + + return pos; +} + +static Scsi_Host_Template arxescsi_template = { + .proc_info = arxescsi_proc_info, + .name = "ARXE SCSI card", + .info = arxescsi_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + .can_queue = 0, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "arxescsi", +}; + +static int __devinit +arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct arxescsi_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + if (!request_mem_region(resbase, reslen, "arxescsi")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&arxescsi_template, sizeof(struct arxescsi_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = NO_IRQ; + host->dma_channel = NO_DMA; + + info = (struct arxescsi_info *)host->hostdata; + info->ec = ec; + + info->info.scsi.io_base = base + 0x2000; + info->info.scsi.irq = host->irq; + info->info.scsi.io_shift = 5; + info->info.ifcfg.clockrate = 24; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 0; + info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 0; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; + info->info.dma.setup = arxescsi_dma_setup; + info->info.dma.pseudo = arxescsi_dma_pseudo; + info->info.dma.stop = arxescsi_dma_stop; + + ec->irqaddr = base; + ec->irqmask = CSTATUS_IRQ; + + ret = fas216_init(host); + if (ret) + goto out_unregister; + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + fas216_release(host); + out_unregister: + scsi_unregister(host); + out_unmap: + iounmap(base); + out_region: + release_mem_region(resbase, reslen); + out: + return ret; +} + +static void __devexit arxescsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id arxescsi_cids[] = { + { MANU_ARXE, PROD_ARXE_SCSI }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver arxescsi_driver = { + .probe = arxescsi_probe, + .remove = __devexit_p(arxescsi_remove), + .id_table = arxescsi_cids, + .drv = { + .name = "arxescsi", + }, +}; + +static int __init init_arxe_scsi_driver(void) +{ + return ecard_register_driver(&arxescsi_driver); +} + +static void __exit exit_arxe_scsi_driver(void) +{ + ecard_remove_driver(&arxescsi_driver); +} + +module_init(init_arxe_scsi_driver); +module_exit(exit_arxe_scsi_driver); + +MODULE_AUTHOR("Stefan Hanske"); +MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines"); +MODULE_LICENSE("GPL"); + diff -urN linux-2.5.69-bk13/drivers/scsi/arm/cumana_1.c linux-2.5.69-bk14/drivers/scsi/arm/cumana_1.c --- linux-2.5.69-bk13/drivers/scsi/arm/cumana_1.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/cumana_1.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,355 @@ +/* + * Generic Generic NCR5380 driver + * + * Copyright 1995-2002, Russell King + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" + +#include + +#define AUTOSENSE +#define PSEUDO_DMA + +#define CUMANASCSI_PUBLIC_RELEASE 1 + +#define NCR5380_implementation_fields int port, ctrl +#define NCR5380_local_declare() struct Scsi_Host *_instance +#define NCR5380_setup(instance) _instance = instance +#define NCR5380_read(reg) cumanascsi_read(_instance, reg) +#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value) +#define NCR5380_intr cumanascsi_intr +#define NCR5380_queue_command cumanascsi_queue_command +#define NCR5380_proc_info cumanascsi_proc_info + +int NCR5380_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#define BOARD_NORMAL 0 +#define BOARD_NCR53C400 1 + +#include "../NCR5380.h" + +void cumanascsi_setup(char *str, int *ints) +{ +} + +const char *cumanascsi_info(struct Scsi_Host *spnt) +{ + return ""; +} + +#ifdef NOT_EFFICIENT +#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) +#define STAT(p) inb((p)+1) +#define IN(p) inb((p)) +#define OUT(v,p) outb((v), (p)) +#else +#define CTRL(p,v) (p[-2308] = (*ctrl = (v))) +#define STAT(p) (p[4]) +#define IN(p) (*(p)) +#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p))) +#define OUT(v,p) (*(p) = (v)) +#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v)) +#endif +#define L(v) (((v)<<16)|((v) & 0x0000ffff)) +#define H(v) (((v)>>16)|((v) & 0xffff0000)) + +static inline int +NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len) +{ + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + int oldctrl = *ctrl; + unsigned long *laddr; +#ifdef NOT_EFFICIENT + int iobase = instance->io_port; + int dma_io = iobase & ~(0x3C0000>>2); +#else + volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); + volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); +#endif + + if(!len) return 0; + + CTRL(iobase, 0x02); + laddr = (unsigned long *)addr; + while(len >= 32) + { + int status; + unsigned long v; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(!(status & 0x40)) + continue; + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + len -= 32; + if(len == 0) + break; + } + + addr = (unsigned char *)laddr; + CTRL(iobase, 0x12); + while(len > 0) + { + int status; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + OUT(*addr++, dma_io); + if(--len == 0) + break; + } + + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + OUT(*addr++, dma_io); + if(--len == 0) + break; + } + } +end: + CTRL(iobase, oldctrl|0x40); + return len; +} + +static inline int +NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len) +{ + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + int oldctrl = *ctrl; + unsigned long *laddr; +#ifdef NOT_EFFICIENT + int iobase = instance->io_port; + int dma_io = iobase & ~(0x3C0000>>2); +#else + volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); + volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); +#endif + + if(!len) return 0; + + CTRL(iobase, 0x00); + laddr = (unsigned long *)addr; + while(len >= 32) + { + int status; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(!(status & 0x40)) + continue; + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + len -= 32; + if(len == 0) + break; + } + + addr = (unsigned char *)laddr; + CTRL(iobase, 0x10); + while(len > 0) + { + int status; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + *addr++ = IN(dma_io); + if(--len == 0) + break; + } + + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + *addr++ = IN(dma_io); + if(--len == 0) + break; + } + } +end: + CTRL(iobase, oldctrl|0x40); + return len; +} + +#undef STAT +#undef CTRL +#undef IN +#undef OUT + +#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) + +static char cumanascsi_read(struct Scsi_Host *instance, int reg) +{ + unsigned int iobase = instance->io_port; + int i; + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + + CTRL(iobase, 0); + i = inb(iobase + 64 + reg); + CTRL(iobase, 0x40); + + return i; +} + +static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value) +{ + int iobase = instance->io_port; + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + + CTRL(iobase, 0); + outb(value, iobase + 64 + reg); + CTRL(iobase, 0x40); +} + +#undef CTRL + +#include "../NCR5380.c" + +static Scsi_Host_Template cumanascsi_template = { + .module = THIS_MODULE, + .name = "Cumana 16-bit SCSI", + .info = cumanascsi_info, + .queuecommand = cumanascsi_queue_command, + .eh_abort_handler = NCR5380_abort, + .eh_device_reset_handler= NCR5380_device_reset, + .eh_bus_reset_handler = NCR5380_bus_reset, + .eh_host_reset_handler = NCR5380_host_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .unchecked_isa_dma = 0, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "CumanaSCSI-1", +}; + +static int __devinit +cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + int ret = -ENOMEM; + + host = scsi_register(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); + if (!host) + goto out; + + host->io_port = ecard_address(ec, ECARD_IOC, ECARD_SLOW) + 0x800; + host->irq = ec->irq; + + NCR5380_init(host, 0); + + host->n_io_port = 255; + if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) { + ret = -EBUSY; + goto out_free; + } + + ((struct NCR5380_hostdata *)host->hostdata)->ctrl = 0; + outb(0x00, host->io_port - 577); + + ret = request_irq(host->irq, cumanascsi_intr, SA_INTERRUPT, + "CumanaSCSI-1", host); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_release; + } + + printk("scsi%d: at port 0x%08lx irq %d", + host->host_no, host->io_port, host->irq); + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", + host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE); + printk("\nscsi%d:", host->host_no); + NCR5380_print_options(host); + printk("\n"); + + ret = scsi_add_host(host, &ec->dev); + if (ret == 0) + goto out; + + free_irq(host->irq, host); + out_release: + release_region(host->io_port, host->n_io_port); + out_free: + scsi_unregister(host); + out: + return ret; +} + +static void __devexit cumanascsi1_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + scsi_remove_host(host); + free_irq(host->irq, host); + release_region(host->io_port, host->n_io_port); + scsi_unregister(host); +} + +static const struct ecard_id cumanascsi1_cids[] = { + { MANU_CUMANA, PROD_CUMANA_SCSI_1 }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver cumanascsi1_driver = { + .probe = cumanascsi1_probe, + .remove = __devexit_p(cumanascsi1_remove), + .id_table = cumanascsi1_cids, + .drv = { + .name = "cumanascsi1", + }, +}; + +static int __init cumanascsi_init(void) +{ + return ecard_register_driver(&cumanascsi1_driver); +} + +static void __exit cumanascsi_exit(void) +{ + ecard_remove_driver(&cumanascsi1_driver); +} + +module_init(cumanascsi_init); +module_exit(cumanascsi_exit); + +MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/scsi/arm/cumana_2.c linux-2.5.69-bk14/drivers/scsi/arm/cumana_2.c --- linux-2.5.69-bk13/drivers/scsi/arm/cumana_2.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/cumana_2.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,574 @@ +/* + * linux/drivers/acorn/scsi/cumana_2.c + * + * Copyright (C) 1997-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 30-08-1997 RMK 0.0.0 Created, READONLY version. + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. + * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. + * 02-05-1998 RMK 0.0.2 Updated & added DMA support. + * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. + * 02-04-2000 RMK 0.0.4 Updated for new error handling code. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +#include + +#define CUMANASCSI2_STATUS (0x0000) +#define STATUS_INT (1 << 0) +#define STATUS_DRQ (1 << 1) +#define STATUS_LATCHED (1 << 3) + +#define CUMANASCSI2_ALATCH (0x0014) +#define ALATCH_ENA_INT (3) +#define ALATCH_DIS_INT (2) +#define ALATCH_ENA_TERM (5) +#define ALATCH_DIS_TERM (4) +#define ALATCH_ENA_BIT32 (11) +#define ALATCH_DIS_BIT32 (10) +#define ALATCH_ENA_DMA (13) +#define ALATCH_DIS_DMA (12) +#define ALATCH_DMA_OUT (15) +#define ALATCH_DMA_IN (14) + +#define CUMANASCSI2_PSEUDODMA (0x0200) + +#define CUMANASCSI2_FAS216_OFFSET (0x0300) +#define CUMANASCSI2_FAS216_SHIFT 2 + +/* + * Version + */ +#define VERSION "1.00 (13/11/2002 2.5.47)" + +/* + * Use term=0,1,0,0,0 to turn terminators on/off + */ +static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +#define NR_SG 256 + +struct cumanascsi2_info { + FAS216_Info info; + struct expansion_card *ec; + + void *status; /* card status register */ + void *alatch; /* Control register */ + unsigned int terms; /* Terminator state */ + void *dmaarea; /* Pseudo DMA area */ + struct scatterlist sg[NR_SG]; /* Scatter DMA list */ +}; + +#define CSTATUS_IRQ (1 << 0) +#define CSTATUS_DRQ (1 << 1) + +/* Prototype: void cumanascsi_2_irqenable(ec, irqnr) + * Purpose : Enable interrupts on Cumana SCSI 2 card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr) +{ + writeb(ALATCH_ENA_INT, ec->irq_data); +} + +/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr) + * Purpose : Disable interrupts on Cumana SCSI 2 card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr) +{ + writeb(ALATCH_DIS_INT, ec->irq_data); +} + +static const expansioncard_ops_t cumanascsi_2_ops = { + .irqenable = cumanascsi_2_irqenable, + .irqdisable = cumanascsi_2_irqdisable, +}; + +/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off) + * Purpose : Turn the Cumana SCSI 2 terminators on or off + * Params : host - card to turn on/off + * : on_off - !0 to turn on, 0 to turn off + */ +static void +cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + + if (on_off) { + info->terms = 1; + writeb(ALATCH_ENA_TERM, info->alatch); + } else { + info->terms = 0; + writeb(ALATCH_DIS_TERM, info->alatch); + } +} + +/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs) + * Purpose : handle interrupts from Cumana SCSI 2 card + * Params : irq - interrupt number + * dev_id - user-defined (Scsi_Host structure) + * regs - processor registers at interrupt + */ +static irqreturn_t +cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cumanascsi2_info *info = dev_id; + + return fas216_intr(&info->info); +} + +/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : type of transfer to be performed + */ +static fasdmatype_t +cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + struct device *dev = scsi_get_device(host); + int dmach = host->dma_channel; + + writeb(ALATCH_DIS_DMA, info->alatch); + + if (dmach != NO_DMA && + (min_type == fasdma_real_all || SCp->this_residual >= 512)) { + int bufs, map_dir, dma_dir, alatch_dir; + + bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); + + if (direction == DMA_OUT) + map_dir = DMA_TO_DEVICE, + dma_dir = DMA_MODE_WRITE, + alatch_dir = ALATCH_DMA_OUT; + else + map_dir = DMA_FROM_DEVICE, + dma_dir = DMA_MODE_READ, + alatch_dir = ALATCH_DMA_IN; + + dma_map_sg(dev, info->sg, bufs + 1, map_dir); + + disable_dma(dmach); + set_dma_sg(dmach, info->sg, bufs + 1); + writeb(alatch_dir, info->alatch); + set_dma_mode(dmach, dma_dir); + enable_dma(dmach); + writeb(ALATCH_ENA_DMA, info->alatch); + writeb(ALATCH_DIS_BIT32, info->alatch); + return fasdma_real_all; + } + + /* + * If we're not doing DMA, + * we'll do pseudo DMA + */ + return fasdma_pio; +} + +/* + * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer) + * Purpose : handles pseudo DMA + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * transfer - minimum number of bytes we expect to transfer + */ +static void +cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, int transfer) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + unsigned int length; + unsigned char *addr; + + length = SCp->this_residual; + addr = SCp->ptr; + + if (direction == DMA_OUT) +#if 0 + while (length > 1) { + unsigned long word; + unsigned int status = readb(info->status); + + if (status & STATUS_INT) + goto end; + + if (!(status & STATUS_DRQ)) + continue; + + word = *addr | *(addr + 1) << 8; + writew(word, info->dmaarea); + addr += 2; + length -= 2; + } +#else + printk ("PSEUDO_OUT???\n"); +#endif + else { + if (transfer && (transfer & 255)) { + while (length >= 256) { + unsigned int status = readb(info->status); + + if (status & STATUS_INT) + return; + + if (!(status & STATUS_DRQ)) + continue; + + readsw(info->dmaarea, addr, 256 >> 1); + addr += 256; + length -= 256; + } + } + + while (length > 0) { + unsigned long word; + unsigned int status = readb(info->status); + + if (status & STATUS_INT) + return; + + if (!(status & STATUS_DRQ)) + continue; + + word = readw(info->dmaarea); + *addr++ = word; + if (--length > 0) { + *addr++ = word >> 8; + length --; + } + } + } +} + +/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void +cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + if (host->dma_channel != NO_DMA) { + writeb(ALATCH_DIS_DMA, info->alatch); + disable_dma(host->dma_channel); + } +} + +/* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *cumanascsi_2_info(struct Scsi_Host *host) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s terminators o%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION, info->terms ? "n" : "ff"); + + return string; +} + +/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) + * Purpose : Set a driver specific function + * Params : host - host to setup + * : buffer - buffer containing string describing operation + * : length - length of string + * Returns : -EINVAL, or 0 + */ +static int +cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) +{ + int ret = length; + + if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) { + buffer += 11; + length -= 11; + + if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { + if (buffer[5] == '1') + cumanascsi_2_terminator_ctl(host, 1); + else if (buffer[5] == '0') + cumanascsi_2_terminator_ctl(host, 0); + else + ret = -EINVAL; + } else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + struct Scsi_Host *host; + struct cumanascsi2_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + if (inout == 1) + return cumanascsi_2_set_proc_info(host, buffer, length); + + info = (struct cumanascsi2_info *)host->hostdata; + + p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", + info->terms ? "n" : "ff"); + + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + *start = buffer + offset; + pos = p - buffer - offset; + if (pos > length) + pos = length; + + return pos; +} + +static Scsi_Host_Template cumanascsi2_template = { + .module = THIS_MODULE, + .proc_info = cumanascsi_2_proc_info, + .name = "Cumana SCSI II", + .info = cumanascsi_2_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + .can_queue = 1, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "cumanascsi2", +}; + +static int __devinit +cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct cumanascsi2_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + if (!request_mem_region(resbase, reslen, "cumanascsi2")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&cumanascsi2_template, + sizeof(struct cumanascsi2_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = ec->irq; + host->dma_channel = ec->dma; + + ecard_set_drvdata(ec, host); + + info = (struct cumanascsi2_info *)host->hostdata; + info->ec = ec; + info->dmaarea = base + CUMANASCSI2_PSEUDODMA; + info->status = base + CUMANASCSI2_STATUS; + info->alatch = base + CUMANASCSI2_ALATCH; + + ec->irqaddr = info->status; + ec->irqmask = STATUS_INT; + ec->irq_data = base + CUMANASCSI2_ALATCH; + ec->ops = &cumanascsi_2_ops; + + cumanascsi_2_terminator_ctl(host, term[ec->slot_no]); + + info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET; + info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT; + info->info.scsi.irq = host->irq; + info->info.ifcfg.clockrate = 40; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 7; + info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; + info->info.dma.setup = cumanascsi_2_dma_setup; + info->info.dma.pseudo = cumanascsi_2_dma_pseudo; + info->info.dma.stop = cumanascsi_2_dma_stop; + + ret = fas216_init(host); + if (ret) + goto out_free; + + ret = request_irq(host->irq, cumanascsi_2_intr, + SA_INTERRUPT, "cumanascsi2", info); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_release; + } + + if (host->dma_channel != NO_DMA) { + if (request_dma(host->dma_channel, "cumanascsi2")) { + printk("scsi%d: DMA%d not free, using PIO\n", + host->host_no, host->dma_channel); + host->dma_channel = NO_DMA; + } else { + set_dma_speed(host->dma_channel, 180); + info->info.ifcfg.capabilities |= FASCAP_DMA; + } + } + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, host); + + out_release: + fas216_release(host); + + out_free: + scsi_unregister(host); + + out_unmap: + iounmap(base); + + out_region: + release_mem_region(resbase, reslen); + + out: + return ret; +} + +static void __devexit cumanascsi2_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, info); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id cumanascsi2_cids[] = { + { MANU_CUMANA, PROD_CUMANA_SCSI_2 }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver cumanascsi2_driver = { + .probe = cumanascsi2_probe, + .remove = __devexit_p(cumanascsi2_remove), + .id_table = cumanascsi2_cids, + .drv = { + .name = "cumanascsi2", + }, +}; + +static int __init cumanascsi2_init(void) +{ + return ecard_register_driver(&cumanascsi2_driver); +} + +static void __exit cumanascsi2_exit(void) +{ + ecard_remove_driver(&cumanascsi2_driver); +} + +module_init(cumanascsi2_init); +module_exit(cumanascsi2_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/scsi/arm/ecoscsi.c linux-2.5.69-bk14/drivers/scsi/arm/ecoscsi.c --- linux-2.5.69-bk13/drivers/scsi/arm/ecoscsi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/ecoscsi.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,283 @@ +#define AUTOSENSE +/* #define PSEUDO_DMA */ + +/* + * EcoSCSI Generic NCR5380 driver + * + * Copyright 1995, Russell King + * + * ALPHA RELEASE 1. + * + * For more information, please consult + * + * NCR 5380 Family + * SCSI Protocol Controller + * Databook + * + * NCR Microelectronics + * 1635 Aeroplaza Drive + * Colorado Springs, CO 80916 + * 1+ (719) 578-3400 + * 1+ (800) 334-5454 + */ + +/* + * Options : + * + * PARITY - enable parity checking. Not supported. + * + * SCSI2 - enable support for SCSI-II tagged queueing. Untested. + * + * USLEEP - enable support for devices that don't disconnect. Untested. + */ + +/* + * $Log: ecoscsi.c,v $ + * Revision 1.2 1998/03/08 05:49:47 davem + * Merge to 2.1.89 + * + * Revision 1.1 1998/02/23 02:45:24 davem + * Merge to 2.1.88 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../scsi.h" +#include "../hosts.h" + +#define NCR5380_implementation_fields int port, ctrl +#define NCR5380_local_declare() struct Scsi_Host *_instance +#define NCR5380_setup(instance) _instance = instance + +#define NCR5380_read(reg) ecoscsi_read(_instance, reg) +#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value) + +#define NCR5380_intr ecoscsi_intr +#define NCR5380_queue_command ecoscsi_queue_command +#define NCR5380_proc_info ecoscsi_proc_info + +#include "../NCR5380.h" + +#define ECOSCSI_PUBLIC_RELEASE 1 + +static char ecoscsi_read(struct Scsi_Host *instance, int reg) +{ + int iobase = instance->io_port; + outb(reg | 8, iobase); + return inb(iobase + 1); +} + +static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value) +{ + int iobase = instance->io_port; + outb(reg | 8, iobase); + outb(value, iobase + 1); +} + +/* + * Function : ecoscsi_setup(char *str, int *ints) + * + * Purpose : LILO command line initialization of the overrides array, + * + * Inputs : str - unused, ints - array of integer parameters with ints[0] + * equal to the number of ints. + * + */ + +void ecoscsi_setup(char *str, int *ints) { +} + +/* + * Function : int ecoscsi_detect(Scsi_Host_Template * tpnt) + * + * Purpose : initializes ecoscsi NCR5380 driver based on the + * command line / compile time port and irq definitions. + * + * Inputs : tpnt - template for this SCSI adapter. + * + * Returns : 1 if a host adapter was found, 0 if not. + * + */ + +int ecoscsi_detect(Scsi_Host_Template * tpnt) +{ + struct Scsi_Host *host; + + tpnt->proc_name = "ecoscsi"; + + host = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); + if (!host) + return 0; + + host->io_port = 0x80ce8000; + host->n_io_port = 144; + host->irq = IRQ_NONE; + + if ( !(request_region(host->io_port, host->n_io_port, "ecoscsi")) ) + goto unregister_scsi; + + ecoscsi_write (host, MODE_REG, 0x20); /* Is it really SCSI? */ + if (ecoscsi_read (host, MODE_REG) != 0x20) /* Write to a reg. */ + goto release_reg; + + ecoscsi_write( host, MODE_REG, 0x00 ); /* it back. */ + if (ecoscsi_read (host, MODE_REG) != 0x00) + goto release_reg; + + NCR5380_init(host, 0); + + printk("scsi%d: at port 0x%08lx irqs disabled", host->host_no, host->io_port); + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", + host->can_queue, host->cmd_per_lun, ECOSCSI_PUBLIC_RELEASE); + printk("\nscsi%d:", host->host_no); + NCR5380_print_options(host); + printk("\n"); + + return 1; + +release_reg: + release_region(host->io_port, host->n_io_port); +unregister_scsi: + scsi_unregister(host); + return 0; +} + +int ecoscsi_release (struct Scsi_Host *shpnt) +{ + if (shpnt->irq != IRQ_NONE) + free_irq (shpnt->irq, NULL); + if (shpnt->io_port) + release_region (shpnt->io_port, shpnt->n_io_port); + return 0; +} + +const char * ecoscsi_info (struct Scsi_Host *spnt) +{ + return ""; +} + +#if 0 +#define STAT(p) inw(p + 144) + +static inline int NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, + int len) +{ + int iobase = host->io_port; +printk("writing %p len %d\n",addr, len); + if(!len) return -1; + + while(1) + { + int status; + while(((status = STAT(iobase)) & 0x100)==0); + } +} + +static inline int NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, + int len) +{ + int iobase = host->io_port; + int iobase2= host->io_port + 0x100; + unsigned char *start = addr; + int s; +printk("reading %p len %d\n",addr, len); + outb(inb(iobase + 128), iobase + 135); + while(len > 0) + { + int status,b,i, timeout; + timeout = 0x07FFFFFF; + while(((status = STAT(iobase)) & 0x100)==0) + { + timeout--; + if(status & 0x200 || !timeout) + { + printk("status = %p\n",status); + outb(0, iobase + 135); + return 1; + } + } + if(len >= 128) + { + for(i=0; i<64; i++) + { + b = inw(iobase + 136); + *addr++ = b; + *addr++ = b>>8; + } + len -= 128; + } + else + { + b = inw(iobase + 136); + *addr ++ = b; + len -= 1; + if(len) + *addr ++ = b>>8; + len -= 1; + } + } + outb(0, iobase + 135); + printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]); + return 1; +} +#endif +#undef STAT + +int NCR5380_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#define BOARD_NORMAL 0 +#define BOARD_NCR53C400 1 + +#include "../NCR5380.c" + +static Scsi_Host_Template ecoscsi_template = { + .module = THIS_MODULE, + .name = "Serial Port EcoSCSI NCR5380", + .detect = ecoscsi_detect, + .release = ecoscsi_release, + .info = ecoscsi_info, + .queuecommand = ecoscsi_queue_command, + .eh_abort_handler = NCR5380_abort, + .eh_device_reset_handler= NCR5380_device_reset, + .eh_bus_reset_handler = NCR5380_bus_reset, + .eh_host_reset_handler = NCR5380_host_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .use_clustering = DISABLE_CLUSTERING +}; + +static int __init ecoscsi_init(void) +{ + scsi_register_host(&ecoscsi_template); + if (ecoscsi_template.present) + return 0; + + scsi_unregister_host(&ecoscsi_template); + return -ENODEV; +} + +static void __exit ecoscsi_exit(void) +{ + scsi_unregister_host(&ecoscsi_template); +} + +module_init(ecoscsi_init); +module_exit(ecoscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Econet-SCSI driver for Acorn machines"); +MODULE_LICENSE("GPL"); + diff -urN linux-2.5.69-bk13/drivers/scsi/arm/eesox.c linux-2.5.69-bk14/drivers/scsi/arm/eesox.c --- linux-2.5.69-bk13/drivers/scsi/arm/eesox.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/eesox.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,684 @@ +/* + * linux/drivers/acorn/scsi/eesox.c + * + * Copyright (C) 1997-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver is based on experimentation. Hence, it may have made + * assumptions about the particular card that I have available, and + * may not be reliable! + * + * Changelog: + * 01-10-1997 RMK Created, READONLY version + * 15-02-1998 RMK READ/WRITE version + * added DMA support and hardware definitions + * 14-03-1998 RMK Updated DMA support + * Added terminator control + * 15-04-1998 RMK Only do PIO if FAS216 will allow it. + * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new + * error handling code. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +#include + +#define EESOX_FAS216_OFFSET 0x3000 +#define EESOX_FAS216_SHIFT 5 + +#define EESOX_DMASTAT 0x2800 +#define EESOX_STAT_INTR 0x01 +#define EESOX_STAT_DMA 0x02 + +#define EESOX_CONTROL 0x2800 +#define EESOX_INTR_ENABLE 0x04 +#define EESOX_TERM_ENABLE 0x02 +#define EESOX_RESET 0x01 + +#define EESOX_DMADATA 0x3800 + +#define VERSION "1.10 (17/01/2003 2.5.59)" + +/* + * Use term=0,1,0,0,0 to turn terminators on/off + */ +static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +#define NR_SG 256 + +struct eesoxscsi_info { + FAS216_Info info; + struct expansion_card *ec; + + void *ctl_port; + unsigned int control; + struct scatterlist sg[NR_SG]; /* Scatter DMA list */ +}; + +/* Prototype: void eesoxscsi_irqenable(ec, irqnr) + * Purpose : Enable interrupts on EESOX SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +eesoxscsi_irqenable(struct expansion_card *ec, int irqnr) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; + + info->control |= EESOX_INTR_ENABLE; + + writeb(info->control, info->ctl_port); +} + +/* Prototype: void eesoxscsi_irqdisable(ec, irqnr) + * Purpose : Disable interrupts on EESOX SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; + + info->control &= ~EESOX_INTR_ENABLE; + + writeb(info->control, info->ctl_port); +} + +static const expansioncard_ops_t eesoxscsi_ops = { + .irqenable = eesoxscsi_irqenable, + .irqdisable = eesoxscsi_irqdisable, +}; + +/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off) + * Purpose : Turn the EESOX SCSI terminators on or off + * Params : host - card to turn on/off + * : on_off - !0 to turn on, 0 to turn off + */ +static void +eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + unsigned long flags; + + spin_lock_irqsave(host->host_lock, flags); + if (on_off) + info->control |= EESOX_TERM_ENABLE; + else + info->control &= ~EESOX_TERM_ENABLE; + + writeb(info->control, info->ctl_port); + spin_unlock_irqrestore(host->host_lock, flags); +} + +/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs) + * Purpose : handle interrupts from EESOX SCSI card + * Params : irq - interrupt number + * dev_id - user-defined (Scsi_Host structure) + * regs - processor registers at interrupt + */ +static irqreturn_t +eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct eesoxscsi_info *info = dev_id; + + return fas216_intr(&info->info); +} + +/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : type of transfer to be performed + */ +static fasdmatype_t +eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + struct device *dev = scsi_get_device(host); + int dmach = host->dma_channel; + + if (dmach != NO_DMA && + (min_type == fasdma_real_all || SCp->this_residual >= 512)) { + int bufs, map_dir, dma_dir; + + bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); + + if (direction == DMA_OUT) + map_dir = DMA_TO_DEVICE, + dma_dir = DMA_MODE_WRITE; + else + map_dir = DMA_FROM_DEVICE, + dma_dir = DMA_MODE_READ; + + dma_map_sg(dev, info->sg, bufs + 1, map_dir); + + disable_dma(dmach); + set_dma_sg(dmach, info->sg, bufs + 1); + set_dma_mode(dmach, dma_dir); + enable_dma(dmach); + return fasdma_real_all; + } + /* + * We don't do DMA, we only do slow PIO + * + * Some day, we will do Pseudo DMA + */ + return fasdma_pseudo; +} + +static void eesoxscsi_buffer_in(void *buf, int length, void *base) +{ + const void *reg_fas = base + EESOX_FAS216_OFFSET; + const void *reg_dmastat = base + EESOX_DMASTAT; + const void *reg_dmadata = base + EESOX_DMADATA; + const register unsigned long mask = 0xffff; + + do { + unsigned int status; + + /* + * Interrupt request? + */ + status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); + if (status & STAT_INT) + break; + + /* + * DMA request active? + */ + status = readb(reg_dmastat); + if (!(status & EESOX_STAT_DMA)) + continue; + + /* + * Get number of bytes in FIFO + */ + status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; + if (status > 16) + status = 16; + if (status > length) + status = length; + + /* + * Align buffer. + */ + if (((u32)buf) & 2 && status >= 2) { + *((u16 *)buf)++ = readl(reg_dmadata); + status -= 2; + length -= 2; + } + + if (status >= 8) { + unsigned long l1, l2; + + l1 = readl(reg_dmadata) & mask; + l1 |= readl(reg_dmadata) << 16; + l2 = readl(reg_dmadata) & mask; + l2 |= readl(reg_dmadata) << 16; + *((u32 *)buf)++ = l1; + *((u32 *)buf)++ = l2; + length -= 8; + continue; + } + + if (status >= 4) { + unsigned long l1; + + l1 = readl(reg_dmadata) & mask; + l1 |= readl(reg_dmadata) << 16; + + *((u32 *)buf)++ = l1; + length -= 4; + continue; + } + + if (status >= 2) { + *((u16 *)buf)++ = readl(reg_dmadata); + length -= 2; + } + } while (length); +} + +static void eesoxscsi_buffer_out(void *buf, int length, void *base) +{ + const void *reg_fas = base + EESOX_FAS216_OFFSET; + const void *reg_dmastat = base + EESOX_DMASTAT; + const void *reg_dmadata = base + EESOX_DMADATA; + + do { + unsigned int status; + + /* + * Interrupt request? + */ + status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); + if (status & STAT_INT) + break; + + /* + * DMA request active? + */ + status = readb(reg_dmastat); + if (!(status & EESOX_STAT_DMA)) + continue; + + /* + * Get number of bytes in FIFO + */ + status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; + if (status > 16) + status = 16; + status = 16 - status; + if (status > length) + status = length; + status &= ~1; + + /* + * Align buffer. + */ + if (((u32)buf) & 2 && status >= 2) { + writel(*((u16 *)buf)++ << 16, reg_dmadata); + status -= 2; + length -= 2; + } + + if (status >= 8) { + unsigned long l1, l2; + + l1 = *((u32 *)buf)++; + l2 = *((u32 *)buf)++; + + writel(l1 << 16, reg_dmadata); + writel(l1, reg_dmadata); + writel(l2 << 16, reg_dmadata); + writel(l2, reg_dmadata); + length -= 8; + continue; + } + + if (status >= 4) { + unsigned long l1; + + l1 = *((u32 *)buf)++; + + writel(l1 << 16, reg_dmadata); + writel(l1, reg_dmadata); + length -= 4; + continue; + } + + if (status >= 2) { + writel(*((u16 *)buf)++ << 16, reg_dmadata); + length -= 2; + } + } while (length); +} + +static void +eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t dir, int transfer_size) +{ + void *base = (void *)host->base; + if (dir == DMA_IN) { + eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, base); + } else { + eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, base); + } +} + +/* Prototype: int eesoxscsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void +eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + if (host->dma_channel != NO_DMA) + disable_dma(host->dma_channel); +} + +/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *eesoxscsi_info(struct Scsi_Host *host) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s terminators o%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff"); + + return string; +} + +/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) + * Purpose : Set a driver specific function + * Params : host - host to setup + * : buffer - buffer containing string describing operation + * : length - length of string + * Returns : -EINVAL, or 0 + */ +static int +eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) +{ + int ret = length; + + if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) { + buffer += 9; + length -= 9; + + if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { + if (buffer[5] == '1') + eesoxscsi_terminator_ctl(host, 1); + else if (buffer[5] == '0') + eesoxscsi_terminator_ctl(host, 0); + else + ret = -EINVAL; + } else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + struct Scsi_Host *host; + struct eesoxscsi_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + if (inout == 1) + return eesoxscsi_set_proc_info(host, buffer, length); + + info = (struct eesoxscsi_info *)host->hostdata; + + p += sprintf(p, "EESOX SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", + info->control & EESOX_TERM_ENABLE ? "n" : "ff"); + + pos += fas216_print_stats(&info->info, buffer + pos); + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + if (pos > length) + pos = length; + + return pos; +} + +static ssize_t eesoxscsi_show_term(struct device *dev, char *buf) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + + return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0); +} + +static ssize_t eesoxscsi_store_term(struct device *dev, const char *buf, size_t len) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + unsigned long flags; + + if (len > 1) { + spin_lock_irqsave(host->host_lock, flags); + if (buf[0] != '0') { + info->control |= EESOX_TERM_ENABLE; + } else { + info->control &= ~EESOX_TERM_ENABLE; + } + writeb(info->control, info->ctl_port); + spin_unlock_irqrestore(host->host_lock, flags); + } + + return len; +} + +static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, + eesoxscsi_show_term, eesoxscsi_store_term); + +static Scsi_Host_Template eesox_template = { + .module = THIS_MODULE, + .proc_info = eesoxscsi_proc_info, + .name = "EESOX SCSI", + .info = eesoxscsi_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + .can_queue = 1, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "eesox", +}; + +static int __devinit +eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct eesoxscsi_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + if (!request_mem_region(resbase, reslen, "eesoxscsi")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&eesox_template, + sizeof(struct eesoxscsi_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = ec->irq; + host->dma_channel = ec->dma; + + ecard_set_drvdata(ec, host); + + info = (struct eesoxscsi_info *)host->hostdata; + info->ec = ec; + info->ctl_port = base + EESOX_CONTROL; + info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0; + writeb(info->control, info->ctl_port); + + ec->irqaddr = base + EESOX_DMASTAT; + ec->irqmask = EESOX_STAT_INTR; + ec->irq_data = info; + ec->ops = &eesoxscsi_ops; + + info->info.scsi.io_base = base + EESOX_FAS216_OFFSET; + info->info.scsi.io_shift = EESOX_FAS216_SHIFT; + info->info.scsi.irq = host->irq; + info->info.ifcfg.clockrate = 40; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 7; + info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; + info->info.dma.setup = eesoxscsi_dma_setup; + info->info.dma.pseudo = eesoxscsi_dma_pseudo; + info->info.dma.stop = eesoxscsi_dma_stop; + + device_create_file(&ec->dev, &dev_attr_bus_term); + + ret = fas216_init(host); + if (ret) + goto out_free; + + ret = request_irq(host->irq, eesoxscsi_intr, 0, "eesoxscsi", info); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_remove; + } + + if (host->dma_channel != NO_DMA) { + if (request_dma(host->dma_channel, "eesox")) { + printk("scsi%d: DMA%d not free, DMA disabled\n", + host->host_no, host->dma_channel); + host->dma_channel = NO_DMA; + } else { + set_dma_speed(host->dma_channel, 180); + info->info.ifcfg.capabilities |= FASCAP_DMA; + info->info.ifcfg.cntl3 |= CNTL3_BS8; + } + } + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, host); + + out_remove: + fas216_remove(host); + + out_free: + device_remove_file(&ec->dev, &dev_attr_bus_term); + scsi_unregister(host); + + out_unmap: + iounmap(base); + + out_region: + release_mem_region(resbase, reslen); + + out: + return ret; +} + +static void __devexit eesoxscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, info); + + device_remove_file(&ec->dev, &dev_attr_bus_term); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id eesoxscsi_cids[] = { + { MANU_EESOX, PROD_EESOX_SCSI2 }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver eesoxscsi_driver = { + .probe = eesoxscsi_probe, + .remove = __devexit_p(eesoxscsi_remove), + .id_table = eesoxscsi_cids, + .drv = { + .name = "eesoxscsi", + }, +}; + +static int __init eesox_init(void) +{ + return ecard_register_driver(&eesoxscsi_driver); +} + +static void __exit eesox_exit(void) +{ + ecard_remove_driver(&eesoxscsi_driver); +} + +module_init(eesox_init); +module_exit(eesox_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); + diff -urN linux-2.5.69-bk13/drivers/scsi/arm/fas216.c linux-2.5.69-bk14/drivers/scsi/arm/fas216.c --- linux-2.5.69-bk13/drivers/scsi/arm/fas216.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/fas216.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,3045 @@ +/* + * linux/drivers/acorn/scsi/fas216.c + * + * Copyright (C) 1997-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and + * other sources, including: + * the AMD Am53CF94 data sheet + * the AMD Am53C94 data sheet + * + * This is a generic driver. To use it, have a look at cumana_2.c. You + * should define your own structure that overlays FAS216_Info, eg: + * struct my_host_data { + * FAS216_Info info; + * ... my host specific data ... + * }; + * + * Changelog: + * 30-08-1997 RMK Created + * 14-09-1997 RMK Started disconnect support + * 08-02-1998 RMK Corrected real DMA support + * 15-02-1998 RMK Started sync xfer support + * 06-04-1998 RMK Tightened conditions for printing incomplete + * transfers + * 02-05-1998 RMK Added extra checks in fas216_reset + * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns + * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT + * 02-04-2000 RMK Converted to use the new error handling, and + * automatically request sense data upon check + * condition status from targets. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +/* NOTE: SCSI2 Synchronous transfers *require* DMA according to + * the data sheet. This restriction is crazy, especially when + * you only want to send 16 bytes! What were the guys who + * designed this chip on at that time? Did they read the SCSI2 + * spec at all? The following sections are taken from the SCSI2 + * standard (s2r10) concerning this: + * + * > IMPLEMENTORS NOTES: + * > (1) Re-negotiation at every selection is not recommended, since a + * > significant performance impact is likely. + * + * > The implied synchronous agreement shall remain in effect until a BUS DEVICE + * > RESET message is received, until a hard reset condition occurs, or until one + * > of the two SCSI devices elects to modify the agreement. The default data + * > transfer mode is asynchronous data transfer mode. The default data transfer + * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard + * > reset condition. + * + * In total, this means that once you have elected to use synchronous + * transfers, you must always use DMA. + * + * I was thinking that this was a good chip until I found this restriction ;( + */ +#define SCSI2_SYNC +#undef SCSI2_TAG + +#undef DEBUG_CONNECT +#undef DEBUG_MESSAGES + +#undef CHECK_STRUCTURE + +#define LOG_CONNECT (1 << 0) +#define LOG_BUSSERVICE (1 << 1) +#define LOG_FUNCTIONDONE (1 << 2) +#define LOG_MESSAGES (1 << 3) +#define LOG_BUFFER (1 << 4) +#define LOG_ERROR (1 << 8) + +static int level_mask = LOG_ERROR; + +MODULE_PARM(level_mask, "i"); + +static int __init fas216_log_setup(char *str) +{ + char *s; + + level_mask = 0; + + while ((s = strsep(&str, ",")) != NULL) { + switch (s[0]) { + case 'a': + if (strcmp(s, "all") == 0) + level_mask |= -1; + break; + case 'b': + if (strncmp(s, "bus", 3) == 0) + level_mask |= LOG_BUSSERVICE; + if (strncmp(s, "buf", 3) == 0) + level_mask |= LOG_BUFFER; + break; + case 'c': + level_mask |= LOG_CONNECT; + break; + case 'e': + level_mask |= LOG_ERROR; + break; + case 'm': + level_mask |= LOG_MESSAGES; + break; + case 'n': + if (strcmp(s, "none") == 0) + level_mask = 0; + break; + case 's': + level_mask |= LOG_FUNCTIONDONE; + break; + } + } + return 1; +} + +__setup("fas216_logging=", fas216_log_setup); + +static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg) +{ + unsigned int off = reg << info->scsi.io_shift; + if (info->scsi.io_base) + return readb(info->scsi.io_base + off); + else + return inb(info->scsi.io_port + off); +} + +static inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val) +{ + unsigned int off = reg << info->scsi.io_shift; + if (info->scsi.io_base) + writeb(val, info->scsi.io_base + off); + else + outb(val, info->scsi.io_port + off); +} + +static void fas216_dumpstate(FAS216_Info *info) +{ + unsigned char is, stat, inst; + + is = fas216_readb(info, REG_IS); + stat = fas216_readb(info, REG_STAT); + inst = fas216_readb(info, REG_INST); + + printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X" + " INST=%02X IS=%02X CFIS=%02X", + fas216_readb(info, REG_CTCL), + fas216_readb(info, REG_CTCM), + fas216_readb(info, REG_CMD), stat, inst, is, + fas216_readb(info, REG_CFIS)); + printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n", + fas216_readb(info, REG_CNTL1), + fas216_readb(info, REG_CNTL2), + fas216_readb(info, REG_CNTL3), + fas216_readb(info, REG_CTCH)); +} + +static void print_SCp(Scsi_Pointer *SCp, const char *prefix, const char *suffix) +{ + printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s", + prefix, SCp->ptr, SCp->this_residual, SCp->buffer, + SCp->buffers_residual, suffix); +} + +static void fas216_dumpinfo(FAS216_Info *info) +{ + static int used = 0; + int i; + + if (used++) + return; + + printk("FAS216_Info=\n"); + printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n", + info->magic_start, info->host, info->SCpnt, + info->origSCpnt); + printk(" scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n", + info->scsi.io_port, info->scsi.io_shift, info->scsi.irq, + info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2], + info->scsi.cfg[3]); + printk(" type=%p phase=%X\n", + info->scsi.type, info->scsi.phase); + print_SCp(&info->scsi.SCp, " SCp={ ", " }\n"); + printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", + info->scsi.async_stp, + info->scsi.disconnectable, info->scsi.aborting); + printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" + " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n", + info->stats.queues, info->stats.removes, info->stats.fins, + info->stats.reads, info->stats.writes, info->stats.miscs, + info->stats.disconnects, info->stats.aborts, info->stats.bus_resets, + info->stats.host_resets); + printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n", + info->ifcfg.clockrate, info->ifcfg.select_timeout, + info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); + for (i = 0; i < 8; i++) { + printk(" busyluns[%d]=%08lx dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", + i, info->busyluns[i], i, + info->device[i].disconnect_ok, info->device[i].stp, + info->device[i].sof, info->device[i].sync_state); + } + printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", + info->dma.transfer_type, info->dma.setup, + info->dma.pseudo, info->dma.stop); + printk(" internal_done=%X magic_end=%lX }\n", + info->internal_done, info->magic_end); +} + +#ifdef CHECK_STRUCTURE +static void __fas216_checkmagic(FAS216_Info *info, const char *func) +{ + int corruption = 0; + if (info->magic_start != MAGIC) { + printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n"); + corruption++; + } + if (info->magic_end != MAGIC) { + printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n"); + corruption++; + } + if (corruption) { + fas216_dumpinfo(info); + panic("scsi memory space corrupted in %s", func); + } +} +#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__) +#else +#define fas216_checkmagic(info) +#endif + +static const char *fas216_bus_phase(int stat) +{ + static const char *phases[] = { + "DATA OUT", "DATA IN", + "COMMAND", "STATUS", + "MISC OUT", "MISC IN", + "MESG OUT", "MESG IN" + }; + + return phases[stat & STAT_BUSMASK]; +} + +static const char *fas216_drv_phase(FAS216_Info *info) +{ + static const char *phases[] = { + [PHASE_IDLE] = "idle", + [PHASE_SELECTION] = "selection", + [PHASE_COMMAND] = "command", + [PHASE_DATAOUT] = "data out", + [PHASE_DATAIN] = "data in", + [PHASE_MSGIN] = "message in", + [PHASE_MSGIN_DISCONNECT]= "disconnect", + [PHASE_MSGOUT_EXPECT] = "expect message out", + [PHASE_MSGOUT] = "message out", + [PHASE_STATUS] = "status", + [PHASE_DONE] = "done", + }; + + if (info->scsi.phase < ARRAY_SIZE(phases) && + phases[info->scsi.phase]) + return phases[info->scsi.phase]; + return "???"; +} + +static char fas216_target(FAS216_Info *info) +{ + if (info->SCpnt) + return '0' + info->SCpnt->device->id; + else + return 'H'; +} + +static void +fas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap) +{ + static char buf[1024]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + printk("scsi%d.%c: %s", info->host->host_no, target, buf); +} + +static void +fas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...) +{ + va_list args; + + if (level != 0 && !(level & level_mask)) + return; + + va_start(args, fmt); + fas216_do_log(info, '0' + SCpnt->device->id, fmt, args); + va_end(args); + + printk(" CDB: "); + print_command(SCpnt->cmnd); +} + +static void +fas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...) +{ + va_list args; + + if (level != 0 && !(level & level_mask)) + return; + + if (target < 0) + target = 'H'; + else + target += '0'; + + va_start(args, fmt); + fas216_do_log(info, target, fmt, args); + va_end(args); + + printk("\n"); +} + +static void fas216_log(FAS216_Info *info, int level, char *fmt, ...) +{ + va_list args; + + if (level != 0 && !(level & level_mask)) + return; + + va_start(args, fmt); + fas216_do_log(info, fas216_target(info), fmt, args); + va_end(args); + + printk("\n"); +} + +#define PH_SIZE 32 + +static struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE]; +static int ph_ptr; + +static void add_debug_list(int stat, int ssr, int isr, int ph) +{ + ph_list[ph_ptr].stat = stat; + ph_list[ph_ptr].ssr = ssr; + ph_list[ph_ptr].isr = isr; + ph_list[ph_ptr].ph = ph; + + ph_ptr = (ph_ptr + 1) & (PH_SIZE-1); +} + +static struct { int command; void *from; } cmd_list[8]; +static int cmd_ptr; + +static void fas216_cmd(FAS216_Info *info, unsigned int command) +{ + cmd_list[cmd_ptr].command = command; + cmd_list[cmd_ptr].from = __builtin_return_address(0); + + cmd_ptr = (cmd_ptr + 1) & 7; + + fas216_writeb(info, REG_CMD, command); +} + +static void print_debug_list(void) +{ + int i; + + i = ph_ptr; + + printk(KERN_ERR "SCSI IRQ trail\n"); + do { + printk(" %02x:%02x:%02x:%1x", + ph_list[i].stat, ph_list[i].ssr, + ph_list[i].isr, ph_list[i].ph); + i = (i + 1) & (PH_SIZE - 1); + if (((i ^ ph_ptr) & 7) == 0) + printk("\n"); + } while (i != ph_ptr); + if ((i ^ ph_ptr) & 7) + printk("\n"); + + i = cmd_ptr; + printk(KERN_ERR "FAS216 commands: "); + do { + printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from); + i = (i + 1) & 7; + } while (i != cmd_ptr); + printk("\n"); +} + +static void fas216_done(FAS216_Info *info, unsigned int result); + +/** + * fas216_get_last_msg - retrive last message from the list + * @info: interface to search + * @pos: current fifo position + * + * Retrieve a last message from the list, using position in fifo. + */ +static inline unsigned short +fas216_get_last_msg(FAS216_Info *info, int pos) +{ + unsigned short packed_msg = NOP; + struct message *msg; + int msgnr = 0; + + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + if (pos >= msg->fifo) + break; + } + + if (msg) { + if (msg->msg[0] == EXTENDED_MESSAGE) + packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; + else + packed_msg = msg->msg[0]; + } + + fas216_log(info, LOG_MESSAGES, + "Message: %04x found at position %02x\n", packed_msg, pos); + + return packed_msg; +} + +/** + * fas216_syncperiod - calculate STP register value + * @info: state structure for interface connected to device + * @ns: period in ns (between subsequent bytes) + * + * Calculate value to be loaded into the STP register for a given period + * in ns. Returns a value suitable for REG_STP. + */ +static int fas216_syncperiod(FAS216_Info *info, int ns) +{ + int value = (info->ifcfg.clockrate * ns) / 1000; + + fas216_checkmagic(info); + + if (value < 4) + value = 4; + else if (value > 35) + value = 35; + + return value & 31; +} + +/** + * fas216_set_sync - setup FAS216 chip for specified transfer period. + * @info: state structure for interface connected to device + * @target: target + * + * Correctly setup FAS216 chip for specified transfer period. + * Notes : we need to switch the chip out of FASTSCSI mode if we have + * a transfer period >= 200ns - otherwise the chip will violate + * the SCSI timings. + */ +static void fas216_set_sync(FAS216_Info *info, int target) +{ + unsigned int cntl3; + + fas216_writeb(info, REG_SOF, info->device[target].sof); + fas216_writeb(info, REG_STP, info->device[target].stp); + + cntl3 = info->scsi.cfg[2]; + if (info->device[target].period >= (200 / 4)) + cntl3 = cntl3 & ~CNTL3_FASTSCSI; + + fas216_writeb(info, REG_CNTL3, cntl3); +} + +/* Synchronous transfer support + * + * Note: The SCSI II r10 spec says (5.6.12): + * + * (2) Due to historical problems with early host adapters that could + * not accept an SDTR message, some targets may not initiate synchronous + * negotiation after a power cycle as required by this standard. Host + * adapters that support synchronous mode may avoid the ensuing failure + * modes when the target is independently power cycled by initiating a + * synchronous negotiation on each REQUEST SENSE and INQUIRY command. + * This approach increases the SCSI bus overhead and is not recommended + * for new implementations. The correct method is to respond to an + * SDTR message with a MESSAGE REJECT message if the either the + * initiator or target devices does not support synchronous transfers + * or does not want to negotiate for synchronous transfers at the time. + * Using the correct method assures compatibility with wide data + * transfers and future enhancements. + * + * We will always initiate a synchronous transfer negotiation request on + * every INQUIRY or REQUEST SENSE message, unless the target itself has + * at some point performed a synchronous transfer negotiation request, or + * we have synchronous transfers disabled for this device. + */ + +/** + * fas216_handlesync - Handle a synchronous transfer message + * @info: state structure for interface + * @msg: message from target + * + * Handle a synchronous transfer message from the target + */ +static void fas216_handlesync(FAS216_Info *info, char *msg) +{ + struct fas216_device *dev = &info->device[info->SCpnt->device->id]; + enum { sync, async, none, reject } res = none; + +#ifdef SCSI2_SYNC + switch (msg[0]) { + case MESSAGE_REJECT: + /* Synchronous transfer request failed. + * Note: SCSI II r10: + * + * SCSI devices that are capable of synchronous + * data transfers shall not respond to an SDTR + * message with a MESSAGE REJECT message. + * + * Hence, if we get this condition, we disable + * negotiation for this device. + */ + if (dev->sync_state == neg_inprogress) { + dev->sync_state = neg_invalid; + res = async; + } + break; + + case EXTENDED_MESSAGE: + switch (dev->sync_state) { + /* We don't accept synchronous transfer requests. + * Respond with a MESSAGE_REJECT to prevent a + * synchronous transfer agreement from being reached. + */ + case neg_invalid: + res = reject; + break; + + /* We were not negotiating a synchronous transfer, + * but the device sent us a negotiation request. + * Honour the request by sending back a SDTR + * message containing our capability, limited by + * the targets capability. + */ + default: + fas216_cmd(info, CMD_SETATN); + if (msg[4] > info->ifcfg.sync_max_depth) + msg[4] = info->ifcfg.sync_max_depth; + if (msg[3] < 1000 / info->ifcfg.clockrate) + msg[3] = 1000 / info->ifcfg.clockrate; + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + msg[3], msg[4]); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + /* This is wrong. The agreement is not in effect + * until this message is accepted by the device + */ + dev->sync_state = neg_targcomplete; + res = sync; + break; + + /* We initiated the synchronous transfer negotiation, + * and have successfully received a response from the + * target. The synchronous transfer agreement has been + * reached. Note: if the values returned are out of our + * bounds, we must reject the message. + */ + case neg_inprogress: + res = reject; + if (msg[4] <= info->ifcfg.sync_max_depth && + msg[3] >= 1000 / info->ifcfg.clockrate) { + dev->sync_state = neg_complete; + res = sync; + } + break; + } + } +#else + res = reject; +#endif + + switch (res) { + case sync: + dev->period = msg[3]; + dev->sof = msg[4]; + dev->stp = fas216_syncperiod(info, msg[3] * 4); + fas216_set_sync(info, info->SCpnt->device->id); + break; + + case reject: + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + case async: + dev->period = info->ifcfg.asyncperiod / 4; + dev->sof = 0; + dev->stp = info->scsi.async_stp; + fas216_set_sync(info, info->SCpnt->device->id); + break; + + case none: + break; + } +} + +/** + * fas216_updateptrs - update data pointers after transfer suspended/paused + * @info: interface's local pointer to update + * @bytes_transferred: number of bytes transferred + * + * Update data pointers after transfer suspended/paused + */ +static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) +{ + Scsi_Pointer *SCp = &info->scsi.SCp; + + fas216_checkmagic(info); + + BUG_ON(bytes_transferred < 0); + + info->SCpnt->request_bufflen -= bytes_transferred; + + while (bytes_transferred != 0) { + if (SCp->this_residual > bytes_transferred) + break; + /* + * We have used up this buffer. Move on to the + * next buffer. + */ + bytes_transferred -= SCp->this_residual; + if (!next_SCp(SCp) && bytes_transferred) { + printk(KERN_WARNING "scsi%d.%c: out of buffers\n", + info->host->host_no, '0' + info->SCpnt->device->id); + return; + } + } + + SCp->this_residual -= bytes_transferred; + if (SCp->this_residual) + SCp->ptr += bytes_transferred; + else + SCp->ptr = NULL; +} + +/** + * fas216_pio - transfer data off of/on to card using programmed IO + * @info: interface to transfer data to/from + * @direction: direction to transfer data (DMA_OUT/DMA_IN) + * + * Transfer data off of/on to card using programmed IO. + * Notes: this is incredibly slow. + */ +static void fas216_pio(FAS216_Info *info, fasdmadir_t direction) +{ + Scsi_Pointer *SCp = &info->scsi.SCp; + + fas216_checkmagic(info); + + if (direction == DMA_OUT) + fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp)); + else + put_next_SCp_byte(SCp, fas216_readb(info, REG_FF)); + + if (SCp->this_residual == 0) + next_SCp(SCp); +} + +static void fas216_set_stc(FAS216_Info *info, unsigned int length) +{ + fas216_writeb(info, REG_STCL, length); + fas216_writeb(info, REG_STCM, length >> 8); + fas216_writeb(info, REG_STCH, length >> 16); +} + +static unsigned int fas216_get_ctc(FAS216_Info *info) +{ + return fas216_readb(info, REG_CTCL) + + (fas216_readb(info, REG_CTCM) << 8) + + (fas216_readb(info, REG_CTCH) << 16); +} + +/** + * fas216_cleanuptransfer - clean up after a transfer has completed. + * @info: interface to clean up + * + * Update the data pointers according to the number of bytes transferred + * on the SCSI bus. + */ +static void fas216_cleanuptransfer(FAS216_Info *info) +{ + unsigned long total, residual, fifo; + fasdmatype_t dmatype = info->dma.transfer_type; + + info->dma.transfer_type = fasdma_none; + + /* + * PIO transfers do not need to be cleaned up. + */ + if (dmatype == fasdma_pio || dmatype == fasdma_none) + return; + + if (dmatype == fasdma_real_all) + total = info->SCpnt->request_bufflen; + else + total = info->scsi.SCp.this_residual; + + residual = fas216_get_ctc(info); + + fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + + fas216_log(info, LOG_BUFFER, "cleaning up from previous " + "transfer: length 0x%06x, residual 0x%x, fifo %d", + total, residual, fifo); + + /* + * If we were performing Data-Out, the transfer counter + * counts down each time a byte is transferred by the + * host to the FIFO. This means we must include the + * bytes left in the FIFO from the transfer counter. + */ + if (info->scsi.phase == PHASE_DATAOUT) + residual += fifo; + + fas216_updateptrs(info, total - residual); +} + +/** + * fas216_transfer - Perform a DMA/PIO transfer off of/on to card + * @info: interface from which device disconnected from + * + * Start a DMA/PIO transfer off of/on to card + */ +static void fas216_transfer(FAS216_Info *info) +{ + fasdmadir_t direction; + fasdmatype_t dmatype; + + fas216_log(info, LOG_BUFFER, + "starttransfer: buffer %p length 0x%06x reqlen 0x%06x", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual, + info->SCpnt->request_bufflen); + + if (!info->scsi.SCp.ptr) { + fas216_log(info, LOG_ERROR, "null buffer passed to " + "fas216_starttransfer"); + print_SCp(&info->scsi.SCp, "SCp: ", "\n"); + print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n"); + return; + } + + /* + * If we have a synchronous transfer agreement in effect, we must + * use DMA mode. If we are using asynchronous transfers, we may + * use DMA mode or PIO mode. + */ + if (info->device[info->SCpnt->device->id].sof) + dmatype = fasdma_real_all; + else + dmatype = fasdma_pio; + + if (info->scsi.phase == PHASE_DATAOUT) + direction = DMA_OUT; + else + direction = DMA_IN; + + if (info->dma.setup) + dmatype = info->dma.setup(info->host, &info->scsi.SCp, + direction, dmatype); + info->dma.transfer_type = dmatype; + + if (dmatype == fasdma_real_all) + fas216_set_stc(info, info->SCpnt->request_bufflen); + else + fas216_set_stc(info, info->scsi.SCp.this_residual); + + switch (dmatype) { + case fasdma_pio: + fas216_log(info, LOG_BUFFER, "PIO transfer"); + fas216_writeb(info, REG_SOF, 0); + fas216_writeb(info, REG_STP, info->scsi.async_stp); + fas216_cmd(info, CMD_TRANSFERINFO); + fas216_pio(info, direction); + break; + + case fasdma_pseudo: + fas216_log(info, LOG_BUFFER, "pseudo transfer"); + fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); + info->dma.pseudo(info->host, &info->scsi.SCp, + direction, info->SCpnt->transfersize); + break; + + case fasdma_real_block: + fas216_log(info, LOG_BUFFER, "block dma transfer"); + fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); + break; + + case fasdma_real_all: + fas216_log(info, LOG_BUFFER, "total dma transfer"); + fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); + break; + + default: + fas216_log(info, LOG_BUFFER | LOG_ERROR, + "invalid FAS216 DMA type"); + break; + } +} + +/** + * fas216_stoptransfer - Stop a DMA transfer onto / off of the card + * @info: interface from which device disconnected from + * + * Called when we switch away from DATA IN or DATA OUT phases. + */ +static void fas216_stoptransfer(FAS216_Info *info) +{ + fas216_checkmagic(info); + + if (info->dma.transfer_type == fasdma_real_all || + info->dma.transfer_type == fasdma_real_block) + info->dma.stop(info->host, &info->scsi.SCp); + + fas216_cleanuptransfer(info); + + if (info->scsi.phase == PHASE_DATAIN) { + unsigned int fifo; + + /* + * If we were performing Data-In, then the FIFO counter + * contains the number of bytes not transferred via DMA + * from the on-board FIFO. Read them manually. + */ + fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + while (fifo && info->scsi.SCp.ptr) { + *info->scsi.SCp.ptr = fas216_readb(info, REG_FF); + fas216_updateptrs(info, 1); + fifo--; + } + } else { + /* + * After a Data-Out phase, there may be unsent + * bytes left in the FIFO. Flush them out. + */ + fas216_cmd(info, CMD_FLUSHFIFO); + } +} + +static void fas216_aborttransfer(FAS216_Info *info) +{ + fas216_checkmagic(info); + + if (info->dma.transfer_type == fasdma_real_all || + info->dma.transfer_type == fasdma_real_block) + info->dma.stop(info->host, &info->scsi.SCp); + + info->dma.transfer_type = fasdma_none; + fas216_cmd(info, CMD_FLUSHFIFO); +} + +static void fas216_kick(FAS216_Info *info); + +/** + * fas216_disconnected_intr - handle device disconnection + * @info: interface from which device disconnected from + * + * Handle device disconnection + */ +static void fas216_disconnect_intr(FAS216_Info *info) +{ + unsigned long flags; + + fas216_checkmagic(info); + + fas216_log(info, LOG_CONNECT, "disconnect phase=%02x", + info->scsi.phase); + + msgqueue_flush(&info->scsi.msgs); + + switch (info->scsi.phase) { + case PHASE_SELECTION: /* while selecting - no target */ + case PHASE_SELSTEPS: + fas216_done(info, DID_NO_CONNECT); + break; + + case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ + info->scsi.disconnectable = 1; + info->scsi.phase = PHASE_IDLE; + info->stats.disconnects += 1; + spin_lock_irqsave(&info->host_lock, flags); + if (info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + spin_unlock_irqrestore(&info->host_lock, flags); + break; + + case PHASE_DONE: /* at end of command - complete */ + fas216_done(info, DID_OK); + break; + + case PHASE_MSGOUT: /* message out - possible ABORT message */ + if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { + info->scsi.aborting = 0; + fas216_done(info, DID_ABORT); + break; + } + + default: /* huh? */ + printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n", + info->host->host_no, fas216_target(info), fas216_drv_phase(info)); + print_debug_list(); + fas216_stoptransfer(info); + fas216_done(info, DID_ERROR); + break; + } +} + +/** + * fas216_reselected_intr - start reconnection of a device + * @info: interface which was reselected + * + * Start reconnection of a device + */ +static void +fas216_reselected_intr(FAS216_Info *info) +{ + unsigned int cfis, i; + unsigned char msg[4]; + unsigned char target, lun, tag; + + fas216_checkmagic(info); + + WARN_ON(info->scsi.phase == PHASE_SELECTION || + info->scsi.phase == PHASE_SELSTEPS); + + cfis = fas216_readb(info, REG_CFIS); + + fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x", + info->scsi.phase, cfis); + + cfis &= CFIS_CF; + + if (cfis < 2 || cfis > 4) { + printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", + info->host->host_no); + goto bad_message; + } + + for (i = 0; i < cfis; i++) + msg[i] = fas216_readb(info, REG_FF); + + if (!(msg[0] & (1 << info->host->this_id)) || + !(msg[1] & 0x80)) + goto initiator_error; + + target = msg[0] & ~(1 << info->host->this_id); + target = ffs(target) - 1; + lun = msg[1] & 7; + tag = 0; + + if (cfis >= 3) { + if (msg[2] != SIMPLE_QUEUE_TAG) + goto initiator_error; + + tag = msg[3]; + } + + /* set up for synchronous transfers */ + fas216_writeb(info, REG_SDID, target); + fas216_set_sync(info, target); + msgqueue_flush(&info->scsi.msgs); + + fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x", + target, lun, tag); + + if (info->scsi.disconnectable && info->SCpnt) { + info->scsi.disconnectable = 0; + if (info->SCpnt->device->id == target && + info->SCpnt->device->lun == lun && + info->SCpnt->tag == tag) { + fas216_log(info, LOG_CONNECT, "reconnected previously executing command"); + } else { + queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); + fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue"); + info->SCpnt = NULL; + } + } + if (!info->SCpnt) { + info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected, + target, lun, tag); + fas216_log(info, LOG_CONNECT, "had to get command"); + } + + if (info->SCpnt) { + /* + * Restore data pointer from SAVED data pointer + */ + info->scsi.SCp = info->SCpnt->SCp; + + fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + info->scsi.phase = PHASE_MSGIN; + } else { + /* + * Our command structure not found - abort the + * command on the target. Since we have no + * record of this command, we can't send + * an INITIATOR DETECTED ERROR message. + */ + fas216_cmd(info, CMD_SETATN); + +#if 0 + if (tag) + msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, tag); + else +#endif + msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + info->scsi.aborting = 1; + } + + fas216_cmd(info, CMD_MSGACCEPTED); + return; + + initiator_error: + printk(KERN_ERR "scsi%d.H: error during reselection: bytes", + info->host->host_no); + for (i = 0; i < cfis; i++) + printk(" %02x", msg[i]); + printk("\n"); + bad_message: + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + fas216_cmd(info, CMD_MSGACCEPTED); +} + +static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen) +{ + int i; + + switch (message[0]) { + case COMMAND_COMPLETE: + if (msglen != 1) + goto unrecognised; + + printk(KERN_ERR "scsi%d.%c: command complete with no " + "status in MESSAGE_IN?\n", + info->host->host_no, fas216_target(info)); + break; + + case SAVE_POINTERS: + if (msglen != 1) + goto unrecognised; + + /* + * Save current data pointer to SAVED data pointer + * SCSI II standard says that we must not acknowledge + * this until we have really saved pointers. + * NOTE: we DO NOT save the command nor status pointers + * as required by the SCSI II standard. These always + * point to the start of their respective areas. + */ + info->SCpnt->SCp = info->scsi.SCp; + info->SCpnt->SCp.sent_command = 0; + fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, + "save data pointers: [%p, %X]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + break; + + case RESTORE_POINTERS: + if (msglen != 1) + goto unrecognised; + + /* + * Restore current data pointer from SAVED data pointer + */ + info->scsi.SCp = info->SCpnt->SCp; + fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, + "restore data pointers: [%p, 0x%x]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + break; + + case DISCONNECT: + if (msglen != 1) + goto unrecognised; + + info->scsi.phase = PHASE_MSGIN_DISCONNECT; + break; + + case MESSAGE_REJECT: + if (msglen != 1) + goto unrecognised; + + switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) { + case EXTENDED_MESSAGE | EXTENDED_SDTR << 8: + fas216_handlesync(info, message); + break; + + default: + fas216_log(info, 0, "reject, last message 0x%04x", + fas216_get_last_msg(info, info->scsi.msgin_fifo)); + } + break; + + case NOP: + break; + + case EXTENDED_MESSAGE: + if (msglen < 3) + goto unrecognised; + + switch (message[2]) { + case EXTENDED_SDTR: /* Sync transfer negotiation request/reply */ + fas216_handlesync(info, message); + break; + + default: + goto unrecognised; + } + break; + + default: + goto unrecognised; + } + return; + +unrecognised: + fas216_log(info, 0, "unrecognised message, rejecting"); + printk("scsi%d.%c: message was", info->host->host_no, fas216_target(info)); + for (i = 0; i < msglen; i++) + printk("%s%02X", i & 31 ? " " : "\n ", message[i]); + printk("\n"); + + /* + * Something strange seems to be happening here - + * I can't use SETATN since the chip gives me an + * invalid command interrupt when I do. Weird. + */ +fas216_cmd(info, CMD_NOP); +fas216_dumpstate(info); + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; +fas216_dumpstate(info); +} + +static int fas216_wait_cmd(FAS216_Info *info, int cmd) +{ + int tout; + int stat; + + fas216_cmd(info, cmd); + + for (tout = 1000; tout; tout -= 1) { + stat = fas216_readb(info, REG_STAT); + if (stat & (STAT_INT|STAT_PARITYERROR)) + break; + udelay(1); + } + + return stat; +} + +static int fas216_get_msg_byte(FAS216_Info *info) +{ + unsigned int stat = fas216_wait_cmd(info, CMD_MSGACCEPTED); + + if ((stat & STAT_INT) == 0) + goto timedout; + + if ((stat & STAT_BUSMASK) != STAT_MESGIN) + goto unexpected_phase_change; + + fas216_readb(info, REG_INST); + + stat = fas216_wait_cmd(info, CMD_TRANSFERINFO); + + if ((stat & STAT_INT) == 0) + goto timedout; + + if (stat & STAT_PARITYERROR) + goto parity_error; + + if ((stat & STAT_BUSMASK) != STAT_MESGIN) + goto unexpected_phase_change; + + fas216_readb(info, REG_INST); + + return fas216_readb(info, REG_FF); + +timedout: + fas216_log(info, LOG_ERROR, "timed out waiting for message byte"); + return -1; + +unexpected_phase_change: + fas216_log(info, LOG_ERROR, "unexpected phase change: status = %02x", stat); + return -2; + +parity_error: + fas216_log(info, LOG_ERROR, "parity error during message in phase"); + return -3; +} + +/** + * fas216_message - handle a function done interrupt from FAS216 chip + * @info: interface which caused function done interrupt + * + * Handle a function done interrupt from FAS216 chip + */ +static void fas216_message(FAS216_Info *info) +{ + unsigned char *message = info->scsi.message; + unsigned int msglen = 1; + int msgbyte = 0; + + fas216_checkmagic(info); + + message[0] = fas216_readb(info, REG_FF); + + if (message[0] == EXTENDED_MESSAGE) { + msgbyte = fas216_get_msg_byte(info); + + if (msgbyte >= 0) { + message[1] = msgbyte; + + for (msglen = 2; msglen < message[1] + 2; msglen++) { + msgbyte = fas216_get_msg_byte(info); + + if (msgbyte >= 0) + message[msglen] = msgbyte; + else + break; + } + } + } + + if (msgbyte == -3) + goto parity_error; + +#ifdef DEBUG_MESSAGES + { + int i; + + printk("scsi%d.%c: message in: ", + info->host->host_no, fas216_target(info)); + for (i = 0; i < msglen; i++) + printk("%02X ", message[i]); + printk("\n"); + } +#endif + + fas216_parse_message(info, message, msglen); + fas216_cmd(info, CMD_MSGACCEPTED); + return; + +parity_error: + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MSG_PARITY_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + fas216_cmd(info, CMD_MSGACCEPTED); + return; +} + +/** + * fas216_send_command - send command after all message bytes have been sent + * @info: interface which caused bus service + * + * Send a command to a target after all message bytes have been sent + */ +static void fas216_send_command(FAS216_Info *info) +{ + int i; + + fas216_checkmagic(info); + + fas216_cmd(info, CMD_NOP|CMD_WITHDMA); + fas216_cmd(info, CMD_FLUSHFIFO); + + /* load command */ + for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++) + fas216_writeb(info, REG_FF, info->SCpnt->cmnd[i]); + + fas216_cmd(info, CMD_TRANSFERINFO); + + info->scsi.phase = PHASE_COMMAND; +} + +/** + * fas216_send_messageout - handle bus service to send a message + * @info: interface which caused bus service + * + * Handle bus service to send a message. + * Note: We do not allow the device to change the data direction! + */ +static void fas216_send_messageout(FAS216_Info *info, int start) +{ + unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); + + fas216_checkmagic(info); + + fas216_cmd(info, CMD_FLUSHFIFO); + + if (tot_msglen) { + struct message *msg; + int msgnr = 0; + + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + int i; + + for (i = start; i < msg->length; i++) + fas216_writeb(info, REG_FF, msg->msg[i]); + + msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); + start = 0; + } + } else + fas216_writeb(info, REG_FF, NOP); + + fas216_cmd(info, CMD_TRANSFERINFO); + + info->scsi.phase = PHASE_MSGOUT; +} + +/** + * fas216_busservice_intr - handle bus service interrupt from FAS216 chip + * @info: interface which caused bus service interrupt + * @stat: Status register contents + * @is: SCSI Status register contents + * + * Handle a bus service interrupt from FAS216 chip + */ +static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int is) +{ + fas216_checkmagic(info); + + fas216_log(info, LOG_BUSSERVICE, + "bus service: stat=%02x is=%02x phase=%02x", + stat, is, info->scsi.phase); + + switch (info->scsi.phase) { + case PHASE_SELECTION: + if ((is & IS_BITS) != IS_MSGBYTESENT) + goto bad_is; + break; + + case PHASE_SELSTEPS: + switch (is & IS_BITS) { + case IS_SELARB: + case IS_MSGBYTESENT: + goto bad_is; + + case IS_NOTCOMMAND: + case IS_EARLYPHASE: + if ((stat & STAT_BUSMASK) == STAT_MESGIN) + break; + goto bad_is; + + case IS_COMPLETE: + break; + } + + default: + break; + } + + fas216_cmd(info, CMD_NOP); + +#define STATE(st,ph) ((ph) << 3 | (st)) + /* This table describes the legal SCSI state transitions, + * as described by the SCSI II spec. + */ + switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { + case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ + case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */ + info->scsi.phase = PHASE_DATAIN; + fas216_transfer(info); + return; + + case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */ + case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */ + fas216_cleanuptransfer(info); + fas216_transfer(info); + return; + + case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */ + fas216_cmd(info, CMD_FLUSHFIFO); + info->scsi.phase = PHASE_DATAOUT; + fas216_transfer(info); + return; + + case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ + case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ + fas216_stoptransfer(info); + case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ + case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ + case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ + case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ + fas216_cmd(info, CMD_INITCMDCOMPLETE); + info->scsi.phase = PHASE_STATUS; + return; + + case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ + case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ + fas216_stoptransfer(info); + case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */ + case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ + case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ + info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + fas216_cmd(info, CMD_FLUSHFIFO); + fas216_cmd(info, CMD_TRANSFERINFO); + info->scsi.phase = PHASE_MSGIN; + return; + + case STATE(STAT_MESGIN, PHASE_MSGIN): + info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + fas216_cmd(info, CMD_TRANSFERINFO); + return; + + case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ + case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ + fas216_send_command(info); + info->scsi.phase = PHASE_COMMAND; + return; + + + /* + * Selection -> Message Out + */ + case STATE(STAT_MESGOUT, PHASE_SELECTION): + fas216_send_messageout(info, 1); + return; + + /* + * Message Out -> Message Out + */ + case STATE(STAT_MESGOUT, PHASE_SELSTEPS): + case STATE(STAT_MESGOUT, PHASE_MSGOUT): + /* + * If we get another message out phase, this usually + * means some parity error occurred. Resend complete + * set of messages. If we have more than one byte to + * send, we need to assert ATN again. + */ + if (info->device[info->SCpnt->device->id].parity_check) { + /* + * We were testing... good, the device + * supports parity checking. + */ + info->device[info->SCpnt->device->id].parity_check = 0; + info->device[info->SCpnt->device->id].parity_enabled = 1; + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + } + + if (msgqueue_msglength(&info->scsi.msgs) > 1) + fas216_cmd(info, CMD_SETATN); + /*FALLTHROUGH*/ + + /* + * Any -> Message Out + */ + case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT): + fas216_send_messageout(info, 0); + return; + + /* Error recovery rules. + * These either attempt to abort or retry the operation. + * TODO: we need more of these + */ + case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ + /* error - we've sent out all the command bytes + * we have. + * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS + * to include the command bytes sent for this to work + * correctly. + */ + printk(KERN_ERR "scsi%d.%c: " + "target trying to receive more command bytes\n", + info->host->host_no, fas216_target(info)); + fas216_cmd(info, CMD_SETATN); + fas216_set_stc(info, 15); + fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + return; + } + + if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { + printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", + info->host->host_no, fas216_target(info), + fas216_bus_phase(stat)); + msgqueue_flush(&info->scsi.msgs); + fas216_cmd(info, CMD_SETATN); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + info->scsi.aborting = 1; + fas216_cmd(info, CMD_TRANSFERINFO); + return; + } + printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", + info->host->host_no, fas216_target(info), + fas216_bus_phase(stat), + fas216_drv_phase(info)); + print_debug_list(); + return; + +bad_is: + fas216_log(info, 0, "bus service at step %d?", is & IS_BITS); + fas216_dumpstate(info); + print_debug_list(); + + fas216_done(info, DID_ERROR); +} + +/** + * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip + * @info: interface which caused function done interrupt + * @stat: Status register contents + * @is: SCSI Status register contents + * + * Handle a function done interrupt from FAS216 chip + */ +static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is) +{ + unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF; + + fas216_checkmagic(info); + + fas216_log(info, LOG_FUNCTIONDONE, + "function done: stat=%02x is=%02x phase=%02x", + stat, is, info->scsi.phase); + + switch (info->scsi.phase) { + case PHASE_STATUS: /* status phase - read status and msg */ + if (fifo_len != 2) { + fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len); + } + /* + * Read status then message byte. + */ + info->scsi.SCp.Status = fas216_readb(info, REG_FF); + info->scsi.SCp.Message = fas216_readb(info, REG_FF); + info->scsi.phase = PHASE_DONE; + fas216_cmd(info, CMD_MSGACCEPTED); + break; + + case PHASE_IDLE: + case PHASE_SELECTION: + case PHASE_SELSTEPS: + break; + + case PHASE_MSGIN: /* message in phase */ + if ((stat & STAT_BUSMASK) == STAT_MESGIN) { + info->scsi.msgin_fifo = fifo_len; + fas216_message(info); + break; + } + + default: + fas216_log(info, 0, "internal phase %s for function done?" + " What do I do with this?", + fas216_target(info), fas216_drv_phase(info)); + } +} + +static void fas216_bus_reset(FAS216_Info *info) +{ + neg_t sync_state; + int i; + + msgqueue_flush(&info->scsi.msgs); + + sync_state = neg_invalid; + +#ifdef SCSI2_SYNC + if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA)) + sync_state = neg_wait; +#endif + + info->scsi.phase = PHASE_IDLE; + info->SCpnt = NULL; /* bug! */ + memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp)); + + for (i = 0; i < 8; i++) { + info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; + info->device[i].sync_state = sync_state; + info->device[i].period = info->ifcfg.asyncperiod / 4; + info->device[i].stp = info->scsi.async_stp; + info->device[i].sof = 0; + info->device[i].wide_xfer = 0; + } + + info->rst_bus_status = 1; + wake_up(&info->eh_wait); +} + +/** + * fas216_intr - handle interrupts to progress a command + * @info: interface to service + * + * Handle interrupts from the interface to progress a command + */ +irqreturn_t fas216_intr(FAS216_Info *info) +{ + unsigned char inst, is, stat; + int handled = IRQ_NONE; + + fas216_checkmagic(info); + + stat = fas216_readb(info, REG_STAT); + is = fas216_readb(info, REG_IS); + inst = fas216_readb(info, REG_INST); + + add_debug_list(stat, is, inst, info->scsi.phase); + + if (stat & STAT_INT) { + if (inst & INST_BUSRESET) { + fas216_log(info, 0, "bus reset detected"); + fas216_bus_reset(info); + scsi_report_bus_reset(info->host, 0); + } else if (inst & INST_ILLEGALCMD) { + fas216_log(info, LOG_ERROR, "illegal command given\n"); + fas216_dumpstate(info); + print_debug_list(); + } else if (inst & INST_DISCONNECT) + fas216_disconnect_intr(info); + else if (inst & INST_RESELECTED) /* reselected */ + fas216_reselected_intr(info); + else if (inst & INST_BUSSERVICE) /* bus service request */ + fas216_busservice_intr(info, stat, is); + else if (inst & INST_FUNCDONE) /* function done */ + fas216_funcdone_intr(info, stat, is); + else + fas216_log(info, 0, "unknown interrupt received:" + " phase %s inst %02X is %02X stat %02X", + fas216_drv_phase(info), inst, is, stat); + handled = IRQ_HANDLED; + } + return handled; +} + +static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + int tot_msglen; + + /* following what the ESP driver says */ + fas216_set_stc(info, 0); + fas216_cmd(info, CMD_NOP | CMD_WITHDMA); + + /* flush FIFO */ + fas216_cmd(info, CMD_FLUSHFIFO); + + /* load bus-id and timeout */ + fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); + fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); + + /* synchronous transfers */ + fas216_set_sync(info, SCpnt->device->id); + + tot_msglen = msgqueue_msglength(&info->scsi.msgs); + +#ifdef DEBUG_MESSAGES + { + struct message *msg; + int msgnr = 0, i; + + printk("scsi%d.%c: message out: ", + info->host->host_no, '0' + SCpnt->device->id); + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + printk("{ "); + for (i = 0; i < msg->length; i++) + printk("%02x ", msg->msg[i]); + printk("} "); + } + printk("\n"); + } +#endif + + if (tot_msglen == 1 || tot_msglen == 3) { + /* + * We have an easy message length to send... + */ + struct message *msg; + int msgnr = 0, i; + + info->scsi.phase = PHASE_SELSTEPS; + + /* load message bytes */ + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + for (i = 0; i < msg->length; i++) + fas216_writeb(info, REG_FF, msg->msg[i]); + msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); + } + + /* load command */ + for (i = 0; i < SCpnt->cmd_len; i++) + fas216_writeb(info, REG_FF, SCpnt->cmnd[i]); + + if (tot_msglen == 1) + fas216_cmd(info, CMD_SELECTATN); + else + fas216_cmd(info, CMD_SELECTATN3); + } else { + /* + * We have an unusual number of message bytes to send. + * Load first byte into fifo, and issue SELECT with ATN and + * stop steps. + */ + struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); + + fas216_writeb(info, REG_FF, msg->msg[0]); + msg->fifo = 1; + + fas216_cmd(info, CMD_SELECTATNSTOP); + } +} + +/* + * Decide whether we need to perform a parity test on this device. + * Can also be used to force parity error conditions during initial + * information transfer phase (message out) for test purposes. + */ +static int parity_test(FAS216_Info *info, int target) +{ +#if 0 + if (target == 3) { + info->device[target].parity_check = 0; + return 1; + } +#endif + return info->device[target].parity_check; +} + +static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + int disconnect_ok; + + /* + * claim host busy + */ + info->scsi.phase = PHASE_SELECTION; + info->scsi.SCp = SCpnt->SCp; + info->SCpnt = SCpnt; + info->dma.transfer_type = fasdma_none; + + if (parity_test(info, SCpnt->device->id)) + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE); + else + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + + /* + * Don't allow request sense commands to disconnect. + */ + disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE && + info->device[SCpnt->device->id].disconnect_ok; + + /* + * build outgoing message bytes + */ + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->device->lun)); + + /* + * add tag message if required + */ + if (SCpnt->tag) + msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); + + do { +#ifdef SCSI2_SYNC + if ((info->device[SCpnt->device->id].sync_state == neg_wait || + info->device[SCpnt->device->id].sync_state == neg_complete) && + (SCpnt->cmnd[0] == REQUEST_SENSE || + SCpnt->cmnd[0] == INQUIRY)) { + info->device[SCpnt->device->id].sync_state = neg_inprogress; + msgqueue_addmsg(&info->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + 1000 / info->ifcfg.clockrate, + info->ifcfg.sync_max_depth); + break; + } +#endif + } while (0); + + __fas216_start_command(info, SCpnt); +} + +static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ +#ifdef SCSI2_TAG + /* + * tagged queuing - allocate a new tag to this command + */ + if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && + SCpnt->cmnd[0] != INQUIRY) { + SCpnt->device->current_tag += 1; + if (SCpnt->device->current_tag == 0) + SCpnt->device->current_tag = 1; + SCpnt->tag = SCpnt->device->current_tag; + } else +#endif + set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + + info->stats.removes += 1; + switch (SCpnt->cmnd[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_12: + info->stats.writes += 1; + break; + case READ_6: + case READ_10: + case READ_12: + info->stats.reads += 1; + break; + default: + info->stats.miscs += 1; + break; + } +} + +static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + struct message *msg; + + /* + * claim host busy + */ + info->scsi.phase = PHASE_SELECTION; + info->scsi.SCp = SCpnt->SCp; + info->SCpnt = SCpnt; + info->dma.transfer_type = fasdma_none; + + fas216_log(info, LOG_ERROR, "sending bus device reset"); + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET); + + /* following what the ESP driver says */ + fas216_set_stc(info, 0); + fas216_cmd(info, CMD_NOP | CMD_WITHDMA); + + /* flush FIFO */ + fas216_cmd(info, CMD_FLUSHFIFO); + + /* load bus-id and timeout */ + fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); + fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); + + /* synchronous transfers */ + fas216_set_sync(info, SCpnt->device->id); + + msg = msgqueue_getmsg(&info->scsi.msgs, 0); + + fas216_writeb(info, REG_FF, BUS_DEVICE_RESET); + msg->fifo = 1; + + fas216_cmd(info, CMD_SELECTATNSTOP); +} + +/** + * fas216_kick - kick a command to the interface + * @info: our host interface to kick + * + * Kick a command to the interface, interface should be idle. + * Notes: Interrupts are always disabled! + */ +static void fas216_kick(FAS216_Info *info) +{ + Scsi_Cmnd *SCpnt = NULL; +#define TYPE_OTHER 0 +#define TYPE_RESET 1 +#define TYPE_QUEUE 2 + int where_from = TYPE_OTHER; + + fas216_checkmagic(info); + + /* + * Obtain the next command to process. + */ + do { + if (info->rstSCpnt) { + SCpnt = info->rstSCpnt; + /* don't remove it */ + where_from = TYPE_RESET; + break; + } + + if (info->reqSCpnt) { + SCpnt = info->reqSCpnt; + info->reqSCpnt = NULL; + break; + } + + if (info->origSCpnt) { + SCpnt = info->origSCpnt; + info->origSCpnt = NULL; + break; + } + + /* retrieve next command */ + if (!SCpnt) { + SCpnt = queue_remove_exclude(&info->queues.issue, + info->busyluns); + where_from = TYPE_QUEUE; + break; + } + } while (0); + + if (!SCpnt) { + /* + * no command pending, so enable reselection. + */ + fas216_cmd(info, CMD_ENABLESEL); + return; + } + + /* + * We're going to start a command, so disable reselection + */ + fas216_cmd(info, CMD_DISABLESEL); + + if (info->scsi.disconnectable && info->SCpnt) { + fas216_log(info, LOG_CONNECT, + "moved command for %d to disconnected queue", + info->SCpnt->device->id); + queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); + info->scsi.disconnectable = 0; + info->SCpnt = NULL; + } + + fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt, + "starting"); + + switch (where_from) { + case TYPE_QUEUE: + fas216_allocate_tag(info, SCpnt); + case TYPE_OTHER: + fas216_start_command(info, SCpnt); + break; + case TYPE_RESET: + fas216_do_bus_device_reset(info, SCpnt); + break; + } + + fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + + /* + * should now get either DISCONNECT or + * (FUNCTION DONE with BUS SERVICE) interrupt + */ +} + +/* + * Clean up from issuing a BUS DEVICE RESET message to a device. + */ +static void +fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ + fas216_log(info, LOG_ERROR, "fas216 device reset complete"); + + info->rstSCpnt = NULL; + info->rst_dev_status = 1; + wake_up(&info->eh_wait); +} + +/** + * fas216_rq_sns_done - Finish processing automatic request sense command + * @info: interface that completed + * @SCpnt: command that completed + * @result: driver byte of result + * + * Finish processing automatic request sense command + */ +static void +fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ + fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, + "request sense complete, result=0x%04x%02x%02x", + result, SCpnt->SCp.Message, SCpnt->SCp.Status); + + if (result != DID_OK || SCpnt->SCp.Status != GOOD) + /* + * Something went wrong. Make sure that we don't + * have valid data in the sense buffer that could + * confuse the higher levels. + */ + memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); +//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id); +//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); } + /* + * Note that we don't set SCpnt->result, since that should + * reflect the status of the command that we were asked by + * the upper layers to process. This would have been set + * correctly by fas216_std_done. + */ + SCpnt->scsi_done(SCpnt); +} + +/** + * fas216_std_done - finish processing of standard command + * @info: interface that completed + * @SCpnt: command that completed + * @result: driver byte of result + * + * Finish processing of standard command + */ +static void +fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ + info->stats.fins += 1; + + SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | + info->scsi.SCp.Status; + + fas216_log_command(info, LOG_CONNECT, SCpnt, + "command complete, result=0x%08x", SCpnt->result); + + /* + * If the driver detected an error, we're all done. + */ + if (host_byte(SCpnt->result) != DID_OK || + msg_byte(SCpnt->result) != COMMAND_COMPLETE) + goto done; + + /* + * If the command returned CHECK_CONDITION or COMMAND_TERMINATED + * status, request the sense information. + */ + if (status_byte(SCpnt->result) == CHECK_CONDITION || + status_byte(SCpnt->result) == COMMAND_TERMINATED) + goto request_sense; + + /* + * If the command did not complete with GOOD status, + * we are all done here. + */ + if (status_byte(SCpnt->result) != GOOD) + goto done; + + /* + * We have successfully completed a command. Make sure that + * we do not have any buffers left to transfer. The world + * is not perfect, and we seem to occasionally hit this. + * It can be indicative of a buggy driver, target or the upper + * levels of the SCSI code. + */ + if (info->scsi.SCp.ptr) { + switch (SCpnt->cmnd[0]) { + case INQUIRY: + case START_STOP: + case MODE_SENSE: + break; + + default: + printk(KERN_ERR "scsi%d.%c: incomplete data transfer " + "detected: res=%08X ptr=%p len=%X CDB: ", + info->host->host_no, '0' + SCpnt->device->id, + SCpnt->result, info->scsi.SCp.ptr, + info->scsi.SCp.this_residual); + print_command(SCpnt->cmnd); + SCpnt->result &= ~(255 << 16); + SCpnt->result |= DID_BAD_TARGET << 16; + goto request_sense; + } + } + +done: + if (SCpnt->scsi_done) { + SCpnt->scsi_done(SCpnt); + return; + } + + panic("scsi%d.H: null scsi_done function in fas216_done", + info->host->host_no); + + +request_sense: + if (SCpnt->cmnd[0] == REQUEST_SENSE) + goto done; + + fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, + "requesting sense"); + memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd)); + SCpnt->cmnd[0] = REQUEST_SENSE; + SCpnt->cmnd[1] = SCpnt->device->lun << 5; + SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->SCp.buffer = NULL; + SCpnt->SCp.buffers_residual = 0; + SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; + SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); + SCpnt->SCp.Message = 0; + SCpnt->SCp.Status = 0; + SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); + SCpnt->sc_data_direction = SCSI_DATA_READ; + SCpnt->use_sg = 0; + SCpnt->tag = 0; + SCpnt->host_scribble = (void *)fas216_rq_sns_done; + + /* + * Place this command into the high priority "request + * sense" slot. This will be the very next command + * executed, unless a target connects to us. + */ + if (info->reqSCpnt) + printk(KERN_WARNING "scsi%d.%c: loosing request command\n", + info->host->host_no, '0' + SCpnt->device->id); + info->reqSCpnt = SCpnt; +} + +/** + * fas216_done - complete processing for current command + * @info: interface that completed + * @result: driver byte of result + * + * Complete processing for current command + */ +static void fas216_done(FAS216_Info *info, unsigned int result) +{ + void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int); + Scsi_Cmnd *SCpnt; + unsigned long flags; + + fas216_checkmagic(info); + + if (!info->SCpnt) + goto no_command; + + SCpnt = info->SCpnt; + info->SCpnt = NULL; + info->scsi.phase = PHASE_IDLE; + + if (info->scsi.aborting) { + fas216_log(info, 0, "uncaught abort - returning DID_ABORT"); + result = DID_ABORT; + info->scsi.aborting = 0; + } + + /* + * Sanity check the completion - if we have zero bytes left + * to transfer, we should not have a valid pointer. + */ + if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { + printk("scsi%d.%c: zero bytes left to transfer, but " + "buffer pointer still valid: ptr=%p len=%08x CDB: ", + info->host->host_no, '0' + SCpnt->device->id, + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + info->scsi.SCp.ptr = NULL; + print_command(SCpnt->cmnd); + } + + /* + * Clear down this command as completed. If we need to request + * the sense information, fas216_kick will re-assert the busy + * status. + */ + info->device[SCpnt->device->id].parity_check = 0; + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + + fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble; + fn(info, SCpnt, result); + + if (info->scsi.irq != NO_IRQ) { + spin_lock_irqsave(&info->host_lock, flags); + if (info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + spin_unlock_irqrestore(&info->host_lock, flags); + } + return; + +no_command: + panic("scsi%d.H: null command in fas216_done", + info->host->host_no); +} + +/** + * fas216_queue_command - queue a command for adapter to process. + * @SCpnt: Command to queue + * @done: done function to call once command is complete + * + * Queue a command for adapter to process. + * Returns: 0 on success, else error. + * Notes: io_request_lock is held, interrupts are disabled. + */ +int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + int result; + + fas216_checkmagic(info); + + fas216_log_command(info, LOG_CONNECT, SCpnt, + "received command (%p)", SCpnt); + + SCpnt->scsi_done = done; + SCpnt->host_scribble = (void *)fas216_std_done; + SCpnt->result = 0; + + init_SCp(SCpnt); + + info->stats.queues += 1; + SCpnt->tag = 0; + + spin_lock(&info->host_lock); + + /* + * Add command into execute queue and let it complete under + * whatever scheme we're using. + */ + result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt); + + /* + * If we successfully added the command, + * kick the interface to get it moving. + */ + if (result == 0 && info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + spin_unlock(&info->host_lock); + + fas216_log_target(info, LOG_CONNECT, -1, "queue %s", + result ? "failure" : "success"); + + return result; +} + +/** + * fas216_internal_done - trigger restart of a waiting thread in fas216_command + * @SCpnt: Command to wake + * + * Trigger restart of a waiting thread in fas216_command + */ +static void fas216_internal_done(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + + fas216_checkmagic(info); + + info->internal_done = 1; +} + +/** + * fas216_command - queue a command for adapter to process. + * @SCpnt: Command to queue + * + * Queue a command for adapter to process. + * Returns: scsi result code. + * Notes: io_request_lock is held, interrupts are disabled. + */ +int fas216_command(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + + fas216_checkmagic(info); + + /* + * We should only be using this if we don't have an interrupt. + * Provide some "incentive" to use the queueing code. + */ + if (info->scsi.irq != NO_IRQ) + BUG(); + + info->internal_done = 0; + fas216_queue_command(SCpnt, fas216_internal_done); + + /* + * This wastes time, since we can't return until the command is + * complete. We can't sleep either since we may get re-entered! + * However, we must re-enable interrupts, or else we'll be + * waiting forever. + */ + spin_unlock_irq(info->host->host_lock); + + while (!info->internal_done) { + /* + * If we don't have an IRQ, then we must poll the card for + * it's interrupt, and use that to call this driver's + * interrupt routine. That way, we keep the command + * progressing. Maybe we can add some inteligence here + * and go to sleep if we know that the device is going + * to be some time (eg, disconnected). + */ + if (fas216_readb(info, REG_STAT) & STAT_INT) { + spin_lock_irq(info->host->host_lock); + fas216_intr(info); + spin_unlock_irq(info->host->host_lock); + } + } + + spin_lock_irq(info->host->host_lock); + + return SCpnt->result; +} + +/* + * Error handler timeout function. Indicate that we timed out, + * and wake up any error handler process so it can continue. + */ +static void fas216_eh_timer(unsigned long data) +{ + FAS216_Info *info = (FAS216_Info *)data; + + fas216_log(info, LOG_ERROR, "error handling timed out\n"); + + del_timer(&info->eh_timer); + + if (info->rst_bus_status == 0) + info->rst_bus_status = -1; + if (info->rst_dev_status == 0) + info->rst_dev_status = -1; + + wake_up(&info->eh_wait); +} + +enum res_find { + res_failed, /* not found */ + res_success, /* command on issue queue */ + res_hw_abort /* command on disconnected dev */ +}; + +/** + * fas216_do_abort - decide how to abort a command + * @SCpnt: command to abort + * + * Decide how to abort a command. + * Returns: abort status + */ +static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + enum res_find res = res_failed; + + if (queue_remove_cmd(&info->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the device nor the + * interface know about the command. + */ + printk("on issue queue "); + + res = res_success; + } else if (queue_remove_cmd(&info->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. We must + * reconnect with the device if possible, and send it + * an abort message. + */ + printk("on disconnected queue "); + + res = res_hw_abort; + } else if (info->SCpnt == SCpnt) { + printk("executing "); + + switch (info->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. + */ + case PHASE_IDLE: + if (info->scsi.disconnectable) { + info->scsi.disconnectable = 0; + info->SCpnt = NULL; + res = res_hw_abort; + } + break; + + default: + break; + } + } else if (info->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + info->origSCpnt = NULL; + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + printk("waiting for execution "); + res = res_success; + } else + printk("unknown "); + + return res; +} + +/** + * fas216_eh_abort - abort this command + * @SCpnt: command to abort + * + * Abort this command. + * Returns: FAILED if unable to abort + * Notes: io_request_lock is taken, and irqs are disabled + */ +int fas216_eh_abort(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + int result = FAILED; + + fas216_checkmagic(info); + + info->stats.aborts += 1; + + printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no); + print_command(SCpnt->data_cmnd); + + print_debug_list(); + fas216_dumpstate(info); + + printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt); + + switch (fas216_find_command(info, SCpnt)) { + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: + printk("success\n"); + result = SUCCESS; + break; + + /* + * We need to reconnect to the target and send it an + * ABORT or ABORT_TAG message. We can only do this + * if the bus is free. + */ + case res_hw_abort: + + + /* + * We are unable to abort the command for some reason. + */ + default: + case res_failed: + printk("failed\n"); + break; + } + + return result; +} + +/** + * fas216_eh_device_reset - Reset the device associated with this command + * @SCpnt: command specifing device to reset + * + * Reset the device associated with this command. + * Returns: FAILED if unable to reset. + * Notes: We won't be re-entered, so we'll only have one device + * reset on the go at one time. + */ +int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + unsigned long flags; + int i, res = FAILED, target = SCpnt->device->id; + + fas216_log(info, LOG_ERROR, "device reset for target %d", target); + + spin_lock_irqsave(&info->host_lock, flags); + + do { + /* + * If we are currently connected to a device, and + * it is the device we want to reset, there is + * nothing we can do here. Chances are it is stuck, + * and we need a bus reset. + */ + if (info->SCpnt && !info->scsi.disconnectable && + info->SCpnt->device->id == SCpnt->device->id) + break; + + /* + * We're going to be resetting this device. Remove + * all pending commands from the driver. By doing + * so, we guarantee that we won't touch the command + * structures except to process the reset request. + */ + queue_remove_all_target(&info->queues.issue, target); + queue_remove_all_target(&info->queues.disconnected, target); + if (info->origSCpnt && info->origSCpnt->device->id == target) + info->origSCpnt = NULL; + if (info->reqSCpnt && info->reqSCpnt->device->id == target) + info->reqSCpnt = NULL; + for (i = 0; i < 8; i++) + clear_bit(target * 8 + i, info->busyluns); + + /* + * Hijack this SCSI command structure to send + * a bus device reset message to this device. + */ + SCpnt->host_scribble = (void *)fas216_devicereset_done; + + info->rst_dev_status = 0; + info->rstSCpnt = SCpnt; + + if (info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + + mod_timer(&info->eh_timer, 30 * HZ); + spin_unlock_irqrestore(&info->host_lock, flags); + + /* + * Wait up to 30 seconds for the reset to complete. + */ + wait_event(info->eh_wait, info->rst_dev_status); + + del_timer_sync(&info->eh_timer); + spin_lock_irqsave(&info->host_lock, flags); + info->rstSCpnt = NULL; + + if (info->rst_dev_status == 1) + res = SUCCESS; + } while (0); + + SCpnt->host_scribble = NULL; + spin_unlock_irqrestore(&info->host_lock, flags); + + fas216_log(info, LOG_ERROR, "device reset complete: %s\n", + res == SUCCESS ? "success" : "failed"); + + return res; +} + +/** + * fas216_eh_bus_reset - Reset the bus associated with the command + * @SCpnt: command specifing bus to reset + * + * Reset the bus associated with the command. + * Returns: FAILED if unable to reset. + * Notes: Further commands are blocked. + */ +int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + unsigned long flags; + Scsi_Device *SDpnt; + + fas216_checkmagic(info); + fas216_log(info, LOG_ERROR, "resetting bus"); + + info->stats.bus_resets += 1; + + spin_lock_irqsave(&info->host_lock, flags); + + /* + * Stop all activity on this interface. + */ + fas216_aborttransfer(info); + fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); + + /* + * Clear any pending interrupts. + */ + while (fas216_readb(info, REG_STAT) & STAT_INT) + fas216_readb(info, REG_INST); + + info->rst_bus_status = 0; + + /* + * For each attached hard-reset device, clear out + * all command structures. Leave the running + * command in place. + */ + list_for_each_entry(SDpnt, &info->host->my_devices, siblings) { + int i; + + if (SDpnt->soft_reset) + continue; + + queue_remove_all_target(&info->queues.issue, SDpnt->id); + queue_remove_all_target(&info->queues.disconnected, SDpnt->id); + if (info->origSCpnt && info->origSCpnt->device->id == SDpnt->id) + info->origSCpnt = NULL; + if (info->reqSCpnt && info->reqSCpnt->device->id == SDpnt->id) + info->reqSCpnt = NULL; + info->SCpnt = NULL; + + for (i = 0; i < 8; i++) + clear_bit(SDpnt->id * 8 + i, info->busyluns); + } + + info->scsi.phase = PHASE_IDLE; + + /* + * Reset the SCSI bus. Device cleanup happens in + * the interrupt handler. + */ + fas216_cmd(info, CMD_RESETSCSI); + + mod_timer(&info->eh_timer, jiffies + HZ); + spin_unlock_irqrestore(&info->host_lock, flags); + + /* + * Wait one second for the interrupt. + */ + wait_event(info->eh_wait, info->rst_bus_status); + del_timer_sync(&info->eh_timer); + + fas216_log(info, LOG_ERROR, "bus reset complete: %s\n", + info->rst_bus_status == 1 ? "success" : "failed"); + + return info->rst_bus_status == 1 ? SUCCESS : FAILED; +} + +/** + * fas216_init_chip - Initialise FAS216 state after reset + * @info: state structure for interface + * + * Initialise FAS216 state after reset + */ +static void fas216_init_chip(FAS216_Info *info) +{ + unsigned int clock = ((info->ifcfg.clockrate - 1) / 5 + 1) & 7; + fas216_writeb(info, REG_CLKF, clock); + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]); + fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); + fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); + fas216_writeb(info, REG_SOF, 0); + fas216_writeb(info, REG_STP, info->scsi.async_stp); + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); +} + +/** + * fas216_eh_host_reset - Reset the host associated with this command + * @SCpnt: command specifing host to reset + * + * Reset the host associated with this command. + * Returns: FAILED if unable to reset. + * Notes: io_request_lock is taken, and irqs are disabled + */ +int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + + fas216_checkmagic(info); + + printk("scsi%d.%c: %s: resetting host\n", + info->host->host_no, '0' + SCpnt->device->id, __FUNCTION__); + + /* + * Reset the SCSI chip. + */ + fas216_cmd(info, CMD_RESETCHIP); + + /* + * Ugly ugly ugly! + * We need to release the host_lock and enable + * IRQs if we sleep, but we must relock and disable + * IRQs after the sleep. + */ + spin_unlock_irq(info->host->host_lock); + scsi_sleep(50 * HZ/100); + spin_lock_irq(info->host->host_lock); + + /* + * Release the SCSI reset. + */ + fas216_cmd(info, CMD_NOP); + + fas216_init_chip(info); + + return SUCCESS; +} + +#define TYPE_UNKNOWN 0 +#define TYPE_NCR53C90 1 +#define TYPE_NCR53C90A 2 +#define TYPE_NCR53C9x 3 +#define TYPE_Am53CF94 4 +#define TYPE_EmFAS216 5 +#define TYPE_QLFAS216 6 + +static char *chip_types[] = { + "unknown", + "NS NCR53C90", + "NS NCR53C90A", + "NS NCR53C9x", + "AMD Am53CF94", + "Emulex FAS216", + "QLogic FAS216" +}; + +static int fas216_detect_type(FAS216_Info *info) +{ + int family, rev; + + /* + * Reset the chip. + */ + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + udelay(50); + fas216_writeb(info, REG_CMD, CMD_NOP); + + /* + * Check to see if control reg 2 is present. + */ + fas216_writeb(info, REG_CNTL3, 0); + fas216_writeb(info, REG_CNTL2, CNTL2_S2FE); + + /* + * If we are unable to read back control reg 2 + * correctly, it is not present, and we have a + * NCR53C90. + */ + if ((fas216_readb(info, REG_CNTL2) & (~0xe0)) != CNTL2_S2FE) + return TYPE_NCR53C90; + + /* + * Now, check control register 3 + */ + fas216_writeb(info, REG_CNTL2, 0); + fas216_writeb(info, REG_CNTL3, 0); + fas216_writeb(info, REG_CNTL3, 5); + + /* + * If we are unable to read the register back + * correctly, we have a NCR53C90A + */ + if (fas216_readb(info, REG_CNTL3) != 5) + return TYPE_NCR53C90A; + + /* + * Now read the ID from the chip. + */ + fas216_writeb(info, REG_CNTL3, 0); + + fas216_writeb(info, REG_CNTL3, CNTL3_ADIDCHK); + fas216_writeb(info, REG_CNTL3, 0); + + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + udelay(50); + fas216_writeb(info, REG_CMD, CMD_WITHDMA | CMD_NOP); + + fas216_writeb(info, REG_CNTL2, CNTL2_ENF); + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + udelay(50); + fas216_writeb(info, REG_CMD, CMD_NOP); + + rev = fas216_readb(info, REG_ID); + family = rev >> 3; + rev &= 7; + + switch (family) { + case 0x01: + if (rev == 4) + return TYPE_Am53CF94; + break; + + case 0x02: + switch (rev) { + case 2: + return TYPE_EmFAS216; + case 3: + return TYPE_QLFAS216; + } + break; + + default: + break; + } + printk("family %x rev %x\n", family, rev); + return TYPE_NCR53C9x; +} + +/** + * fas216_reset_state - Initialise driver internal state + * @info: state to initialise + * + * Initialise driver internal state + */ +static void fas216_reset_state(FAS216_Info *info) +{ + int i; + + fas216_checkmagic(info); + + fas216_bus_reset(info); + + /* + * Clear out all stale info in our state structure + */ + memset(info->busyluns, 0, sizeof(info->busyluns)); + info->scsi.disconnectable = 0; + info->scsi.aborting = 0; + + for (i = 0; i < 8; i++) { + info->device[i].parity_enabled = 0; + info->device[i].parity_check = 1; + } + + /* + * Drain all commands on disconnected queue + */ + while (queue_remove(&info->queues.disconnected) != NULL); + + /* + * Remove executing commands. + */ + info->SCpnt = NULL; + info->reqSCpnt = NULL; + info->rstSCpnt = NULL; + info->origSCpnt = NULL; +} + +/** + * fas216_init - initialise FAS/NCR/AMD SCSI structures. + * @host: a driver-specific filled-out structure + * + * Initialise FAS/NCR/AMD SCSI structures. + * Returns: 0 on success + */ +int fas216_init(struct Scsi_Host *host) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + + info->magic_start = MAGIC; + info->magic_end = MAGIC; + info->host = host; + info->scsi.cfg[0] = host->this_id | CNTL1_PERE; + info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE; + info->scsi.cfg[2] = info->ifcfg.cntl3 | + CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM; + info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); + + info->rst_dev_status = -1; + info->rst_bus_status = -1; + init_waitqueue_head(&info->eh_wait); + init_timer(&info->eh_timer); + info->eh_timer.data = (unsigned long)info; + info->eh_timer.function = fas216_eh_timer; + + spin_lock_init(&info->host_lock); + + memset(&info->stats, 0, sizeof(info->stats)); + + msgqueue_initialise(&info->scsi.msgs); + + if (!queue_initialise(&info->queues.issue)) + return -ENOMEM; + + if (!queue_initialise(&info->queues.disconnected)) { + queue_free(&info->queues.issue); + return -ENOMEM; + } + + return 0; +} + +/** + * fas216_add - initialise FAS/NCR/AMD SCSI ic. + * @host: a driver-specific filled-out structure + * @dev: parent device + * + * Initialise FAS/NCR/AMD SCSI ic. + * Returns: 0 on success + */ +int fas216_add(struct Scsi_Host *host, struct device *dev) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + int type, ret; + + if (info->ifcfg.clockrate <= 10 || info->ifcfg.clockrate > 40) { + printk(KERN_CRIT "fas216: invalid clock rate %u MHz\n", + info->ifcfg.clockrate); + return -EINVAL; + } + + fas216_reset_state(info); + type = fas216_detect_type(info); + info->scsi.type = chip_types[type]; + + udelay(300); + + /* + * Initialise the chip correctly. + */ + fas216_init_chip(info); + + /* + * Reset the SCSI bus. We don't want to see + * the resulting reset interrupt, so mask it + * out. + */ + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_DISR); + fas216_writeb(info, REG_CMD, CMD_RESETSCSI); + + /* + * scsi standard says wait 250ms + */ + spin_unlock_irq(info->host->host_lock); + scsi_sleep(100*HZ/100); + spin_lock_irq(info->host->host_lock); + + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + fas216_readb(info, REG_INST); + + fas216_checkmagic(info); + + ret = scsi_add_host(host, dev); + if (ret) + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + + return ret; +} + +void fas216_remove(struct Scsi_Host *host) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + + fas216_checkmagic(info); + scsi_remove_host(host); + + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); +} + +/** + * fas216_release - release all resources for FAS/NCR/AMD SCSI ic. + * @host: a driver-specific filled-out structure + * + * release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. + */ +void fas216_release(struct Scsi_Host *host) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + + queue_free(&info->queues.disconnected); + queue_free(&info->queues.issue); +} + +int fas216_print_host(FAS216_Info *info, char *buffer) +{ + return sprintf(buffer, + "\n" + "Chip : %s\n" + " Address: 0x%08lx\n" + " IRQ : %d\n" + " DMA : %d\n", + info->scsi.type, info->host->io_port, + info->host->irq, info->host->dma_channel); +} + +int fas216_print_stats(FAS216_Info *info, char *buffer) +{ + char *p = buffer; + + p += sprintf(p, "\n" + "Command Statistics:\n" + " Queued : %u\n" + " Issued : %u\n" + " Completed : %u\n" + " Reads : %u\n" + " Writes : %u\n" + " Others : %u\n" + " Disconnects: %u\n" + " Aborts : %u\n" + " Bus resets : %u\n" + " Host resets: %u\n", + info->stats.queues, info->stats.removes, + info->stats.fins, info->stats.reads, + info->stats.writes, info->stats.miscs, + info->stats.disconnects, info->stats.aborts, + info->stats.bus_resets, info->stats.host_resets); + + return p - buffer; +} + +int fas216_print_devices(FAS216_Info *info, char *buffer) +{ + struct fas216_device *dev; + Scsi_Device *scd; + char *p = buffer; + + p += sprintf(p, "Device/Lun TaggedQ Parity Sync\n"); + + list_for_each_entry(scd, &info->host->my_devices, siblings) { + dev = &info->device[scd->id]; + p += sprintf(p, " %d/%d ", scd->id, scd->lun); + if (scd->tagged_supported) + p += sprintf(p, "%3sabled(%3d) ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + else + p += sprintf(p, "unsupported "); + + p += sprintf(p, "%3sabled ", dev->parity_enabled ? "en" : "dis"); + + if (dev->sof) + p += sprintf(p, "offset %d, %d ns\n", + dev->sof, dev->period * 4); + else + p += sprintf(p, "async\n"); + } + + return p - buffer; +} + +EXPORT_SYMBOL(fas216_init); +EXPORT_SYMBOL(fas216_add); +EXPORT_SYMBOL(fas216_queue_command); +EXPORT_SYMBOL(fas216_command); +EXPORT_SYMBOL(fas216_intr); +EXPORT_SYMBOL(fas216_remove); +EXPORT_SYMBOL(fas216_release); +EXPORT_SYMBOL(fas216_eh_abort); +EXPORT_SYMBOL(fas216_eh_device_reset); +EXPORT_SYMBOL(fas216_eh_bus_reset); +EXPORT_SYMBOL(fas216_eh_host_reset); +EXPORT_SYMBOL(fas216_print_host); +EXPORT_SYMBOL(fas216_print_stats); +EXPORT_SYMBOL(fas216_print_devices); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/scsi/arm/fas216.h linux-2.5.69-bk14/drivers/scsi/arm/fas216.h --- linux-2.5.69-bk13/drivers/scsi/arm/fas216.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/fas216.h 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,394 @@ +/* + * linux/drivers/acorn/scsi/fas216.h + * + * Copyright (C) 1997-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * FAS216 generic driver + */ +#ifndef FAS216_H +#define FAS216_H + +#ifndef NO_IRQ +#define NO_IRQ 255 +#endif + +#include "queue.h" +#include "msgqueue.h" + +/* FAS register definitions */ + +/* transfer count low */ +#define REG_CTCL (0) +#define REG_STCL (0) + +/* transfer count medium */ +#define REG_CTCM (1) +#define REG_STCM (1) + +/* fifo data */ +#define REG_FF (2) + +/* command */ +#define REG_CMD (3) +#define CMD_NOP 0x00 +#define CMD_FLUSHFIFO 0x01 +#define CMD_RESETCHIP 0x02 +#define CMD_RESETSCSI 0x03 + +#define CMD_TRANSFERINFO 0x10 +#define CMD_INITCMDCOMPLETE 0x11 +#define CMD_MSGACCEPTED 0x12 +#define CMD_PADBYTES 0x18 +#define CMD_SETATN 0x1a +#define CMD_RSETATN 0x1b + +#define CMD_SELECTWOATN 0x41 +#define CMD_SELECTATN 0x42 +#define CMD_SELECTATNSTOP 0x43 +#define CMD_ENABLESEL 0x44 +#define CMD_DISABLESEL 0x45 +#define CMD_SELECTATN3 0x46 +#define CMD_RESEL3 0x47 + +#define CMD_WITHDMA 0x80 + +/* status register (read) */ +#define REG_STAT (4) +#define STAT_IO (1 << 0) /* IO phase */ +#define STAT_CD (1 << 1) /* CD phase */ +#define STAT_MSG (1 << 2) /* MSG phase */ +#define STAT_TRANSFERDONE (1 << 3) /* Transfer completed */ +#define STAT_TRANSFERCNTZ (1 << 4) /* Transfer counter is zero */ +#define STAT_PARITYERROR (1 << 5) /* Parity error */ +#define STAT_REALBAD (1 << 6) /* Something bad */ +#define STAT_INT (1 << 7) /* Interrupt */ + +#define STAT_BUSMASK (STAT_MSG|STAT_CD|STAT_IO) +#define STAT_DATAOUT (0) /* Data out */ +#define STAT_DATAIN (STAT_IO) /* Data in */ +#define STAT_COMMAND (STAT_CD) /* Command out */ +#define STAT_STATUS (STAT_CD|STAT_IO) /* Status In */ +#define STAT_MESGOUT (STAT_MSG|STAT_CD) /* Message out */ +#define STAT_MESGIN (STAT_MSG|STAT_CD|STAT_IO) /* Message In */ + +/* bus ID for select / reselect */ +#define REG_SDID (4) +#define BUSID(target) ((target) & 7) + +/* Interrupt status register (read) */ +#define REG_INST (5) +#define INST_SELWOATN (1 << 0) /* Select w/o ATN */ +#define INST_SELATN (1 << 1) /* Select w/ATN */ +#define INST_RESELECTED (1 << 2) /* Reselected */ +#define INST_FUNCDONE (1 << 3) /* Function done */ +#define INST_BUSSERVICE (1 << 4) /* Bus service */ +#define INST_DISCONNECT (1 << 5) /* Disconnect */ +#define INST_ILLEGALCMD (1 << 6) /* Illegal command */ +#define INST_BUSRESET (1 << 7) /* SCSI Bus reset */ + +/* Timeout register (write) */ +#define REG_STIM (5) + +/* Sequence step register (read) */ +#define REG_IS (6) +#define IS_BITS 0x07 +#define IS_SELARB 0x00 /* Select & Arb ok */ +#define IS_MSGBYTESENT 0x01 /* One byte message sent*/ +#define IS_NOTCOMMAND 0x02 /* Not in command state */ +#define IS_EARLYPHASE 0x03 /* Early phase change */ +#define IS_COMPLETE 0x04 /* Command ok */ +#define IS_SOF 0x08 /* Sync off flag */ + +/* Transfer period step (write) */ +#define REG_STP (6) + +/* Synchronous Offset (write) */ +#define REG_SOF (7) + +/* Fifo state register (read) */ +#define REG_CFIS (7) +#define CFIS_CF 0x1f /* Num bytes in FIFO */ +#define CFIS_IS 0xe0 /* Step */ + +/* config register 1 */ +#define REG_CNTL1 (8) +#define CNTL1_CID (7 << 0) /* Chip ID */ +#define CNTL1_STE (1 << 3) /* Self test enable */ +#define CNTL1_PERE (1 << 4) /* Parity enable reporting en. */ +#define CNTL1_PTE (1 << 5) /* Parity test enable */ +#define CNTL1_DISR (1 << 6) /* Disable Irq on SCSI reset */ +#define CNTL1_ETM (1 << 7) /* Extended Timing Mode */ + +/* Clock conversion factor (read) */ +#define REG_CLKF (9) +#define CLKF_F37MHZ 0x00 /* 35.01 - 40 MHz */ +#define CLKF_F10MHZ 0x02 /* 10 MHz */ +#define CLKF_F12MHZ 0x03 /* 10.01 - 15 MHz */ +#define CLKF_F17MHZ 0x04 /* 15.01 - 20 MHz */ +#define CLKF_F22MHZ 0x05 /* 20.01 - 25 MHz */ +#define CLKF_F27MHZ 0x06 /* 25.01 - 30 MHz */ +#define CLKF_F32MHZ 0x07 /* 30.01 - 35 MHz */ + +/* Chip test register (write) */ +#define REG_FTM (10) +#define TEST_FTM 0x01 /* Force target mode */ +#define TEST_FIM 0x02 /* Force initiator mode */ +#define TEST_FHI 0x04 /* Force high impedance mode */ + +/* Configuration register 2 (read/write) */ +#define REG_CNTL2 (11) +#define CNTL2_PGDP (1 << 0) /* Pass Th/Generate Data Parity */ +#define CNTL2_PGRP (1 << 1) /* Pass Th/Generate Reg Parity */ +#define CNTL2_ACDPE (1 << 2) /* Abort on Cmd/Data Parity Err */ +#define CNTL2_S2FE (1 << 3) /* SCSI2 Features Enable */ +#define CNTL2_TSDR (1 << 4) /* Tristate DREQ */ +#define CNTL2_SBO (1 << 5) /* Select Byte Order */ +#define CNTL2_ENF (1 << 6) /* Enable features */ +#define CNTL2_DAE (1 << 7) /* Data Alignment Enable */ + +/* Configuration register 3 (read/write) */ +#define REG_CNTL3 (12) +#define CNTL3_BS8 (1 << 0) /* Burst size 8 */ +#define CNTL3_MDM (1 << 1) /* Modify DMA mode */ +#define CNTL3_LBTM (1 << 2) /* Last Byte Transfer mode */ +#define CNTL3_FASTCLK (1 << 3) /* Fast SCSI clocking */ +#define CNTL3_FASTSCSI (1 << 4) /* Fast SCSI */ +#define CNTL3_G2CB (1 << 5) /* Group2 SCSI support */ +#define CNTL3_QTAG (1 << 6) /* Enable 3 byte msgs */ +#define CNTL3_ADIDCHK (1 << 7) /* Additional ID check */ + +/* High transfer count (read/write) */ +#define REG_CTCH (14) +#define REG_STCH (14) + +/* ID register (read only) */ +#define REG_ID (14) + +/* Data alignment */ +#define REG_DAL (15) + +typedef enum { + PHASE_IDLE, /* we're not planning on doing anything */ + PHASE_SELECTION, /* selecting a device */ + PHASE_SELSTEPS, /* selection with command steps */ + PHASE_COMMAND, /* command sent */ + PHASE_MESSAGESENT, /* selected, and we're sending cmd */ + PHASE_DATAOUT, /* data out to device */ + PHASE_DATAIN, /* data in from device */ + PHASE_MSGIN, /* message in from device */ + PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */ + PHASE_MSGOUT, /* after message out phase */ + PHASE_MSGOUT_EXPECT, /* expecting message out */ + PHASE_STATUS, /* status from device */ + PHASE_DONE /* Command complete */ +} phase_t; + +typedef enum { + DMA_OUT, /* DMA from memory to chip */ + DMA_IN /* DMA from chip to memory */ +} fasdmadir_t; + +typedef enum { + fasdma_none, /* No dma */ + fasdma_pio, /* PIO mode */ + fasdma_pseudo, /* Pseudo DMA */ + fasdma_real_block, /* Real DMA, on block by block basis */ + fasdma_real_all /* Real DMA, on request by request */ +} fasdmatype_t; + +typedef enum { + neg_wait, /* Negociate with device */ + neg_inprogress, /* Negociation sent */ + neg_complete, /* Negociation complete */ + neg_targcomplete, /* Target completed negociation */ + neg_invalid /* Negociation not supported */ +} neg_t; + +#define MAGIC 0x441296bdUL +#define NR_MSGS 8 + +#define FASCAP_DMA (1 << 0) +#define FASCAP_PSEUDODMA (1 << 1) + +typedef struct { + unsigned long magic_start; + spinlock_t host_lock; + struct Scsi_Host *host; /* host */ + Scsi_Cmnd *SCpnt; /* currently processing command */ + Scsi_Cmnd *origSCpnt; /* original connecting command */ + Scsi_Cmnd *reqSCpnt; /* request sense command */ + Scsi_Cmnd *rstSCpnt; /* reset command */ + Scsi_Cmnd *pending_SCpnt[8]; /* per-device pending commands */ + int next_pending; /* next pending device */ + + /* + * Error recovery + */ + wait_queue_head_t eh_wait; + struct timer_list eh_timer; + unsigned int rst_dev_status; + unsigned int rst_bus_status; + + /* driver information */ + struct { + phase_t phase; /* current phase */ + void *io_base; /* iomem base of FAS216 */ + unsigned int io_port; /* base address of FAS216 */ + unsigned int io_shift; /* shift to adjust reg offsets by */ + unsigned char cfg[4]; /* configuration registers */ + const char *type; /* chip type */ + unsigned int irq; /* interrupt */ + + Scsi_Pointer SCp; /* current commands data pointer */ + + MsgQueue_t msgs; /* message queue for connected device */ + + unsigned int async_stp; /* Async transfer STP value */ + unsigned char msgin_fifo; /* bytes in fifo at time of message in */ + unsigned char message[256]; /* last message received from device */ + + unsigned char disconnectable:1; /* this command can be disconnected */ + unsigned char aborting:1; /* aborting command */ + } scsi; + + /* statistics information */ + struct { + unsigned int queues; + unsigned int removes; + unsigned int fins; + unsigned int reads; + unsigned int writes; + unsigned int miscs; + unsigned int disconnects; + unsigned int aborts; + unsigned int bus_resets; + unsigned int host_resets; + } stats; + + /* configuration information */ + struct { + unsigned char clockrate; /* clock rate of FAS device (MHz) */ + unsigned char select_timeout; /* timeout (R5) */ + unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */ + unsigned char wide_max_size; /* Maximum wide transfer size */ + unsigned char cntl3; /* Control Reg 3 */ + unsigned int asyncperiod; /* Async transfer period (ns) */ + unsigned int capabilities; /* driver capabilities */ + unsigned int disconnect_ok:1; /* Disconnects allowed? */ + } ifcfg; + + /* queue handling */ + struct { + Queue_t issue; /* issue queue */ + Queue_t disconnected; /* disconnected command queue */ + } queues; + + /* per-device info */ + struct fas216_device { + unsigned char disconnect_ok:1; /* device can disconnect */ + unsigned char parity_enabled:1; /* parity checking enabled */ + unsigned char parity_check:1; /* need to check parity checking */ + unsigned char period; /* sync xfer period in (*4ns) */ + unsigned char stp; /* synchronous transfer period */ + unsigned char sof; /* synchronous offset register */ + unsigned char wide_xfer; /* currently negociated wide transfer */ + neg_t sync_state; /* synchronous transfer mode */ + neg_t wide_state; /* wide transfer mode */ + } device[8]; + unsigned long busyluns[64/sizeof(unsigned long)];/* array of bits indicating LUNs busy */ + + /* dma */ + struct { + fasdmatype_t transfer_type; /* current type of DMA transfer */ + fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma); + void (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer); + void (*stop) (struct Scsi_Host *host, Scsi_Pointer *SCp); + } dma; + + /* miscellaneous */ + int internal_done; /* flag to indicate request done */ + + unsigned long magic_end; +} FAS216_Info; + +/* Function: int fas216_init (struct Scsi_Host *instance) + * Purpose : initialise FAS/NCR/AMD SCSI structures. + * Params : instance - a driver-specific filled-out structure + * Returns : 0 on success + */ +extern int fas216_init (struct Scsi_Host *instance); + +/* Function: int fas216_add (struct Scsi_Host *instance, struct device *dev) + * Purpose : initialise FAS/NCR/AMD SCSI ic. + * Params : instance - a driver-specific filled-out structure + * Returns : 0 on success + */ +extern int fas216_add (struct Scsi_Host *instance, struct device *dev); + +/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + * Purpose : queue a command for adapter to process. + * Params : SCpnt - Command to queue + * done - done function to call once command is complete + * Returns : 0 - success, else error + */ +extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); + +/* Function: int fas216_command (Scsi_Cmnd *SCpnt) + * Purpose : queue a command for adapter to process. + * Params : SCpnt - Command to queue + * Returns : scsi result code + */ +extern int fas216_command (Scsi_Cmnd *); + +/* Function: irqreturn_t fas216_intr (FAS216_Info *info) + * Purpose : handle interrupts from the interface to progress a command + * Params : info - interface to service + */ +extern irqreturn_t fas216_intr (FAS216_Info *info); + +extern void fas216_remove (struct Scsi_Host *instance); + +/* Function: void fas216_release (struct Scsi_Host *instance) + * Purpose : release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. + * Params : instance - a driver-specific filled-out structure + * Returns : 0 on success + */ +extern void fas216_release (struct Scsi_Host *instance); + +extern int fas216_print_host(FAS216_Info *info, char *buffer); +extern int fas216_print_stats(FAS216_Info *info, char *buffer); +extern int fas216_print_devices(FAS216_Info *info, char *buffer); + +/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort this command + * Params : SCpnt - command to abort + * Returns : FAILED if unable to abort + */ +extern int fas216_eh_abort(Scsi_Cmnd *SCpnt); + +/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the device associated with this command + * Params : SCpnt - command specifing device to reset + * Returns : FAILED if unable to reset + */ +extern int fas216_eh_device_reset(Scsi_Cmnd *SCpnt); + +/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the complete bus associated with this command + * Params : SCpnt - command specifing bus to reset + * Returns : FAILED if unable to reset + */ +extern int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt); + +/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the host associated with this command + * Params : SCpnt - command specifing host to reset + * Returns : FAILED if unable to reset + */ +extern int fas216_eh_host_reset(Scsi_Cmnd *SCpnt); + +#endif /* FAS216_H */ diff -urN linux-2.5.69-bk13/drivers/scsi/arm/msgqueue.c linux-2.5.69-bk14/drivers/scsi/arm/msgqueue.c --- linux-2.5.69-bk13/drivers/scsi/arm/msgqueue.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/msgqueue.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,171 @@ +/* + * linux/drivers/acorn/scsi/msgqueue.c + * + * Copyright (C) 1997-1998 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * message queue handling + */ +#include +#include +#include +#include + +#include "msgqueue.h" + +/* + * Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) + * Purpose : Allocate a message queue entry + * Params : msgq - message queue to claim entry for + * Returns : message queue entry or NULL. + */ +static struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) +{ + struct msgqueue_entry *mq; + + if ((mq = msgq->free) != NULL) + msgq->free = mq->next; + + return mq; +} + +/* + * Function: void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) + * Purpose : free a message queue entry + * Params : msgq - message queue to free entry from + * mq - message queue entry to free + */ +static void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) +{ + if (mq) { + mq->next = msgq->free; + msgq->free = mq; + } +} + +/* + * Function: void msgqueue_initialise(MsgQueue_t *msgq) + * Purpose : initialise a message queue + * Params : msgq - queue to initialise + */ +void msgqueue_initialise(MsgQueue_t *msgq) +{ + int i; + + msgq->qe = NULL; + msgq->free = &msgq->entries[0]; + + for (i = 0; i < NR_MESSAGES; i++) + msgq->entries[i].next = &msgq->entries[i + 1]; + + msgq->entries[NR_MESSAGES - 1].next = NULL; +} + + +/* + * Function: void msgqueue_free(MsgQueue_t *msgq) + * Purpose : free a queue + * Params : msgq - queue to free + */ +void msgqueue_free(MsgQueue_t *msgq) +{ +} + +/* + * Function: int msgqueue_msglength(MsgQueue_t *msgq) + * Purpose : calculate the total length of all messages on the message queue + * Params : msgq - queue to examine + * Returns : number of bytes of messages in queue + */ +int msgqueue_msglength(MsgQueue_t *msgq) +{ + struct msgqueue_entry *mq = msgq->qe; + int length = 0; + + for (mq = msgq->qe; mq; mq = mq->next) + length += mq->msg.length; + + return length; +} + +/* + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) + * Purpose : return a message + * Params : msgq - queue to obtain message from + * : msgno - message number + * Returns : pointer to message string, or NULL + */ +struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) +{ + struct msgqueue_entry *mq; + + for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--); + + return mq ? &mq->msg : NULL; +} + +/* + * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) + * Purpose : add a message onto a message queue + * Params : msgq - queue to add message on + * length - length of message + * ... - message bytes + * Returns : != 0 if successful + */ +int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) +{ + struct msgqueue_entry *mq = mqe_alloc(msgq); + va_list ap; + + if (mq) { + struct msgqueue_entry **mqp; + int i; + + va_start(ap, length); + for (i = 0; i < length; i++) + mq->msg.msg[i] = va_arg(ap, unsigned int); + va_end(ap); + + mq->msg.length = length; + mq->msg.fifo = 0; + mq->next = NULL; + + mqp = &msgq->qe; + while (*mqp) + mqp = &(*mqp)->next; + + *mqp = mq; + } + + return mq != NULL; +} + +/* + * Function: void msgqueue_flush(MsgQueue_t *msgq) + * Purpose : flush all messages from message queue + * Params : msgq - queue to flush + */ +void msgqueue_flush(MsgQueue_t *msgq) +{ + struct msgqueue_entry *mq, *mqnext; + + for (mq = msgq->qe; mq; mq = mqnext) { + mqnext = mq->next; + mqe_free(msgq, mq); + } + msgq->qe = NULL; +} + +EXPORT_SYMBOL(msgqueue_initialise); +EXPORT_SYMBOL(msgqueue_free); +EXPORT_SYMBOL(msgqueue_msglength); +EXPORT_SYMBOL(msgqueue_getmsg); +EXPORT_SYMBOL(msgqueue_addmsg); +EXPORT_SYMBOL(msgqueue_flush); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SCSI message queue handling"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/scsi/arm/msgqueue.h linux-2.5.69-bk14/drivers/scsi/arm/msgqueue.h --- linux-2.5.69-bk13/drivers/scsi/arm/msgqueue.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/msgqueue.h 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,82 @@ +/* + * linux/drivers/acorn/scsi/msgqueue.h + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * message queue handling + */ +#ifndef MSGQUEUE_H +#define MSGQUEUE_H + +struct message { + char msg[8]; + int length; + int fifo; +}; + +struct msgqueue_entry { + struct message msg; + struct msgqueue_entry *next; +}; + +#define NR_MESSAGES 4 + +typedef struct { + struct msgqueue_entry *qe; + struct msgqueue_entry *free; + struct msgqueue_entry entries[NR_MESSAGES]; +} MsgQueue_t; + +/* + * Function: void msgqueue_initialise(MsgQueue_t *msgq) + * Purpose : initialise a message queue + * Params : msgq - queue to initialise + */ +extern void msgqueue_initialise(MsgQueue_t *msgq); + +/* + * Function: void msgqueue_free(MsgQueue_t *msgq) + * Purpose : free a queue + * Params : msgq - queue to free + */ +extern void msgqueue_free(MsgQueue_t *msgq); + +/* + * Function: int msgqueue_msglength(MsgQueue_t *msgq) + * Purpose : calculate the total length of all messages on the message queue + * Params : msgq - queue to examine + * Returns : number of bytes of messages in queue + */ +extern int msgqueue_msglength(MsgQueue_t *msgq); + +/* + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) + * Purpose : return a message & its length + * Params : msgq - queue to obtain message from + * : msgno - message number + * Returns : pointer to message string, or NULL + */ +extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno); + +/* + * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) + * Purpose : add a message onto a message queue + * Params : msgq - queue to add message on + * length - length of message + * ... - message bytes + * Returns : != 0 if successful + */ +extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...); + +/* + * Function: void msgqueue_flush(MsgQueue_t *msgq) + * Purpose : flush all messages from message queue + * Params : msgq - queue to flush + */ +extern void msgqueue_flush(MsgQueue_t *msgq); + +#endif diff -urN linux-2.5.69-bk13/drivers/scsi/arm/oak.c linux-2.5.69-bk14/drivers/scsi/arm/oak.c --- linux-2.5.69-bk13/drivers/scsi/arm/oak.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/oak.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,215 @@ +/* + * Oak Generic NCR5380 driver + * + * Copyright 1995-2002, Russell King + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" + +#define AUTOSENSE +/*#define PSEUDO_DMA*/ + +#define OAKSCSI_PUBLIC_RELEASE 1 + +#define NCR5380_read(reg) oakscsi_read(_instance, reg) +#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value) +#define NCR5380_intr oakscsi_intr +#define NCR5380_queue_command oakscsi_queue_command +#define NCR5380_proc_info oakscsi_proc_info + +int NCR5380_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#define NCR5380_implementation_fields int port, ctrl +#define NCR5380_local_declare() struct Scsi_Host *_instance +#define NCR5380_setup(instance) _instance = instance + +#define BOARD_NORMAL 0 +#define BOARD_NCR53C400 1 + +#include "../NCR5380.h" + +#undef START_DMA_INITIATOR_RECEIVE_REG +#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128) + +const char * oakscsi_info (struct Scsi_Host *spnt) +{ + return ""; +} + +#define STAT(p) inw(p + 144) +extern void inswb(int from, void *to, int len); + +static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, + int len) +{ + int iobase = instance->io_port; +printk("writing %p len %d\n",addr, len); + if(!len) return -1; + + while(1) + { + int status; + while(((status = STAT(iobase)) & 0x100)==0); + } +} + +static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, + int len) +{ + int iobase = instance->io_port; +printk("reading %p len %d\n", addr, len); + while(len > 0) + { + int status, timeout; + unsigned long b; + + timeout = 0x01FFFFFF; + + while(((status = STAT(iobase)) & 0x100)==0) + { + timeout--; + if(status & 0x200 || !timeout) + { + printk("status = %08X\n",status); + return 1; + } + } + if(len >= 128) + { + inswb(iobase + 136, addr, 128); + addr += 128; + len -= 128; + } + else + { + b = (unsigned long) inw(iobase + 136); + *addr ++ = b; + len -= 1; + if(len) + *addr ++ = b>>8; + len -= 1; + } + } + return 0; +} + +#define oakscsi_read(instance,reg) (inb((instance)->io_port + (reg))) +#define oakscsi_write(instance,reg,val) (outb((val), (instance)->io_port + (reg))) + +#undef STAT + +#include "../NCR5380.c" + +static Scsi_Host_Template oakscsi_template = { + .module = THIS_MODULE, + .proc_info = oakscsi_proc_info, + .name = "Oak 16-bit SCSI", + .info = oakscsi_info, + .queuecommand = oakscsi_queue_command, + .eh_abort_handler = NCR5380_abort, + .eh_device_reset_handler= NCR5380_device_reset, + .eh_bus_reset_handler = NCR5380_bus_reset, + .eh_host_reset_handler = NCR5380_host_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "oakscsi", +}; + +static int __devinit +oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + int ret = -ENOMEM; + + host = scsi_register(&oakscsi_template, sizeof(struct NCR5380_hostdata)); + if (!host) + goto out; + + host->io_port = ecard_address(ec, ECARD_MEMC, 0); + host->irq = IRQ_NONE; + host->n_io_port = 255; + + ret = -EBUSY; + if (!request_region (host->io_port, host->n_io_port, "Oak SCSI")) + goto unreg; + + NCR5380_init(host, 0); + + printk("scsi%d: at port 0x%08lx irqs disabled", + host->host_no, host->io_port); + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", + host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE); + printk("\nscsi%d:", host->host_no); + NCR5380_print_options(host); + printk("\n"); + + ret = scsi_add_host(host, &ec->dev); + if (ret == 0) + goto out; + + release_region(host->io_port, host->n_io_port); + unreg: + scsi_unregister(host); + out: + return ret; +} + +static void __devexit oakscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + scsi_remove_host(host); + + release_region(host->io_port, host->n_io_port); + scsi_unregister(host); +} + +static const struct ecard_id oakscsi_cids[] = { + { MANU_OAK, PROD_OAK_SCSI }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver oakscsi_driver = { + .probe = oakscsi_probe, + .remove = __devexit_p(oakscsi_remove), + .id_table = oakscsi_cids, + .drv = { + .name = "oakscsi", + }, +}; + +static int __init oakscsi_init(void) +{ + return ecard_register_driver(&oakscsi_driver); +} + +static void __exit oakscsi_exit(void) +{ + ecard_remove_driver(&oakscsi_driver); +} + +module_init(oakscsi_init); +module_exit(oakscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Oak SCSI driver"); +MODULE_LICENSE("GPL"); + diff -urN linux-2.5.69-bk13/drivers/scsi/arm/powertec.c linux-2.5.69-bk14/drivers/scsi/arm/powertec.c --- linux-2.5.69-bk13/drivers/scsi/arm/powertec.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/powertec.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,486 @@ +/* + * linux/drivers/acorn/scsi/powertec.c + * + * Copyright (C) 1997-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +#include + +#define POWERTEC_FAS216_OFFSET 0x3000 +#define POWERTEC_FAS216_SHIFT 6 + +#define POWERTEC_INTR_STATUS 0x2000 +#define POWERTEC_INTR_BIT 0x80 + +#define POWERTEC_RESET_CONTROL 0x1018 +#define POWERTEC_RESET_BIT 1 + +#define POWERTEC_TERM_CONTROL 0x2018 +#define POWERTEC_TERM_ENABLE 1 + +#define POWERTEC_INTR_CONTROL 0x101c +#define POWERTEC_INTR_ENABLE 1 +#define POWERTEC_INTR_DISABLE 0 + +#define VERSION "1.10 (19/01/2003 2.5.59)" + +/* + * Use term=0,1,0,0,0 to turn terminators on/off. + * One entry per slot. + */ +static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +#define NR_SG 256 + +struct powertec_info { + FAS216_Info info; + struct expansion_card *ec; + void *term_port; + unsigned int term_ctl; + struct scatterlist sg[NR_SG]; +}; + +/* Prototype: void powertecscsi_irqenable(ec, irqnr) + * Purpose : Enable interrupts on Powertec SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +powertecscsi_irqenable(struct expansion_card *ec, int irqnr) +{ + writeb(POWERTEC_INTR_ENABLE, ec->irq_data); +} + +/* Prototype: void powertecscsi_irqdisable(ec, irqnr) + * Purpose : Disable interrupts on Powertec SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +powertecscsi_irqdisable(struct expansion_card *ec, int irqnr) +{ + writeb(POWERTEC_INTR_DISABLE, ec->irq_data); +} + +static const expansioncard_ops_t powertecscsi_ops = { + .irqenable = powertecscsi_irqenable, + .irqdisable = powertecscsi_irqdisable, +}; + +/* Prototype: void powertecscsi_terminator_ctl(host, on_off) + * Purpose : Turn the Powertec SCSI terminators on or off + * Params : host - card to turn on/off + * : on_off - !0 to turn on, 0 to turn off + */ +static void +powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off) +{ + struct powertec_info *info = (struct powertec_info *)host->hostdata; + + info->term_ctl = on_off ? POWERTEC_TERM_ENABLE : 0; + writeb(info->term_ctl, info->term_port); +} + +/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs) + * Purpose : handle interrupts from Powertec SCSI card + * Params : irq - interrupt number + * dev_id - user-defined (Scsi_Host structure) + * regs - processor registers at interrupt + */ +static irqreturn_t +powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct powertec_info *info = dev_id; + + return fas216_intr(&info->info); +} + +/* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : type of transfer to be performed + */ +static fasdmatype_t +powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + struct powertec_info *info = (struct powertec_info *)host->hostdata; + struct device *dev = scsi_get_device(host); + int dmach = host->dma_channel; + + if (info->info.ifcfg.capabilities & FASCAP_DMA && + min_type == fasdma_real_all) { + int bufs, map_dir, dma_dir; + + bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); + + if (direction == DMA_OUT) + map_dir = DMA_TO_DEVICE, + dma_dir = DMA_MODE_WRITE; + else + map_dir = DMA_FROM_DEVICE, + dma_dir = DMA_MODE_READ; + + dma_map_sg(dev, info->sg, bufs + 1, map_dir); + + disable_dma(dmach); + set_dma_sg(dmach, info->sg, bufs + 1); + set_dma_mode(dmach, dma_dir); + enable_dma(dmach); + return fasdma_real_all; + } + + /* + * If we're not doing DMA, + * we'll do slow PIO + */ + return fasdma_pio; +} + +/* Prototype: int powertecscsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void +powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + if (host->dma_channel != NO_DMA) + disable_dma(host->dma_channel); +} + +/* Prototype: const char *powertecscsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *powertecscsi_info(struct Scsi_Host *host) +{ + struct powertec_info *info = (struct powertec_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s terminators o%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION, info->term_ctl ? "n" : "ff"); + + return string; +} + +/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) + * Purpose : Set a driver specific function + * Params : host - host to setup + * : buffer - buffer containing string describing operation + * : length - length of string + * Returns : -EINVAL, or 0 + */ +static int +powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) +{ + int ret = length; + + if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) { + buffer += 12; + length -= 12; + + if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { + if (buffer[5] == '1') + powertecscsi_terminator_ctl(host, 1); + else if (buffer[5] == '0') + powertecscsi_terminator_ctl(host, 0); + else + ret = -EINVAL; + } else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int powertecscsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + struct Scsi_Host *host; + struct powertec_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + if (inout == 1) + return powertecscsi_set_proc_info(host, buffer, length); + + info = (struct powertec_info *)host->hostdata; + + p += sprintf(p, "PowerTec SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", + info->term_ctl ? "n" : "ff"); + + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + *start = buffer + offset; + pos = p - buffer - offset; + if (pos > length) + pos = length; + + return pos; +} + +static ssize_t powertecscsi_show_term(struct device *dev, char *buf) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct powertec_info *info = (struct powertec_info *)host->hostdata; + + return sprintf(buf, "%d\n", info->term_ctl ? 1 : 0); +} + +static ssize_t +powertecscsi_store_term(struct device *dev, const char *buf, size_t len) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + + if (len > 1) + powertecscsi_terminator_ctl(host, buf[0] != '0'); + + return len; +} + +static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, + powertecscsi_show_term, powertecscsi_store_term); + +static Scsi_Host_Template powertecscsi_template = { + .module = THIS_MODULE, + .proc_info = powertecscsi_proc_info, + .name = "PowerTec SCSI", + .info = powertecscsi_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + + .can_queue = 8, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .use_clustering = ENABLE_CLUSTERING, + .proc_name = "powertec", +}; + +static int __devinit +powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct powertec_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + if (!request_mem_region(resbase, reslen, "powertecscsi")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&powertecscsi_template, + sizeof (struct powertec_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = ec->irq; + host->dma_channel = ec->dma; + + ec->irqaddr = base + POWERTEC_INTR_STATUS; + ec->irqmask = POWERTEC_INTR_BIT; + ec->irq_data = base + POWERTEC_INTR_CONTROL; + ec->ops = &powertecscsi_ops; + + ecard_set_drvdata(ec, host); + + info = (struct powertec_info *)host->hostdata; + info->term_port = base + POWERTEC_TERM_CONTROL; + powertecscsi_terminator_ctl(host, term[ec->slot_no]); + + device_create_file(&ec->dev, &dev_attr_bus_term); + + info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET; + info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT; + info->info.scsi.irq = host->irq; + info->info.ifcfg.clockrate = 40; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 7; + info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = 0; + info->info.dma.setup = powertecscsi_dma_setup; + info->info.dma.pseudo = NULL; + info->info.dma.stop = powertecscsi_dma_stop; + + ret = fas216_init(host); + if (ret) + goto out_free; + + ret = request_irq(host->irq, powertecscsi_intr, + SA_INTERRUPT, "powertec", info); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_release; + } + + if (host->dma_channel != NO_DMA) { + if (request_dma(host->dma_channel, "powertec")) { + printk("scsi%d: DMA%d not free, using PIO\n", + host->host_no, host->dma_channel); + host->dma_channel = NO_DMA; + } else { + set_dma_speed(host->dma_channel, 180); + info->info.ifcfg.capabilities |= FASCAP_DMA; + } + } + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, host); + + out_release: + fas216_release(host); + + out_free: + device_remove_file(&ec->dev, &dev_attr_bus_term); + scsi_unregister(host); + + out_unmap: + iounmap(base); + + out_region: + release_mem_region(resbase, reslen); + + out: + return ret; +} + +static void __devexit powertecscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct powertecscsi_info *info = (struct powertecscsi_info *)host->hostdata; + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + device_remove_file(&ec->dev, &dev_attr_bus_term); + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, info); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id powertecscsi_cids[] = { + { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver powertecscsi_driver = { + .probe = powertecscsi_probe, + .remove = __devexit_p(powertecscsi_remove), + .id_table = powertecscsi_cids, + .drv = { + .name = "powertecscsi", + }, +}; + +static int __init powertecscsi_init(void) +{ + return ecard_register_driver(&powertecscsi_driver); +} + +static void __exit powertecscsi_exit(void) +{ + ecard_remove_driver(&powertecscsi_driver); +} + +module_init(powertecscsi_init); +module_exit(powertecscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Powertec SCSI driver"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/scsi/arm/queue.c linux-2.5.69-bk14/drivers/scsi/arm/queue.c --- linux-2.5.69-bk13/drivers/scsi/arm/queue.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/queue.c 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,319 @@ +/* + * linux/drivers/acorn/scsi/queue.c: queue handling primitives + * + * Copyright (C) 1997-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 15-Sep-1997 RMK Created. + * 11-Oct-1997 RMK Corrected problem with queue_remove_exclude + * not updating internal linked list properly + * (was causing commands to go missing). + * 30-Aug-2000 RMK Use Linux list handling and spinlocks + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../scsi.h" + +#define DEBUG + +typedef struct queue_entry { + struct list_head list; + Scsi_Cmnd *SCpnt; +#ifdef DEBUG + unsigned long magic; +#endif +} QE_t; + +#ifdef DEBUG +#define QUEUE_MAGIC_FREE 0xf7e1c9a3 +#define QUEUE_MAGIC_USED 0xf7e1cc33 + +#define SET_MAGIC(q,m) ((q)->magic = (m)) +#define BAD_MAGIC(q,m) ((q)->magic != (m)) +#else +#define SET_MAGIC(q,m) do { } while (0) +#define BAD_MAGIC(q,m) (0) +#endif + +#include "queue.h" + +#define NR_QE 32 + +/* + * Function: void queue_initialise (Queue_t *queue) + * Purpose : initialise a queue + * Params : queue - queue to initialise + */ +int queue_initialise (Queue_t *queue) +{ + unsigned int nqueues = NR_QE; + QE_t *q; + + spin_lock_init(&queue->queue_lock); + INIT_LIST_HEAD(&queue->head); + INIT_LIST_HEAD(&queue->free); + + /* + * If life was easier, then SCpnt would have a + * host-available list head, and we wouldn't + * need to keep free lists or allocate this + * memory. + */ + queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL); + if (q) { + for (; nqueues; q++, nqueues--) { + SET_MAGIC(q, QUEUE_MAGIC_FREE); + q->SCpnt = NULL; + list_add(&q->list, &queue->free); + } + } + + return queue->alloc != NULL; +} + +/* + * Function: void queue_free (Queue_t *queue) + * Purpose : free a queue + * Params : queue - queue to free + */ +void queue_free (Queue_t *queue) +{ + if (!list_empty(&queue->head)) + printk(KERN_WARNING "freeing non-empty queue %p\n", queue); + if (queue->alloc) + kfree(queue->alloc); +} + + +/* + * Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) + * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head. + * Params : queue - destination queue + * SCpnt - command to add + * head - add command to head of queue + * Returns : 0 on error, !0 on success + */ +int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) +{ + unsigned long flags; + struct list_head *l; + QE_t *q; + int ret = 0; + + spin_lock_irqsave(&queue->queue_lock, flags); + if (list_empty(&queue->free)) + goto empty; + + l = queue->free.next; + list_del(l); + + q = list_entry(l, QE_t, list); + if (BAD_MAGIC(q, QUEUE_MAGIC_FREE)) + BUG(); + + SET_MAGIC(q, QUEUE_MAGIC_USED); + q->SCpnt = SCpnt; + + if (head) + list_add(l, &queue->head); + else + list_add_tail(l, &queue->head); + + ret = 1; +empty: + spin_unlock_irqrestore(&queue->queue_lock, flags); + return ret; +} + +static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) +{ + QE_t *q; + + /* + * Move the entry from the "used" list onto the "free" list + */ + list_del(ent); + q = list_entry(ent, QE_t, list); + if (BAD_MAGIC(q, QUEUE_MAGIC_USED)) + BUG(); + + SET_MAGIC(q, QUEUE_MAGIC_FREE); + list_add(ent, &queue->free); + + return q->SCpnt; +} + +/* + * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude) + * Purpose : remove a SCSI command from a queue + * Params : queue - queue to remove command from + * exclude - bit array of target&lun which is busy + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) +{ + unsigned long flags; + struct list_head *l; + Scsi_Cmnd *SCpnt = NULL; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) { + SCpnt = __queue_remove(queue, l); + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return SCpnt; +} + +/* + * Function: Scsi_Cmnd *queue_remove (queue) + * Purpose : removes first SCSI command from a queue + * Params : queue - queue to remove command from + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +Scsi_Cmnd *queue_remove(Queue_t *queue) +{ + unsigned long flags; + Scsi_Cmnd *SCpnt = NULL; + + spin_lock_irqsave(&queue->queue_lock, flags); + if (!list_empty(&queue->head)) + SCpnt = __queue_remove(queue, queue->head.next); + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return SCpnt; +} + +/* + * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) + * Purpose : remove a SCSI command from the queue for a specified target/lun/tag + * Params : queue - queue to remove command from + * target - target that we want + * lun - lun on device + * tag - tag on device + * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements + */ +Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag) +{ + unsigned long flags; + struct list_head *l; + Scsi_Cmnd *SCpnt = NULL; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun && + q->SCpnt->tag == tag) { + SCpnt = __queue_remove(queue, l); + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return SCpnt; +} + +/* + * Function: queue_remove_all_target(queue, target) + * Purpose : remove all SCSI commands from the queue for a specified target + * Params : queue - queue to remove command from + * target - target device id + * Returns : nothing + */ +void queue_remove_all_target(Queue_t *queue, int target) +{ + unsigned long flags; + struct list_head *l; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt->device->id == target) + __queue_remove(queue, l); + } + spin_unlock_irqrestore(&queue->queue_lock, flags); +} + +/* + * Function: int queue_probetgtlun (queue, target, lun) + * Purpose : check to see if we have a command in the queue for the specified + * target/lun. + * Params : queue - queue to look in + * target - target we want to probe + * lun - lun on target + * Returns : 0 if not found, != 0 if found + */ +int queue_probetgtlun (Queue_t *queue, int target, int lun) +{ + unsigned long flags; + struct list_head *l; + int found = 0; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return found; +} + +/* + * Function: int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) + * Purpose : remove a specific command from the queues + * Params : queue - queue to look in + * SCpnt - command to find + * Returns : 0 if not found + */ +int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) +{ + unsigned long flags; + struct list_head *l; + int found = 0; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt == SCpnt) { + __queue_remove(queue, l); + found = 1; + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return found; +} + +EXPORT_SYMBOL(queue_initialise); +EXPORT_SYMBOL(queue_free); +EXPORT_SYMBOL(__queue_add); +EXPORT_SYMBOL(queue_remove); +EXPORT_SYMBOL(queue_remove_exclude); +EXPORT_SYMBOL(queue_remove_tgtluntag); +EXPORT_SYMBOL(queue_remove_cmd); +EXPORT_SYMBOL(queue_remove_all_target); +EXPORT_SYMBOL(queue_probetgtlun); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SCSI command queueing"); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/drivers/scsi/arm/queue.h linux-2.5.69-bk14/drivers/scsi/arm/queue.h --- linux-2.5.69-bk13/drivers/scsi/arm/queue.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/queue.h 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,105 @@ +/* + * linux/drivers/acorn/scsi/queue.h: queue handling + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef QUEUE_H +#define QUEUE_H + +typedef struct { + struct list_head head; + struct list_head free; + spinlock_t queue_lock; + void *alloc; /* start of allocated mem */ +} Queue_t; + +/* + * Function: void queue_initialise (Queue_t *queue) + * Purpose : initialise a queue + * Params : queue - queue to initialise + */ +extern int queue_initialise (Queue_t *queue); + +/* + * Function: void queue_free (Queue_t *queue) + * Purpose : free a queue + * Params : queue - queue to free + */ +extern void queue_free (Queue_t *queue); + +/* + * Function: Scsi_Cmnd *queue_remove (queue) + * Purpose : removes first SCSI command from a queue + * Params : queue - queue to remove command from + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +extern Scsi_Cmnd *queue_remove (Queue_t *queue); + +/* + * Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude) + * Purpose : remove a SCSI command from a queue + * Params : queue - queue to remove command from + * exclude - array of busy LUNs + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned long *exclude); + +#define queue_add_cmd_ordered(queue,SCpnt) \ + __queue_add(queue,SCpnt,(SCpnt)->cmnd[0] == REQUEST_SENSE) +#define queue_add_cmd_tail(queue,SCpnt) \ + __queue_add(queue,SCpnt,0) +/* + * Function: int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) + * Purpose : Add a new command onto a queue + * Params : queue - destination queue + * SCpnt - command to add + * head - add command to head of queue + * Returns : 0 on error, !0 on success + */ +extern int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head); + +/* + * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) + * Purpose : remove a SCSI command from the queue for a specified target/lun/tag + * Params : queue - queue to remove command from + * target - target that we want + * lun - lun on device + * tag - tag on device + * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements + */ +extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag); + +/* + * Function: queue_remove_all_target(queue, target) + * Purpose : remove all SCSI commands from the queue for a specified target + * Params : queue - queue to remove command from + * target - target device id + * Returns : nothing + */ +extern void queue_remove_all_target(Queue_t *queue, int target); + +/* + * Function: int queue_probetgtlun (queue, target, lun) + * Purpose : check to see if we have a command in the queue for the specified + * target/lun. + * Params : queue - queue to look in + * target - target we want to probe + * lun - lun on target + * Returns : 0 if not found, != 0 if found + */ +extern int queue_probetgtlun (Queue_t *queue, int target, int lun); + +/* + * Function: int queue_remove_cmd (Queue_t *queue, Scsi_Cmnd *SCpnt) + * Purpose : remove a specific command from the queues + * Params : queue - queue to look in + * SCpnt - command to find + * Returns : 0 if not found + */ +int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt); + +#endif /* QUEUE_H */ diff -urN linux-2.5.69-bk13/drivers/scsi/arm/scsi.h linux-2.5.69-bk14/drivers/scsi/arm/scsi.h --- linux-2.5.69-bk13/drivers/scsi/arm/scsi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/drivers/scsi/arm/scsi.h 2003-05-20 04:46:45.000000000 -0700 @@ -0,0 +1,115 @@ +/* + * linux/drivers/acorn/scsi/scsi.h + * + * Copyright (C) 2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Commonly used scsi driver functions. + */ + +#define BELT_AND_BRACES + +/* + * The scatter-gather list handling. This contains all + * the yucky stuff that needs to be fixed properly. + */ +static inline int copy_SCp_to_sg(struct scatterlist *sg, Scsi_Pointer *SCp, int max) +{ + int bufs = SCp->buffers_residual; + + BUG_ON(bufs + 1 > max); + + sg->page = virt_to_page(SCp->ptr); + sg->offset = ((unsigned int)SCp->ptr) & ~PAGE_MASK; + sg->length = SCp->this_residual; + + if (bufs) + memcpy(sg + 1, SCp->buffer + 1, + sizeof(struct scatterlist) * bufs); + return bufs + 1; +} + +static inline int next_SCp(Scsi_Pointer *SCp) +{ + int ret = SCp->buffers_residual; + if (ret) { + SCp->buffer++; + SCp->buffers_residual--; + SCp->ptr = (char *) + (page_address(SCp->buffer->page) + + SCp->buffer->offset); + SCp->this_residual = SCp->buffer->length; + } else { + SCp->ptr = NULL; + SCp->this_residual = 0; + } + return ret; +} + +static inline unsigned char get_next_SCp_byte(Scsi_Pointer *SCp) +{ + char c = *SCp->ptr; + + SCp->ptr += 1; + SCp->this_residual -= 1; + + return c; +} + +static inline void put_next_SCp_byte(Scsi_Pointer *SCp, unsigned char c) +{ + *SCp->ptr = c; + SCp->ptr += 1; + SCp->this_residual -= 1; +} + +static inline void init_SCp(Scsi_Cmnd *SCpnt) +{ + memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer)); + + if (SCpnt->use_sg) { + unsigned long len = 0; + int buf; + + SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; + SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; + SCpnt->SCp.ptr = (char *) + (page_address(SCpnt->SCp.buffer->page) + + SCpnt->SCp.buffer->offset); + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + +#ifdef BELT_AND_BRACES + /* + * Calculate correct buffer length. Some commands + * come in with the wrong request_bufflen. + */ + for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++) + len += SCpnt->SCp.buffer[buf].length; + + if (SCpnt->request_bufflen != len) + printk(KERN_WARNING "scsi%d.%c: bad request buffer " + "length %d, should be %ld\n", SCpnt->device->host->host_no, + '0' + SCpnt->device->id, SCpnt->request_bufflen, len); + SCpnt->request_bufflen = len; +#endif + } else { + SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer; + SCpnt->SCp.this_residual = SCpnt->request_bufflen; + } + + /* + * If the upper SCSI layers pass a buffer, but zero length, + * we aren't interested in the buffer pointer. + */ + if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) { +#if 0 //def BELT_AND_BRACES + printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for " + "command ", SCpnt->host->host_no, '0' + SCpnt->target); + print_command(SCpnt->cmnd); +#endif + SCpnt->SCp.ptr = NULL; + } +} diff -urN linux-2.5.69-bk13/drivers/scsi/esp.c linux-2.5.69-bk14/drivers/scsi/esp.c --- linux-2.5.69-bk13/drivers/scsi/esp.c 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/scsi/esp.c 2003-05-20 04:46:45.000000000 -0700 @@ -1215,11 +1215,33 @@ #endif /* !CONFIG_SUN4 */ +/* + */ +static int esp_release(struct Scsi_Host *host) +{ + struct esp *esp = (struct esp *) host->hostdata; + + ESP_INTSOFF(esp->dregs); +#if 0 + esp_reset_dma(esp); + esp_reset_esp(esp); +#endif + + free_irq(esp->ehost->irq, esp); + sbus_free_consistent(esp->sdev, 16, + (void *) esp->esp_command, esp->esp_command_dvma); + sbus_iounmap(esp->eregs, ESP_REG_SIZE); + esp->dma->allocated = 0; + esp_chain_del(esp); + + return 0; +} + /* The info function will return whatever useful * information the developer sees fit. If not provided, then * the name field will be used instead. */ -const char *esp_info(struct Scsi_Host *host) +static const char *esp_info(struct Scsi_Host *host) { struct esp *esp; @@ -4370,6 +4392,7 @@ .detect = esp_detect, .slave_alloc = esp_slave_alloc, .slave_destroy = esp_slave_destroy, + .release = esp_release, .info = esp_info, .command = esp_command, .queuecommand = esp_queue, diff -urN linux-2.5.69-bk13/drivers/scsi/megaraid.c linux-2.5.69-bk14/drivers/scsi/megaraid.c --- linux-2.5.69-bk13/drivers/scsi/megaraid.c 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/drivers/scsi/megaraid.c 2003-05-20 04:46:45.000000000 -0700 @@ -4664,7 +4664,7 @@ mbox = (mbox_t *)raw_mbox; - memset(mbox, 0, sizeof(mbox)); + memset(mbox, 0, sizeof(*mbox)); memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); @@ -4697,7 +4697,7 @@ mbox = (mbox_t *)raw_mbox; - memset(mbox, 0, sizeof(mbox)); + memset(mbox, 0, sizeof(*mbox)); /* * issue command to find out what channels are raid/scsi @@ -4818,7 +4818,7 @@ mbox = (mbox_t *)raw_mbox; - memset(mbox, 0, sizeof(mbox)); + memset(mbox, 0, sizeof(*mbox)); /* * issue command @@ -4847,7 +4847,7 @@ mbox = (mbox_t *)raw_mbox; - memset(mbox, 0, sizeof (mbox)); + memset(mbox, 0, sizeof(*mbox)); /* * issue command to find out if controller supports extended CDBs. */ diff -urN linux-2.5.69-bk13/drivers/video/cyber2000fb.c linux-2.5.69-bk14/drivers/video/cyber2000fb.c --- linux-2.5.69-bk13/drivers/video/cyber2000fb.c 2003-05-04 16:53:07.000000000 -0700 +++ linux-2.5.69-bk14/drivers/video/cyber2000fb.c 2003-05-20 04:46:47.000000000 -0700 @@ -146,7 +146,7 @@ * Hardware Cyber2000 Acceleration */ static void -cyber2000fb_fillrect(struct fb_info *info, struct fb_fillrect *rect) +cyber2000fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct cfb_info *cfb = (struct cfb_info *)info; unsigned long dst, col; @@ -178,7 +178,7 @@ } static void -cyber2000fb_copyarea(struct fb_info *info, struct fb_copyarea *region) +cyber2000fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) { struct cfb_info *cfb = (struct cfb_info *)info; unsigned int cmd = CO_CMD_L_PATTERN_FGCOL; @@ -189,24 +189,25 @@ return; } + cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); + cyber2000fb_writew(region->width - 1, CO_REG_PIXWIDTH, cfb); + cyber2000fb_writew(region->height - 1, CO_REG_PIXHEIGHT, cfb); + + src = region->sx + region->sy * cfb->fb.var.xres_virtual; + dst = region->dx + region->dy * cfb->fb.var.xres_virtual; + if (region->sx < region->dx) { - region->sx += region->width - 1; - region->dx += region->width - 1; + src += region->width - 1; + dst += region->width - 1; cmd |= CO_CMD_L_INC_LEFT; } if (region->sy < region->dy) { - region->sy += region->height - 1; - region->dy += region->height - 1; + src += (region->height - 1) * cfb->fb.var.xres_virtual; + dst += (region->height - 1) * cfb->fb.var.xres_virtual; cmd |= CO_CMD_L_INC_UP; } - cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); - cyber2000fb_writew(region->width - 1, CO_REG_PIXWIDTH, cfb); - cyber2000fb_writew(region->height - 1, CO_REG_PIXHEIGHT, cfb); - - src = region->sx + region->sy * cfb->fb.var.xres_virtual; - dst = region->dx + region->dy * cfb->fb.var.xres_virtual; if (cfb->fb.var.bits_per_pixel == 24) { cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb); src *= 3; @@ -221,9 +222,9 @@ } static void -cyber2000fb_imageblit(struct fb_info *info, struct fb_image *image) +cyber2000fb_imageblit(struct fb_info *info, const struct fb_image *image) { - struct cfb_info *cfb = (struct cfb_info *)info; +// struct cfb_info *cfb = (struct cfb_info *)info; // if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) { cfb_imageblit(info, image); @@ -754,7 +755,6 @@ var->blue.msb_right = 0; switch (var->bits_per_pixel) { -#ifdef FBCON_HAS_CFB8 case 8: /* PSEUDOCOLOUR, 256 */ var->transp.offset = 0; var->transp.length = 0; @@ -765,8 +765,7 @@ var->blue.offset = 0; var->blue.length = 8; break; -#endif -#ifdef FBCON_HAS_CFB16 + case 16:/* DIRECTCOLOUR, 64k or 32k */ switch (var->green.length) { case 6: /* RGB565, 64k */ @@ -804,8 +803,7 @@ break; } break; -#endif -#ifdef FBCON_HAS_CFB24 + case 24:/* TRUECOLOUR, 16m */ var->transp.offset = 0; var->transp.length = 0; @@ -816,8 +814,7 @@ var->blue.offset = 0; var->blue.length = 8; break; -#endif -#ifdef FBCON_HAS_CFB32 + case 32:/* TRUECOLOUR, 16m */ var->transp.offset = 24; var->transp.length = 8; @@ -828,7 +825,7 @@ var->blue.offset = 0; var->blue.length = 8; break; -#endif + default: return -EINVAL; } @@ -1601,15 +1598,17 @@ /* * Use MCLK from BIOS. FIXME: what about hotplug? */ -#ifndef __arm__ cfb->mclk_mult = cyber2000_grphr(EXT_MCLK_MULT, cfb); cfb->mclk_div = cyber2000_grphr(EXT_MCLK_DIV, cfb); -#else + +#ifdef __arm__ /* * MCLK on the NetWinder and the Shark is fixed at 75MHz */ - cfb->mclk_mult = 0xdb; - cfb->mclk_div = 0x54; + if (machine_is_netwinder()) { + cfb->mclk_mult = 0xdb; + cfb->mclk_div = 0x54; + } #endif err = cyberpro_common_probe(cfb); diff -urN linux-2.5.69-bk13/drivers/video/radeonfb.c linux-2.5.69-bk14/drivers/video/radeonfb.c --- linux-2.5.69-bk13/drivers/video/radeonfb.c 2003-05-04 16:53:42.000000000 -0700 +++ linux-2.5.69-bk14/drivers/video/radeonfb.c 2003-05-20 04:46:47.000000000 -0700 @@ -2840,8 +2840,7 @@ } /* map the regions */ - rinfo->mmio_base = (u32) ioremap (rinfo->mmio_base_phys, - RADEON_REGSIZE); + rinfo->mmio_base = (unsigned long) ioremap (rinfo->mmio_base_phys, RADEON_REGSIZE); if (!rinfo->mmio_base) { printk ("radeonfb: cannot map MMIO\n"); release_mem_region (rinfo->mmio_base_phys, @@ -2978,8 +2977,7 @@ } } - rinfo->fb_base = (u32) ioremap (rinfo->fb_base_phys, - rinfo->video_ram); + rinfo->fb_base = (unsigned long) ioremap (rinfo->fb_base_phys, rinfo->video_ram); if (!rinfo->fb_base) { printk ("radeonfb: cannot map FB\n"); iounmap ((void*)rinfo->mmio_base); diff -urN linux-2.5.69-bk13/fs/cifs/AUTHORS linux-2.5.69-bk14/fs/cifs/AUTHORS --- linux-2.5.69-bk13/fs/cifs/AUTHORS 2003-05-04 16:52:50.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/AUTHORS 2003-05-20 04:46:47.000000000 -0700 @@ -20,3 +20,9 @@ Zwane Mwaikambo Andi Kleen +Test case and Bug Report contributors +------------------------------------- +Thanks to those in the community who have submitted detailed bug reports +and debug of problems they have found: Jochen Dolze, David Blaine, +Rene Scharfe, Martin Josefsson and others. + diff -urN linux-2.5.69-bk13/fs/cifs/CHANGES linux-2.5.69-bk14/fs/cifs/CHANGES --- linux-2.5.69-bk13/fs/cifs/CHANGES 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/CHANGES 2003-05-20 04:46:47.000000000 -0700 @@ -1,3 +1,16 @@ +Version 0.77 +------------ +Fix display of NTFS DFS junctions to display as symlinks. +They are the network equivalent. Fix oops in +cifs_partialpagewrite caused by missing spinlock protection +of openfile linked list. Allow writebehind caching errors to +be returned to the application at file close. + +Version 0.76 +------------ +Clean up options displayed in /proc/mounts by show_options to +be more consistent with other filesystems. + Version 0.75 ------------ Fix delete of readonly file to Windows servers. Reflect diff -urN linux-2.5.69-bk13/fs/cifs/TODO linux-2.5.69-bk14/fs/cifs/TODO --- linux-2.5.69-bk13/fs/cifs/TODO 2003-05-04 16:53:42.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/TODO 2003-05-20 04:46:47.000000000 -0700 @@ -18,6 +18,8 @@ d) Kerberos/SPNEGO session setup support - (started) e) NTLMv2 authentication and MD5-HMAC signing SMB PDUs - (mostly implemented) + signing necessary for some Windows 2003 servers in domain + controller mode. f) oplock support (ie safe CIFS distributed file caching) is not quite complete. In addition Directory entry caching relies on a 1 second timer, rather than @@ -52,7 +54,8 @@ than resending (helps reduce server resource utilization and avoid spurious oplock breaks). -KNOWN BUGS (updated March 7, 2003) + +KNOWN BUGS (updated May 16, 2003) ==================================== 1) existing symbolic links (Windows reparse points) are recognized but can not be created remotely. They are implemented for Samba and those that @@ -60,6 +63,11 @@ symlink text beginning with slash 2) delete of file with read-only attribute set will fail (may be ok) 3) mount helper syntax not quite matching man page +4) follow_link and readdir code does not follow dfs junctions +but recognizes them +5) create of new files to FAT partitions on Windows servers can +succeed but still return access denied (appears to be Windows +not client problem). NTFS partitions do not have this problem. Misc testing to do ================= @@ -68,6 +76,6 @@ 2) Run dbench -3) Finish FSX testing on SMP now that we workaround the Samba bug +3) Finish high stress fsx testing on SMP clients 4) Additional performance testing and optimization diff -urN linux-2.5.69-bk13/fs/cifs/cifs_debug.c linux-2.5.69-bk14/fs/cifs/cifs_debug.c --- linux-2.5.69-bk13/fs/cifs/cifs_debug.c 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/cifs_debug.c 2003-05-20 04:46:47.000000000 -0700 @@ -89,8 +89,8 @@ ses->serverOS, ses->serverNOS, ses->capabilities); buf += length; if(ses->server) - buf += sprintf(buf, "\tLocal Users To Same Server: %d ", - atomic_read(&ses->server->socketUseCount)); + buf += sprintf(buf, "\tLocal Users To Same Server: %d ", + atomic_read(&ses->server->socketUseCount)); } read_unlock(&GlobalSMBSeslock); sprintf(buf, "\n"); diff -urN linux-2.5.69-bk13/fs/cifs/cifs_fs_sb.h linux-2.5.69-bk14/fs/cifs/cifs_fs_sb.h --- linux-2.5.69-bk13/fs/cifs/cifs_fs_sb.h 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/cifs_fs_sb.h 2003-05-20 04:46:47.000000000 -0700 @@ -22,7 +22,7 @@ struct cifsTconInfo *tcon; /* primary mount */ struct list_head nested_tcon_q; struct nls_table *local_nls; - unsigned int rsize; - unsigned int wsize; + unsigned int rsize; + unsigned int wsize; }; #endif /* _CIFS_FS_SB_H */ diff -urN linux-2.5.69-bk13/fs/cifs/cifsfs.c linux-2.5.69-bk14/fs/cifs/cifsfs.c --- linux-2.5.69-bk13/fs/cifs/cifsfs.c 2003-05-20 04:46:36.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/cifsfs.c 2003-05-20 04:46:47.000000000 -0700 @@ -209,8 +209,8 @@ /* * cifs_show_options() is for displaying mount options in /proc/mounts. - * It tries to avoid showing settings that were not changed from their - * defaults. + * Not all settable options are displayed but most of the important + * ones are. */ static int cifs_show_options(struct seq_file *s, struct vfsmount *m) @@ -219,15 +219,19 @@ cifs_sb = CIFS_SB(m->mnt_sb); - if (cifs_sb) + if (cifs_sb) { if (cifs_sb->tcon) { - seq_printf(s, ", TARGET: %s ", cifs_sb->tcon->treeName); - seq_printf(s, "FS TYPE: %s ", - cifs_sb->tcon->nativeFileSystem); + seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName); if (cifs_sb->tcon->ses->userName) - seq_printf(s, " USER: %s ", + seq_printf(s, ",username=%s", cifs_sb->tcon->ses->userName); + if(cifs_sb->tcon->ses->domainName) + seq_printf(s, ",domain=%s", + cifs_sb->tcon->ses->domainName); } + seq_printf(s, ",rsize=%d",cifs_sb->rsize); + seq_printf(s, ",wsize=%d",cifs_sb->wsize); + } return 0; } @@ -423,6 +427,7 @@ static int cifs_oplock_thread(void * dummyarg) { struct list_head * tmp; + struct list_head * tmp1; struct oplock_q_entry * oplock_item; struct file * pfile; struct cifsTconInfo *pTcon; @@ -438,9 +443,8 @@ /* BB add missing code */ cFYI(1,("oplock thread woken up - flush inode")); /* BB remove */ write_lock(&GlobalMid_Lock); - list_for_each(tmp, &GlobalOplock_Q) { - oplock_item = list_entry(tmp, struct - oplock_q_entry, + list_for_each_safe(tmp, tmp1, &GlobalOplock_Q) { + oplock_item = list_entry(tmp, struct oplock_q_entry, qhead); if(oplock_item) { pTcon = oplock_item->tcon; @@ -449,6 +453,9 @@ DeleteOplockQEntry(oplock_item); write_unlock(&GlobalMid_Lock); rc = filemap_fdatawrite(pfile->f_dentry->d_inode->i_mapping); + if(rc) + CIFS_I(pfile->f_dentry->d_inode)->write_behind_rc + = rc; cFYI(1,("Oplock flush file %p rc %d",pfile,rc)); /* send oplock break */ write_lock(&GlobalMid_Lock); diff -urN linux-2.5.69-bk13/fs/cifs/cifsglob.h linux-2.5.69-bk14/fs/cifs/cifsglob.h --- linux-2.5.69-bk13/fs/cifs/cifsglob.h 2003-05-04 16:53:07.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/cifsglob.h 2003-05-20 04:46:47.000000000 -0700 @@ -157,8 +157,8 @@ int capabilities; char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for tcp names - will ipv6 and sctp addresses fit here?? */ char userName[MAX_USERNAME_SIZE + 1]; - char domainName[MAX_USERNAME_SIZE + 1]; - char password_with_pad[CIFS_ENCPWD_SIZE]; + char domainName[MAX_USERNAME_SIZE + 1]; + char password_with_pad[CIFS_ENCPWD_SIZE]; }; /* @@ -204,7 +204,7 @@ __u16 netfid; /* file id from remote */ /* BB add lock scope info here if needed */ ; /* lock scope id (0 if none) */ - struct file * pfile; /* needed for writepage */ + struct file * pfile; /* needed for writepage */ int endOfSearch:1; /* we have reached end of search */ int closePend:1; /* file is marked to close */ int emptyDir:1; @@ -221,6 +221,7 @@ struct list_head lockList; /* BB add in lists for dirty pages - i.e. write caching info for oplock */ struct list_head openFileList; + int write_behind_rc; __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ atomic_t inUse; /* num concurrent users (local openers cifs) of file*/ unsigned long time; /* jiffies of last update/check of inode */ diff -urN linux-2.5.69-bk13/fs/cifs/cifspdu.h linux-2.5.69-bk14/fs/cifs/cifspdu.h --- linux-2.5.69-bk13/fs/cifs/cifspdu.h 2003-05-04 16:53:40.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/cifspdu.h 2003-05-20 04:46:47.000000000 -0700 @@ -1263,6 +1263,7 @@ __u16 ServerType; /* 0x0001 = CIFS server */ __u16 ReferralFlags; /* or proximity - not clear which since always set to zero - SNIA spec says 0x01 means strip off PathConsumed chars before submitting RequestFileName to remote node */ __u16 TimeToLive; + __u16 Proximity; __u16 DfsPathOffset; __u16 DfsAlternatePathOffset; __u16 NetworkAddressOffset; diff -urN linux-2.5.69-bk13/fs/cifs/cifsproto.h linux-2.5.69-bk14/fs/cifs/cifsproto.h --- linux-2.5.69-bk13/fs/cifs/cifsproto.h 2003-05-04 16:52:49.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/cifsproto.h 2003-05-20 04:46:47.000000000 -0700 @@ -127,13 +127,15 @@ extern int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, const unsigned char *searchName, unsigned char **targetUNCs, - int *number_of_UNC_in_array, + unsigned int *number_of_UNC_in_array, const struct nls_table *nls_codepage); extern int connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, const struct nls_table *nls_codepage); - +extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, + const char *old_path, const struct nls_table *nls_codepage, + unsigned int *pnum_referrals, unsigned char ** preferrals); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct statfs *FSData, const struct nls_table *nls_codepage); diff -urN linux-2.5.69-bk13/fs/cifs/cifssmb.c linux-2.5.69-bk14/fs/cifs/cifssmb.c --- linux-2.5.69-bk13/fs/cifs/cifssmb.c 2003-05-04 16:53:07.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/cifssmb.c 2003-05-20 04:46:47.000000000 -0700 @@ -1625,15 +1625,18 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, const unsigned char *searchName, unsigned char **targetUNCs, - int *number_of_UNC_in_array, + unsigned int *number_of_UNC_in_array, const struct nls_table *nls_codepage) { /* TRANS2_GET_DFS_REFERRAL */ TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; + struct dfs_referral_level_3 * referrals = NULL; int rc = 0; int bytes_returned; int name_len; + unsigned int i; + char * temp; *number_of_UNC_in_array = 0; *targetUNCs = NULL; @@ -1654,8 +1657,8 @@ if (ses->capabilities & CAP_DFS) { pSMB->hdr.Flags2 |= SMBFLG2_DFS; } - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_UNICODE) { pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; @@ -1701,6 +1704,76 @@ cFYI(1, ("Send error in GetDFSRefer = %d", rc)); } else { /* decode response */ /* BB Add logic to parse referrals here */ + pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset); + pSMBr->DataCount = le16_to_cpu(pSMBr->DataCount); + cFYI(1, + ("Decoding GetDFSRefer response. BCC: %d Offset %d", + pSMBr->ByteCount, pSMBr->DataOffset)); + if ((pSMBr->ByteCount < 17) || (pSMBr->DataOffset > 512)) /* BB also check enough total bytes returned */ + rc = -EIO; /* bad smb */ + else { + referrals = + (struct dfs_referral_level_3 *) + (8 /* sizeof start of data block */ + + pSMBr->DataOffset + + (char *) &pSMBr->hdr.Protocol); + cFYI(1,("num_referrals: %d dfs flags: 0x%x ... \nfor referral one refer size: 0x%x srv type: 0x%x refer flags: 0x%x ttl: 0x%x",pSMBr->NumberOfReferrals,pSMBr->DFSFlags, referrals->ReferralSize,referrals->ServerType,referrals->ReferralFlags,referrals->TimeToLive)); + /* BB This field is actually two bytes in from start of + data block so we could do safety check that DataBlock + begins at address of pSMBr->NumberOfReferrals */ + *number_of_UNC_in_array = le16_to_cpu(pSMBr->NumberOfReferrals); + + /* BB Fix below so can return more than one referral */ + if(*number_of_UNC_in_array > 1) + *number_of_UNC_in_array = 1; + + /* get the length of the strings describing refs */ + name_len = 0; + for(i=0;i<*number_of_UNC_in_array;i++) { + /* make sure that DfsPathOffset not past end */ + referrals->DfsPathOffset = le16_to_cpu(referrals->DfsPathOffset); + if(referrals->DfsPathOffset > pSMBr->DataCount) { + /* if invalid referral, stop here and do + not try to copy any more */ + *number_of_UNC_in_array = i; + break; + } + temp = ((char *)referrals) + referrals->DfsPathOffset; + + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len += UniStrnlen((wchar_t *)temp,pSMBr->DataCount); + } else { + name_len += strnlen(temp,pSMBr->DataCount); + } + referrals++; + /* BB add check that referral pointer does not fall off end PDU */ + + } + /* BB add check for name_len bigger than bcc */ + *targetUNCs = + kmalloc(name_len+1+ (*number_of_UNC_in_array),GFP_KERNEL); + /* copy the ref strings */ + referrals = + (struct dfs_referral_level_3 *) + (8 /* sizeof data hdr */ + + pSMBr->DataOffset + + (char *) &pSMBr->hdr.Protocol); + + for(i=0;i<*number_of_UNC_in_array;i++) { + temp = ((char *)referrals) + referrals->DfsPathOffset; + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { + cifs_strfromUCS_le(*targetUNCs, + (wchar_t *) temp, name_len, nls_codepage); + } else { + strncpy(*targetUNCs,temp,name_len); + } + /* BB update target_uncs pointers */ + referrals++; + } + temp = *targetUNCs; + temp[name_len] = 0; + } + } if (pSMB) buf_release(pSMB); diff -urN linux-2.5.69-bk13/fs/cifs/connect.c linux-2.5.69-bk14/fs/cifs/connect.c --- linux-2.5.69-bk13/fs/cifs/connect.c 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/connect.c 2003-05-20 04:46:47.000000000 -0700 @@ -57,8 +57,8 @@ mode_t file_mode; mode_t dir_mode; int rw; - unsigned int rsize; - unsigned int wsize; + unsigned int rsize; + unsigned int wsize; unsigned short int port; }; @@ -572,10 +572,31 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, const struct nls_table *nls_codepage) { + unsigned char *referrals = NULL; + unsigned int num_referrals; + int rc = 0; + + rc = get_dfs_path(xid, pSesInfo,old_path, nls_codepage, + &num_referrals, &referrals); + + /* BB Add in code to: if valid refrl, if not ip address contact + the helper that resolves tcp names, mount to it, try to + tcon to it unmount it if fail */ + + /* BB free memory for referrals string BB */ + + return rc; +} + +int +get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, + const char *old_path, const struct nls_table *nls_codepage, + unsigned int *pnum_referrals, unsigned char ** preferrals) +{ char *temp_unc; int rc = 0; - int num_referrals = 0; - unsigned char *referrals = NULL; + + *pnum_referrals = 0; if (pSesInfo->ipc_tid == 0) { temp_unc = kmalloc(2 /* for slashes */ + @@ -594,11 +615,10 @@ kfree(temp_unc); } if (rc == 0) - rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, &referrals, - &num_referrals, nls_codepage); - - return -ENODEV; /* BB remove and add return code processing */ + rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals, + pnum_referrals, nls_codepage); + return rc; } int setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, struct nls_table * nls_info) diff -urN linux-2.5.69-bk13/fs/cifs/file.c linux-2.5.69-bk14/fs/cifs/file.c --- linux-2.5.69-bk13/fs/cifs/file.c 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/file.c 2003-05-20 04:46:47.000000000 -0700 @@ -110,10 +110,14 @@ pCifsFile->netfid = netfid; pCifsFile->pid = current->pid; pCifsFile->pfile = file; /* needed for writepage */ + write_lock(&file->f_owner.lock); + write_lock(&GlobalSMBSeslock); list_add(&pCifsFile->tlist,&pTcon->openFileList); pCifsInode = CIFS_I(file->f_dentry->d_inode); if(pCifsInode->openFileList.next) list_add(&pCifsFile->flist,&pCifsInode->openFileList); + write_unlock(&GlobalSMBSeslock); + write_unlock(&file->f_owner.lock); if(file->f_flags & O_CREAT) { /* time to set mode which we can not set earlier due to problems creating new read-only files */ @@ -152,11 +156,13 @@ { int rc = 0; struct cifsFileInfo *open_file = NULL; - struct file * file = NULL; + struct file * file = NULL; struct list_head *tmp; + struct list_head *tmp1; /* list all files open on tree connection */ - list_for_each(tmp, &pTcon->openFileList) { + read_lock(&GlobalSMBSeslock); + list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { open_file = list_entry(tmp,struct cifsFileInfo, flist); if(open_file) { if(open_file->search_resume_name) { @@ -166,7 +172,9 @@ kfree(open_file); if(file) { file->private_data = NULL; + read_unlock(&GlobalSMBSeslock); rc = cifs_open(file->f_dentry->d_inode,file); + read_lock(&GlobalSMBSeslock); if(rc) { cFYI(1,("reconnecting file %s failed with %d", file->f_dentry->d_name.name,rc)); @@ -177,6 +185,7 @@ } } } + read_unlock(&GlobalSMBSeslock); return rc; } @@ -195,9 +204,11 @@ cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; if (pSMBFile) { + write_lock(&file->f_owner.lock); if(pSMBFile->flist.next) list_del(&pSMBFile->flist); list_del(&pSMBFile->tlist); + write_unlock(&file->f_owner.lock); rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); if(pSMBFile->search_resume_name) kfree(pSMBFile->search_resume_name); @@ -206,6 +217,8 @@ } else rc = -EBADF; + if((rc ==0) && CIFS_I(inode)->write_behind_rc) + rc = CIFS_I(inode)->write_behind_rc; FreeXid(xid); return rc; } @@ -407,6 +420,7 @@ struct cifsInodeInfo *cifsInode; struct cifsFileInfo *open_file = NULL; struct list_head *tmp; + struct list_head *tmp1; int xid; xid = GetXid(); @@ -445,14 +459,17 @@ cifsInode = CIFS_I(mapping->host); - list_for_each(tmp, &cifsInode->openFileList) { + read_lock(&GlobalSMBSeslock); + list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) { open_file = list_entry(tmp,struct cifsFileInfo, flist); /* We could check if file is open for writing first */ if((open_file->pfile) && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { + read_unlock(&GlobalSMBSeslock); bytes_written = cifs_write(open_file->pfile, write_data, - to-from, &offset); + to-from, &offset); + read_lock(&GlobalSMBSeslock); /* Does mm or vfs already set times? */ inode->i_atime = inode->i_mtime = CURRENT_TIME; if ((bytes_written > 0) && (offset)) { @@ -462,6 +479,7 @@ } } } + read_unlock(&GlobalSMBSeslock); if(open_file == NULL) { cFYI(1,("No writeable filehandles for inode")); rc = -EIO; @@ -548,7 +566,8 @@ dentry->d_name.name, datasync)); rc = filemap_fdatawrite(inode->i_mapping); - + if(rc == 0) + CIFS_I(inode)->write_behind_rc = 0; FreeXid(xid); return rc; } @@ -600,6 +619,9 @@ /* filemapfdatawrite appears easier for the time being */ rc = filemap_fdatawrite(inode->i_mapping); + if(rc == 0) /* reset wb rc if we were able to write out dirty pages */ + CIFS_I(inode)->write_behind_rc = 0; + cFYI(1,("Flush inode %p file %p rc %d",inode,file,rc)); return rc; @@ -678,12 +700,16 @@ while (bytes_read > 0) { if(list_empty(pages)) break; + + spin_lock(&mapping->page_lock); page = list_entry(pages->prev, struct page, list); list_del(&page->list); + spin_unlock(&mapping->page_lock); + if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) { page_cache_release(page); - cFYI(1,("Add page cache failed")); + cFYI(1,("Add page cache failed")); continue; } @@ -739,12 +765,14 @@ pagevec_init(&lru_pvec, 0); for(i = 0;ipage_lock); if(list_empty(page_list)) { break; } page = list_entry(page_list->prev, struct page, list); offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + spin_unlock(&mapping->page_lock); + /* for reads over a certain size could initiate async read ahead */ cFYI(0,("Read %d pages into cache at offset %ld ", @@ -762,12 +790,15 @@ if ((rc < 0) || (smb_read_data == NULL)) { cFYI(1,("Read error in readpages: %d",rc)); /* clean up remaing pages off list */ + + spin_lock(&mapping->page_lock); while (!list_empty(page_list) && (i < num_pages)) { page = list_entry(page_list->prev, struct page, list); list_del(&page->list); } + spin_unlock(&mapping->page_lock); break; - } else if (bytes_read > 0){ + } else if (bytes_read > 0) { pSMBr = (struct smb_com_read_rsp *)smb_read_data; cifs_copy_cache_pages(mapping, page_list, bytes_read, smb_read_data + 4 /* RFC1000 hdr */ + @@ -783,9 +814,9 @@ } if(smb_read_data) { buf_release(smb_read_data); - smb_read_data = 0; - } - bytes_read = 0; + smb_read_data = 0; + } + bytes_read = 0; } pagevec_lru_add(&lru_pvec); diff -urN linux-2.5.69-bk13/fs/cifs/inode.c linux-2.5.69-bk14/fs/cifs/inode.c --- linux-2.5.69-bk13/fs/cifs/inode.c 2003-05-04 16:52:49.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/inode.c 2003-05-20 04:46:47.000000000 -0700 @@ -53,10 +53,6 @@ /* dump_mem("\nUnixQPathInfo return data", &findData, sizeof(findData)); */ if (rc) { if (rc == -EREMOTE) { -/* rc = *//* CIFSGetDFSRefer(xid, pTcon->ses, search_path, - &referrals, - &num_referrals, - cifs_sb->local_nls); */ tmp_path = kmalloc(strnlen (pTcon->treeName, @@ -180,11 +176,6 @@ /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ if (rc) { if (rc == -EREMOTE) { - /* BB add call to new func rc = GetDFSReferral(); */ -/* rc = *//* CIFSGetDFSRefer(xid, pTcon->ses, search_path, - &referrals, - &num_referrals, - cifs_sb->local_nls); */ tmp_path = kmalloc(strnlen (pTcon->treeName, diff -urN linux-2.5.69-bk13/fs/cifs/link.c linux-2.5.69-bk14/fs/cifs/link.c --- linux-2.5.69-bk13/fs/cifs/link.c 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/link.c 2003-05-20 04:46:47.000000000 -0700 @@ -113,6 +113,9 @@ /* BB Should we be using page symlink ops here? */ if (rc == 0) { + +/* BB Add special case check for Samba DFS symlinks */ + target_path[PATH_MAX-1] = 0; rc = vfs_follow_link(nd, target_path); } @@ -186,7 +189,10 @@ struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; + char *tmp_path = NULL; char * tmpbuffer; + unsigned char * referrals = NULL; + int num_referrals = 0; int len; __u16 fid; @@ -206,6 +212,7 @@ FreeXid(xid); return -ENOMEM; } + /* BB add read reparse point symlink code and Unix extensions symlink code here BB */ if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, @@ -224,8 +231,36 @@ if(CIFSSMBClose(xid, pTcon, fid)) { cFYI(1,("Error closing junction point (open for ioctl)")); } + if(rc == -EIO) { + /* Query if DFS Junction */ + tmp_path = + kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1, + GFP_KERNEL); + if (tmp_path) { + strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE); + strncat(tmp_path, full_path, MAX_PATHCONF); + rc = get_dfs_path(xid, pTcon->ses, tmp_path, + cifs_sb->local_nls, &num_referrals, &referrals); + cFYI(1,("Get DFS for %s rc = %d ",tmp_path, rc)); + if((num_referrals == 0) && (rc == 0)) + rc = -EACCES; + else { + cFYI(1,("num referral: %d",num_referrals)); + if(referrals) { + cFYI(1,("referral string: %s ",referrals)); + strncpy(tmpbuffer, referrals, len-1); + } + } + + kfree(tmp_path); + if(referrals) { + kfree(referrals); + } + } + /* BB add code like else decode referrals then memcpy to + tmpbuffer and free referrals string array BB */ + } } - } /* BB Anything else to do to handle recursive links? */ /* BB Should we be using page ops here? */ @@ -238,10 +273,12 @@ rc)); } - if (tmpbuffer) + if (tmpbuffer) { kfree(tmpbuffer); - if (full_path) + } + if (full_path) { kfree(full_path); + } FreeXid(xid); return rc; } diff -urN linux-2.5.69-bk13/fs/cifs/md5.c linux-2.5.69-bk14/fs/cifs/md5.c --- linux-2.5.69-bk13/fs/cifs/md5.c 2003-05-04 16:53:33.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/md5.c 2003-05-20 04:46:47.000000000 -0700 @@ -148,7 +148,7 @@ MD5Transform(ctx->buf, (__u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memmove(digest, ctx->buf, 16); - memset(ctx, 0, sizeof (ctx)); /* In case it's sensitive */ + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ diff -urN linux-2.5.69-bk13/fs/cifs/transport.c linux-2.5.69-bk14/fs/cifs/transport.c --- linux-2.5.69-bk13/fs/cifs/transport.c 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/fs/cifs/transport.c 2003-05-20 04:46:47.000000000 -0700 @@ -115,7 +115,7 @@ list_add_tail(&temp->qhead, &GlobalOplock_Q); write_unlock(&GlobalMid_Lock); } - return temp; + return temp; } diff -urN linux-2.5.69-bk13/fs/namespace.c linux-2.5.69-bk14/fs/namespace.c --- linux-2.5.69-bk13/fs/namespace.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/fs/namespace.c 2003-05-20 04:46:47.000000000 -0700 @@ -217,6 +217,8 @@ if (!path_buf) return -ENOMEM; path = d_path(mnt->mnt_root, mnt, path_buf, PAGE_SIZE); + if (IS_ERR(path)) + path = " (too long)"; mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); seq_putc(m, ' '); @@ -418,36 +420,54 @@ #endif } +static int +lives_below_in_same_fs(struct dentry *d, struct dentry *dentry) +{ + while (1) { + if (d == dentry) + return 1; + if (d == NULL || d == d->d_parent) + return 0; + d = d->d_parent; + } +} + static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) { - struct vfsmount *p, *next, *q, *res; + struct vfsmount *res, *p, *q, *r, *s; + struct list_head *h; struct nameidata nd; - p = mnt; - res = nd.mnt = q = clone_mnt(p, dentry); + res = q = clone_mnt(mnt, dentry); if (!q) goto Enomem; - q->mnt_parent = q; - q->mnt_mountpoint = p->mnt_mountpoint; + q->mnt_mountpoint = mnt->mnt_mountpoint; - while ( (next = next_mnt(p, mnt)) != NULL) { - while (p != next->mnt_parent) { - p = p->mnt_parent; - q = q->mnt_parent; + p = mnt; + for (h = mnt->mnt_mounts.next; h != &mnt->mnt_mounts; h = h->next) { + r = list_entry(h, struct vfsmount, mnt_child); + if (!lives_below_in_same_fs(r->mnt_mountpoint, dentry)) + continue; + + for (s = r; s; s = next_mnt(s, r)) { + while (p != s->mnt_parent) { + p = p->mnt_parent; + q = q->mnt_parent; + } + p = s; + nd.mnt = q; + nd.dentry = p->mnt_mountpoint; + q = clone_mnt(p, p->mnt_root); + if (!q) + goto Enomem; + spin_lock(&dcache_lock); + list_add_tail(&q->mnt_list, &res->mnt_list); + attach_mnt(q, &nd); + spin_unlock(&dcache_lock); } - p = next; - nd.mnt = q; - nd.dentry = p->mnt_mountpoint; - q = clone_mnt(p, p->mnt_root); - if (!q) - goto Enomem; - spin_lock(&dcache_lock); - list_add_tail(&q->mnt_list, &res->mnt_list); - attach_mnt(q, &nd); - spin_unlock(&dcache_lock); } return res; -Enomem: + Enomem: if (res) { spin_lock(&dcache_lock); umount_tree(res); diff -urN linux-2.5.69-bk13/fs/open.c linux-2.5.69-bk14/fs/open.c --- linux-2.5.69-bk13/fs/open.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/fs/open.c 2003-05-20 04:46:47.000000000 -0700 @@ -902,7 +902,7 @@ /* * Called when an inode is about to be open. - * We use this to disallow opening RW large files on 32bit systems if + * We use this to disallow opening large files on 32bit systems if * the caller didn't specify O_LARGEFILE. On 64bit systems we force * on this flag in sys_open. */ diff -urN linux-2.5.69-bk13/fs/reiserfs/bitmap.c linux-2.5.69-bk14/fs/reiserfs/bitmap.c --- linux-2.5.69-bk13/fs/reiserfs/bitmap.c 2003-05-04 16:53:33.000000000 -0700 +++ linux-2.5.69-bk14/fs/reiserfs/bitmap.c 2003-05-20 04:46:47.000000000 -0700 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -733,7 +734,7 @@ int rest = amount_needed; int nr_allocated; - while (rest > 0) { + while (rest > 0 && start <= finish) { nr_allocated = scan_bitmap (hint->th, &start, finish, 1, rest + prealloc_size, !hint->formatted_node, hint->block); @@ -879,7 +880,9 @@ if ( !blocks ) return; + spin_lock(&REISERFS_SB(sb)->bitmap_lock); REISERFS_SB(sb)->reserved_blocks += blocks; + spin_unlock(&REISERFS_SB(sb)->bitmap_lock); } /* Unreserve @blocks amount of blocks in fs pointed by @sb */ @@ -896,6 +899,22 @@ if ( !blocks ) return; + spin_lock(&REISERFS_SB(sb)->bitmap_lock); REISERFS_SB(sb)->reserved_blocks -= blocks; + spin_unlock(&REISERFS_SB(sb)->bitmap_lock); RFALSE( REISERFS_SB(sb)->reserved_blocks < 0, "amount of blocks reserved became zero?"); } + +/* This function estimates how much pages we will be able to write to FS + used for reiserfs_file_write() purposes for now. */ +int reiserfs_can_fit_pages ( struct super_block *sb /* superblock of filesystem + to estimate space */ ) +{ + unsigned long space; + + spin_lock(&REISERFS_SB(sb)->bitmap_lock); + space = (SB_FREE_BLOCKS(sb) - REISERFS_SB(sb)->reserved_blocks) >> ( PAGE_CACHE_SHIFT - sb->s_blocksize_bits); + spin_unlock(&REISERFS_SB(sb)->bitmap_lock); + + return space; +} diff -urN linux-2.5.69-bk13/fs/reiserfs/do_balan.c linux-2.5.69-bk14/fs/reiserfs/do_balan.c --- linux-2.5.69-bk13/fs/reiserfs/do_balan.c 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/fs/reiserfs/do_balan.c 2003-05-20 04:46:47.000000000 -0700 @@ -319,8 +319,6 @@ int new_item_len; int version; - RFALSE (!is_direct_le_ih (ih), - "PAP-12075: only direct inserted item can be broken. %h", ih); ret_val = leaf_shift_left (tb, tb->lnum[0]-1, -1); /* Calculate item length to insert to S[0] */ @@ -343,7 +341,7 @@ version = ih_version (ih); /* Calculate key component, item length and body to insert into S[0] */ - set_le_ih_k_offset( ih, le_ih_k_offset( ih ) + tb->lbytes ); + set_le_ih_k_offset( ih, le_ih_k_offset( ih ) + (tb->lbytes << (is_indirect_le_ih(ih)?tb->tb_sb->s_blocksize_bits - UNFM_P_SHIFT:0)) ); put_ih_item_len( ih, new_item_len ); if ( tb->lbytes > zeros_num ) { @@ -452,23 +450,28 @@ ih_item_len( B_N_PITEM_HEAD(tb->L[0],n+item_pos-ret_val)), l_n,body, zeros_num > l_n ? l_n : zeros_num ); - - RFALSE( l_n && - is_indirect_le_ih(B_N_PITEM_HEAD - (tb->L[0], - n + item_pos - ret_val)), - "PAP-12110: pasting more than 1 unformatted node pointer into indirect item"); - /* 0-th item in S0 can be only of DIRECT type when l_n != 0*/ { - int version; - - version = ih_version (B_N_PITEM_HEAD (tbS0, 0)); - set_le_key_k_offset (version, B_N_PKEY (tbS0, 0), - le_key_k_offset (version, B_N_PKEY (tbS0, 0)) + l_n); - version = ih_version (B_N_PITEM_HEAD(tb->CFL[0],tb->lkey[0])); - set_le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0]), - le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0])) + l_n); + int version; + int temp_l = l_n; + + RFALSE (ih_item_len (B_N_PITEM_HEAD (tbS0, 0)), + "PAP-12106: item length must be 0"); + RFALSE (comp_short_le_keys (B_N_PKEY (tbS0, 0), + B_N_PKEY (tb->L[0], + n + item_pos - ret_val)), + "PAP-12107: items must be of the same file"); + if (is_indirect_le_ih(B_N_PITEM_HEAD (tb->L[0], + n + item_pos - ret_val))) { + temp_l = l_n << (tb->tb_sb->s_blocksize_bits - UNFM_P_SHIFT); + } + /* update key of first item in S0 */ + version = ih_version (B_N_PITEM_HEAD (tbS0, 0)); + set_le_key_k_offset (version, B_N_PKEY (tbS0, 0), + le_key_k_offset (version, B_N_PKEY (tbS0, 0)) + temp_l); + /* update left delimiting key */ + set_le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0]), + le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0])) + temp_l); } /* Calculate new body, position in item and insert_size[0] */ @@ -537,7 +540,7 @@ ); /* if appended item is indirect item, put unformatted node into un list */ if (is_indirect_le_ih (pasted)) - set_ih_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); + set_ih_free_space (pasted, 0); tb->insert_size[0] = 0; zeros_num = 0; } @@ -565,15 +568,11 @@ { /* new item or its part falls to R[0] */ if ( item_pos == n - tb->rnum[0] + 1 && tb->rbytes != -1 ) { /* part of new item falls into R[0] */ - int old_key_comp, old_len, r_zeros_number; + loff_t old_key_comp, old_len, r_zeros_number; const char * r_body; int version; loff_t offset; - RFALSE( !is_direct_le_ih (ih), - "PAP-12135: only direct item can be split. (%h)", - ih); - leaf_shift_right(tb,tb->rnum[0]-1,-1); version = ih_version(ih); @@ -582,7 +581,7 @@ old_len = ih_item_len(ih); /* Calculate key component and item length to insert into R[0] */ - offset = le_ih_k_offset( ih ) + (old_len - tb->rbytes ); + offset = le_ih_k_offset( ih ) + ((old_len - tb->rbytes )<<(is_indirect_le_ih(ih)?tb->tb_sb->s_blocksize_bits - UNFM_P_SHIFT:0)); set_le_ih_k_offset( ih, offset ); put_ih_item_len( ih, tb->rbytes); /* Insert part of the item into R[0] */ @@ -590,13 +589,13 @@ bi.bi_bh = tb->R[0]; bi.bi_parent = tb->FR[0]; bi.bi_position = get_right_neighbor_position (tb, 0); - if ( offset - old_key_comp > zeros_num ) { + if ( (old_len - tb->rbytes) > zeros_num ) { r_zeros_number = 0; - r_body = body + offset - old_key_comp - zeros_num; + r_body = body + (old_len - tb->rbytes) - zeros_num; } else { r_body = body; - r_zeros_number = zeros_num - (offset - old_key_comp); + r_zeros_number = zeros_num - (old_len - tb->rbytes); zeros_num -= r_zeros_number; } @@ -707,12 +706,17 @@ { int version; + unsigned long temp_rem = n_rem; version = ih_version (B_N_PITEM_HEAD (tb->R[0],0)); + if (is_indirect_le_key(version,B_N_PKEY(tb->R[0],0))){ + temp_rem = n_rem << (tb->tb_sb->s_blocksize_bits - + UNFM_P_SHIFT); + } set_le_key_k_offset (version, B_N_PKEY(tb->R[0],0), - le_key_k_offset (version, B_N_PKEY(tb->R[0],0)) + n_rem); + le_key_k_offset (version, B_N_PKEY(tb->R[0],0)) + temp_rem); set_le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0]), - le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0])) + n_rem); + le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0])) + temp_rem); } /* k_offset (B_N_PKEY(tb->R[0],0)) += n_rem; k_offset (B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0])) += n_rem;*/ @@ -736,13 +740,12 @@ leaf_paste_in_buffer(&bi, 0, n_shift, tb->insert_size[0] - n_rem, r_body, r_zeros_number); if (is_indirect_le_ih (B_N_PITEM_HEAD(tb->R[0],0))) { - +#if 0 RFALSE( n_rem, "PAP-12160: paste more than one unformatted node pointer"); - - set_ih_free_space (B_N_PITEM_HEAD(tb->R[0],0), ((struct unfm_nodeinfo*)body)->unfm_freespace); +#endif + set_ih_free_space (B_N_PITEM_HEAD(tb->R[0],0), 0); } - tb->insert_size[0] = n_rem; if ( ! n_rem ) pos_in_item ++; @@ -781,7 +784,7 @@ } if (is_indirect_le_ih (pasted)) - set_ih_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); + set_ih_free_space (pasted, 0); zeros_num = tb->insert_size[0] = 0; } } @@ -858,12 +861,6 @@ const char * r_body; int version; - RFALSE( !is_direct_le_ih(ih), - /* The items which can be inserted are: - Stat_data item, direct item, indirect item and directory item which consist of only two entries "." and "..". - These items must not be broken except for a direct one. */ - "PAP-12205: non-direct item can not be broken when inserting"); - /* Move snum[i]-1 items from S[0] to S_new[i] */ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i] - 1, -1, S_new[i]); /* Remember key component and item length */ @@ -873,7 +870,7 @@ /* Calculate key component and item length to insert into S_new[i] */ set_le_ih_k_offset( ih, - le_ih_k_offset(ih) + (old_len - sbytes[i] ) ); + le_ih_k_offset(ih) + ((old_len - sbytes[i] )<<(is_indirect_le_ih(ih)?tb->tb_sb->s_blocksize_bits - UNFM_P_SHIFT:0)) ); put_ih_item_len( ih, sbytes[i] ); @@ -883,13 +880,13 @@ bi.bi_parent = 0; bi.bi_position = 0; - if ( le_ih_k_offset (ih) - old_key_comp > zeros_num ) { + if ( (old_len - sbytes[i]) > zeros_num ) { r_zeros_number = 0; - r_body = body + (le_ih_k_offset(ih) - old_key_comp) - zeros_num; + r_body = body + (old_len - sbytes[i]) - zeros_num; } else { r_body = body; - r_zeros_number = zeros_num - (le_ih_k_offset (ih) - old_key_comp); + r_zeros_number = zeros_num - (old_len - sbytes[i]); zeros_num -= r_zeros_number; } @@ -1010,11 +1007,13 @@ tmp = B_N_PITEM_HEAD(S_new[i],0); if (is_indirect_le_ih (tmp)) { - if (n_rem) - reiserfs_panic (tb->tb_sb, "PAP-12230: balance_leaf: invalid action with indirect item"); - set_ih_free_space (tmp, ((struct unfm_nodeinfo*)body)->unfm_freespace); + set_ih_free_space (tmp, 0); + set_le_ih_k_offset( tmp, le_ih_k_offset(tmp) + + (n_rem << (tb->tb_sb->s_blocksize_bits - UNFM_P_SHIFT))); + } else { + set_le_ih_k_offset( tmp, le_ih_k_offset(tmp) + + n_rem ); } - set_le_ih_k_offset( tmp, le_ih_k_offset(tmp) + n_rem ); } tb->insert_size[0] = n_rem; @@ -1060,7 +1059,7 @@ /* if we paste to indirect item update ih_free_space */ if (is_indirect_le_ih (pasted)) - set_ih_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); + set_ih_free_space (pasted, 0); zeros_num = tb->insert_size[0] = 0; } } @@ -1152,11 +1151,12 @@ leaf_paste_in_buffer (&bi, item_pos, pos_in_item, tb->insert_size[0], body, zeros_num); if (is_indirect_le_ih (pasted)) { - +#if 0 RFALSE( tb->insert_size[0] != UNFM_P_SIZE, "PAP-12280: insert_size for indirect item must be %d, not %d", UNFM_P_SIZE, tb->insert_size[0]); - set_ih_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); +#endif + set_ih_free_space (pasted, 0); } tb->insert_size[0] = 0; } diff -urN linux-2.5.69-bk13/fs/reiserfs/file.c linux-2.5.69-bk14/fs/reiserfs/file.c --- linux-2.5.69-bk13/fs/reiserfs/file.c 2003-05-04 16:52:49.000000000 -0700 +++ linux-2.5.69-bk14/fs/reiserfs/file.c 2003-05-20 04:46:47.000000000 -0700 @@ -6,6 +6,8 @@ #include #include #include +#include +#include /* ** We pack the tails of files on file close, not at the time they are written. @@ -140,9 +142,1018 @@ return error ; } +/* I really do not want to play with memory shortage right now, so + to simplify the code, we are not going to write more than this much pages at + a time. This still should considerably improve performance compared to 4k + at a time case. This is 32 pages of 4k size. */ +#define REISERFS_WRITE_PAGES_AT_A_TIME (128 * 1024) / PAGE_CACHE_SIZE + +/* Allocates blocks for a file to fulfil write request. + Maps all unmapped but prepared pages from the list. + Updates metadata with newly allocated blocknumbers as needed */ +int reiserfs_allocate_blocks_for_region( + struct inode *inode, /* Inode we work with */ + loff_t pos, /* Writing position */ + int num_pages, /* number of pages write going + to touch */ + int write_bytes, /* amount of bytes to write */ + struct page **prepared_pages, /* array of + prepared pages + */ + int blocks_to_allocate /* Amount of blocks we + need to allocate to + fit the data into file + */ + ) +{ + struct cpu_key key; // cpu key of item that we are going to deal with + struct item_head *ih; // pointer to item head that we are going to deal with + struct buffer_head *bh; // Buffer head that contains items that we are going to deal with + struct reiserfs_transaction_handle th; // transaction handle for transaction we are going to create. + __u32 * item; // pointer to item we are going to deal with + INITIALIZE_PATH(path); // path to item, that we are going to deal with. + b_blocknr_t allocated_blocks[blocks_to_allocate]; // Pointer to a place where allocated blocknumbers would be stored. Right now statically allocated, later that will change. + reiserfs_blocknr_hint_t hint; // hint structure for block allocator. + size_t res; // return value of various functions that we call. + int curr_block; // current block used to keep track of unmapped blocks. + int i; // loop counter + int itempos; // position in item + unsigned int from = (pos & (PAGE_CACHE_SIZE - 1)); // writing position in + // first page + unsigned int to = ((pos + write_bytes - 1) & (PAGE_CACHE_SIZE - 1)) + 1; /* last modified byte offset in last page */ + __u64 hole_size ; // amount of blocks for a file hole, if it needed to be created. + int modifying_this_item = 0; // Flag for items traversal code to keep track + // of the fact that we already prepared + // current block for journal + + + RFALSE(!blocks_to_allocate, "green-9004: tried to allocate zero blocks?"); + + /* First we compose a key to point at the writing position, we want to do + that outside of any locking region. */ + make_cpu_key (&key, inode, pos+1, TYPE_ANY, 3/*key length*/); + + /* If we came here, it means we absolutely need to open a transaction, + since we need to allocate some blocks */ + reiserfs_write_lock(inode->i_sb); // Journaling stuff and we need that. + journal_begin(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); // Wish I know if this number enough + reiserfs_update_inode_transaction(inode) ; + + /* Look for the in-tree position of our write, need path for block allocator */ + res = search_for_position_by_key(inode->i_sb, &key, &path); + if ( res == IO_ERROR ) { + res = -EIO; + goto error_exit; + } + + /* Allocate blocks */ + /* First fill in "hint" structure for block allocator */ + hint.th = &th; // transaction handle. + hint.path = &path; // Path, so that block allocator can determine packing locality or whatever it needs to determine. + hint.inode = inode; // Inode is needed by block allocator too. + hint.search_start = 0; // We have no hint on where to search free blocks for block allocator. + hint.key = key.on_disk_key; // on disk key of file. + hint.block = inode->i_blocks>>(inode->i_sb->s_blocksize_bits-9); // Number of disk blocks this file occupies already. + hint.formatted_node = 0; // We are allocating blocks for unformatted node. + hint.preallocate = 0; // We do not do any preallocation for now. + + /* Call block allocator to allocate blocks */ + res = reiserfs_allocate_blocknrs(&hint, allocated_blocks, blocks_to_allocate, blocks_to_allocate); + if ( res != CARRY_ON ) { + if ( res == NO_DISK_SPACE ) { + /* We flush the transaction in case of no space. This way some + blocks might become free */ + SB_JOURNAL(inode->i_sb)->j_must_wait = 1; + restart_transaction(&th, inode, &path); + + /* We might have scheduled, so search again */ + res = search_for_position_by_key(inode->i_sb, &key, &path); + if ( res == IO_ERROR ) { + res = -EIO; + goto error_exit; + } + + /* update changed info for hint structure. */ + res = reiserfs_allocate_blocknrs(&hint, allocated_blocks, blocks_to_allocate, blocks_to_allocate); + if ( res != CARRY_ON ) { + res = -ENOSPC; + pathrelse(&path); + goto error_exit; + } + } else { + res = -ENOSPC; + pathrelse(&path); + goto error_exit; + } + } + +#ifdef __BIG_ENDIAN + // Too bad, I have not found any way to convert a given region from + // cpu format to little endian format + { + int i; + for ( i = 0; i < blocks_to_allocate ; i++) + allocated_blocks[i]=cpu_to_le32(allocated_blocks[i]); + } +#endif + + /* Blocks allocating well might have scheduled and tree might have changed, + let's search the tree again */ + /* find where in the tree our write should go */ + res = search_for_position_by_key(inode->i_sb, &key, &path); + if ( res == IO_ERROR ) { + res = -EIO; + goto error_exit_free_blocks; + } + + bh = get_last_bh( &path ); // Get a bufferhead for last element in path. + ih = get_ih( &path ); // Get a pointer to last item head in path. + item = get_item( &path ); // Get a pointer to last item in path + + /* Let's see what we have found */ + if ( res != POSITION_FOUND ) { /* position not found, this means that we + might need to append file with holes + first */ + // Since we are writing past the file's end, we need to find out if + // there is a hole that needs to be inserted before our writing + // position, and how many blocks it is going to cover (we need to + // populate pointers to file blocks representing the hole with zeros) + + hole_size = (pos + 1 - (le_key_k_offset( get_inode_item_key_version(inode), &(ih->ih_key))+op_bytes_number(ih, inode->i_sb->s_blocksize))) >> inode->i_sb->s_blocksize_bits; + + if ( hole_size > 0 ) { + int to_paste = min_t(__u64, hole_size, MAX_ITEM_LEN(inode->i_sb->s_blocksize)/UNFM_P_SIZE ); // How much data to insert first time. + /* area filled with zeroes, to supply as list of zero blocknumbers + We allocate it outside of loop just in case loop would spin for + several iterations. */ + char *zeros = kmalloc(to_paste*UNFM_P_SIZE, GFP_ATOMIC); // We cannot insert more than MAX_ITEM_LEN bytes anyway. + if ( !zeros ) { + res = -ENOMEM; + goto error_exit_free_blocks; + } + memset ( zeros, 0, to_paste*UNFM_P_SIZE); + do { + to_paste = min_t(__u64, hole_size, MAX_ITEM_LEN(inode->i_sb->s_blocksize)/UNFM_P_SIZE ); + if ( is_indirect_le_ih(ih) ) { + /* Ok, there is existing indirect item already. Need to append it */ + /* Calculate position past inserted item */ + make_cpu_key( &key, inode, le_key_k_offset( get_inode_item_key_version(inode), &(ih->ih_key)) + op_bytes_number(ih, inode->i_sb->s_blocksize), TYPE_INDIRECT, 3); + res = reiserfs_paste_into_item( &th, &path, &key, (char *)zeros, UNFM_P_SIZE*to_paste); + if ( res ) { + kfree(zeros); + goto error_exit_free_blocks; + } + } else if ( is_statdata_le_ih(ih) ) { + /* No existing item, create it */ + /* item head for new item */ + struct item_head ins_ih; + + /* create a key for our new item */ + make_cpu_key( &key, inode, 1, TYPE_INDIRECT, 3); + + /* Create new item head for our new item */ + make_le_item_head (&ins_ih, &key, key.version, 1, + TYPE_INDIRECT, to_paste*UNFM_P_SIZE, + 0 /* free space */); + + /* Find where such item should live in the tree */ + res = search_item (inode->i_sb, &key, &path); + if ( res != ITEM_NOT_FOUND ) { + /* item should not exist, otherwise we have error */ + if ( res != -ENOSPC ) { + reiserfs_warning ("green-9008: search_by_key (%K) returned %d\n", + &key, res); + } + res = -EIO; + kfree(zeros); + goto error_exit_free_blocks; + } + res = reiserfs_insert_item( &th, &path, &key, &ins_ih, (char *)zeros); + } else { + reiserfs_panic(inode->i_sb, "green-9011: Unexpected key type %K\n", &key); + } + if ( res ) { + kfree(zeros); + goto error_exit_free_blocks; + } + /* Now we want to check if transaction is too full, and if it is + we restart it. This will also free the path. */ + if (journal_transaction_should_end(&th, th.t_blocks_allocated)) + restart_transaction(&th, inode, &path); + + /* Well, need to recalculate path and stuff */ + set_cpu_key_k_offset( &key, cpu_key_k_offset(&key) + (to_paste << inode->i_blkbits)); + res = search_for_position_by_key(inode->i_sb, &key, &path); + if ( res == IO_ERROR ) { + res = -EIO; + kfree(zeros); + goto error_exit_free_blocks; + } + bh=get_last_bh(&path); + ih=get_ih(&path); + item = get_item(&path); + hole_size -= to_paste; + } while ( hole_size ); + kfree(zeros); + } + } + + // Go through existing indirect items first + // replace all zeroes with blocknumbers from list + // Note that if no corresponding item was found, by previous search, + // it means there are no existing in-tree representation for file area + // we are going to overwrite, so there is nothing to scan through for holes. + for ( curr_block = 0, itempos = path.pos_in_item ; curr_block < blocks_to_allocate && res == POSITION_FOUND ; ) { + + if ( itempos >= ih_item_len(ih)/UNFM_P_SIZE ) { + /* We run out of data in this indirect item, let's look for another + one. */ + /* First if we are already modifying current item, log it */ + if ( modifying_this_item ) { + journal_mark_dirty (&th, inode->i_sb, bh); + modifying_this_item = 0; + } + /* Then set the key to look for a new indirect item (offset of old + item is added to old item length */ + set_cpu_key_k_offset( &key, le_key_k_offset( get_inode_item_key_version(inode), &(ih->ih_key)) + op_bytes_number(ih, inode->i_sb->s_blocksize)); + /* Search ofor position of new key in the tree. */ + res = search_for_position_by_key(inode->i_sb, &key, &path); + if ( res == IO_ERROR) { + res = -EIO; + goto error_exit_free_blocks; + } + bh=get_last_bh(&path); + ih=get_ih(&path); + item = get_item(&path); + itempos = path.pos_in_item; + continue; // loop to check all kinds of conditions and so on. + } + /* Ok, we have correct position in item now, so let's see if it is + representing file hole (blocknumber is zero) and fill it if needed */ + if ( !item[itempos] ) { + /* Ok, a hole. Now we need to check if we already prepared this + block to be journaled */ + while ( !modifying_this_item ) { // loop until succeed + /* Well, this item is not journaled yet, so we must prepare + it for journal first, before we can change it */ + struct item_head tmp_ih; // We copy item head of found item, + // here to detect if fs changed under + // us while we were preparing for + // journal. + int fs_gen; // We store fs generation here to find if someone + // changes fs under our feet + + copy_item_head (&tmp_ih, ih); // Remember itemhead + fs_gen = get_generation (inode->i_sb); // remember fs generation + reiserfs_prepare_for_journal(inode->i_sb, bh, 1); // Prepare a buffer within which indirect item is stored for changing. + if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) { + // Sigh, fs was changed under us, we need to look for new + // location of item we are working with + + /* unmark prepaerd area as journaled and search for it's + new position */ + reiserfs_restore_prepared_buffer(inode->i_sb, bh); + res = search_for_position_by_key(inode->i_sb, &key, &path); + if ( res == IO_ERROR) { + res = -EIO; + goto error_exit_free_blocks; + } + bh=get_last_bh(&path); + ih=get_ih(&path); + item = get_item(&path); + // Itempos is still the same + continue; + } + modifying_this_item = 1; + } + item[itempos] = allocated_blocks[curr_block]; // Assign new block + curr_block++; + } + itempos++; + } + + if ( modifying_this_item ) { // We need to log last-accessed block, if it + // was modified, but not logged yet. + journal_mark_dirty (&th, inode->i_sb, bh); + } + + if ( curr_block < blocks_to_allocate ) { + // Oh, well need to append to indirect item, or to create indirect item + // if there weren't any + if ( is_indirect_le_ih(ih) ) { + // Existing indirect item - append. First calculate key for append + // position. We do not need to recalculate path as it should + // already point to correct place. + make_cpu_key( &key, inode, le_key_k_offset( get_inode_item_key_version(inode), &(ih->ih_key)) + op_bytes_number(ih, inode->i_sb->s_blocksize), TYPE_INDIRECT, 3); + res = reiserfs_paste_into_item( &th, &path, &key, (char *)(allocated_blocks+curr_block), UNFM_P_SIZE*(blocks_to_allocate-curr_block)); + if ( res ) { + goto error_exit_free_blocks; + } + } else if (is_statdata_le_ih(ih) ) { + // Last found item was statdata. That means we need to create indirect item. + struct item_head ins_ih; /* itemhead for new item */ + + /* create a key for our new item */ + make_cpu_key( &key, inode, 1, TYPE_INDIRECT, 3); // Position one, + // because that's + // where first + // indirect item + // begins + /* Create new item head for our new item */ + make_le_item_head (&ins_ih, &key, key.version, 1, TYPE_INDIRECT, + (blocks_to_allocate-curr_block)*UNFM_P_SIZE, + 0 /* free space */); + /* Find where such item should live in the tree */ + res = search_item (inode->i_sb, &key, &path); + if ( res != ITEM_NOT_FOUND ) { + /* Well, if we have found such item already, or some error + occured, we need to warn user and return error */ + if ( res != -ENOSPC ) { + reiserfs_warning ("green-9009: search_by_key (%K) returned %d\n", + &key, res); + } + res = -EIO; + goto error_exit_free_blocks; + } + /* Insert item into the tree with the data as its body */ + res = reiserfs_insert_item( &th, &path, &key, &ins_ih, (char *)(allocated_blocks+curr_block)); + } else { + reiserfs_panic(inode->i_sb, "green-9010: unexpected item type for key %K\n",&key); + } + } + + /* Now the final thing, if we have grew the file, we must update it's size*/ + if ( pos + write_bytes > inode->i_size) { + inode->i_size = pos + write_bytes; // Set new size + } + + /* Amount of on-disk blocks used by file have changed, update it */ + inode->i_blocks += blocks_to_allocate << (inode->i_blkbits - 9); + reiserfs_update_sd(&th, inode); // And update on-disk metadata + // finish all journal stuff now, We are not going to play with metadata + // anymore. + pathrelse(&path); + journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); + reiserfs_write_unlock(inode->i_sb); + + // go through all the pages/buffers and map the buffers to newly allocated + // blocks (so that system knows where to write these pages later). + curr_block = 0; + for ( i = 0; i < num_pages ; i++ ) { + struct page *page=prepared_pages[i]; //current page + struct buffer_head *head = page_buffers(page);// first buffer for a page + int block_start, block_end; // in-page offsets for buffers. + + if (!page_buffers(page)) + reiserfs_panic(inode->i_sb, "green-9005: No buffers for prepared page???"); + + /* For each buffer in page */ + for(bh = head, block_start = 0; bh != head || !block_start; + block_start=block_end, bh = bh->b_this_page) { + if (!bh) + reiserfs_panic(inode->i_sb, "green-9006: Allocated but absent buffer for a page?"); + block_end = block_start+inode->i_sb->s_blocksize; + if (i == 0 && block_end <= from ) + /* if this buffer is before requested data to map, skip it */ + continue; + if (i == num_pages - 1 && block_start >= to) + /* If this buffer is after requested data to map, abort + processing of current page */ + break; + + if ( !buffer_mapped(bh) ) { // Ok, unmapped buffer, need to map it + map_bh( bh, inode->i_sb, le32_to_cpu(allocated_blocks[curr_block])); + curr_block++; + } + } + } + + RFALSE( curr_block > blocks_to_allocate, "green-9007: Used too many blocks? weird"); + + return 0; + +// Need to deal with transaction here. +error_exit_free_blocks: + pathrelse(&path); + // free blocks + for( i = 0; i < blocks_to_allocate; i++ ) + reiserfs_free_block( &th, le32_to_cpu(allocated_blocks[i])); + +error_exit: + journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); + reiserfs_write_unlock(inode->i_sb); + + return res; +} + +/* Unlock pages prepared by reiserfs_prepare_file_region_for_write */ +void reiserfs_unprepare_pages(struct page **prepared_pages, /* list of locked pages */ + int num_pages /* amount of pages */) { + int i; // loop counter + + for (i=0; i < num_pages ; i++) { + struct page *page = prepared_pages[i]; + + try_to_free_buffers(page); + kunmap(page); + unlock_page(page); + page_cache_release(page); + } +} + +/* This function will copy data from userspace to specified pages within + supplied byte range */ +int reiserfs_copy_from_user_to_file_region( + loff_t pos, /* In-file position */ + int num_pages, /* Number of pages affected */ + int write_bytes, /* Amount of bytes to write */ + struct page **prepared_pages, /* pointer to + array to + prepared pages + */ + const char *buf /* Pointer to user-supplied + data*/ + ) +{ + long page_fault=0; // status of copy_from_user. + int i; // loop counter. + int offset; // offset in page + + for ( i = 0, offset = (pos & (PAGE_CACHE_SIZE-1)); i < num_pages ; i++,offset=0) { + int count = min_t(int,PAGE_CACHE_SIZE-offset,write_bytes); // How much of bytes to write to this page + struct page *page=prepared_pages[i]; // Current page we process. + + fault_in_pages_readable( buf, count); + + /* Copy data from userspace to the current page */ + kmap(page); + page_fault = __copy_from_user(page_address(page)+offset, buf, count); // Copy the data. + /* Flush processor's dcache for this page */ + flush_dcache_page(page); + kunmap(page); + buf+=count; + write_bytes-=count; + + if (page_fault) + break; // Was there a fault? abort. + } + + return page_fault?-EFAULT:0; +} + + + +/* Submit pages for write. This was separated from actual file copying + because we might want to allocate block numbers in-between. + This function assumes that caller will adjust file size to correct value. */ +int reiserfs_submit_file_region_for_write( + loff_t pos, /* Writing position offset */ + int num_pages, /* Number of pages to write */ + int write_bytes, /* number of bytes to write */ + struct page **prepared_pages /* list of pages */ + ) +{ + int status; // return status of block_commit_write. + int retval = 0; // Return value we are going to return. + int i; // loop counter + int offset; // Writing offset in page. + + for ( i = 0, offset = (pos & (PAGE_CACHE_SIZE-1)); i < num_pages ; i++,offset=0) { + int count = min_t(int,PAGE_CACHE_SIZE-offset,write_bytes); // How much of bytes to write to this page + struct page *page=prepared_pages[i]; // Current page we process. + + status = block_commit_write(page, offset, offset+count); + if ( status ) + retval = status; // To not overcomplicate matters We are going to + // submit all the pages even if there was error. + // we only remember error status to report it on + // exit. + write_bytes-=count; + SetPageReferenced(page); + unlock_page(page); // We unlock the page as it was locked by earlier call + // to grab_cache_page + page_cache_release(page); + } + return retval; +} + +/* Look if passed writing region is going to touch file's tail + (if it is present). And if it is, convert the tail to unformatted node */ +int reiserfs_check_for_tail_and_convert( struct inode *inode, /* inode to deal with */ + loff_t pos, /* Writing position */ + int write_bytes /* amount of bytes to write */ + ) +{ + INITIALIZE_PATH(path); // needed for search_for_position + struct cpu_key key; // Key that would represent last touched writing byte. + struct item_head *ih; // item header of found block; + int res; // Return value of various functions we call. + int cont_expand_offset; // We will put offset for generic_cont_expand here + // This can be int just because tails are created + // only for small files. + +/* this embodies a dependency on a particular tail policy */ + if ( inode->i_size >= inode->i_sb->s_blocksize*4 ) { + /* such a big files do not have tails, so we won't bother ourselves + to look for tails, simply return */ + return 0; + } + + reiserfs_write_lock(inode->i_sb); + /* find the item containing the last byte to be written, or if + * writing past the end of the file then the last item of the + * file (and then we check its type). */ + make_cpu_key (&key, inode, pos+write_bytes+1, TYPE_ANY, 3/*key length*/); + res = search_for_position_by_key(inode->i_sb, &key, &path); + if ( res == IO_ERROR ) { + reiserfs_write_unlock(inode->i_sb); + return -EIO; + } + ih = get_ih(&path); + res = 0; + if ( is_direct_le_ih(ih) ) { + /* Ok, closest item is file tail (tails are stored in "direct" + * items), so we need to unpack it. */ + /* To not overcomplicate matters, we just call generic_cont_expand + which will in turn call other stuff and finally will boil down to + reiserfs_get_block() that would do necessary conversion. */ + cont_expand_offset = le_key_k_offset(get_inode_item_key_version(inode), &(ih->ih_key)); + pathrelse(&path); + res = generic_cont_expand( inode, cont_expand_offset); + } else + pathrelse(&path); + + reiserfs_write_unlock(inode->i_sb); + return res; +} + +/* This function locks pages starting from @pos for @inode. + @num_pages pages are locked and stored in + @prepared_pages array. Also buffers are allocated for these pages. + First and last page of the region is read if it is overwritten only + partially. If last page did not exist before write (file hole or file + append), it is zeroed, then. + Returns number of unallocated blocks that should be allocated to cover + new file data.*/ +int reiserfs_prepare_file_region_for_write( + struct inode *inode /* Inode of the file */, + loff_t pos, /* position in the file */ + int num_pages, /* number of pages to + prepare */ + int write_bytes, /* Amount of bytes to be + overwritten from + @pos */ + struct page **prepared_pages /* pointer to array + where to store + prepared pages */ + ) +{ + int res=0; // Return values of different functions we call. + unsigned long index = pos >> PAGE_CACHE_SHIFT; // Offset in file in pages. + int from = (pos & (PAGE_CACHE_SIZE - 1)); // Writing offset in first page + int to = ((pos + write_bytes - 1) & (PAGE_CACHE_SIZE - 1)) + 1; + /* offset of last modified byte in last + page */ + struct address_space *mapping = inode->i_mapping; // Pages are mapped here. + int i; // Simple counter + int blocks = 0; /* Return value (blocks that should be allocated) */ + struct buffer_head *bh, *head; // Current bufferhead and first bufferhead + // of a page. + unsigned block_start, block_end; // Starting and ending offsets of current + // buffer in the page. + struct buffer_head *wait[2], **wait_bh=wait; // Buffers for page, if + // Page appeared to be not up + // to date. Note how we have + // at most 2 buffers, this is + // because we at most may + // partially overwrite two + // buffers for one page. One at // the beginning of write area + // and one at the end. + // Everything inthe middle gets // overwritten totally. + + struct cpu_key key; // cpu key of item that we are going to deal with + struct item_head *ih = NULL; // pointer to item head that we are going to deal with + struct buffer_head *itembuf=NULL; // Buffer head that contains items that we are going to deal with + INITIALIZE_PATH(path); // path to item, that we are going to deal with. + __u32 * item=0; // pointer to item we are going to deal with + + + if ( num_pages < 1 ) { + reiserfs_warning("green-9001: reiserfs_prepare_file_region_for_write called with zero number of pages to process\n"); + return -EFAULT; + } + + /* We have 2 loops for pages. In first loop we grab and lock the pages, so + that nobody would touch these until we release the pages. Then + we'd start to deal with mapping buffers to blocks. */ + for ( i = 0; i < num_pages; i++) { + prepared_pages[i] = grab_cache_page(mapping, index + i); // locks the page + if ( !prepared_pages[i]) { + res = -ENOMEM; + goto failed_page_grabbing; + } + if (!page_has_buffers(prepared_pages[i])) + create_empty_buffers(prepared_pages[i], inode->i_sb->s_blocksize, 0); + } + + /* Let's count amount of blocks for a case where all the blocks + overwritten are new (we will substract already allocated blocks later)*/ + if ( num_pages > 2 ) + /* These are full-overwritten pages so we count all the blocks in + these pages are counted as needed to be allocated */ + blocks = (num_pages - 2) << (PAGE_CACHE_SHIFT - inode->i_blkbits); + + /* count blocks needed for first page (possibly partially written) */ + blocks += ((PAGE_CACHE_SIZE - from) >> inode->i_blkbits) + + !!(from & (inode->i_sb->s_blocksize-1)); /* roundup */ + + /* Now we account for last page. If last page == first page (we + overwrite only one page), we substract all the blocks past the + last writing position in a page out of already calculated number + of blocks */ + blocks += ((num_pages > 1) << (PAGE_CACHE_SHIFT-inode->i_blkbits)) - + ((PAGE_CACHE_SIZE - to) >> inode->i_blkbits); + /* Note how we do not roundup here since partial blocks still + should be allocated */ + + /* Now if all the write area lies past the file end, no point in + maping blocks, since there is none, so we just zero out remaining + parts of first and last pages in write area (if needed) */ + if ( (pos & ~(PAGE_CACHE_SIZE - 1)) > inode->i_size ) { + if ( from != 0 ) {/* First page needs to be partially zeroed */ + char *kaddr = kmap_atomic(prepared_pages[0], KM_USER0); + memset(kaddr, 0, from); + kunmap_atomic( kaddr, KM_USER0); + } + if ( to != PAGE_CACHE_SIZE ) { /* Last page needs to be partially zeroed */ + char *kaddr = kmap_atomic(prepared_pages[num_pages-1], KM_USER0); + memset(kaddr+to, 0, PAGE_CACHE_SIZE - to); + kunmap_atomic( kaddr, KM_USER0); + } + + /* Since all blocks are new - use already calculated value */ + return blocks; + } + + /* Well, since we write somewhere into the middle of a file, there is + possibility we are writing over some already allocated blocks, so + let's map these blocks and substract number of such blocks out of blocks + we need to allocate (calculated above) */ + /* Mask write position to start on blocksize, we do it out of the + loop for performance reasons */ + pos &= ~(inode->i_sb->s_blocksize - 1); + /* Set cpu key to the starting position in a file (on left block boundary)*/ + make_cpu_key (&key, inode, 1 + ((pos) & ~(inode->i_sb->s_blocksize - 1)), TYPE_ANY, 3/*key length*/); + + reiserfs_write_lock(inode->i_sb); // We need that for at least search_by_key() + for ( i = 0; i < num_pages ; i++ ) { + int item_pos=-1; /* Position in indirect item */ + + head = page_buffers(prepared_pages[i]); + /* For each buffer in the page */ + for(bh = head, block_start = 0; bh != head || !block_start; + block_start=block_end, bh = bh->b_this_page) { + if (!bh) + reiserfs_panic(inode->i_sb, "green-9002: Allocated but absent buffer for a page?"); + /* Find where this buffer ends */ + block_end = block_start+inode->i_sb->s_blocksize; + if (i == 0 && block_end <= from ) + /* if this buffer is before requested data to map, skip it*/ + continue; + + if (i == num_pages - 1 && block_start >= to) { + /* If this buffer is after requested data to map, abort + processing of current page */ + break; + } + + if ( buffer_mapped(bh) && bh->b_blocknr !=0 ) { + /* This is optimisation for a case where buffer is mapped + and have blocknumber assigned. In case significant amount + of such buffers are present, we may avoid some amount + of search_by_key calls. + Probably it would be possible to move parts of this code + out of BKL, but I afraid that would overcomplicate code + without any noticeable benefit. + */ + item_pos++; + /* Update the key */ + set_cpu_key_k_offset( &key, cpu_key_k_offset(&key) + inode->i_sb->s_blocksize); + blocks--; // Decrease the amount of blocks that need to be + // allocated + continue; // Go to the next buffer + } + + if ( !itembuf || /* if first iteration */ + item_pos >= ih_item_len(ih)/UNFM_P_SIZE) + { /* or if we progressed past the + current unformatted_item */ + /* Try to find next item */ + res = search_for_position_by_key(inode->i_sb, &key, &path); + /* Abort if no more items */ + if ( res != POSITION_FOUND ) + break; + + /* Update information about current indirect item */ + itembuf = get_last_bh( &path ); + ih = get_ih( &path ); + item = get_item( &path ); + item_pos = path.pos_in_item; + + RFALSE( !is_indirect_le_ih (ih), "green-9003: indirect item expected"); + } + + /* See if there is some block associated with the file + at that position, map the buffer to this block */ + if ( get_block_num(item,item_pos) ) { + map_bh(bh, inode->i_sb, get_block_num(item,item_pos)); + blocks--; // Decrease the amount of blocks that need to be + // allocated + } + item_pos++; + /* Update the key */ + set_cpu_key_k_offset( &key, cpu_key_k_offset(&key) + inode->i_sb->s_blocksize); + } + } + pathrelse(&path); // Free the path + reiserfs_write_unlock(inode->i_sb); + + /* Now zero out unmappend buffers for the first and last pages of + write area or issue read requests if page is mapped. */ + /* First page, see if it is not uptodate */ + if ( !PageUptodate(prepared_pages[0]) ) { + head = page_buffers(prepared_pages[0]); + + /* For each buffer in page */ + for(bh = head, block_start = 0; bh != head || !block_start; + block_start=block_end, bh = bh->b_this_page) { + + if (!bh) + reiserfs_panic(inode->i_sb, "green-9002: Allocated but absent buffer for a page?"); + /* Find where this buffer ends */ + block_end = block_start+inode->i_sb->s_blocksize; + if ( block_end <= from ) + /* if this buffer is before requested data to map, skip it*/ + continue; + if ( block_start < from ) { /* Aha, our partial buffer */ + if ( buffer_mapped(bh) ) { /* If it is mapped, we need to + issue READ request for it to + not loose data */ + ll_rw_block(READ, 1, &bh); + *wait_bh++=bh; + } else { /* Not mapped, zero it */ + char *kaddr = kmap_atomic(prepared_pages[0], KM_USER0); + memset(kaddr+block_start, 0, from-block_start); + kunmap_atomic( kaddr, KM_USER0); + set_buffer_uptodate(bh); + } + } + } + } + + /* Last page, see if it is not uptodate, or if the last page is past the end of the file. */ + if ( !PageUptodate(prepared_pages[num_pages-1]) || + ((pos+write_bytes)>>PAGE_CACHE_SHIFT) > (inode->i_size>>PAGE_CACHE_SHIFT) ) { + head = page_buffers(prepared_pages[num_pages-1]); + + /* for each buffer in page */ + for(bh = head, block_start = 0; bh != head || !block_start; + block_start=block_end, bh = bh->b_this_page) { + + if (!bh) + reiserfs_panic(inode->i_sb, "green-9002: Allocated but absent buffer for a page?"); + /* Find where this buffer ends */ + block_end = block_start+inode->i_sb->s_blocksize; + if ( block_start >= to ) + /* if this buffer is after requested data to map, skip it*/ + break; + if ( block_end > to ) { /* Aha, our partial buffer */ + if ( buffer_mapped(bh) ) { /* If it is mapped, we need to + issue READ request for it to + not loose data */ + ll_rw_block(READ, 1, &bh); + *wait_bh++=bh; + } else { /* Not mapped, zero it */ + char *kaddr = kmap_atomic(prepared_pages[num_pages-1], KM_USER0); + memset(kaddr+to, 0, block_end-to); + kunmap_atomic( kaddr, KM_USER0); + set_buffer_uptodate(bh); + } + } + } + } + + /* Wait for read requests we made to happen, if necessary */ + while(wait_bh > wait) { + wait_on_buffer(*--wait_bh); + if (!buffer_uptodate(*wait_bh)) { + res = -EIO; + goto failed_read; + } + } + + return blocks; +failed_page_grabbing: + num_pages = i; +failed_read: + reiserfs_unprepare_pages(prepared_pages, num_pages); + return res; +} + +/* Write @count bytes at position @ppos in a file indicated by @file + from the buffer @buf. + + generic_file_write() is only appropriate for filesystems that are not seeking to optimize performance and want + something simple that works. It is not for serious use by general purpose filesystems, excepting the one that it was + written for (ext2/3). This is for several reasons: + + * It has no understanding of any filesystem specific optimizations. + + * It enters the filesystem repeatedly for each page that is written. + + * It depends on reiserfs_get_block() function which if implemented by reiserfs performs costly search_by_key + * operation for each page it is supplied with. By contrast reiserfs_file_write() feeds as much as possible at a time + * to reiserfs which allows for fewer tree traversals. + + * Each indirect pointer insertion takes a lot of cpu, because it involves memory moves inside of blocks. + + * Asking the block allocation code for blocks one at a time is slightly less efficient. + + All of these reasons for not using only generic file write were understood back when reiserfs was first miscoded to + use it, but we were in a hurry to make code freeze, and so it couldn't be revised then. This new code should make + things right finally. + + Future Features: providing search_by_key with hints. + +*/ +ssize_t reiserfs_file_write( struct file *file, /* the file we are going to write into */ + const char *buf, /* pointer to user supplied data +(in userspace) */ + size_t count, /* amount of bytes to write */ + loff_t *ppos /* pointer to position in file that we start writing at. Should be updated to + * new current position before returning. */ ) +{ + size_t already_written = 0; // Number of bytes already written to the file. + loff_t pos; // Current position in the file. + size_t res; // return value of various functions that we call. + struct inode *inode = file->f_dentry->d_inode; // Inode of the file that we are writing to. + struct page * prepared_pages[REISERFS_WRITE_PAGES_AT_A_TIME]; + /* To simplify coding at this time, we store + locked pages in array for now */ + if ( count <= PAGE_CACHE_SIZE || file->f_flags & O_DIRECT) + return generic_file_write(file, buf, count, ppos); + + if ( unlikely((ssize_t) count < 0 )) + return -EINVAL; + + if (unlikely(!access_ok(VERIFY_READ, buf, count))) + return -EFAULT; + + down(&inode->i_sem); // locks the entire file for just us + + pos = *ppos; + + /* Check if we can write to specified region of file, file + is not overly big and this kind of stuff. Adjust pos and + count, if needed */ + res = generic_write_checks(inode, file, &pos, &count, 0); + if (res) + goto out; + + if ( count == 0 ) + goto out; + + remove_suid(file->f_dentry); + inode_update_time(inode, 1); /* Both mtime and ctime */ + + // Ok, we are done with all the checks. + + // Now we should start real work + + /* If we are going to write past the file's packed tail or if we are going + to overwrite part of the tail, we need that tail to be converted into + unformatted node */ + res = reiserfs_check_for_tail_and_convert( inode, pos, count); + if (res) + goto out; + + while ( count > 0) { + /* This is the main loop in which we running until some error occures + or until we write all of the data. */ + int num_pages;/* amount of pages we are going to write this iteration */ + int write_bytes; /* amount of bytes to write during this iteration */ + int blocks_to_allocate; /* how much blocks we need to allocate for + this iteration */ + + /* (pos & (PAGE_CACHE_SIZE-1)) is an idiom for offset into a page of pos*/ + num_pages = !!((pos+count) & (PAGE_CACHE_SIZE - 1)) + /* round up partial + pages */ + ((count + (pos & (PAGE_CACHE_SIZE-1))) >> PAGE_CACHE_SHIFT); + /* convert size to amount of + pages */ + reiserfs_write_lock(inode->i_sb); + if ( num_pages > REISERFS_WRITE_PAGES_AT_A_TIME + || num_pages > reiserfs_can_fit_pages(inode->i_sb) ) { + /* If we were asked to write more data than we want to or if there + is not that much space, then we shorten amount of data to write + for this iteration. */ + num_pages = min_t(int, REISERFS_WRITE_PAGES_AT_A_TIME, reiserfs_can_fit_pages(inode->i_sb)); + /* Also we should not forget to set size in bytes accordingly */ + write_bytes = (num_pages << PAGE_CACHE_SHIFT) - + (pos & (PAGE_CACHE_SIZE-1)); + /* If position is not on the + start of the page, we need + to substract the offset + within page */ + } else + write_bytes = count; + + /* reserve the blocks to be allocated later, so that later on + we still have the space to write the blocks to */ + reiserfs_claim_blocks_to_be_allocated(inode->i_sb, num_pages << (PAGE_CACHE_SHIFT - inode->i_blkbits)); + reiserfs_write_unlock(inode->i_sb); + + if ( !num_pages ) { /* If we do not have enough space even for */ + res = -ENOSPC; /* single page, return -ENOSPC */ + if ( pos > (inode->i_size & (inode->i_sb->s_blocksize-1))) + break; // In case we are writing past the file end, break. + // Otherwise we are possibly overwriting the file, so + // let's set write size to be equal or less than blocksize. + // This way we get it correctly for file holes. + // But overwriting files on absolutelly full volumes would not + // be very efficient. Well, people are not supposed to fill + // 100% of disk space anyway. + write_bytes = min_t(int, count, inode->i_sb->s_blocksize - (pos & (inode->i_sb->s_blocksize - 1))); + num_pages = 1; + } + + /* Prepare for writing into the region, read in all the + partially overwritten pages, if needed. And lock the pages, + so that nobody else can access these until we are done. + We get number of actual blocks needed as a result.*/ + blocks_to_allocate = reiserfs_prepare_file_region_for_write(inode, pos, num_pages, write_bytes, prepared_pages); + if ( blocks_to_allocate < 0 ) { + res = blocks_to_allocate; + reiserfs_release_claimed_blocks(inode->i_sb, num_pages << (PAGE_CACHE_SHIFT - inode->i_blkbits)); + break; + } + + /* First we correct our estimate of how many blocks we need */ + reiserfs_release_claimed_blocks(inode->i_sb, (num_pages << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits)) - blocks_to_allocate ); + + if ( blocks_to_allocate > 0) {/*We only allocate blocks if we need to*/ + /* Fill in all the possible holes and append the file if needed */ + res = reiserfs_allocate_blocks_for_region(inode, pos, num_pages, write_bytes, prepared_pages, blocks_to_allocate); + } else if ( pos + write_bytes > inode->i_size ) { + /* File might have grown even though no new blocks were added */ + inode->i_size = pos + write_bytes; + inode->i_sb->s_op->dirty_inode(inode); + } + + /* well, we have allocated the blocks, so it is time to free + the reservation we made earlier. */ + reiserfs_release_claimed_blocks(inode->i_sb, blocks_to_allocate); + if ( res ) { + reiserfs_unprepare_pages(prepared_pages, num_pages); + break; + } + +/* NOTE that allocating blocks and filling blocks can be done in reverse order + and probably we would do that just to get rid of garbage in files after a + crash */ + + /* Copy data from user-supplied buffer to file's pages */ + res = reiserfs_copy_from_user_to_file_region(pos, num_pages, write_bytes, prepared_pages, buf); + if ( res ) { + reiserfs_unprepare_pages(prepared_pages, num_pages); + break; + } + + /* Send the pages to disk and unlock them. */ + res = reiserfs_submit_file_region_for_write(pos, num_pages, write_bytes, prepared_pages); + if ( res ) + break; + + already_written += write_bytes; + buf += write_bytes; + *ppos = pos += write_bytes; + count -= write_bytes; + } + + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) + res = generic_osync_inode(inode, OSYNC_METADATA|OSYNC_DATA); + + up(&inode->i_sem); + return (already_written != 0)?already_written:res; + +out: + up(&inode->i_sem); // unlock the file on exit. + return res; +} + struct file_operations reiserfs_file_operations = { .read = generic_file_read, - .write = generic_file_write, + .write = reiserfs_file_write, .ioctl = reiserfs_ioctl, .mmap = generic_file_mmap, .release = reiserfs_file_release, diff -urN linux-2.5.69-bk13/fs/reiserfs/inode.c linux-2.5.69-bk14/fs/reiserfs/inode.c --- linux-2.5.69-bk13/fs/reiserfs/inode.c 2003-05-04 16:53:09.000000000 -0700 +++ linux-2.5.69-bk14/fs/reiserfs/inode.c 2003-05-20 04:46:47.000000000 -0700 @@ -14,6 +14,8 @@ #include #include +extern int reiserfs_default_io_size; /* default io size devuned in super.c */ + /* args for the create parameter of reiserfs_get_block */ #define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */ #define GET_BLOCK_CREATE 1 /* add anything you need to find block */ @@ -766,7 +768,11 @@ pointer to 'block'-th block use block, which is already allocated */ struct cpu_key tmp_key; - struct unfm_nodeinfo un = {0, 0}; + unp_t unf_single=0; // We use this in case we need to allocate only + // one block which is a fastpath + unp_t *un; + __u64 max_to_insert=MAX_ITEM_LEN(inode->i_sb->s_blocksize)/UNFM_P_SIZE; + __u64 blocks_needed; RFALSE( pos_in_item != ih_item_len(ih) / UNFM_P_SIZE, "vs-804: invalid position for append"); @@ -775,30 +781,58 @@ le_key_k_offset (version, &(ih->ih_key)) + op_bytes_number (ih, inode->i_sb->s_blocksize), //pos_in_item * inode->i_sb->s_blocksize, TYPE_INDIRECT, 3);// key type is unimportant - - if (cpu_key_k_offset (&tmp_key) == cpu_key_k_offset (&key)) { + + blocks_needed = 1 + ((cpu_key_k_offset (&key) - cpu_key_k_offset (&tmp_key)) >> inode->i_sb->s_blocksize_bits); + RFALSE( blocks_needed < 0, "green-805: invalid offset"); + + if ( blocks_needed == 1 ) { + un = &unf_single; + } else { + un=kmalloc( min(blocks_needed,max_to_insert)*UNFM_P_SIZE, + GFP_ATOMIC); // We need to avoid scheduling. + if ( !un) { + un = &unf_single; + blocks_needed = 1; + max_to_insert = 0; + } else + memset(un, 0, UNFM_P_SIZE * min(blocks_needed,max_to_insert)); + } + if ( blocks_needed <= max_to_insert) { /* we are going to add target block to the file. Use allocated block for that */ - un.unfm_nodenum = cpu_to_le32 (allocated_block_nr); + un[blocks_needed-1] = cpu_to_le32 (allocated_block_nr); set_block_dev_mapped (bh_result, allocated_block_nr, inode); set_buffer_new(bh_result); done = 1; } else { /* paste hole to the indirect item */ + /* If kmalloc failed, max_to_insert becomes zero and it means we + only have space for one block */ + blocks_needed=max_to_insert?max_to_insert:1; } - retval = reiserfs_paste_into_item (&th, &path, &tmp_key, (char *)&un, UNFM_P_SIZE); + retval = reiserfs_paste_into_item (&th, &path, &tmp_key, (char *)un, UNFM_P_SIZE * blocks_needed); + + if (blocks_needed != 1) + kfree(un); + if (retval) { reiserfs_free_block (&th, allocated_block_nr); goto failure; } - if (un.unfm_nodenum) + if (done) { inode->i_blocks += inode->i_sb->s_blocksize / 512; + } else { + /* We need to mark new file size in case this function will be + interrupted/aborted later on. And we may do this only for + holes. */ + inode->i_size += inode->i_sb->s_blocksize * blocks_needed; + } //mark_tail_converted (inode); } - + if (done == 1) break; - + /* this loop could log more blocks than we had originally asked ** for. So, we have to allow the transaction to end if it is ** too big or too full. Update the inode so things are @@ -876,7 +910,7 @@ copy_key (INODE_PKEY (inode), &(ih->ih_key)); - inode->i_blksize = PAGE_SIZE; + inode->i_blksize = reiserfs_default_io_size; INIT_LIST_HEAD(&(REISERFS_I(inode)->i_prealloc_list )); REISERFS_I(inode)->i_flags = 0; @@ -1566,7 +1600,7 @@ } // these do not go to on-disk stat data inode->i_ino = le32_to_cpu (ih.ih_key.k_objectid); - inode->i_blksize = PAGE_SIZE; + inode->i_blksize = reiserfs_default_io_size; // store in in-core inode the key of stat data and version all // object items will have (directory items will have old offset diff -urN linux-2.5.69-bk13/fs/reiserfs/super.c linux-2.5.69-bk14/fs/reiserfs/super.c --- linux-2.5.69-bk13/fs/reiserfs/super.c 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/fs/reiserfs/super.c 2003-05-20 04:46:47.000000000 -0700 @@ -532,6 +532,11 @@ {NULL, 0} }; +int reiserfs_default_io_size = 128 * 1024; /* Default recommended I/O size is 128k. + There might be broken applications that are + confused by this. Use nolargeio mount option + to get usual i/o size = PAGE_SIZE. + */ /* proceed only one option from a list *cur - string containing of mount options opts - array of options which are accepted @@ -657,6 +662,7 @@ {"block-allocator", 'a', balloc, -1}, {"resize", 'r', 0, -1}, {"jdev", 'j', 0, -1}, + {"nolargeio", 'w', 0, -1}, {NULL, 0, 0, -1} }; @@ -688,6 +694,10 @@ } } + if ( c == 'w' ) { + reiserfs_default_io_size = PAGE_SIZE; + } + if (c == 'j') { if (arg && *arg && jdev_name) { *jdev_name = arg; @@ -1318,6 +1328,7 @@ reiserfs_proc_register( s, "oidmap", reiserfs_oidmap_in_proc ); reiserfs_proc_register( s, "journal", reiserfs_journal_in_proc ); init_waitqueue_head (&(sbi->s_wait)); + sbi->bitmap_lock = SPIN_LOCK_UNLOCKED; return (0); diff -urN linux-2.5.69-bk13/fs/reiserfs/tail_conversion.c linux-2.5.69-bk14/fs/reiserfs/tail_conversion.c --- linux-2.5.69-bk13/fs/reiserfs/tail_conversion.c 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/fs/reiserfs/tail_conversion.c 2003-05-20 04:46:47.000000000 -0700 @@ -30,7 +30,7 @@ key of unfm pointer to be pasted */ int n_blk_size, n_retval; /* returned value for reiserfs_insert_item and clones */ - struct unfm_nodeinfo unfm_ptr; /* Handle on an unformatted node + unp_t unfm_ptr; /* Handle on an unformatted node that will be inserted in the tree. */ @@ -59,8 +59,7 @@ p_le_ih = PATH_PITEM_HEAD (path); - unfm_ptr.unfm_nodenum = cpu_to_le32 (unbh->b_blocknr); - unfm_ptr.unfm_freespace = 0; // ??? + unfm_ptr = cpu_to_le32 (unbh->b_blocknr); if ( is_statdata_le_ih (p_le_ih) ) { /* Insert new indirect item. */ diff -urN linux-2.5.69-bk13/fs/sysfs/symlink.c linux-2.5.69-bk14/fs/sysfs/symlink.c --- linux-2.5.69-bk13/fs/sysfs/symlink.c 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/fs/sysfs/symlink.c 2003-05-20 04:46:47.000000000 -0700 @@ -80,7 +80,7 @@ char * s; depth = object_depth(kobj); - size = object_path_length(target) + depth * 3 - 1; + size = object_path_length(target) + depth * 3 + 1; if (size > PATH_MAX) return -ENAMETOOLONG; pr_debug("%s: depth = %d, size = %d\n",__FUNCTION__,depth,size); diff -urN linux-2.5.69-bk13/fs/xfs/Makefile linux-2.5.69-bk14/fs/xfs/Makefile --- linux-2.5.69-bk13/fs/xfs/Makefile 2003-05-04 16:53:35.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/Makefile 2003-05-20 04:46:47.000000000 -0700 @@ -132,7 +132,6 @@ # Objects in support/ xfs-y += $(addprefix support/, \ debug.o \ - kmem.o \ ktrace.o \ move.o \ mrlock.o \ diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_globals.c linux-2.5.69-bk14/fs/xfs/linux/xfs_globals.c --- linux-2.5.69-bk13/fs/xfs/linux/xfs_globals.c 2003-05-04 16:53:32.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_globals.c 2003-05-20 04:46:47.000000000 -0700 @@ -48,7 +48,7 @@ * Tunable XFS parameters. xfs_params is required even when CONFIG_SYSCTL=n, * other XFS code uses these values. */ -xfs_param_t xfs_params = { 0, 1, 0, 0, 0, 3 }; +xfs_param_t xfs_params = { 0, 1, 0, 0, 0, 3, 30 * HZ }; /* * Global system credential structure. diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_iops.c linux-2.5.69-bk14/fs/xfs/linux/xfs_iops.c --- linux-2.5.69-bk13/fs/xfs/linux/xfs_iops.c 2003-05-04 16:53:09.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_iops.c 2003-05-20 04:46:47.000000000 -0700 @@ -152,8 +152,6 @@ ip->i_rdev = to_kdev_t(rdev); validate_fields(dir); d_instantiate(dentry, ip); - mark_inode_dirty_sync(ip); - mark_inode_dirty_sync(dir); } if (!error && have_default_acl) { @@ -240,7 +238,6 @@ VN_HOLD(vp); validate_fields(ip); d_instantiate(dentry, ip); - mark_inode_dirty_sync(ip); } return -error; } @@ -261,8 +258,6 @@ if (!error) { validate_fields(dir); /* For size only */ validate_fields(inode); - mark_inode_dirty_sync(inode); - mark_inode_dirty_sync(dir); } return -error; @@ -296,8 +291,6 @@ d_instantiate(dentry, ip); validate_fields(dir); validate_fields(ip); /* size needs update */ - mark_inode_dirty_sync(ip); - mark_inode_dirty_sync(dir); } return -error; } @@ -315,8 +308,6 @@ if (!error) { validate_fields(inode); validate_fields(dir); - mark_inode_dirty_sync(inode); - mark_inode_dirty_sync(dir); } return -error; } @@ -346,7 +337,6 @@ validate_fields(odir); if (ndir != odir) validate_fields(ndir); - mark_inode_dirty(ndir); return 0; } @@ -520,7 +510,6 @@ if (!error) { vn_revalidate(vp); - mark_inode_dirty_sync(inode); } return error; } diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_lrw.c linux-2.5.69-bk14/fs/xfs/linux/xfs_lrw.c --- linux-2.5.69-bk13/fs/xfs/linux/xfs_lrw.c 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_lrw.c 2003-05-20 04:46:47.000000000 -0700 @@ -887,29 +887,23 @@ return (xfs_bioerror_relse(bp)); } - -void -XFS_bflush(xfs_buftarg_t *target) -{ - pagebuf_delwri_flush(target, PBDF_WAIT, NULL); -} - /* - * If the underlying (log or data) device is readonly, there are some + * If the underlying (data/log/rt) device is readonly, there are some * operations that cannot proceed. */ int -xfs_dev_is_read_only(xfs_mount_t *mp, char *message) +xfs_dev_is_read_only( + xfs_mount_t *mp, + char *message) { - if (bdev_read_only(mp->m_ddev_targp->pbr_bdev) || - bdev_read_only(mp->m_logdev_targp->pbr_bdev) || - (mp->m_rtdev_targp && bdev_read_only(mp->m_rtdev_targp->pbr_bdev))) { + if (xfs_readonly_buftarg(mp->m_ddev_targp) || + xfs_readonly_buftarg(mp->m_logdev_targp) || + (mp->m_rtdev_targp && xfs_readonly_buftarg(mp->m_rtdev_targp))) { cmn_err(CE_NOTE, "XFS: %s required on read-only device.", message); cmn_err(CE_NOTE, "XFS: write access unavailable, cannot proceed."); return EROFS; } - return 0; } diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_super.c linux-2.5.69-bk14/fs/xfs/linux/xfs_super.c --- linux-2.5.69-bk13/fs/xfs/linux/xfs_super.c 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_super.c 2003-05-20 04:46:47.000000000 -0700 @@ -217,13 +217,27 @@ } void -xfs_free_buftarg( +xfs_flush_buftarg( xfs_buftarg_t *btp) { pagebuf_delwri_flush(btp, PBDF_WAIT, NULL); +} + +void +xfs_free_buftarg( + xfs_buftarg_t *btp) +{ + xfs_flush_buftarg(btp); kmem_free(btp, sizeof(*btp)); } +int +xfs_readonly_buftarg( + xfs_buftarg_t *btp) +{ + return bdev_read_only(btp->pbr_bdev); +} + void xfs_relse_buftarg( xfs_buftarg_t *btp) @@ -331,9 +345,10 @@ } /* - * We do not actually write the inode here, just mark the - * super block dirty so that sync_supers calls us and - * forces the flush. + * Attempt to flush the inode, this will actually fail + * if the inode is pinned, but we dirty the inode again + * at the point when it is unpinned after a log write, + * since this is when the inode itself becomes flushable. */ STATIC void linvfs_write_inode( @@ -348,8 +363,6 @@ if (sync) flags |= FLUSH_SYNC; VOP_IFLUSH(vp, flags, error); - if (error == EAGAIN) - inode->i_sb->s_dirt = 1; } } @@ -369,6 +382,61 @@ } } + +#define SYNCD_FLAGS (SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR) + +STATIC int +syncd(void *arg) +{ + vfs_t *vfsp = (vfs_t *) arg; + int error; + + daemonize("xfs_syncd"); + + vfsp->vfs_sync_task = current; + wmb(); + wake_up(&vfsp->vfs_wait_sync_task); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(xfs_params.sync_interval); + if (vfsp->vfs_flag & VFS_UMOUNT) + break; + if (vfsp->vfs_flag & VFS_RDONLY) + continue; + VFS_SYNC(vfsp, SYNCD_FLAGS, NULL, error); + } + + vfsp->vfs_sync_task = NULL; + wmb(); + wake_up(&vfsp->vfs_wait_sync_task); + + return 0; +} + +STATIC int +linvfs_start_syncd(vfs_t *vfsp) +{ + int pid; + + pid = kernel_thread(syncd, (void *) vfsp, + CLONE_VM | CLONE_FS | CLONE_FILES); + if (pid < 0) + return pid; + wait_event(vfsp->vfs_wait_sync_task, vfsp->vfs_sync_task); + return 0; +} + +STATIC void +linvfs_stop_syncd(vfs_t *vfsp) +{ + vfsp->vfs_flag |= VFS_UMOUNT; + wmb(); + + wake_up_process(vfsp->vfs_sync_task); + wait_event(vfsp->vfs_wait_sync_task, !vfsp->vfs_sync_task); +} + STATIC void linvfs_put_super( struct super_block *sb) @@ -376,8 +444,9 @@ vfs_t *vfsp = LINVFS_GET_VFS(sb); int error; + linvfs_stop_syncd(vfsp); VFS_SYNC(vfsp, SYNC_ATTR|SYNC_DELWRI, NULL, error); - if (error == 0) + if (!error) VFS_UNMOUNT(vfsp, 0, NULL, error); if (error) { printk("XFS unmount got error %d\n", error); @@ -395,10 +464,13 @@ vfs_t *vfsp = LINVFS_GET_VFS(sb); int error; - sb->s_dirt = 0; - if (sb->s_flags & MS_RDONLY) + if (sb->s_flags & MS_RDONLY) { + sb->s_dirt = 0; /* paranoia */ return; - VFS_SYNC(vfsp, SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR, NULL, error); + } + /* Push the log and superblock a little */ + VFS_SYNC(vfsp, SYNC_FSDATA, NULL, error); + sb->s_dirt = 0; } STATIC int @@ -424,12 +496,8 @@ int error; VFS_PARSEARGS(vfsp, options, args, 1, error); - if (error) - goto out; - - VFS_MNTUPDATE(vfsp, flags, args, error); - -out: + if (!error) + VFS_MNTUPDATE(vfsp, flags, args, error); kmem_free(args, sizeof(*args)); return error; } @@ -438,11 +506,10 @@ linvfs_freeze_fs( struct super_block *sb) { - vfs_t *vfsp; + vfs_t *vfsp = LINVFS_GET_VFS(sb); vnode_t *vp; int error; - vfsp = LINVFS_GET_VFS(sb); if (sb->s_flags & MS_RDONLY) return; VFS_ROOT(vfsp, &vp, error); @@ -454,11 +521,10 @@ linvfs_unfreeze_fs( struct super_block *sb) { - vfs_t *vfsp; + vfs_t *vfsp = LINVFS_GET_VFS(sb); vnode_t *vp; int error; - vfsp = LINVFS_GET_VFS(sb); VFS_ROOT(vfsp, &vp, error); VOP_IOCTL(vp, LINVFS_GET_IP(vp), NULL, XFS_IOC_THAW, 0, error); VN_RELE(vp); @@ -652,7 +718,8 @@ goto fail_vnrele; if (is_bad_inode(sb->s_root->d_inode)) goto fail_vnrele; - + if (linvfs_start_syncd(vfsp)) + goto fail_vnrele; vn_trace_exit(rootvp, __FUNCTION__, (inst_t *)__return_address); kmem_free(args, sizeof(*args)); diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_super.h linux-2.5.69-bk14/fs/xfs/linux/xfs_super.h --- linux-2.5.69-bk13/fs/xfs/linux/xfs_super.h 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_super.h 2003-05-20 04:46:47.000000000 -0700 @@ -101,7 +101,8 @@ extern struct pb_target *xfs_alloc_buftarg(struct block_device *); extern void xfs_relse_buftarg(struct pb_target *); extern void xfs_free_buftarg(struct pb_target *); - +extern void xfs_flush_buftarg(struct pb_target *); +extern int xfs_readonly_buftarg(struct pb_target *); extern void xfs_setsize_buftarg(struct pb_target *, unsigned int, unsigned int); extern unsigned int xfs_getsize_buftarg(struct pb_target *); diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_syncd.c linux-2.5.69-bk14/fs/xfs/linux/xfs_syncd.c --- linux-2.5.69-bk13/fs/xfs/linux/xfs_syncd.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_syncd.c 2003-05-20 04:46:47.000000000 -0700 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include + +#define SYNCD_FLAGS (SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR) + +int syncd(void *arg) +{ + vfs_t *vfsp = (vfs_t *) arg; + int error; + + daemonize("xfs_syncd"); + + vfsp->vfs_sync_task = current; + wmb(); + wake_up(&vfsp->vfs_wait_sync_task); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(xfs_params.sync_interval); + if (vfsp->vfs_flag & VFS_UMOUNT) + break; + if (vfsp->vfs_flag & VFS_RDONLY); + continue; + VFS_SYNC(vfsp, SYNCD_FLAGS, NULL, error); + } + + vfsp->vfs_sync_task = NULL; + wmb(); + wake_up(&vfsp->vfs_wait_sync_task); + + return 0; +} + +int +linvfs_start_syncd(vfs_t *vfsp) +{ + int pid; + + pid = kernel_thread(syncd, (void *) vfsp, + CLONE_VM | CLONE_FS | CLONE_FILES); + if (pid < 0) + return pid; + wait_event(vfsp->vfs_wait_sync_task, vfsp->vfs_sync_task); + return 0; +} + +void +linvfs_stop_syncd(vfs_t *vfsp) +{ + vfsp->vfs_flag |= VFS_UMOUNT; + wmb(); + + wake_up_process(vfsp->vfs_sync_task); + wait_event(vfsp->vfs_wait_sync_task, !vfsp->vfs_sync_task); +} diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_sysctl.c linux-2.5.69-bk14/fs/xfs/linux/xfs_sysctl.c --- linux-2.5.69-bk13/fs/xfs/linux/xfs_sysctl.c 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_sysctl.c 2003-05-20 04:46:47.000000000 -0700 @@ -36,8 +36,8 @@ #include -STATIC ulong xfs_min[XFS_PARAM] = { 0, 0, 0, 0, 0, 0 }; -STATIC ulong xfs_max[XFS_PARAM] = { 1, 1, 1, 1, 127, 3 }; +STATIC ulong xfs_min[XFS_PARAM] = { 0, 0, 0, 0, 0, 0, HZ }; +STATIC ulong xfs_max[XFS_PARAM] = { 1, 1, 1, 1, 127, 3, HZ * 60 }; static struct ctl_table_header *xfs_table_header; @@ -92,6 +92,10 @@ sizeof(ulong), 0644, NULL, &proc_doulongvec_minmax, &sysctl_intvec, NULL, &xfs_min[5], &xfs_max[5]}, + {XFS_SYNC_INTERVAL, "sync_interval", &xfs_params.sync_interval, + sizeof(ulong), 0644, NULL, &proc_doulongvec_minmax, + &sysctl_intvec, NULL, &xfs_min[6], &xfs_max[6]}, + {0} }; diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_sysctl.h linux-2.5.69-bk14/fs/xfs/linux/xfs_sysctl.h --- linux-2.5.69-bk13/fs/xfs/linux/xfs_sysctl.h 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_sysctl.h 2003-05-20 04:46:47.000000000 -0700 @@ -49,6 +49,7 @@ ulong symlink_mode; /* Symlink creat mode affected by umask. */ ulong panic_mask; /* bitmask to specify panics on errors. */ ulong error_level; /* Degree of reporting for internal probs*/ + ulong sync_interval; /* time between sync calls */ } xfs_param_t; /* @@ -73,6 +74,7 @@ XFS_SYMLINK_MODE = 4, XFS_PANIC_MASK = 5, XFS_ERRLEVEL = 6, + XFS_SYNC_INTERVAL = 7, }; extern xfs_param_t xfs_params; diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_vfs.c linux-2.5.69-bk14/fs/xfs/linux/xfs_vfs.c --- linux-2.5.69-bk13/fs/xfs/linux/xfs_vfs.c 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_vfs.c 2003-05-20 04:46:47.000000000 -0700 @@ -238,6 +238,7 @@ vfsp = kmem_zalloc(sizeof(vfs_t), KM_SLEEP); bhv_head_init(VFS_BHVHEAD(vfsp), "vfs"); + init_waitqueue_head(&vfsp->vfs_wait_sync_task); return vfsp; } diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_vfs.h linux-2.5.69-bk14/fs/xfs/linux/xfs_vfs.h --- linux-2.5.69-bk13/fs/xfs/linux/xfs_vfs.h 2003-05-04 16:52:49.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_vfs.h 2003-05-20 04:46:47.000000000 -0700 @@ -48,6 +48,8 @@ fsid_t *vfs_altfsid; /* An ID fixed for life of FS */ bhv_head_t vfs_bh; /* head of vfs behavior chain */ struct super_block *vfs_super; /* Linux superblock structure */ + struct task_struct *vfs_sync_task; + wait_queue_head_t vfs_wait_sync_task; } vfs_t; #define vfs_fbhv vfs_bh.bh_first /* 1st on vfs behavior chain */ @@ -78,7 +80,8 @@ #define VFS_RDONLY 0x0001 /* read-only vfs */ #define VFS_GRPID 0x0002 /* group-ID assigned from directory */ #define VFS_DMI 0x0004 /* filesystem has the DMI enabled */ -#define VFS_END 0x0004 /* max flag */ +#define VFS_UMOUNT 0x0008 /* unmount in progress */ +#define VFS_END 0x0008 /* max flag */ #define SYNC_ATTR 0x0001 /* sync attributes */ #define SYNC_CLOSE 0x0002 /* close file system down */ @@ -87,6 +90,7 @@ #define SYNC_FSDATA 0x0020 /* flush fs data (e.g. superblocks) */ #define SYNC_BDFLUSH 0x0010 /* BDFLUSH is calling -- don't block */ + typedef int (*vfs_mount_t)(bhv_desc_t *, struct xfs_mount_args *, struct cred *); typedef int (*vfs_parseargs_t)(bhv_desc_t *, char *, diff -urN linux-2.5.69-bk13/fs/xfs/linux/xfs_vnode.h linux-2.5.69-bk14/fs/xfs/linux/xfs_vnode.h --- linux-2.5.69-bk13/fs/xfs/linux/xfs_vnode.h 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/linux/xfs_vnode.h 2003-05-20 04:46:47.000000000 -0700 @@ -562,8 +562,7 @@ (!list_empty(&(LINVFS_GET_IP(vp)->i_mapping->i_mmap_shared)))) #define VN_CACHED(vp) (LINVFS_GET_IP(vp)->i_mapping->nrpages) #define VN_DIRTY(vp) (!list_empty(&(LINVFS_GET_IP(vp)->i_mapping->dirty_pages))) -#define VMODIFY(vp) { VN_FLAGSET(vp, VMODIFIED); \ - mark_inode_dirty(LINVFS_GET_IP(vp)); } +#define VMODIFY(vp) VN_FLAGSET(vp, VMODIFIED) #define VUNMODIFY(vp) VN_FLAGCLR(vp, VMODIFIED) /* diff -urN linux-2.5.69-bk13/fs/xfs/pagebuf/page_buf.c linux-2.5.69-bk14/fs/xfs/pagebuf/page_buf.c --- linux-2.5.69-bk13/fs/xfs/pagebuf/page_buf.c 2003-05-04 16:52:59.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/pagebuf/page_buf.c 2003-05-20 04:46:47.000000000 -0700 @@ -68,7 +68,7 @@ #define BN_ALIGN_MASK ((1 << (PAGE_CACHE_SHIFT - BBSHIFT)) - 1) #ifndef GFP_READAHEAD -#define GFP_READAHEAD __GFP_NOWARN +#define GFP_READAHEAD (__GFP_NOWARN|__GFP_NORETRY) #endif /* @@ -76,11 +76,11 @@ */ #ifdef PAGEBUF_TRACE -static spinlock_t pb_trace_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t pb_trace_lock = SPIN_LOCK_UNLOCKED; struct pagebuf_trace_buf pb_trace; EXPORT_SYMBOL(pb_trace); EXPORT_SYMBOL(pb_trace_func); -#define CIRC_INC(i) (((i) + 1) & (PB_TRACE_BUFSIZE - 1)) +#define CIRC_INC(i) (((i) + 1) & (PB_TRACE_BUFSIZE - 1)) void pb_trace_func( @@ -181,7 +181,7 @@ * dev_t is 16 bits, loff_t is always 64 bits */ base ^= dev; - for (bit = hval = 0; base != 0 && bit < sizeof(base) * 8; bit += NBITS) { + for (bit = hval = 0; base && bit < sizeof(base) * 8; bit += NBITS) { hval ^= (int)base & (NHASH-1); base >>= NBITS; } @@ -189,18 +189,18 @@ } /* - * Mapping of multi-page buffers into contingous virtual space + * Mapping of multi-page buffers into contiguous virtual space */ STATIC void *pagebuf_mapout_locked(page_buf_t *); -STATIC spinlock_t as_lock = SPIN_LOCK_UNLOCKED; typedef struct a_list { - void *vm_addr; + void *vm_addr; struct a_list *next; } a_list_t; -STATIC a_list_t *as_free_head; -STATIC int as_list_len; +STATIC a_list_t *as_free_head; +STATIC int as_list_len; +STATIC spinlock_t as_lock = SPIN_LOCK_UNLOCKED; /* @@ -1897,13 +1897,6 @@ } #endif /* CONFIG_PROC_FS */ -STATIC void -pagebuf_shaker(void) -{ - pagebuf_daemon_wakeup(1); -} - - /* * Initialization and Termination */ @@ -1943,7 +1936,6 @@ #endif pagebuf_daemon_start(); - kmem_shake_register(pagebuf_shaker); return 0; } @@ -1959,7 +1951,6 @@ pagebuf_daemon_stop(); kmem_cache_destroy(pagebuf_cache); - kmem_shake_deregister(pagebuf_shaker); unregister_sysctl_table(pagebuf_table_header); #ifdef CONFIG_PROC_FS diff -urN linux-2.5.69-bk13/fs/xfs/quota/xfs_qm.c linux-2.5.69-bk14/fs/xfs/quota/xfs_qm.c --- linux-2.5.69-bk13/fs/xfs/quota/xfs_qm.c 2003-05-04 16:52:52.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/quota/xfs_qm.c 2003-05-20 04:46:47.000000000 -0700 @@ -88,7 +88,7 @@ STATIC int xfs_qm_quotacheck(xfs_mount_t *); STATIC int xfs_qm_init_quotainos(xfs_mount_t *); -STATIC void xfs_qm_shake(void); +STATIC int xfs_qm_shake(int, unsigned int); #ifdef DEBUG extern mutex_t qcheck_lock; @@ -112,6 +112,8 @@ #define XQM_LIST_PRINT(l, NXT, title) do { } while (0) #endif +struct shrinker *xfs_qm_shrinker; + /* * Initialize the XQM structure. * Note that there is not one quota manager per file system. @@ -161,7 +163,7 @@ } else xqm->qm_dqzone = qm_dqzone; - kmem_shake_register(xfs_qm_shake); + xfs_qm_shrinker = set_shrinker(DEFAULT_SEEKS, xfs_qm_shake); /* * The t_dqinfo portion of transactions. @@ -193,7 +195,8 @@ ASSERT(xqm != NULL); ASSERT(xqm->qm_nrefs == 0); - kmem_shake_deregister(xfs_qm_shake); + + remove_shrinker(xfs_qm_shrinker); hsize = xqm->qm_dqhashmask + 1; for (i = 0; i < hsize; i++) { xfs_qm_list_destroy(&(xqm->qm_usr_dqhtable[i])); @@ -2088,7 +2091,7 @@ xfs_dqunlock(dqp); xfs_qm_freelist_unlock(xfs_Gqm); if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) - return (nreclaimed != howmany); + goto out; XQM_STATS_INC(xqmstats.xs_qm_dqwants); goto tryagain; } @@ -2163,7 +2166,7 @@ XFS_DQ_HASH_UNLOCK(hash); xfs_qm_freelist_unlock(xfs_Gqm); if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) - return (nreclaimed != howmany); + goto out; goto tryagain; } xfs_dqtrace_entry(dqp, "DQSHAKE: UNLINKING"); @@ -2188,7 +2191,8 @@ dqp = nextdqp; } xfs_qm_freelist_unlock(xfs_Gqm); - return (nreclaimed != howmany); + out: + return nreclaimed; } @@ -2197,13 +2201,15 @@ * running low. */ /* ARGSUSED */ -STATIC void -xfs_qm_shake(void) +STATIC int +xfs_qm_shake(int nr_to_scan, unsigned int gfp_mask) { int ndqused, nfree, n; + if (!(gfp_mask & __GFP_WAIT)) + return 0; if (!xfs_Gqm) - return; + return 0; nfree = xfs_Gqm->qm_dqfreelist.qh_nelems; /* free dquots */ /* incore dquots in all f/s's */ @@ -2212,12 +2218,12 @@ ASSERT(ndqused >= 0); if (nfree <= ndqused && nfree < ndquot) - return; + return 0; ndqused *= xfs_Gqm->qm_dqfree_ratio; /* target # of free dquots */ n = nfree - ndqused - ndquot; /* # over target */ - (void) xfs_qm_shake_freelist(MAX(nfree, n)); + return xfs_qm_shake_freelist(MAX(nfree, n)); } diff -urN linux-2.5.69-bk13/fs/xfs/support/kmem.h linux-2.5.69-bk14/fs/xfs/support/kmem.h --- linux-2.5.69-bk13/fs/xfs/support/kmem.h 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/support/kmem.h 2003-05-20 04:46:47.000000000 -0700 @@ -32,31 +32,118 @@ #ifndef __XFS_SUPPORT_KMEM_H__ #define __XFS_SUPPORT_KMEM_H__ +#include +#include #include +#include /* - * memory management routines + * Cutoff point to use vmalloc instead of kmalloc. */ -#define KM_SLEEP 0x0001 -#define KM_NOSLEEP 0x0002 -#define KM_NOFS 0x0004 +#define MAX_SLAB_SIZE 0x10000 +/* + * XFS uses slightly different names for these due to the + * IRIX heritage. + */ #define kmem_zone kmem_cache_s #define kmem_zone_t kmem_cache_t -extern kmem_zone_t *kmem_zone_init(int, char *); -extern void *kmem_zone_zalloc(kmem_zone_t *, int); -extern void *kmem_zone_alloc(kmem_zone_t *, int); -extern void kmem_zone_free(kmem_zone_t *, void *); - -extern void *kmem_alloc(size_t, int); -extern void *kmem_realloc(void *, size_t, size_t, int); -extern void *kmem_zalloc(size_t, int); -extern void kmem_free(void *, size_t); +#define KM_SLEEP 0x0001 +#define KM_NOSLEEP 0x0002 +#define KM_NOFS 0x0004 -typedef void (*kmem_shake_func_t)(void); -extern void kmem_shake_register(kmem_shake_func_t); -extern void kmem_shake_deregister(kmem_shake_func_t); +/* + * XXX get rid of the unconditional __GFP_NOFAIL by adding + * a KM_FAIL flag and using it where we're allowed to fail. + */ +static __inline unsigned int +flag_convert(int flags) +{ +#if DEBUG + if (unlikely(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS))) { + printk(KERN_WARNING + "XFS: memory allocation with wrong flags (%x)\n", flags); + BUG(); + } +#endif + + if (flags & KM_NOSLEEP) + return GFP_ATOMIC; + /* If we're in a transaction, FS activity is not ok */ + else if ((current->flags & PF_FSTRANS) || (flags & KM_NOFS)) + return GFP_NOFS | __GFP_NOFAIL; + return GFP_KERNEL | __GFP_NOFAIL; +} + +static __inline void * +kmem_alloc(size_t size, int flags) +{ + if (unlikely(MAX_SLAB_SIZE < size)) + /* Avoid doing filesystem sensitive stuff to get this */ + return __vmalloc(size, flag_convert(flags), PAGE_KERNEL); + return kmalloc(size, flag_convert(flags)); +} + +static __inline void * +kmem_zalloc(size_t size, int flags) +{ + void *ptr = kmem_alloc(size, flags); + if (likely(ptr != NULL)) + memset(ptr, 0, size); + return ptr; +} + +static __inline void +kmem_free(void *ptr, size_t size) +{ + if (unlikely((unsigned long)ptr < VMALLOC_START || + (unsigned long)ptr >= VMALLOC_END)) + kfree(ptr); + else + vfree(ptr); +} + +static __inline void * +kmem_realloc(void *ptr, size_t newsize, size_t oldsize, int flags) +{ + void *new = kmem_alloc(newsize, flags); + + if (likely(ptr != NULL)) { + if (likely(new != NULL)) + memcpy(new, ptr, min(oldsize, newsize)); + kmem_free(ptr, oldsize); + } + + return new; +} + +static __inline kmem_zone_t * +kmem_zone_init(int size, char *zone_name) +{ + return kmem_cache_create(zone_name, size, 0, 0, NULL, NULL); +} + +static __inline void * +kmem_zone_alloc(kmem_zone_t *zone, int flags) +{ + return kmem_cache_alloc(zone, flag_convert(flags)); +} + +static __inline void * +kmem_zone_zalloc(kmem_zone_t *zone, int flags) +{ + void *ptr = kmem_zone_alloc(zone, flags); + if (likely(ptr != NULL)) + memset(ptr, 0, kmem_cache_size(zone)); + return ptr; +} + +static __inline void +kmem_zone_free(kmem_zone_t *zone, void *ptr) +{ + kmem_cache_free(zone, ptr); +} #endif /* __XFS_SUPPORT_KMEM_H__ */ diff -urN linux-2.5.69-bk13/fs/xfs/xfs_ag.h linux-2.5.69-bk14/fs/xfs/xfs_ag.h --- linux-2.5.69-bk13/fs/xfs/xfs_ag.h 2003-05-04 16:53:36.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_ag.h 2003-05-20 04:46:47.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -185,9 +185,8 @@ #endif #define XFS_AGFL_SIZE(mp) ((mp)->m_sb.sb_sectsize / sizeof(xfs_agblock_t)) -/* -- nathans TODO ... use of BBSIZE here - should be sector size -- */ typedef struct xfs_agfl { - xfs_agblock_t agfl_bno[BBSIZE/sizeof(xfs_agblock_t)]; + xfs_agblock_t agfl_bno[1]; /* actually XFS_AGFL_SIZE(mp) */ } xfs_agfl_t; /* diff -urN linux-2.5.69-bk13/fs/xfs/xfs_buf.h linux-2.5.69-bk14/fs/xfs/xfs_buf.h --- linux-2.5.69-bk13/fs/xfs/xfs_buf.h 2003-05-04 16:53:42.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_buf.h 2003-05-20 04:46:47.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -283,7 +283,6 @@ return error; } - #define XFS_bdwrite(pb) \ pagebuf_iostart(pb, PBF_DELWRI | PBF_ASYNC) @@ -307,15 +306,15 @@ * of its metadata. */ -extern void XFS_bflush(xfs_buftarg_t *); -#define xfs_binval(buftarg) XFS_bflush(buftarg) +#define xfs_binval(buftarg) xfs_flush_buftarg(buftarg) + +#define XFS_bflush(buftarg) xfs_flush_buftarg(buftarg) #define xfs_incore_relse(buftarg,delwri_only,wait) \ xfs_relse_buftarg(buftarg) #define xfs_baread(target, rablkno, ralen) \ - pagebuf_readahead((target), (rablkno), \ - (ralen), PBF_DONT_BLOCK) + pagebuf_readahead((target), (rablkno), (ralen), PBF_DONT_BLOCK) #define XFS_getrbuf(sleep,mp) \ pagebuf_get_empty((mp)->m_ddev_targp) diff -urN linux-2.5.69-bk13/fs/xfs/xfs_iget.c linux-2.5.69-bk14/fs/xfs/xfs_iget.c --- linux-2.5.69-bk13/fs/xfs/xfs_iget.c 2003-05-04 16:53:42.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_iget.c 2003-05-20 04:46:47.000000000 -0700 @@ -252,6 +252,11 @@ if (newnode) { xfs_iocore_inode_reinit(ip); } + + XFS_MOUNT_ILOCK(mp); + list_del_init(&ip->i_reclaim); + XFS_MOUNT_IUNLOCK(mp); + vn_trace_exit(vp, "xfs_iget.found", (inst_t *)__return_address); goto return_ip; @@ -467,8 +472,10 @@ } bdp = vn_bhv_lookup(VN_BHV_HEAD(vp), &xfs_vnodeops); - if (bdp == NULL) + if (bdp == NULL) { + XFS_STATS_INC(xfsstats.xs_ig_dup); goto inode_allocate; + } ip = XFS_BHVTOI(bdp); if (lock_flags != 0) xfs_ilock(ip, lock_flags); @@ -720,6 +727,9 @@ } } + /* Deal with the deleted inodes list */ + list_del_init(&ip->i_reclaim); + mp->m_ireclaims++; XFS_MOUNT_IUNLOCK(mp); } diff -urN linux-2.5.69-bk13/fs/xfs/xfs_inode.c linux-2.5.69-bk14/fs/xfs/xfs_inode.c --- linux-2.5.69-bk13/fs/xfs/xfs_inode.c 2003-05-04 16:53:42.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_inode.c 2003-05-20 04:46:47.000000000 -0700 @@ -656,7 +656,9 @@ int nex; int real_size; int size; +#if ARCH_CONVERT != ARCH_NOCONVERT int i; +#endif ifp = XFS_IFORK_PTR(ip, whichfork); nex = XFS_DFORK_NEXTENTS_ARCH(dip, whichfork, ARCH_CONVERT); @@ -976,6 +978,8 @@ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); } + INIT_LIST_HEAD(&ip->i_reclaim); + /* * The inode format changed when we moved the link count and * made it 32 bits long. If this is an old format inode, @@ -2625,6 +2629,15 @@ ASSERT(atomic_read(&ip->i_pincount) > 0); if (atomic_dec_and_test(&ip->i_pincount)) { + vnode_t *vp = XFS_ITOV_NULL(ip); + + /* make sync come back and flush this inode */ + if (vp) { + struct inode *inode = LINVFS_GET_IP(vp); + + mark_inode_dirty_sync(inode); + } + wake_up(&ip->i_ipin_wait); } } @@ -3640,6 +3653,8 @@ */ SYNCHRONIZE(); ip->i_update_core = 1; + if (!(inode->i_state & I_LOCK)) + mark_inode_dirty(inode); } #ifdef XFS_ILOCK_TRACE diff -urN linux-2.5.69-bk13/fs/xfs/xfs_inode.h linux-2.5.69-bk14/fs/xfs/xfs_inode.h --- linux-2.5.69-bk13/fs/xfs/xfs_inode.h 2003-05-04 16:53:36.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_inode.h 2003-05-20 04:46:47.000000000 -0700 @@ -243,6 +243,7 @@ struct xfs_inode *i_mprev; /* ptr to prev inode */ struct xfs_inode **i_prevp; /* ptr to prev i_next */ struct xfs_mount *i_mount; /* fs mount struct ptr */ + struct list_head i_reclaim; /* reclaim list */ struct bhv_desc i_bhv_desc; /* inode behavior descriptor*/ struct xfs_dquot *i_udquot; /* user dquot */ struct xfs_dquot *i_gdquot; /* group dquot */ @@ -477,7 +478,7 @@ void xfs_ifunlock(xfs_inode_t *); void xfs_ireclaim(xfs_inode_t *); int xfs_finish_reclaim(xfs_inode_t *, int, int); -int xfs_finish_reclaim_all(struct xfs_mount *); +int xfs_finish_reclaim_all(struct xfs_mount *, int); /* * xfs_inode.c prototypes. diff -urN linux-2.5.69-bk13/fs/xfs/xfs_log.c linux-2.5.69-bk14/fs/xfs/xfs_log.c --- linux-2.5.69-bk13/fs/xfs/xfs_log.c 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_log.c 2003-05-20 04:46:47.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -414,19 +414,6 @@ } /* - * Initialize log manager data. This routine is intended to be called when - * a system boots up. It is not a per filesystem initialization. - * - * As you can see, we currently do nothing. - */ -int -xfs_log_init(void) -{ - return( 0 ); -} - - -/* * 1. Reserve an amount of on-disk log space and return a ticket corresponding * to the reservation. * 2. Potentially, push buffers at tail of log to disk. @@ -497,8 +484,6 @@ xfs_daddr_t blk_offset, int num_bblks) { - xlog_t *log; - if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) cmn_err(CE_NOTE, "XFS mounting filesystem %s", mp->m_fsname); else { @@ -508,7 +493,7 @@ ASSERT(XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY); } - mp->m_log = log = xlog_alloc_log(mp, log_dev, blk_offset, num_bblks); + mp->m_log = xlog_alloc_log(mp, log_dev, blk_offset, num_bblks); #if defined(DEBUG) || defined(XLOG_NOLOG) if (! xlog_debug) { @@ -528,19 +513,19 @@ if (readonly) vfsp->vfs_flag &= ~VFS_RDONLY; - error = xlog_recover(log, readonly); + error = xlog_recover(mp->m_log, readonly); if (readonly) vfsp->vfs_flag |= VFS_RDONLY; if (error) { cmn_err(CE_WARN, "XFS: log mount/recovery failed"); - xlog_unalloc_log(log); + xlog_unalloc_log(mp->m_log); return error; } } /* Normal transactions can now occur */ - log->l_flags &= ~XLOG_ACTIVE_RECOVERY; + mp->m_log->l_flags &= ~XLOG_ACTIVE_RECOVERY; /* End mounting message in xfs_log_mount_finish */ return 0; @@ -1191,28 +1176,39 @@ int i; int iclogsize; - log = (void *)kmem_zalloc(sizeof(xlog_t), KM_SLEEP); + log = (xlog_t *)kmem_zalloc(sizeof(xlog_t), KM_SLEEP); log->l_mp = mp; log->l_dev = log_dev; log->l_logsize = BBTOB(num_bblks); log->l_logBBstart = blk_offset; log->l_logBBsize = num_bblks; - log->l_roundoff = 0; log->l_covered_state = XLOG_STATE_COVER_IDLE; log->l_flags |= XLOG_ACTIVE_RECOVERY; log->l_prev_block = -1; ASSIGN_ANY_LSN(log->l_tail_lsn, 1, 0, ARCH_NOCONVERT); - /* log->l_tail_lsn = 0x100000000LL; cycle = 1; current block = 0 */ + /* log->l_tail_lsn = 0x100000000LL; cycle = 1; current block = 0 */ log->l_last_sync_lsn = log->l_tail_lsn; log->l_curr_cycle = 1; /* 0 is bad since this is initial value */ - log->l_curr_block = 0; /* filled in by xlog_recover */ - log->l_grant_reserve_bytes = 0; log->l_grant_reserve_cycle = 1; - log->l_grant_write_bytes = 0; log->l_grant_write_cycle = 1; - log->l_quotaoffs_flag = 0; /* XFS_LI_QUOTAOFF logitems */ + + if (XFS_SB_VERSION_HASLOGV2(&mp->m_sb)) { + if (mp->m_sb.sb_logsunit <= 1) { + log->l_stripemask = 1; + } else { + log->l_stripemask = 1 << + xfs_highbit32(mp->m_sb.sb_logsunit >> BBSHIFT); + } + } + if (XFS_SB_VERSION_HASSECTOR(&mp->m_sb)) { + log->l_sectbb_log = mp->m_sb.sb_logsectlog - BBSHIFT; + ASSERT(log->l_sectbb_log <= mp->m_sectbb_log); + ASSERT(XFS_SB_VERSION_HASLOGV2(&mp->m_sb)); + ASSERT(mp->m_sb.sb_logsectlog >= BBSHIFT); + } + log->l_sectbb_mask = (1 << log->l_sectbb_log) - 1; xlog_get_iclog_buffer_size(mp, log); @@ -2811,10 +2807,9 @@ /* Round up to next log-sunit */ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { - if (log->l_curr_block & (log->l_mp->m_lstripemask - 1)) { - roundup = log->l_mp->m_lstripemask - - (log->l_curr_block & - (log->l_mp->m_lstripemask - 1)); + if (log->l_curr_block & (log->l_stripemask - 1)) { + roundup = log->l_stripemask - + (log->l_curr_block & (log->l_stripemask - 1)); } else { roundup = 0; } @@ -3293,15 +3288,17 @@ { xfs_buf_t *bp; uint cycle_no; + xfs_caddr_t ptr; xfs_daddr_t i; if (BLOCK_LSN(iclog->ic_header.h_lsn, ARCH_CONVERT) < 10) { cycle_no = CYCLE_LSN(iclog->ic_header.h_lsn, ARCH_CONVERT); - bp = xlog_get_bp(1, log->l_mp); + bp = xlog_get_bp(log, 1); ASSERT(bp); for (i = 0; i < BLOCK_LSN(iclog->ic_header.h_lsn, ARCH_CONVERT); i++) { xlog_bread(log, i, 1, bp); - if (GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT) != cycle_no) + ptr = xlog_align(log, i, 1, bp); + if (GET_CYCLE(ptr, ARCH_CONVERT) != cycle_no) xlog_warn("XFS: xlog_verify_disk_cycle_no: bad cycle no"); } xlog_put_bp(bp); diff -urN linux-2.5.69-bk13/fs/xfs/xfs_log.h linux-2.5.69-bk14/fs/xfs/xfs_log.h --- linux-2.5.69-bk13/fs/xfs/xfs_log.h 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_log.h 2003-05-20 04:46:47.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -153,7 +153,6 @@ int xfs_log_force(struct xfs_mount *mp, xfs_lsn_t lsn, uint flags); -int xfs_log_init(void); int xfs_log_mount(struct xfs_mount *mp, dev_t log_dev, xfs_daddr_t start_block, diff -urN linux-2.5.69-bk13/fs/xfs/xfs_log_priv.h linux-2.5.69-bk14/fs/xfs/xfs_log_priv.h --- linux-2.5.69-bk13/fs/xfs/xfs_log_priv.h 2003-05-04 16:53:02.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_log_priv.h 2003-05-20 04:46:47.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -73,6 +73,9 @@ #define XLOG_HEADER_SIZE 512 +#define XLOG_REC_SHIFT(log) \ + BTOBB(1 << (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? \ + XLOG_MAX_RECORD_BSHIFT : XLOG_BIG_RECORD_BSHIFT)) #define XLOG_TOTAL_REC_SHIFT(log) \ BTOBB(XLOG_MAX_ICLOGS << (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? \ XLOG_MAX_RECORD_BSHIFT : XLOG_BIG_RECORD_BSHIFT)) @@ -202,9 +205,9 @@ #define LOG_LOCK(log) mutex_spinlock(&(log)->l_icloglock) #define LOG_UNLOCK(log, s) mutex_spinunlock(&(log)->l_icloglock, s) -#define xlog_panic(s) {cmn_err(CE_PANIC, s); } -#define xlog_exit(s) {cmn_err(CE_PANIC, s); } -#define xlog_warn(s) {cmn_err(CE_WARN, s); } +#define xlog_panic(args...) cmn_err(CE_PANIC, ## args) +#define xlog_exit(args...) cmn_err(CE_PANIC, ## args) +#define xlog_warn(args...) cmn_err(CE_WARN, ## args) /* * In core log state @@ -403,6 +406,7 @@ uint xh_cycle; /* write cycle of log : 4 */ uint xh_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE]; /* : 256 */ } xlog_rec_ext_header_t; + #ifdef __KERNEL__ /* * - A log record header is 512 bytes. There is plenty of room to grow the @@ -441,12 +445,10 @@ char *ic_datap; /* pointer to iclog data */ } xlog_iclog_fields_t; -typedef struct xlog_in_core2 { - union { - xlog_rec_header_t hic_header; - xlog_rec_ext_header_t hic_xheader; - char hic_sector[XLOG_HEADER_SIZE]; - } ic_h; +typedef union xlog_in_core2 { + xlog_rec_header_t hic_header; + xlog_rec_ext_header_t hic_xheader; + char hic_sector[XLOG_HEADER_SIZE]; } xlog_in_core_2_t; typedef struct xlog_in_core { @@ -473,7 +475,7 @@ #define ic_bwritecnt hic_fields.ic_bwritecnt #define ic_state hic_fields.ic_state #define ic_datap hic_fields.ic_datap -#define ic_header hic_data->ic_h.hic_header +#define ic_header hic_data->hic_header /* * The reservation head lsn is not made up of a cycle number and block number. @@ -530,8 +532,11 @@ uint l_flags; uint l_quotaoffs_flag;/* XFS_DQ_*, if QUOTAOFFs found */ struct xfs_buf_cancel **l_buf_cancel_table; + int l_stripemask; /* log stripe mask */ int l_iclog_hsize; /* size of iclog header */ int l_iclog_heads; /* number of iclog header sectors */ + uint l_sectbb_log; /* log2 of sector size in bbs */ + uint l_sectbb_mask; /* sector size in bbs alignment mask */ } xlog_t; @@ -546,11 +551,13 @@ extern int xlog_recover(xlog_t *log, int readonly); extern int xlog_recover_finish(xlog_t *log, int mfsi_flags); extern void xlog_pack_data(xlog_t *log, xlog_in_core_t *iclog); -extern struct xfs_buf *xlog_get_bp(int,xfs_mount_t *); -extern void xlog_put_bp(struct xfs_buf *); -extern int xlog_bread(xlog_t *, xfs_daddr_t blkno, int bblks, struct xfs_buf *bp); extern void xlog_recover_process_iunlinks(xlog_t *log); +extern struct xfs_buf *xlog_get_bp(xlog_t *, int); +extern void xlog_put_bp(struct xfs_buf *); +extern int xlog_bread(xlog_t *, xfs_daddr_t, int, struct xfs_buf *); +extern xfs_caddr_t xlog_align(xlog_t *, xfs_daddr_t, int, struct xfs_buf *); + #define XLOG_TRACE_GRAB_FLUSH 1 #define XLOG_TRACE_REL_FLUSH 2 #define XLOG_TRACE_SLEEP_FLUSH 3 diff -urN linux-2.5.69-bk13/fs/xfs/xfs_log_recover.c linux-2.5.69-bk14/fs/xfs/xfs_log_recover.c --- linux-2.5.69-bk13/fs/xfs/xfs_log_recover.c 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_log_recover.c 2003-05-20 04:46:47.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -65,53 +65,68 @@ #include "xfs_quota.h" #include "xfs_rw.h" -STATIC int xlog_find_zeroed(struct log *log, xfs_daddr_t *blk_no); - -STATIC int xlog_clear_stale_blocks(xlog_t *log, xfs_lsn_t tail_lsn); +STATIC int xlog_find_zeroed(xlog_t *, xfs_daddr_t *); +STATIC int xlog_clear_stale_blocks(xlog_t *, xfs_lsn_t); STATIC void xlog_recover_insert_item_backq(xlog_recover_item_t **q, xlog_recover_item_t *item); - #if defined(DEBUG) -STATIC void xlog_recover_check_summary(xlog_t *log); -STATIC void xlog_recover_check_ail(xfs_mount_t *mp, xfs_log_item_t *lip, - int gen); +STATIC void xlog_recover_check_summary(xlog_t *); +STATIC void xlog_recover_check_ail(xfs_mount_t *, xfs_log_item_t *, int); #else #define xlog_recover_check_summary(log) #define xlog_recover_check_ail(mp, lip, gen) -#endif /* DEBUG */ +#endif +/* + * Sector aligned buffer routines for buffer create/read/write/access + */ + +#define XLOG_SECTOR_ROUNDUP_BBCOUNT(log, bbs) \ + ( ((log)->l_sectbb_mask && (bbs & (log)->l_sectbb_mask)) ? \ + ((bbs + (log)->l_sectbb_mask + 1) & ~(log)->l_sectbb_mask) : (bbs) ) +#define XLOG_SECTOR_ROUNDDOWN_BLKNO(log, bno) ((bno) & ~(log)->l_sectbb_mask) + xfs_buf_t * -xlog_get_bp(int num_bblks, xfs_mount_t *mp) +xlog_get_bp( + xlog_t *log, + int num_bblks) { - xfs_buf_t *bp; - ASSERT(num_bblks > 0); - bp = XFS_ngetrbuf(BBTOB(num_bblks),mp); - return bp; -} /* xlog_get_bp */ - + if (log->l_sectbb_log) { + if (num_bblks > 1) + num_bblks += XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1); + num_bblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, num_bblks); + } + return XFS_ngetrbuf(BBTOB(num_bblks), log->l_mp); +} void -xlog_put_bp(xfs_buf_t *bp) +xlog_put_bp( + xfs_buf_t *bp) { XFS_nfreerbuf(bp); -} /* xlog_put_bp */ +} /* * nbblks should be uint, but oh well. Just want to catch that 32-bit length. */ int -xlog_bread(xlog_t *log, - xfs_daddr_t blk_no, - int nbblks, - xfs_buf_t *bp) +xlog_bread( + xlog_t *log, + xfs_daddr_t blk_no, + int nbblks, + xfs_buf_t *bp) { - int error; + int error; + + if (log->l_sectbb_log) { + blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no); + nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks); + } - ASSERT(log); ASSERT(nbblks > 0); ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp)); ASSERT(bp); @@ -123,14 +138,11 @@ XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp); xfsbdstrat(log->l_mp, bp); - if ((error = xfs_iowait(bp))) { + if ((error = xfs_iowait(bp))) xfs_ioerror_alert("xlog_bread", log->l_mp, bp, XFS_BUF_ADDR(bp)); - return (error); - } return error; -} /* xlog_bread */ - +} /* * Write out the buffer at the given block for the given number of blocks. @@ -139,12 +151,17 @@ */ int xlog_bwrite( - xlog_t *log, - int blk_no, - int nbblks, + xlog_t *log, + xfs_daddr_t blk_no, + int nbblks, xfs_buf_t *bp) { - int error; + int error; + + if (log->l_sectbb_log) { + blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no); + nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks); + } ASSERT(nbblks > 0); ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp)); @@ -160,94 +177,109 @@ if ((error = xfs_bwrite(log->l_mp, bp))) xfs_ioerror_alert("xlog_bwrite", log->l_mp, bp, XFS_BUF_ADDR(bp)); + return error; +} - return (error); -} /* xlog_bwrite */ +xfs_caddr_t +xlog_align( + xlog_t *log, + xfs_daddr_t blk_no, + int nbblks, + xfs_buf_t *bp) +{ + xfs_caddr_t ptr; + + if (!log->l_sectbb_log) + return XFS_BUF_PTR(bp); + + ptr = XFS_BUF_PTR(bp) + BBTOB((int)blk_no & log->l_sectbb_mask); + ASSERT(XFS_BUF_SIZE(bp) >= + BBTOB(nbblks + (blk_no & log->l_sectbb_mask))); + return ptr; +} #ifdef DEBUG /* - * check log record header for recovery + * dump debug superblock and log record information */ -static void -xlog_header_check_dump(xfs_mount_t *mp, xlog_rec_header_t *head) +STATIC void +xlog_header_check_dump( + xfs_mount_t *mp, + xlog_rec_header_t *head) { - int b; + int b; - printk("%s: SB : uuid = ", __FUNCTION__); - for (b=0;b<16;b++) printk("%02x",((unsigned char *)&mp->m_sb.sb_uuid)[b]); - printk(", fmt = %d\n",XLOG_FMT); - printk(" log : uuid = "); - for (b=0;b<16;b++) printk("%02x",((unsigned char *)&head->h_fs_uuid)[b]); - printk(", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT)); + printk("%s: SB : uuid = ", __FUNCTION__); + for (b = 0; b < 16; b++) + printk("%02x",((unsigned char *)&mp->m_sb.sb_uuid)[b]); + printk(", fmt = %d\n", XLOG_FMT); + printk(" log : uuid = "); + for (b = 0; b < 16; b++) + printk("%02x",((unsigned char *)&head->h_fs_uuid)[b]); + printk(", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT)); } +#else +#define xlog_header_check_dump(mp, head) #endif /* * check log record header for recovery */ - STATIC int -xlog_header_check_recover(xfs_mount_t *mp, xlog_rec_header_t *head) +xlog_header_check_recover( + xfs_mount_t *mp, + xlog_rec_header_t *head) { - ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - - /* - * IRIX doesn't write the h_fmt field and leaves it zeroed - * (XLOG_FMT_UNKNOWN). This stops us from trying to recover - * a dirty log created in IRIX. - */ - - if (unlikely(INT_GET(head->h_fmt, ARCH_CONVERT) != XLOG_FMT)) { - xlog_warn("XFS: dirty log written in incompatible format - can't recover"); -#ifdef DEBUG - xlog_header_check_dump(mp, head); -#endif - XFS_ERROR_REPORT("xlog_header_check_recover(1)", - XFS_ERRLEVEL_HIGH, mp); - return XFS_ERROR(EFSCORRUPTED); - } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { - xlog_warn("XFS: dirty log entry has mismatched uuid - can't recover"); -#ifdef DEBUG - xlog_header_check_dump(mp, head); -#endif - XFS_ERROR_REPORT("xlog_header_check_recover(2)", - XFS_ERRLEVEL_HIGH, mp); - return XFS_ERROR(EFSCORRUPTED); - } + ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - return 0; + /* + * IRIX doesn't write the h_fmt field and leaves it zeroed + * (XLOG_FMT_UNKNOWN). This stops us from trying to recover + * a dirty log created in IRIX. + */ + if (unlikely(INT_GET(head->h_fmt, ARCH_CONVERT) != XLOG_FMT)) { + xlog_warn( + "XFS: dirty log written in incompatible format - can't recover"); + xlog_header_check_dump(mp, head); + XFS_ERROR_REPORT("xlog_header_check_recover(1)", + XFS_ERRLEVEL_HIGH, mp); + return XFS_ERROR(EFSCORRUPTED); + } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { + xlog_warn( + "XFS: dirty log entry has mismatched uuid - can't recover"); + xlog_header_check_dump(mp, head); + XFS_ERROR_REPORT("xlog_header_check_recover(2)", + XFS_ERRLEVEL_HIGH, mp); + return XFS_ERROR(EFSCORRUPTED); + } + return 0; } /* * read the head block of the log and check the header */ - STATIC int -xlog_header_check_mount(xfs_mount_t *mp, xlog_rec_header_t *head) +xlog_header_check_mount( + xfs_mount_t *mp, + xlog_rec_header_t *head) { - ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - - if (uuid_is_nil(&head->h_fs_uuid)) { - - /* - * IRIX doesn't write the h_fs_uuid or h_fmt fields. If - * h_fs_uuid is nil, we assume this log was last mounted - * by IRIX and continue. - */ - - xlog_warn("XFS: nil uuid in log - IRIX style log"); - - } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { - xlog_warn("XFS: log has mismatched uuid - can't recover"); -#ifdef DEBUG - xlog_header_check_dump(mp, head); -#endif - XFS_ERROR_REPORT("xlog_header_check_mount", - XFS_ERRLEVEL_HIGH, mp); - return XFS_ERROR(EFSCORRUPTED); - } + ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - return 0; + if (uuid_is_nil(&head->h_fs_uuid)) { + /* + * IRIX doesn't write the h_fs_uuid or h_fmt fields. If + * h_fs_uuid is nil, we assume this log was last mounted + * by IRIX and continue. + */ + xlog_warn("XFS: nil uuid in log - IRIX style log"); + } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { + xlog_warn("XFS: log has mismatched uuid - can't recover"); + xlog_header_check_dump(mp, head); + XFS_ERROR_REPORT("xlog_header_check_mount", + XFS_ERRLEVEL_HIGH, mp); + return XFS_ERROR(EFSCORRUPTED); + } + return 0; } STATIC void @@ -255,6 +287,7 @@ struct xfs_buf *bp) { xfs_mount_t *mp; + ASSERT(XFS_BUF_FSPRIVATE(bp, void *)); if (XFS_BUF_GETERROR(bp)) { @@ -279,12 +312,14 @@ * necessarily be perfect. */ int -xlog_find_cycle_start(xlog_t *log, - xfs_buf_t *bp, - xfs_daddr_t first_blk, - xfs_daddr_t *last_blk, - uint cycle) +xlog_find_cycle_start( + xlog_t *log, + xfs_buf_t *bp, + xfs_daddr_t first_blk, + xfs_daddr_t *last_blk, + uint cycle) { + xfs_caddr_t offset; xfs_daddr_t mid_blk; uint mid_cycle; int error; @@ -293,7 +328,8 @@ while (mid_blk != first_blk && mid_blk != *last_blk) { if ((error = xlog_bread(log, mid_blk, 1, bp))) return error; - mid_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + offset = xlog_align(log, mid_blk, 1, bp); + mid_cycle = GET_CYCLE(offset, ARCH_CONVERT); if (mid_cycle == cycle) { *last_blk = mid_blk; /* last_half_cycle == mid_cycle */ @@ -307,8 +343,7 @@ (mid_blk == *last_blk && mid_blk-1 == first_blk)); return 0; -} /* xlog_find_cycle_start */ - +} /* * Check that the range of blocks does not contain the cycle number @@ -320,27 +355,27 @@ * Set blkno to -1 if we encounter no errors. This is an invalid block number * since we don't ever expect logs to get this large. */ - STATIC int -xlog_find_verify_cycle( xlog_t *log, - xfs_daddr_t start_blk, - int nbblks, - uint stop_on_cycle_no, - xfs_daddr_t *new_blk) +xlog_find_verify_cycle( + xlog_t *log, + xfs_daddr_t start_blk, + int nbblks, + uint stop_on_cycle_no, + xfs_daddr_t *new_blk) { - xfs_daddr_t i, j; - uint cycle; - xfs_buf_t *bp; - char *buf = NULL; - int error = 0; - xfs_daddr_t bufblks; + xfs_daddr_t i, j; + uint cycle; + xfs_buf_t *bp; + xfs_daddr_t bufblks; + xfs_caddr_t buf = NULL; + int error = 0; bufblks = 1 << ffs(nbblks); - while (!(bp = xlog_get_bp(bufblks, log->l_mp))) { + while (!(bp = xlog_get_bp(log, bufblks))) { /* can't get enough memory to do everything in one big buffer */ bufblks >>= 1; - if (!bufblks) + if (bufblks <= log->l_sectbb_log) return ENOMEM; } @@ -352,7 +387,7 @@ if ((error = xlog_bread(log, i, bcount, bp))) goto out; - buf = XFS_BUF_PTR(bp); + buf = xlog_align(log, i, bcount, bp); for (j = 0; j < bcount; j++) { cycle = GET_CYCLE(buf, ARCH_CONVERT); if (cycle == stop_on_cycle_no) { @@ -368,10 +403,8 @@ out: xlog_put_bp(bp); - return error; -} /* xlog_find_verify_cycle */ - +} /* * Potentially backup over partial log record write. @@ -385,98 +418,103 @@ * extra_bblks is the number of blocks potentially verified on a previous * call to this routine. */ - STATIC int -xlog_find_verify_log_record(xlog_t *log, - xfs_daddr_t start_blk, - xfs_daddr_t *last_blk, - int extra_bblks) -{ - xfs_daddr_t i; - xfs_buf_t *bp; - char *buf = NULL; - xlog_rec_header_t *head = NULL; - int error = 0; - int smallmem = 0; - int num_blks = *last_blk - start_blk; - int xhdrs; - - ASSERT(start_blk != 0 || *last_blk != start_blk); - - if (!(bp = xlog_get_bp(num_blks, log->l_mp))) { - if (!(bp = xlog_get_bp(1, log->l_mp))) - return ENOMEM; - smallmem = 1; - buf = XFS_BUF_PTR(bp); - } else { - if ((error = xlog_bread(log, start_blk, num_blks, bp))) - goto out; - buf = XFS_BUF_PTR(bp) + ((num_blks - 1) << BBSHIFT); - } - - for (i = (*last_blk) - 1; i >= 0; i--) { - if (i < start_blk) { - /* legal log record not found */ - xlog_warn("XFS: Log inconsistent (didn't find previous header)"); - ASSERT(0); - error = XFS_ERROR(EIO); - goto out; +xlog_find_verify_log_record( + xlog_t *log, + xfs_daddr_t start_blk, + xfs_daddr_t *last_blk, + int extra_bblks) +{ + xfs_daddr_t i; + xfs_buf_t *bp; + xfs_caddr_t offset = NULL; + xlog_rec_header_t *head = NULL; + int error = 0; + int smallmem = 0; + int num_blks = *last_blk - start_blk; + int xhdrs; + + ASSERT(start_blk != 0 || *last_blk != start_blk); + + if (!(bp = xlog_get_bp(log, num_blks))) { + if (!(bp = xlog_get_bp(log, 1))) + return ENOMEM; + smallmem = 1; + } else { + if ((error = xlog_bread(log, start_blk, num_blks, bp))) + goto out; + offset = xlog_align(log, start_blk, num_blks, bp); + offset += ((num_blks - 1) << BBSHIFT); } - if (smallmem && (error = xlog_bread(log, i, 1, bp))) - goto out; - head = (xlog_rec_header_t*)buf; - - if (INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM) - break; - - if (!smallmem) - buf -= BBSIZE; - } - - /* - * We hit the beginning of the physical log & still no header. Return - * to caller. If caller can handle a return of -1, then this routine - * will be called again for the end of the physical log. - */ - if (i == -1) { - error = -1; - goto out; - } - - /* we have the final block of the good log (the first block - * of the log record _before_ the head. So we check the uuid. - */ - - if ((error = xlog_header_check_mount(log->l_mp, head))) - goto out; - - /* - * We may have found a log record header before we expected one. - * last_blk will be the 1st block # with a given cycle #. We may end - * up reading an entire log record. In this case, we don't want to - * reset last_blk. Only when last_blk points in the middle of a log - * record do we update last_blk. - */ - if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { - uint h_size = INT_GET(head->h_size, ARCH_CONVERT); - - xhdrs = h_size / XLOG_HEADER_CYCLE_SIZE; - if (h_size % XLOG_HEADER_CYCLE_SIZE) - xhdrs++; - } else { - xhdrs = 1; - } - - if (*last_blk - i + extra_bblks - != BTOBB(INT_GET(head->h_len, ARCH_CONVERT))+xhdrs) - *last_blk = i; + for (i = (*last_blk) - 1; i >= 0; i--) { + if (i < start_blk) { + /* legal log record not found */ + xlog_warn( + "XFS: Log inconsistent (didn't find previous header)"); + ASSERT(0); + error = XFS_ERROR(EIO); + goto out; + } -out: - xlog_put_bp(bp); + if (smallmem) { + if ((error = xlog_bread(log, i, 1, bp))) + goto out; + offset = xlog_align(log, i, 1, bp); + } + + head = (xlog_rec_header_t *)offset; + + if (XLOG_HEADER_MAGIC_NUM == + INT_GET(head->h_magicno, ARCH_CONVERT)) + break; + + if (!smallmem) + offset -= BBSIZE; + } + + /* + * We hit the beginning of the physical log & still no header. Return + * to caller. If caller can handle a return of -1, then this routine + * will be called again for the end of the physical log. + */ + if (i == -1) { + error = -1; + goto out; + } + + /* + * We have the final block of the good log (the first block + * of the log record _before_ the head. So we check the uuid. + */ + if ((error = xlog_header_check_mount(log->l_mp, head))) + goto out; - return error; -} /* xlog_find_verify_log_record */ + /* + * We may have found a log record header before we expected one. + * last_blk will be the 1st block # with a given cycle #. We may end + * up reading an entire log record. In this case, we don't want to + * reset last_blk. Only when last_blk points in the middle of a log + * record do we update last_blk. + */ + if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { + uint h_size = INT_GET(head->h_size, ARCH_CONVERT); + + xhdrs = h_size / XLOG_HEADER_CYCLE_SIZE; + if (h_size % XLOG_HEADER_CYCLE_SIZE) + xhdrs++; + } else { + xhdrs = 1; + } + + if (*last_blk - i + extra_bblks + != BTOBB(INT_GET(head->h_len, ARCH_CONVERT)) + xhdrs) + *last_blk = i; + +out: + xlog_put_bp(bp); + return error; +} /* * Head is defined to be the point of the log where the next log write @@ -489,252 +527,257 @@ * last_blk contains the block number of the first block with a given * cycle number. * - * Also called from xfs_log_print.c - * * Return: zero if normal, non-zero if error. */ int -xlog_find_head(xlog_t *log, - xfs_daddr_t *return_head_blk) +xlog_find_head( + xlog_t *log, + xfs_daddr_t *return_head_blk) { - xfs_buf_t *bp; - xfs_daddr_t new_blk, first_blk, start_blk, last_blk, head_blk; - int num_scan_bblks; - uint first_half_cycle, last_half_cycle; - uint stop_on_cycle; - int error, log_bbnum = log->l_logBBsize; - - /* Is the end of the log device zeroed? */ - if ((error = xlog_find_zeroed(log, &first_blk)) == -1) { - *return_head_blk = first_blk; - - /* is the whole lot zeroed? */ - if (!first_blk) { - /* Linux XFS shouldn't generate totally zeroed logs - - * mkfs etc write a dummy unmount record to a fresh - * log so we can store the uuid in there - */ - xlog_warn("XFS: totally zeroed log"); - } + xfs_buf_t *bp; + xfs_caddr_t offset; + xfs_daddr_t new_blk, first_blk, start_blk, last_blk, head_blk; + int num_scan_bblks; + uint first_half_cycle, last_half_cycle; + uint stop_on_cycle; + int error, log_bbnum = log->l_logBBsize; + + /* Is the end of the log device zeroed? */ + if ((error = xlog_find_zeroed(log, &first_blk)) == -1) { + *return_head_blk = first_blk; + + /* Is the whole lot zeroed? */ + if (!first_blk) { + /* Linux XFS shouldn't generate totally zeroed logs - + * mkfs etc write a dummy unmount record to a fresh + * log so we can store the uuid in there + */ + xlog_warn("XFS: totally zeroed log"); + } - return 0; - } else if (error) { - xlog_warn("XFS: empty log check failed"); - return error; - } + return 0; + } else if (error) { + xlog_warn("XFS: empty log check failed"); + return error; + } - first_blk = 0; /* get cycle # of 1st block */ - bp = xlog_get_bp(1,log->l_mp); - if (!bp) - return ENOMEM; - if ((error = xlog_bread(log, 0, 1, bp))) - goto bp_err; - first_half_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); - - last_blk = head_blk = log_bbnum-1; /* get cycle # of last block */ - if ((error = xlog_bread(log, last_blk, 1, bp))) - goto bp_err; - last_half_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); - ASSERT(last_half_cycle != 0); - - /* - * If the 1st half cycle number is equal to the last half cycle number, - * then the entire log is stamped with the same cycle number. In this - * case, head_blk can't be set to zero (which makes sense). The below - * math doesn't work out properly with head_blk equal to zero. Instead, - * we set it to log_bbnum which is an illegal block number, but this - * value makes the math correct. If head_blk doesn't changed through - * all the tests below, *head_blk is set to zero at the very end rather - * than log_bbnum. In a sense, log_bbnum and zero are the same block - * in a circular file. - */ - if (first_half_cycle == last_half_cycle) { - /* - * In this case we believe that the entire log should have cycle - * number last_half_cycle. We need to scan backwards from the - * end verifying that there are no holes still containing - * last_half_cycle - 1. If we find such a hole, then the start - * of that hole will be the new head. The simple case looks like - * x | x ... | x - 1 | x - * Another case that fits this picture would be - * x | x + 1 | x ... | x - * In this case the head really is somwhere at the end of the - * log, as one of the latest writes at the beginning was incomplete. - * One more case is - * x | x + 1 | x ... | x - 1 | x - * This is really the combination of the above two cases, and the - * head has to end up at the start of the x-1 hole at the end of - * the log. - * - * In the 256k log case, we will read from the beginning to the - * end of the log and search for cycle numbers equal to x-1. We - * don't worry about the x+1 blocks that we encounter, because - * we know that they cannot be the head since the log started with - * x. - */ - head_blk = log_bbnum; - stop_on_cycle = last_half_cycle - 1; - } else { - /* - * In this case we want to find the first block with cycle number - * matching last_half_cycle. We expect the log to be some - * variation on - * x + 1 ... | x ... - * The first block with cycle number x (last_half_cycle) will be - * where the new head belongs. First we do a binary search for - * the first occurrence of last_half_cycle. The binary search - * may not be totally accurate, so then we scan back from there - * looking for occurrences of last_half_cycle before us. If - * that backwards scan wraps around the beginning of the log, - * then we look for occurrences of last_half_cycle - 1 at the - * end of the log. The cases we're looking for look like - * x + 1 ... | x | x + 1 | x ... - * ^ binary search stopped here - * or - * x + 1 ... | x ... | x - 1 | x - * <---------> less than scan distance - */ - stop_on_cycle = last_half_cycle; - if ((error = xlog_find_cycle_start(log, bp, first_blk, - &head_blk, last_half_cycle))) - goto bp_err; - } + first_blk = 0; /* get cycle # of 1st block */ + bp = xlog_get_bp(log, 1); + if (!bp) + return ENOMEM; + if ((error = xlog_bread(log, 0, 1, bp))) + goto bp_err; + offset = xlog_align(log, 0, 1, bp); + first_half_cycle = GET_CYCLE(offset, ARCH_CONVERT); - /* - * Now validate the answer. Scan back some number of maximum possible - * blocks and make sure each one has the expected cycle number. The - * maximum is determined by the total possible amount of buffering - * in the in-core log. The following number can be made tighter if - * we actually look at the block size of the filesystem. - */ - num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log); - if (head_blk >= num_scan_bblks) { - /* - * We are guaranteed that the entire check can be performed - * in one buffer. - */ - start_blk = head_blk - num_scan_bblks; - if ((error = xlog_find_verify_cycle(log, start_blk, num_scan_bblks, - stop_on_cycle, &new_blk))) - goto bp_err; - if (new_blk != -1) - head_blk = new_blk; - } else { /* need to read 2 parts of log */ - /* - * We are going to scan backwards in the log in two parts. First - * we scan the physical end of the log. In this part of the log, - * we are looking for blocks with cycle number last_half_cycle - 1. - * If we find one, then we know that the log starts there, as we've - * found a hole that didn't get written in going around the end - * of the physical log. The simple case for this is - * x + 1 ... | x ... | x - 1 | x - * <---------> less than scan distance - * If all of the blocks at the end of the log have cycle number - * last_half_cycle, then we check the blocks at the start of the - * log looking for occurrences of last_half_cycle. If we find one, - * then our current estimate for the location of the first - * occurrence of last_half_cycle is wrong and we move back to the - * hole we've found. This case looks like - * x + 1 ... | x | x + 1 | x ... - * ^ binary search stopped here - * Another case we need to handle that only occurs in 256k logs is - * x + 1 ... | x ... | x+1 | x ... - * ^ binary search stops here - * In a 256k log, the scan at the end of the log will see the x+1 - * blocks. We need to skip past those since that is certainly not - * the head of the log. By searching for last_half_cycle-1 we - * accomplish that. - */ - start_blk = log_bbnum - num_scan_bblks + head_blk; - ASSERT(head_blk <= INT_MAX && (xfs_daddr_t) num_scan_bblks-head_blk >= 0); - if ((error = xlog_find_verify_cycle(log, start_blk, - num_scan_bblks-(int)head_blk, (stop_on_cycle - 1), - &new_blk))) + last_blk = head_blk = log_bbnum - 1; /* get cycle # of last block */ + if ((error = xlog_bread(log, last_blk, 1, bp))) goto bp_err; - if (new_blk != -1) { - head_blk = new_blk; - goto bad_blk; + offset = xlog_align(log, last_blk, 1, bp); + last_half_cycle = GET_CYCLE(offset, ARCH_CONVERT); + ASSERT(last_half_cycle != 0); + + /* + * If the 1st half cycle number is equal to the last half cycle number, + * then the entire log is stamped with the same cycle number. In this + * case, head_blk can't be set to zero (which makes sense). The below + * math doesn't work out properly with head_blk equal to zero. Instead, + * we set it to log_bbnum which is an illegal block number, but this + * value makes the math correct. If head_blk doesn't changed through + * all the tests below, *head_blk is set to zero at the very end rather + * than log_bbnum. In a sense, log_bbnum and zero are the same block + * in a circular file. + */ + if (first_half_cycle == last_half_cycle) { + /* + * In this case we believe that the entire log should have + * cycle number last_half_cycle. We need to scan backwards + * from the end verifying that there are no holes still + * containing last_half_cycle - 1. If we find such a hole, + * then the start of that hole will be the new head. The + * simple case looks like + * x | x ... | x - 1 | x + * Another case that fits this picture would be + * x | x + 1 | x ... | x + * In this case the head really is somwhere at the end of the + * log, as one of the latest writes at the beginning was + * incomplete. + * One more case is + * x | x + 1 | x ... | x - 1 | x + * This is really the combination of the above two cases, and + * the head has to end up at the start of the x-1 hole at the + * end of the log. + * + * In the 256k log case, we will read from the beginning to the + * end of the log and search for cycle numbers equal to x-1. + * We don't worry about the x+1 blocks that we encounter, + * because we know that they cannot be the head since the log + * started with x. + */ + head_blk = log_bbnum; + stop_on_cycle = last_half_cycle - 1; + } else { + /* + * In this case we want to find the first block with cycle + * number matching last_half_cycle. We expect the log to be + * some variation on + * x + 1 ... | x ... + * The first block with cycle number x (last_half_cycle) will + * be where the new head belongs. First we do a binary search + * for the first occurrence of last_half_cycle. The binary + * search may not be totally accurate, so then we scan back + * from there looking for occurrences of last_half_cycle before + * us. If that backwards scan wraps around the beginning of + * the log, then we look for occurrences of last_half_cycle - 1 + * at the end of the log. The cases we're looking for look + * like + * x + 1 ... | x | x + 1 | x ... + * ^ binary search stopped here + * or + * x + 1 ... | x ... | x - 1 | x + * <---------> less than scan distance + */ + stop_on_cycle = last_half_cycle; + if ((error = xlog_find_cycle_start(log, bp, first_blk, + &head_blk, last_half_cycle))) + goto bp_err; } /* - * Scan beginning of log now. The last part of the physical log - * is good. This scan needs to verify that it doesn't find the - * last_half_cycle. + * Now validate the answer. Scan back some number of maximum possible + * blocks and make sure each one has the expected cycle number. The + * maximum is determined by the total possible amount of buffering + * in the in-core log. The following number can be made tighter if + * we actually look at the block size of the filesystem. */ - start_blk = 0; - ASSERT(head_blk <= INT_MAX); - if ((error = xlog_find_verify_cycle(log, start_blk, (int) head_blk, - stop_on_cycle, &new_blk))) - goto bp_err; - if (new_blk != -1) - head_blk = new_blk; - } - -bad_blk: - /* - * Now we need to make sure head_blk is not pointing to a block in - * the middle of a log record. - */ - num_scan_bblks = BTOBB(XLOG_MAX_RECORD_BSIZE); - if (head_blk >= num_scan_bblks) { - start_blk = head_blk - num_scan_bblks; /* don't read head_blk */ - - /* start ptr at last block ptr before head_blk */ - if ((error = xlog_find_verify_log_record(log, - start_blk, - &head_blk, - 0)) == -1) { - error = XFS_ERROR(EIO); - goto bp_err; - } else if (error) - goto bp_err; - } else { - start_blk = 0; - ASSERT(head_blk <= INT_MAX); - if ((error = xlog_find_verify_log_record(log, - start_blk, - &head_blk, - 0)) == -1) { - /* We hit the beginning of the log during our search */ - start_blk = log_bbnum - num_scan_bblks + head_blk; - new_blk = log_bbnum; - ASSERT(start_blk <= INT_MAX && (xfs_daddr_t) log_bbnum-start_blk >= 0); - ASSERT(head_blk <= INT_MAX); - if ((error = xlog_find_verify_log_record(log, - start_blk, - &new_blk, - (int)head_blk)) == -1) { - error = XFS_ERROR(EIO); - goto bp_err; - } else if (error) - goto bp_err; - if (new_blk != log_bbnum) - head_blk = new_blk; - } else if (error) - goto bp_err; - } + num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log); + if (head_blk >= num_scan_bblks) { + /* + * We are guaranteed that the entire check can be performed + * in one buffer. + */ + start_blk = head_blk - num_scan_bblks; + if ((error = xlog_find_verify_cycle(log, + start_blk, num_scan_bblks, + stop_on_cycle, &new_blk))) + goto bp_err; + if (new_blk != -1) + head_blk = new_blk; + } else { /* need to read 2 parts of log */ + /* + * We are going to scan backwards in the log in two parts. + * First we scan the physical end of the log. In this part + * of the log, we are looking for blocks with cycle number + * last_half_cycle - 1. + * If we find one, then we know that the log starts there, as + * we've found a hole that didn't get written in going around + * the end of the physical log. The simple case for this is + * x + 1 ... | x ... | x - 1 | x + * <---------> less than scan distance + * If all of the blocks at the end of the log have cycle number + * last_half_cycle, then we check the blocks at the start of + * the log looking for occurrences of last_half_cycle. If we + * find one, then our current estimate for the location of the + * first occurrence of last_half_cycle is wrong and we move + * back to the hole we've found. This case looks like + * x + 1 ... | x | x + 1 | x ... + * ^ binary search stopped here + * Another case we need to handle that only occurs in 256k + * logs is + * x + 1 ... | x ... | x+1 | x ... + * ^ binary search stops here + * In a 256k log, the scan at the end of the log will see the + * x + 1 blocks. We need to skip past those since that is + * certainly not the head of the log. By searching for + * last_half_cycle-1 we accomplish that. + */ + start_blk = log_bbnum - num_scan_bblks + head_blk; + ASSERT(head_blk <= INT_MAX && + (xfs_daddr_t) num_scan_bblks - head_blk >= 0); + if ((error = xlog_find_verify_cycle(log, start_blk, + num_scan_bblks - (int)head_blk, + (stop_on_cycle - 1), &new_blk))) + goto bp_err; + if (new_blk != -1) { + head_blk = new_blk; + goto bad_blk; + } + + /* + * Scan beginning of log now. The last part of the physical + * log is good. This scan needs to verify that it doesn't find + * the last_half_cycle. + */ + start_blk = 0; + ASSERT(head_blk <= INT_MAX); + if ((error = xlog_find_verify_cycle(log, + start_blk, (int)head_blk, + stop_on_cycle, &new_blk))) + goto bp_err; + if (new_blk != -1) + head_blk = new_blk; + } + + bad_blk: + /* + * Now we need to make sure head_blk is not pointing to a block in + * the middle of a log record. + */ + num_scan_bblks = XLOG_REC_SHIFT(log); + if (head_blk >= num_scan_bblks) { + start_blk = head_blk - num_scan_bblks; /* don't read head_blk */ + + /* start ptr at last block ptr before head_blk */ + if ((error = xlog_find_verify_log_record(log, start_blk, + &head_blk, 0)) == -1) { + error = XFS_ERROR(EIO); + goto bp_err; + } else if (error) + goto bp_err; + } else { + start_blk = 0; + ASSERT(head_blk <= INT_MAX); + if ((error = xlog_find_verify_log_record(log, start_blk, + &head_blk, 0)) == -1) { + /* We hit the beginning of the log during our search */ + start_blk = log_bbnum - num_scan_bblks + head_blk; + new_blk = log_bbnum; + ASSERT(start_blk <= INT_MAX && + (xfs_daddr_t) log_bbnum-start_blk >= 0); + ASSERT(head_blk <= INT_MAX); + if ((error = xlog_find_verify_log_record(log, + start_blk, &new_blk, + (int)head_blk)) == -1) { + error = XFS_ERROR(EIO); + goto bp_err; + } else if (error) + goto bp_err; + if (new_blk != log_bbnum) + head_blk = new_blk; + } else if (error) + goto bp_err; + } - xlog_put_bp(bp); - if (head_blk == log_bbnum) - *return_head_blk = 0; - else - *return_head_blk = head_blk; - /* - * When returning here, we have a good block number. Bad block - * means that during a previous crash, we didn't have a clean break - * from cycle number N to cycle number N-1. In this case, we need - * to find the first block with cycle number N-1. - */ - return 0; + xlog_put_bp(bp); + if (head_blk == log_bbnum) + *return_head_blk = 0; + else + *return_head_blk = head_blk; + /* + * When returning here, we have a good block number. Bad block + * means that during a previous crash, we didn't have a clean break + * from cycle number N to cycle number N-1. In this case, we need + * to find the first block with cycle number N-1. + */ + return 0; -bp_err: + bp_err: xlog_put_bp(bp); if (error) xlog_warn("XFS: failed to find log head"); - return error; -} /* xlog_find_head */ +} /* * Find the sync block number or the tail of the log. @@ -753,13 +796,15 @@ * available. */ int -xlog_find_tail(xlog_t *log, - xfs_daddr_t *head_blk, - xfs_daddr_t *tail_blk, - int readonly) +xlog_find_tail( + xlog_t *log, + xfs_daddr_t *head_blk, + xfs_daddr_t *tail_blk, + int readonly) { xlog_rec_header_t *rhead; xlog_op_header_t *op_head; + xfs_caddr_t offset = NULL; xfs_buf_t *bp; int error, i, found; xfs_daddr_t umount_data_blk; @@ -775,13 +820,14 @@ if ((error = xlog_find_head(log, head_blk))) return error; - bp = xlog_get_bp(1,log->l_mp); + bp = xlog_get_bp(log, 1); if (!bp) return ENOMEM; if (*head_blk == 0) { /* special case */ if ((error = xlog_bread(log, 0, 1, bp))) goto bread_err; - if (GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT) == 0) { + offset = xlog_align(log, 0, 1, bp); + if (GET_CYCLE(offset, ARCH_CONVERT) == 0) { *tail_blk = 0; /* leave all other log inited values alone */ goto exit; @@ -795,8 +841,9 @@ for (i = (int)(*head_blk) - 1; i >= 0; i--) { if ((error = xlog_bread(log, i, 1, bp))) goto bread_err; + offset = xlog_align(log, i, 1, bp); if (XLOG_HEADER_MAGIC_NUM == - INT_GET(*(uint *)(XFS_BUF_PTR(bp)), ARCH_CONVERT)) { + INT_GET(*(uint *)offset, ARCH_CONVERT)) { found = 1; break; } @@ -811,8 +858,9 @@ for (i = log->l_logBBsize - 1; i >= (int)(*head_blk); i--) { if ((error = xlog_bread(log, i, 1, bp))) goto bread_err; + offset = xlog_align(log, i, 1, bp); if (XLOG_HEADER_MAGIC_NUM == - INT_GET(*(uint*)(XFS_BUF_PTR(bp)), ARCH_CONVERT)) { + INT_GET(*(uint*)offset, ARCH_CONVERT)) { found = 2; break; } @@ -825,7 +873,7 @@ } /* find blk_no of tail of log */ - rhead = (xlog_rec_header_t *)XFS_BUF_PTR(bp); + rhead = (xlog_rec_header_t *)offset; *tail_blk = BLOCK_LSN(rhead->h_tail_lsn, ARCH_CONVERT); /* @@ -885,7 +933,8 @@ if ((error = xlog_bread(log, umount_data_blk, 1, bp))) { goto bread_err; } - op_head = (xlog_op_header_t *)XFS_BUF_PTR(bp); + offset = xlog_align(log, umount_data_blk, 1, bp); + op_head = (xlog_op_header_t *)offset; if (op_head->oh_flags & XLOG_UNMOUNT_TRANS) { /* * Set tail and last sync so that newly written @@ -900,7 +949,6 @@ } } -#ifdef __KERNEL__ /* * Make sure that there are no blocks in front of the head * with the same cycle number as the head. This can happen @@ -920,11 +968,9 @@ * But... if the -device- itself is readonly, just skip this. * We can't recover this device anyway, so it won't matter. */ - - if (!bdev_read_only(log->l_mp->m_logdev_targp->pbr_bdev)) { + if (!xfs_readonly_buftarg(log->l_mp->m_logdev_targp)) { error = xlog_clear_stale_blocks(log, tail_lsn); } -#endif bread_err: exit: @@ -932,10 +978,8 @@ if (error) xlog_warn("XFS: failed to locate log tail"); - return error; -} /* xlog_find_tail */ - +} /* * Is the log zeroed at all? @@ -954,22 +998,25 @@ * >0 => error has occurred */ int -xlog_find_zeroed(struct log *log, - xfs_daddr_t *blk_no) +xlog_find_zeroed( + xlog_t *log, + xfs_daddr_t *blk_no) { xfs_buf_t *bp; + xfs_caddr_t offset; uint first_cycle, last_cycle; xfs_daddr_t new_blk, last_blk, start_blk; xfs_daddr_t num_scan_bblks; int error, log_bbnum = log->l_logBBsize; /* check totally zeroed log */ - bp = xlog_get_bp(1,log->l_mp); + bp = xlog_get_bp(log, 1); if (!bp) return ENOMEM; if ((error = xlog_bread(log, 0, 1, bp))) goto bp_err; - first_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + offset = xlog_align(log, 0, 1, bp); + first_cycle = GET_CYCLE(offset, ARCH_CONVERT); if (first_cycle == 0) { /* completely zeroed log */ *blk_no = 0; xlog_put_bp(bp); @@ -979,7 +1026,8 @@ /* check partially zeroed log */ if ((error = xlog_bread(log, log_bbnum-1, 1, bp))) goto bp_err; - last_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + offset = xlog_align(log, log_bbnum-1, 1, bp); + last_cycle = GET_CYCLE(offset, ARCH_CONVERT); if (last_cycle != 0) { /* log completely written to */ xlog_put_bp(bp); return 0; @@ -1040,67 +1088,106 @@ if (error) return error; return -1; -} /* xlog_find_zeroed */ +} /* - * This is simply a subroutine used by xlog_clear_stale_blocks() below + * These are simple subroutines used by xlog_clear_stale_blocks() below * to initialize a buffer full of empty log record headers and write * them into the log. */ +STATIC void +xlog_add_record( + xlog_t *log, + xfs_caddr_t buf, + int cycle, + int block, + int tail_cycle, + int tail_block) +{ + xlog_rec_header_t *recp = (xlog_rec_header_t *)buf; + + memset(buf, 0, BBSIZE); + INT_SET(recp->h_magicno, ARCH_CONVERT, XLOG_HEADER_MAGIC_NUM); + INT_SET(recp->h_cycle, ARCH_CONVERT, cycle); + INT_SET(recp->h_version, ARCH_CONVERT, + XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? 2 : 1); + ASSIGN_ANY_LSN(recp->h_lsn, cycle, block, ARCH_CONVERT); + ASSIGN_ANY_LSN(recp->h_tail_lsn, tail_cycle, tail_block, ARCH_CONVERT); + INT_SET(recp->h_fmt, ARCH_CONVERT, XLOG_FMT); + memcpy(&recp->h_fs_uuid, &log->l_mp->m_sb.sb_uuid, sizeof(uuid_t)); +} + STATIC int xlog_write_log_records( - xlog_t *log, - int cycle, - int start_block, - int blocks, - int tail_cycle, - int tail_block) -{ - xlog_rec_header_t *recp; - int i, j; - int end_block = start_block + blocks; - int error = 0; - xfs_buf_t *bp; - char *buf; - int bufblks; + xlog_t *log, + int cycle, + int start_block, + int blocks, + int tail_cycle, + int tail_block) +{ + xfs_caddr_t offset; + xfs_buf_t *bp; + int balign, ealign; + int sectbb = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1); + int end_block = start_block + blocks; + int bufblks; + int error = 0; + int i, j = 0; bufblks = 1 << ffs(blocks); - while (!(bp = xlog_get_bp(bufblks, log->l_mp))) { + while (!(bp = xlog_get_bp(log, bufblks))) { bufblks >>= 1; - if (!bufblks) + if (bufblks <= log->l_sectbb_log) return ENOMEM; } - buf = XFS_BUF_PTR(bp); - recp = (xlog_rec_header_t*)buf; - - memset(buf, 0, BBSIZE); - INT_SET(recp->h_magicno, ARCH_CONVERT, XLOG_HEADER_MAGIC_NUM); - INT_SET(recp->h_cycle, ARCH_CONVERT, cycle); - INT_SET(recp->h_version, ARCH_CONVERT, - XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? 2 : 1); - ASSIGN_ANY_LSN(recp->h_tail_lsn, tail_cycle, tail_block, ARCH_CONVERT); + /* We may need to do a read at the start to fill in part of + * the buffer in the starting sector not covered by the first + * write below. + */ + balign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, start_block); + if (balign != start_block) { + if ((error = xlog_bread(log, start_block, 1, bp))) { + xlog_put_bp(bp); + return error; + } + j = start_block - balign; + } for (i = start_block; i < end_block; i += bufblks) { - int bcount = min(bufblks, end_block - start_block); - /* with plenty of memory, we duplicate the block - * right through the buffer and modify each entry - */ - ASSIGN_ANY_LSN(recp->h_lsn, cycle, i, ARCH_CONVERT); - for (j = 1; j < bcount; j++) { - buf += BBSIZE; - recp = (xlog_rec_header_t*)buf; - memcpy(buf, XFS_BUF_PTR(bp), BBSIZE); - ASSIGN_ANY_LSN(recp->h_lsn, cycle, i+j, ARCH_CONVERT); - } - /* then write the whole lot out at once */ - error = xlog_bwrite(log, start_block, bcount, bp); - start_block += bcount; - buf = XFS_BUF_PTR(bp); - recp = (xlog_rec_header_t*)buf; + int bcount, endcount; + + bcount = min(bufblks, end_block - start_block); + endcount = bcount - j; + + /* We may need to do a read at the end to fill in part of + * the buffer in the final sector not covered by the write. + * If this is the same sector as the above read, skip it. + */ + ealign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, end_block); + if (j == 0 && (start_block + endcount > ealign)) { + offset = XFS_BUF_PTR(bp); + balign = BBTOB(ealign - start_block); + XFS_BUF_SET_PTR(bp, offset + balign, BBTOB(sectbb)); + if ((error = xlog_bread(log, ealign, sectbb, bp))) + break; + XFS_BUF_SET_PTR(bp, offset, bufblks); + } + + offset = xlog_align(log, start_block, endcount, bp); + for (; j < endcount; j++) { + xlog_add_record(log, offset, cycle, i+j, + tail_cycle, tail_block); + offset += BBSIZE; + } + error = xlog_bwrite(log, start_block, endcount, bp); + if (error) + break; + start_block += endcount; + j = 0; } xlog_put_bp(bp); - return error; } @@ -1244,10 +1331,11 @@ */ STATIC xlog_recover_t * -xlog_recover_find_tid(xlog_recover_t *q, - xlog_tid_t tid) +xlog_recover_find_tid( + xlog_recover_t *q, + xlog_tid_t tid) { - xlog_recover_t *p = q; + xlog_recover_t *p = q; while (p != NULL) { if (p->r_log_tid == tid) @@ -1255,42 +1343,43 @@ p = p->r_next; } return p; -} /* xlog_recover_find_tid */ - +} STATIC void -xlog_recover_put_hashq(xlog_recover_t **q, - xlog_recover_t *trans) +xlog_recover_put_hashq( + xlog_recover_t **q, + xlog_recover_t *trans) { trans->r_next = *q; *q = trans; -} /* xlog_recover_put_hashq */ - +} STATIC void -xlog_recover_add_item(xlog_recover_item_t **itemq) +xlog_recover_add_item( + xlog_recover_item_t **itemq) { - xlog_recover_item_t *item; + xlog_recover_item_t *item; item = kmem_zalloc(sizeof(xlog_recover_item_t), 0); xlog_recover_insert_item_backq(itemq, item); -} /* xlog_recover_add_item */ - +} STATIC int -xlog_recover_add_to_cont_trans(xlog_recover_t *trans, - xfs_caddr_t dp, - int len) +xlog_recover_add_to_cont_trans( + xlog_recover_t *trans, + xfs_caddr_t dp, + int len) { xlog_recover_item_t *item; - xfs_caddr_t ptr, old_ptr; + xfs_caddr_t ptr, old_ptr; int old_len; item = trans->r_itemq; if (item == 0) { /* finish copying rest of trans header */ xlog_recover_add_item(&trans->r_itemq); - ptr = (xfs_caddr_t)&trans->r_theader+sizeof(xfs_trans_header_t)-len; + ptr = (xfs_caddr_t) &trans->r_theader + + sizeof(xfs_trans_header_t) - len; memcpy(ptr, dp, len); /* d, s, l */ return 0; } @@ -1304,10 +1393,10 @@ item->ri_buf[item->ri_cnt-1].i_len += len; item->ri_buf[item->ri_cnt-1].i_addr = ptr; return 0; -} /* xlog_recover_add_to_cont_trans */ - +} -/* The next region to add is the start of a new region. It could be +/* + * The next region to add is the start of a new region. It could be * a whole region or it could be the first part of a new region. Because * of this, the assumption here is that the type and size fields of all * format structures fit into the first 32 bits of the structure. @@ -1320,13 +1409,14 @@ * will appear in the current log item. */ STATIC int -xlog_recover_add_to_trans(xlog_recover_t *trans, - xfs_caddr_t dp, - int len) -{ - xfs_inode_log_format_t *in_f; /* any will do */ - xlog_recover_item_t *item; - xfs_caddr_t ptr; +xlog_recover_add_to_trans( + xlog_recover_t *trans, + xfs_caddr_t dp, + int len) +{ + xfs_inode_log_format_t *in_f; /* any will do */ + xlog_recover_item_t *item; + xfs_caddr_t ptr; if (!len) return 0; @@ -1339,7 +1429,7 @@ return 0; } - ptr = kmem_alloc(len, 0); + ptr = kmem_alloc(len, KM_SLEEP); memcpy(ptr, dp, len); in_f = (xfs_inode_log_format_t *)ptr; @@ -1362,29 +1452,29 @@ item->ri_buf[item->ri_cnt].i_len = len; item->ri_cnt++; return 0; -} /* xlog_recover_add_to_trans */ - +} STATIC void -xlog_recover_new_tid(xlog_recover_t **q, - xlog_tid_t tid, - xfs_lsn_t lsn) +xlog_recover_new_tid( + xlog_recover_t **q, + xlog_tid_t tid, + xfs_lsn_t lsn) { - xlog_recover_t *trans; + xlog_recover_t *trans; - trans = kmem_zalloc(sizeof(xlog_recover_t), 0); + trans = kmem_zalloc(sizeof(xlog_recover_t), KM_SLEEP); trans->r_log_tid = tid; trans->r_lsn = lsn; xlog_recover_put_hashq(q, trans); -} /* xlog_recover_new_tid */ - +} STATIC int -xlog_recover_unlink_tid(xlog_recover_t **q, - xlog_recover_t *trans) +xlog_recover_unlink_tid( + xlog_recover_t **q, + xlog_recover_t *trans) { - xlog_recover_t *tp; - int found = 0; + xlog_recover_t *tp; + int found = 0; ASSERT(trans != 0); if (trans == *q) { @@ -1407,11 +1497,12 @@ tp->r_next = tp->r_next->r_next; } return 0; -} /* xlog_recover_unlink_tid */ +} STATIC void -xlog_recover_insert_item_backq(xlog_recover_item_t **q, - xlog_recover_item_t *item) +xlog_recover_insert_item_backq( + xlog_recover_item_t **q, + xlog_recover_item_t *item) { if (*q == 0) { item->ri_prev = item->ri_next = item; @@ -1422,55 +1513,53 @@ (*q)->ri_prev = item; item->ri_prev->ri_next = item; } -} /* xlog_recover_insert_item_backq */ +} STATIC void -xlog_recover_insert_item_frontq(xlog_recover_item_t **q, - xlog_recover_item_t *item) +xlog_recover_insert_item_frontq( + xlog_recover_item_t **q, + xlog_recover_item_t *item) { xlog_recover_insert_item_backq(q, item); *q = item; -} /* xlog_recover_insert_item_frontq */ +} STATIC int -xlog_recover_reorder_trans(xlog_t *log, - xlog_recover_t *trans) +xlog_recover_reorder_trans( + xlog_t *log, + xlog_recover_t *trans) { - xlog_recover_item_t *first_item, *itemq, *itemq_next; + xlog_recover_item_t *first_item, *itemq, *itemq_next; - first_item = itemq = trans->r_itemq; - trans->r_itemq = NULL; - do { - itemq_next = itemq->ri_next; - switch (ITEM_TYPE(itemq)) { - case XFS_LI_BUF: - case XFS_LI_6_1_BUF: - case XFS_LI_5_3_BUF: { - xlog_recover_insert_item_frontq(&trans->r_itemq, itemq); - break; - } - case XFS_LI_INODE: - case XFS_LI_6_1_INODE: - case XFS_LI_5_3_INODE: - case XFS_LI_DQUOT: - case XFS_LI_QUOTAOFF: - case XFS_LI_EFD: - case XFS_LI_EFI: { - xlog_recover_insert_item_backq(&trans->r_itemq, itemq); - break; - } - default: { - xlog_warn( + first_item = itemq = trans->r_itemq; + trans->r_itemq = NULL; + do { + itemq_next = itemq->ri_next; + switch (ITEM_TYPE(itemq)) { + case XFS_LI_BUF: + case XFS_LI_6_1_BUF: + case XFS_LI_5_3_BUF: + xlog_recover_insert_item_frontq(&trans->r_itemq, itemq); + break; + case XFS_LI_INODE: + case XFS_LI_6_1_INODE: + case XFS_LI_5_3_INODE: + case XFS_LI_DQUOT: + case XFS_LI_QUOTAOFF: + case XFS_LI_EFD: + case XFS_LI_EFI: + xlog_recover_insert_item_backq(&trans->r_itemq, itemq); + break; + default: + xlog_warn( "XFS: xlog_recover_reorder_trans: unrecognized type of log operation"); - ASSERT(0); - return XFS_ERROR(EIO); - } - } - itemq = itemq_next; - } while (first_item != itemq); - return 0; -} /* xlog_recover_reorder_trans */ - + ASSERT(0); + return XFS_ERROR(EIO); + } + itemq = itemq_next; + } while (first_item != itemq); + return 0; +} /* * Build up the table of buf cancel records so that we don't replay @@ -1485,17 +1574,18 @@ * record during the second pass. */ STATIC void -xlog_recover_do_buffer_pass1(xlog_t *log, - xfs_buf_log_format_t *buf_f) +xlog_recover_do_buffer_pass1( + xlog_t *log, + xfs_buf_log_format_t *buf_f) { xfs_buf_cancel_t *bcp; xfs_buf_cancel_t *nextp; xfs_buf_cancel_t *prevp; xfs_buf_cancel_t **bucket; xfs_buf_log_format_v1_t *obuf_f; - xfs_daddr_t blkno=0; - uint len=0; - ushort flags=0; + xfs_daddr_t blkno = 0; + uint len = 0; + ushort flags = 0; switch (buf_f->blf_type) { case XFS_LI_BUF: @@ -1515,9 +1605,8 @@ /* * If this isn't a cancel buffer item, then just return. */ - if (!(flags & XFS_BLI_CANCEL)) { + if (!(flags & XFS_BLI_CANCEL)) return; - } /* * Insert an xfs_buf_cancel record into the hash table of @@ -1531,8 +1620,8 @@ * the bucket. */ if (*bucket == NULL) { - bcp = (xfs_buf_cancel_t*)kmem_alloc(sizeof(xfs_buf_cancel_t), - KM_SLEEP); + bcp = (xfs_buf_cancel_t *)kmem_alloc(sizeof(xfs_buf_cancel_t), + KM_SLEEP); bcp->bc_blkno = blkno; bcp->bc_len = len; bcp->bc_refcount = 1; @@ -1557,8 +1646,8 @@ nextp = nextp->bc_next; } ASSERT(prevp != NULL); - bcp = (xfs_buf_cancel_t*)kmem_alloc(sizeof(xfs_buf_cancel_t), - KM_SLEEP); + bcp = (xfs_buf_cancel_t *)kmem_alloc(sizeof(xfs_buf_cancel_t), + KM_SLEEP); bcp->bc_blkno = blkno; bcp->bc_len = len; bcp->bc_refcount = 1; @@ -1580,17 +1669,17 @@ * made at that point. */ STATIC int -xlog_recover_do_buffer_pass2(xlog_t *log, - xfs_buf_log_format_t *buf_f) +xlog_recover_do_buffer_pass2( + xlog_t *log, + xfs_buf_log_format_t *buf_f) { xfs_buf_cancel_t *bcp; xfs_buf_cancel_t *prevp; xfs_buf_cancel_t **bucket; xfs_buf_log_format_v1_t *obuf_f; - xfs_daddr_t blkno=0; - ushort flags=0; - uint len=0; - + xfs_daddr_t blkno = 0; + ushort flags = 0; + uint len = 0; switch (buf_f->blf_type) { case XFS_LI_BUF: @@ -1667,7 +1756,6 @@ return 0; } - /* * Perform recovery for a buffer full of inodes. In these buffers, * the only data which should be recovered is that which corresponds @@ -1682,10 +1770,11 @@ * sent to xlog_recover_do_reg_buffer() below during recovery. */ STATIC int -xlog_recover_do_inode_buffer(xfs_mount_t *mp, - xlog_recover_item_t *item, - xfs_buf_t *bp, - xfs_buf_log_format_t *buf_f) +xlog_recover_do_inode_buffer( + xfs_mount_t *mp, + xlog_recover_item_t *item, + xfs_buf_t *bp, + xfs_buf_log_format_t *buf_f) { int i; int item_index; @@ -1698,8 +1787,8 @@ xfs_agino_t *logged_nextp; xfs_agino_t *buffer_nextp; xfs_buf_log_format_v1_t *obuf_f; - unsigned int *data_map=NULL; - unsigned int map_size=0; + unsigned int *data_map = NULL; + unsigned int map_size = 0; switch (buf_f->blf_type) { case XFS_LI_BUF: @@ -1790,7 +1879,7 @@ } return 0; -} /* xlog_recover_do_inode_buffer */ +} /* * Perform a 'normal' buffer recovery. Each logged region of the @@ -1800,17 +1889,18 @@ */ /*ARGSUSED*/ STATIC void -xlog_recover_do_reg_buffer(xfs_mount_t *mp, - xlog_recover_item_t *item, - xfs_buf_t *bp, - xfs_buf_log_format_t *buf_f) +xlog_recover_do_reg_buffer( + xfs_mount_t *mp, + xlog_recover_item_t *item, + xfs_buf_t *bp, + xfs_buf_log_format_t *buf_f) { int i; int bit; int nbits; xfs_buf_log_format_v1_t *obuf_f; - unsigned int *data_map=NULL; - unsigned int map_size=0; + unsigned int *data_map = NULL; + unsigned int map_size = 0; int error; switch (buf_f->blf_type) { @@ -1860,7 +1950,7 @@ /* Shouldn't be any more regions */ ASSERT(i == item->ri_total); -} /* xlog_recover_do_reg_buffer */ +} /* * Do some primitive error checking on ondisk dquot data structures. @@ -1991,7 +2081,7 @@ xfs_buf_t *bp, xfs_buf_log_format_t *buf_f) { - uint type; + uint type; /* * Filesystems are required to send in quota flags at mount time. @@ -2038,9 +2128,10 @@ * for more details on the implementation of the table of cancel records. */ STATIC int -xlog_recover_do_buffer_trans(xlog_t *log, - xlog_recover_item_t *item, - int pass) +xlog_recover_do_buffer_trans( + xlog_t *log, + xlog_recover_item_t *item, + int pass) { xfs_buf_log_format_t *buf_f; xfs_buf_log_format_v1_t *obuf_f; @@ -2075,19 +2166,19 @@ } } switch (buf_f->blf_type) { - case XFS_LI_BUF: + case XFS_LI_BUF: blkno = buf_f->blf_blkno; len = buf_f->blf_len; flags = buf_f->blf_flags; break; - case XFS_LI_6_1_BUF: - case XFS_LI_5_3_BUF: + case XFS_LI_6_1_BUF: + case XFS_LI_5_3_BUF: obuf_f = (xfs_buf_log_format_v1_t*)buf_f; blkno = obuf_f->blf_blkno; len = obuf_f->blf_len; flags = obuf_f->blf_flags; break; - default: + default: xfs_fs_cmn_err(CE_ALERT, log->l_mp, "xfs_log_recover: unknown buffer type 0x%x, dev 0x%x", buf_f->blf_type, log->l_dev); @@ -2152,12 +2243,13 @@ } return (error); -} /* xlog_recover_do_buffer_trans */ +} STATIC int -xlog_recover_do_inode_trans(xlog_t *log, - xlog_recover_item_t *item, - int pass) +xlog_recover_do_inode_trans( + xlog_t *log, + xlog_recover_item_t *item, + int pass) { xfs_inode_log_format_t *in_f; xfs_mount_t *mp; @@ -2377,7 +2469,6 @@ } } - write_inode_buffer: if (ITEM_TYPE(item) == XFS_LI_INODE) { ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL || @@ -2391,8 +2482,7 @@ } return (error); -} /* xlog_recover_do_inode_trans */ - +} /* * Recover QUOTAOFF records. We simply make a note of it in the xlog_t @@ -2400,11 +2490,12 @@ * of that type. */ STATIC int -xlog_recover_do_quotaoff_trans(xlog_t *log, - xlog_recover_item_t *item, - int pass) +xlog_recover_do_quotaoff_trans( + xlog_t *log, + xlog_recover_item_t *item, + int pass) { - xfs_qoff_logformat_t *qoff_f; + xfs_qoff_logformat_t *qoff_f; if (pass == XLOG_RECOVER_PASS2) { return (0); @@ -2425,14 +2516,14 @@ return (0); } - /* * Recover a dquot record */ STATIC int -xlog_recover_do_dquot_trans(xlog_t *log, - xlog_recover_item_t *item, - int pass) +xlog_recover_do_dquot_trans( + xlog_t *log, + xlog_recover_item_t *item, + int pass) { xfs_mount_t *mp; xfs_buf_t *bp; @@ -2516,7 +2607,7 @@ xfs_bdwrite(mp, bp); return (0); -} /* xlog_recover_do_dquot_trans */ +} /* * This routine is called to create an in-core extent free intent @@ -2526,10 +2617,11 @@ * LSN. */ STATIC void -xlog_recover_do_efi_trans(xlog_t *log, - xlog_recover_item_t *item, - xfs_lsn_t lsn, - int pass) +xlog_recover_do_efi_trans( + xlog_t *log, + xlog_recover_item_t *item, + xfs_lsn_t lsn, + int pass) { xfs_mount_t *mp; xfs_efi_log_item_t *efip; @@ -2558,7 +2650,7 @@ * xfs_trans_update_ail() drops the AIL lock. */ xfs_trans_update_ail(mp, (xfs_log_item_t *)efip, lsn, s); -} /* xlog_recover_do_efi_trans */ +} /* @@ -2570,13 +2662,14 @@ * AIL and free it. */ STATIC void -xlog_recover_do_efd_trans(xlog_t *log, - xlog_recover_item_t *item, - int pass) +xlog_recover_do_efd_trans( + xlog_t *log, + xlog_recover_item_t *item, + int pass) { xfs_mount_t *mp; xfs_efd_log_format_t *efd_formatp; - xfs_efi_log_item_t *efip=NULL; + xfs_efi_log_item_t *efip = NULL; xfs_log_item_t *lip; int gen; int nexts; @@ -2629,9 +2722,9 @@ ((nexts - 1) * sizeof(xfs_extent_t))); } else { kmem_zone_free(xfs_efi_zone, efip); + } } - } -} /* xlog_recover_do_efd_trans */ +} /* * Perform the transaction @@ -2640,12 +2733,13 @@ * EFIs and EFDs get queued up by adding entries into the AIL for them. */ STATIC int -xlog_recover_do_trans(xlog_t *log, - xlog_recover_t *trans, - int pass) +xlog_recover_do_trans( + xlog_t *log, + xlog_recover_t *trans, + int pass) { - int error = 0; - xlog_recover_item_t *item, *first_item; + int error = 0; + xlog_recover_item_t *item, *first_item; if ((error = xlog_recover_reorder_trans(log, trans))) return error; @@ -2695,8 +2789,7 @@ } while (first_item != item); return error; -} /* xlog_recover_do_trans */ - +} /* * Free up any resources allocated by the transaction @@ -2704,10 +2797,11 @@ * Remember that EFIs, EFDs, and IUNLINKs are handled later. */ STATIC void -xlog_recover_free_trans(xlog_recover_t *trans) +xlog_recover_free_trans( + xlog_recover_t *trans) { - xlog_recover_item_t *first_item, *item, *free_item; - int i; + xlog_recover_item_t *first_item, *item, *free_item; + int i; item = first_item = trans->r_itemq; do { @@ -2725,16 +2819,16 @@ } while (first_item != item); /* Free the transaction recover structure */ kmem_free(trans, sizeof(xlog_recover_t)); -} /* xlog_recover_free_trans */ - +} STATIC int -xlog_recover_commit_trans(xlog_t *log, - xlog_recover_t **q, - xlog_recover_t *trans, - int pass) +xlog_recover_commit_trans( + xlog_t *log, + xlog_recover_t **q, + xlog_recover_t *trans, + int pass) { - int error; + int error; if ((error = xlog_recover_unlink_tid(q, trans))) return error; @@ -2742,18 +2836,16 @@ return error; xlog_recover_free_trans(trans); /* no error */ return 0; -} /* xlog_recover_commit_trans */ - +} -/*ARGSUSED*/ STATIC int -xlog_recover_unmount_trans(xlog_recover_t *trans) +xlog_recover_unmount_trans( + xlog_recover_t *trans) { /* Do nothing now */ xlog_warn("XFS: xlog_recover_unmount_trans: Unmount LR"); - return( 0 ); -} /* xlog_recover_unmount_trans */ - + return 0; +} /* * There are two valid states of the r_state field. 0 indicates that the @@ -2765,97 +2857,101 @@ * NOTE: skip LRs with 0 data length. */ STATIC int -xlog_recover_process_data(xlog_t *log, - xlog_recover_t *rhash[], - xlog_rec_header_t *rhead, - xfs_caddr_t dp, - int pass) -{ - xfs_caddr_t lp = dp+INT_GET(rhead->h_len, ARCH_CONVERT); - int num_logops = INT_GET(rhead->h_num_logops, ARCH_CONVERT); - xlog_op_header_t *ohead; - xlog_recover_t *trans; - xlog_tid_t tid; - int error; - unsigned long hash; - uint flags; - - /* check the log format matches our own - else we can't recover */ - if (xlog_header_check_recover(log->l_mp, rhead)) - return (XFS_ERROR(EIO)); - - while ((dp < lp) && num_logops) { - ASSERT(dp + sizeof(xlog_op_header_t) <= lp); - ohead = (xlog_op_header_t *)dp; - dp += sizeof(xlog_op_header_t); - if (ohead->oh_clientid != XFS_TRANSACTION && - ohead->oh_clientid != XFS_LOG) { - xlog_warn("XFS: xlog_recover_process_data: bad clientid"); - ASSERT(0); - return (XFS_ERROR(EIO)); - } - tid = INT_GET(ohead->oh_tid, ARCH_CONVERT); - hash = XLOG_RHASH(tid); - trans = xlog_recover_find_tid(rhash[hash], tid); - if (trans == NULL) { /* not found; add new tid */ - if (ohead->oh_flags & XLOG_START_TRANS) - xlog_recover_new_tid(&rhash[hash], tid, INT_GET(rhead->h_lsn, ARCH_CONVERT)); - } else { - ASSERT(dp+INT_GET(ohead->oh_len, ARCH_CONVERT) <= lp); - flags = ohead->oh_flags & ~XLOG_END_TRANS; - if (flags & XLOG_WAS_CONT_TRANS) - flags &= ~XLOG_CONTINUE_TRANS; - switch (flags) { - case XLOG_COMMIT_TRANS: { - error = xlog_recover_commit_trans(log, &rhash[hash], - trans, pass); - break; - } - case XLOG_UNMOUNT_TRANS: { - error = xlog_recover_unmount_trans(trans); - break; - } - case XLOG_WAS_CONT_TRANS: { - error = xlog_recover_add_to_cont_trans(trans, dp, - INT_GET(ohead->oh_len, ARCH_CONVERT)); - break; - } - case XLOG_START_TRANS : { - xlog_warn("XFS: xlog_recover_process_data: bad transaction"); - ASSERT(0); - error = XFS_ERROR(EIO); - break; - } - case 0: - case XLOG_CONTINUE_TRANS: { - error = xlog_recover_add_to_trans(trans, dp, - INT_GET(ohead->oh_len, ARCH_CONVERT)); - break; +xlog_recover_process_data( + xlog_t *log, + xlog_recover_t *rhash[], + xlog_rec_header_t *rhead, + xfs_caddr_t dp, + int pass) +{ + xfs_caddr_t lp; + int num_logops; + xlog_op_header_t *ohead; + xlog_recover_t *trans; + xlog_tid_t tid; + int error; + unsigned long hash; + uint flags; + + lp = dp + INT_GET(rhead->h_len, ARCH_CONVERT); + num_logops = INT_GET(rhead->h_num_logops, ARCH_CONVERT); + + /* check the log format matches our own - else we can't recover */ + if (xlog_header_check_recover(log->l_mp, rhead)) + return (XFS_ERROR(EIO)); + + while ((dp < lp) && num_logops) { + ASSERT(dp + sizeof(xlog_op_header_t) <= lp); + ohead = (xlog_op_header_t *)dp; + dp += sizeof(xlog_op_header_t); + if (ohead->oh_clientid != XFS_TRANSACTION && + ohead->oh_clientid != XFS_LOG) { + xlog_warn( + "XFS: xlog_recover_process_data: bad clientid"); + ASSERT(0); + return (XFS_ERROR(EIO)); } - default: { - xlog_warn("XFS: xlog_recover_process_data: bad flag"); - ASSERT(0); - error = XFS_ERROR(EIO); - break; + tid = INT_GET(ohead->oh_tid, ARCH_CONVERT); + hash = XLOG_RHASH(tid); + trans = xlog_recover_find_tid(rhash[hash], tid); + if (trans == NULL) { /* not found; add new tid */ + if (ohead->oh_flags & XLOG_START_TRANS) + xlog_recover_new_tid(&rhash[hash], tid, + INT_GET(rhead->h_lsn, ARCH_CONVERT)); + } else { + ASSERT(dp+INT_GET(ohead->oh_len, ARCH_CONVERT) <= lp); + flags = ohead->oh_flags & ~XLOG_END_TRANS; + if (flags & XLOG_WAS_CONT_TRANS) + flags &= ~XLOG_CONTINUE_TRANS; + switch (flags) { + case XLOG_COMMIT_TRANS: + error = xlog_recover_commit_trans(log, + &rhash[hash], trans, pass); + break; + case XLOG_UNMOUNT_TRANS: + error = xlog_recover_unmount_trans(trans); + break; + case XLOG_WAS_CONT_TRANS: + error = xlog_recover_add_to_cont_trans(trans, + dp, INT_GET(ohead->oh_len, + ARCH_CONVERT)); + break; + case XLOG_START_TRANS: + xlog_warn( + "XFS: xlog_recover_process_data: bad transaction"); + ASSERT(0); + error = XFS_ERROR(EIO); + break; + case 0: + case XLOG_CONTINUE_TRANS: + error = xlog_recover_add_to_trans(trans, + dp, INT_GET(ohead->oh_len, + ARCH_CONVERT)); + break; + default: + xlog_warn( + "XFS: xlog_recover_process_data: bad flag"); + ASSERT(0); + error = XFS_ERROR(EIO); + break; + } + if (error) + return error; } - } /* switch */ - if (error) - return error; - } /* if */ - dp += INT_GET(ohead->oh_len, ARCH_CONVERT); - num_logops--; - } - return( 0 ); -} /* xlog_recover_process_data */ - + dp += INT_GET(ohead->oh_len, ARCH_CONVERT); + num_logops--; + } + return 0; +} /* * Process an extent free intent item that was recovered from * the log. We need to free the extents that it describes. */ STATIC void -xlog_recover_process_efi(xfs_mount_t *mp, - xfs_efi_log_item_t *efip) +xlog_recover_process_efi( + xfs_mount_t *mp, + xfs_efi_log_item_t *efip) { xfs_efd_log_item_t *efdp; xfs_trans_t *tp; @@ -2900,8 +2996,7 @@ efip->efi_flags |= XFS_EFI_RECOVERED; xfs_trans_commit(tp, 0, NULL); -} /* xlog_recover_process_efi */ - +} /* * Verify that once we've encountered something other than an EFI @@ -2909,13 +3004,13 @@ */ #if defined(DEBUG) STATIC void -xlog_recover_check_ail(xfs_mount_t *mp, - xfs_log_item_t *lip, - int gen) +xlog_recover_check_ail( + xfs_mount_t *mp, + xfs_log_item_t *lip, + int gen) { - int orig_gen; + int orig_gen = gen; - orig_gen = gen; do { ASSERT(lip->li_type != XFS_LI_EFI); lip = xfs_trans_next_ail(mp, lip, &gen, NULL); @@ -2930,7 +3025,6 @@ } #endif /* DEBUG */ - /* * When this is called, all of the EFIs which did not have * corresponding EFDs should be in the AIL. What we do now @@ -2950,7 +3044,8 @@ * we see something other than an EFI in the AIL. */ STATIC void -xlog_recover_process_efis(xlog_t *log) +xlog_recover_process_efis( + xlog_t *log) { xfs_log_item_t *lip; xfs_efi_log_item_t *efip; @@ -2986,8 +3081,7 @@ lip = xfs_trans_next_ail(mp, lip, &gen, NULL); } AIL_UNLOCK(mp, s); -} /* xlog_recover_process_efis */ - +} /* * This routine performs a transaction to null out a bad inode pointer @@ -3030,8 +3124,7 @@ (offset + sizeof(xfs_agino_t) - 1)); (void) xfs_trans_commit(tp, 0, NULL); -} /* xlog_recover_clear_agi_bucket */ - +} /* * xlog_iunlink_recover @@ -3046,7 +3139,8 @@ * atomic. */ void -xlog_recover_process_iunlinks(xlog_t *log) +xlog_recover_process_iunlinks( + xlog_t *log) { xfs_mount_t *mp; xfs_agnumber_t agno; @@ -3188,40 +3282,47 @@ } mp->m_dmevmask = mp_dmevmask; +} -} /* xlog_recover_process_iunlinks */ - - -/* - * Stamp cycle number in every block - * - * This routine is also called in xfs_log.c - */ -/*ARGSUSED*/ -void -xlog_pack_data(xlog_t *log, xlog_in_core_t *iclog) -{ - int i, j, k; - int size = iclog->ic_offset + iclog->ic_roundoff; - xfs_caddr_t dp; - union ich { - xlog_rec_ext_header_t hic_xheader; - char hic_sector[XLOG_HEADER_SIZE]; - } *xhdr; - uint cycle_lsn; #ifdef DEBUG - uint *up; - uint chksum = 0; +STATIC void +xlog_pack_data_checksum( + xlog_t *log, + xlog_in_core_t *iclog, + int size) +{ + int i; + uint *up; + uint chksum = 0; up = (uint *)iclog->ic_datap; /* divide length by 4 to get # words */ - for (i=0; i> 2; i++) { + for (i = 0; i < (size >> 2); i++) { chksum ^= INT_GET(*up, ARCH_CONVERT); up++; } INT_SET(iclog->ic_header.h_chksum, ARCH_CONVERT, chksum); -#endif /* DEBUG */ +} +#else +#define xlog_pack_data_checksum(log, iclog, size) +#endif + +/* + * Stamp cycle number in every block + */ +void +xlog_pack_data( + xlog_t *log, + xlog_in_core_t *iclog) +{ + int i, j, k; + int size = iclog->ic_offset + iclog->ic_roundoff; + uint cycle_lsn; + xfs_caddr_t dp; + xlog_in_core_2_t *xhdr; + + xlog_pack_data_checksum(log, iclog, size); cycle_lsn = CYCLE_LSN_NOCONV(iclog->ic_header.h_lsn, ARCH_CONVERT); @@ -3234,7 +3335,7 @@ } if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { - xhdr = (union ich*)&iclog->ic_header; + xhdr = (xlog_in_core_2_t *)&iclog->ic_header; for ( ; i < BTOBB(size); i++) { j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE); k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE); @@ -3247,45 +3348,18 @@ xhdr[i].hic_xheader.xh_cycle = cycle_lsn; } } - -} /* xlog_pack_data */ - - -/*ARGSUSED*/ -STATIC void -xlog_unpack_data(xlog_rec_header_t *rhead, - xfs_caddr_t dp, - xlog_t *log) -{ - int i, j, k; - union ich { - xlog_rec_header_t hic_header; - xlog_rec_ext_header_t hic_xheader; - char hic_sector[XLOG_HEADER_SIZE]; - } *xhdr; +} #if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) - uint *up = (uint *)dp; - uint chksum = 0; -#endif - - for (i=0; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)) && - i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) { - *(uint *)dp = *(uint *)&rhead->h_cycle_data[i]; - dp += BBSIZE; - } - - if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { - xhdr = (union ich*)rhead; - for ( ; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); i++) { - j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE); - k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE); - *(uint *)dp = xhdr[j].hic_xheader.xh_cycle_data[k]; - dp += BBSIZE; - } - } +STATIC void +xlog_unpack_data_checksum( + xlog_rec_header_t *rhead, + xfs_caddr_t dp, + xlog_t *log) +{ + uint *up = (uint *)dp; + uint chksum = 0; -#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) /* divide length by 4 to get # words */ for (i=0; i < INT_GET(rhead->h_len, ARCH_CONVERT) >> 2; i++) { chksum ^= INT_GET(*up, ARCH_CONVERT); @@ -3306,9 +3380,38 @@ log->l_flags |= XLOG_CHKSUM_MISMATCH; } } -#endif /* DEBUG && XFS_LOUD_RECOVERY */ -} /* xlog_unpack_data */ +} +#else +#define xlog_unpack_data_checksum(rhead, dp, log) +#endif + +STATIC void +xlog_unpack_data( + xlog_rec_header_t *rhead, + xfs_caddr_t dp, + xlog_t *log) +{ + int i, j, k; + xlog_in_core_2_t *xhdr; + + for (i = 0; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)) && + i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) { + *(uint *)dp = *(uint *)&rhead->h_cycle_data[i]; + dp += BBSIZE; + } + + if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { + xhdr = (xlog_in_core_2_t *)rhead; + for ( ; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); i++) { + j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE); + k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE); + *(uint *)dp = xhdr[j].hic_xheader.xh_cycle_data[k]; + dp += BBSIZE; + } + } + xlog_unpack_data_checksum(rhead, dp, log); +} /* * Read the log from tail to head and process the log records found. @@ -3319,223 +3422,294 @@ * here. */ STATIC int -xlog_do_recovery_pass(xlog_t *log, - xfs_daddr_t head_blk, - xfs_daddr_t tail_blk, - int pass) -{ - xlog_rec_header_t *rhead; - xfs_daddr_t blk_no; - xfs_caddr_t bufaddr; - xfs_buf_t *hbp, *dbp; - int error, h_size; - int bblks, split_bblks; - int hblks, split_hblks, wrapped_hblks; - xlog_recover_t *rhash[XLOG_RHASH_SIZE]; - - error = 0; - - - /* - * Read the header of the tail block and get the iclog buffer size from - * h_size. Use this to tell how many sectors make up the log header. - */ - if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { - /* - * When using variable length iclogs, read first sector of iclog - * header and extract the header size from it. Get a new hbp that - * is the correct size. +xlog_do_recovery_pass( + xlog_t *log, + xfs_daddr_t head_blk, + xfs_daddr_t tail_blk, + int pass) +{ + xlog_rec_header_t *rhead; + xfs_daddr_t blk_no; + xfs_caddr_t bufaddr, offset; + xfs_buf_t *hbp, *dbp; + int error = 0, h_size; + int bblks, split_bblks; + int hblks, split_hblks, wrapped_hblks; + xlog_recover_t *rhash[XLOG_RHASH_SIZE]; + + /* + * Read the header of the tail block and get the iclog buffer size from + * h_size. Use this to tell how many sectors make up the log header. */ - hbp = xlog_get_bp(1, log->l_mp); - if (!hbp) - return ENOMEM; - if ((error = xlog_bread(log, tail_blk, 1, hbp))) - goto bread_err1; - rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp); - ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == + if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { + /* + * When using variable length iclogs, read first sector of + * iclog header and extract the header size from it. Get a + * new hbp that is the correct size. + */ + hbp = xlog_get_bp(log, 1); + if (!hbp) + return ENOMEM; + if ((error = xlog_bread(log, tail_blk, 1, hbp))) + goto bread_err1; + offset = xlog_align(log, tail_blk, 1, hbp); + rhead = (xlog_rec_header_t *)offset; + ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - if ((INT_GET(rhead->h_version, ARCH_CONVERT) & (~XLOG_VERSION_OKBITS)) != 0) { - xlog_warn("XFS: xlog_do_recovery_pass: unrecognised log version number."); - error = XFS_ERROR(EIO); - goto bread_err1; - } - h_size = INT_GET(rhead->h_size, ARCH_CONVERT); + if ((INT_GET(rhead->h_version, ARCH_CONVERT) & + (~XLOG_VERSION_OKBITS)) != 0) { + xlog_warn( + "XFS: xlog_do_recovery_pass: unrecognised log version number."); + error = XFS_ERROR(EIO); + goto bread_err1; + } + h_size = INT_GET(rhead->h_size, ARCH_CONVERT); - if ((INT_GET(rhead->h_version, ARCH_CONVERT) & XLOG_VERSION_2) && - (h_size > XLOG_HEADER_CYCLE_SIZE)) { - hblks = h_size / XLOG_HEADER_CYCLE_SIZE; - if (h_size % XLOG_HEADER_CYCLE_SIZE) - hblks++; - xlog_put_bp(hbp); - hbp = xlog_get_bp(hblks, log->l_mp); + if ((INT_GET(rhead->h_version, ARCH_CONVERT) + & XLOG_VERSION_2) && + (h_size > XLOG_HEADER_CYCLE_SIZE)) { + hblks = h_size / XLOG_HEADER_CYCLE_SIZE; + if (h_size % XLOG_HEADER_CYCLE_SIZE) + hblks++; + xlog_put_bp(hbp); + hbp = xlog_get_bp(log, hblks); + } else { + hblks = 1; + } } else { - hblks=1; + ASSERT(log->l_sectbb_log == 0); + hblks = 1; + hbp = xlog_get_bp(log, 1); + h_size = XLOG_BIG_RECORD_BSIZE; } - } else { - hblks=1; - hbp = xlog_get_bp(1, log->l_mp); - h_size = XLOG_BIG_RECORD_BSIZE; - } - - if (!hbp) - return ENOMEM; - dbp = xlog_get_bp(BTOBB(h_size),log->l_mp); - if (!dbp) { - xlog_put_bp(hbp); - return ENOMEM; - } - - memset(rhash, 0, sizeof(rhash)); - if (tail_blk <= head_blk) { - for (blk_no = tail_blk; blk_no < head_blk; ) { - if ((error = xlog_bread(log, blk_no, hblks, hbp))) - goto bread_err2; - rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp); - ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= INT_MAX)); - bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); /* blocks in data section */ - - if (unlikely((INT_GET(rhead->h_magicno, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM) || - (BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) > INT_MAX)) || - (bblks <= 0) || - (blk_no > log->l_logBBsize))) { - XFS_ERROR_REPORT("xlog_do_recovery_pass(1)", - XFS_ERRLEVEL_LOW, log->l_mp); - error = EFSCORRUPTED; - goto bread_err2; - } - if ((INT_GET(rhead->h_version, ARCH_CONVERT) & (~XLOG_VERSION_OKBITS)) != 0) { - xlog_warn("XFS: xlog_do_recovery_pass: unrecognised log version number."); - error = XFS_ERROR(EIO); - goto bread_err2; - } - bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); /* blocks in data section */ - if (bblks > 0) { - if ((error = xlog_bread(log, blk_no+hblks, bblks, dbp))) - goto bread_err2; - xlog_unpack_data(rhead, XFS_BUF_PTR(dbp), log); - if ((error = xlog_recover_process_data(log, rhash, - rhead, XFS_BUF_PTR(dbp), - pass))) - goto bread_err2; - } - blk_no += (bblks+hblks); + if (!hbp) + return ENOMEM; + dbp = xlog_get_bp(log, BTOBB(h_size)); + if (!dbp) { + xlog_put_bp(hbp); + return ENOMEM; } - } else { - /* - * Perform recovery around the end of the physical log. When the head - * is not on the same cycle number as the tail, we can't do a sequential - * recovery as above. - */ - blk_no = tail_blk; - while (blk_no < log->l_logBBsize) { - /* - * Check for header wrapping around physical end-of-log - */ - wrapped_hblks = 0; - if (blk_no+hblks <= log->l_logBBsize) { - /* Read header in one read */ - if ((error = xlog_bread(log, blk_no, hblks, hbp))) - goto bread_err2; - } else { - /* This log record is split across physical end of log */ - split_hblks = 0; - if (blk_no != log->l_logBBsize) { - /* some data is before physical end of log */ - ASSERT(blk_no <= INT_MAX); - split_hblks = log->l_logBBsize - (int)blk_no; - ASSERT(split_hblks > 0); - if ((error = xlog_bread(log, blk_no, split_hblks, hbp))) - goto bread_err2; - } - bufaddr = XFS_BUF_PTR(hbp); - XFS_BUF_SET_PTR(hbp, bufaddr + BBTOB(split_hblks), - BBTOB(hblks - split_hblks)); - wrapped_hblks = hblks - split_hblks; - if ((error = xlog_bread(log, 0, wrapped_hblks, hbp))) - goto bread_err2; - XFS_BUF_SET_PTR(hbp, bufaddr, hblks); - } - rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp); - ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= INT_MAX)); - bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); - - /* LR body must have data or it wouldn't have been written */ - ASSERT(bblks > 0); - blk_no += hblks; /* successfully read header */ - - if (unlikely((INT_GET(rhead->h_magicno, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM) || - (BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) > INT_MAX)) || - (bblks <= 0))) { - XFS_ERROR_REPORT("xlog_do_recovery_pass(2)", - XFS_ERRLEVEL_LOW, log->l_mp); - error = EFSCORRUPTED; - goto bread_err2; - } - /* Read in data for log record */ - if (blk_no+bblks <= log->l_logBBsize) { - if ((error = xlog_bread(log, blk_no, bblks, dbp))) - goto bread_err2; - } else { - /* This log record is split across physical end of log */ - split_bblks = 0; - if (blk_no != log->l_logBBsize) { - - /* some data is before physical end of log */ - ASSERT(blk_no <= INT_MAX); - split_bblks = log->l_logBBsize - (int)blk_no; - ASSERT(split_bblks > 0); - if ((error = xlog_bread(log, blk_no, split_bblks, dbp))) - goto bread_err2; - } - bufaddr = XFS_BUF_PTR(dbp); - XFS_BUF_SET_PTR(dbp, bufaddr + BBTOB(split_bblks), - BBTOB(bblks - split_bblks)); - if ((error = xlog_bread(log, wrapped_hblks, - bblks - split_bblks, dbp))) - goto bread_err2; - XFS_BUF_SET_PTR(dbp, bufaddr, XLOG_BIG_RECORD_BSIZE); - } - xlog_unpack_data(rhead, XFS_BUF_PTR(dbp), log); - if ((error = xlog_recover_process_data(log, rhash, - rhead, XFS_BUF_PTR(dbp), - pass))) - goto bread_err2; - blk_no += bblks; - } - - ASSERT(blk_no >= log->l_logBBsize); - blk_no -= log->l_logBBsize; - - /* read first part of physical log */ - while (blk_no < head_blk) { - if ((error = xlog_bread(log, blk_no, hblks, hbp))) - goto bread_err2; - rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp); - ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); - ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= INT_MAX)); - bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); - ASSERT(bblks > 0); - if ((error = xlog_bread(log, blk_no+hblks, bblks, dbp))) - goto bread_err2; - xlog_unpack_data(rhead, XFS_BUF_PTR(dbp), log); - if ((error = xlog_recover_process_data(log, rhash, - rhead, XFS_BUF_PTR(dbp), - pass))) - goto bread_err2; - blk_no += (bblks+hblks); - } - } - -bread_err2: - xlog_put_bp(dbp); -bread_err1: - xlog_put_bp(hbp); + memset(rhash, 0, sizeof(rhash)); + if (tail_blk <= head_blk) { + for (blk_no = tail_blk; blk_no < head_blk; ) { + if ((error = xlog_bread(log, blk_no, hblks, hbp))) + goto bread_err2; + offset = xlog_align(log, blk_no, hblks, hbp); + rhead = (xlog_rec_header_t *)offset; + ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == + XLOG_HEADER_MAGIC_NUM); + ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= + INT_MAX)); + /* blocks in data section */ + bblks = (int)BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); + + if (unlikely( + (INT_GET(rhead->h_magicno, ARCH_CONVERT) != + XLOG_HEADER_MAGIC_NUM) || + (BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) > + INT_MAX)) || + (bblks <= 0) || + (blk_no > log->l_logBBsize))) { + XFS_ERROR_REPORT("xlog_do_recovery_pass(1)", + XFS_ERRLEVEL_LOW, log->l_mp); + error = EFSCORRUPTED; + goto bread_err2; + } + + if ((INT_GET(rhead->h_version, ARCH_CONVERT) & + (~XLOG_VERSION_OKBITS)) != 0) { + xlog_warn( + "XFS: xlog_do_recovery_pass: unrecognised log version number."); + error = XFS_ERROR(EIO); + goto bread_err2; + } + /* blocks in data section */ + bblks = (int)BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); + if (bblks > 0) { + if ((error = xlog_bread(log, blk_no + hblks, + bblks, dbp))) + goto bread_err2; + offset = xlog_align(log, blk_no + hblks, + bblks, dbp); + xlog_unpack_data(rhead, offset, log); + if ((error = xlog_recover_process_data(log, + rhash, rhead, offset, pass))) + goto bread_err2; + } + blk_no += (bblks+hblks); + } + } else { + /* + * Perform recovery around the end of the physical log. + * When the head is not on the same cycle number as the tail, + * we can't do a sequential recovery as above. + */ + blk_no = tail_blk; + while (blk_no < log->l_logBBsize) { + /* + * Check for header wrapping around physical end-of-log + */ + wrapped_hblks = 0; + if (blk_no+hblks <= log->l_logBBsize) { + /* Read header in one read */ + if ((error = xlog_bread(log, blk_no, + hblks, hbp))) + goto bread_err2; + offset = xlog_align(log, blk_no, hblks, hbp); + } else { + /* This LR is split across physical log end */ + offset = NULL; + split_hblks = 0; + if (blk_no != log->l_logBBsize) { + /* some data before physical log end */ + ASSERT(blk_no <= INT_MAX); + split_hblks = log->l_logBBsize - (int)blk_no; + ASSERT(split_hblks > 0); + if ((error = xlog_bread(log, blk_no, + split_hblks, hbp))) + goto bread_err2; + offset = xlog_align(log, blk_no, + split_hblks, hbp); + } + /* + * Note: this black magic still works with + * large sector sizes (non-512) only because: + * - we increased the buffer size originally + * by 1 sector giving us enough extra space + * for the second read; + * - the log start is guaranteed to be sector + * aligned; + * - we read the log end (LR header start) + * _first_, then the log start (LR header end) + * - order is important. + */ + bufaddr = XFS_BUF_PTR(hbp); + XFS_BUF_SET_PTR(hbp, + bufaddr + BBTOB(split_hblks), + BBTOB(hblks - split_hblks)); + wrapped_hblks = hblks - split_hblks; + if ((error = xlog_bread(log, 0, + wrapped_hblks, hbp))) + goto bread_err2; + XFS_BUF_SET_PTR(hbp, bufaddr, hblks); + if (!offset) + offset = xlog_align(log, 0, + wrapped_hblks, hbp); + } + rhead = (xlog_rec_header_t *)offset; + ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == + XLOG_HEADER_MAGIC_NUM); + ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= + INT_MAX)); + bblks = (int)BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); + + /* LR body must have data or it wouldn't have been + * written */ + ASSERT(bblks > 0); + blk_no += hblks; /* successfully read header */ + + if (unlikely( + (INT_GET(rhead->h_magicno, ARCH_CONVERT) != + XLOG_HEADER_MAGIC_NUM) || + (BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) > + INT_MAX)) || + (bblks <= 0))) { + XFS_ERROR_REPORT("xlog_do_recovery_pass(2)", + XFS_ERRLEVEL_LOW, log->l_mp); + error = EFSCORRUPTED; + goto bread_err2; + } + + /* Read in data for log record */ + if (blk_no+bblks <= log->l_logBBsize) { + if ((error = xlog_bread(log, blk_no, + bblks, dbp))) + goto bread_err2; + offset = xlog_align(log, blk_no, bblks, dbp); + } else { + /* This log record is split across the + * physical end of log */ + offset = NULL; + split_bblks = 0; + if (blk_no != log->l_logBBsize) { + /* some data is before the physical + * end of log */ + ASSERT(!wrapped_hblks); + ASSERT(blk_no <= INT_MAX); + split_bblks = + log->l_logBBsize - (int)blk_no; + ASSERT(split_bblks > 0); + if ((error = xlog_bread(log, blk_no, + split_bblks, dbp))) + goto bread_err2; + offset = xlog_align(log, blk_no, + split_bblks, dbp); + } + /* + * Note: this black magic still works with + * large sector sizes (non-512) only because: + * - we increased the buffer size originally + * by 1 sector giving us enough extra space + * for the second read; + * - the log start is guaranteed to be sector + * aligned; + * - we read the log end (LR header start) + * _first_, then the log start (LR header end) + * - order is important. + */ + bufaddr = XFS_BUF_PTR(dbp); + XFS_BUF_SET_PTR(dbp, + bufaddr + BBTOB(split_bblks), + BBTOB(bblks - split_bblks)); + if ((error = xlog_bread(log, wrapped_hblks, + bblks - split_bblks, dbp))) + goto bread_err2; + XFS_BUF_SET_PTR(dbp, bufaddr, + XLOG_BIG_RECORD_BSIZE); + if (!offset) + offset = xlog_align(log, wrapped_hblks, + bblks - split_bblks, dbp); + } + xlog_unpack_data(rhead, offset, log); + if ((error = xlog_recover_process_data(log, rhash, + rhead, offset, pass))) + goto bread_err2; + blk_no += bblks; + } + + ASSERT(blk_no >= log->l_logBBsize); + blk_no -= log->l_logBBsize; + + /* read first part of physical log */ + while (blk_no < head_blk) { + if ((error = xlog_bread(log, blk_no, hblks, hbp))) + goto bread_err2; + offset = xlog_align(log, blk_no, hblks, hbp); + rhead = (xlog_rec_header_t *)offset; + ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == + XLOG_HEADER_MAGIC_NUM); + ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= + INT_MAX)); + bblks = (int)BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); + ASSERT(bblks > 0); + if ((error = xlog_bread(log, blk_no+hblks, bblks, dbp))) + goto bread_err2; + offset = xlog_align(log, blk_no+hblks, bblks, dbp); + xlog_unpack_data(rhead, offset, log); + if ((error = xlog_recover_process_data(log, rhash, + rhead, offset, pass))) + goto bread_err2; + blk_no += (bblks+hblks); + } + } - return error; + bread_err2: + xlog_put_bp(dbp); + bread_err1: + xlog_put_bp(hbp); + return error; } /* @@ -3552,9 +3726,10 @@ * the log recovery has been completed. */ STATIC int -xlog_do_log_recovery(xlog_t *log, - xfs_daddr_t head_blk, - xfs_daddr_t tail_blk) +xlog_do_log_recovery( + xlog_t *log, + xfs_daddr_t head_blk, + xfs_daddr_t tail_blk) { int error; #ifdef DEBUG @@ -3599,9 +3774,10 @@ * Do the actual recovery */ STATIC int -xlog_do_recover(xlog_t *log, - xfs_daddr_t head_blk, - xfs_daddr_t tail_blk) +xlog_do_recover( + xlog_t *log, + xfs_daddr_t head_blk, + xfs_daddr_t tail_blk) { int error; xfs_buf_t *bp; @@ -3663,7 +3839,7 @@ /* Normal transactions can now occur */ log->l_flags &= ~XLOG_ACTIVE_RECOVERY; return 0; -} /* xlog_do_recover */ +} /* * Perform recovery and re-initialize some log variables in xlog_find_tail. @@ -3671,22 +3847,18 @@ * Return error or zero. */ int -xlog_recover(xlog_t *log, int readonly) +xlog_recover( + xlog_t *log, + int readonly) { - xfs_daddr_t head_blk, tail_blk; - int error; + xfs_daddr_t head_blk, tail_blk; + int error; /* find the tail of the log */ - if ((error = xlog_find_tail(log, &head_blk, &tail_blk, readonly))) return error; if (tail_blk != head_blk) { -#ifndef __KERNEL__ - extern xfs_daddr_t HEAD_BLK, TAIL_BLK; - head_blk = HEAD_BLK; - tail_blk = TAIL_BLK; -#endif /* There used to be a comment here: * * disallow recovery on read-only mounts. note -- mount @@ -3698,36 +3870,21 @@ * under the vfs layer, so we can get away with it unless * the device itself is read-only, in which case we fail. */ -#ifdef __KERNEL__ if ((error = xfs_dev_is_read_only(log->l_mp, "recovery required"))) { return error; } -#else - if (readonly) { - return ENOSPC; - } -#endif -#ifdef __KERNEL__ -#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) cmn_err(CE_NOTE, "Starting XFS recovery on filesystem: %s (dev: %d/%d)", log->l_mp->m_fsname, MAJOR(log->l_dev), MINOR(log->l_dev)); -#else - cmn_err(CE_NOTE, - "!Starting XFS recovery on filesystem: %s (dev: %d/%d)", - log->l_mp->m_fsname, MAJOR(log->l_dev), - MINOR(log->l_dev)); -#endif -#endif + error = xlog_do_recover(log, head_blk, tail_blk); log->l_flags |= XLOG_RECOVERY_NEEDED; } return error; -} /* xlog_recover */ - +} /* * In the first part of recovery we replay inodes and buffers and build @@ -3739,7 +3896,9 @@ * in the real-time portion of the file system. */ int -xlog_recover_finish(xlog_t *log, int mfsi_flags) +xlog_recover_finish( + xlog_t *log, + int mfsi_flags) { /* * Now we're ready to do the transactions needed for the @@ -3761,23 +3920,16 @@ (XFS_LOG_FORCE | XFS_LOG_SYNC)); if ( (mfsi_flags & XFS_MFSI_NOUNLINK) == 0 ) { - xlog_recover_process_iunlinks(log); } xlog_recover_check_summary(log); -#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) cmn_err(CE_NOTE, "Ending XFS recovery on filesystem: %s (dev: %d/%d)", log->l_mp->m_fsname, MAJOR(log->l_dev), MINOR(log->l_dev)); -#else - cmn_err(CE_NOTE, - "!Ending XFS recovery on filesystem: %s (dev: %d/%d)", - log->l_mp->m_fsname, MAJOR(log->l_dev), - MINOR(log->l_dev)); -#endif + log->l_flags &= ~XLOG_RECOVERY_NEEDED; } else { cmn_err(CE_DEBUG, @@ -3785,7 +3937,7 @@ log->l_mp->m_fsname); } return 0; -} /* xlog_recover_finish */ +} #if defined(DEBUG) @@ -3794,7 +3946,8 @@ * are consistent with the superblock counters. */ void -xlog_recover_check_summary(xlog_t *log) +xlog_recover_check_summary( + xlog_t *log) { xfs_mount_t *mp; xfs_agf_t *agfp; diff -urN linux-2.5.69-bk13/fs/xfs/xfs_mount.c linux-2.5.69-bk14/fs/xfs/xfs_mount.c --- linux-2.5.69-bk13/fs/xfs/xfs_mount.c 2003-05-04 16:53:09.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_mount.c 2003-05-20 04:46:47.000000000 -0700 @@ -467,7 +467,11 @@ bp = xfs_buf_read_flags(mp->m_ddev_targp, XFS_SB_DADDR, BTOBB(sector_size), extra_flags); - ASSERT(bp); + if (!bp || XFS_BUF_ISERROR(bp)) { + cmn_err(CE_WARN, "XFS: SB read failed"); + error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM; + goto fail; + } ASSERT(XFS_BUF_ISBUSY(bp)); ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); @@ -482,9 +486,7 @@ error = xfs_mount_validate_sb(mp, &(mp->m_sb)); if (error) { cmn_err(CE_WARN, "XFS: SB validate failed"); - XFS_BUF_UNMANAGE(bp); - xfs_buf_relse(bp); - return error; + goto fail; } /* @@ -494,9 +496,8 @@ cmn_err(CE_WARN, "XFS: device supports only %u byte sectors (not %u)", sector_size, mp->m_sb.sb_sectsize); - XFS_BUF_UNMANAGE(bp); - xfs_buf_relse(bp); - return XFS_ERROR(ENOSYS); + error = ENOSYS; + goto fail; } /* @@ -509,7 +510,11 @@ sector_size = mp->m_sb.sb_sectsize; bp = xfs_buf_read_flags(mp->m_ddev_targp, XFS_SB_DADDR, BTOBB(sector_size), extra_flags); - ASSERT(bp); + if (!bp || XFS_BUF_ISERROR(bp)) { + cmn_err(CE_WARN, "XFS: SB re-read failed"); + error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM; + goto fail; + } ASSERT(XFS_BUF_ISBUSY(bp)); ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); } @@ -518,6 +523,13 @@ xfs_buf_relse(bp); ASSERT(XFS_BUF_VALUSEMA(bp) > 0); return 0; + + fail: + if (bp) { + XFS_BUF_UNMANAGE(bp); + xfs_buf_relse(bp); + } + return error; } @@ -546,16 +558,7 @@ mp->m_blockmask = sbp->sb_blocksize - 1; mp->m_blockwsize = sbp->sb_blocksize >> XFS_WORDLOG; mp->m_blockwmask = mp->m_blockwsize - 1; - - - if (XFS_SB_VERSION_HASLOGV2(sbp)) { - if (sbp->sb_logsunit <= 1) { - mp->m_lstripemask = 1; - } else { - mp->m_lstripemask = - 1 << xfs_highbit32(sbp->sb_logsunit >> BBSHIFT); - } - } + INIT_LIST_HEAD(&mp->m_del_inodes); /* * Setup for attributes, in case they get created. @@ -601,7 +604,6 @@ sbp->sb_inopblock); mp->m_ialloc_blks = mp->m_ialloc_inos >> sbp->sb_inopblog; } - /* * xfs_mountfs * diff -urN linux-2.5.69-bk13/fs/xfs/xfs_mount.h linux-2.5.69-bk14/fs/xfs/xfs_mount.h --- linux-2.5.69-bk13/fs/xfs/xfs_mount.h 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_mount.h 2003-05-20 04:46:47.000000000 -0700 @@ -68,6 +68,7 @@ ((xfs_agblock_t)(XFS_BB_TO_FSBT(mp, d) % (mp)->m_sb.sb_agblocks)) #else struct cred; +struct log; struct vfs; struct vnode; struct xfs_mount_args; @@ -296,13 +297,14 @@ int m_ihsize; /* size of next field */ struct xfs_ihash *m_ihash; /* fs private inode hash table*/ struct xfs_inode *m_inodes; /* active inode list */ + struct list_head m_del_inodes; /* inodes to reclaim */ mutex_t m_ilock; /* inode list mutex */ uint m_ireclaims; /* count of calls to reclaim*/ uint m_readio_log; /* min read size log bytes */ uint m_readio_blocks; /* min read size blocks */ uint m_writeio_log; /* min write size log bytes */ uint m_writeio_blocks; /* min write size blocks */ - void *m_log; /* log specific stuff */ + struct log *m_log; /* log specific stuff */ int m_logbufs; /* number of log buffers */ int m_logbsize; /* size of each log buffer */ uint m_rsumlevels; /* rt summary levels */ @@ -357,7 +359,6 @@ #endif int m_dalign; /* stripe unit */ int m_swidth; /* stripe width */ - int m_lstripemask; /* log stripe mask */ int m_sinoalign; /* stripe unit inode alignmnt */ int m_attr_magicpct;/* 37% of the blocksize */ int m_dir_magicpct; /* 37% of the dir blocksize */ diff -urN linux-2.5.69-bk13/fs/xfs/xfs_vfsops.c linux-2.5.69-bk14/fs/xfs/xfs_vfsops.c --- linux-2.5.69-bk13/fs/xfs/xfs_vfsops.c 2003-05-04 16:53:07.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_vfsops.c 2003-05-20 04:46:47.000000000 -0700 @@ -620,7 +620,7 @@ if (*flags & MS_RDONLY) { pagebuf_delwri_flush(mp->m_ddev_targp, 0, NULL); - xfs_finish_reclaim_all(mp); + xfs_finish_reclaim_all(mp, 0); do { VFS_SYNC(vfsp, SYNC_ATTR|SYNC_WAIT, NULL, error); @@ -849,19 +849,14 @@ * xfs sync routine for internal use * * This routine supports all of the flags defined for the generic VFS_SYNC - * interface as explained above under xys_sync. In the interests of not + * interface as explained above under xfs_sync. In the interests of not * changing interfaces within the 6.5 family, additional internallly- * required functions are specified within a separate xflags parameter, * only available by calling this routine. * - * xflags: - * XFS_XSYNC_RELOC - Sync for relocation. Don't try to get behavior - * locks as this will cause you to hang. Not all - * combinations of flags are necessarily supported - * when this is specified. */ -int -xfs_syncsub( +STATIC int +xfs_sync_inodes( xfs_mount_t *mp, int flags, int xflags, @@ -877,12 +872,10 @@ uint64_t fflag; uint lock_flags; uint base_lock_flags; - uint log_flags; boolean_t mount_locked; boolean_t vnode_refed; int preempt; xfs_dinode_t *dip; - xfs_buf_log_item_t *bip; xfs_iptr_t *ipointer; #ifdef DEBUG boolean_t ipointer_in = B_FALSE; @@ -961,16 +954,6 @@ base_lock_flags |= XFS_IOLOCK_SHARED; } - /* - * Sync out the log. This ensures that the log is periodically - * flushed even if there is not enough activity to fill it up. - */ - if (flags & SYNC_WAIT) { - xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC); - } else { - xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE); - } - XFS_MOUNT_ILOCK(mp); ip = mp->m_inodes; @@ -1016,27 +999,23 @@ ip = ip->i_mnext; continue; } - if ((ip->i_update_core == 0) && - ((ip->i_itemp == NULL) || - !(ip->i_itemp->ili_format.ilf_fields & XFS_ILOG_ALL))) { - if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) { - ip = ip->i_mnext; - } else if ((xfs_ipincount(ip) == 0) && + if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) { + ip = ip->i_mnext; + } else if ((xfs_ipincount(ip) == 0) && xfs_iflock_nowait(ip)) { - IPOINTER_INSERT(ip, mp); + IPOINTER_INSERT(ip, mp); - xfs_finish_reclaim(ip, 1, - XFS_IFLUSH_DELWRI_ELSE_SYNC); + xfs_finish_reclaim(ip, 1, + XFS_IFLUSH_DELWRI_ELSE_ASYNC); - XFS_MOUNT_ILOCK(mp); - mount_locked = B_TRUE; - IPOINTER_REMOVE(ip, mp); - } else { - xfs_iunlock(ip, XFS_ILOCK_EXCL); - ip = ip->i_mnext; - } - continue; + XFS_MOUNT_ILOCK(mp); + mount_locked = B_TRUE; + IPOINTER_REMOVE(ip, mp); + } else { + xfs_iunlock(ip, XFS_ILOCK_EXCL); + ip = ip->i_mnext; } + continue; } if (XFS_FORCED_SHUTDOWN(mp) && !(flags & SYNC_CLOSE)) { @@ -1148,21 +1127,9 @@ xfs_iunlock(ip, XFS_ILOCK_SHARED); if (XFS_FORCED_SHUTDOWN(mp)) { - if (xflags & XFS_XSYNC_RELOC) { - fs_tosspages(XFS_ITOBHV(ip), 0, -1, - FI_REMAPF); - } - else { - VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF); - } + VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF); } else { - if (xflags & XFS_XSYNC_RELOC) { - fs_flushinval_pages(XFS_ITOBHV(ip), - 0, -1, FI_REMAPF); - } - else { - VOP_FLUSHINVAL_PAGES(vp, 0, -1, FI_REMAPF); - } + VOP_FLUSHINVAL_PAGES(vp, 0, -1, FI_REMAPF); } xfs_ilock(ip, XFS_ILOCK_SHARED); @@ -1418,16 +1385,55 @@ ASSERT(ipointer_in == B_FALSE); + kmem_free(ipointer, sizeof(xfs_iptr_t)); + return XFS_ERROR(last_error); +} + +/* + * xfs sync routine for internal use + * + * This routine supports all of the flags defined for the generic VFS_SYNC + * interface as explained above under xfs_sync. In the interests of not + * changing interfaces within the 6.5 family, additional internallly- + * required functions are specified within a separate xflags parameter, + * only available by calling this routine. + * + */ +int +xfs_syncsub( + xfs_mount_t *mp, + int flags, + int xflags, + int *bypassed) +{ + int error = 0; + int last_error = 0; + uint log_flags = XFS_LOG_FORCE; + xfs_buf_t *bp; + xfs_buf_log_item_t *bip; + + /* + * Sync out the log. This ensures that the log is periodically + * flushed even if there is not enough activity to fill it up. + */ + if (flags & SYNC_WAIT) + log_flags |= XFS_LOG_SYNC; + + xfs_log_force(mp, (xfs_lsn_t)0, log_flags); + + if (flags & (SYNC_ATTR|SYNC_DELWRI)) { + if (flags & SYNC_BDFLUSH) + xfs_finish_reclaim_all(mp, 1); + else + error = xfs_sync_inodes(mp, flags, xflags, bypassed); + } + /* * Flushing out dirty data above probably generated more * log activity, so if this isn't vfs_sync() then flush - * the log again. If SYNC_WAIT is set then do it synchronously. + * the log again. */ - if (!(flags & SYNC_BDFLUSH)) { - log_flags = XFS_LOG_FORCE; - if (flags & SYNC_WAIT) { - log_flags |= XFS_LOG_SYNC; - } + if (flags & SYNC_DELWRI) { xfs_log_force(mp, (xfs_lsn_t)0, log_flags); } @@ -1463,11 +1469,10 @@ * that point so it can become pinned in between * there and here. */ - if (XFS_BUF_ISPINNED(bp)) { - xfs_log_force(mp, (xfs_lsn_t)0, - XFS_LOG_FORCE); - } - XFS_BUF_BFLAGS(bp) |= fflag; + if (XFS_BUF_ISPINNED(bp)) + xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE); + if (!(flags & SYNC_WAIT)) + XFS_BUF_BFLAGS(bp) |= XFS_B_ASYNC; error = xfs_bwrite(mp, bp); } if (error) { @@ -1478,9 +1483,9 @@ /* * Now check to see if the log needs a "dummy" transaction. */ - if (xfs_log_need_covered(mp)) { xfs_trans_t *tp; + xfs_inode_t *ip; /* * Put a dummy transaction in the log to tell @@ -1491,7 +1496,6 @@ XFS_ICHANGE_LOG_RES(mp), 0, 0, 0))) { xfs_trans_cancel(tp, 0); - kmem_free(ipointer, sizeof(xfs_iptr_t)); return error; } @@ -1503,6 +1507,7 @@ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); error = xfs_trans_commit(tp, 0, NULL); xfs_iunlock(ip, XFS_ILOCK_EXCL); + xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE); } /* @@ -1516,7 +1521,6 @@ } } - kmem_free(ipointer, sizeof(xfs_iptr_t)); return XFS_ERROR(last_error); } diff -urN linux-2.5.69-bk13/fs/xfs/xfs_vnodeops.c linux-2.5.69-bk14/fs/xfs/xfs_vnodeops.c --- linux-2.5.69-bk13/fs/xfs/xfs_vnodeops.c 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfs_vnodeops.c 2003-05-20 04:46:47.000000000 -0700 @@ -658,7 +658,7 @@ if (vap->va_size > ip->i_d.di_size) { code = xfs_igrow_start(ip, vap->va_size, credp); xfs_iunlock(ip, XFS_ILOCK_EXCL); - } else if (vap->va_size < ip->i_d.di_size) { + } else if (vap->va_size <= ip->i_d.di_size) { xfs_iunlock(ip, XFS_ILOCK_EXCL); xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, (xfs_fsize_t)vap->va_size); @@ -701,7 +701,7 @@ if (vap->va_size > ip->i_d.di_size) { xfs_igrow_finish(tp, ip, vap->va_size, !(flags & ATTR_DMI)); - } else if ((vap->va_size < ip->i_d.di_size) || + } else if ((vap->va_size <= ip->i_d.di_size) || ((vap->va_size == 0) && ip->i_d.di_nextents)) { /* * signal a sync transaction unless @@ -3786,27 +3786,30 @@ flush_flags = XFS_IFLUSH_SYNC; else #endif - flush_flags = XFS_IFLUSH_DELWRI; + flush_flags = XFS_IFLUSH_DELWRI_ELSE_ASYNC; xfs_ifunlock(ip); xfs_iunlock(ip, XFS_ILOCK_SHARED); error = xfs_itobp(mp, NULL, ip, &dip, &bp, 0); if (error) - goto eagain; + return error; xfs_buf_relse(bp); if (xfs_ilock_nowait(ip, XFS_ILOCK_SHARED) == 0) - goto eagain; + return EAGAIN; + + if (xfs_ipincount(ip) || + !xfs_iflock_nowait(ip)) { + xfs_iunlock(ip, XFS_ILOCK_SHARED); + return EAGAIN; + } - if ((xfs_ipincount(ip) == 0) && - xfs_iflock_nowait(ip)) - error = xfs_iflush(ip, flush_flags); + error = xfs_iflush(ip, flush_flags); } else { error = EAGAIN; } xfs_iunlock(ip, XFS_ILOCK_SHARED); } else { -eagain: error = EAGAIN; } } @@ -3934,6 +3937,8 @@ /* Protect sync from us */ XFS_MOUNT_ILOCK(mp); vn_bhv_remove(VN_BHV_HEAD(vp), XFS_ITOBHV(ip)); + list_add_tail(&ip->i_reclaim, &mp->m_del_inodes); + XFS_MOUNT_IUNLOCK(mp); } return 0; @@ -4010,40 +4015,33 @@ } int -xfs_finish_reclaim_all(xfs_mount_t *mp) +xfs_finish_reclaim_all(xfs_mount_t *mp, int noblock) { int purged; + struct list_head *curr, *next; xfs_inode_t *ip; - vnode_t *vp; int done = 0; while (!done) { purged = 0; XFS_MOUNT_ILOCK(mp); - ip = mp->m_inodes; - if (ip == NULL) { + list_for_each_safe(curr, next, &mp->m_del_inodes) { + ip = list_entry(curr, xfs_inode_t, i_reclaim); + if (noblock) { + if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) + continue; + if (xfs_ipincount(ip) || + !xfs_iflock_nowait(ip)) { + xfs_iunlock(ip, XFS_ILOCK_EXCL); + continue; + } + } + XFS_MOUNT_IUNLOCK(mp); + xfs_finish_reclaim(ip, noblock, + XFS_IFLUSH_DELWRI_ELSE_ASYNC); + purged = 1; break; } - do { - /* Make sure we skip markers inserted by sync */ - if (ip->i_mount == NULL) { - ip = ip->i_mnext; - continue; - } - - /* - * It's up to our caller to purge the root - * and quota vnodes later. - */ - vp = XFS_ITOV_NULL(ip); - - if (!vp) { - XFS_MOUNT_IUNLOCK(mp); - xfs_finish_reclaim(ip, 0, XFS_IFLUSH_ASYNC); - purged = 1; - break; - } - } while (ip != mp->m_inodes); done = !purged; } diff -urN linux-2.5.69-bk13/fs/xfs/xfsidbg.c linux-2.5.69-bk14/fs/xfs/xfsidbg.c --- linux-2.5.69-bk13/fs/xfs/xfsidbg.c 2003-05-04 16:53:37.000000000 -0700 +++ linux-2.5.69-bk14/fs/xfs/xfsidbg.c 2003-05-20 04:46:47.000000000 -0700 @@ -4269,8 +4269,10 @@ kdb_printf("iclog_bak: 0x%p iclog_size: 0x%x (%d) num iclogs: %d\n", log->l_iclog_bak, log->l_iclog_size, log->l_iclog_size, log->l_iclog_bufs); - kdb_printf("l_iclog_hsize %d l_iclog_heads %d\n", - log->l_iclog_hsize, log->l_iclog_heads); + kdb_printf("l_stripemask %d l_iclog_hsize %d l_iclog_heads %d\n", + log->l_stripemask, log->l_iclog_hsize, log->l_iclog_heads); + kdb_printf("l_sectbb_log %u l_sectbb_mask %u\n", + log->l_sectbb_log, log->l_sectbb_mask); kdb_printf("&grant_lock: 0x%p resHeadQ: 0x%p wrHeadQ: 0x%p\n", &log->l_grant_lock, log->l_reserve_headq, log->l_write_headq); kdb_printf("GResCycle: %d GResBytes: %d GWrCycle: %d GWrBytes: %d\n", @@ -4712,7 +4714,6 @@ (xfs_dfiloff_t)mp->m_dirfreeblk); kdb_printf("chsize %d chash 0x%p\n", mp->m_chsize, mp->m_chash); - kdb_printf("m_lstripemask %d\n", mp->m_lstripemask); kdb_printf("m_frozen %d m_active_trans %d\n", mp->m_frozen, mp->m_active_trans.counter); if (mp->m_fsname != NULL) diff -urN linux-2.5.69-bk13/include/asm-arm/mach/dma.h linux-2.5.69-bk14/include/asm-arm/mach/dma.h --- linux-2.5.69-bk13/include/asm-arm/mach/dma.h 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-arm/mach/dma.h 2003-05-20 04:46:47.000000000 -0700 @@ -41,6 +41,7 @@ unsigned int dma_base; /* Controller base address */ int dma_irq; /* Controller IRQ */ struct scatterlist cur_sg; /* Current controller buffer */ + unsigned int state; struct dma_ops *d_ops; }; diff -urN linux-2.5.69-bk13/include/asm-i386/genapic.h linux-2.5.69-bk14/include/asm-i386/genapic.h --- linux-2.5.69-bk13/include/asm-i386/genapic.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-i386/genapic.h 2003-05-20 04:46:47.000000000 -0700 @@ -59,6 +59,7 @@ unsigned (*get_apic_id)(unsigned long x); unsigned long apic_id_mask; + unsigned int (*cpu_mask_to_apicid)(unsigned long cpumask); /* ipi */ void (*send_IPI_mask)(int mask, int vector); @@ -95,6 +96,7 @@ APICFUNC(mps_oem_check), \ APICFUNC(get_apic_id), \ .apic_id_mask = APIC_ID_MASK, \ + APICFUNC(cpu_mask_to_apicid), \ APICFUNC(acpi_madt_oem_check), \ APICFUNC(send_IPI_mask), \ APICFUNC(send_IPI_allbutself), \ diff -urN linux-2.5.69-bk13/include/asm-i386/mach-bigsmp/mach_apic.h linux-2.5.69-bk14/include/asm-i386/mach-bigsmp/mach_apic.h --- linux-2.5.69-bk13/include/asm-i386/mach-bigsmp/mach_apic.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-i386/mach-bigsmp/mach_apic.h 2003-05-20 04:46:47.000000000 -0700 @@ -40,10 +40,18 @@ #define apicid_cluster(apicid) (apicid & 0xF0) +static inline unsigned get_apic_id(unsigned long x) +{ + return (((x)>>24)&0x0F); +} + +#define GET_APIC_ID(x) get_apic_id(x) + static inline unsigned long calculate_ldr(unsigned long old) { unsigned long id; - id = xapic_phys_to_log_apicid(hard_smp_processor_id()); + id = xapic_phys_to_log_apicid( + GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR))); return ((old & ~APIC_LDR_MASK) | SET_APIC_LOGICAL_ID(id)); } @@ -129,13 +137,6 @@ #define APIC_ID_MASK (0x0F<<24) -static inline unsigned get_apic_id(unsigned long x) -{ - return (((x)>>24)&0x0F); -} - -#define GET_APIC_ID(x) get_apic_id(x) - static inline unsigned int cpu_mask_to_apicid (unsigned long cpumask) { int num_bits_set; diff -urN linux-2.5.69-bk13/include/asm-i386/mach-generic/mach_apic.h linux-2.5.69-bk14/include/asm-i386/mach-generic/mach_apic.h --- linux-2.5.69-bk13/include/asm-i386/mach-generic/mach_apic.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-i386/mach-generic/mach_apic.h 2003-05-20 04:46:47.000000000 -0700 @@ -26,5 +26,6 @@ #define check_apicid_used (genapic->check_apicid_used) #define GET_APIC_ID (genapic->get_apic_id) #define APIC_ID_MASK (genapic->apic_id_mask) +#define cpu_mask_to_apicid (genapic->cpu_mask_to_apicid) #endif /* __ASM_MACH_APIC_H */ diff -urN linux-2.5.69-bk13/include/asm-i386/mach-summit/mach_apic.h linux-2.5.69-bk14/include/asm-i386/mach-summit/mach_apic.h --- linux-2.5.69-bk13/include/asm-i386/mach-summit/mach_apic.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-i386/mach-summit/mach_apic.h 2003-05-20 04:46:47.000000000 -0700 @@ -48,12 +48,20 @@ extern u8 bios_cpu_apicid[]; +static inline unsigned get_apic_id(unsigned long x) +{ + return (((x)>>24)&0xFF); +} + +#define GET_APIC_ID(x) get_apic_id(x) + static inline void init_apic_ldr(void) { unsigned long val, id; if (x86_summit) - id = xapic_phys_to_log_apicid(hard_smp_processor_id()); + id = xapic_phys_to_log_apicid( + GET_APIC_ID(*(unsigned long *)(APIC_BASE+APIC_ID))); else id = 1UL << smp_processor_id(); apic_write_around(APIC_DFR, APIC_DFR_VALUE); @@ -137,13 +145,6 @@ #define APIC_ID_MASK (0xFF<<24) -static inline unsigned get_apic_id(unsigned long x) -{ - return (((x)>>24)&0xFF); -} - -#define GET_APIC_ID(x) get_apic_id(x) - static inline unsigned int cpu_mask_to_apicid (unsigned long cpumask) { int num_bits_set; diff -urN linux-2.5.69-bk13/include/asm-i386/mmu_context.h linux-2.5.69-bk14/include/asm-i386/mmu_context.h --- linux-2.5.69-bk13/include/asm-i386/mmu_context.h 2003-05-04 16:52:48.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-i386/mmu_context.h 2003-05-20 04:46:47.000000000 -0700 @@ -45,8 +45,8 @@ #ifdef CONFIG_SMP else { cpu_tlbstate[cpu].state = TLBSTATE_OK; - if (cpu_tlbstate[cpu].active_mm != next) - BUG(); + BUG_ON(cpu_tlbstate[cpu].active_mm != next); + if (!test_and_set_bit(cpu, &next->cpu_vm_mask)) { /* We were in lazy tlb mode and leave_mm disabled * tlb flush IPI delivery. We must reload %cr3. diff -urN linux-2.5.69-bk13/include/asm-i386/spinlock.h linux-2.5.69-bk14/include/asm-i386/spinlock.h --- linux-2.5.69-bk13/include/asm-i386/spinlock.h 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-i386/spinlock.h 2003-05-20 04:46:47.000000000 -0700 @@ -5,6 +5,7 @@ #include #include #include +#include extern int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); @@ -70,10 +71,8 @@ static inline void _raw_spin_unlock(spinlock_t *lock) { #ifdef CONFIG_DEBUG_SPINLOCK - if (lock->magic != SPINLOCK_MAGIC) - BUG(); - if (!spin_is_locked(lock)) - BUG(); + BUG_ON(lock->magic != SPINLOCK_MAGIC); + BUG_ON(!spin_is_locked(lock)); #endif __asm__ __volatile__( spin_unlock_string @@ -91,10 +90,8 @@ { char oldval = 1; #ifdef CONFIG_DEBUG_SPINLOCK - if (lock->magic != SPINLOCK_MAGIC) - BUG(); - if (!spin_is_locked(lock)) - BUG(); + BUG_ON(lock->magic != SPINLOCK_MAGIC); + BUG_ON(!spin_is_locked(lock)); #endif __asm__ __volatile__( spin_unlock_string @@ -118,8 +115,8 @@ #ifdef CONFIG_DEBUG_SPINLOCK __label__ here; here: - if (lock->magic != SPINLOCK_MAGIC) { -printk("eip: %p\n", &&here); + if (unlikely(lock->magic != SPINLOCK_MAGIC)) { + printk("eip: %p\n", &&here); BUG(); } #endif @@ -174,8 +171,7 @@ static inline void _raw_read_lock(rwlock_t *rw) { #ifdef CONFIG_DEBUG_SPINLOCK - if (rw->magic != RWLOCK_MAGIC) - BUG(); + BUG_ON(rw->magic != RWLOCK_MAGIC); #endif __build_read_lock(rw, "__read_lock_failed"); } @@ -183,8 +179,7 @@ static inline void _raw_write_lock(rwlock_t *rw) { #ifdef CONFIG_DEBUG_SPINLOCK - if (rw->magic != RWLOCK_MAGIC) - BUG(); + BUG_ON(rw->magic != RWLOCK_MAGIC); #endif __build_write_lock(rw, "__write_lock_failed"); } diff -urN linux-2.5.69-bk13/include/asm-i386/types.h linux-2.5.69-bk14/include/asm-i386/types.h --- linux-2.5.69-bk13/include/asm-i386/types.h 2003-05-04 16:53:32.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-i386/types.h 2003-05-20 04:46:47.000000000 -0700 @@ -51,7 +51,7 @@ /* DMA addresses come in generic and 64-bit flavours. */ -#ifdef CONFIG_HIGHMEM +#ifdef CONFIG_HIGHMEM64G typedef u64 dma_addr_t; #else typedef u32 dma_addr_t; diff -urN linux-2.5.69-bk13/include/asm-ppc/agp.h linux-2.5.69-bk14/include/asm-ppc/agp.h --- linux-2.5.69-bk13/include/asm-ppc/agp.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.69-bk14/include/asm-ppc/agp.h 2003-05-20 04:46:47.000000000 -0700 @@ -0,0 +1,13 @@ +#ifndef AGP_H +#define AGP_H 1 + +#include + +/* nothing much needed here */ + +#define map_page_into_agp(page) +#define unmap_page_from_agp(page) +#define flush_agp_mappings() +#define flush_agp_cache() mb() + +#endif diff -urN linux-2.5.69-bk13/include/asm-sparc/bitops.h linux-2.5.69-bk14/include/asm-sparc/bitops.h --- linux-2.5.69-bk13/include/asm-sparc/bitops.h 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-sparc/bitops.h 2003-05-20 04:46:47.000000000 -0700 @@ -20,7 +20,7 @@ * within the first byte. Sparc is BIG-Endian. Unless noted otherwise * all bit-ops return 0 if bit was previously clear and != 0 otherwise. */ -static __inline__ int test_and_set_bit(unsigned long nr, volatile void *addr) +static __inline__ int test_and_set_bit(unsigned long nr, volatile unsigned long *addr) { register unsigned long mask asm("g2"); register unsigned long *ADDR asm("g1"); @@ -39,7 +39,7 @@ return mask != 0; } -static __inline__ void set_bit(unsigned long nr, volatile void *addr) +static __inline__ void set_bit(unsigned long nr, volatile unsigned long *addr) { register unsigned long mask asm("g2"); register unsigned long *ADDR asm("g1"); @@ -56,7 +56,7 @@ : "g3", "g4", "g5", "g7", "cc"); } -static __inline__ int test_and_clear_bit(unsigned long nr, volatile void *addr) +static __inline__ int test_and_clear_bit(unsigned long nr, volatile unsigned long *addr) { register unsigned long mask asm("g2"); register unsigned long *ADDR asm("g1"); @@ -75,7 +75,7 @@ return mask != 0; } -static __inline__ void clear_bit(unsigned long nr, volatile void *addr) +static __inline__ void clear_bit(unsigned long nr, volatile unsigned long *addr) { register unsigned long mask asm("g2"); register unsigned long *ADDR asm("g1"); @@ -92,7 +92,7 @@ : "g3", "g4", "g5", "g7", "cc"); } -static __inline__ int test_and_change_bit(unsigned long nr, volatile void *addr) +static __inline__ int test_and_change_bit(unsigned long nr, volatile unsigned long *addr) { register unsigned long mask asm("g2"); register unsigned long *ADDR asm("g1"); @@ -111,7 +111,7 @@ return mask != 0; } -static __inline__ void change_bit(unsigned long nr, volatile void *addr) +static __inline__ void change_bit(unsigned long nr, volatile unsigned long *addr) { register unsigned long mask asm("g2"); register unsigned long *ADDR asm("g1"); @@ -131,7 +131,7 @@ /* * non-atomic versions */ -static __inline__ void __set_bit(int nr, volatile void *addr) +static __inline__ void __set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1UL << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -139,7 +139,7 @@ *p |= mask; } -static __inline__ void __clear_bit(int nr, volatile void *addr) +static __inline__ void __clear_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1UL << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -147,7 +147,7 @@ *p &= ~mask; } -static __inline__ void __change_bit(int nr, volatile void *addr) +static __inline__ void __change_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1UL << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -155,7 +155,7 @@ *p ^= mask; } -static __inline__ int __test_and_set_bit(int nr, volatile void *addr) +static __inline__ int __test_and_set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1UL << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -165,7 +165,7 @@ return (old & mask) != 0; } -static __inline__ int __test_and_clear_bit(int nr, volatile void *addr) +static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1UL << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -175,7 +175,7 @@ return (old & mask) != 0; } -static __inline__ int __test_and_change_bit(int nr, volatile void *addr) +static __inline__ int __test_and_change_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1UL << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -189,9 +189,9 @@ #define smp_mb__after_clear_bit() do { } while(0) /* The following routine need not be atomic. */ -static __inline__ int test_bit(int nr, __const__ void *addr) +static __inline__ int test_bit(int nr, __const__ volatile unsigned long *addr) { - return (1 & (((__const__ unsigned int *) addr)[nr >> 5] >> (nr & 31))) != 0; + return (1UL & (((unsigned long *)addr)[nr >> 5] >> (nr & 31))) != 0UL; } /* The easy/cheese version for now. */ @@ -288,9 +288,10 @@ * 'size' bits, starting the search at bit 'offset'. This is largely based * on Linus's ALPHA routines, which are pretty portable BTW. */ -static __inline__ unsigned long find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) +static __inline__ unsigned long find_next_zero_bit(unsigned long *addr, + unsigned long size, unsigned long offset) { - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); + unsigned long *p = addr + (offset >> 5); unsigned long result = offset & ~31UL; unsigned long tmp; @@ -361,7 +362,7 @@ /* */ -static __inline__ int test_le_bit(int nr, __const__ void * addr) +static __inline__ int test_le_bit(int nr, __const__ unsigned long * addr) { __const__ unsigned char *ADDR = (__const__ unsigned char *) addr; return (ADDR[nr >> 3] >> (nr & 7)) & 1; @@ -370,7 +371,7 @@ /* * non-atomic versions */ -static __inline__ void __set_le_bit(int nr, void *addr) +static __inline__ void __set_le_bit(int nr, unsigned long *addr) { unsigned char *ADDR = (unsigned char *)addr; @@ -378,7 +379,7 @@ *ADDR |= 1 << (nr & 0x07); } -static __inline__ void __clear_le_bit(int nr, void *addr) +static __inline__ void __clear_le_bit(int nr, unsigned long *addr) { unsigned char *ADDR = (unsigned char *)addr; @@ -386,7 +387,7 @@ *ADDR &= ~(1 << (nr & 0x07)); } -static __inline__ int __test_and_set_le_bit(int nr, void *addr) +static __inline__ int __test_and_set_le_bit(int nr, unsigned long *addr) { int mask, retval; unsigned char *ADDR = (unsigned char *)addr; @@ -398,7 +399,7 @@ return retval; } -static __inline__ int __test_and_clear_le_bit(int nr, void *addr) +static __inline__ int __test_and_clear_le_bit(int nr, unsigned long *addr) { int mask, retval; unsigned char *ADDR = (unsigned char *)addr; @@ -410,9 +411,10 @@ return retval; } -static __inline__ unsigned long find_next_zero_le_bit(void *addr, unsigned long size, unsigned long offset) +static __inline__ unsigned long find_next_zero_le_bit(unsigned long *addr, + unsigned long size, unsigned long offset) { - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); + unsigned long *p = addr + (offset >> 5); unsigned long result = offset & ~31UL; unsigned long tmp; diff -urN linux-2.5.69-bk13/include/asm-sparc/bug.h linux-2.5.69-bk14/include/asm-sparc/bug.h --- linux-2.5.69-bk13/include/asm-sparc/bug.h 2003-05-04 16:53:57.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-sparc/bug.h 2003-05-20 04:46:47.000000000 -0700 @@ -2,24 +2,18 @@ #ifndef _SPARC_BUG_H #define _SPARC_BUG_H -/* - * XXX I am hitting compiler bugs with __builtin_trap. This has - * hit me before and rusty was blaming his netfilter bugs on - * this so lets disable it. - Anton - */ -#if 0 -/* We need the mb()'s so we don't trigger a compiler bug - Anton */ -#define BUG() do { \ - mb(); \ - __builtin_trap(); \ - mb(); \ -} while(0) -#else -#define BUG() do { \ - printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; \ +#ifdef CONFIG_DEBUG_BUGVERBOSE +extern void do_BUG(const char *file, int line); +#define BUG() do { \ + do_BUG(__FILE__, __LINE__); \ + __builtin_trap(); \ } while (0) +#else +#define BUG() __builtin_trap() #endif -#define PAGE_BUG(page) BUG() +#define PAGE_BUG(page) do { \ + BUG(); \ +} while (0) #endif diff -urN linux-2.5.69-bk13/include/asm-sparc/io-unit.h linux-2.5.69-bk14/include/asm-sparc/io-unit.h --- linux-2.5.69-bk13/include/asm-sparc/io-unit.h 2003-05-04 16:53:13.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-sparc/io-unit.h 2003-05-20 04:46:47.000000000 -0700 @@ -41,7 +41,7 @@ #define IOUPTE_PARITY 0x00000001 /* Parity is checked during DVMA */ struct iounit_struct { - unsigned int bmap[(IOUNIT_DMA_SIZE >> (PAGE_SHIFT + 3)) / sizeof(unsigned int)]; + unsigned long bmap[(IOUNIT_DMA_SIZE >> (PAGE_SHIFT + 3)) / sizeof(unsigned long)]; spinlock_t lock; iopte_t *page_table; unsigned long rotor[3]; diff -urN linux-2.5.69-bk13/include/asm-sparc/smp.h linux-2.5.69-bk14/include/asm-sparc/smp.h --- linux-2.5.69-bk13/include/asm-sparc/smp.h 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/include/asm-sparc/smp.h 2003-05-20 04:46:47.000000000 -0700 @@ -169,9 +169,6 @@ #endif #define smp_processor_id() hard_smp_processor_id() -/* XXX We really need to implement this now. -DaveM */ -extern __inline__ void smp_send_reschedule(int cpu) { } -extern __inline__ void smp_send_stop(void) { } #endif /* !(__ASSEMBLY__) */ diff -urN linux-2.5.69-bk13/include/linux/fs.h linux-2.5.69-bk14/include/linux/fs.h --- linux-2.5.69-bk13/include/linux/fs.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/fs.h 2003-05-20 04:46:48.000000000 -0700 @@ -313,7 +313,7 @@ struct address_space { struct inode *host; /* owner: inode, block_device */ struct radix_tree_root page_tree; /* radix tree of all pages */ - spinlock_t page_lock; /* and rwlock protecting it */ + spinlock_t page_lock; /* and spinlock protecting it */ struct list_head clean_pages; /* list of clean pages */ struct list_head dirty_pages; /* list of dirty pages */ struct list_head locked_pages; /* list of locked pages */ diff -urN linux-2.5.69-bk13/include/linux/i2c-id.h linux-2.5.69-bk14/include/linux/i2c-id.h --- linux-2.5.69-bk13/include/linux/i2c-id.h 2003-05-04 16:53:01.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/i2c-id.h 2003-05-20 04:46:48.000000000 -0700 @@ -245,7 +245,7 @@ #define I2C_HW_SMBUS_SIS5595 0x06 #define I2C_HW_SMBUS_ALI1535 0x07 #define I2C_HW_SMBUS_SIS630 0x08 -#define I2C_HW_SMBUS_SIS645 0x09 +#define I2C_HW_SMBUS_SIS96X 0x09 #define I2C_HW_SMBUS_AMD8111 0x0a #define I2C_HW_SMBUS_SCX200 0x0b #define I2C_HW_SMBUS_NFORCE2 0x0c diff -urN linux-2.5.69-bk13/include/linux/mm.h linux-2.5.69-bk14/include/linux/mm.h --- linux-2.5.69-bk13/include/linux/mm.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/mm.h 2003-05-20 04:46:48.000000000 -0700 @@ -218,7 +218,7 @@ */ #define put_page_testzero(p) \ ({ \ - BUG_ON(page_count(page) == 0); \ + BUG_ON(page_count(p) == 0); \ atomic_dec_and_test(&(p)->count); \ }) diff -urN linux-2.5.69-bk13/include/linux/netfilter_ipv4/ip_nat_core.h linux-2.5.69-bk14/include/linux/netfilter_ipv4/ip_nat_core.h --- linux-2.5.69-bk13/include/linux/netfilter_ipv4/ip_nat_core.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/netfilter_ipv4/ip_nat_core.h 2003-05-20 04:46:48.000000000 -0700 @@ -30,10 +30,4 @@ extern struct ip_nat_protocol ip_nat_protocol_tcp; extern struct ip_nat_protocol ip_nat_protocol_udp; extern struct ip_nat_protocol ip_nat_protocol_icmp; - -/* Call this before modifying an existing IP packet: ensures it is - modifiable and linear to the point you care about (writable_len). - Returns true or false. */ -extern int skb_ip_make_writable(struct sk_buff **pskb, - unsigned int writable_len); #endif /* _IP_NAT_CORE_H */ diff -urN linux-2.5.69-bk13/include/linux/netfilter_ipv4.h linux-2.5.69-bk14/include/linux/netfilter_ipv4.h --- linux-2.5.69-bk13/include/linux/netfilter_ipv4.h 2003-05-04 16:53:40.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/netfilter_ipv4.h 2003-05-20 04:46:48.000000000 -0700 @@ -75,6 +75,12 @@ #endif /*CONFIG_NETFILTER_DEBUG*/ extern int ip_route_me_harder(struct sk_buff **pskb); + +/* Call this before modifying an existing IP packet: ensures it is + modifiable and linear to the point you care about (writable_len). + Returns true or false. */ +extern int skb_ip_make_writable(struct sk_buff **pskb, + unsigned int writable_len); #endif /*__KERNEL__*/ #endif /*__LINUX_IP_NETFILTER_H*/ diff -urN linux-2.5.69-bk13/include/linux/pci_ids.h linux-2.5.69-bk14/include/linux/pci_ids.h --- linux-2.5.69-bk13/include/linux/pci_ids.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/pci_ids.h 2003-05-20 04:46:48.000000000 -0700 @@ -569,6 +569,9 @@ #define PCI_DEVICE_ID_SI_751 0x0751 #define PCI_DEVICE_ID_SI_752 0x0752 #define PCI_DEVICE_ID_SI_900 0x0900 +#define PCI_DEVICE_ID_SI_961 0x0961 +#define PCI_DEVICE_ID_SI_962 0x0962 +#define PCI_DEVICE_ID_SI_963 0x0963 #define PCI_DEVICE_ID_SI_5107 0x5107 #define PCI_DEVICE_ID_SI_5300 0x5300 #define PCI_DEVICE_ID_SI_5511 0x5511 @@ -1128,6 +1131,7 @@ #define PCI_DEVICE_ID_VIA_8754 0x3168 #define PCI_DEVICE_ID_VIA_8235 0x3177 #define PCI_DEVICE_ID_VIA_P4N333 0x3178 +#define PCI_DEVICE_ID_VIA_K8T400M_0 0x3188 #define PCI_DEVICE_ID_VIA_8377_0 0x3189 #define PCI_DEVICE_ID_VIA_KM400 0x3205 #define PCI_DEVICE_ID_VIA_P4M400 0x3209 diff -urN linux-2.5.69-bk13/include/linux/reiserfs_fs.h linux-2.5.69-bk14/include/linux/reiserfs_fs.h --- linux-2.5.69-bk13/include/linux/reiserfs_fs.h 2003-05-04 16:53:41.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/reiserfs_fs.h 2003-05-20 04:46:48.000000000 -0700 @@ -1268,6 +1268,7 @@ /* Size of pointer to the unformatted node. */ #define UNFM_P_SIZE (sizeof(unp_t)) +#define UNFM_P_SHIFT 2 // in in-core inode key is stored on le form #define INODE_PKEY(inode) ((struct key *)(REISERFS_I(inode)->i_key)) @@ -1838,7 +1839,7 @@ void padd_item (char * item, int total_length, int length); /* inode.c */ - +void restart_transaction(struct reiserfs_transaction_handle *th, struct inode *inode, struct path *path); void reiserfs_read_locked_inode(struct inode * inode, struct reiserfs_iget_args *args) ; int reiserfs_find_actor(struct inode * inode, void *p) ; int reiserfs_init_locked_inode(struct inode * inode, void *p) ; @@ -2111,6 +2112,7 @@ #endif void reiserfs_claim_blocks_to_be_allocated( struct super_block *sb, int blocks); void reiserfs_release_claimed_blocks( struct super_block *sb, int blocks); +int reiserfs_can_fit_pages(struct super_block *sb); /* hashes.c */ __u32 keyed_hash (const signed char *msg, int len); diff -urN linux-2.5.69-bk13/include/linux/reiserfs_fs_sb.h linux-2.5.69-bk14/include/linux/reiserfs_fs_sb.h --- linux-2.5.69-bk13/include/linux/reiserfs_fs_sb.h 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/reiserfs_fs_sb.h 2003-05-20 04:46:48.000000000 -0700 @@ -397,6 +397,7 @@ reiserfs_proc_info_data_t s_proc_info_data; struct proc_dir_entry *procdir; int reserved_blocks; /* amount of blocks reserved for further allocations */ + spinlock_t bitmap_lock; /* this lock on now only used to protect reserved_blocks variable */ }; /* Definitions of reiserfs on-disk properties: */ diff -urN linux-2.5.69-bk13/include/linux/sched.h linux-2.5.69-bk14/include/linux/sched.h --- linux-2.5.69-bk13/include/linux/sched.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/sched.h 2003-05-20 04:46:48.000000000 -0700 @@ -530,6 +530,7 @@ extern int FASTCALL(wake_up_state(struct task_struct * tsk, unsigned int state)); extern int FASTCALL(wake_up_process(struct task_struct * tsk)); +extern int FASTCALL(wake_up_process_kick(struct task_struct * tsk)); extern void FASTCALL(wake_up_forked_process(struct task_struct * tsk)); extern void FASTCALL(sched_exit(task_t * p)); @@ -636,14 +637,14 @@ extern task_t *child_reaper; extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *); -extern struct task_struct *do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); +extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); +extern struct task_struct * copy_process(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); #ifdef CONFIG_SMP extern void wait_task_inactive(task_t * p); #else #define wait_task_inactive(p) do { } while (0) #endif -extern void kick_if_running(task_t * p); #define remove_parent(p) list_del_init(&(p)->sibling) #define add_parent(p, parent) list_add_tail(&(p)->sibling,&(parent)->children) diff -urN linux-2.5.69-bk13/include/linux/wait.h linux-2.5.69-bk14/include/linux/wait.h --- linux-2.5.69-bk13/include/linux/wait.h 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/include/linux/wait.h 2003-05-20 04:46:48.000000000 -0700 @@ -111,15 +111,12 @@ #define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1) #define wake_up_nr(x, nr) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr) #define wake_up_all(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0) +#define wake_up_all_sync(x) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0) #define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE, 1) #define wake_up_interruptible_nr(x, nr) __wake_up((x),TASK_INTERRUPTIBLE, nr) #define wake_up_interruptible_all(x) __wake_up((x),TASK_INTERRUPTIBLE, 0) #define wake_up_locked(x) __wake_up_locked((x), TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE) -#ifdef CONFIG_SMP #define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE, 1) -#else -#define wake_up_interruptible_sync(x) __wake_up((x),TASK_INTERRUPTIBLE, 1) -#endif #define __wait_event(wq, condition) \ do { \ diff -urN linux-2.5.69-bk13/include/media/audiochip.h linux-2.5.69-bk14/include/media/audiochip.h --- linux-2.5.69-bk13/include/media/audiochip.h 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/include/media/audiochip.h 2003-05-20 04:46:48.000000000 -0700 @@ -18,6 +18,15 @@ #define AUDIO_MUTE 0x80 #define AUDIO_UNMUTE 0x81 +/* all the stuff below is obsolete and just here for reference. I'll + * remove it once the driver is tested and works fine. + * + * Instead creating alot of tiny API's for all kinds of different + * chips, we'll just pass throuth the v4l ioctl structs (v4l2 not + * yet...). It is a bit less flexible, but most/all used i2c chips + * make sense in v4l context only. So I think that's acceptable... + */ + /* misc stuff to pass around config info to i2c chips */ #define AUDC_CONFIG_PINNACLE _IOW('m',32,int) diff -urN linux-2.5.69-bk13/include/media/tuner.h linux-2.5.69-bk14/include/media/tuner.h --- linux-2.5.69-bk13/include/media/tuner.h 2003-05-04 16:53:35.000000000 -0700 +++ linux-2.5.69-bk14/include/media/tuner.h 2003-05-20 04:46:48.000000000 -0700 @@ -64,7 +64,7 @@ #define TUNER_LG_PAL_NEW_TAPC 37 #define TUNER_PHILIPS_FM1216ME_MK3 38 #define TUNER_LG_NTSC_NEW_TAPC 39 - +#define TUNER_HITACHI_NTSC 40 @@ -83,6 +83,7 @@ #define SHARP 6 #define Samsung 7 #define Microtune 8 +#define HITACHI 9 #define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ #define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ diff -urN linux-2.5.69-bk13/include/media/video-buf.h linux-2.5.69-bk14/include/media/video-buf.h --- linux-2.5.69-bk13/include/media/video-buf.h 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/include/media/video-buf.h 2003-05-20 04:46:48.000000000 -0700 @@ -196,6 +196,7 @@ int videobuf_queue_is_busy(struct videobuf_queue *q); void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q); +enum v4l2_field videobuf_next_field(struct videobuf_queue *q); void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, enum v4l2_buf_type type); int videobuf_reqbufs(struct file *file, struct videobuf_queue *q, diff -urN linux-2.5.69-bk13/include/net/ip.h linux-2.5.69-bk14/include/net/ip.h --- linux-2.5.69-bk13/include/net/ip.h 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/include/net/ip.h 2003-05-20 04:46:48.000000000 -0700 @@ -298,4 +298,15 @@ extern int ip_seq_release(struct inode *inode, struct file *file); extern int ipv4_proc_init(void); +/* sysctl helpers - any sysctl which holds a value that ends up being + * fed into the routing cache should use these handlers. + */ +int ipv4_doint_and_flush(ctl_table *ctl, int write, + struct file* filp, void *buffer, + size_t *lenp); +int ipv4_doint_and_flush_strategy(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, + void **context); + #endif /* _IP_H */ diff -urN linux-2.5.69-bk13/kernel/fork.c linux-2.5.69-bk14/kernel/fork.c --- linux-2.5.69-bk13/kernel/fork.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/kernel/fork.c 2003-05-20 04:46:48.000000000 -0700 @@ -752,12 +752,12 @@ * parts of the process environment (as per the clone * flags). The actual kick-off is left to the caller. */ -static struct task_struct *copy_process(unsigned long clone_flags, - unsigned long stack_start, - struct pt_regs *regs, - unsigned long stack_size, - int __user *parent_tidptr, - int __user *child_tidptr) +struct task_struct *copy_process(unsigned long clone_flags, + unsigned long stack_start, + struct pt_regs *regs, + unsigned long stack_size, + int __user *parent_tidptr, + int __user *child_tidptr) { int retval; struct task_struct *p = NULL; @@ -1067,15 +1067,16 @@ * It copies the process, and if successful kick-starts * it and waits for it to finish using the VM if required. */ -struct task_struct *do_fork(unsigned long clone_flags, - unsigned long stack_start, - struct pt_regs *regs, - unsigned long stack_size, - int __user *parent_tidptr, - int __user *child_tidptr) +long do_fork(unsigned long clone_flags, + unsigned long stack_start, + struct pt_regs *regs, + unsigned long stack_size, + int __user *parent_tidptr, + int __user *child_tidptr) { struct task_struct *p; int trace = 0; + long pid; if (unlikely(current->ptrace)) { trace = fork_traceflag (clone_flags); @@ -1084,6 +1085,12 @@ } p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr); + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. + */ + pid = IS_ERR(p) ? PTR_ERR(p) : p->pid; + if (!IS_ERR(p)) { struct completion vfork; @@ -1104,7 +1111,7 @@ ++total_forks; if (unlikely (trace)) { - current->ptrace_message = (unsigned long) p->pid; + current->ptrace_message = pid; ptrace_notify ((trace << 8) | SIGTRAP); } @@ -1119,7 +1126,7 @@ */ set_need_resched(); } - return p; + return pid; } /* SLAB cache for signal_struct structures (tsk->signal) */ diff -urN linux-2.5.69-bk13/kernel/ksyms.c linux-2.5.69-bk14/kernel/ksyms.c --- linux-2.5.69-bk13/kernel/ksyms.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/kernel/ksyms.c 2003-05-20 04:46:48.000000000 -0700 @@ -223,6 +223,7 @@ EXPORT_SYMBOL(generic_cont_expand); EXPORT_SYMBOL(cont_prepare_write); EXPORT_SYMBOL(generic_commit_write); +EXPORT_SYMBOL(block_commit_write); EXPORT_SYMBOL(block_truncate_page); EXPORT_SYMBOL(generic_block_bmap); EXPORT_SYMBOL(generic_file_read); @@ -556,6 +557,8 @@ EXPORT_SYMBOL(make_bad_inode); EXPORT_SYMBOL(is_bad_inode); EXPORT_SYMBOL(__inode_dir_notify); +EXPORT_SYMBOL(generic_osync_inode); +EXPORT_SYMBOL(remove_suid); #ifdef CONFIG_UID16 EXPORT_SYMBOL(overflowuid); diff -urN linux-2.5.69-bk13/kernel/sched.c linux-2.5.69-bk14/kernel/sched.c --- linux-2.5.69-bk13/kernel/sched.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/kernel/sched.c 2003-05-20 04:46:48.000000000 -0700 @@ -340,10 +340,9 @@ * Update all the scheduling statistics stuff. (sleep average * calculation, priority modifiers, etc.) */ -static inline int activate_task(task_t *p, runqueue_t *rq) +static inline void activate_task(task_t *p, runqueue_t *rq) { long sleep_time = jiffies - p->last_run - 1; - int requeue_waker = 0; if (sleep_time > 0) { int sleep_avg; @@ -372,8 +371,6 @@ } } __activate_task(p, rq); - - return requeue_waker; } /* @@ -454,27 +451,12 @@ } #endif -/* - * kick_if_running - kick the remote CPU if the task is running currently. - * - * This code is used by the signal code to signal tasks - * which are in user-mode, as quickly as possible. - * - * (Note that we do this lockless - if the task does anything - * while the message is in flight then it will notice the - * sigpending condition anyway.) - */ -void kick_if_running(task_t * p) -{ - if ((task_running(task_rq(p), p)) && (task_cpu(p) != smp_processor_id())) - resched_task(p); -} - /*** * try_to_wake_up - wake up a thread * @p: the to-be-woken-up thread * @state: the mask of task states that can be woken * @sync: do a synchronous wakeup? + * @kick: kick the CPU if the task is already running? * * Put it on the run-queue if it's not already there. The "current" * thread is always on the run-queue (except when the actual @@ -484,10 +466,10 @@ * * returns failure only if the task is already active. */ -static int try_to_wake_up(task_t * p, unsigned int state, int sync) +static int try_to_wake_up(task_t * p, unsigned int state, int sync, int kick) { - int success = 0, requeue_waker = 0; unsigned long flags; + int success = 0; long old_state; runqueue_t *rq; @@ -513,42 +495,34 @@ if (sync) __activate_task(p, rq); else { - requeue_waker = activate_task(p, rq); + activate_task(p, rq); if (p->prio < rq->curr->prio) resched_task(rq->curr); } success = 1; - } + } else + if (unlikely(kick) && task_running(rq, p)) + resched_task(rq->curr); p->state = TASK_RUNNING; } task_rq_unlock(rq, &flags); - /* - * We have to do this outside the other spinlock, the two - * runqueues might be different: - */ - if (requeue_waker) { - prio_array_t *array; - - rq = task_rq_lock(current, &flags); - array = current->array; - dequeue_task(current, array); - current->prio = effective_prio(current); - enqueue_task(current, array); - task_rq_unlock(rq, &flags); - } - return success; } int wake_up_process(task_t * p) { - return try_to_wake_up(p, TASK_STOPPED | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0); + return try_to_wake_up(p, TASK_STOPPED | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0, 0); +} + +int wake_up_process_kick(task_t * p) +{ + return try_to_wake_up(p, TASK_STOPPED | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0, 1); } int wake_up_state(task_t *p, unsigned int state) { - return try_to_wake_up(p, state, 0); + return try_to_wake_up(p, state, 0, 0); } /* @@ -1206,7 +1180,7 @@ /* Task might have expired already, but not scheduled off yet */ if (p->array != rq->active) { set_tsk_need_resched(p); - return; + goto out; } spin_lock(&rq->lock); /* @@ -1233,7 +1207,7 @@ dequeue_task(p, rq->active); enqueue_task(p, rq->active); } - goto out; + goto out_unlock; } if (!--p->time_slice) { dequeue_task(p, rq->active); @@ -1249,8 +1223,9 @@ } else enqueue_task(p, rq->active); } -out: +out_unlock: spin_unlock(&rq->lock); +out: rebalance_tick(rq, 0); } @@ -1389,7 +1364,7 @@ int default_wake_function(wait_queue_t *curr, unsigned mode, int sync) { task_t *p = curr->task; - return try_to_wake_up(p, mode, sync); + return try_to_wake_up(p, mode, sync, 0); } /* @@ -1440,8 +1415,6 @@ __wake_up_common(q, mode, 1, 0); } -#ifdef CONFIG_SMP - /** * __wake_up - sync- wake up threads blocked on a waitqueue. * @q: the waitqueue @@ -1452,6 +1425,8 @@ * away soon, so while the target thread will be woken up, it will not * be migrated to another CPU - ie. the two threads are 'synchronized' * with each other. This can prevent needless bouncing between CPUs. + * + * On UP it can prevent extra preemption. */ void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) { @@ -1468,8 +1443,6 @@ spin_unlock_irqrestore(&q->lock, flags); } -#endif - void complete(struct completion *x) { unsigned long flags; diff -urN linux-2.5.69-bk13/kernel/signal.c linux-2.5.69-bk14/kernel/signal.c --- linux-2.5.69-bk13/kernel/signal.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/kernel/signal.c 2003-05-20 04:46:48.000000000 -0700 @@ -521,18 +521,6 @@ set_tsk_thread_flag(t,TIF_SIGPENDING); /* - * If the task is running on a different CPU - * force a reschedule on the other CPU to make - * it notice the new signal quickly. - * - * The code below is a tad loose and might occasionally - * kick the wrong CPU if we catch the process in the - * process of changing - but no harm is done by that - * other than doing an extra (lightweight) IPI interrupt. - */ - if (t->state == TASK_RUNNING) - kick_if_running(t); - /* * If resume is set, we want to wake it up in the TASK_STOPPED case. * We don't check for TASK_STOPPED because there is a race with it * executing another processor and just now entering stopped state. @@ -543,7 +531,7 @@ if (resume) mask |= TASK_STOPPED; if (t->state & mask) { - wake_up_process(t); + wake_up_process_kick(t); return; } } diff -urN linux-2.5.69-bk13/mm/memory.c linux-2.5.69-bk14/mm/memory.c --- linux-2.5.69-bk13/mm/memory.c 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/mm/memory.c 2003-05-20 04:46:48.000000000 -0700 @@ -1108,17 +1108,12 @@ if (inode->i_size < offset) goto do_expand; inode->i_size = offset; + pgoff = (offset + PAGE_SIZE - 1) >> PAGE_SHIFT; down(&mapping->i_shared_sem); - if (list_empty(&mapping->i_mmap) && list_empty(&mapping->i_mmap_shared)) - goto out_unlock; - - pgoff = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - if (!list_empty(&mapping->i_mmap)) + if (unlikely(!list_empty(&mapping->i_mmap))) vmtruncate_list(&mapping->i_mmap, pgoff); - if (!list_empty(&mapping->i_mmap_shared)) + if (unlikely(!list_empty(&mapping->i_mmap_shared))) vmtruncate_list(&mapping->i_mmap_shared, pgoff); - -out_unlock: up(&mapping->i_shared_sem); truncate_inode_pages(mapping, offset); goto out_truncate; diff -urN linux-2.5.69-bk13/mm/truncate.c linux-2.5.69-bk14/mm/truncate.c --- linux-2.5.69-bk13/mm/truncate.c 2003-05-04 16:53:42.000000000 -0700 +++ linux-2.5.69-bk14/mm/truncate.c 2003-05-20 04:46:48.000000000 -0700 @@ -173,15 +173,13 @@ } pagevec_release(&pvec); } - if (lstart == 0 && mapping->nrpages) - printk("%s: I goofed!\n", __FUNCTION__); } /** * invalidate_mapping_pages - Invalidate all the unlocked pages of one inode - * @inode: the address_space which holds the pages to invalidate - * @end: the index of the last page to invalidate (inclusive) - * @nr_pages: defines the pagecache span. Invalidate up to @start + @nr_pages + * @mapping: the address_space which holds the pages to invalidate + * @start: the offset 'from' which to invalidate + * @end: the offset 'to' which to invalidate (inclusive) * * This function only removes the unlocked pages, if you want to * remove all the pages of one inode, you must call truncate_inode_pages. diff -urN linux-2.5.69-bk13/net/core/netfilter.c linux-2.5.69-bk14/net/core/netfilter.c --- linux-2.5.69-bk13/net/core/netfilter.c 2003-05-04 16:53:14.000000000 -0700 +++ linux-2.5.69-bk14/net/core/netfilter.c 2003-05-20 04:46:49.000000000 -0700 @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -683,8 +686,69 @@ return err; } + +int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len) +{ + struct sk_buff *nskb; + unsigned int iplen; + + if (writable_len > (*pskb)->len) + return 0; + + /* Not exclusive use of packet? Must copy. */ + if (skb_shared(*pskb) || skb_cloned(*pskb)) + goto copy_skb; + + /* Alexey says IP hdr is always modifiable and linear, so ok. */ + if (writable_len <= (*pskb)->nh.iph->ihl*4) + return 1; + + iplen = writable_len - (*pskb)->nh.iph->ihl*4; + + /* DaveM says protocol headers are also modifiable. */ + switch ((*pskb)->nh.iph->protocol) { + case IPPROTO_TCP: { + struct tcphdr hdr; + if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, + &hdr, sizeof(hdr)) != 0) + goto copy_skb; + if (writable_len <= (*pskb)->nh.iph->ihl*4 + hdr.doff*4) + goto pull_skb; + goto copy_skb; + } + case IPPROTO_UDP: + if (writable_len<=(*pskb)->nh.iph->ihl*4+sizeof(struct udphdr)) + goto pull_skb; + goto copy_skb; + case IPPROTO_ICMP: + if (writable_len + <= (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr)) + goto pull_skb; + goto copy_skb; + /* Insert other cases here as desired */ + } + +copy_skb: + nskb = skb_copy(*pskb, GFP_ATOMIC); + if (!nskb) + return 0; + BUG_ON(skb_is_nonlinear(nskb)); + + /* Rest of kernel will get very unhappy if we pass it a + suddenly-orphaned skbuff */ + if ((*pskb)->sk) + skb_set_owner_w(nskb, (*pskb)->sk); + kfree_skb(*pskb); + *pskb = nskb; + return 1; + +pull_skb: + return pskb_may_pull(*pskb, writable_len); +} +EXPORT_SYMBOL(skb_ip_make_writable); #endif /*CONFIG_INET*/ + /* This does not belong here, but ipt_REJECT needs it if connection tracking in use: without this, connection may not be in hash table, and hence manufactured ICMP or RST packets will not be associated diff -urN linux-2.5.69-bk13/net/ipv4/devinet.c linux-2.5.69-bk14/net/ipv4/devinet.c --- linux-2.5.69-bk13/net/ipv4/devinet.c 2003-05-04 16:53:08.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv4/devinet.c 2003-05-20 04:46:49.000000000 -0700 @@ -1065,9 +1065,9 @@ return ret; } -static int ipv4_doint_and_flush(ctl_table *ctl, int write, - struct file* filp, void *buffer, - size_t *lenp) +int ipv4_doint_and_flush(ctl_table *ctl, int write, + struct file* filp, void *buffer, + size_t *lenp) { int *valp = ctl->data; int val = *valp; @@ -1079,10 +1079,10 @@ return ret; } -static int ipv4_doint_and_flush_strategy(ctl_table *table, int *name, int nlen, - void *oldval, size_t *oldlenp, - void *newval, size_t newlen, - void **context) +int ipv4_doint_and_flush_strategy(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, + void **context) { int *valp = table->data; int new; diff -urN linux-2.5.69-bk13/net/ipv4/ipcomp.c linux-2.5.69-bk14/net/ipv4/ipcomp.c --- linux-2.5.69-bk13/net/ipv4/ipcomp.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv4/ipcomp.c 2003-05-20 04:46:49.000000000 -0700 @@ -155,6 +155,7 @@ struct iphdr iph; char buf[60]; } tmp_iph; + int hdr_len = 0; if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) { err = -EINVAL; @@ -167,7 +168,11 @@ goto error; /* Don't bother compressing */ - if (skb->len < ipcd->threshold) { + if (!x->props.mode) { + iph = skb->nh.iph; + hdr_len = iph->ihl * 4; + } + if ((skb->len - hdr_len) < ipcd->threshold) { if (x->props.mode) { ipcomp_tunnel_encap(x, skb); iph = skb->nh.iph; diff -urN linux-2.5.69-bk13/net/ipv4/netfilter/ip_nat_core.c linux-2.5.69-bk14/net/ipv4/netfilter/ip_nat_core.c --- linux-2.5.69-bk13/net/ipv4/netfilter/ip_nat_core.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv4/netfilter/ip_nat_core.c 2003-05-20 04:46:49.000000000 -0700 @@ -969,65 +969,6 @@ return 0; } -int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len) -{ - struct sk_buff *nskb; - unsigned int iplen; - - if (writable_len > (*pskb)->len) - return 0; - - /* Not exclusive use of packet? Must copy. */ - if (skb_shared(*pskb) || skb_cloned(*pskb)) - goto copy_skb; - - /* Alexey says IP hdr is always modifiable and linear, so ok. */ - if (writable_len <= (*pskb)->nh.iph->ihl*4) - return 1; - - iplen = writable_len - (*pskb)->nh.iph->ihl*4; - - /* DaveM says protocol headers are also modifiable. */ - switch ((*pskb)->nh.iph->protocol) { - case IPPROTO_TCP: { - struct tcphdr hdr; - if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, - &hdr, sizeof(hdr)) != 0) - goto copy_skb; - if (writable_len <= (*pskb)->nh.iph->ihl*4 + hdr.doff*4) - goto pull_skb; - goto copy_skb; - } - case IPPROTO_UDP: - if (writable_len<=(*pskb)->nh.iph->ihl*4+sizeof(struct udphdr)) - goto pull_skb; - goto copy_skb; - case IPPROTO_ICMP: - if (writable_len - <= (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr)) - goto pull_skb; - goto copy_skb; - /* Insert other cases here as desired */ - } - -copy_skb: - nskb = skb_copy(*pskb, GFP_ATOMIC); - if (!nskb) - return 0; - BUG_ON(skb_is_nonlinear(nskb)); - - /* Rest of kernel will get very unhappy if we pass it a - suddenly-orphaned skbuff */ - if ((*pskb)->sk) - skb_set_owner_w(nskb, (*pskb)->sk); - kfree_skb(*pskb); - *pskb = nskb; - return 1; - -pull_skb: - return pskb_may_pull(*pskb, writable_len); -} - int __init ip_nat_init(void) { size_t i; diff -urN linux-2.5.69-bk13/net/ipv4/netfilter/ip_nat_standalone.c linux-2.5.69-bk14/net/ipv4/netfilter/ip_nat_standalone.c --- linux-2.5.69-bk13/net/ipv4/netfilter/ip_nat_standalone.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv4/netfilter/ip_nat_standalone.c 2003-05-20 04:46:49.000000000 -0700 @@ -380,5 +380,4 @@ EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); EXPORT_SYMBOL(ip_nat_mangle_udp_packet); EXPORT_SYMBOL(ip_nat_used_tuple); -EXPORT_SYMBOL(skb_ip_make_writable); MODULE_LICENSE("GPL"); diff -urN linux-2.5.69-bk13/net/ipv4/sysctl_net_ipv4.c linux-2.5.69-bk14/net/ipv4/sysctl_net_ipv4.c --- linux-2.5.69-bk13/net/ipv4/sysctl_net_ipv4.c 2003-05-04 16:53:36.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv4/sysctl_net_ipv4.c 2003-05-20 04:46:49.000000000 -0700 @@ -160,7 +160,8 @@ .data = &sysctl_ip_default_ttl, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec + .proc_handler = &ipv4_doint_and_flush, + .strategy = &ipv4_doint_and_flush_strategy, }, { .ctl_name = NET_IPV4_AUTOCONFIG, diff -urN linux-2.5.69-bk13/net/ipv6/addrconf.c linux-2.5.69-bk14/net/ipv6/addrconf.c --- linux-2.5.69-bk13/net/ipv6/addrconf.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv6/addrconf.c 2003-05-20 04:46:49.000000000 -0700 @@ -2389,22 +2389,14 @@ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFADDR, GFP_ATOMIC); } -static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] = -{ - { NULL, NULL, }, - { NULL, NULL, }, - { NULL, NULL, }, - { NULL, NULL, }, - - { inet6_rtm_newaddr, NULL, }, - { inet6_rtm_deladdr, NULL, }, - { NULL, inet6_dump_ifaddr, }, - { NULL, NULL, }, - - { inet6_rtm_newroute, NULL, }, - { inet6_rtm_delroute, NULL, }, - { inet6_rtm_getroute, inet6_dump_fib, }, - { NULL, NULL, }, +static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = { + [RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, }, + [RTM_DELADDR - RTM_BASE] = { .doit = inet6_rtm_deladdr, }, + [RTM_GETADDR - RTM_BASE] = { .dumpit = inet6_dump_ifaddr, }, + [RTM_NEWROUTE - RTM_BASE] = { .doit = inet6_rtm_newroute, }, + [RTM_DELROUTE - RTM_BASE] = { .doit = inet6_rtm_delroute, }, + [RTM_GETROUTE - RTM_BASE] = { .doit = inet6_rtm_getroute, + .dumpit = inet6_dump_fib, }, }; static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) diff -urN linux-2.5.69-bk13/net/ipv6/exthdrs.c linux-2.5.69-bk14/net/ipv6/exthdrs.c --- linux-2.5.69-bk13/net/ipv6/exthdrs.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv6/exthdrs.c 2003-05-20 04:46:49.000000000 -0700 @@ -54,10 +54,9 @@ * It MUST NOT touch skb->h. */ -struct tlvtype_proc -{ +struct tlvtype_proc { int type; - int (*func) (struct sk_buff *, int offset); + int (*func)(struct sk_buff *skb, int offset); }; /********************* @@ -175,8 +174,7 @@ return -1; } -static struct inet6_protocol destopt_protocol = -{ +static struct inet6_protocol destopt_protocol = { .handler = ipv6_destopt_rcv, .flags = INET6_PROTO_NOPOLICY, }; @@ -199,8 +197,7 @@ return 0; } -static struct inet6_protocol nodata_protocol = -{ +static struct inet6_protocol nodata_protocol = { .handler = ipv6_nodata_rcv, .flags = INET6_PROTO_NOPOLICY, }; @@ -328,8 +325,7 @@ return -1; } -static struct inet6_protocol rthdr_protocol = -{ +static struct inet6_protocol rthdr_protocol = { .handler = ipv6_rthdr_rcv, .flags = INET6_PROTO_NOPOLICY, }; @@ -462,9 +458,15 @@ } static struct tlvtype_proc tlvprochopopt_lst[] = { - {IPV6_TLV_ROUTERALERT, ipv6_hop_ra}, - {IPV6_TLV_JUMBO, ipv6_hop_jumbo}, - {-1, NULL} + { + .type = IPV6_TLV_ROUTERALERT, + .func = ipv6_hop_ra, + }, + { + .type = IPV6_TLV_JUMBO, + .func = ipv6_hop_jumbo, + }, + { -1, } }; int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff) diff -urN linux-2.5.69-bk13/net/ipv6/icmp.c linux-2.5.69-bk14/net/ipv6/icmp.c --- linux-2.5.69-bk13/net/ipv6/icmp.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv6/icmp.c 2003-05-20 04:46:49.000000000 -0700 @@ -737,11 +737,26 @@ int err; int fatal; } tab_unreach[] = { - { ENETUNREACH, 0}, /* NOROUTE */ - { EACCES, 1}, /* ADM_PROHIBITED */ - { EHOSTUNREACH, 0}, /* Was NOT_NEIGHBOUR, now reserved */ - { EHOSTUNREACH, 0}, /* ADDR_UNREACH */ - { ECONNREFUSED, 1}, /* PORT_UNREACH */ + { /* NOROUTE */ + .err = ENETUNREACH, + .fatal = 0, + }, + { /* ADM_PROHIBITED */ + .err = EACCES, + .fatal = 1, + }, + { /* Was NOT_NEIGHBOUR, now reserved */ + .err = EHOSTUNREACH, + .fatal = 0, + }, + { /* ADDR_UNREACH */ + .err = EHOSTUNREACH, + .fatal = 0, + }, + { /* PORT_UNREACH */ + .err = ECONNREFUSED, + .fatal = 1, + }, }; int icmpv6_err_convert(int type, int code, int *err) diff -urN linux-2.5.69-bk13/net/ipv6/ip6_fib.c linux-2.5.69-bk14/net/ipv6/ip6_fib.c --- linux-2.5.69-bk13/net/ipv6/ip6_fib.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv6/ip6_fib.c 2003-05-20 04:46:49.000000000 -0700 @@ -91,12 +91,13 @@ * result of redirects, path MTU changes, etc. */ -static __u32 rt_sernum = 0; +static __u32 rt_sernum; static struct timer_list ip6_fib_timer = TIMER_INITIALIZER(fib6_run_gc, 0, 0); static struct fib6_walker_t fib6_walker_list = { - &fib6_walker_list, &fib6_walker_list, + .prev = &fib6_walker_list, + .next = &fib6_walker_list, }; #define FOR_WALKERS(w) for ((w)=fib6_walker_list.next; (w) != &fib6_walker_list; (w)=(w)->next) diff -urN linux-2.5.69-bk13/net/ipv6/route.c linux-2.5.69-bk14/net/ipv6/route.c --- linux-2.5.69-bk13/net/ipv6/route.c 2003-05-20 04:46:37.000000000 -0700 +++ linux-2.5.69-bk14/net/ipv6/route.c 2003-05-20 04:46:49.000000000 -0700 @@ -108,7 +108,7 @@ .dev = &loopback_dev, .obsolete = -1, .error = -ENETUNREACH, - .metrics[RTAX_HOPLIMIT-1] = 255, + .metrics = { [RTAX_HOPLIMIT - 1] = 255, }, .input = ip6_pkt_discard, .output = ip6_pkt_discard, .ops = &ip6_dst_ops, @@ -121,9 +121,8 @@ }; struct fib6_node ip6_routing_table = { - NULL, NULL, NULL, NULL, - &ip6_null_entry, - 0, RTN_ROOT|RTN_TL_ROOT|RTN_RTINFO, 0 + .leaf = &ip6_null_entry, + .fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO, }; /* Protects all the ip6 fib */ @@ -174,7 +173,7 @@ /* * pointer to the last default router chosen. BH is disabled locally. */ -static struct rt6_info *rt6_dflt_pointer = NULL; +static struct rt6_info *rt6_dflt_pointer; static spinlock_t rt6_dflt_lock = SPIN_LOCK_UNLOCKED; /* Default Router Selection (RFC 2461 6.3.6) */ @@ -559,7 +558,7 @@ } } -static int ip6_dst_gc() +static int ip6_dst_gc(void) { static unsigned expire = 30*HZ; static unsigned long last_gc; diff -urN linux-2.5.69-bk13/net/netrom/nr_route.c linux-2.5.69-bk14/net/netrom/nr_route.c --- linux-2.5.69-bk13/net/netrom/nr_route.c 2003-05-04 16:53:56.000000000 -0700 +++ linux-2.5.69-bk14/net/netrom/nr_route.c 2003-05-20 04:46:49.000000000 -0700 @@ -110,7 +110,8 @@ kfree(nr_neigh); return -ENOMEM; } - memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); + memcpy(nr_neigh->digipeat, ax25_digi, + sizeof(*ax25_digi)); } spin_lock_bh(&nr_neigh_lock); @@ -387,7 +388,7 @@ kfree(nr_neigh); return -ENOMEM; } - memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); + memcpy(nr_neigh->digipeat, ax25_digi, sizeof(*ax25_digi)); } spin_lock_bh(&nr_neigh_lock); diff -urN linux-2.5.69-bk13/sound/oss/cs4281/cs4281m.c linux-2.5.69-bk14/sound/oss/cs4281/cs4281m.c --- linux-2.5.69-bk13/sound/oss/cs4281/cs4281m.c 2003-05-04 16:53:56.000000000 -0700 +++ linux-2.5.69-bk14/sound/oss/cs4281/cs4281m.c 2003-05-20 04:46:49.000000000 -0700 @@ -1944,8 +1944,8 @@ len -= x; } CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO - "cs4281: clear_advance(): memset %d at 0x%.8x for %d size \n", - (unsigned)c, (unsigned)((char *) buf) + bptr, len)); + "cs4281: clear_advance(): memset %d at %p for %d size \n", + (unsigned)c, ((char *) buf) + bptr, len)); memset(((char *) buf) + bptr, c, len); } @@ -1980,9 +1980,8 @@ wake_up(&s->dma_adc.wait); } CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", - (unsigned)s, s->dma_adc.hwptr, - s->dma_adc.total_bytes, s->dma_adc.count)); + "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", + s, s->dma_adc.hwptr, s->dma_adc.total_bytes, s->dma_adc.count)); } // update DAC pointer // @@ -2014,11 +2013,10 @@ // Continue to play silence until the _release. // CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): memset %d at 0x%.8x for %d size \n", + "cs4281: cs4281_update_ptr(): memset %d at %p for %d size \n", (unsigned)(s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - (unsigned)s->dma_dac.rawbuf, - s->dma_dac.dmasize)); + s->dma_dac.rawbuf, s->dma_dac.dmasize)); memset(s->dma_dac.rawbuf, (s->prop_dac. fmt & (AFMT_U8 | AFMT_U16_LE)) ? @@ -2049,9 +2047,8 @@ } } CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", - (unsigned) s, s->dma_dac.hwptr, - s->dma_dac.total_bytes, s->dma_dac.count)); + "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", + s, s->dma_dac.hwptr, s->dma_dac.total_bytes, s->dma_dac.count)); } } @@ -2182,8 +2179,7 @@ VALIDATE_STATE(s); CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4281: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n", - (unsigned) s, cmd)); + "cs4281: mixer_ioctl(): s=%p cmd=0x%.8x\n", s, cmd)); #if CSDEBUG cs_printioctl(cmd); #endif @@ -2748,9 +2744,8 @@ CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs4281: CopySamples()+ ")); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " dst=0x%x src=0x%x count=%d iChannels=%d fmt=0x%x\n", - (unsigned) dst, (unsigned) src, (unsigned) count, - (unsigned) iChannels, (unsigned) fmt)); + " dst=%p src=%p count=%d iChannels=%d fmt=0x%x\n", + dst, src, (unsigned) count, (unsigned) iChannels, (unsigned) fmt)); // Gershwin does format conversion in hardware so normally // we don't do any host based coversion. The data formatter @@ -2830,9 +2825,9 @@ void *src = hwsrc; //default to the standard destination buffer addr CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO - "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=0x%.8x\n", + "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=%p\n", s->prop_adc.fmt, s->prop_adc.fmt_original, - (unsigned) cnt, (unsigned) dest)); + (unsigned) cnt, dest)); if (cnt > s->dma_adc.dmasize) { cnt = s->dma_adc.dmasize; @@ -2877,7 +2872,7 @@ unsigned copied = 0; CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4281: cs4281_read()+ %d \n", count)); + printk(KERN_INFO "cs4281: cs4281_read()+ %Zu \n", count)); VALIDATE_STATE(s); if (ppos != &file->f_pos) @@ -2900,7 +2895,7 @@ // while (count > 0) { CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - "_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n", + "_read() count>0 count=%Zu .count=%d .swptr=%d .hwptr=%d \n", count, s->dma_adc.count, s->dma_adc.swptr, s->dma_adc.hwptr)); spin_lock_irqsave(&s->lock, flags); @@ -2957,11 +2952,10 @@ // the "cnt" is the number of bytes to read. CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO - "_read() copy_to cnt=%d count=%d ", cnt, count)); + "_read() copy_to cnt=%d count=%Zu ", cnt, count)); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", - s->dma_adc.dmasize, s->dma_adc.count, - (unsigned) buffer, ret)); + " .dmasize=%d .count=%d buffer=%p ret=%Zd\n", + s->dma_adc.dmasize, s->dma_adc.count, buffer, ret)); if (cs_copy_to_user (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied)) @@ -2977,7 +2971,7 @@ start_adc(s); } CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4281: cs4281_read()- %d\n", ret)); + printk(KERN_INFO "cs4281: cs4281_read()- %Zd\n", ret)); return ret; } @@ -2993,7 +2987,7 @@ int cnt; CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4281: cs4281_write()+ count=%d\n", + printk(KERN_INFO "cs4281: cs4281_write()+ count=%Zu\n", count)); VALIDATE_STATE(s); @@ -3049,7 +3043,7 @@ start_dac(s); } CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4281: cs4281_write()- %d\n", ret)); + printk(KERN_INFO "cs4281: cs4281_write()- %Zd\n", ret)); return ret; } @@ -3170,8 +3164,7 @@ int val, mapped, ret; CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): file=0x%.8x cmd=0x%.8x\n", - (unsigned) file, cmd)); + "cs4281: cs4281_ioctl(): file=%p cmd=0x%.8x\n", file, cmd)); #if CSDEBUG cs_printioctl(cmd); #endif @@ -3601,8 +3594,8 @@ (struct cs4281_state *) file->private_data; CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO - "cs4281: cs4281_release(): inode=0x%.8x file=0x%.8x f_mode=%d\n", - (unsigned) inode, (unsigned) file, file->f_mode)); + "cs4281: cs4281_release(): inode=%p file=%p f_mode=%d\n", + inode, file, file->f_mode)); VALIDATE_STATE(s); @@ -3636,8 +3629,8 @@ struct list_head *entry; CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4281: cs4281_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", - (unsigned) inode, (unsigned) file, file->f_mode)); + "cs4281: cs4281_open(): inode=%p file=%p f_mode=0x%x\n", + inode, file, file->f_mode)); list_for_each(entry, &cs4281_devs) { @@ -4347,10 +4340,8 @@ CS_DBGOUT(CS_INIT, 2, printk(KERN_INFO - "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=0x%.8x pBA1=0x%.8x \n", - (unsigned) temp1, (unsigned) temp2, - (unsigned) s->pBA0, (unsigned) s->pBA1)); - + "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=%p pBA1=%p \n", + (unsigned) temp1, (unsigned) temp2, s->pBA0, s->pBA1)); CS_DBGOUT(CS_INIT, 2, printk(KERN_INFO "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n", @@ -4397,15 +4388,13 @@ if (pmdev) { CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO - "cs4281: probe() pm_register() succeeded (0x%x).\n", - (unsigned)pmdev)); + "cs4281: probe() pm_register() succeeded (%p).\n", pmdev)); pmdev->data = s; } else { CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 0, printk(KERN_INFO - "cs4281: probe() pm_register() failed (0x%x).\n", - (unsigned)pmdev)); + "cs4281: probe() pm_register() failed (%p).\n", pmdev)); s->pm.flags |= CS4281_PM_NOT_REGISTERED; } #endif diff -urN linux-2.5.69-bk13/sound/oss/cs4281/cs4281pm-24.c linux-2.5.69-bk14/sound/oss/cs4281/cs4281pm-24.c --- linux-2.5.69-bk13/sound/oss/cs4281/cs4281pm-24.c 2003-05-04 16:53:33.000000000 -0700 +++ linux-2.5.69-bk14/sound/oss/cs4281/cs4281pm-24.c 2003-05-20 04:46:49.000000000 -0700 @@ -46,8 +46,8 @@ struct cs4281_state *state; CS_DBGOUT(CS_PM, 2, printk(KERN_INFO - "cs4281: cs4281_pm_callback dev=0x%x rqst=0x%x state=%d\n", - (unsigned)dev,(unsigned)rqst,(unsigned)data)); + "cs4281: cs4281_pm_callback dev=%p rqst=0x%x state=%p\n", + dev,(unsigned)rqst,data)); state = (struct cs4281_state *) dev->data; if (state) { switch(rqst) { diff -urN linux-2.5.69-bk13/sound/pci/emu10k1/emufx.c linux-2.5.69-bk14/sound/pci/emu10k1/emufx.c --- linux-2.5.69-bk13/sound/pci/emu10k1/emufx.c 2003-05-04 16:53:31.000000000 -0700 +++ linux-2.5.69-bk14/sound/pci/emu10k1/emufx.c 2003-05-20 04:46:49.000000000 -0700 @@ -2206,7 +2206,7 @@ unsigned short fxbus_mask, extin_mask, extout_mask; int res; - memset(info, 0, sizeof(info)); + memset(info, 0, sizeof(*info)); info->card = emu->card_type; info->internal_tram_size = emu->fx8010.itram_size; info->external_tram_size = emu->fx8010.etram_size;