From 566c4337119807861e31db34c01c0250d0d882b2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:40:44 -0500 Subject: [PATCH] - David Miller: sparc and net updates. Fix merge_segments. - Dan Aloni: ISA PnP name parsing cleanup - Mohammad Haque and others: hunt down tq initializations. - Petr Vandrovec: ncpfs config changes - Neil Brown: raid and md cleanups - Pete Zaitcev: ymfpci update - Alan Cox: sync (network driver MODULE_OWNER and cleanups) - Martin Diehl: pirq router for VLSI 82C534 (HP OmniBook and others) - Tigran Aivazian: ia32 microcode driver update - Tim Waugh: parport fixes (ECP write, documentation) - Richard Henderson: alpha update - David Woodhouse: MTD update - Trond Myklebust: index the NFS inode cache using the file handle. This makes NetApp snapshot directories do the right thing. --- CREDITS | 2 +- Documentation/Changes | 13 +- Documentation/Configure.help | 12 +- Documentation/DMA-mapping.txt | 2 +- Documentation/DocBook/videobook.tmpl | 2 +- Documentation/cachetlb.txt | 2 +- Documentation/ioctl-number.txt | 4 +- Documentation/kbuild/makefiles.txt | 68 ++ MAINTAINERS | 2 +- arch/alpha/kernel/core_cia.c | 4 +- arch/alpha/kernel/pci.c | 6 +- arch/alpha/kernel/pci_iommu.c | 4 +- arch/alpha/lib/Makefile | 30 +- arch/alpha/lib/checksum.c | 32 +- arch/alpha/lib/csum_partial_copy.c | 35 +- arch/alpha/lib/ev6-memchr.S | 191 ++++ arch/alpha/lib/ev6-memcpy.S | 248 +++++ arch/alpha/lib/ev6-memset.S | 596 ++++++++++++ arch/alpha/lib/ev6-strcpy.S | 23 - arch/alpha/lib/ev6-strncpy.S | 36 - arch/alpha/lib/ev6-stxcpy.S | 321 +++++++ arch/alpha/lib/ev6-stxncpy.S | 399 ++++++++ arch/alpha/lib/ev67-strchr.S | 4 +- arch/alpha/lib/memcpy.c | 21 - arch/alpha/lib/memmove.S | 103 +++ arch/alpha/lib/strcpy.S | 1 + arch/alpha/lib/strncpy.S | 4 + arch/alpha/lib/stxcpy.S | 3 +- arch/alpha/lib/stxncpy.S | 3 +- arch/i386/config.in | 2 +- arch/i386/kernel/dmi_scan.c | 182 ++++ arch/i386/kernel/microcode.c | 122 ++- arch/i386/kernel/pci-irq.c | 60 +- arch/sparc/boot/piggyback.c | 49 +- arch/sparc/kernel/ioport.c | 5 +- arch/sparc/kernel/pcic.c | 14 +- arch/sparc/kernel/sparc_ksyms.c | 7 +- arch/sparc/kernel/sys_sparc.c | 25 +- arch/sparc/mm/init.c | 9 +- arch/sparc/mm/srmmu.c | 76 +- arch/sparc/mm/sun4c.c | 79 +- arch/sparc64/kernel/sparc64_ksyms.c | 3 +- arch/sparc64/kernel/sys_sparc.c | 24 +- arch/sparc64/kernel/sys_sparc32.c | 10 +- arch/sparc64/mm/init.c | 13 +- drivers/atm/ambassador.c | 2 +- drivers/block/ll_rw_blk.c | 155 +++- drivers/block/paride/pseudo.h | 2 +- drivers/char/drm/gamma_dma.c | 2 +- drivers/char/drm/i810_dma.c | 2 +- drivers/char/drm/mga_dma.c | 2 +- drivers/char/n_r3964.c | 24 +- drivers/char/scan_keyb.c | 2 +- drivers/char/sx.h | 2 +- drivers/char/vt.c | 4 +- drivers/i2o/i2o_lan.c | 12 +- drivers/ieee1394/guid.c | 2 +- drivers/ieee1394/ohci1394.c | 2 +- drivers/isdn/hisax/config.c | 2 +- drivers/isdn/hisax/isdnl1.c | 2 +- drivers/isdn/hysdn/boardergo.c | 2 +- drivers/isdn/pcbit/drv.c | 2 +- drivers/md/Makefile | 20 +- drivers/md/linear.c | 36 +- drivers/md/lvm.c | 7 +- drivers/md/md.c | 94 +- drivers/md/raid0.c | 29 +- drivers/md/raid1.c | 173 ++-- drivers/md/raid5.c | 15 +- drivers/media/video/zr36120.h | 2 +- drivers/mtd/Config.in | 44 +- drivers/mtd/Makefile | 8 +- drivers/mtd/doc2000.c | 1101 +++++++++++++--------- drivers/mtd/doc2001.c | 356 +++++--- drivers/mtd/docecc.c | 522 +++++++++++ drivers/mtd/docprobe.c | 97 +- drivers/mtd/map_ram.c | 13 +- drivers/mtd/map_rom.c | 46 +- drivers/mtd/mtdblock.c | 677 ++++++++++---- drivers/mtd/mtdchar.c | 195 ++-- drivers/mtd/mtdcore.c | 158 +--- drivers/mtd/mtdpart.c | 228 +++++ drivers/mtd/nftl.c | 1263 +++++++++++--------------- drivers/mtd/nftlmount.c | 678 ++++++++++++++ drivers/mtd/nora.c | 50 +- drivers/mtd/octagon-5066.c | 57 +- drivers/mtd/physmap.c | 38 +- drivers/mtd/pnc2000.c | 140 +-- drivers/mtd/rpxlite.c | 105 +-- drivers/mtd/vmax301.c | 61 +- drivers/net/acenic.c | 2 +- drivers/net/aironet4500_core.c | 2 +- drivers/net/eth16i.c | 8 +- drivers/net/ethertap.c | 9 +- drivers/net/ewrk3.c | 17 +- drivers/net/fmv18x.c | 6 +- drivers/net/hamachi.c | 12 +- drivers/net/hamradio/baycom_epp.c | 3 +- drivers/net/hamradio/hdlcdrv.c | 21 +- drivers/net/hp-plus.c | 20 +- drivers/net/hp.c | 26 +- drivers/net/hp100.c | 15 +- drivers/net/ibmlana.c | 28 +- drivers/net/isa-skeleton.c | 18 +- drivers/net/lne390.c | 26 +- drivers/net/mvme147.c | 6 +- drivers/net/natsemi.c | 11 +- drivers/net/pcnet32.c | 44 +- drivers/net/plip.c | 6 +- drivers/net/pppoe.c | 9 +- drivers/net/rclanmtl.c | 2 +- drivers/net/sb1000.c | 17 +- drivers/net/setup.c | 9 - drivers/net/sis900.c | 13 +- drivers/net/sk_mca.c | 7 +- drivers/net/smc-mca.c | 26 +- drivers/net/smc-ultra.c | 7 +- drivers/net/smc-ultra32.c | 84 +- drivers/net/smc9194.c | 167 ++-- drivers/net/sundance.c | 12 +- drivers/net/sunhme.c | 8 +- drivers/net/tlan.c | 6 +- drivers/net/tulip/tulip_core.c | 4 +- drivers/net/via-rhine.c | 13 +- drivers/net/wan/comx-hw-mixcom.c | 2 +- drivers/net/wan/comx.h | 4 +- drivers/net/wan/sdlamain.c | 9 +- drivers/net/wan/x25_asy.h | 2 +- drivers/net/wd.c | 16 +- drivers/net/winbond-840.c | 12 +- drivers/net/yellowfin.c | 54 +- drivers/parport/ChangeLog | 6 + drivers/parport/Makefile | 1 + drivers/parport/parport_gsc.c | 573 ++++++++++++ drivers/parport/parport_pc.c | 8 +- drivers/pci/pci.c | 5 +- drivers/pci/setup-bus.c | 6 +- drivers/pci/setup-res.c | 11 +- drivers/pnp/isapnp.c | 33 +- drivers/s390/net/ctc.c | 2 +- drivers/sbus/audio/dmy.c | 2 +- drivers/sbus/char/aurora.c | 2 +- drivers/sbus/char/sab82532.c | 4 +- drivers/scsi/Makefile | 3 +- drivers/scsi/imm.c | 21 +- drivers/scsi/imm.h | 5 +- drivers/scsi/ips.h | 2 +- drivers/scsi/ppa.c | 18 +- drivers/scsi/ppa.h | 5 +- drivers/sound/vwsnd.c | 2 + drivers/sound/ymfpci.c | 82 +- drivers/sound/ymfpci.h | 8 +- drivers/usb/serial/digi_acceleport.c | 2 +- drivers/usb/serial/keyspan.c | 1 - drivers/usb/serial/keyspan.h | 1 + drivers/usb/serial/keyspan_pda.c | 4 +- drivers/video/mdacon.c | 18 - fs/buffer.c | 6 +- fs/ncpfs/Config.in | 2 - fs/ncpfs/ioctl.c | 4 - fs/ncpfs/ncplib_kernel.h | 6 +- fs/nfs/inode.c | 65 +- fs/nfs/read.c | 2 +- fs/proc/generic.c | 2 +- fs/readdir.c | 1 - fs/smbfs/sock.c | 2 +- include/asm-generic/pgtable.h | 2 +- include/asm-i386/apic.h | 2 +- include/asm-i386/msr.h | 4 + include/asm-i386/pgtable.h | 2 +- include/asm-i386/processor.h | 3 +- include/asm-sparc/hdreg.h | 4 +- include/asm-sparc/processor.h | 3 +- include/asm-sparc64/pgalloc.h | 11 +- include/asm-sparc64/processor.h | 3 +- include/linux/atmdev.h | 2 +- include/linux/module.h | 2 + include/linux/mtd/doc2000.h | 34 +- include/linux/mtd/map.h | 19 +- include/linux/mtd/mtd.h | 50 +- include/linux/mtd/nand.h | 169 +++- include/linux/mtd/nand_ids.h | 52 ++ include/linux/mtd/nftl.h | 44 +- include/linux/mtd/partitions.h | 50 + include/linux/nfs_fs.h | 2 +- include/linux/raid/md_k.h | 4 +- include/linux/raid/raid1.h | 1 - include/linux/skbuff.h | 6 +- include/linux/spinlock.h | 4 +- include/net/sock.h | 1 + kernel/Makefile | 7 + kernel/fork.c | 4 +- mm/memory.c | 45 +- mm/mmap.c | 17 +- mm/mremap.c | 2 +- net/atm/proc.c | 2 +- net/core/dev.c | 44 +- net/core/dv.c | 2 +- net/core/skbuff.c | 12 +- net/decnet/dn_nsp_in.c | 6 +- net/decnet/dn_route.c | 5 +- net/ipv4/devinet.c | 2 +- net/ipv4/ip_fragment.c | 23 +- net/ipv4/ip_input.c | 8 +- net/ipv4/netfilter/ip_queue.c | 7 - net/ipv4/netfilter/ipt_MIRROR.c | 2 +- net/ipv4/tcp_ipv4.c | 4 +- net/ipv6/ip6_input.c | 7 +- net/ipv6/reassembly.c | 13 +- net/ipv6/tcp_ipv6.c | 4 +- net/packet/af_packet.c | 13 +- net/x25/af_x25.c | 3 +- scripts/kernel-doc | 40 +- 213 files changed, 8202 insertions(+), 3507 deletions(-) create mode 100644 arch/alpha/lib/ev6-memchr.S create mode 100644 arch/alpha/lib/ev6-memcpy.S create mode 100644 arch/alpha/lib/ev6-memset.S delete mode 100644 arch/alpha/lib/ev6-strcpy.S delete mode 100644 arch/alpha/lib/ev6-strncpy.S create mode 100644 arch/alpha/lib/ev6-stxcpy.S create mode 100644 arch/alpha/lib/ev6-stxncpy.S create mode 100644 arch/alpha/lib/memmove.S create mode 100644 arch/i386/kernel/dmi_scan.c create mode 100644 drivers/mtd/docecc.c create mode 100644 drivers/mtd/mtdpart.c create mode 100644 drivers/mtd/nftlmount.c create mode 100644 drivers/parport/parport_gsc.c create mode 100644 include/linux/mtd/nand_ids.h create mode 100644 include/linux/mtd/partitions.h diff --git a/CREDITS b/CREDITS index 287a21415ac1..b2e00bf576fe 100644 --- a/CREDITS +++ b/CREDITS @@ -41,7 +41,7 @@ N: Tigran A. Aivazian E: tigran@veritas.com W: http://www.ocston.org/~tigran D: BFS filesystem -D: Intel P6 CPU microcode update support +D: Intel IA32 CPU microcode update support D: Various kernel patches S: United Kingdom diff --git a/Documentation/Changes b/Documentation/Changes index abcb21c6479c..ed3bfb6024eb 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -185,10 +185,10 @@ PCMCIA (PC Card) support is now partially implemented in the main kernel source. Pay attention when you recompile your kernel ;-). Also, be sure to upgrade to the latest pcmcia-cs release. -Intel P6 microcode ------------------- +Intel IA32 microcode +-------------------- -A driver has been added to allow updating of Intel P6 microcode, +A driver has been added to allow updating of Intel IA32 microcode, accessible as both a devfs regular file and as a normal (misc) character device. If you are not using devfs you may need to: @@ -199,6 +199,13 @@ chmod 0644 /dev/cpu/microcode as root before you can use this. You'll probably also want to get the user-space microcode_ctl utility to use with this. +If you have compiled the driver as a module you may need to add +the following line: + +alias char-major-10-184 microcode + +to your /etc/modules.conf file. + Networking ========== diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 135426b104f3..7ac3fdd28fda 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -13442,13 +13442,13 @@ CONFIG_TOSHIBA Toshiba Linux utilities website at: http://www.buzzard.org.uk/toshiba/ -/dev/cpu/microcode - Intel P6 CPU microcode support +/dev/cpu/microcode - Intel IA32 CPU microcode support CONFIG_MICROCODE If you say Y here and also to "/dev file system support" in the 'File systems' section, you will be able to update the microcode on - Intel processors in the P6 family, e.g. Pentium Pro, Pentium II, - Pentium III, Xeon etc. You will obviously need the actual microcode - binary data itself which is not shipped with the Linux kernel. + Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II, + Pentium III, Pentium 4, Xeon etc. You will obviously need the actual + microcode binary data itself which is not shipped with the Linux kernel. For latest news and information on obtaining all the required ingredients for this driver, check: @@ -13457,7 +13457,9 @@ CONFIG_MICROCODE This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called microcode.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read Documentation/modules.txt. If you use + modprobe or kmod you may also want to add the line + 'alias char-major-10-184 microcode' to your /etc/modules.conf file. /dev/cpu/*/msr - Model-specific register support CONFIG_X86_MSR diff --git a/Documentation/DMA-mapping.txt b/Documentation/DMA-mapping.txt index 0ffccdee2361..b59a97dd102d 100644 --- a/Documentation/DMA-mapping.txt +++ b/Documentation/DMA-mapping.txt @@ -341,7 +341,7 @@ to use the pci_dma_sync_*() interfaces. struct my_card_header *hp; /* Examine the header to see if we wish - * to except the data. But synchronize + * to accept the data. But synchronize * the DMA transfer with the CPU first * so that we see updated contents. */ diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl index 2174af671430..3798fc310809 100644 --- a/Documentation/DocBook/videobook.tmpl +++ b/Documentation/DocBook/videobook.tmpl @@ -66,7 +66,7 @@ vertical blanking data interfaces are also provided. - + Radio Devices There are a wide variety of radio interfaces available for PC's, and these diff --git a/Documentation/cachetlb.txt b/Documentation/cachetlb.txt index 5201a2f54629..f3ae78497971 100644 --- a/Documentation/cachetlb.txt +++ b/Documentation/cachetlb.txt @@ -257,7 +257,7 @@ the proper points in time. Here is the new interface: - void copy_user_page(void *from, void *to, unsigned long address) + void copy_user_page(void *to, void *from, unsigned long address) void clear_user_page(void *to, unsigned long address) These two routines store data in user anonymous or COW diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 0edc635ff290..4fd9a4b5af96 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -74,8 +74,8 @@ Code Seq# Include File Comments 0x22 all scsi/sg.h '1' 00-1F PPS kit from Ulrich Windl -'6' 00-10 Intel P6 microcode update driver - +'6' 00-10 Intel IA32 microcode update driver + '8' all SNP8023 advanced NIC card 'A' 00-1F linux/apm_bios.h diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index 1b63892c61e2..ef789acf2ec6 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -32,6 +32,8 @@ This document describes the Linux kernel Makefiles. 7.6 Compilation flags 7.7 Miscellaneous variables 8 New-style variables + 8.1 New variables + 8.2 Converting to old-style 9 Compatibility with Linux Kernel 2.2 10 Credits @@ -521,6 +523,8 @@ contains boilerplate code which converts from new-style variables to old-style variables. This is because Rules.make processes only the old-style variables. +See section 8.2 ("Converting to old-style") for examples. + --- 6.4 Rules.make section @@ -679,6 +683,25 @@ The public interface of Rules.make consists of the following variables: options still control whether or not its $(O_TARGET) goes into vmlinux. See the $(M_OBJS) example below. + Sometimes the ordering of all $(OX_OBJS) files before all + $(O_OBJS) files can be a problem, particularly if both + $(O_OBJS) files and $(OX_OBJS) files contain __initcall + declarations where order is important. To avoid this imposed + ordering, the use of $(OX_OBJS) can be dropped altogether and + $(MIX_OBJS) used instead. + + If this approach is used, then: + - All objects to be linked into vmlinux should be listed in + $(O_OBJS) in the desired order. + - All objects to be created as modules should be listed in + $(M_OBJS) + - All objects that export symbols should also be listed in + $(MIX_OBJS). + + This has the same effect as maintaining the + exported/non-exported split, except that there is more control + over the ordering of object files in vmlinux. + --- 7.3 Library file goals @@ -865,6 +888,14 @@ The public interface of Rules.make consists of the following variables: $(LD) -r -o $@ $(sb-objs) + As is mentioned in section 7.2 ("Object file goals"), + $(MIX_OBJS) can also be used simply to list all objects that + export any symbols. If this approach is taken, then + $(O_OBJS), $(L_OBJS), $(M_OBJS) and $(MI_OBJS) should simply + lists all of the vmlinux object files, library object files, + module object files and intermediate module files + respectively. Duplication between $(MI_OBJS) and $(MIX_OBJS) + is not a problem. --- 7.6 Compilation flags @@ -993,6 +1024,8 @@ variables into old-style variables. There is also some mixing, where people define most variables using "new style" but then fall back to "old style" for a few lines. +--- 8.1 New variables + obj-y obj-m obj-n obj- These variables replace $(O_OBJS), $(OX_OBJS), $(M_OBJS), @@ -1184,6 +1217,41 @@ people define most variables using "new style" but then fall back to This means nls should be added to (subdir-y) and $(subdir-m) if CONFIG_NFS = y. +--- 8.2 Converting to old-style + + The following example is taken from drivers/usb/Makefile. + Note that this uses MIX_OBJS to avoid the need for OX_OBJS and + MX_OBJS and thus to maintain the ordering of objects in $(obj-y) + + # Translate to Rules.make lists. + multi-used := $(filter $(list-multi), $(obj-y) $(obj-m)) + multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs)) + active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m)) + + O_OBJS := $(obj-y) + M_OBJS := $(obj-m) + MIX_OBJS := $(filter $(export-objs), $(active-objs)) + + An example for libraries from drivers/acorn/scsi/Makefile: + + # Translate to Rules.make lists. + + L_OBJS := $(filter-out $(export-objs), $(obj-y)) + LX_OBJS := $(filter $(export-objs), $(obj-y)) + M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) + MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + + As ordering is not so important in libraries, this still uses + LX_OBJS and MX_OBJS, though (presumably) it could be changed to + use MIX_OBJS as follows: + + active-objs := $(sort $(obj-y) $(obj-m)) + L_OBJS := $(obj-y) + M_OBJS := $(obj-m) + MIX_OBJS := $(filter $(export-objs), $(active-objs)) + + + which is clearly shorted and arguably clearer. === 9 Compatibility with Linux Kernel 2.2 diff --git a/MAINTAINERS b/MAINTAINERS index 5fd8361cfac4..16e3b02a6c68 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -640,7 +640,7 @@ M: jgarzik@mandrakesoft.com W: http://sourceforge.net/projects/gkernel/ S: Maintained -INTEL P6 MICROCODE UPDATE SUPPORT +INTEL IA32 MICROCODE UPDATE SUPPORT P: Tigran Aivazian M: tigran@veritas.com S: Maintained diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c index b736f9516ce8..6203b0247680 100644 --- a/arch/alpha/kernel/core_cia.c +++ b/arch/alpha/kernel/core_cia.c @@ -700,11 +700,11 @@ do_init_arch(int is_pyxis) *(vip)CIA_IOC_PCI_W1_BASE = 0x40000000 | 1; *(vip)CIA_IOC_PCI_W1_MASK = (0x40000000 - 1) & 0xfff00000; - *(vip)CIA_IOC_PCI_T1_BASE = 0; + *(vip)CIA_IOC_PCI_T1_BASE = 0 >> 2; *(vip)CIA_IOC_PCI_W2_BASE = 0x80000000 | 1; *(vip)CIA_IOC_PCI_W2_MASK = (0x40000000 - 1) & 0xfff00000; - *(vip)CIA_IOC_PCI_T2_BASE = 0x40000000; + *(vip)CIA_IOC_PCI_T2_BASE = 0x40000000 >> 2; *(vip)CIA_IOC_PCI_W3_BASE = 0; } diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index e17cbcd70892..ddf3d9755ca1 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -90,10 +90,8 @@ quirk_cypress_ide_ports(struct pci_dev *dev) { if (dev->class >> 8 != PCI_CLASS_STORAGE_IDE) return; - dev->resource[1].start |= 2; - dev->resource[1].end = dev->resource[1].start; - pci_claim_resource(dev, 0); - pci_claim_resource(dev, 1); + dev->resource[0].flags = 0; + dev->resource[1].flags = 0; } static void __init diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c index 3b727631a6b4..d4eb6a5d9876 100644 --- a/arch/alpha/kernel/pci_iommu.c +++ b/arch/alpha/kernel/pci_iommu.c @@ -613,10 +613,10 @@ pci_dma_supported(struct pci_dev *pdev, dma_addr_t mask) /* Check that we have a scatter-gather arena that fits. */ hose = pdev ? pdev->sysdata : pci_isa_hose; arena = hose->sg_isa; - if (arena && arena->dma_base + arena->size <= mask) + if (arena && arena->dma_base + arena->size - 1 <= mask) return 1; arena = hose->sg_pci; - if (arena && arena->dma_base + arena->size <= mask) + if (arena && arena->dma_base + arena->size - 1 <= mask) return 1; return 0; diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile index 5de58a6ad2ad..cb23a987d491 100644 --- a/arch/alpha/lib/Makefile +++ b/arch/alpha/lib/Makefile @@ -20,12 +20,30 @@ ifeq ($(CONFIG_ALPHA_EV67),y) ev67 := ev67- endif -OBJS = __divqu.o __remqu.o __divlu.o __remlu.o memset.o memcpy.o io.o \ - checksum.o csum_partial_copy.o $(ev67)strlen.o \ - $(ev67)strcat.o $(ev6)strcpy.o $(ev67)strncat.o $(ev6)strncpy.o \ - stxcpy.o stxncpy.o $(ev67)strchr.o strrchr.o memchr.o \ - $(ev6)copy_user.o $(ev6)clear_user.o $(ev6)strncpy_from_user.o \ - $(ev67)strlen_user.o $(ev6)csum_ipv6_magic.o strcasecmp.o fpreg.o \ +OBJS = __divqu.o __remqu.o __divlu.o __remlu.o \ + $(ev6)memset.o \ + $(ev6)memcpy.o \ + memmove.o \ + io.o \ + checksum.o \ + csum_partial_copy.o \ + $(ev67)strlen.o \ + $(ev67)strcat.o \ + strcpy.o \ + $(ev67)strncat.o \ + strncpy.o \ + $(ev6)stxcpy.o \ + $(ev6)stxncpy.o \ + $(ev67)strchr.o \ + strrchr.o \ + $(ev6)memchr.o \ + $(ev6)copy_user.o \ + $(ev6)clear_user.o \ + $(ev6)strncpy_from_user.o \ + $(ev67)strlen_user.o \ + $(ev6)csum_ipv6_magic.o \ + strcasecmp.o \ + fpreg.o \ callback_srm.o srm_puts.o srm_printk.o lib.a: $(OBJS) diff --git a/arch/alpha/lib/checksum.c b/arch/alpha/lib/checksum.c index 5165279f0da2..7f29ac81c7f3 100644 --- a/arch/alpha/lib/checksum.c +++ b/arch/alpha/lib/checksum.c @@ -3,6 +3,10 @@ * * This file contains network checksum routines that are better done * in an architecture-specific manner due to speed.. + * Comments in other versions indicate that the algorithms are from RFC1071 + * + * accellerated versions (and 21264 assembly versions ) contributed by + * Rick Gorton */ #include @@ -11,15 +15,25 @@ static inline unsigned short from64to16(unsigned long x) { - /* add up 32-bit words for 33 bits */ - x = (x & 0xffffffff) + (x >> 32); - /* add up 16-bit and 17-bit words for 17+c bits */ - x = (x & 0xffff) + (x >> 16); - /* add up 16-bit and 2-bit for 16+c bit */ - x = (x & 0xffff) + (x >> 16); - /* add up carry.. */ - x = (x & 0xffff) + (x >> 16); - return x; + /* Using extract instructions is a bit more efficient + than the original shift/bitmask version. */ + + union { + unsigned long ul; + unsigned int ui[2]; + unsigned short us[4]; + } in_v, tmp_v, out_v; + + in_v.ul = x; + tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1]; + + /* Since the bits of tmp_v.sh[3] are going to always be zero, + we don't have to bother to add that in. */ + out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1] + + (unsigned long) tmp_v.us[2]; + + /* Similarly, out_v.us[2] is always zero for the final add. */ + return out_v.us[0] + out_v.us[1]; } /* diff --git a/arch/alpha/lib/csum_partial_copy.c b/arch/alpha/lib/csum_partial_copy.c index 5638a1a0c582..f8129b5f50d3 100644 --- a/arch/alpha/lib/csum_partial_copy.c +++ b/arch/alpha/lib/csum_partial_copy.c @@ -2,6 +2,8 @@ * csum_partial_copy - do IP checksumming and copy * * (C) Copyright 1996 Linus Torvalds + * accellerated versions (and 21264 assembly versions ) contributed by + * Rick Gorton * * Don't look at this too closely - you'll go mad. The things * we do for performance.. @@ -68,6 +70,31 @@ __asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y)) }) +static inline unsigned short from64to16(unsigned long x) +{ + /* Using extract instructions is a bit more efficient + than the original shift/bitmask version. */ + + union { + unsigned long ul; + unsigned int ui[2]; + unsigned short us[4]; + } in_v, tmp_v, out_v; + + in_v.ul = x; + tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1]; + + /* Since the bits of tmp_v.sh[3] are going to always be zero, + we don't have to bother to add that in. */ + out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1] + + (unsigned long) tmp_v.us[2]; + + /* Similarly, out_v.us[2] is always zero for the final add. */ + return out_v.us[0] + out_v.us[1]; +} + + + /* * Ok. This isn't fun, but this is the EASY case. */ @@ -335,13 +362,7 @@ do_csum_partial_copy_from_user(const char *src, char *dst, int len, soff, doff, len-8, checksum, partial_dest, errp); } - /* 64 -> 33 bits */ - checksum = (checksum & 0xffffffff) + (checksum >> 32); - /* 33 -> < 32 bits */ - checksum = (checksum & 0xffff) + (checksum >> 16); - /* 32 -> 16 bits */ - checksum = (checksum & 0xffff) + (checksum >> 16); - checksum = (checksum & 0xffff) + (checksum >> 16); + checksum = from64to16 (checksum); } return checksum; } diff --git a/arch/alpha/lib/ev6-memchr.S b/arch/alpha/lib/ev6-memchr.S new file mode 100644 index 000000000000..a8e843dbcc23 --- /dev/null +++ b/arch/alpha/lib/ev6-memchr.S @@ -0,0 +1,191 @@ +/* + * arch/alpha/lib/ev6-memchr.S + * + * 21264 version contributed by Rick Gorton + * + * Finds characters in a memory area. Optimized for the Alpha: + * + * - memory accessed as aligned quadwords only + * - uses cmpbge to compare 8 bytes in parallel + * - does binary search to find 0 byte in last + * quadword (HAKMEM needed 12 instructions to + * do this instead of the 9 instructions that + * binary search needs). + * + * For correctness consider that: + * + * - only minimum number of quadwords may be accessed + * - the third argument is an unsigned long + * + * Much of the information about 21264 scheduling/coding comes from: + * Compiler Writer's Guide for the Alpha 21264 + * abbreviated as 'CWG' in other comments here + * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html + * Scheduling notation: + * E - either cluster + * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 + * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 + * Try not to change the actual algorithm if possible for consistency. + */ + + .set noreorder + .set noat + + .align 4 + .globl memchr + .ent memchr +memchr: + .frame $30,0,$26,0 + .prologue 0 + + # Hack -- if someone passes in (size_t)-1, hoping to just + # search til the end of the address space, we will overflow + # below when we find the address of the last byte. Given + # that we will never have a 56-bit address space, cropping + # the length is the easiest way to avoid trouble. + zap $18, 0x80, $5 # U : Bound length + beq $18, $not_found # U : + ldq_u $1, 0($16) # L : load first quadword Latency=3 + and $17, 0xff, $17 # E : L L U U : 00000000000000ch + + insbl $17, 1, $2 # U : 000000000000ch00 + cmpult $18, 9, $4 # E : small (< 1 quad) string? + or $2, $17, $17 # E : 000000000000chch + lda $3, -1($31) # E : U L L U + + sll $17, 16, $2 # U : 00000000chch0000 + addq $16, $5, $5 # E : Max search address + or $2, $17, $17 # E : 00000000chchchch + sll $17, 32, $2 # U : U L L U : chchchch00000000 + + or $2, $17, $17 # E : chchchchchchchch + extql $1, $16, $7 # U : $7 is upper bits + beq $4, $first_quad # U : + ldq_u $6, -1($5) # L : L U U L : eight or less bytes to search Latency=3 + + extqh $6, $16, $6 # U : 2 cycle stall for $6 + mov $16, $0 # E : + nop # E : + or $7, $6, $1 # E : L U L U $1 = quadword starting at $16 + + # Deal with the case where at most 8 bytes remain to be searched + # in $1. E.g.: + # $18 = 6 + # $1 = ????c6c5c4c3c2c1 +$last_quad: + negq $18, $6 # E : + xor $17, $1, $1 # E : + srl $3, $6, $6 # U : $6 = mask of $18 bits set + cmpbge $31, $1, $2 # E : L U L U + + nop + nop + and $2, $6, $2 # E : + beq $2, $not_found # U : U L U L + +$found_it: +#if defined(__alpha_fix__) && defined(__alpha_cix__) + /* + * Since we are guaranteed to have set one of the bits, we don't + * have to worry about coming back with a 0x40 out of cttz... + */ + cttz $2, $3 # U0 : + addq $0, $3, $0 # E : All done + nop # E : + ret # L0 : L U L U +#else + /* + * Slow and clunky. It can probably be improved. + * An exercise left for others. + */ + negq $2, $3 # E : + and $2, $3, $2 # E : + and $2, 0x0f, $1 # E : + addq $0, 4, $3 # E : + + cmoveq $1, $3, $0 # E : Latency 2, extra map cycle + nop # E : keep with cmov + and $2, 0x33, $1 # E : + addq $0, 2, $3 # E : U L U L : 2 cycle stall on $0 + + cmoveq $1, $3, $0 # E : Latency 2, extra map cycle + nop # E : keep with cmov + and $2, 0x55, $1 # E : + addq $0, 1, $3 # E : U L U L : 2 cycle stall on $0 + + cmoveq $1, $3, $0 # E : Latency 2, extra map cycle + nop + nop + ret # L0 : L U L U +#endif + + # Deal with the case where $18 > 8 bytes remain to be + # searched. $16 may not be aligned. + .align 4 +$first_quad: + andnot $16, 0x7, $0 # E : + insqh $3, $16, $2 # U : $2 = 0000ffffffffffff ($16<0:2> ff) + xor $1, $17, $1 # E : + or $1, $2, $1 # E : U L U L $1 = ====ffffffffffff + + cmpbge $31, $1, $2 # E : + bne $2, $found_it # U : + # At least one byte left to process. + ldq $1, 8($0) # L : + subq $5, 1, $18 # E : U L U L + + addq $0, 8, $0 # E : + # Make $18 point to last quad to be accessed (the + # last quad may or may not be partial). + andnot $18, 0x7, $18 # E : + cmpult $0, $18, $2 # E : + beq $2, $final # U : U L U L + + # At least two quads remain to be accessed. + + subq $18, $0, $4 # E : $4 <- nr quads to be processed + and $4, 8, $4 # E : odd number of quads? + bne $4, $odd_quad_count # U : + # At least three quads remain to be accessed + mov $1, $4 # E : L U L U : move prefetched value to correct reg + + .align 4 +$unrolled_loop: + ldq $1, 8($0) # L : prefetch $1 + xor $17, $4, $2 # E : + cmpbge $31, $2, $2 # E : + bne $2, $found_it # U : U L U L + + addq $0, 8, $0 # E : + nop # E : + nop # E : + nop # E : + +$odd_quad_count: + xor $17, $1, $2 # E : + ldq $4, 8($0) # L : prefetch $4 + cmpbge $31, $2, $2 # E : + addq $0, 8, $6 # E : + + bne $2, $found_it # U : + cmpult $6, $18, $6 # E : + addq $0, 8, $0 # E : + nop # E : + + bne $6, $unrolled_loop # U : + mov $4, $1 # E : move prefetched value into $1 + nop # E : + nop # E : + +$final: subq $5, $0, $18 # E : $18 <- number of bytes left to do + nop # E : + nop # E : + bne $18, $last_quad # U : + +$not_found: + mov $31, $0 # E : + nop # E : + nop # E : + ret # L0 : + + .end memchr diff --git a/arch/alpha/lib/ev6-memcpy.S b/arch/alpha/lib/ev6-memcpy.S new file mode 100644 index 000000000000..7ebcbc27b7de --- /dev/null +++ b/arch/alpha/lib/ev6-memcpy.S @@ -0,0 +1,248 @@ +/* + * arch/alpha/lib/ev6-memcpy.S + * 21264 version by Rick Gorton + * + * Reasonably optimized memcpy() routine for the Alpha 21264 + * + * - memory accessed as aligned quadwords only + * - uses bcmpge to compare 8 bytes in parallel + * + * Much of the information about 21264 scheduling/coding comes from: + * Compiler Writer's Guide for the Alpha 21264 + * abbreviated as 'CWG' in other comments here + * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html + * Scheduling notation: + * E - either cluster + * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 + * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 + * + * Temp usage notes: + * $1,$2, - scratch + */ + + .set noreorder + .set noat + + .align 4 + .globl memcpy + .ent memcpy +memcpy: + .frame $30,0,$26,0 + .prologue 0 + + mov $16, $0 # E : copy dest to return + ble $18, $nomoredata # U : done with the copy? + xor $16, $17, $1 # E : are source and dest alignments the same? + and $1, 7, $1 # E : are they the same mod 8? + + bne $1, $misaligned # U : Nope - gotta do this the slow way + /* source and dest are same mod 8 address */ + and $16, 7, $1 # E : Are both 0mod8? + beq $1, $both_0mod8 # U : Yes + nop # E : + + /* + * source and dest are same misalignment. move a byte at a time + * until a 0mod8 alignment for both is reached. + * At least one byte more to move + */ + +$head_align: + ldbu $1, 0($17) # L : grab a byte + subq $18, 1, $18 # E : count-- + addq $17, 1, $17 # E : src++ + stb $1, 0($16) # L : + addq $16, 1, $16 # E : dest++ + and $16, 7, $1 # E : Are we at 0mod8 yet? + ble $18, $nomoredata # U : done with the copy? + bne $1, $head_align # U : + +$both_0mod8: + cmple $18, 127, $1 # E : Can we unroll the loop? + bne $1, $no_unroll # U : + and $16, 63, $1 # E : get mod64 alignment + beq $1, $do_unroll # U : no single quads to fiddle + +$single_head_quad: + ldq $1, 0($17) # L : get 8 bytes + subq $18, 8, $18 # E : count -= 8 + addq $17, 8, $17 # E : src += 8 + nop # E : + + stq $1, 0($16) # L : store + addq $16, 8, $16 # E : dest += 8 + and $16, 63, $1 # E : get mod64 alignment + bne $1, $single_head_quad # U : still not fully aligned + +$do_unroll: + addq $16, 64, $7 # E : Initial (+1 trip) wh64 address + cmple $18, 63, $1 # E : Can we go through the unrolled loop? + bne $1, $tail_quads # U : Nope + nop # E : + +$unroll_body: + wh64 ($7) # L1 : memory subsystem hint: 64 bytes at + # ($7) are about to be over-written + ldq $6, 0($17) # L0 : bytes 0..7 + nop # E : + nop # E : + + ldq $4, 8($17) # L : bytes 8..15 + ldq $5, 16($17) # L : bytes 16..23 + addq $7, 64, $7 # E : Update next wh64 address + nop # E : + + ldq $3, 24($17) # L : bytes 24..31 + addq $16, 64, $1 # E : fallback value for wh64 + nop # E : + nop # E : + + addq $17, 32, $17 # E : src += 32 bytes + stq $6, 0($16) # L : bytes 0..7 + nop # E : + nop # E : + + stq $4, 8($16) # L : bytes 8..15 + stq $5, 16($16) # L : bytes 16..23 + subq $18, 192, $2 # E : At least two more trips to go? + nop # E : + + stq $3, 24($16) # L : bytes 24..31 + addq $16, 32, $16 # E : dest += 32 bytes + nop # E : + nop # E : + + ldq $6, 0($17) # L : bytes 0..7 + ldq $4, 8($17) # L : bytes 8..15 + cmovlt $2, $1, $7 # E : Latency 2, extra map slot - Use + # fallback wh64 address if < 2 more trips + nop # E : + + ldq $5, 16($17) # L : bytes 16..23 + ldq $3, 24($17) # L : bytes 24..31 + addq $16, 32, $16 # E : dest += 32 + subq $18, 64, $18 # E : count -= 64 + + addq $17, 32, $17 # E : src += 32 + stq $6, -32($16) # L : bytes 0..7 + stq $4, -24($16) # L : bytes 8..15 + cmple $18, 63, $1 # E : At least one more trip? + + stq $5, -16($16) # L : bytes 16..23 + stq $3, -8($16) # L : bytes 24..31 + nop # E : + beq $1, $unroll_body + +$tail_quads: +$no_unroll: + .align 4 + subq $18, 8, $18 # E : At least a quad left? + blt $18, $less_than_8 # U : Nope + nop # E : + nop # E : + +$move_a_quad: + ldq $1, 0($17) # L : fetch 8 + subq $18, 8, $18 # E : count -= 8 + addq $17, 8, $17 # E : src += 8 + nop # E : + + stq $1, 0($16) # L : store 8 + addq $16, 8, $16 # E : dest += 8 + bge $18, $move_a_quad # U : + nop # E : + +$less_than_8: + .align 4 + addq $18, 8, $18 # E : add back for trailing bytes + ble $18, $nomoredata # U : All-done + nop # E : + nop # E : + + /* Trailing bytes */ +$tail_bytes: + subq $18, 1, $18 # E : count-- + ldbu $1, 0($17) # L : fetch a byte + addq $17, 1, $17 # E : src++ + nop # E : + + stb $1, 0($16) # L : store a byte + addq $16, 1, $16 # E : dest++ + bgt $18, $tail_bytes # U : more to be done? + nop # E : + + /* branching to exit takes 3 extra cycles, so replicate exit here */ + ret $31, ($26), 1 # L0 : + nop # E : + nop # E : + nop # E : + +$misaligned: + mov $0, $4 # E : dest temp + and $0, 7, $1 # E : dest alignment mod8 + beq $1, $dest_0mod8 # U : life doesnt totally suck + nop + +$aligndest: + ble $18, $nomoredata # U : + ldbu $1, 0($17) # L : fetch a byte + subq $18, 1, $18 # E : count-- + addq $17, 1, $17 # E : src++ + + stb $1, 0($4) # L : store it + addq $4, 1, $4 # E : dest++ + and $4, 7, $1 # E : dest 0mod8 yet? + bne $1, $aligndest # U : go until we are aligned. + + /* Source has unknown alignment, but dest is known to be 0mod8 */ +$dest_0mod8: + subq $18, 8, $18 # E : At least a quad left? + blt $18, $misalign_tail # U : Nope + ldq_u $3, 0($17) # L : seed (rotating load) of 8 bytes + nop # E : + +$mis_quad: + ldq_u $16, 8($17) # L : Fetch next 8 + extql $3, $17, $3 # U : masking + extqh $16, $17, $1 # U : masking + bis $3, $1, $1 # E : merged bytes to store + + subq $18, 8, $18 # E : count -= 8 + addq $17, 8, $17 # E : src += 8 + stq $1, 0($4) # L : store 8 (aligned) + mov $16, $3 # E : "rotate" source data + + addq $4, 8, $4 # E : dest += 8 + bge $18, $mis_quad # U : More quads to move + nop + nop + +$misalign_tail: + addq $18, 8, $18 # E : account for tail stuff + ble $18, $nomoredata # U : + nop + nop + +$misalign_byte: + ldbu $1, 0($17) # L : fetch 1 + subq $18, 1, $18 # E : count-- + addq $17, 1, $17 # E : src++ + nop # E : + + stb $1, 0($4) # L : store + addq $4, 1, $4 # E : dest++ + bgt $18, $misalign_byte # U : more to go? + nop + + +$nomoredata: + ret $31, ($26), 1 # L0 : + nop # E : + nop # E : + nop # E : + + .end memcpy + +/* For backwards module compatability. */ +__memcpy = memcpy +.globl __memcpy diff --git a/arch/alpha/lib/ev6-memset.S b/arch/alpha/lib/ev6-memset.S new file mode 100644 index 000000000000..626929fc5a95 --- /dev/null +++ b/arch/alpha/lib/ev6-memset.S @@ -0,0 +1,596 @@ +/* + * arch/alpha/lib/ev6-memset.S + * + * This is an efficient (and relatively small) implementation of the C library + * "memset()" function for the 21264 implementation of Alpha. + * + * 21264 version contributed by Rick Gorton + * + * Much of the information about 21264 scheduling/coding comes from: + * Compiler Writer's Guide for the Alpha 21264 + * abbreviated as 'CWG' in other comments here + * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html + * Scheduling notation: + * E - either cluster + * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 + * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 + * The algorithm for the leading and trailing quadwords remains the same, + * however the loop has been unrolled to enable better memory throughput, + * and the code has been replicated for each of the entry points: __memset + * and __memsetw to permit better scheduling to eliminate the stalling + * encountered during the mask replication. + * A future enhancement might be to put in a byte store loop for really + * small (say < 32 bytes) memset()s. Whether or not that change would be + * a win in the kernel would depend upon the contextual usage. + * WARNING: Maintaining this is going to be more work than the above version, + * as fixes will need to be made in multiple places. The performance gain + * is worth it. + */ + + .set noat + .set noreorder +.text + .globl __memset + .globl __memsetw + .globl __constant_c_memset + .globl memset + + .ent __memset +.align 5 +__memset: +memset: + .frame $30,0,$26,0 + .prologue 0 + + /* + * Serious stalling happens. The only way to mitigate this is to + * undertake a major re-write to interleave the constant materialization + * with other parts of the fall-through code. This is important, even + * though it makes maintenance tougher. + * Do this later. + */ + and $17,255,$1 # E : 00000000000000ch + insbl $17,1,$2 # U : 000000000000ch00 + bis $16,$16,$0 # E : return value + ble $18,end_b # U : zero length requested? + + addq $18,$16,$6 # E : max address to write to + bis $1,$2,$17 # E : 000000000000chch + insbl $1,2,$3 # U : 0000000000ch0000 + insbl $1,3,$4 # U : 00000000ch000000 + + or $3,$4,$3 # E : 00000000chch0000 + inswl $17,4,$5 # U : 0000chch00000000 + xor $16,$6,$1 # E : will complete write be within one quadword? + inswl $17,6,$2 # U : chch000000000000 + + or $17,$3,$17 # E : 00000000chchchch + or $2,$5,$2 # E : chchchch00000000 + bic $1,7,$1 # E : fit within a single quadword? + and $16,7,$3 # E : Target addr misalignment + + or $17,$2,$17 # E : chchchchchchchch + beq $1,within_quad_b # U : + nop # E : + beq $3,aligned_b # U : target is 0mod8 + + /* + * Target address is misaligned, and won't fit within a quadword + */ + ldq_u $4,0($16) # L : Fetch first partial + bis $16,$16,$5 # E : Save the address + insql $17,$16,$2 # U : Insert new bytes + subq $3,8,$3 # E : Invert (for addressing uses) + + addq $18,$3,$18 # E : $18 is new count ($3 is negative) + mskql $4,$16,$4 # U : clear relevant parts of the quad + subq $16,$3,$16 # E : $16 is new aligned destination + bis $2,$4,$1 # E : Final bytes + + nop + stq_u $1,0($5) # L : Store result + nop + nop + +.align 4 +aligned_b: + /* + * We are now guaranteed to be quad aligned, with at least + * one partial quad to write. + */ + + sra $18,3,$3 # U : Number of remaining quads to write + and $18,7,$18 # E : Number of trailing bytes to write + bis $16,$16,$5 # E : Save dest address + beq $3,no_quad_b # U : tail stuff only + + /* + * it's worth the effort to unroll this and use wh64 if possible + * Lifted a bunch of code from clear_user.S + * At this point, entry values are: + * $16 Current destination address + * $5 A copy of $16 + * $6 The max quadword address to write to + * $18 Number trailer bytes + * $3 Number quads to write + */ + + and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop) + subq $3, 16, $4 # E : Only try to unroll if > 128 bytes + subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64) + blt $4, loop_b # U : + + /* + * We know we've got at least 16 quads, minimum of one trip + * through unrolled loop. Do a quad at a time to get us 0mod64 + * aligned. + */ + + nop # E : + nop # E : + nop # E : + beq $1, $bigalign_b # U : + +$alignmod64_b: + stq $17, 0($5) # L : + subq $3, 1, $3 # E : For consistency later + addq $1, 8, $1 # E : Increment towards zero for alignment + addq $5, 8, $4 # E : Initial wh64 address (filler instruction) + + nop + nop + addq $5, 8, $5 # E : Inc address + blt $1, $alignmod64_b # U : + +$bigalign_b: + /* + * $3 - number quads left to go + * $5 - target address (aligned 0mod64) + * $17 - mask of stuff to store + * Scratch registers available: $7, $2, $4, $1 + * we know that we'll be taking a minimum of one trip through + * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle + * Assumes the wh64 needs to be for 2 trips through the loop in the future + * The wh64 is issued on for the starting destination address for trip +2 + * through the loop, and if there are less than two trips left, the target + * address will be for the current trip. + */ + +$do_wh64_b: + wh64 ($4) # L1 : memory subsystem write hint + subq $3, 24, $2 # E : For determining future wh64 addresses + stq $17, 0($5) # L : + nop # E : + + addq $5, 128, $4 # E : speculative target of next wh64 + stq $17, 8($5) # L : + stq $17, 16($5) # L : + addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr) + + stq $17, 24($5) # L : + stq $17, 32($5) # L : + cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle + nop + + stq $17, 40($5) # L : + stq $17, 48($5) # L : + subq $3, 16, $2 # E : Repeat the loop at least once more? + nop + + stq $17, 56($5) # L : + addq $5, 64, $5 # E : + subq $3, 8, $3 # E : + bge $2, $do_wh64_b # U : + + nop + nop + nop + beq $3, no_quad_b # U : Might have finished already + +.align 4 + /* + * Simple loop for trailing quadwords, or for small amounts + * of data (where we can't use an unrolled loop and wh64) + */ +loop_b: + stq $17,0($5) # L : + subq $3,1,$3 # E : Decrement number quads left + addq $5,8,$5 # E : Inc address + bne $3,loop_b # U : more? + +no_quad_b: + /* + * Write 0..7 trailing bytes. + */ + nop # E : + beq $18,end_b # U : All done? + ldq $7,0($5) # L : + mskqh $7,$6,$2 # U : Mask final quad + + insqh $17,$6,$4 # U : New bits + bis $2,$4,$1 # E : Put it all together + stq $1,0($5) # L : And back to memory + ret $31,($26),1 # L0 : + +within_quad_b: + ldq_u $1,0($16) # L : + insql $17,$16,$2 # U : New bits + mskql $1,$16,$4 # U : Clear old + bis $2,$4,$2 # E : New result + + mskql $2,$6,$4 # U : + mskqh $1,$6,$2 # U : + bis $2,$4,$1 # E : + stq_u $1,0($16) # L : + +end_b: + nop + nop + nop + ret $31,($26),1 # L0 : + .end __memset + + /* + * This is the original body of code, prior to replication and + * rescheduling. Leave it here, as there may be calls to this + * entry point. + */ +.align 4 + .ent __memset +__constant_c_memset: + .frame $30,0,$26,0 + .prologue 0 + + addq $18,$16,$6 # E : max address to write to + bis $16,$16,$0 # E : return value + xor $16,$6,$1 # E : will complete write be within one quadword? + ble $18,end # U : zero length requested? + + bic $1,7,$1 # E : fit within a single quadword + beq $1,within_one_quad # U : + and $16,7,$3 # E : Target addr misalignment + beq $3,aligned # U : target is 0mod8 + + /* + * Target address is misaligned, and won't fit within a quadword + */ + ldq_u $4,0($16) # L : Fetch first partial + bis $16,$16,$5 # E : Save the address + insql $17,$16,$2 # U : Insert new bytes + subq $3,8,$3 # E : Invert (for addressing uses) + + addq $18,$3,$18 # E : $18 is new count ($3 is negative) + mskql $4,$16,$4 # U : clear relevant parts of the quad + subq $16,$3,$16 # E : $16 is new aligned destination + bis $2,$4,$1 # E : Final bytes + + nop + stq_u $1,0($5) # L : Store result + nop + nop + +.align 4 +aligned: + /* + * We are now guaranteed to be quad aligned, with at least + * one partial quad to write. + */ + + sra $18,3,$3 # U : Number of remaining quads to write + and $18,7,$18 # E : Number of trailing bytes to write + bis $16,$16,$5 # E : Save dest address + beq $3,no_quad # U : tail stuff only + + /* + * it's worth the effort to unroll this and use wh64 if possible + * Lifted a bunch of code from clear_user.S + * At this point, entry values are: + * $16 Current destination address + * $5 A copy of $16 + * $6 The max quadword address to write to + * $18 Number trailer bytes + * $3 Number quads to write + */ + + and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop) + subq $3, 16, $4 # E : Only try to unroll if > 128 bytes + subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64) + blt $4, loop # U : + + /* + * We know we've got at least 16 quads, minimum of one trip + * through unrolled loop. Do a quad at a time to get us 0mod64 + * aligned. + */ + + nop # E : + nop # E : + nop # E : + beq $1, $bigalign # U : + +$alignmod64: + stq $17, 0($5) # L : + subq $3, 1, $3 # E : For consistency later + addq $1, 8, $1 # E : Increment towards zero for alignment + addq $5, 8, $4 # E : Initial wh64 address (filler instruction) + + nop + nop + addq $5, 8, $5 # E : Inc address + blt $1, $alignmod64 # U : + +$bigalign: + /* + * $3 - number quads left to go + * $5 - target address (aligned 0mod64) + * $17 - mask of stuff to store + * Scratch registers available: $7, $2, $4, $1 + * we know that we'll be taking a minimum of one trip through + * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle + * Assumes the wh64 needs to be for 2 trips through the loop in the future + * The wh64 is issued on for the starting destination address for trip +2 + * through the loop, and if there are less than two trips left, the target + * address will be for the current trip. + */ + +$do_wh64: + wh64 ($4) # L1 : memory subsystem write hint + subq $3, 24, $2 # E : For determining future wh64 addresses + stq $17, 0($5) # L : + nop # E : + + addq $5, 128, $4 # E : speculative target of next wh64 + stq $17, 8($5) # L : + stq $17, 16($5) # L : + addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr) + + stq $17, 24($5) # L : + stq $17, 32($5) # L : + cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle + nop + + stq $17, 40($5) # L : + stq $17, 48($5) # L : + subq $3, 16, $2 # E : Repeat the loop at least once more? + nop + + stq $17, 56($5) # L : + addq $5, 64, $5 # E : + subq $3, 8, $3 # E : + bge $2, $do_wh64 # U : + + nop + nop + nop + beq $3, no_quad # U : Might have finished already + +.align 4 + /* + * Simple loop for trailing quadwords, or for small amounts + * of data (where we can't use an unrolled loop and wh64) + */ +loop: + stq $17,0($5) # L : + subq $3,1,$3 # E : Decrement number quads left + addq $5,8,$5 # E : Inc address + bne $3,loop # U : more? + +no_quad: + /* + * Write 0..7 trailing bytes. + */ + nop # E : + beq $18,end # U : All done? + ldq $7,0($5) # L : + mskqh $7,$6,$2 # U : Mask final quad + + insqh $17,$6,$4 # U : New bits + bis $2,$4,$1 # E : Put it all together + stq $1,0($5) # L : And back to memory + ret $31,($26),1 # L0 : + +within_one_quad: + ldq_u $1,0($16) # L : + insql $17,$16,$2 # U : New bits + mskql $1,$16,$4 # U : Clear old + bis $2,$4,$2 # E : New result + + mskql $2,$6,$4 # U : + mskqh $1,$6,$2 # U : + bis $2,$4,$1 # E : + stq_u $1,0($16) # L : + +end: + nop + nop + nop + ret $31,($26),1 # L0 : + .end __constant_c_memset + + /* + * This is a replicant of the __constant_c_memset code, rescheduled + * to mask stalls. Note that entry point names also had to change + */ + .align 5 + .ent __memsetw + +__memsetw: + .frame $30,0,$26,0 + .prologue 0 + + inswl $17,0,$5 # U : 000000000000c1c2 + inswl $17,2,$2 # U : 00000000c1c20000 + bis $16,$16,$0 # E : return value + addq $18,$16,$6 # E : max address to write to + + ble $18, end_w # U : zero length requested? + inswl $17,4,$3 # U : 0000c1c200000000 + inswl $17,6,$4 # U : c1c2000000000000 + xor $16,$6,$1 # E : will complete write be within one quadword? + + or $2,$5,$2 # E : 00000000c1c2c1c2 + or $3,$4,$17 # E : c1c2c1c200000000 + bic $1,7,$1 # E : fit within a single quadword + and $16,7,$3 # E : Target addr misalignment + + or $17,$2,$17 # E : c1c2c1c2c1c2c1c2 + beq $1,within_quad_w # U : + nop + beq $3,aligned_w # U : target is 0mod8 + + /* + * Target address is misaligned, and won't fit within a quadword + */ + ldq_u $4,0($16) # L : Fetch first partial + bis $16,$16,$5 # E : Save the address + insql $17,$16,$2 # U : Insert new bytes + subq $3,8,$3 # E : Invert (for addressing uses) + + addq $18,$3,$18 # E : $18 is new count ($3 is negative) + mskql $4,$16,$4 # U : clear relevant parts of the quad + subq $16,$3,$16 # E : $16 is new aligned destination + bis $2,$4,$1 # E : Final bytes + + nop + stq_u $1,0($5) # L : Store result + nop + nop + +.align 4 +aligned_w: + /* + * We are now guaranteed to be quad aligned, with at least + * one partial quad to write. + */ + + sra $18,3,$3 # U : Number of remaining quads to write + and $18,7,$18 # E : Number of trailing bytes to write + bis $16,$16,$5 # E : Save dest address + beq $3,no_quad_w # U : tail stuff only + + /* + * it's worth the effort to unroll this and use wh64 if possible + * Lifted a bunch of code from clear_user.S + * At this point, entry values are: + * $16 Current destination address + * $5 A copy of $16 + * $6 The max quadword address to write to + * $18 Number trailer bytes + * $3 Number quads to write + */ + + and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop) + subq $3, 16, $4 # E : Only try to unroll if > 128 bytes + subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64) + blt $4, loop_w # U : + + /* + * We know we've got at least 16 quads, minimum of one trip + * through unrolled loop. Do a quad at a time to get us 0mod64 + * aligned. + */ + + nop # E : + nop # E : + nop # E : + beq $1, $bigalign_w # U : + +$alignmod64_w: + stq $17, 0($5) # L : + subq $3, 1, $3 # E : For consistency later + addq $1, 8, $1 # E : Increment towards zero for alignment + addq $5, 8, $4 # E : Initial wh64 address (filler instruction) + + nop + nop + addq $5, 8, $5 # E : Inc address + blt $1, $alignmod64_w # U : + +$bigalign_w: + /* + * $3 - number quads left to go + * $5 - target address (aligned 0mod64) + * $17 - mask of stuff to store + * Scratch registers available: $7, $2, $4, $1 + * we know that we'll be taking a minimum of one trip through + * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle + * Assumes the wh64 needs to be for 2 trips through the loop in the future + * The wh64 is issued on for the starting destination address for trip +2 + * through the loop, and if there are less than two trips left, the target + * address will be for the current trip. + */ + +$do_wh64_w: + wh64 ($4) # L1 : memory subsystem write hint + subq $3, 24, $2 # E : For determining future wh64 addresses + stq $17, 0($5) # L : + nop # E : + + addq $5, 128, $4 # E : speculative target of next wh64 + stq $17, 8($5) # L : + stq $17, 16($5) # L : + addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr) + + stq $17, 24($5) # L : + stq $17, 32($5) # L : + cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle + nop + + stq $17, 40($5) # L : + stq $17, 48($5) # L : + subq $3, 16, $2 # E : Repeat the loop at least once more? + nop + + stq $17, 56($5) # L : + addq $5, 64, $5 # E : + subq $3, 8, $3 # E : + bge $2, $do_wh64_w # U : + + nop + nop + nop + beq $3, no_quad_w # U : Might have finished already + +.align 4 + /* + * Simple loop for trailing quadwords, or for small amounts + * of data (where we can't use an unrolled loop and wh64) + */ +loop_w: + stq $17,0($5) # L : + subq $3,1,$3 # E : Decrement number quads left + addq $5,8,$5 # E : Inc address + bne $3,loop_w # U : more? + +no_quad_w: + /* + * Write 0..7 trailing bytes. + */ + nop # E : + beq $18,end_w # U : All done? + ldq $7,0($5) # L : + mskqh $7,$6,$2 # U : Mask final quad + + insqh $17,$6,$4 # U : New bits + bis $2,$4,$1 # E : Put it all together + stq $1,0($5) # L : And back to memory + ret $31,($26),1 # L0 : + +within_quad_w: + ldq_u $1,0($16) # L : + insql $17,$16,$2 # U : New bits + mskql $1,$16,$4 # U : Clear old + bis $2,$4,$2 # E : New result + + mskql $2,$6,$4 # U : + mskqh $1,$6,$2 # U : + bis $2,$4,$1 # E : + stq_u $1,0($16) # L : + +end_w: + nop + nop + nop + ret $31,($26),1 # L0 : + + .end __memsetw diff --git a/arch/alpha/lib/ev6-strcpy.S b/arch/alpha/lib/ev6-strcpy.S deleted file mode 100644 index 8a6673dbe4bb..000000000000 --- a/arch/alpha/lib/ev6-strcpy.S +++ /dev/null @@ -1,23 +0,0 @@ -/* - * arch/alpha/lib/strcpy.S - * Contributed by Richard Henderson (rth@tamu.edu) - * - * Copy a null-terminated string from SRC to DST. Return a pointer - * to the null-terminator in the source. - */ - - .text - - .align 4 - .globl strcpy - .ent strcpy -strcpy: - .frame $30, 0, $26 - .prologue 0 - - mov $16, $0 # set up return value - mov $26, $23 # set up return address - br __stxcpy # do the copy - nop - - .end strcpy diff --git a/arch/alpha/lib/ev6-strncpy.S b/arch/alpha/lib/ev6-strncpy.S deleted file mode 100644 index 053146bf5c04..000000000000 --- a/arch/alpha/lib/ev6-strncpy.S +++ /dev/null @@ -1,36 +0,0 @@ -/* - * arch/alpha/lib/strncpy.S - * Contributed by Richard Henderson (rth@tamu.edu) - * - * Copy no more than COUNT bytes of the null-terminated string from - * SRC to DST. If SRC does not cover all of COUNT, the balance is - * zeroed. - * - * Or, rather, if the kernel cared about that weird ANSI quirk. This - * version has cropped that bit o' nastiness as well as assuming that - * __stxncpy is in range of a branch. - */ - - .set noat - .set noreorder - - .text - - .align 4 - .globl strncpy - .ent strncpy -strncpy: - .frame $30, 0, $26 - .prologue 0 - - mov $16, $0 # set return value now - beq $18, 0f - mov $26, $23 # set return address - br __stxncpy # do the work of the copy - -0: ret - nop - nop - nop - - .end strncpy diff --git a/arch/alpha/lib/ev6-stxcpy.S b/arch/alpha/lib/ev6-stxcpy.S new file mode 100644 index 000000000000..f5b409e9ad03 --- /dev/null +++ b/arch/alpha/lib/ev6-stxcpy.S @@ -0,0 +1,321 @@ +/* + * arch/alpha/lib/ev6-stxcpy.S + * 21264 version contributed by Rick Gorton + * + * Copy a null-terminated string from SRC to DST. + * + * This is an internal routine used by strcpy, stpcpy, and strcat. + * As such, it uses special linkage conventions to make implementation + * of these public functions more efficient. + * + * On input: + * t9 = return address + * a0 = DST + * a1 = SRC + * + * On output: + * t12 = bitmask (with one bit set) indicating the last byte written + * a0 = unaligned address of the last *word* written + * + * Furthermore, v0, a3-a5, t11, and t12 are untouched. + * + * Much of the information about 21264 scheduling/coding comes from: + * Compiler Writer's Guide for the Alpha 21264 + * abbreviated as 'CWG' in other comments here + * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html + * Scheduling notation: + * E - either cluster + * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 + * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 + * Try not to change the actual algorithm if possible for consistency. + */ + +#include + + .set noat + .set noreorder + + .text + +/* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that + doesn't like putting the entry point for a procedure somewhere in the + middle of the procedure descriptor. Work around this by putting the + aligned copy in its own procedure descriptor */ + + + .ent stxcpy_aligned + .align 4 +stxcpy_aligned: + .frame sp, 0, t9 + .prologue 0 + + /* On entry to this basic block: + t0 == the first destination word for masking back in + t1 == the first source word. */ + + /* Create the 1st output word and detect 0's in the 1st input word. */ + lda t2, -1 # E : build a mask against false zero + mskqh t2, a1, t2 # U : detection in the src word (stall) + mskqh t1, a1, t3 # U : + ornot t1, t2, t2 # E : (stall) + + mskql t0, a1, t0 # U : assemble the first output word + cmpbge zero, t2, t8 # E : bits set iff null found + or t0, t3, t1 # E : (stall) + bne t8, $a_eos # U : (stall) + + /* On entry to this basic block: + t0 == the first destination word for masking back in + t1 == a source word not containing a null. */ + /* Nops here to separate store quads from load quads */ + +$a_loop: + stq_u t1, 0(a0) # L : + addq a0, 8, a0 # E : + nop + nop + + ldq_u t1, 0(a1) # L : Latency=3 + addq a1, 8, a1 # E : + cmpbge zero, t1, t8 # E : (3 cycle stall) + beq t8, $a_loop # U : (stall for t8) + + /* Take care of the final (partial) word store. + On entry to this basic block we have: + t1 == the source word containing the null + t8 == the cmpbge mask that found it. */ +$a_eos: + negq t8, t6 # E : find low bit set + and t8, t6, t12 # E : (stall) + /* For the sake of the cache, don't read a destination word + if we're not going to need it. */ + and t12, 0x80, t6 # E : (stall) + bne t6, 1f # U : (stall) + + /* We're doing a partial word store and so need to combine + our source and original destination words. */ + ldq_u t0, 0(a0) # L : Latency=3 + subq t12, 1, t6 # E : + zapnot t1, t6, t1 # U : clear src bytes >= null (stall) + or t12, t6, t8 # E : (stall) + + zap t0, t8, t0 # E : clear dst bytes <= null + or t0, t1, t1 # E : (stall) + nop + nop + +1: stq_u t1, 0(a0) # L : + ret (t9) # L0 : Latency=3 + nop + nop + + .end stxcpy_aligned + + .align 4 + .ent __stxcpy + .globl __stxcpy +__stxcpy: + .frame sp, 0, t9 + .prologue 0 + + /* Are source and destination co-aligned? */ + xor a0, a1, t0 # E : + unop # E : + and t0, 7, t0 # E : (stall) + bne t0, $unaligned # U : (stall) + + /* We are co-aligned; take care of a partial first word. */ + ldq_u t1, 0(a1) # L : load first src word + and a0, 7, t0 # E : take care not to load a word ... + addq a1, 8, a1 # E : + beq t0, stxcpy_aligned # U : ... if we wont need it (stall) + + ldq_u t0, 0(a0) # L : + br stxcpy_aligned # L0 : Latency=3 + nop + nop + + +/* The source and destination are not co-aligned. Align the destination + and cope. We have to be very careful about not reading too much and + causing a SEGV. */ + + .align 4 +$u_head: + /* We know just enough now to be able to assemble the first + full source word. We can still find a zero at the end of it + that prevents us from outputting the whole thing. + + On entry to this basic block: + t0 == the first dest word, for masking back in, if needed else 0 + t1 == the low bits of the first source word + t6 == bytemask that is -1 in dest word bytes */ + + ldq_u t2, 8(a1) # L : + addq a1, 8, a1 # E : + extql t1, a1, t1 # U : (stall on a1) + extqh t2, a1, t4 # U : (stall on a1) + + mskql t0, a0, t0 # U : + or t1, t4, t1 # E : + mskqh t1, a0, t1 # U : (stall on t1) + or t0, t1, t1 # E : (stall on t1) + + or t1, t6, t6 # E : + cmpbge zero, t6, t8 # E : (stall) + lda t6, -1 # E : for masking just below + bne t8, $u_final # U : (stall) + + mskql t6, a1, t6 # U : mask out the bits we have + or t6, t2, t2 # E : already extracted before (stall) + cmpbge zero, t2, t8 # E : testing eos (stall) + bne t8, $u_late_head_exit # U : (stall) + + /* Finally, we've got all the stupid leading edge cases taken care + of and we can set up to enter the main loop. */ + + stq_u t1, 0(a0) # L : store first output word + addq a0, 8, a0 # E : + extql t2, a1, t0 # U : position ho-bits of lo word + ldq_u t2, 8(a1) # U : read next high-order source word + + addq a1, 8, a1 # E : + cmpbge zero, t2, t8 # E : (stall for t2) + nop # E : + bne t8, $u_eos # U : (stall) + + /* Unaligned copy main loop. In order to avoid reading too much, + the loop is structured to detect zeros in aligned source words. + This has, unfortunately, effectively pulled half of a loop + iteration out into the head and half into the tail, but it does + prevent nastiness from accumulating in the very thing we want + to run as fast as possible. + + On entry to this basic block: + t0 == the shifted high-order bits from the previous source word + t2 == the unshifted current source word + + We further know that t2 does not contain a null terminator. */ + + .align 3 +$u_loop: + extqh t2, a1, t1 # U : extract high bits for current word + addq a1, 8, a1 # E : (stall) + extql t2, a1, t3 # U : extract low bits for next time (stall) + addq a0, 8, a0 # E : + + or t0, t1, t1 # E : current dst word now complete + ldq_u t2, 0(a1) # L : Latency=3 load high word for next time + stq_u t1, -8(a0) # L : save the current word (stall) + mov t3, t0 # E : + + cmpbge zero, t2, t8 # E : test new word for eos + beq t8, $u_loop # U : (stall) + nop + nop + + /* We've found a zero somewhere in the source word we just read. + If it resides in the lower half, we have one (probably partial) + word to write out, and if it resides in the upper half, we + have one full and one partial word left to write out. + + On entry to this basic block: + t0 == the shifted high-order bits from the previous source word + t2 == the unshifted current source word. */ +$u_eos: + extqh t2, a1, t1 # U : + or t0, t1, t1 # E : first (partial) source word complete (stall) + cmpbge zero, t1, t8 # E : is the null in this first bit? (stall) + bne t8, $u_final # U : (stall) + +$u_late_head_exit: + stq_u t1, 0(a0) # L : the null was in the high-order bits + addq a0, 8, a0 # E : + extql t2, a1, t1 # U : + cmpbge zero, t1, t8 # E : (stall) + + /* Take care of a final (probably partial) result word. + On entry to this basic block: + t1 == assembled source word + t8 == cmpbge mask that found the null. */ +$u_final: + negq t8, t6 # E : isolate low bit set + and t6, t8, t12 # E : (stall) + and t12, 0x80, t6 # E : avoid dest word load if we can (stall) + bne t6, 1f # U : (stall) + + ldq_u t0, 0(a0) # E : + subq t12, 1, t6 # E : + or t6, t12, t8 # E : (stall) + zapnot t1, t6, t1 # U : kill source bytes >= null (stall) + + zap t0, t8, t0 # U : kill dest bytes <= null (2 cycle data stall) + or t0, t1, t1 # E : (stall) + nop + nop + +1: stq_u t1, 0(a0) # L : + ret (t9) # L0 : Latency=3 + nop + nop + + /* Unaligned copy entry point. */ + .align 4 +$unaligned: + + ldq_u t1, 0(a1) # L : load first source word + and a0, 7, t4 # E : find dest misalignment + and a1, 7, t5 # E : find src misalignment + /* Conditionally load the first destination word and a bytemask + with 0xff indicating that the destination byte is sacrosanct. */ + mov zero, t0 # E : + + mov zero, t6 # E : + beq t4, 1f # U : + ldq_u t0, 0(a0) # L : + lda t6, -1 # E : + + mskql t6, a0, t6 # U : + nop + nop + nop +1: + subq a1, t4, a1 # E : sub dest misalignment from src addr + /* If source misalignment is larger than dest misalignment, we need + extra startup checks to avoid SEGV. */ + cmplt t4, t5, t12 # E : + beq t12, $u_head # U : + lda t2, -1 # E : mask out leading garbage in source + + mskqh t2, t5, t2 # U : + ornot t1, t2, t3 # E : (stall) + cmpbge zero, t3, t8 # E : is there a zero? (stall) + beq t8, $u_head # U : (stall) + + /* At this point we've found a zero in the first partial word of + the source. We need to isolate the valid source data and mask + it into the original destination data. (Incidentally, we know + that we'll need at least one byte of that original dest word.) */ + + ldq_u t0, 0(a0) # L : + negq t8, t6 # E : build bitmask of bytes <= zero + and t6, t8, t12 # E : (stall) + and a1, 7, t5 # E : + + subq t12, 1, t6 # E : + or t6, t12, t8 # E : (stall) + srl t12, t5, t12 # U : adjust final null return value + zapnot t2, t8, t2 # U : prepare source word; mirror changes (stall) + + and t1, t2, t1 # E : to source validity mask + extql t2, a1, t2 # U : + extql t1, a1, t1 # U : (stall) + andnot t0, t2, t0 # .. e1 : zero place for source to reside (stall) + + or t0, t1, t1 # e1 : and put it there + stq_u t1, 0(a0) # .. e0 : (stall) + ret (t9) # e1 : + nop + + .end __stxcpy + diff --git a/arch/alpha/lib/ev6-stxncpy.S b/arch/alpha/lib/ev6-stxncpy.S new file mode 100644 index 000000000000..7848b4c8076a --- /dev/null +++ b/arch/alpha/lib/ev6-stxncpy.S @@ -0,0 +1,399 @@ +/* + * arch/alpha/lib/ev6-stxncpy.S + * 21264 version contributed by Rick Gorton + * + * Copy no more than COUNT bytes of the null-terminated string from + * SRC to DST. + * + * This is an internal routine used by strncpy, stpncpy, and strncat. + * As such, it uses special linkage conventions to make implementation + * of these public functions more efficient. + * + * On input: + * t9 = return address + * a0 = DST + * a1 = SRC + * a2 = COUNT + * + * Furthermore, COUNT may not be zero. + * + * On output: + * t0 = last word written + * t10 = bitmask (with one bit set) indicating the byte position of + * the end of the range specified by COUNT + * t12 = bitmask (with one bit set) indicating the last byte written + * a0 = unaligned address of the last *word* written + * a2 = the number of full words left in COUNT + * + * Furthermore, v0, a3-a5, t11, t12, and $at are untouched. + * + * Much of the information about 21264 scheduling/coding comes from: + * Compiler Writer's Guide for the Alpha 21264 + * abbreviated as 'CWG' in other comments here + * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html + * Scheduling notation: + * E - either cluster + * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 + * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 + * Try not to change the actual algorithm if possible for consistency. + */ + +#include + + .set noat + .set noreorder + + .text + +/* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that + doesn't like putting the entry point for a procedure somewhere in the + middle of the procedure descriptor. Work around this by putting the + aligned copy in its own procedure descriptor */ + + + .ent stxncpy_aligned + .align 4 +stxncpy_aligned: + .frame sp, 0, t9, 0 + .prologue 0 + + /* On entry to this basic block: + t0 == the first destination word for masking back in + t1 == the first source word. */ + + /* Create the 1st output word and detect 0's in the 1st input word. */ + lda t2, -1 # E : build a mask against false zero + mskqh t2, a1, t2 # U : detection in the src word (stall) + mskqh t1, a1, t3 # U : + ornot t1, t2, t2 # E : (stall) + + mskql t0, a1, t0 # U : assemble the first output word + cmpbge zero, t2, t8 # E : bits set iff null found + or t0, t3, t0 # E : (stall) + beq a2, $a_eoc # U : + + bne t8, $a_eos # U : + nop + nop + nop + + /* On entry to this basic block: + t0 == a source word not containing a null. */ + + /* + * nops here to: + * separate store quads from load quads + * limit of 1 bcond/quad to permit training + */ +$a_loop: + stq_u t0, 0(a0) # L : + addq a0, 8, a0 # E : + subq a2, 1, a2 # E : + nop + + ldq_u t0, 0(a1) # L : + addq a1, 8, a1 # E : + cmpbge zero, t0, t8 # E : + beq a2, $a_eoc # U : + + beq t8, $a_loop # U : + nop + nop + nop + + /* Take care of the final (partial) word store. At this point + the end-of-count bit is set in t8 iff it applies. + + On entry to this basic block we have: + t0 == the source word containing the null + t8 == the cmpbge mask that found it. */ + +$a_eos: + negq t8, t12 # E : find low bit set + and t8, t12, t12 # E : (stall) + /* For the sake of the cache, don't read a destination word + if we're not going to need it. */ + and t12, 0x80, t6 # E : (stall) + bne t6, 1f # U : (stall) + + /* We're doing a partial word store and so need to combine + our source and original destination words. */ + ldq_u t1, 0(a0) # L : + subq t12, 1, t6 # E : + or t12, t6, t8 # E : (stall) + zapnot t0, t8, t0 # U : clear src bytes > null (stall) + + zap t1, t8, t1 # .. e1 : clear dst bytes <= null + or t0, t1, t0 # e1 : (stall) + nop + nop + +1: stq_u t0, 0(a0) # L : + ret (t9) # L0 : Latency=3 + nop + nop + + /* Add the end-of-count bit to the eos detection bitmask. */ +$a_eoc: + or t10, t8, t8 # E : + br $a_eos # L0 : Latency=3 + nop + nop + + .end stxncpy_aligned + + .align 4 + .ent __stxncpy + .globl __stxncpy +__stxncpy: + .frame sp, 0, t9, 0 + .prologue 0 + + /* Are source and destination co-aligned? */ + xor a0, a1, t1 # E : + and a0, 7, t0 # E : find dest misalignment + and t1, 7, t1 # E : (stall) + addq a2, t0, a2 # E : bias count by dest misalignment (stall) + + subq a2, 1, a2 # E : + and a2, 7, t2 # E : (stall) + srl a2, 3, a2 # U : a2 = loop counter = (count - 1)/8 (stall) + addq zero, 1, t10 # E : + + sll t10, t2, t10 # U : t10 = bitmask of last count byte + bne t1, $unaligned # U : + /* We are co-aligned; take care of a partial first word. */ + ldq_u t1, 0(a1) # L : load first src word + addq a1, 8, a1 # E : + + beq t0, stxncpy_aligned # U : avoid loading dest word if not needed + ldq_u t0, 0(a0) # L : + nop + nop + + br stxncpy_aligned # .. e1 : + nop + nop + nop + + + +/* The source and destination are not co-aligned. Align the destination + and cope. We have to be very careful about not reading too much and + causing a SEGV. */ + + .align 4 +$u_head: + /* We know just enough now to be able to assemble the first + full source word. We can still find a zero at the end of it + that prevents us from outputting the whole thing. + + On entry to this basic block: + t0 == the first dest word, unmasked + t1 == the shifted low bits of the first source word + t6 == bytemask that is -1 in dest word bytes */ + + ldq_u t2, 8(a1) # L : Latency=3 load second src word + addq a1, 8, a1 # E : + mskql t0, a0, t0 # U : mask trailing garbage in dst + extqh t2, a1, t4 # U : (3 cycle stall on t2) + + or t1, t4, t1 # E : first aligned src word complete (stall) + mskqh t1, a0, t1 # U : mask leading garbage in src (stall) + or t0, t1, t0 # E : first output word complete (stall) + or t0, t6, t6 # E : mask original data for zero test (stall) + + cmpbge zero, t6, t8 # E : + beq a2, $u_eocfin # U : + nop + nop + + bne t8, $u_final # U : + lda t6, -1 # E : mask out the bits we have + mskql t6, a1, t6 # U : already seen (stall) + stq_u t0, 0(a0) # L : store first output word + + or t6, t2, t2 # E : + cmpbge zero, t2, t8 # E : find nulls in second partial (stall) + addq a0, 8, a0 # E : + subq a2, 1, a2 # E : + + bne t8, $u_late_head_exit # U : + /* Finally, we've got all the stupid leading edge cases taken care + of and we can set up to enter the main loop. */ + extql t2, a1, t1 # U : position hi-bits of lo word + ldq_u t2, 8(a1) # L : read next high-order source word + addq a1, 8, a1 # E : + + cmpbge zero, t2, t8 # E : (stall) + beq a2, $u_eoc # U : + nop + nop + + bne t8, $u_eos # e1 : + nop + nop + nop + + /* Unaligned copy main loop. In order to avoid reading too much, + the loop is structured to detect zeros in aligned source words. + This has, unfortunately, effectively pulled half of a loop + iteration out into the head and half into the tail, but it does + prevent nastiness from accumulating in the very thing we want + to run as fast as possible. + + On entry to this basic block: + t1 == the shifted high-order bits from the previous source word + t2 == the unshifted current source word + + We further know that t2 does not contain a null terminator. */ + + .align 4 +$u_loop: + extqh t2, a1, t0 # U : extract high bits for current word + addq a1, 8, a1 # E : + extql t2, a1, t3 # U : extract low bits for next time + addq a0, 8, a0 # E : + + or t0, t1, t0 # E : current dst word now complete + ldq_u t2, 0(a1) # U : Latency=3 load high word for next time + stq_u t0, -8(a0) # U : save the current word (stall) + mov t3, t1 # E : + + subq a2, 1, a2 # E : + cmpbge zero, t2, t8 # E : test new word for eos (2 cycle stall for data) + beq a2, $u_eoc # U : (stall) + nop + + beq t8, $u_loop # U : + nop + nop + nop + + /* We've found a zero somewhere in the source word we just read. + If it resides in the lower half, we have one (probably partial) + word to write out, and if it resides in the upper half, we + have one full and one partial word left to write out. + + On entry to this basic block: + t1 == the shifted high-order bits from the previous source word + t2 == the unshifted current source word. */ +$u_eos: + extqh t2, a1, t0 # U : + or t0, t1, t0 # E : first (partial) source word complete (stall) + cmpbge zero, t0, t8 # E : is the null in this first bit? (stall) + bne t8, $u_final # U : (stall) + + stq_u t0, 0(a0) # L : the null was in the high-order bits + addq a0, 8, a0 # E : + subq a2, 1, a2 # E : + nop + +$u_late_head_exit: + extql t2, a1, t0 # U : + cmpbge zero, t0, t8 # E : + or t8, t10, t6 # E : (stall) + cmoveq a2, t6, t8 # E : Latency=2, extra map slot (stall) + + /* Take care of a final (probably partial) result word. + On entry to this basic block: + t0 == assembled source word + t8 == cmpbge mask that found the null. */ +$u_final: + negq t8, t6 # E : isolate low bit set + and t6, t8, t12 # E : (stall) + and t12, 0x80, t6 # E : avoid dest word load if we can (stall) + bne t6, 1f # U : (stall) + + ldq_u t1, 0(a0) # L : + subq t12, 1, t6 # E : + or t6, t12, t8 # E : (stall) + zapnot t0, t8, t0 # U : kill source bytes > null + + zap t1, t8, t1 # U : kill dest bytes <= null + or t0, t1, t0 # E : (stall) + nop + nop + +1: stq_u t0, 0(a0) # L : + ret (t9) # L0 : Latency=3 + +$u_eoc: # end-of-count + extqh t2, a1, t0 # U : + or t0, t1, t0 # E : (stall) + cmpbge zero, t0, t8 # E : (stall) + nop + +$u_eocfin: # end-of-count, final word + or t10, t8, t8 # E : + br $u_final # L0 : Latency=3 + nop + nop + + /* Unaligned copy entry point. */ + .align 4 +$unaligned: + + ldq_u t1, 0(a1) # L : load first source word + and a0, 7, t4 # E : find dest misalignment + and a1, 7, t5 # E : find src misalignment + /* Conditionally load the first destination word and a bytemask + with 0xff indicating that the destination byte is sacrosanct. */ + mov zero, t0 # E : + + mov zero, t6 # E : + beq t4, 1f # U : + ldq_u t0, 0(a0) # L : + lda t6, -1 # E : + + mskql t6, a0, t6 # U : + nop + nop + nop +1: + subq a1, t4, a1 # E : sub dest misalignment from src addr + + /* If source misalignment is larger than dest misalignment, we need + extra startup checks to avoid SEGV. */ + + cmplt t4, t5, t12 # E : + extql t1, a1, t1 # U : shift src into place + lda t2, -1 # E : for creating masks later + beq t12, $u_head # U : (stall) + + mskqh t2, t5, t2 # U : begin src byte validity mask + cmpbge zero, t1, t8 # E : is there a zero? + extql t2, a1, t2 # U : + or t8, t10, t5 # E : test for end-of-count too + + cmpbge zero, t2, t3 # E : + cmoveq a2, t5, t8 # E : Latency=2, extra map slot + nop # E : keep with cmoveq + andnot t8, t3, t8 # E : (stall) + + beq t8, $u_head # U : + /* At this point we've found a zero in the first partial word of + the source. We need to isolate the valid source data and mask + it into the original destination data. (Incidentally, we know + that we'll need at least one byte of that original dest word.) */ + ldq_u t0, 0(a0) # L : + negq t8, t6 # E : build bitmask of bytes <= zero + mskqh t1, t4, t1 # U : + + and t6, t8, t12 # E : + subq t12, 1, t6 # E : (stall) + or t6, t12, t8 # E : (stall) + zapnot t2, t8, t2 # U : prepare source word; mirror changes (stall) + + zapnot t1, t8, t1 # U : to source validity mask + andnot t0, t2, t0 # E : zero place for source to reside + or t0, t1, t0 # E : and put it there (stall both t0, t1) + stq_u t0, 0(a0) # L : (stall) + + ret (t9) # L0 : Latency=3 + nop + nop + nop + + .end __stxncpy + diff --git a/arch/alpha/lib/ev67-strchr.S b/arch/alpha/lib/ev67-strchr.S index b817f82a0c73..03c56d583bd6 100644 --- a/arch/alpha/lib/ev67-strchr.S +++ b/arch/alpha/lib/ev67-strchr.S @@ -55,9 +55,9 @@ strchr: or t2, t3, t0 # E : bits set iff char match or zero match andnot t0, t4, t0 # E : clear garbage bits - cttz t3, a2 # U0 : speculative (in case we get a match) + cttz t0, a2 # U0 : speculative (in case we get a match) nop # E : - bne t0, $found # U : Stall on t0 + bne t0, $found # U : /* * Yuk. This loop is going to stall like crazy waiting for the diff --git a/arch/alpha/lib/memcpy.c b/arch/alpha/lib/memcpy.c index d715f02195c3..64083fc73238 100644 --- a/arch/alpha/lib/memcpy.c +++ b/arch/alpha/lib/memcpy.c @@ -161,24 +161,3 @@ void * memcpy(void * dest, const void *src, size_t n) /* For backward modules compatibility, define __memcpy. */ asm("__memcpy = memcpy; .globl __memcpy"); - -void *memmove (void *dest, const void *src, size_t n) -{ - if (dest <= src) { - if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) - __memcpy_aligned_up ((unsigned long) dest, - (unsigned long) src, n); - else - __memcpy_unaligned_up ((unsigned long) dest, - (unsigned long) src, n); - } - else { - if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) - __memcpy_aligned_dn ((unsigned long) dest, - (unsigned long) src, n); - else - __memcpy_unaligned_dn ((unsigned long) dest, - (unsigned long) src, n); - } - return dest; -} diff --git a/arch/alpha/lib/memmove.S b/arch/alpha/lib/memmove.S new file mode 100644 index 000000000000..3c8567e4e58e --- /dev/null +++ b/arch/alpha/lib/memmove.S @@ -0,0 +1,103 @@ +/* + * arch/alpha/lib/memmove.S + * + * Barely optimized memmove routine for Alpha EV5. + * + * This is hand-massaged output from the original memcpy.c. We defer to + * memcpy whenever possible; the backwards copy loops are not unrolled. + */ + + .set noat + .set noreorder + .text + + .align 4 + .globl memmove + .ent memmove +memmove: + addq $16,$18,$4 + addq $17,$18,$5 + cmpule $4,$17,$1 /* dest + n <= src */ + cmpule $5,$16,$2 /* dest >= src + n */ + + bis $1,$2,$1 + mov $16,$0 + xor $16,$17,$2 + bne $1,memcpy + + and $2,7,$2 /* Test for src/dest co-alignment. */ + bne $2,$misaligned + + and $4,7,$1 + beq $1,$skip_aligned_byte_loop_head + +$aligned_byte_loop_head: + lda $4,-1($4) + lda $5,-1($5) + unop + ble $18,$egress + + ldq_u $3,0($5) + ldq_u $2,0($4) + lda $18,-1($18) + extbl $3,$5,$1 + + insbl $1,$4,$1 + mskbl $2,$4,$2 + bis $1,$2,$1 + and $4,7,$6 + + stq_u $1,0($4) + bne $6,$aligned_byte_loop_head + +$skip_aligned_byte_loop_head: + lda $18,-8($18) + blt $18,$skip_aligned_word_loop + +$aligned_word_loop: + ldq $1,-8($5) + nop + lda $5,-8($5) + lda $18,-8($18) + + stq $1,-8($4) + nop + lda $4,-8($4) + bge $18,$aligned_word_loop + +$skip_aligned_word_loop: + lda $18,8($18) + bgt $18,$byte_loop_tail + unop + ret $31,($26),1 + + .align 4 +$misaligned: + nop + fnop + unop + beq $18,$egress + +$byte_loop_tail: + ldq_u $3,-1($5) + ldq_u $2,-1($4) + lda $5,-1($5) + lda $4,-1($4) + + lda $18,-1($18) + extbl $3,$5,$1 + insbl $1,$4,$1 + mskbl $2,$4,$2 + + bis $1,$2,$1 + stq_u $1,0($4) + nop + bgt $18,$byte_loop_tail + +$egress: + ret $31,($26),1 + nop + nop + nop + + .end memmove diff --git a/arch/alpha/lib/strcpy.S b/arch/alpha/lib/strcpy.S index dd21b5208f27..e0728e4ad21f 100644 --- a/arch/alpha/lib/strcpy.S +++ b/arch/alpha/lib/strcpy.S @@ -17,6 +17,7 @@ strcpy: mov $16, $0 # set up return value mov $26, $23 # set up return address + unop br __stxcpy # do the copy .end strcpy diff --git a/arch/alpha/lib/strncpy.S b/arch/alpha/lib/strncpy.S index dbc011c34a30..7d64d21d5af3 100644 --- a/arch/alpha/lib/strncpy.S +++ b/arch/alpha/lib/strncpy.S @@ -27,6 +27,10 @@ strncpy: beq $18, 0f mov $26, $23 # set return address br __stxncpy # do the work of the copy + 0: ret + nop + nop + nop .end strncpy diff --git a/arch/alpha/lib/stxcpy.S b/arch/alpha/lib/stxcpy.S index b7b9da4829b6..5a427634d825 100644 --- a/arch/alpha/lib/stxcpy.S +++ b/arch/alpha/lib/stxcpy.S @@ -1,4 +1,5 @@ -/* stxcpy.S +/* + * arch/alpha/lib/stxcpy.S * Contributed by Richard Henderson (rth@tamu.edu) * * Copy a null-terminated string from SRC to DST. diff --git a/arch/alpha/lib/stxncpy.S b/arch/alpha/lib/stxncpy.S index 5bab622e0134..d7cddb074fb2 100644 --- a/arch/alpha/lib/stxncpy.S +++ b/arch/alpha/lib/stxncpy.S @@ -1,4 +1,5 @@ -/* stxncpy.S +/* + * arch/alpha/lib/stxncpy.S * Contributed by Richard Henderson (rth@tamu.edu) * * Copy no more than COUNT bytes of the null-terminated string from diff --git a/arch/i386/config.in b/arch/i386/config.in index d6c63a8b83e7..87fc106f87a7 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -140,7 +140,7 @@ if [ "$CONFIG_MWINCHIP3D" = "y" ]; then fi tristate 'Toshiba Laptop support' CONFIG_TOSHIBA -tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE +tristate '/dev/cpu/microcode - Intel IA32 CPU microcode support' CONFIG_MICROCODE tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c new file mode 100644 index 000000000000..b439568c1f97 --- /dev/null +++ b/arch/i386/kernel/dmi_scan.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include +#include +#include + +struct dmi_header +{ + u8 type; + u8 length; + u16 handle; +}; + +static char * __init dmi_string(struct dmi_header *dm, u8 s) +{ + u8 *bp=(u8 *)dm; + bp+=dm->length; + s--; + while(s>0) + { + bp+=strlen(bp); + bp++; + s--; + } + return bp; +} + +static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *)) +{ + u8 *buf; + struct dmi_header *dm; + u8 *data; + int i=1; + int last = 0; + + buf = ioremap(base, len); + if(buf==NULL) + return -1; + + data = buf; + while(itype < last) + break; + last = dm->type; + decode(dm); + data+=dm->length; + while(*data || data[1]) + data++; + data+=2; + i++; + } + iounmap(buf); + return 0; +} + + +int __init dmi_iterate(void (*decode)(struct dmi_header *)) +{ + unsigned char buf[20]; + long fp=0xE0000L; + fp -= 16; + + while( fp < 0xFFFFF) + { + fp+=16; + isa_memcpy_fromio(buf, fp, 20); + if(memcmp(buf, "_DMI_", 5)==0) + { + u16 num=buf[13]<<8|buf[12]; + u16 len=buf[7]<<8|buf[6]; + u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]; + + printk(KERN_INFO "DMI %d.%d present.\n", + buf[14]>>4, buf[14]&0x0F); + printk(KERN_INFO "%d structures occupying %d bytes.\n", + buf[13]<<8|buf[12], + buf[7]<<8|buf[6]); + printk(KERN_INFO "DMI table at 0x%08X.\n", + buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]); + if(dmi_table(base,len, num, decode)==0) + return 0; + } + } + return -1; +} + + +/* + * Process a DMI table entry. Right now all we care about are the BIOS + * and machine entries. For 2.4 we should pull the smbus controller info + * out of here. + */ + +static void __init dmi_decode(struct dmi_header *dm) +{ + u8 *data = (u8 *)dm; + char *p; + + switch(dm->type) + { + case 0: + p=dmi_string(dm,data[4]); + + if(*p && *p!=' ') + { + printk("BIOS Vendor: %s\n", p); + printk("BIOS Version: %s\n", + dmi_string(dm, data[5])); + printk("BIOS Release: %s\n", + dmi_string(dm, data[8])); + } + + /* + * Check for clue free BIOS implementations who use + * the following QA technique + * + * [ Write BIOS Code ]<------ + * | ^ + * < Does it Compile >----N-- + * |Y ^ + * < Does it Boot Win98 >-N-- + * |Y + * [Ship It] + * + * Phoenix A04 08/24/2000 is known bad (Dell Inspiron 5000e) + * Phoenix A07 09/29/2000 is known good (Dell Inspiron 5000) + */ + + if(strcmp(dmi_string(dm, data[4]), "Phoenix Technologies LTD")==0) + { + if(strcmp(dmi_string(dm, data[5]), "A04")==0 + && strcmp(dmi_string(dm, data[8]), "08/24/2000")==0) + { + apm_info.get_power_status_broken = 1; + printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n"); + } + } + break; + case 1: + p=dmi_string(dm,data[4]); + + if(*p && *p!=' ') + { + printk("System Vendor: %s.\n",p); + printk("Product Name: %s.\n", + dmi_string(dm, data[5])); + printk("Version %s.\n", + dmi_string(dm, data[6])); + printk("Serial Number %s.\n", + dmi_string(dm, data[7])); + } + break; + case 2: + p=dmi_string(dm,data[4]); + + if(*p && *p!=' ') + { + printk("Board Vendor: %s.\n",p); + printk("Board Name: %s.\n", + dmi_string(dm, data[5])); + printk("Board Version: %s.\n", + dmi_string(dm, data[6])); + } + break; + case 3: + p=dmi_string(dm,data[8]); + if(*p && *p!=' ') + printk("Asset Tag: %s.\n", p); + break; + } +} + +static int __init dmi_scan_machine(void) +{ + return dmi_iterate(dmi_decode); +} + +module_init(dmi_scan_machine); diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index 5f99254952da..f0a88c20e7f3 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -4,13 +4,13 @@ * Copyright (C) 2000 Tigran Aivazian * * This driver allows to upgrade microcode on Intel processors - * belonging to P6 family - PentiumPro, Pentium II, - * Pentium III, Xeon etc. + * belonging to IA-32 family - PentiumPro, Pentium II, + * Pentium III, Xeon, Pentium 4, etc. * - * Reference: Section 8.10 of Volume III, Intel Pentium III Manual, - * Order Number 243192 or free download from: + * Reference: Section 8.10 of Volume III, Intel Pentium 4 Manual, + * Order Number 245472 or free download from: * - * http://developer.intel.com/design/pentiumii/manuals/243192.htm + * http://developer.intel.com/design/pentium4/manuals/245472.htm * * For more information, go to http://www.urbanmyth.org/microcode * @@ -44,6 +44,9 @@ * to be 0 on my machine which is why it worked even when I * disabled update by the BIOS) * Thanks to Eric W. Biederman for the fix. + * 1.08 11 Dec 2000, Richard Schaal and + * Tigran Aivazian + * Intel Pentium 4 processor support and bugfixes. */ #include @@ -58,12 +61,20 @@ #include #include -#define MICROCODE_VERSION "1.07" +#define MICROCODE_VERSION "1.08" -MODULE_DESCRIPTION("Intel CPU (P6) microcode update driver"); +MODULE_DESCRIPTION("Intel CPU (IA-32) microcode update driver"); MODULE_AUTHOR("Tigran Aivazian "); EXPORT_NO_SYMBOLS; +#define MICRO_DEBUG 0 + +#if MICRO_DEBUG +#define printf(x...) printk(##x) +#else +#define printf(x...) +#endif + /* VFS interface */ static int microcode_open(struct inode *, struct file *); static ssize_t microcode_read(struct file *, char *, size_t, loff_t *); @@ -81,6 +92,7 @@ static unsigned int microcode_num; /* number of chunks in microcode */ static char *mc_applied; /* array of applied microcode blocks */ static unsigned int mc_fsize; /* file size of /dev/cpu/microcode */ +/* we share file_operations between misc and devfs mechanisms */ static struct file_operations microcode_fops = { owner: THIS_MODULE, read: microcode_read, @@ -99,23 +111,27 @@ static devfs_handle_t devfs_handle; static int __init microcode_init(void) { - int error = 0; + int error; - if (misc_register(µcode_dev) < 0) { + error = misc_register(µcode_dev); + if (error) printk(KERN_WARNING "microcode: can't misc_register on minor=%d\n", MICROCODE_MINOR); - error = 1; - } + devfs_handle = devfs_register(NULL, "cpu/microcode", DEVFS_FL_DEFAULT, 0, 0, S_IFREG | S_IRUSR | S_IWUSR, µcode_fops, NULL); if (devfs_handle == NULL && error) { printk(KERN_ERR "microcode: failed to devfs_register()\n"); - return -EINVAL; + goto out; } - printk(KERN_INFO "P6 Microcode Update Driver v%s\n", MICROCODE_VERSION); - return 0; + printk(KERN_INFO + "IA-32 Microcode Update Driver: v%s \n", + MICROCODE_VERSION); + +out: + return error; } static void __exit microcode_exit(void) @@ -124,12 +140,12 @@ static void __exit microcode_exit(void) devfs_unregister(devfs_handle); if (mc_applied) kfree(mc_applied); - printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n", + printk(KERN_INFO "IA-32 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION); } -module_init(microcode_init); -module_exit(microcode_exit); +module_init(microcode_init) +module_exit(microcode_exit) static int microcode_open(struct inode *unused1, struct file *unused2) { @@ -175,18 +191,19 @@ static void do_update_one(void *unused) unsigned int pf = 0, val[2], rev, sig; int i,found=0; - req->err = 1; /* assume the worst */ + req->err = 1; /* assume update will fail on this cpu */ - if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 != 6){ - printk(KERN_ERR "microcode: CPU%d not an Intel P6\n", cpu_num); + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || + test_bit(X86_FEATURE_IA64, &c->x86_capability)){ + printk(KERN_ERR "microcode: CPU%d not a capable Intel processor\n", cpu_num); return; } sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8); - if (c->x86_model >= 5) { - /* get processor flags from BBL_CR_OVRD MSR (0x17) */ - rdmsr(0x17, val[0], val[1]); + if ((c->x86_model >= 5) || (c->x86 > 6)) { + /* get processor flags from MSR 0x17 */ + rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); pf = 1 << ((val[1] >> 18) & 7); } @@ -195,9 +212,28 @@ static void do_update_one(void *unused) microcode[i].ldrver == 1 && microcode[i].hdrver == 1) { found=1; - wrmsr(0x8B, 0, 0); + + printf("Microcode\n"); + printf(" Header Revision %d\n",microcode[i].hdrver); + printf(" Date %x/%x/%x\n", + ((microcode[i].date >> 24 ) & 0xff), + ((microcode[i].date >> 16 ) & 0xff), + (microcode[i].date & 0xFFFF)); + printf(" Type %x Family %x Model %x Stepping %x\n", + ((microcode[i].sig >> 12) & 0x3), + ((microcode[i].sig >> 8) & 0xf), + ((microcode[i].sig >> 4) & 0xf), + ((microcode[i].sig & 0xf))); + printf(" Checksum %x\n",microcode[i].cksum); + printf(" Loader Revision %x\n",microcode[i].ldrver); + printf(" Processor Flags %x\n\n",microcode[i].pf); + + /* trick, to work even if there was no prior update by the BIOS */ + wrmsr(MSR_IA32_UCODE_REV, 0, 0); __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx"); - rdmsr(0x8B, val[0], rev); + + /* get current (on-cpu) revision into rev (ignore val[0]) */ + rdmsr(MSR_IA32_UCODE_REV, val[0], rev); if (microcode[i].rev < rev) { printk(KERN_ERR "microcode: CPU%d not 'upgrading' to earlier revision" @@ -219,13 +255,20 @@ static void do_update_one(void *unused) break; } - wrmsr(0x79, (unsigned int)(m->bits), 0); + /* write microcode via MSR 0x79 */ + wrmsr(MSR_IA32_UCODE_WRITE, (unsigned int)(m->bits), 0); + + /* serialize */ __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx"); - rdmsr(0x8B, val[0], val[1]); + /* get the current revision from MSR 0x8B */ + rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); + + /* notify the caller of success on this cpu */ req->err = 0; req->slot = i; - printk(KERN_ERR "microcode: CPU%d updated from revision " + + printk(KERN_INFO "microcode: CPU%d updated from revision " "%d to %d, date=%08x\n", cpu_num, rev, val[1], m->date); } @@ -239,18 +282,21 @@ static void do_update_one(void *unused) static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos) { - if (*ppos >= mc_fsize) - return 0; + ssize_t ret = 0; + down_read(µcode_rwsem); + if (*ppos >= mc_fsize) + goto out; if (*ppos + len > mc_fsize) len = mc_fsize - *ppos; - if (copy_to_user(buf, mc_applied + *ppos, len)) { - up_read(µcode_rwsem); - return -EFAULT; - } + ret = -EFAULT; + if (copy_to_user(buf, mc_applied + *ppos, len)) + goto out; *ppos += len; + ret = len; +out: up_read(µcode_rwsem); - return len; + return ret; } static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos) @@ -267,8 +313,8 @@ static ssize_t microcode_write(struct file *file, const char *buf, size_t len, l mc_applied = kmalloc(smp_num_cpus*sizeof(struct microcode), GFP_KERNEL); if (!mc_applied) { - printk(KERN_ERR "microcode: out of memory for saved microcode\n"); up_write(µcode_rwsem); + printk(KERN_ERR "microcode: out of memory for saved microcode\n"); return -ENOMEM; } } @@ -307,10 +353,12 @@ static int microcode_ioctl(struct inode *inode, struct file *file, case MICROCODE_IOCFREE: down_write(µcode_rwsem); if (mc_applied) { + int bytes = smp_num_cpus * sizeof(struct microcode); + devfs_set_file_size(devfs_handle, 0); kfree(mc_applied); mc_applied = NULL; - printk(KERN_INFO "microcode: freed %d bytes\n", mc_fsize); + printk(KERN_INFO "microcode: freed %d bytes\n", bytes); mc_fsize = 0; up_write(µcode_rwsem); return 0; diff --git a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c index 58984fd080bc..cdf4fc827488 100644 --- a/arch/i386/kernel/pci-irq.c +++ b/arch/i386/kernel/pci-irq.c @@ -298,6 +298,33 @@ static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, i return 1; } +/* + * VLSI: nibble offset 0x74 - educated guess due to routing table and + * config space of VLSI 82C534 PCI-bridge/router (1004:0102) + * Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard + * devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6 + * for the busbridge to the docking station. + */ + +static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + if (pirq > 8) { + printk("VLSI router pirq escape (%d)\n", pirq); + return 0; + } + return read_config_nybble(router, 0x74, pirq-1); +} + +static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + if (pirq > 8) { + printk("VLSI router pirq escape (%d)\n", pirq); + return 0; + } + write_config_nybble(router, 0x74, pirq-1, irq); + return 1; +} + #ifdef CONFIG_PCI_BIOS static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) @@ -329,6 +356,7 @@ static struct irq_router pirq_routers[] = { { "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set }, { "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set }, + { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set }, { "default", 0, 0, NULL, NULL } }; @@ -374,7 +402,7 @@ static void __init pirq_find_router(void) pirq_router_dev->slot_name); } -static struct irq_info *pirq_get_info(struct pci_dev *dev, int pin) +static struct irq_info *pirq_get_info(struct pci_dev *dev) { struct irq_routing_table *rt = pirq_table; int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); @@ -392,25 +420,28 @@ static void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs static int pcibios_lookup_irq(struct pci_dev *dev, int assign) { + u8 pin; struct irq_info *info; - int i, pirq, pin, newirq; + int i, pirq, newirq; int irq = 0; u32 mask; struct irq_router *r = pirq_router; - struct pci_dev *dev2, *d; + struct pci_dev *dev2; char *msg = NULL; if (!pirq_table) return 0; /* Find IRQ routing entry */ - pin = pci_get_interrupt_pin(dev, &d); - if (pin < 0) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) { DBG(" -> no interrupt pin\n"); return 0; } - DBG("IRQ for %s(%d) via %s", dev->slot_name, pin, d->slot_name); - info = pirq_get_info(d, pin); + pin = pin - 1; + + DBG("IRQ for %s:%d", dev->slot_name, pin); + info = pirq_get_info(dev); if (!info) { DBG(" -> not found in routing table\n"); return 0; @@ -443,7 +474,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) DBG(" -> newirq=%d", newirq); /* Try to get current IRQ */ - if (r->get && (irq = r->get(pirq_router_dev, d, pirq))) { + if (r->get && (irq = r->get(pirq_router_dev, dev, pirq))) { DBG(" -> got IRQ %d\n", irq); msg = "Found"; /* We refuse to override the dev->irq information. Give a warning! */ @@ -453,7 +484,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) } } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { DBG(" -> assigning IRQ %d", newirq); - if (r->set(pirq_router_dev, d, pirq, newirq)) { + if (r->set(pirq_router_dev, dev, pirq, newirq)) { eisa_set_level_irq(newirq); DBG(" ... OK\n"); msg = "Assigned"; @@ -473,9 +504,14 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) /* Update IRQ for all devices with the same pirq value */ pci_for_each_dev(dev2) { - if ((pin = pci_get_interrupt_pin(dev2, &d)) >= 0 && - (info = pirq_get_info(d, pin)) && - info->irq[pin].link == pirq) { + pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin); + if (!pin) + continue; + pin--; + info = pirq_get_info(dev2); + if (!info) + continue; + if (info->irq[pin].link == pirq) { dev2->irq = irq; pirq_penalty[irq]++; if (dev != dev2) diff --git a/arch/sparc/boot/piggyback.c b/arch/sparc/boot/piggyback.c index 04049be7aa56..6962cc68ed5b 100644 --- a/arch/sparc/boot/piggyback.c +++ b/arch/sparc/boot/piggyback.c @@ -1,9 +1,10 @@ -/* $Id: piggyback.c,v 1.3 2000/03/11 00:22:26 zaitcev Exp $ +/* $Id: piggyback.c,v 1.4 2000/12/05 00:48:57 anton Exp $ Simple utility to make a single-image install kernel with initial ramdisk for Sparc tftpbooting without need to set up nfs. - + Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - + Pete Zaitcev endian fixes for cross-compiles, 2000. + 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 @@ -34,6 +35,24 @@ * as PROM looks for a.out image only. */ +unsigned short ld2(char *p) +{ + return (p[0] << 8) | p[1]; +} + +unsigned int ld4(char *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +void st4(char *p, unsigned int x) +{ + p[0] = x >> 24; + p[1] = x >> 16; + p[2] = x >> 8; + p[3] = x; +} + void usage(void) { /* fs_img.gz is an image of initial ramdisk. */ @@ -50,7 +69,8 @@ void die(char *str) int main(int argc,char **argv) { - char buffer [1024], *q, *r; + static char aout_magic[] = { 0x01, 0x03, 0x01, 0x07 }; + unsigned char buffer[1024], *q, *r; unsigned int i, j, k, start, end, offset; FILE *map; struct stat s; @@ -74,21 +94,20 @@ int main(int argc,char **argv) } if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]); if (read(image,buffer,512) != 512) die(argv[1]); - if (!memcmp (buffer, "\177ELF", 4)) { - unsigned int *p = (unsigned int *)(buffer + *(unsigned int *)(buffer + 28)); - - i = p[1] + *(unsigned int *)(buffer + 24) - p[2]; + if (memcmp (buffer, "\177ELF", 4) == 0) { + q = buffer + ld4(buffer + 28); + i = ld4(q + 4) + ld4(buffer + 24) - ld4(q + 8); if (lseek(image,i,0) < 0) die("lseek"); if (read(image,buffer,512) != 512) die(argv[1]); j = 0; - } else if (*(unsigned int *)buffer == 0x01030107) { + } else if (memcmp(buffer, aout_magic, 4) == 0) { i = j = 32; } else { fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n"); exit(1); } k = i; - i += ((*(unsigned short *)(buffer + j + 2))<<2) - 512; + i += (ld2(buffer + j + 2)<<2) - 512; if (lseek(image,i,0) < 0) die("lseek"); if (read(image,buffer,1024) != 1024) die(argv[1]); for (q = buffer, r = q + 512; q < r; q += 4) { @@ -101,10 +120,12 @@ int main(int argc,char **argv) } offset = i + (q - buffer) + 10; if (lseek(image, offset, 0) < 0) die ("lseek"); - *(unsigned *)buffer = 0; - *(unsigned *)(buffer + 4) = 0x01000000; - *(unsigned *)(buffer + 8) = ((end + 32 + 4095) & ~4095); - *(unsigned *)(buffer + 12) = s.st_size; + + st4(buffer, 0); + st4(buffer + 4, 0x01000000); + st4(buffer + 8, (end + 32 + 4095) & ~4095); + st4(buffer + 12, s.st_size); + if (write(image,buffer+2,14) != 14) die (argv[1]); if (lseek(image, k - start + ((end + 32 + 4095) & ~4095), 0) < 0) die ("lseek"); if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]); diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 2c82f4b22a7f..41ef9737ea4e 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -1,4 +1,4 @@ -/* $Id: ioport.c,v 1.41 2000/11/27 07:46:31 anton Exp $ +/* $Id: ioport.c,v 1.42 2000/12/05 00:56:36 anton Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -586,7 +586,8 @@ void pci_free_consistent(struct pci_dev *pdev, size_t n, void *p, dma_addr_t ba) * Once the device is given the dma address, the device owns this memory * until either pci_unmap_single or pci_dma_sync_single is performed. */ -dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) +dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, + int direction) { if (direction == PCI_DMA_NONE) BUG(); diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index e7b4e96de8a5..c9ba85b2704c 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -1,4 +1,4 @@ -/* $Id: pcic.c,v 1.19 2000/11/08 04:49:17 davem Exp $ +/* $Id: pcic.c,v 1.20 2000/12/05 00:56:36 anton Exp $ * pcic.c: Sparc/PCI controller support * * Copyright (C) 1998 V. Roganov and G. Raiko @@ -556,8 +556,8 @@ static void pcic_map_pci_device(struct linux_pcic *pcic, */ printk("PCIC: Skipping I/O space at 0x%lx," "this will Oops if a driver attaches;" - "device '%s' (%x,%x)\n", address, namebuf, - dev->device, dev->vendor); + "device '%s' at %02x:%02x)\n", address, + namebuf, dev->bus->number, dev->devfn); } } } @@ -568,12 +568,12 @@ pcic_fill_irq(struct linux_pcic *pcic, struct pci_dev *dev, int node) { struct pcic_ca2irq *p; int i, ivec; - char namebuf[64]; /* P3 remove */ + char namebuf[64]; if (node == 0 || node == -1) { strcpy(namebuf, "???"); } else { - prom_getstring(node, "name", namebuf, sizeof(namebuf)); /* P3 remove */ + prom_getstring(node, "name", namebuf, sizeof(namebuf)); } if ((p = pcic->pcic_imap) == 0) { @@ -612,8 +612,8 @@ pcic_fill_irq(struct linux_pcic *pcic, struct pci_dev *dev, int node) if (p->irq == 0 || p->irq >= 15) { /* Corrupted map */ printk("PCIC: BAD IRQ %d\n", p->irq); for (;;) {} } - printk("PCIC: setting irq %x for device (%x,%x)\n", - p->irq, dev->device, dev->vendor); + printk("PCIC: setting irq %d at pin %d for device %02x:%02x\n", + p->irq, p->pin, dev->bus->number, dev->devfn); dev->irq = p->irq; i = p->pin; diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 997e4d0ced62..d9883654e5f9 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.104 2000/09/06 05:43:00 anton Exp $ +/* $Id: sparc_ksyms.c,v 1.105 2000/12/11 05:24:25 anton Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -111,6 +111,11 @@ EXPORT_SYMBOL_PRIVATE(_rw_read_enter); EXPORT_SYMBOL_PRIVATE(_rw_read_exit); EXPORT_SYMBOL_PRIVATE(_rw_write_enter); #endif +/* semaphores */ +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_trylock); +EXPORT_SYMBOL(__down_interruptible); /* rw semaphores */ EXPORT_SYMBOL_NOVERS(___down_read); EXPORT_SYMBOL_NOVERS(___down_write); diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index dd011c1430e0..498fdb26f4bf 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.66 2000/07/10 20:57:35 davem Exp $ +/* $Id: sys_sparc.c,v 1.67 2000/11/30 08:37:31 anton Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -34,6 +34,8 @@ asmlinkage unsigned long sys_getpagesize(void) return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */ } +#define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1)) + unsigned long get_unmapped_area(unsigned long addr, unsigned long len) { struct vm_area_struct * vmm; @@ -45,7 +47,11 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len) return 0; if (!addr) addr = TASK_UNMAPPED_BASE; - addr = PAGE_ALIGN(addr); + + if (current->thread.flags & SPARC_FLAG_MMAPSHARED) + addr = COLOUR_ALIGN(addr); + else + addr = PAGE_ALIGN(addr); for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { /* At this point: (!vmm || addr < vmm->vm_end). */ @@ -58,6 +64,8 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len) if (!vmm || addr + len <= vmm->vm_start) return addr; addr = vmm->vm_end; + if (current->thread.flags & SPARC_FLAG_MMAPSHARED) + addr = COLOUR_ALIGN(addr); } } @@ -224,10 +232,16 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len, goto out_putf; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + if (flags & MAP_SHARED) + current->thread.flags |= SPARC_FLAG_MMAPSHARED; + down(¤t->mm->mmap_sem); retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); up(¤t->mm->mmap_sem); + current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); + out_putf: if (file) fput(file); @@ -259,6 +273,7 @@ asmlinkage unsigned long sparc_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr) { + struct vm_area_struct *vma; unsigned long ret = -EINVAL; if (ARCH_SUN4C_SUN4) { if (old_len > 0x20000000 || new_len > 0x20000000) @@ -270,6 +285,9 @@ asmlinkage unsigned long sparc_mremap(unsigned long addr, new_len > TASK_SIZE - PAGE_SIZE) goto out; down(¤t->mm->mmap_sem); + vma = find_vma(current->mm, addr); + if (vma && (vma->vm_flags & VM_SHARED)) + current->thread.flags |= SPARC_FLAG_MMAPSHARED; if (flags & MREMAP_FIXED) { if (ARCH_SUN4C_SUN4 && new_addr < 0xe0000000 && @@ -290,6 +308,7 @@ asmlinkage unsigned long sparc_mremap(unsigned long addr, } ret = do_mremap(addr, old_len, new_len, flags, new_addr); out_sem: + current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); up(¤t->mm->mmap_sem); out: return ret; @@ -302,12 +321,10 @@ c_sys_nis_syscall (struct pt_regs *regs) static int count = 0; if (count++ > 5) return -ENOSYS; - lock_kernel(); printk ("%s[%d]: Unimplemented SPARC system call %d\n", current->comm, current->pid, (int)regs->u_regs[1]); #ifdef DEBUG_UNIMP_SYSCALL show_regs (regs); #endif - unlock_kernel(); return -ENOSYS; } diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index 85161a3a6c6c..d27495bd6cef 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.95 2000/11/10 04:49:56 davem Exp $ +/* $Id: init.c,v 1.96 2000/11/30 08:51:50 anton Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -579,7 +579,8 @@ void si_meminfo(struct sysinfo *val) void flush_page_to_ram(struct page *page) { - unsigned long vaddr = (unsigned long) kmap(page); - __flush_page_to_ram(vaddr); - kunmap(page); + unsigned long vaddr = (unsigned long)page_address(page); + + if (vaddr) + __flush_page_to_ram(vaddr); } diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index b32199c13b74..0dba7d0a3ee3 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.224 2000/11/09 22:40:05 davem Exp $ +/* $Id: srmmu.c,v 1.225 2000/11/30 08:37:31 anton Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -51,7 +51,6 @@ enum mbus_module srmmu_modtype; unsigned int hwbug_bitmask; int vac_cache_size; int vac_line_size; -int vac_badbits; extern struct resource sparc_iomap; @@ -1286,75 +1285,6 @@ static void srmmu_destroy_context(struct mm_struct *mm) } } -static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma, - unsigned long address, pte_t pte) -{ - if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) { - struct vm_area_struct *vmaring; - struct file *file; - struct address_space *mapping; - unsigned long flags, offset, vaddr, start; - int alias_found = 0; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - __save_and_cli(flags); - - file = vma->vm_file; - if (!file) - goto done; - mapping = file->f_dentry->d_inode->i_mapping; - offset = (address & PAGE_MASK) - vma->vm_start; - spin_lock(&mapping->i_shared_lock); - vmaring = mapping->i_mmap_shared; - if (vmaring != NULL) do { - /* Do not mistake ourselves as another mapping. */ - if(vmaring == vma) - continue; - - vaddr = vmaring->vm_start + offset; - if ((vaddr ^ address) & vac_badbits) { - alias_found++; - start = vmaring->vm_start; - while (start < vmaring->vm_end) { - pgdp = srmmu_pgd_offset(vmaring->vm_mm, start); - if(!pgdp) goto next; - pmdp = srmmu_pmd_offset(pgdp, start); - if(!pmdp) goto next; - ptep = srmmu_pte_offset(pmdp, start); - if(!ptep) goto next; - - if((pte_val(*ptep) & SRMMU_ET_MASK) == SRMMU_VALID) { -#if 0 - printk("Fixing USER/USER alias [%ld:%08lx]\n", - vmaring->vm_mm->context, start); -#endif - flush_cache_page(vmaring, start); - srmmu_set_pte(ptep, __pte((pte_val(*ptep) & - ~SRMMU_CACHE))); - flush_tlb_page(vmaring, start); - } - next: - start += PAGE_SIZE; - } - } - } while ((vmaring = vmaring->vm_next_share) != NULL); - spin_unlock(&mapping->i_shared_lock); - - if(alias_found && ((pte_val(pte) & SRMMU_CACHE) != 0)) { - pgdp = srmmu_pgd_offset(vma->vm_mm, address); - pmdp = srmmu_pmd_offset(pgdp, address); - ptep = srmmu_pte_offset(pmdp, address); - flush_cache_page(vma, address); - srmmu_set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE))); - flush_tlb_page(vma, address); - } - done: - __restore_flags(flags); - } -} - /* Init various srmmu chip types. */ static void __init srmmu_is_bad(void) { @@ -1389,7 +1319,6 @@ static void __init init_vac_layout(void) } vac_cache_size = cache_lines * vac_line_size; - vac_badbits = (vac_cache_size - 1) & PAGE_MASK; #ifdef CONFIG_SMP if(vac_cache_size > max_size) max_size = vac_cache_size; @@ -1410,7 +1339,6 @@ static void __init init_vac_layout(void) #ifdef CONFIG_SMP vac_cache_size = max_size; vac_line_size = min_line_size; - vac_badbits = (vac_cache_size - 1) & PAGE_MASK; #endif printk("SRMMU: Using VAC size of %d bytes, line size %d bytes.\n", (int)vac_cache_size, (int)vac_line_size); @@ -1465,7 +1393,6 @@ static void __init init_hypersparc(void) BTFIXUPSET_CALL(flush_page_for_dma, hypersparc_flush_page_for_dma, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM); poke_srmmu = poke_hypersparc; hypersparc_setup_blockops(); @@ -1532,7 +1459,6 @@ static void __init init_cypress_common(void) BTFIXUPSET_CALL(flush_sig_insns, cypress_flush_sig_insns, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(flush_page_for_dma, cypress_flush_page_for_dma, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM); poke_srmmu = poke_cypress; } diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index edba89672894..1b32dd8ef8ec 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.201 2000/11/09 22:39:36 davem Exp $ +/* $Id: sun4c.c,v 1.202 2000/12/01 03:17:31 anton Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -2373,78 +2373,6 @@ static int sun4c_check_pgt_cache(int low, int high) return freed; } -/* There are really two cases of aliases to watch out for, and these - * are: - * - * 1) A user's page which can be aliased with the kernels virtual - * mapping of the physical page. - * - * 2) Multiple user mappings of the same inode/anonymous object - * such that two copies of the same data for the same phys page - * can live (writable) in the cache at the same time. - * - * We handle number 1 by flushing the kernel copy of the page always - * after COW page operations. - * - * NOTE: We are a bit slowed down now because the VMA arg is indeed used - * now, so our ref/mod bit tracking quick userfaults eat a few more - * cycles than they used to. - */ -static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long address, pte_t pte) -{ - pgd_t *pgdp; - pte_t *ptep; - - if (vma->vm_file) { - struct address_space *mapping; - unsigned long offset = (address & PAGE_MASK) - vma->vm_start; - struct vm_area_struct *vmaring; - int alias_found = 0; - - mapping = vma->vm_file->f_dentry->d_inode->i_mapping; - spin_lock(&mapping->i_shared_lock); - vmaring = mapping->i_mmap_shared; - if (vmaring != NULL) do { - unsigned long vaddr = vmaring->vm_start + offset; - unsigned long start; - - /* Do not mistake ourselves as another mapping. */ - if (vmaring == vma) - continue; - - if (S4CVAC_BADALIAS(vaddr, address)) { - alias_found++; - start = vmaring->vm_start; - while (start < vmaring->vm_end) { - pgdp = sun4c_pgd_offset(vmaring->vm_mm, start); - if (!pgdp) - goto next; - ptep = sun4c_pte_offset((pmd_t *) pgdp, start); - if (!ptep) - goto next; - - if (pte_val(*ptep) & _SUN4C_PAGE_PRESENT) { - flush_cache_page(vmaring, start); - *ptep = __pte(pte_val(*ptep) | - _SUN4C_PAGE_NOCACHE); - flush_tlb_page(vmaring, start); - } - next: - start += PAGE_SIZE; - } - } - } while ((vmaring = vmaring->vm_next_share) != NULL); - spin_unlock(&mapping->i_shared_lock); - - if (alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { - pgdp = sun4c_pgd_offset(vma->vm_mm, address); - ptep = sun4c_pte_offset((pmd_t *) pgdp, address); - *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); - pte = *ptep; - } - } -} - /* An experiment, turn off by default for now... -DaveM */ #define SUN4C_PRELOAD_PSEG @@ -2486,8 +2414,6 @@ void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, p #endif start += PAGE_SIZE; } - if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) - sun4c_vac_alias_fixup(vma, address, pte); #ifndef SUN4C_PRELOAD_PSEG sun4c_put_pte(address, pte_val(pte)); #endif @@ -2500,9 +2426,6 @@ void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, p add_lru(entry); } - if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) - sun4c_vac_alias_fixup(vma, address, pte); - sun4c_put_pte(address, pte_val(pte)); restore_flags(flags); } diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index e1ae982bf4b9..a0311626e131 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.98 2000/11/13 10:03:32 davem Exp $ +/* $Id: sparc64_ksyms.c,v 1.99 2000/12/09 04:15:24 anton Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -179,6 +179,7 @@ EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(__flushw_user); +EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(__flush_dcache_page); EXPORT_SYMBOL(mstk48t02_regs); diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 235d3488931b..391979c87720 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.46 2000/08/29 07:01:54 davem Exp $ +/* $Id: sys_sparc.c,v 1.47 2000/11/29 05:56:12 anton Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -40,6 +40,8 @@ asmlinkage unsigned long sys_getpagesize(void) return PAGE_SIZE; } +#define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1)) + unsigned long get_unmapped_area(unsigned long addr, unsigned long len) { struct vm_area_struct * vmm; @@ -51,7 +53,11 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len) return 0; if (!addr) addr = TASK_UNMAPPED_BASE; - addr = PAGE_ALIGN(addr); + + if (current->thread.flags & SPARC_FLAG_MMAPSHARED) + addr = COLOUR_ALIGN(addr); + else + addr = PAGE_ALIGN(addr); task_size -= len; @@ -66,6 +72,8 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len) if (!vmm || addr + len <= vmm->vm_start) return addr; addr = vmm->vm_end; + if (current->thread.flags & SPARC_FLAG_MMAPSHARED) + addr = COLOUR_ALIGN(addr); } } @@ -232,10 +240,15 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, goto out_putf; } + if (flags & MAP_SHARED) + current->thread.flags |= SPARC_FLAG_MMAPSHARED; + down(¤t->mm->mmap_sem); retval = do_mmap(file, addr, len, prot, flags, off); up(¤t->mm->mmap_sem); + current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); + out_putf: if (file) fput(file); @@ -264,6 +277,7 @@ asmlinkage unsigned long sys64_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr) { + struct vm_area_struct *vma; unsigned long ret = -EINVAL; if (current->thread.flags & SPARC_FLAG_32BIT) goto out; @@ -272,6 +286,9 @@ asmlinkage unsigned long sys64_mremap(unsigned long addr, if (addr < PAGE_OFFSET && addr + old_len > -PAGE_OFFSET) goto out; down(¤t->mm->mmap_sem); + vma = find_vma(current->mm, addr); + if (vma && (vma->vm_flags & VM_SHARED)) + current->thread.flags |= SPARC_FLAG_MMAPSHARED; if (flags & MREMAP_FIXED) { if (new_addr < PAGE_OFFSET && new_addr + new_len > -PAGE_OFFSET) @@ -280,13 +297,14 @@ asmlinkage unsigned long sys64_mremap(unsigned long addr, ret = -ENOMEM; if (!(flags & MREMAP_MAYMOVE)) goto out_sem; - new_addr = get_unmapped_area (addr, new_len); + new_addr = get_unmapped_area(addr, new_len); if (!new_addr) goto out_sem; flags |= MREMAP_FIXED; } ret = do_mremap(addr, old_len, new_len, flags, new_addr); out_sem: + current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); up(¤t->mm->mmap_sem); out: return ret; diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 9b211d86d338..0964409c9a91 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.166 2000/11/10 04:49:56 davem Exp $ +/* $Id: sys_sparc32.c,v 1.168 2000/12/11 18:59:35 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -2966,6 +2966,7 @@ static int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm) err = copy_from_user(kaddr + offset, (char *)A(str), bytes_to_copy); + flush_dcache_page(page); flush_page_to_ram(page); kunmap(page); @@ -4133,6 +4134,7 @@ asmlinkage unsigned long sys32_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, u32 __new_addr) { + struct vm_area_struct *vma; unsigned long ret = -EINVAL; unsigned long new_addr = AA(__new_addr); @@ -4141,6 +4143,9 @@ asmlinkage unsigned long sys32_mremap(unsigned long addr, if (addr > 0xf0000000UL - old_len) goto out; down(¤t->mm->mmap_sem); + vma = find_vma(current->mm, addr); + if (vma && (vma->vm_flags & VM_SHARED)) + current->thread.flags |= SPARC_FLAG_MMAPSHARED; if (flags & MREMAP_FIXED) { if (new_addr > 0xf0000000UL - new_len) goto out_sem; @@ -4148,13 +4153,14 @@ asmlinkage unsigned long sys32_mremap(unsigned long addr, ret = -ENOMEM; if (!(flags & MREMAP_MAYMOVE)) goto out_sem; - new_addr = get_unmapped_area (addr, new_len); + new_addr = get_unmapped_area(addr, new_len); if (!new_addr) goto out_sem; flags |= MREMAP_FIXED; } ret = do_mremap(addr, old_len, new_len, flags, new_addr); out_sem: + current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED); up(¤t->mm->mmap_sem); out: return ret; diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 6da2d0b8560d..2890927567f6 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.159 2000/11/06 06:59:04 davem Exp $ +/* $Id: init.c,v 1.161 2000/12/09 20:16:58 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) @@ -113,6 +113,17 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t p __update_mmu_cache(vma, address, pte); } +/* In arch/sparc64/mm/ultra.S */ +extern void __flush_icache_page(unsigned long); + +void flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long kaddr; + + for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) + __flush_icache_page(__get_phys(kaddr)); +} + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index b860253d3ac1..8a24cfb66f66 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -2397,7 +2397,7 @@ static int __init amb_probe (void) { #ifdef FILL_RX_POOLS_IN_BH // initialise bottom half - dev->bh.next = 0; + INIT_LIST_HEAD(&dev->bh.list); dev->bh.sync = 0; dev->bh.routine = (void (*)(void *)) fill_rx_pools; dev->bh.data = dev; diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index a228034de4d7..20e363b8e62d 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -176,14 +176,15 @@ static int __blk_cleanup_queue(struct list_head *head) * blk_cleanup_queue: - release a &request_queue_t when it is no longer needed * @q: the request queue to be released * - * Description: blk_cleanup_queue is the pair to blk_init_queue(). It should - * be called when a request queue is being released; typically when a block - * device is being de-registered. - * Currently, its primary task it to free all the &struct request structures - * that were allocated to the queue. - * Caveat: - * Hopefully the low level driver will have finished any outstanding - * requests first... + * Description: + * blk_cleanup_queue is the pair to blk_init_queue(). It should + * be called when a request queue is being released; typically + * when a block device is being de-registered. Currently, its + * primary task it to free all the &struct request structures that + * were allocated to the queue. + * Caveat: + * Hopefully the low level driver will have finished any + * outstanding requests first... **/ void blk_cleanup_queue(request_queue_t * q) { @@ -234,19 +235,21 @@ void blk_queue_headactive(request_queue_t * q, int active) * @plug: the function to be called to plug a queue * * Description: - * A request queue will be "plugged" if a request is added to it while it - * is empty. This allows a number of requests to be added before any are - * processed, thus providing an opportunity for these requests to be merged - * or re-ordered. - * The default plugging function (generic_plug_device()) sets the "plugged" - * flag for the queue and adds a task to the $tq_disk task queue to unplug - * the queue and call the request function at a later time. + * A request queue will be "plugged" if a request is added to it + * while it is empty. This allows a number of requests to be added + * before any are processed, thus providing an opportunity for these + * requests to be merged or re-ordered. + * The default plugging function (generic_plug_device()) sets the + * "plugged" flag for the queue and adds a task to the $tq_disk task + * queue to unplug the queue and call the request function at a + * later time. * - * A device driver may provide an alternate plugging function by passing it to - * blk_queue_pluggable(). This function should set the "plugged" flag if it - * want calls to the request_function to be blocked, and should place a - * task on $tq_disk which will unplug the queue. Alternately it can simply - * do nothing and there-by disable plugging of the device. + * A device driver may provide an alternate plugging function by + * passing it to blk_queue_pluggable(). This function should set + * the "plugged" flag if it want calls to the request_function to be + * blocked, and should place a task on $tq_disk which will unplug + * the queue. Alternately it can simply do nothing and there-by + * disable plugging of the device. **/ void blk_queue_pluggable (request_queue_t * q, plug_device_fn *plug) @@ -261,14 +264,21 @@ void blk_queue_pluggable (request_queue_t * q, plug_device_fn *plug) * @mfn: the alternate make_request function * * Description: - * The normal way for &struct buffer_heads to be passed to a device driver - * it to collect into requests on a request queue, and allow the device - * driver to select requests off that queue when it is ready. This works - * well for many block devices. However some block devices (typically - * virtual devices such as md or lvm) do not benefit from the processes on - * the request queue, and are served best by having the requests passed - * directly to them. This can be achieved by providing a function to - * blk_queue_make_request(). + * The normal way for &struct buffer_heads to be passed to a device + * driver is for them to be collected into requests on a request + * queue, and then to allow the device driver to select requests + * off that queue when it is ready. This works well for many block + * devices. However some block devices (typically virtual devices + * such as md or lvm) do not benefit from the processing on the + * request queue, and are served best by having the requests passed + * directly to them. This can be achieved by providing a function + * to blk_queue_make_request(). + * + * Caveat: + * The driver that does this *must* be able to deal appropriately + * with buffers in "highmemory", either by calling bh_kmap() to get + * a kernel mapping, to by calling create_bounce() to create a + * buffer in normal memory. **/ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn) @@ -637,7 +647,10 @@ static void attempt_merge(request_queue_t * q, next = blkdev_next_request(req); if (req->sector + req->nr_sectors != next->sector) return; - if (req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors || next->sem) + if (req->cmd != next->cmd + || req->rq_dev != next->rq_dev + || req->nr_sectors + next->nr_sectors > max_sectors + || next->sem) return; /* * If we are not allowed to merge these requests, then @@ -749,7 +762,8 @@ again: goto get_rq; } - el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments); + el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, + &max_sectors, &max_segments); switch (el_ret) { case ELEVATOR_BACK_MERGE: @@ -833,6 +847,40 @@ end_io: return 0; } +/** + * generic_make_request: hand a buffer head to it's device driver for I/O + * @rw: READ, WRITE, or READA - what sort of I/O is desired. + * @bh: The buffer head describing the location in memory and on the device. + * + * generic_make_request() is used to make I/O requests of block + * devices. It is passed a &struct buffer_head and a &rw value. The + * %READ and %WRITE options are (hopefully) obvious in meaning. The + * %READA value means that a read is required, but that the driver is + * free to fail the request if, for example, it cannot get needed + * resources immediately. + * + * generic_make_request() does not return any status. The + * success/failure status of the request, along with notification of + * completion, is delivered asynchronously through the bh->b_end_io + * function described (one day) else where. + * + * The caller of generic_make_request must make sure that b_page, + * b_addr, b_size are set to describe the memory buffer, that b_rdev + * and b_rsector are set to describe the device address, and the + * b_end_io and optionally b_private are set to describe how + * completion notification should be signaled. BH_Mapped should also + * be set (to confirm that b_dev and b_blocknr are valid). + * + * generic_make_request and the drivers it calls may use b_reqnext, + * and may change b_rdev and b_rsector. So the values of these fields + * should NOT be depended on after the call to generic_make_request. + * Because of this, the caller should record the device address + * information in b_dev and b_blocknr. + * + * Apart from those fields mentioned above, no other fields, and in + * particular, no other flags, are changed by generic_make_request or + * any lower level drivers. + * */ void generic_make_request (int rw, struct buffer_head * bh) { int major = MAJOR(bh->b_rdev); @@ -886,8 +934,18 @@ void generic_make_request (int rw, struct buffer_head * bh) } -/* - * Submit a buffer head for IO. +/** + * submit_bh: submit a buffer_head to the block device later for I/O + * @rw: whether to %READ or %WRITE, or mayve to %READA (read ahead) + * @bh: The &struct buffer_head which describes the I/O + * + * submit_bh() is very similar in purpose to generic_make_request(), and + * uses that function to do most of the work. + * + * The extra functionality provided by submit_bh is to determine + * b_rsector from b_blocknr and b_size, and to set b_rdev from b_dev. + * This is is appropriate for IO requests that come from the buffer + * cache and page cache which (currently) always use aligned blocks. */ void submit_bh(int rw, struct buffer_head * bh) { @@ -915,9 +973,36 @@ static void end_buffer_io_sync(struct buffer_head *bh, int uptodate) unlock_buffer(bh); } -/* This function can be used to request a number of buffers from a block - device. Currently the only restriction is that all buffers must belong to - the same device */ +/** + * ll_rw_block: low-level access to block devices + * @rw: whether to %READ or %WRITE or maybe %READA (readahead) + * @nr: number of &struct buffer_heads in the array + * @bhs: array of pointers to &struct buffer_head + * + * ll_rw_block() takes an array of pointers to &struct buffer_heads, + * and requests an I/O operation on them, either a %READ or a %WRITE. + * The third %READA option is described in the documentation for + * generic_make_request() which ll_rw_block() calls. + * + * This function provides extra functionality that is not in + * generic_make_request() that is relevant to buffers in the buffer + * cache or page cache. In particular it drops any buffer that it + * cannot get a lock on (with the BH_Lock state bit), any buffer that + * appears to be clean when doing a write request, and any buffer that + * appears to be up-to-date when doing read request. Further it marks + * as clean buffers that are processed for writing (the buffer cache + * wont assume that they are actually clean until the buffer gets + * unlocked). + * + * ll_rw_block sets b_end_io to simple completion handler that marks + * the buffer up-to-date (if approriate), unlocks the buffer and wakes + * any waiters. As client that needs a more interesting completion + * routine should call submit_bh() (or generic_make_request()) + * directly. + * + * Caveat: + * All of the buffers must be for the same device, and must also be + * of the current approved size for the device. */ void ll_rw_block(int rw, int nr, struct buffer_head * bhs[]) { diff --git a/drivers/block/paride/pseudo.h b/drivers/block/paride/pseudo.h index d1155a8aa187..0e1a8d2e5e3b 100644 --- a/drivers/block/paride/pseudo.h +++ b/drivers/block/paride/pseudo.h @@ -50,7 +50,7 @@ static int ps_nice = 0; static spinlock_t ps_spinlock __attribute__((unused)) = SPIN_LOCK_UNLOCKED; static struct timer_list ps_timer = { function: ps_timer_int }; -static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL}; +static struct tq_struct ps_tq = { routine: ps_tq_int }; static void ps_set_intr( void (*continuation)(void), int (*ready)(void), diff --git a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c index 4ab67281ca52..4854c56d591b 100644 --- a/drivers/char/drm/gamma_dma.c +++ b/drivers/char/drm/gamma_dma.c @@ -651,7 +651,7 @@ int gamma_irq_install(drm_device_t *dev, int irq) dev->dma->next_queue = NULL; dev->dma->this_buffer = NULL; - dev->tq.next = NULL; + INIT_LIST_HEAD(&dev->tq.list); dev->tq.sync = 0; dev->tq.routine = gamma_dma_schedule_tq_wrapper; dev->tq.data = dev; diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c index 26fe0c1c1527..aa824a79cc94 100644 --- a/drivers/char/drm/i810_dma.c +++ b/drivers/char/drm/i810_dma.c @@ -924,7 +924,7 @@ int i810_irq_install(drm_device_t *dev, int irq) dev->dma->next_queue = NULL; dev->dma->this_buffer = NULL; - dev->tq.next = NULL; + INIT_LIST_HEAD(&dev->tq.list); dev->tq.sync = 0; dev->tq.routine = i810_dma_task_queue; dev->tq.data = dev; diff --git a/drivers/char/drm/mga_dma.c b/drivers/char/drm/mga_dma.c index 2b293c5ffc0d..356376ca9cfe 100644 --- a/drivers/char/drm/mga_dma.c +++ b/drivers/char/drm/mga_dma.c @@ -818,7 +818,7 @@ int mga_irq_install(drm_device_t *dev, int irq) dev->dma->next_buffer = NULL; dev->dma->next_queue = NULL; dev->dma->this_buffer = NULL; - dev->tq.next = NULL; + INIT_LIST_HEAD(&dev->tq.list); dev->tq.sync = 0; dev->tq.routine = mga_dma_task_queue; dev->tq.data = dev; diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index d916898002b4..8a825753a333 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -1157,12 +1157,12 @@ static int r3964_open(struct tty_struct *tty) * Add 'on_timer' to timer task queue * (will be called from timer bh) */ - pInfo->bh_1.next = NULL; + INIT_LIST_HEAD(&pInfo->bh_1.list); pInfo->bh_1.sync = 0; pInfo->bh_1.routine = &on_timer_1; pInfo->bh_1.data = pInfo; - pInfo->bh_2.next = NULL; + INIT_LIST_HEAD(&pInfo->bh_2.list); pInfo->bh_2.sync = 0; pInfo->bh_2.routine = &on_timer_2; pInfo->bh_2.data = pInfo; @@ -1174,7 +1174,6 @@ static int r3964_open(struct tty_struct *tty) static void r3964_close(struct tty_struct *tty) { - struct tq_struct *tq, *prev; struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; struct r3964_client_info *pClient, *pNext; struct r3964_message *pMsg; @@ -1187,19 +1186,12 @@ static void r3964_close(struct tty_struct *tty) * Make sure that our task queue isn't activated. If it * is, take it out of the linked list. */ - save_flags(flags); - cli(); - - for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { - if ((tq == &pInfo->bh_1) || (tq==&pInfo->bh_2)) { - if (prev) - prev->next = tq->next; - else - tq_timer = tq->next; - break; - } - } - restore_flags(flags); + spin_lock_irqsave(&tqueue_lock, flags); + if (pInfo->bh_1.sync) + list_del(&pInfo->bh_1.list); + if (pInfo->bh_2.sync) + list_del(&pInfo->bh_2.list); + spin_unlock_irqrestore(&tqueue_lock, flags); /* Remove client-structs and message queues: */ pClient=pInfo->firstClient; diff --git a/drivers/char/scan_keyb.c b/drivers/char/scan_keyb.c index f2435e9ec1fa..6ba861be2615 100644 --- a/drivers/char/scan_keyb.c +++ b/drivers/char/scan_keyb.c @@ -120,7 +120,7 @@ int register_scan_keyboard(void (*scan)(unsigned char *buffer), void __init scan_kbd_init(void) { - task_scan_kbd.next=NULL; + INIT_LIST_HEAD(task_scan_kbd.list); task_scan_kbd.sync=0; task_scan_kbd.routine=scan_kbd; task_scan_kbd.data=NULL; diff --git a/drivers/char/sx.h b/drivers/char/sx.h index 662b27ca2aa6..8d77c3c77375 100644 --- a/drivers/char/sx.h +++ b/drivers/char/sx.h @@ -31,7 +31,7 @@ struct sx_port { int c_dcd; struct sx_board *board; int line; - int locks; + long locks; }; struct sx_board { diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 1bc175d16a2f..4165a37e337a 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -62,7 +62,7 @@ struct vt_struct *vt_cons[MAX_NR_CONSOLES]; */ unsigned char keyboard_type = KB_101; -#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__) +#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__) asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); #endif @@ -472,7 +472,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ucval = keyboard_type; goto setchar; -#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__) +#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__) /* * These cannot be implemented on any machine that implements * ioperm() in user level (such as Alpha PCs). diff --git a/drivers/i2o/i2o_lan.c b/drivers/i2o/i2o_lan.c index d47323c9375d..8b9e4c448136 100644 --- a/drivers/i2o/i2o_lan.c +++ b/drivers/i2o/i2o_lan.c @@ -112,8 +112,10 @@ static struct i2o_handler i2o_lan_handler = { }; static int lan_context; -static struct tq_struct i2o_post_buckets_task = { - 0, 0, (void (*)(void *))i2o_lan_receive_post, (void *) 0 +DECLARE_TASK_QUEUE(i2o_post_buckets_task); +struct tq_struct run_i2o_post_buckets_task = { + routine: (void (*)(void *)) run_task_queue, + data: (void *) 0 }; /* Functions to handle message failures and transaction errors: @@ -379,8 +381,8 @@ static void i2o_lan_receive_post_reply(struct i2o_handler *h, /* If DDM has already consumed bucket_thresh buckets, post new ones */ if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) { - i2o_post_buckets_task.data = (void *)dev; - queue_task(&i2o_post_buckets_task, &tq_immediate); + run_i2o_post_buckets_task.data = (void *)dev; + queue_task(&run_i2o_post_buckets_task, &tq_immediate); mark_bh(IMMEDIATE_BH); } @@ -1401,7 +1403,7 @@ struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) atomic_set(&priv->tx_out, 0); priv->tx_count = 0; - priv->i2o_batch_send_task.next = NULL; + INIT_LIST_HEAD(&priv->i2o_batch_send_task.list); priv->i2o_batch_send_task.sync = 0; priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send; priv->i2o_batch_send_task.data = (void *)dev; diff --git a/drivers/ieee1394/guid.c b/drivers/ieee1394/guid.c index 1aa453292e66..6745222f3f92 100644 --- a/drivers/ieee1394/guid.c +++ b/drivers/ieee1394/guid.c @@ -163,7 +163,7 @@ static void host_reset(struct hpsb_host *host) return; } - greq->tq.next = NULL; + INIT_LIST_HEAD(&greq->tq.list); greq->tq.sync = 0; greq->tq.routine = (void (*)(void*))pkt_complete; greq->tq.data = greq; diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index 6dc42555dfdd..3b41afb55a29 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -1585,7 +1585,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, /* initialize bottom handler */ d->task.sync = 0; - d->task.next = NULL; + INIT_LIST_HEAD(&d->task.list); d->task.routine = dma_rcv_bh; d->task.data = (void*)d; diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index b7812b830029..f25e2a159fa1 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1180,7 +1180,7 @@ checkcard(int cardnr, char *id, int *busy_flag) cs->tx_skb = NULL; cs->tx_cnt = 0; cs->event = 0; - cs->tqueue.next = 0; + INIT_LIST_HEAD(&cs->tqueue.list); cs->tqueue.sync = 0; cs->tqueue.data = cs; diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 45d89f8b3671..cda9e422c734 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -343,7 +343,7 @@ init_bcstate(struct IsdnCardState *cs, bcs->cs = cs; bcs->channel = bc; - bcs->tqueue.next = 0; + INIT_LIST_HEAD(&bcs->tqueue.list); bcs->tqueue.sync = 0; bcs->tqueue.routine = (void *) (void *) BChannel_bh; bcs->tqueue.data = bcs; diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c index 2a2d93bfaec1..941a4bfabff2 100644 --- a/drivers/isdn/hysdn/boardergo.c +++ b/drivers/isdn/hysdn/boardergo.c @@ -458,7 +458,7 @@ ergo_inithardware(hysdn_card * card) card->writebootseq = ergo_writebootseq; card->waitpofready = ergo_waitpofready; card->set_errlog_state = ergo_set_errlog_state; - card->irq_queue.next = 0; + INIT_LIST_HEAD(&card->irq_queue.list); card->irq_queue.sync = 0; card->irq_queue.data = card; /* init task queue for interrupt */ card->irq_queue.routine = (void *) (void *) ergo_irq_bh; diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index 125742e6a893..4f421e75c4da 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -135,7 +135,7 @@ int pcbit_init_dev(int board, int mem_base, int irq) dev->b2->id = 1; - dev->qdelivery.next = NULL; + INIT_LIST_HEAD(&dev->qdelivery.list); dev->qdelivery.sync = 0; dev->qdelivery.routine = pcbit_deliver; dev->qdelivery.data = dev; diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 7e6bdbaf4236..c37ce84dbac1 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -16,10 +16,13 @@ obj-m := obj-n := obj- := -# NOTE: xor.o must link *before* md.o so that auto-detect -# of raid5 arrays works (and doesn't Oops). Fortunately -# they are both export-objs, so setting the order here -# works. +# Note: link order is important. All raid personalities +# and xor.o must come before md.o, as they each initialise +# themselves, and md.o may use the personalities when it +# auto-initialised. +# The use of MIX_OBJS allows link order to be maintained even +# though some are export-objs and some aren't. + obj-$(CONFIG_MD_LINEAR) += linear.o obj-$(CONFIG_MD_RAID0) += raid0.o obj-$(CONFIG_MD_RAID1) += raid1.o @@ -28,10 +31,11 @@ obj-$(CONFIG_BLK_DEV_MD) += md.o obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o # Translate to Rules.make lists. -O_OBJS := $(filter-out $(export-objs), $(obj-y)) -OX_OBJS := $(filter $(export-objs), $(obj-y)) -M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) -MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +active-objs := $(sort $(obj-y) $(obj-m)) + +O_OBJS := $(obj-y) +M_OBJS := $(obj-m) +MIX_OBJS := $(filter $(export-objs), $(active-objs)) include $(TOPDIR)/Rules.make diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 855bc44dde82..e6b50b84d820 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -84,21 +84,19 @@ static int linear_run (mddev_t *mddev) dev_info_t *disk = conf->disks + j; if (size < 0) { - table->dev1 = disk; - table++; + table[-1].dev1 = disk; } size += disk->size; - while (size) { + while (size>0) { table->dev0 = disk; - size -= conf->smallest->size; - if (size < 0) - break; table->dev1 = NULL; + size -= conf->smallest->size; table++; } } - table->dev1 = NULL; + if (table-conf->hash_table != nb_zone) + BUG(); return 0; @@ -136,7 +134,8 @@ static int linear_make_request (mddev_t *mddev, if (!hash->dev1) { printk ("linear_make_request : hash->dev1==NULL for block %ld\n", block); - return -1; + buffer_IO_error(bh); + return 0; } tmp_dev = hash->dev1; } else @@ -145,7 +144,8 @@ static int linear_make_request (mddev_t *mddev, if (block >= (tmp_dev->size + tmp_dev->offset) || block < tmp_dev->offset) { printk ("linear_make_request: Block %ld out of bounds on dev %s size %ld offset %ld\n", block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset); - return -1; + buffer_IO_error(bh); + return 0; } bh->b_rdev = tmp_dev->dev; bh->b_rsector = bh->b_rsector - (tmp_dev->offset << 1); @@ -190,24 +190,16 @@ static mdk_personality_t linear_personality= status: linear_status, }; -#ifndef MODULE - -void md__init linear_init (void) +static int md__init linear_init (void) { - register_md_personality (LINEAR, &linear_personality); + return register_md_personality (LINEAR, &linear_personality); } -#else - -int init_module (void) -{ - return (register_md_personality (LINEAR, &linear_personality)); -} - -void cleanup_module (void) +static void linear_exit (void) { unregister_md_personality (LINEAR); } -#endif +module_init(linear_init); +module_exit(linear_exit); diff --git a/drivers/md/lvm.c b/drivers/md/lvm.c index 5aa88df9e453..f9433232eb9f 100644 --- a/drivers/md/lvm.c +++ b/drivers/md/lvm.c @@ -123,6 +123,7 @@ * - avoided inline strings functions lvm_strlen etc. * 14/02/2000 - support for 2.3.43 * - integrated Andrea Arcangeli's snapshot code + * 07/12/2000 - make sure lvm_make_request_fn returns correct value - 0 or 1 - NeilBrown * */ @@ -1488,8 +1489,10 @@ static void lvm_dummy_device_request(request_queue_t * t) */ static int lvm_make_request_fn(request_queue_t *q, int rw, struct buffer_head *bh) { - lvm_map(bh, rw); - return 1; + if (lvm_map(bh, rw)<0) + return 0; /* failure, buffer_IO_error has been called, don't recurse */ + else + return 1; /* all ok, mapping done, call lower level driver */ } /* diff --git a/drivers/md/md.c b/drivers/md/md.c index 86664dcd136d..8542bc2b067f 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -179,7 +179,7 @@ static int md_make_request (request_queue_t *q, int rw, struct buffer_head * bh) return mddev->pers->make_request(mddev, rw, bh); else { buffer_IO_error(bh); - return -1; + return 0; } } @@ -203,6 +203,7 @@ static mddev_t * alloc_mddev (kdev_t dev) init_MUTEX(&mddev->resync_sem); MD_INIT_LIST_HEAD(&mddev->disks); MD_INIT_LIST_HEAD(&mddev->all_mddevs); + atomic_set(&mddev->active, 0); /* * The 'base' mddev is the one with data NULL. @@ -656,32 +657,25 @@ static void unbind_rdev_from_array (mdk_rdev_t * rdev) static int lock_rdev (mdk_rdev_t *rdev) { int err = 0; + struct block_device *bdev; - /* - * First insert a dummy inode. - */ - if (rdev->inode) - MD_BUG(); - rdev->inode = get_empty_inode(); - if (!rdev->inode) + bdev = bdget(rdev->dev); + if (bdev == NULL) return -ENOMEM; - /* - * we dont care about any other fields - */ - rdev->inode->i_dev = rdev->inode->i_rdev = rdev->dev; - insert_inode_hash(rdev->inode); - - memset(&rdev->filp, 0, sizeof(rdev->filp)); - rdev->filp.f_mode = 3; /* read write */ + err = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE); + if (!err) { + rdev->bdev = bdev; + } return err; } static void unlock_rdev (mdk_rdev_t *rdev) { - if (!rdev->inode) + if (!rdev->bdev) MD_BUG(); - iput(rdev->inode); - rdev->inode = NULL; + blkdev_put(rdev->bdev, BDEV_FILE); + bdput(rdev->bdev); + rdev->bdev = NULL; } static void export_rdev (mdk_rdev_t * rdev) @@ -1149,7 +1143,7 @@ static int md_import_device (kdev_t newdev, int on_disk) abort_free: if (rdev->sb) { - if (rdev->inode) + if (rdev->bdev) unlock_rdev(rdev); free_disk_sb(rdev); } @@ -1718,12 +1712,20 @@ out: #define STILL_MOUNTED KERN_WARNING \ "md: md%d still mounted.\n" +#define STILL_IN_USE \ +"md: md%d still in use.\n" static int do_md_stop (mddev_t * mddev, int ro) { int err = 0, resync_interrupted = 0; kdev_t dev = mddev_to_kdev(mddev); + if (atomic_read(&mddev->active)>1) { + printk(STILL_IN_USE, mdidx(mddev)); + OUT(-EBUSY); + } + + /* this shouldn't be needed as above would have fired */ if (!ro && get_super(dev)) { printk (STILL_MOUNTED, mdidx(mddev)); OUT(-EBUSY); @@ -1859,8 +1861,10 @@ static void autorun_array (mddev_t *mddev) * the 'same_array' list. Then order this list based on superblock * update time (freshest comes first), kick out 'old' disks and * compare superblocks. If everything's fine then run it. + * + * If "unit" is allocated, then bump its reference count */ -static void autorun_devices (void) +static void autorun_devices (kdev_t countdev) { struct md_list_head candidates; struct md_list_head *tmp; @@ -1902,6 +1906,12 @@ static void autorun_devices (void) continue; } mddev = alloc_mddev(md_kdev); + if (mddev == NULL) { + printk("md: cannot allocate memory for md drive.\n"); + break; + } + if (md_kdev == countdev) + atomic_inc(&mddev->active); printk("created md%d\n", mdidx(mddev)); ITERATE_RDEV_GENERIC(candidates,pending,rdev,tmp) { bind_rdev_to_array(rdev, mddev); @@ -1945,7 +1955,7 @@ static void autorun_devices (void) #define AUTORUNNING KERN_INFO \ "md: auto-running md%d.\n" -static int autostart_array (kdev_t startdev) +static int autostart_array (kdev_t startdev, kdev_t countdev) { int err = -EINVAL, i; mdp_super_t *sb = NULL; @@ -2002,7 +2012,7 @@ static int autostart_array (kdev_t startdev) /* * possibly return codes */ - autorun_devices(); + autorun_devices(countdev); return 0; abort: @@ -2077,7 +2087,7 @@ int md__init md_run_setup(void) md_list_add(&rdev->pending, &pending_raid_disks); } - autorun_devices(); + autorun_devices(-1); } dev_cnt = -1; /* make sure further calls to md_autodetect_dev are ignored */ @@ -2607,6 +2617,8 @@ static int md_ioctl (struct inode *inode, struct file *file, err = -ENOMEM; goto abort; } + atomic_inc(&mddev->active); + /* * alloc_mddev() should possibly self-lock. */ @@ -2640,7 +2652,7 @@ static int md_ioctl (struct inode *inode, struct file *file, /* * possibly make it lock the array ... */ - err = autostart_array((kdev_t)arg); + err = autostart_array((kdev_t)arg, dev); if (err) { printk("autostart %s failed!\n", partition_name((kdev_t)arg)); @@ -2820,14 +2832,26 @@ abort: static int md_open (struct inode *inode, struct file *file) { /* - * Always succeed + * Always succeed, but increment the usage count */ + mddev_t *mddev = kdev_to_mddev(inode->i_rdev); + if (mddev) + atomic_inc(&mddev->active); return (0); } +static int md_release (struct inode *inode, struct file * file) +{ + mddev_t *mddev = kdev_to_mddev(inode->i_rdev); + if (mddev) + atomic_dec(&mddev->active); + return 0; +} + static struct block_device_operations md_fops= { open: md_open, + release: md_release, ioctl: md_ioctl, }; @@ -3576,12 +3600,6 @@ static void md_geninit (void) create_proc_read_entry("mdstat", 0, NULL, md_status_read_proc, NULL); #endif } -void hsm_init (void); -void translucent_init (void); -void linear_init (void); -void raid0_init (void); -void raid1_init (void); -void raid5_init (void); int md__init md_init (void) { @@ -3617,18 +3635,6 @@ int md__init md_init (void) md_register_reboot_notifier(&md_notifier); raid_table_header = register_sysctl_table(raid_root_table, 1); -#ifdef CONFIG_MD_LINEAR - linear_init (); -#endif -#ifdef CONFIG_MD_RAID0 - raid0_init (); -#endif -#ifdef CONFIG_MD_RAID1 - raid1_init (); -#endif -#ifdef CONFIG_MD_RAID5 - raid5_init (); -#endif md_geninit(); return (0); } diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 09f3f854762f..576dd3b43a7c 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -275,16 +275,18 @@ static int raid0_make_request (mddev_t *mddev, bad_map: printk ("raid0_make_request bug: can't convert block across chunks or bigger than %dk %ld %d\n", chunk_size, bh->b_rsector, bh->b_size >> 10); - return -1; + goto outerr; bad_hash: printk("raid0_make_request bug: hash==NULL for block %ld\n", block); - return -1; + goto outerr; bad_zone0: printk ("raid0_make_request bug: hash->zone0==NULL for block %ld\n", block); - return -1; + goto outerr; bad_zone1: printk ("raid0_make_request bug: hash->zone1==NULL for block %ld\n", block); - return -1; + outerr: + buffer_IO_error(bh); + return 0; } static int raid0_status (char *page, mddev_t *mddev) @@ -333,24 +335,17 @@ static mdk_personality_t raid0_personality= status: raid0_status, }; -#ifndef MODULE - -void raid0_init (void) -{ - register_md_personality (RAID0, &raid0_personality); -} - -#else - -int init_module (void) +static int md__init raid0_init (void) { - return (register_md_personality (RAID0, &raid0_personality)); + return register_md_personality (RAID0, &raid0_personality); } -void cleanup_module (void) +static void raid0_exit (void) { unregister_md_personality (RAID0); } -#endif +module_init(raid0_init); +module_exit(raid0_exit); + diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 85073b564683..3a381b6a22fc 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -463,16 +463,12 @@ static int raid1_read_balance (raid1_conf_t *conf, struct buffer_head *bh) if (conf->resync_mirrors) goto rb_out; - if (conf->working_disks < 2) { - int i = 0; - - while( !conf->mirrors[new_disk].operational && - (i < MD_SB_DISKS) ) { - new_disk = conf->mirrors[new_disk].next; - i++; - } - - if (i >= MD_SB_DISKS) { + + /* make sure that disk is operational */ + while( !conf->mirrors[new_disk].operational) { + if (new_disk <= 0) new_disk = conf->raid_disks; + new_disk--; + if (new_disk == disk) { /* * This means no working disk was found * Nothing much to do, lets not change anything @@ -480,11 +476,13 @@ static int raid1_read_balance (raid1_conf_t *conf, struct buffer_head *bh) */ new_disk = conf->last_used; + + goto rb_out; } - - goto rb_out; } - + disk = new_disk; + /* now disk == new_disk == starting point for search */ + /* * Don't touch anything for sequential reads. */ @@ -501,16 +499,16 @@ static int raid1_read_balance (raid1_conf_t *conf, struct buffer_head *bh) if (conf->sect_count >= conf->mirrors[new_disk].sect_limit) { conf->sect_count = 0; - - while( new_disk != conf->mirrors[new_disk].next ) { - if ((conf->mirrors[new_disk].write_only) || - (!conf->mirrors[new_disk].operational) ) - continue; - - new_disk = conf->mirrors[new_disk].next; - break; - } - + + do { + if (new_disk<=0) + new_disk = conf->raid_disks; + new_disk--; + if (new_disk == disk) + break; + } while ((conf->mirrors[new_disk].write_only) || + (!conf->mirrors[new_disk].operational)); + goto rb_out; } @@ -519,8 +517,10 @@ static int raid1_read_balance (raid1_conf_t *conf, struct buffer_head *bh) /* Find the disk which is closest */ - while( conf->mirrors[disk].next != conf->last_used ) { - disk = conf->mirrors[disk].next; + do { + if (disk <= 0) + disk = conf->raid_disks; + disk--; if ((conf->mirrors[disk].write_only) || (!conf->mirrors[disk].operational)) @@ -534,7 +534,7 @@ static int raid1_read_balance (raid1_conf_t *conf, struct buffer_head *bh) current_distance = new_distance; new_disk = disk; } - } + } while (disk != conf->last_used); rb_out: conf->mirrors[new_disk].head_position = this_sector + sectors; @@ -702,16 +702,6 @@ static int raid1_status (char *page, mddev_t *mddev) return sz; } -static void unlink_disk (raid1_conf_t *conf, int target) -{ - int disks = MD_SB_DISKS; - int i; - - for (i = 0; i < disks; i++) - if (conf->mirrors[i].next == target) - conf->mirrors[i].next = conf->mirrors[target].next; -} - #define LAST_DISK KERN_ALERT \ "raid1: only one disk left and IO error.\n" @@ -735,7 +725,6 @@ static void mark_disk_bad (mddev_t *mddev, int failed) mdp_super_t *sb = mddev->sb; mirror->operational = 0; - unlink_disk(conf, failed); mark_disk_faulty(sb->disks+mirror->number); mark_disk_nonsync(sb->disks+mirror->number); mark_disk_inactive(sb->disks+mirror->number); @@ -786,25 +775,6 @@ static int raid1_error (mddev_t *mddev, kdev_t dev) #undef DISK_FAILED #undef START_SYNCING -/* - * Insert the spare disk into the drive-ring - */ -static void link_disk(raid1_conf_t *conf, struct mirror_info *mirror) -{ - int j, next; - int disks = MD_SB_DISKS; - struct mirror_info *p = conf->mirrors; - - for (j = 0; j < disks; j++, p++) - if (p->operational && !p->write_only) { - next = p->next; - p->next = mirror->raid_disk; - mirror->next = next; - return; - } - - printk("raid1: bug: no read-operational devices\n"); -} static void print_raid1_conf (raid1_conf_t *conf) { @@ -828,6 +798,32 @@ static void print_raid1_conf (raid1_conf_t *conf) } } +static void close_sync(raid1_conf_t *conf) +{ + mddev_t *mddev = conf->mddev; + /* If reconstruction was interrupted, we need to close the "active" and "pending" + * holes. + * we know that there are no active rebuild requests, os cnt_active == cnt_ready ==0 + */ + /* this is really needed when recovery stops too... */ + spin_lock_irq(&conf->segment_lock); + conf->start_active = conf->start_pending; + conf->start_ready = conf->start_pending; + wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); + conf->start_active =conf->start_ready = conf->start_pending = conf->start_future; + conf->start_future = mddev->sb->size+1; + conf->cnt_pending = conf->cnt_future; + conf->cnt_future = 0; + conf->phase = conf->phase ^1; + wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); + conf->start_active = conf->start_ready = conf->start_pending = conf->start_future = 0; + conf->phase = 0; + conf->cnt_future = conf->cnt_done;; + conf->cnt_done = 0; + spin_unlock_irq(&conf->segment_lock); + wake_up(&conf->wait_done); +} + static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state) { int err = 0; @@ -940,6 +936,7 @@ static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state) * Deactivate a spare disk: */ case DISKOP_SPARE_INACTIVE: + close_sync(conf); sdisk = conf->mirrors + spare_disk; sdisk->operational = 0; sdisk->write_only = 0; @@ -952,7 +949,7 @@ static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state) * property) */ case DISKOP_SPARE_ACTIVE: - + close_sync(conf); sdisk = conf->mirrors + spare_disk; fdisk = conf->mirrors + failed_disk; @@ -1017,7 +1014,6 @@ static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state) */ fdisk->spare = 0; fdisk->write_only = 0; - link_disk(conf, fdisk); /* * if we activate a spare, we definitely replace a @@ -1244,27 +1240,7 @@ static void raid1syncd (void *data) conf->resync_mirrors = 0; } - /* If reconstruction was interrupted, we need to close the "active" and "pending" - * holes. - * we know that there are no active rebuild requests, os cnt_active == cnt_ready ==0 - */ - /* this is really needed when recovery stops too... */ - spin_lock_irq(&conf->segment_lock); - conf->start_active = conf->start_pending; - conf->start_ready = conf->start_pending; - wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); - conf->start_active =conf->start_ready = conf->start_pending = conf->start_future; - conf->start_future = mddev->sb->size+1; - conf->cnt_pending = conf->cnt_future; - conf->cnt_future = 0; - conf->phase = conf->phase ^1; - wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); - conf->start_active = conf->start_ready = conf->start_pending = conf->start_future = 0; - conf->phase = 0; - conf->cnt_future = conf->cnt_done;; - conf->cnt_done = 0; - spin_unlock_irq(&conf->segment_lock); - wake_up(&conf->wait_done); + close_sync(conf); up(&mddev->recovery_sem); raid1_shrink_buffers(conf); @@ -1325,6 +1301,7 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) struct raid1_bh *r1_bh; struct buffer_head *bh; int bsize; + int disk; spin_lock_irq(&conf->segment_lock); if (!block_nr) { @@ -1377,6 +1354,16 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) * could dedicate one to rebuild and others to * service read requests .. */ + disk = conf->last_used; + /* make sure disk is operational */ + while (!conf->mirrors[disk].operational) { + if (disk <= 0) disk = conf->raid_disks; + disk--; + if (disk == conf->last_used) + break; + } + conf->last_used = disk; + mirror = conf->mirrors+conf->last_used; r1_bh = raid1_alloc_buf (conf); @@ -1396,7 +1383,7 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) bh->b_list = BUF_LOCKED; bh->b_dev = mirror->dev; bh->b_rdev = mirror->dev; - bh->b_state = (1<b_state = (1<b_page) BUG(); if (!bh->b_data) @@ -1717,19 +1704,10 @@ static int raid1_run (mddev_t *mddev) * find the first working one and use it as a starting point * to read balancing. */ - for (j = 0; !conf->mirrors[j].operational; j++) + for (j = 0; !conf->mirrors[j].operational && j < MD_SB_DISKS; j++) /* nothing */; conf->last_used = j; - /* - * initialize the 'working disks' list. - */ - for (i = conf->raid_disks - 1; i >= 0; i--) { - if (conf->mirrors[i].operational) { - conf->mirrors[i].next = j; - j = i; - } - } if (conf->working_disks != sb->raid_disks) { printk(KERN_ALERT "raid1: md%d, not all disks are operational -- trying to recover array\n", mdidx(mddev)); @@ -1882,19 +1860,16 @@ static mdk_personality_t raid1_personality= sync_request: raid1_sync_request }; -int raid1_init (void) +static int md__init raid1_init (void) { return register_md_personality (RAID1, &raid1_personality); } -#ifdef MODULE -int init_module (void) -{ - return raid1_init(); -} - -void cleanup_module (void) +static void raid1_exit (void) { unregister_md_personality (RAID1); } -#endif + +module_init(raid1_init); +module_exit(raid1_exit); + diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 45bc126df74f..55c50c5e7acf 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2352,19 +2352,16 @@ static mdk_personality_t raid5_personality= sync_request: raid5_sync_request }; -int raid5_init (void) +static int md__init raid5_init (void) { return register_md_personality (RAID5, &raid5_personality); } -#ifdef MODULE -int init_module (void) -{ - return raid5_init(); -} - -void cleanup_module (void) +static void raid5_exit (void) { unregister_md_personality (RAID5); } -#endif + +module_init(raid5_init); +module_exit(raid5_exit); + diff --git a/drivers/media/video/zr36120.h b/drivers/media/video/zr36120.h index e500de10dca1..571f8e84b58a 100644 --- a/drivers/media/video/zr36120.h +++ b/drivers/media/video/zr36120.h @@ -130,7 +130,7 @@ struct zoran int tuner_type; /* tuner type, when found */ int running; /* are we rolling? */ rwlock_t lock; - int state; /* what is requested of us? */ + long state; /* what is requested of us? */ #define STATE_OVERLAY 0 #define STATE_VBI 1 struct vidinfo* workqueue; /* buffers to grab, head is active */ diff --git a/drivers/mtd/Config.in b/drivers/mtd/Config.in index c9747479a1f5..87bf2e9b6857 100644 --- a/drivers/mtd/Config.in +++ b/drivers/mtd/Config.in @@ -1,15 +1,21 @@ -# $Id: Config.in,v 1.20 2000/07/13 12:40:46 scote1 Exp $ +# $Id: No. :) $ mainmenu_option next_comment comment 'Memory Technology Devices (MTD)' tristate 'Memory Technology Device (MTD) support' CONFIG_MTD -if [ "$CONFIG_MTD" != "n" ]; then - dep_tristate ' M-Systems Disk-On-Chip 1000 support' CONFIG_MTD_DOC1000 $CONFIG_MTD - dep_tristate ' M-Systems Disk-On-Chip 2000' CONFIG_MTD_DOC2000 $CONFIG_MTD - dep_tristate ' M-Systems Disk-On-Chip Millennium' CONFIG_MTD_DOC2001 $CONFIG_MTD +if [ "$CONFIG_MTD" = "y" -o "$CONFIG_MTD" = "m" ]; then + bool 'Debugging' CONFIG_MTD_DEBUG + if [ "$CONFIG_MTD_DEBUG" = "y" ]; then + int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0 + fi + +comment 'Disk-On-Chip Device Drivers' + dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver' CONFIG_MTD_DOC2001 $CONFIG_MTD if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then define_tristate CONFIG_MTD_DOCPROBE y else @@ -19,6 +25,13 @@ if [ "$CONFIG_MTD" != "n" ]; then define_tristate CONFIG_MTD_DOCPROBE n fi fi + if [ "$CONFIG_MTD_DOCPROBE" = "y" -o "$CONFIG_MTD_DOCPROBE" = "m" ]; then + hex ' Physical address of DiskOnChip' CONFIG_MTD_DOCPROBE_ADDRESS 0x0000 + bool ' Probe high addresses' CONFIG_MTD_DOCPROBE_HIGH + bool ' Probe for 0x55 0xAA BIOS Extension Signature' CONFIG_MTD_DOCPROBE_55AA + fi + +comment 'RAM/ROM Device Drivers' dep_tristate ' Use extra onboard system memory as MTD device' CONFIG_MTD_SLRAM $CONFIG_MTD dep_tristate ' Ramix PMC551 PCI Mezzanine ram card support' CONFIG_MTD_PMC551 $CONFIG_MTD $CONFIG_PCI if [ "$CONFIG_MTD_PMC551" != "n" ]; then @@ -31,20 +44,21 @@ if [ "$CONFIG_MTD" != "n" ]; then int 'Size of the erase sectors in kB' CONFIG_MTDRAM_ERASE_SIZE 128 fi -comment 'MTD drivers for mapped chips' +comment 'Linearly Mapped Flash Device Drivers' dep_tristate ' Common Flash Interface (CFI) support' CONFIG_MTD_CFI $CONFIG_MTD dep_tristate ' CFI support for Intel/Sharp Extended Command Set chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_CFI dep_tristate ' CFI support for AMD/Fujitsu Standard Command Set chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_CFI + dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD + dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD # These will later become config-options define_bool CONFIG_MTD_JEDEC n -define_bool CONFIG_MTD_RAM n -define_bool CONFIG_MTD_ROM n - dep_tristate ' Flash chip mapping in physical memory' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI - if [ "$CONFIG_MTD_PHYSMAP" != "n" ]; then - hex 'Physical start location of flash chip mapping' CONFIG_MTD_PHYSMAP_START 0x8000000 - hex 'Physical length of flash chip mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000 + dep_tristate ' Flash chip mapping in physical memory' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI + if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then + hex ' Physical start location of flash chip mapping' CONFIG_MTD_PHYSMAP_START 0x8000000 + hex ' Physical length of flash chip mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000 + int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2 fi comment 'Drivers for chip mappings' @@ -57,11 +71,11 @@ comment 'Drivers for chip mappings' comment 'User modules and translation layers for MTD devices' dep_tristate ' Direct chardevice access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD - dep_tristate ' Pseudo-blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD + dep_tristate ' Caching blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD dep_tristate ' FTL (Flash Translation Layer) support' CONFIG_FTL $CONFIG_MTD dep_tristate ' NFTL (NAND Flash Translation Layer) support' CONFIG_NFTL $CONFIG_MTD - if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NFTL" != "n" ]; then - bool ' Write support for NFTL (EXPERIMENTAL)' CONFIG_NFTL_RW + if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then + bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW fi fi diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 3f28868e3899..7fdbf65a4f44 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -44,7 +44,7 @@ SUB_DIRS := ALL_SUB_DIRS := MOD_SUB_DIRS := -export-objs := mtdcore.o +export-objs := mtdcore.o mtdpart.o list-multi := # MTD devices @@ -52,7 +52,7 @@ obj-$(CONFIG_MTD) += mtdcore.o obj-$(CONFIG_MTD_DOC1000) += doc1000.o obj-$(CONFIG_MTD_DOC2000) += doc2000.o obj-$(CONFIG_MTD_DOC2001) += doc2001.o -obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o +obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o obj-$(CONFIG_MTD_SLRAM) += slram.o obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o @@ -70,7 +70,7 @@ obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_MIXMEM) += mixmem.o obj-$(CONFIG_MTD_NORA) += nora.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o -obj-$(CONFIG_MTD_PNC2000) += pnc2000.o +obj-$(CONFIG_MTD_PNC2000) += pnc2000.o mtdpart.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_VMAX) += vmax301.o @@ -78,7 +78,7 @@ obj-$(CONFIG_MTD_VMAX) += vmax301.o obj-$(CONFIG_MTD_CHAR) += mtdchar.o obj-$(CONFIG_MTD_BLOCK) += mtdblock.o obj-$(CONFIG_FTL) += ftl.o -obj-$(CONFIG_NFTL) += nftl.o +obj-$(CONFIG_NFTL) += nftl.o nftlmount.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff --git a/drivers/mtd/doc2000.c b/drivers/mtd/doc2000.c index cef67512b0d7..22ef68e62fda 100644 --- a/drivers/mtd/doc2000.c +++ b/drivers/mtd/doc2000.c @@ -1,8 +1,11 @@ -/* Linux driver for Disk-On-Chip 2000 */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse */ -/* $Id: doc2000.c,v 1.24 2000/07/13 10:03:31 dwmw2 Exp $ */ +/* + * Linux driver for Disk-On-Chip 2000 and Millennium + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * + * $Id: doc2000.c,v 1.39 2000/12/01 17:34:29 dwmw2 Exp $ + */ #include #include @@ -19,286 +22,383 @@ #include #include +#include #include -//#define PRERELEASE +#define DOC_SUPPORT_2000 +#define DOC_SUPPORT_MILLENNIUM -static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); -static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); -static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eecbuf); -static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf); -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); -static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); +#ifdef DOC_SUPPORT_2000 +#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k) +#else +#define DoC_is_2000(doc) (0) +#endif +#ifdef DOC_SUPPORT_MILLENNIUM +#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) +#else +#define DoC_is_Millennium(doc) (0) +#endif + +/* #define ECC_DEBUG */ + +/* I have no idea why some DoC chips can not use memcpy_from|to_io(). + * This may be due to the different revisions of the ASIC controller built-in or + * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment + * this: + #undef USE_MEMCPY +*/ + +static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, u_char *eccbuf); +static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, u_char *eccbuf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, u_char *buf); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, const u_char *buf); +static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); static struct mtd_info *doc2klist = NULL; -/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +/* Perform the required delay cycles by reading from the appropriate register */ +static void DoC_Delay(struct DiskOnChip *doc, unsigned short cycles) +{ + volatile char dummy; + int i; + + for (i = 0; i < cycles; i++) { + if (DoC_is_Millennium(doc)) + dummy = ReadDOC(doc->virtadr, NOP); + else + dummy = ReadDOC(doc->virtadr, DOCStatus); + } + +} -static int _DoC_WaitReady (unsigned long docptr) +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(struct DiskOnChip *doc) { - //long c=-1; - short c=-1; + unsigned long docptr = doc->virtadr; + unsigned short c = 0xffff; - DEBUG(2,"_DoC_WaitReady called for out-of-line wait\n"); + DEBUG(MTD_DEBUG_LEVEL3, + "_DoC_WaitReady called for out-of-line wait\n"); /* Out-of-line routine to wait for chip response */ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c) ; if (c == 0) - DEBUG(2, "_DoC_WaitReady timed out.\n"); - - return (c==0); + DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); + + return (c == 0); } -static inline int DoC_WaitReady(unsigned long docptr) +static inline int DoC_WaitReady(struct DiskOnChip *doc) { + unsigned long docptr = doc->virtadr; /* This is inline, to optimise the common case, where it's ready instantly */ - volatile char dummy; int ret = 0; - /* Out-of-line routine to wait for chip response */ - /* TPW: Add 4 reads - see Software Requirement 2.3.2 */ - dummy = ReadDOC(docptr, CDSNControl); - dummy = ReadDOC(docptr, CDSNControl); - dummy = ReadDOC(docptr, CDSNControl); - dummy = ReadDOC(docptr, CDSNControl); - + /* 4 read form NOP register should be issued in prior to the read from CDSNControl + see Software Requirement 11.4 item 2. */ + DoC_Delay(doc, 4); + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) - ret = _DoC_WaitReady(docptr); /* Call the out-of-line routine to wait */ - - /* TPW: Add 2 reads - see Software Requirement 2.3.2 */ - dummy = ReadDOC(docptr, CDSNControl); - dummy = ReadDOC(docptr, CDSNControl); + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + + /* issue 2 read from NOP register after reading from CDSNControl register + see Software Requirement 11.4 item 2. */ + DoC_Delay(doc, 2); return ret; } +/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to + bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -/* DoC_Command: Send a flash command to the flash chip */ - -static inline int DoC_Command(unsigned long docptr, unsigned char command, unsigned char xtraflags) +static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, + unsigned char xtraflags) { + unsigned long docptr = doc->virtadr; + + if (DoC_is_2000(doc)) + xtraflags |= CDSN_CTRL_FLASH_IO; + /* Assert the CLE (Command Latch Enable) line to the flash chip */ - WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, - docptr, CDSNControl); + WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + if (DoC_is_Millennium(doc)) + WriteDOC(command, docptr, CDSNSlowIO); /* Send the command */ - WriteDOC(command, docptr, 2k_CDSN_IO); - + WriteDOC_(command, docptr, doc->ioreg); + /* Lower the CLE line */ - WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); + WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ - /* Wait for the chip to respond */ - return DoC_WaitReady(docptr); + /* Wait for the chip to respond - Software requirement 11.4.1 (extended for any command) */ + return DoC_WaitReady(doc); } -/* DoC_Address: Set the current address for the flash chip */ +/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to + bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -static inline int DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs, - unsigned char xtraflags1, unsigned char xtraflags2) +static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs, + unsigned char xtraflags1, unsigned char xtraflags2) { - /* Assert the ALE (Address Latch Enable line to the flash chip */ - WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, - docptr, CDSNControl); + unsigned long docptr; + int i; + + docptr = doc->virtadr; + + if (DoC_is_2000(doc)) + xtraflags1 |= CDSN_CTRL_FLASH_IO; + + /* Assert the ALE (Address Latch Enable) line to the flash chip */ + WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl); + + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ /* Send the address */ - /* Three cases: - numbytes == 1: Send single byte, bits 0-7. - numbytes == 2: Send bits 9-16 followed by 17-23 - numbytes == 3: Send 0-7, 9-16, then 17-23 - */ - if (numbytes != 2) - WriteDOC(ofs & 0xff, docptr, 2k_CDSN_IO); - - if (numbytes != 1) { - WriteDOC((ofs >> 9) & 0xff, docptr, 2k_CDSN_IO); - WriteDOC((ofs >> 17) & 0xff, docptr, 2k_CDSN_IO); + /* Devices with 256-byte page are addressed as: + Column (bits 0-7), Page (bits 8-15, 16-23, 24-31) + * there is no device on the market with page256 + and more than 24 bits. + Devices with 512-byte page are addressed as: + Column (bits 0-7), Page (bits 9-16, 17-24, 25-31) + * 25-31 is sent only if the chip support it. + * bit 8 changes the read command to be sent + (NAND_CMD_READ0 or NAND_CMD_READ1). + */ + + if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) { + if (DoC_is_Millennium(doc)) + WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); + WriteDOC_(ofs & 0xff, docptr, doc->ioreg); } - /* Lower the ALE line */ - WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl); + + if (doc->page256) { + ofs = ofs >> 8; + } else { + ofs = ofs >> 9; + } + + if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) { + for (i = 0; i < doc->pageadrlen; i++, ofs = ofs >> 8) { + if (DoC_is_Millennium(doc)) + WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); + WriteDOC_(ofs & 0xff, docptr, doc->ioreg); + } + } + + DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ - /* Wait for the chip to respond */ - return DoC_WaitReady(docptr); + /* FIXME: The SlowIO's for millennium could be replaced by + a single WritePipeTerm here. mf. */ + + /* Lower the ALE line */ + WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, + CDSNControl); + + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + /* Wait for the chip to respond - Software requirement 11.4.1 */ + return DoC_WaitReady(doc); +} + +/* Read a buffer from DoC, taking care of Millennium odditys */ +static void DoC_ReadBuf(struct DiskOnChip *doc, u_char * buf, int len) +{ + int dummy; + int modulus = 0xffff; + unsigned long docptr; + int i; + + docptr = doc->virtadr; + + if (len <= 0) + return; + + if (DoC_is_Millennium(doc)) { + /* Read the data via the internal pipeline through CDSN IO register, + see Pipelined Read Operations 11.3 */ + dummy = ReadDOC(docptr, ReadPipeInit); + + /* Millennium should use the LastDataRead register - Pipeline Reads */ + len--; + + /* This is needed for correctly ECC calculation */ + modulus = 0xff; + } + + for (i = 0; i < len; i++) + buf[i] = ReadDOC_(docptr, doc->ioreg + (i & modulus)); + + if (DoC_is_Millennium(doc)) { + buf[i] = ReadDOC(docptr, LastDataRead); + } +} + +/* Write a buffer to DoC, taking care of Millennium odditys */ +static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len) +{ + unsigned long docptr; + int i; + + docptr = doc->virtadr; + + if (len <= 0) + return; + + for (i = 0; i < len; i++) + WriteDOC_(buf[i], docptr, doc->ioreg + i); + + if (DoC_is_Millennium(doc)) { + WriteDOC(0x00, docptr, WritePipeTerm); + } } + /* DoC_SelectChip: Select a given flash chip within the current floor */ -static inline int DoC_SelectChip(unsigned long docptr, int chip) +static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip) { + unsigned long docptr = doc->virtadr; + + /* Software requirement 11.4.4 before writing DeviceSelect */ + /* Deassert the CE line to eliminate glitches on the FCE# outputs */ + WriteDOC(CDSN_CTRL_WP, docptr, CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + /* Select the individual flash chip requested */ - WriteDOC( chip, docptr, CDSNDeviceSelect); - + WriteDOC(chip, docptr, CDSNDeviceSelect); + DoC_Delay(doc, 4); + + /* Reassert the CE line */ + WriteDOC(CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP, docptr, + CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + /* Wait for it to be ready */ - return DoC_WaitReady(docptr); + return DoC_WaitReady(doc); } /* DoC_SelectFloor: Select a given floor (bank of flash chips) */ -static inline int DoC_SelectFloor(unsigned long docptr, int floor) +static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor) { + unsigned long docptr = doc->virtadr; + /* Select the floor (bank) of chips required */ - WriteDOC( floor, docptr, FloorSelect); + WriteDOC(floor, docptr, FloorSelect); /* Wait for the chip to be ready */ - return DoC_WaitReady(docptr); + return DoC_WaitReady(doc); } - + /* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) { - int mfr, id, chipshift=0; - char *mfrname=NULL, *idname=NULL; + int mfr, id, i; + volatile char dummy; /* Page in the required floor/chip */ - DoC_SelectFloor(doc->virtadr, floor); - DoC_SelectChip(doc->virtadr, chip); + DoC_SelectFloor(doc, floor); + DoC_SelectChip(doc, chip); /* Reset the chip */ - if (DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP)) { - DEBUG(2, "DoC_Command (reset) for %d,%d returned true\n", floor,chip); + if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) { + DEBUG(MTD_DEBUG_LEVEL2, + "DoC_Command (reset) for %d,%d returned true\n", + floor, chip); return 0; } - - /* Read the NAND chip ID: 1. Send ReadID command */ - if(DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP)) { - DEBUG(2,"DoC_Command (ReadID) for %d,%d returned true\n", floor,chip); + + + /* Read the NAND chip ID: 1. Send ReadID command */ + if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) { + DEBUG(MTD_DEBUG_LEVEL2, + "DoC_Command (ReadID) for %d,%d returned true\n", + floor, chip); return 0; } - /* Read the NAND chip ID: 2. Send address byte zero - */ - DoC_Address(doc->virtadr, 1, 0, CDSN_CTRL_WP, 0); - + /* Read the NAND chip ID: 2. Send address byte zero */ + DoC_Address(doc, ADDR_COLUMN, 0, CDSN_CTRL_WP, 0); + /* Read the manufacturer and device id codes from the device */ - mfr = ReadDOC(doc->virtadr, 2k_CDSN_IO); - id = ReadDOC(doc->virtadr, 2k_CDSN_IO); - + + /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + mfr = ReadDOC_(doc->virtadr, doc->ioreg); + + /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + id = ReadDOC_(doc->virtadr, doc->ioreg); + /* No response - return failure */ if (mfr == 0xff || mfr == 0) return 0; - + /* Check it's the same as the first chip we identified. * M-Systems say that any given DiskOnChip device should only * contain _one_ type of flash part, although that's not a * hardware restriction. */ if (doc->mfr) { if (doc->mfr == mfr && doc->id == id) - return 1; /* This is another the same the first */ + return 1; /* This is another the same the first */ else - printk(KERN_WARNING "Flash chip at floor %d, chip %d is different:\n", + printk(KERN_WARNING + "Flash chip at floor %d, chip %d is different:\n", floor, chip); } - - /* Print (and store if first time) the manufacturer and ID codes. */ - - switch(mfr) { - case NAND_MFR_TOSHIBA: /* Toshiba */ - mfrname = "Toshiba"; - - switch(id) { - case 0x64: - idname = "TC5816BDC"; - chipshift = 21; - break; - - case 0x6b: - idname = "TC5832DC"; - chipshift = 22; - break; - - case 0x73: - idname = "TH58V128DC"; - chipshift = 24; - break; - - case 0x75: - idname = "TC58256FT/DC"; - chipshift = 25; - break; - - case 0xe5: - idname = "TC58V32DC"; - chipshift = 22; - break; - - case 0xe6: - idname = "TC58V64DC"; - chipshift = 23; - break; - - case 0xea: - idname = "TC58V16BDC"; - chipshift = 21; - break; - } - break; /* End of Toshiba parts */ - - case NAND_MFR_SAMSUNG: /* Samsung */ - mfrname = "Samsung"; - - switch(id) { - case 0x64: - idname = "KM29N16000"; - chipshift = 21; - - case 0x73: - idname = "KM29U128T"; - chipshift = 24; - break; - - case 0x75: - idname = "KM29U256T"; - chipshift = 25; - break; - - case 0xe3: - idname = "KM29W32000"; - chipshift = 22; - break; - - case 0xe6: - idname = "KM29U64000"; - chipshift = 23; - break; - - case 0xea: - idname = "KM29W16000"; - chipshift = 21; - break; - } - break; /* End of Samsung parts */ - } - - /* If we've identified it fully, print the full names */ - if (idname) { -#ifdef PRERELEASE - DEBUG(1, "Flash chip found: %2.2X %2.2X (%s %s)\n", - mfr,id,mfrname,idname); -#endif - /* If this is the first chip, store the id codes */ - if (!doc->mfr) { - doc->mfr = mfr; - doc->id = id; - doc->chipshift = chipshift; - return 1; + + /* Print and store the manufacturer and ID codes. */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (mfr == nand_flash_ids[i].manufacture_id && + id == nand_flash_ids[i].model_id) { + printk(KERN_INFO + "Flash chip found: Manufacturer ID: %2.2X, " + "Chip ID: %2.2X (%s)\n", mfr, id, + nand_flash_ids[i].name); + if (!doc->mfr) { + doc->mfr = mfr; + doc->id = id; + doc->chipshift = + nand_flash_ids[i].chipshift; + doc->page256 = nand_flash_ids[i].page256; + doc->pageadrlen = + nand_flash_ids[i].pageadrlen; + doc->erasesize = + nand_flash_ids[i].erasesize; + return 1; + } + return 0; } - return 0; } + /* We haven't fully identified the chip. Print as much as we know. */ - if (mfrname) - printk(KERN_WARNING "Unknown %s flash chip found: %2.2X %2.2X\n", mfrname, - id, mfr); - else - printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n", id, mfr); - - printk(KERN_WARNING "Please report to David.Woodhouse@mvhi.com\n"); + printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n", + id, mfr); + + printk(KERN_WARNING "Please report to dwmw2@infradead.org\n"); return 0; -} +} /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ @@ -306,18 +406,22 @@ static void DoC_ScanChips(struct DiskOnChip *this) { int floor, chip; int numchips[MAX_FLOORS]; + int maxchips = MAX_CHIPS; int ret = 1; - + this->numchips = 0; this->mfr = 0; this->id = 0; - + + if (DoC_is_Millennium(this)) + maxchips = MAX_CHIPS_MIL; + /* For each floor, find the number of valid chips it contains */ - for (floor = 0 ; floor < MAX_FLOORS ; floor++) { + for (floor = 0; floor < MAX_FLOORS; floor++) { ret = 1; - numchips[floor]=0; - for (chip = 0 ; chip < MAX_CHIPS && ret != 0; chip++ ) { - + numchips[floor] = 0; + for (chip = 0; chip < maxchips && ret != 0; chip++) { + ret = DoC_IdentChip(this, floor, chip); if (ret) { numchips[floor]++; @@ -325,26 +429,26 @@ static void DoC_ScanChips(struct DiskOnChip *this) } } } - + /* If there are none at all that we recognise, bail */ if (!this->numchips) { printk("No flash chips recognised.\n"); return; } - + /* Allocate an array to hold the information for each chip */ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); - if (!this->chips){ + if (!this->chips) { printk("No memory for allocating chip info structures\n"); return; } - + ret = 0; - + /* Fill out the chip array with {floor, chipno} for each * detected chip in the device. */ for (floor = 0; floor < MAX_FLOORS; floor++) { - for (chip = 0 ; chip < numchips[floor] ; chip++) { + for (chip = 0; chip < numchips[floor]; chip++) { this->chips[ret].floor = floor; this->chips[ret].chip = chip; this->chips[ret].curadr = 0; @@ -356,8 +460,9 @@ static void DoC_ScanChips(struct DiskOnChip *this) /* Calculate and print the total size of the device */ this->totlen = this->numchips * (1 << this->chipshift); - printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mb\n", this->numchips , - this->totlen >> 20); + printk(KERN_INFO + "%d flash chips found. Total DiskOnChip size: %ld Mb\n", + this->numchips, this->totlen >> 20); } @@ -371,15 +476,15 @@ static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) * purpose. If it's value is the same on both chips, they might * be the same chip, and we write to one and check for a change in * the other. It's unclear if this register is usuable in the - * DoC 2000 (it's in the Millenium docs), but it seems to work. */ + * DoC 2000 (it's in the Millennium docs), but it seems to work. */ tmp1 = ReadDOC(doc1->virtadr, AliasResolution); tmp2 = ReadDOC(doc2->virtadr, AliasResolution); if (tmp1 != tmp2) return 0; - - WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution); + + WriteDOC((tmp1 + 1) % 0xff, doc1->virtadr, AliasResolution); tmp2 = ReadDOC(doc2->virtadr, AliasResolution); - if (tmp2 == (tmp1+1) % 0xff) + if (tmp2 == (tmp1 + 1) % 0xff) retval = 1; else retval = 0; @@ -387,11 +492,10 @@ static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) /* Restore register contents. May not be necessary, but do it just to * be safe. */ WriteDOC(tmp1, doc1->virtadr, AliasResolution); - + return retval; } - static const char im_name[] = "DoC2k_init"; /* This routine is made available to other mtd code via @@ -403,36 +507,48 @@ static const char im_name[] = "DoC2k_init"; */ static void DoC2k_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (doc2klist) - old = (struct DiskOnChip *)doc2klist->priv; + old = (struct DiskOnChip *) doc2klist->priv; while (old) { if (DoC2k_is_alias(old, this)) { - printk(KERN_NOTICE "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n", + printk(KERN_NOTICE + "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n", this->physadr); - iounmap((void *)this->virtadr); + iounmap((void *) this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = (struct DiskOnChip *) old->nextdoc->priv; else old = NULL; } - - - mtd->name = "DiskOnChip 2000"; - printk(KERN_NOTICE "DiskOnChip 2000 found at address 0x%lX\n",this->physadr); + + + switch (this->ChipID) { + case DOC_ChipID_Doc2k: + mtd->name = "DiskOnChip 2000"; + this->ioreg = DoC_2k_CDSN_IO; + break; + case DOC_ChipID_DocMil: + mtd->name = "DiskOnChip Millennium"; + this->ioreg = DoC_Mil_CDSN_IO; + break; + } + + printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, + this->physadr); mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; mtd->size = 0; - mtd->erasesize = 0x2000; + mtd->erasesize = 0; mtd->oobblock = 512; mtd->oobsize = 16; mtd->module = THIS_MODULE; @@ -446,131 +562,182 @@ static void DoC2k_init(struct mtd_info *mtd) mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; - + this->totlen = 0; this->numchips = 0; - + this->curfloor = -1; this->curchip = -1; - + /* Ident all the chips present. */ DoC_ScanChips(this); - + if (!this->totlen) { kfree(mtd); - iounmap((void *)this->virtadr); + iounmap((void *) this->virtadr); } else { this->nextdoc = doc2klist; doc2klist = mtd; - mtd->size = this->totlen; + mtd->size = this->totlen; + mtd->erasesize = this->erasesize; add_mtd_device(mtd); return; } } - -static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) { /* Just a special case of doc_read_ecc */ return doc_read_ecc(mtd, from, len, retlen, buf, NULL); } -static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf) +static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * eccbuf) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - int di=0; /* Yes, DI is a hangover from when I was disassembling the binary driver */ + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; unsigned long docptr; struct Nand *mychip; + unsigned char syndrome[6]; + volatile char dummy; + int i, len256 = 0, ret=0; docptr = this->virtadr; /* Don't allow read past end of device */ if (from >= this->totlen) return -EINVAL; - + /* Don't allow a single read to cross a 512-byte block boundary */ - if (from + len > ( (from | 0x1ff) + 1)) + if (from + len > ((from | 0x1ff) + 1)) len = ((from | 0x1ff) + 1) - from; + /* The ECC will not be calculated correctly if less than 512 is read */ + if (len != 0x200 && eccbuf) + printk(KERN_WARNING + "ECC needs a full sector read (adr: %lx size %lx)\n", + (long) from, (long) len); + + /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */ + + /* Find the chip which is to be used and select it */ mychip = &this->chips[from >> (this->chipshift)]; - + if (this->curfloor != mychip->floor) { - DoC_SelectFloor(docptr, mychip->floor); - DoC_SelectChip(docptr, mychip->chip); + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); } - else if (this->curchip != mychip->chip) { - DoC_SelectChip(docptr, mychip->chip); - } - + this->curfloor = mychip->floor; this->curchip = mychip->chip; - + + DoC_Command(this, + (!this->page256 + && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, + CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP, + CDSN_CTRL_ECC_IO); if (eccbuf) { /* Prime the ECC engine */ - WriteDOC ( DOC_ECC_RESET, docptr, ECCConf); - WriteDOC ( DOC_ECC_EN, docptr, ECCConf); + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN, docptr, ECCConf); + } else { + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); } - DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP); - DoC_Address(docptr, 3, from, CDSN_CTRL_WP , CDSN_CTRL_ECC_IO); + /* treat crossing 256-byte sector for 2M x 8bits devices */ + if (this->page256 && from + len > (from | 0xff) + 1) { + len256 = (from | 0xff) + 1 - from; + DoC_ReadBuf(this, buf, len256); - for (di=0; di < len ; di++) { - buf[di] = ReadDOC(docptr, 2k_CDSN_IO); + DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, from + len256, + CDSN_CTRL_WP, CDSN_CTRL_ECC_IO); } + DoC_ReadBuf(this, &buf[len256], len - len256); + /* Let the caller know we completed it */ *retlen = len; if (eccbuf) { /* Read the ECC data through the DiskOnChip ECC logic */ - for (di=0; di<6; di++) { - eccbuf[di] = ReadDOC(docptr, 2k_CDSN_IO); - } - + /* Note: this will work even with 2M x 8bit devices as */ + /* they have 8 bytes of OOB per 256 page. mf. */ + DoC_ReadBuf(this, eccbuf, 6); + /* Flush the pipeline */ - (void) ReadDOC(docptr, 2k_ECCStatus); - (void) ReadDOC(docptr, 2k_ECCStatus); - + if (DoC_is_Millennium(this)) { + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + i = ReadDOC(docptr, ECCConf); + } else { + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + i = ReadDOC(docptr, 2k_ECCStatus); + } + /* Check the ECC Status */ - if (ReadDOC(docptr, 2k_ECCStatus) & 0x80) { + if (i & 0x80) { + int nb_errors; /* There was an ECC error */ +#ifdef ECC_DEBUG printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); +#endif + /* Read the ECC syndrom through the DiskOnChip ECC logic. + These syndrome will be all ZERO when there is no error */ + for (i = 0; i < 6; i++) { + syndrome[i] = + ReadDOC(docptr, ECCSyndrome0 + i); + } + nb_errors = doc_decode_ecc(buf, syndrome); - /* FIXME: Implement ECC error correction, don't just whinge */ - - /* We return error, but have actually done the read. Not that - this can be told to user-space, via sys_read(), but at least - MTD-aware stuff can know about it by checking *retlen */ - return -EIO; +#ifdef ECC_DEBUG + printk("Errors corrected: %x\n", nb_errors); +#endif + if (nb_errors < 0) { + /* We return error, but have actually done the read. Not that + this can be told to user-space, via sys_read(), but at least + MTD-aware stuff can know about it by checking *retlen */ + ret = -EIO; + } } + #ifdef PSYCHO_DEBUG - else - printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", - (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4], - eccbuf[5]); + printk("ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long)from, eccbuf[0], eccbuf[1], eccbuf[2], + eccbuf[3], eccbuf[4], eccbuf[5]); #endif - /* Reset the ECC engine */ - WriteDOC(DOC_ECC_RESV, docptr , ECCConf); - + /* disable the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr , ECCConf); } - return 0; + return ret; } -static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) { - static char as[6]; - return doc_write_ecc(mtd, to, len, retlen, buf, as); + char eccbuf[6]; + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf); } -static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf) +static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, + u_char * eccbuf) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - int di=0; + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ unsigned long docptr; + volatile char dummy; + int len256 = 0; struct Nand *mychip; docptr = this->virtadr; @@ -578,82 +745,118 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *r /* Don't allow write past end of device */ if (to >= this->totlen) return -EINVAL; -#if 0 + /* Don't allow a single write to cross a 512-byte block boundary */ - if (to + len > ( (to | 0x1ff) + 1)) + if (to + len > ((to | 0x1ff) + 1)) len = ((to | 0x1ff) + 1) - to; -#else - /* Don't allow writes which aren't exactly one block */ - if (to & 0x1ff || len != 0x200) - return -EINVAL; -#endif + /* The ECC will not be calculated correctly if less than 512 is written */ + if (len != 0x200 && eccbuf) + printk(KERN_WARNING + "ECC needs a full sector write (adr: %lx size %lx)\n", + (long) to, (long) len); + + /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ /* Find the chip which is to be used and select it */ mychip = &this->chips[to >> (this->chipshift)]; - + if (this->curfloor != mychip->floor) { - DoC_SelectFloor(docptr, mychip->floor); - DoC_SelectChip(docptr, mychip->chip); + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); } - else if (this->curchip != mychip->chip) { - DoC_SelectChip(docptr, mychip->chip); - } - + this->curfloor = mychip->floor; this->curchip = mychip->chip; - + /* Set device to main plane of flash */ - DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); - DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP); + DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(this, + (!this->page256 + && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, + CDSN_CTRL_WP); + + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO); if (eccbuf) { /* Prime the ECC engine */ - WriteDOC ( DOC_ECC_RESET, docptr, ECCConf); - WriteDOC ( DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + } else { + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); } - DoC_Command(docptr, NAND_CMD_SEQIN, 0); - DoC_Address(docptr, 3, to, 0, CDSN_CTRL_ECC_IO); + /* treat crossing 256-byte sector for 2M x 8bits devices */ + if (this->page256 && to + len > (to | 0xff) + 1) { + len256 = (to | 0xff) + 1 - to; + DoC_WriteBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); - for (di=0; di < len ; di++) { - WriteDOC(buf[di], docptr, 2k_CDSN_IO); + if (ReadDOC_(docptr, this->ioreg) & 1) { + printk("Error programming flash\n"); + /* Error in programming */ + *retlen = 0; + return -EIO; + } + + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0, + CDSN_CTRL_ECC_IO); } + DoC_WriteBuf(this, &buf[len256], len - len256); if (eccbuf) { - WriteDOC( CDSN_CTRL_ECC_IO | CDSN_CTRL_CE , docptr, CDSNControl ); - -#if 1 - /* eduardp@m-sys.com says this shouldn't be necessary, - * but it doesn't actually work without it, so I've - * left it in for now. dwmw2. - */ - - WriteDOC( 0, docptr, 2k_CDSN_IO); - WriteDOC( 0, docptr, 2k_CDSN_IO); - WriteDOC( 0, docptr, 2k_CDSN_IO); -#endif + WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr, + CDSNControl); + + if (DoC_is_Millennium(this)) { + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + } else { + WriteDOC_(0, docptr, this->ioreg); + WriteDOC_(0, docptr, this->ioreg); + WriteDOC_(0, docptr, this->ioreg); + } + /* Read the ECC data through the DiskOnChip ECC logic */ - for (di=0; di<6; di++) { + for (di = 0; di < 6; di++) { eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); } + + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + #ifdef PSYCHO_DEBUG - printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", - (long) to, eccbuf[0], eccbuf[1], eccbuf[2], - eccbuf[3], eccbuf[4], eccbuf[5] ); + printk + ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], + eccbuf[4], eccbuf[5]); #endif - /* Reset the ECC engine */ - WriteDOC(DOC_ECC_RESV, docptr , ECCConf); - } - DoC_Command(docptr, NAND_CMD_PAGEPROG, 0); + DoC_Command(this, NAND_CMD_PAGEPROG, 0); - DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); /* There's an implicit DoC_WaitReady() in DoC_Command */ - if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { printk("Error programming flash\n"); /* Error in programming */ *retlen = 0; @@ -662,84 +865,155 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *r /* Let the caller know we completed it */ *retlen = len; + + if (eccbuf) { + unsigned char x[8]; + size_t dummy; + + /* Write the ECC data to flash */ + for (di=0; di<6; di++) + x[di] = eccbuf[di]; + + x[6]=0x55; + x[7]=0x55; + + return doc_write_oob(mtd, to, 8, &dummy, x); + } return 0; } - - -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t * retlen, u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - int i; + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + int len256 = 0; unsigned long docptr; struct Nand *mychip; - + docptr = this->virtadr; - + mychip = &this->chips[ofs >> this->chipshift]; - + if (this->curfloor != mychip->floor) { - DoC_SelectFloor(docptr, mychip->floor); - DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { - DoC_SelectChip(docptr, mychip->chip); + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); } this->curfloor = mychip->floor; this->curchip = mychip->chip; - - - - DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); - DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0); - - for (i=0; ipage256) { + if (!(ofs & 0x8)) + ofs += 0x100; + else + ofs -= 0x8; + } + + DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs, CDSN_CTRL_WP, 0); + + /* treat crossing 8-byte OOB data for 2M x 8bit devices */ + /* Note: datasheet says it should automaticaly wrap to the */ + /* next OOB block, but it didn't work here. mf. */ + if (this->page256 && ofs + len > (ofs | 0x7) + 1) { + len256 = (ofs | 0x7) + 1 - ofs; + DoC_ReadBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), + CDSN_CTRL_WP, 0); + } + + DoC_ReadBuf(this, &buf[len256], len - len256); + *retlen = len; return 0; } -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - int i; - unsigned long docptr; - struct Nand *mychip; + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + int len256 = 0; + unsigned long docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + int dummy; - // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, + // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); - docptr = this->virtadr; - - mychip = &this->chips[ofs >> this->chipshift]; - + /* Find the chip which is to be used and select it */ if (this->curfloor != mychip->floor) { - DoC_SelectFloor(docptr, mychip->floor); - DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { - DoC_SelectChip(docptr, mychip->chip); + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); } this->curfloor = mychip->floor; this->curchip = mychip->chip; - - DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); - DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); - DoC_Command(docptr, NAND_CMD_SEQIN, 0); - DoC_Address(docptr, 3, ofs, 0, 0); - - for (i=0; ipage256) { + if (!(ofs & 0x8)) + ofs += 0x100; + else + ofs -= 0x8; + } + + /* issue the Serial Data In command to initial the Page Program process */ + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs, 0, 0); + + /* treat crossing 8-byte OOB data for 2M x 8bit devices */ + /* Note: datasheet says it should automaticaly wrap to the */ + /* next OOB block, but it didn't work here. mf. */ + if (this->page256 && ofs + len > (ofs | 0x7) + 1) { + len256 = (ofs | 0x7) + 1 - ofs; + DoC_WriteBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { + printk("Error programming oob data\n"); + /* There was an error */ + *retlen = 0; + return -EIO; + } + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), 0, 0); + } + + DoC_WriteBuf(this, &buf[len256], len - len256); + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + DoC_Command(this, NAND_CMD_STATUS, 0); /* DoC_WaitReady() is implicit in DoC_Command */ - if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { printk("Error programming oob data\n"); /* There was an error */ *retlen = 0; @@ -751,102 +1025,89 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *r } - -int doc_erase (struct mtd_info *mtd, struct erase_info *instr) +int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; unsigned long ofs = instr->addr; unsigned long len = instr->len; unsigned long docptr; struct Nand *mychip; - - if(len != mtd->erasesize) - printk(KERN_WARNING "Erase not right size (%lx != %lx)n", len, mtd->erasesize); - + + if (len != mtd->erasesize) + printk(KERN_WARNING "Erase not right size (%lx != %lx)n", + len, mtd->erasesize); docptr = this->virtadr; - + mychip = &this->chips[ofs >> this->chipshift]; - + if (this->curfloor != mychip->floor) { - DoC_SelectFloor(docptr, mychip->floor); - DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { - DoC_SelectChip(docptr, mychip->chip); + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); } this->curfloor = mychip->floor; this->curchip = mychip->chip; - + instr->state = MTD_ERASE_PENDING; - DoC_Command(docptr, NAND_CMD_ERASE1, 0); - DoC_Address(docptr, 2, ofs, 0, 0); - DoC_Command(docptr, NAND_CMD_ERASE2, 0); + DoC_Command(this, NAND_CMD_ERASE1, 0); + DoC_Address(this, ADDR_PAGE, ofs, 0, 0); + DoC_Command(this, NAND_CMD_ERASE2, 0); instr->state = MTD_ERASING; - DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); - if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + if (ReadDOC_(docptr, this->ioreg) & 1) { printk("Error writing\n"); /* There was an error */ instr->state = MTD_ERASE_FAILED; - } - else + } else instr->state = MTD_ERASE_DONE; - if (instr->callback) + if (instr->callback) instr->callback(instr); - + return 0; } - - - /**************************************************************************** * * Module stuff * ****************************************************************************/ -static int __init init_doc2000(void) -{ - inter_module_register(im_name, THIS_MODULE, &DoC2k_init); - return 0; -} - -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define cleanup_doc2000 cleanup_module -#endif -#define __exit +#define init_doc2000 init_module #endif +int __init init_doc2000(void) +{ + inter_module_register(im_name, THIS_MODULE, &DoC2k_init); + return 0; +} static void __exit cleanup_doc2000(void) { struct mtd_info *mtd; struct DiskOnChip *this; - while((mtd=doc2klist)) { - this = (struct DiskOnChip *)mtd->priv; + while ((mtd = doc2klist)) { + this = (struct DiskOnChip *) mtd->priv; doc2klist = this->nextdoc; - + del_mtd_device(mtd); - - iounmap((void *)this->virtadr); + + iounmap((void *) this->virtadr); kfree(this->chips); kfree(mtd); } inter_module_unregister(im_name); - } -module_init(init_doc2000); - -#if LINUX_VERSION_CODE > 0x20300 module_exit(cleanup_doc2000); -#endif +module_init(init_doc2000); diff --git a/drivers/mtd/doc2001.c b/drivers/mtd/doc2001.c index 8a9f032357f4..79aa3630dbc3 100644 --- a/drivers/mtd/doc2001.c +++ b/drivers/mtd/doc2001.c @@ -1,7 +1,11 @@ -/* Linux driver for Disk-On-Chip Millennium */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse */ -/* $Id: doc2001.c,v 1.7 2000/07/13 10:41:39 dwmw2 Exp $ */ + +/* + * Linux driver for Disk-On-Chip Millennium + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * + * $Id: doc2001.c,v 1.24 2000/12/01 13:11:02 dwmw2 Exp $ + */ #include #include @@ -18,38 +22,26 @@ #include #include +#include #include -static struct { - char * name; - int manufacture_id; - int model_id; - int chipshift; -} nand_flash_ids[] = { - {"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21}, - {"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22}, - {"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24}, - {"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25}, - {"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22}, - {"Toshiba TC58V64DC", NAND_MFR_TOSHIBA, 0xe6, 23}, - {"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21}, - {"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21}, - {"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24}, - {"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25}, - {"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22}, - {"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23}, - {"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21}, - {NULL,} -}; - -static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); -static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf); -static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eecbuf); -static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, u_char *eccbuf); +/* #define ECC_DEBUG */ + +/* I have no idea why some DoC chips can not use memcop_form|to_io(). + * This may be due to the different revisions of the ASIC controller built-in or + * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment + * this: + #undef USE_MEMCPY +*/ + +static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, u_char *eccbuf); +static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, u_char *eccbuf); static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, @@ -58,6 +50,7 @@ static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); static struct mtd_info *docmillist = NULL; +/* Perform the required delay cycles by reading from the NOP register */ static void DoC_Delay(unsigned long docptr, unsigned short cycles) { volatile char dummy; @@ -72,14 +65,20 @@ static int _DoC_WaitReady(unsigned long docptr) { unsigned short c = 0xffff; + DEBUG(MTD_DEBUG_LEVEL3, + "_DoC_WaitReady called for out-of-line wait\n"); + /* Out-of-line routine to wait for chip response */ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c) ; + if (c == 0) + DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); + return (c == 0); } -static __inline__ int DoC_WaitReady(unsigned long docptr) +static inline int DoC_WaitReady(unsigned long docptr) { /* This is inline, to optimise the common case, where it's ready instantly */ int ret = 0; @@ -99,33 +98,35 @@ static __inline__ int DoC_WaitReady(unsigned long docptr) return ret; } -/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to bypass - the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after - writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -static __inline__ void DoC_Command(unsigned long docptr, unsigned char command, - unsigned char xtraflags) +/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to + bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ + +static inline void DoC_Command(unsigned long docptr, unsigned char command, + unsigned char xtraflags) { /* Assert the CLE (Command Latch Enable) line to the flash chip */ - WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl); + WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl); DoC_Delay(docptr, 4); /* Send the command */ WriteDOC(command, docptr, CDSNSlowIO); WriteDOC(command, docptr, Mil_CDSN_IO); - + /* Lower the CLE line */ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); DoC_Delay(docptr, 4); } -/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to bypass - the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after - writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -static __inline__ void DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs, +/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to + bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ + +static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long ofs, unsigned char xtraflags1, unsigned char xtraflags2) { - /* Assert the ALE (Address Latch Enable line to the flash chip */ - WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl); + /* Assert the ALE (Address Latch Enable) line to the flash chip */ + WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl); DoC_Delay(docptr, 4); /* Send the address */ @@ -217,11 +218,11 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) if (mfr == 0xff || mfr == 0) return 0; - /* FIXME: to deal with mulit-flash on multi-Millennium case more carefully */ + /* FIXME: to deal with multi-flash on multi-Millennium case more carefully */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (mfr == nand_flash_ids[i].manufacture_id && id == nand_flash_ids[i].model_id) { - printk(KERN_INFO "Flash chip found: Manufacture ID: %2.2X, " + printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " "Chip ID: %2.2X (%s)\n", mfr, id, nand_flash_ids[i].name); doc->mfr = mfr; @@ -235,7 +236,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) return 0; else return 1; -} +} /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ static void DoC_ScanChips(struct DiskOnChip *this) @@ -243,11 +244,11 @@ static void DoC_ScanChips(struct DiskOnChip *this) int floor, chip; int numchips[MAX_FLOORS_MIL]; int ret; - + this->numchips = 0; this->mfr = 0; this->id = 0; - + /* For each floor, find the number of valid chips it contains */ for (floor = 0,ret = 1; floor < MAX_FLOORS_MIL; floor++) { numchips[floor] = 0; @@ -264,14 +265,14 @@ static void DoC_ScanChips(struct DiskOnChip *this) printk("No flash chips recognised.\n"); return; } - + /* Allocate an array to hold the information for each chip */ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); if (!this->chips){ printk("No memory for allocating chip info structures\n"); return; } - + /* Fill out the chip array with {floor, chipno} for each * detected chip in the device. */ for (floor = 0, ret = 0; floor < MAX_FLOORS_MIL; floor++) { @@ -286,7 +287,7 @@ static void DoC_ScanChips(struct DiskOnChip *this) /* Calculate and print the total size of the device */ this->totlen = this->numchips * (1 << this->chipshift); - printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mbytes\n", + printk(KERN_NOTICE "%d flash chips found. Total DiskOnChip size: %ld Mbytes\n", this->numchips ,this->totlen >> 20); } @@ -317,7 +318,7 @@ static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) /* Restore register contents. May not be necessary, but do it just to * be safe. */ WriteDOC(tmp1, doc1->virtadr, AliasResolution); - + return retval; } @@ -330,7 +331,7 @@ static const char im_name[] = "DoCMil_init"; * this module is non-zero, i.e. between inter_module_get and * inter_module_put. Keith Owens 29 Oct 2000. */ -void DoCMil_init(struct mtd_info *mtd) +static void DoCMil_init(struct mtd_info *mtd) { struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; struct DiskOnChip *old = NULL; @@ -355,12 +356,15 @@ void DoCMil_init(struct mtd_info *mtd) mtd->name = "DiskOnChip Millennium"; printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n", - this->physadr); + this->physadr); mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; mtd->size = 0; + + /* FIXME: erase size is not always 8kB */ mtd->erasesize = 0x2000; + mtd->oobblock = 512; mtd->oobsize = 16; mtd->module = THIS_MODULE; @@ -374,15 +378,15 @@ void DoCMil_init(struct mtd_info *mtd) mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; - + this->totlen = 0; - this->numchips = 0; + this->numchips = 0; this->curfloor = -1; this->curchip = -1; - + /* Ident all the chips present. */ DoC_ScanChips(this); - + if (!this->totlen) { kfree(mtd); iounmap((void *)this->virtadr); @@ -405,8 +409,9 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf) { - int i; + int i, ret; volatile char dummy; + unsigned char syndrome[6]; struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; unsigned long docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; @@ -429,45 +434,55 @@ static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, this->curfloor = mychip->floor; this->curchip = mychip->chip; + /* issue the Read0 or Read1 command depend on which half of the page + we are accessing. Polling the Flash Ready bit after issue 3 bytes + address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/ + DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP); + DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00); + DoC_WaitReady(docptr); + if (eccbuf) { /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ WriteDOC (DOC_ECC_RESET, docptr, ECCConf); WriteDOC (DOC_ECC_EN, docptr, ECCConf); } else { - /* disable the ECC engine, FIXME: is this correct ?? */ + /* disable the ECC engine */ WriteDOC (DOC_ECC_RESET, docptr, ECCConf); - WriteDOC (DOC_ECC_DIS, docptr, ECCConf); + WriteDOC (DOC_ECC_DIS, docptr, ECCConf); } - /* issue the Read0 or Read1 command depend on which half of the page - we are accessing. Polling the Flash Ready bit after issue 3 bytes - address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/ - DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP); - DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00); - DoC_WaitReady(docptr); - /* Read the data via the internal pipeline through CDSN IO register, see Pipelined Read Operations 11.3 */ dummy = ReadDOC(docptr, ReadPipeInit); +#ifndef USE_MEMCPY for (i = 0; i < len-1; i++) { - buf[i] = ReadDOC(docptr, Mil_CDSN_IO); + /* N.B. you have to increase the source address in this way or the + ECC logic will not work properly */ + buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); } - buf[i] = ReadDOC(docptr, LastDataRead); +#else + memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1); +#endif + buf[len - 1] = ReadDOC(docptr, LastDataRead); /* Let the caller know we completed it */ *retlen = len; + ret = 0; if (eccbuf) { - /* FIXME: are we reading the ECC from the ECC logic of DOC or - the spare data space on the flash chip i.e. How do we - control the Spare Area Enable bit of the flash ?? */ - /* Read the ECC data through the DiskOnChip ECC logic + /* Read the ECC data from Spare Data Area, see Reed-Solomon EDC/ECC 11.1 */ dummy = ReadDOC(docptr, ReadPipeInit); +#ifndef USE_MEMCPY for (i = 0; i < 5; i++) { - eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO); + /* N.B. you have to increase the source address in this way or the + ECC logic will not work properly */ + eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); } - eccbuf[i] = ReadDOC(docptr, LastDataRead); +#else + memcpy_fromio(eccbuf, docptr + DoC_Mil_CDSN_IO, 5); +#endif + eccbuf[5] = ReadDOC(docptr, LastDataRead); /* Flush the pipeline */ dummy = ReadDOC(docptr, ECCConf); @@ -475,34 +490,45 @@ static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, /* Check the ECC Status */ if (ReadDOC(docptr, ECCConf) & 0x80) { + int nb_errors; /* There was an ECC error */ +#ifdef ECC_DEBUG printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); - - /* FIXME: Implement ECC error correction, don't just whinge */ - - /* We return error, but have actually done the read. Not that - this can be told to user-space, via sys_read(), but at least - MTD-aware stuff can know about it by checking *retlen */ - return -EIO; +#endif + /* Read the ECC syndrom through the DiskOnChip ECC logic. + These syndrome will be all ZERO when there is no error */ + for (i = 0; i < 6; i++) { + syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i); + } + nb_errors = doc_decode_ecc(buf, syndrome); +#ifdef ECC_DEBUG + printk("Errors corrected: %x\n", nb_errors); +#endif + if (nb_errors < 0) { + /* We return error, but have actually done the read. Not that + this can be told to user-space, via sys_read(), but at least + MTD-aware stuff can know about it by checking *retlen */ + ret = -EIO; + } } + #ifdef PSYCHO_DEBUG - else - printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", - (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], - eccbuf[4], eccbuf[5]); + printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], + eccbuf[4], eccbuf[5]); #endif - /* Reset the ECC engine */ - WriteDOC(DOC_ECC_RESV, docptr , ECCConf); + /* disable the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr , ECCConf); } - return 0; + return ret; } static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - static char as[6]; - return doc_write_ecc(mtd, to, len, retlen, buf, as); + char eccbuf[6]; + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf); } static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, @@ -532,42 +558,48 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, if (this->curfloor != mychip->floor) { DoC_SelectFloor(docptr, mychip->floor); DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { + } else if (this->curchip != mychip->chip) { DoC_SelectChip(docptr, mychip->chip); } this->curfloor = mychip->floor; this->curchip = mychip->chip; /* Reset the chip, see Software Requirement 11.4 item 1. */ - DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_RESET, 0x00); DoC_WaitReady(docptr); /* Set device to main plane of flash */ - DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_READ0, 0x00); + + /* issue the Serial Data In command to initial the Page Program process */ + DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); + DoC_Address(docptr, 3, to, 0x00, 0x00); + DoC_WaitReady(docptr); if (eccbuf) { /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ WriteDOC (DOC_ECC_RESET, docptr, ECCConf); WriteDOC (DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); } else { - /* disable the ECC engine, FIXME: is this correct ?? */ + /* disable the ECC engine */ WriteDOC (DOC_ECC_RESET, docptr, ECCConf); WriteDOC (DOC_ECC_DIS, docptr, ECCConf); } - /* issue the Serial Data In command to initial the Page Program process */ - DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); - DoC_Address(docptr, 3, to, 0x00, 0x00); - /* Write the data via the internal pipeline through CDSN IO register, see Pipelined Write Operations 11.2 */ +#ifndef USE_MEMCPY for (i = 0; i < len; i++) { - WriteDOC(buf[i], docptr, Mil_CDSN_IO); + /* N.B. you have to increase the source address in this way or the + ECC logic will not work properly */ + WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); } +#else + memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); +#endif WriteDOC(0x00, docptr, WritePipeTerm); if (eccbuf) { - /* Write ECC data to flash, the ECC info is generated by the DiskOnChip DECC logic + /* Write ECC data to flash, the ECC info is generated by the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ WriteDOC(0, docptr, NOP); WriteDOC(0, docptr, NOP); @@ -578,10 +610,26 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, eccbuf[i] = ReadDOC(docptr, ECCSyndrome0 + i); } + /* ignore the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr , ECCConf); + +#ifndef USE_MEMCPY /* Write the ECC data to flash */ for (i = 0; i < 6; i++) { - WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO); + /* N.B. you have to increase the source address in this way or the + ECC logic will not work properly */ + WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO + i); } +#else + memcpy_toio(docptr + DoC_Mil_CDSN_IO, eccbuf, 6); +#endif + + /* write the block status BLOCK_USED (0x5555) at the end of ECC data + FIXME: this is only a hack for programming the IPL area for LinuxBIOS + and should be replace with proper codes in user space utilities */ + WriteDOC(0x55, docptr, Mil_CDSN_IO); + WriteDOC(0x55, docptr, Mil_CDSN_IO + 1); + WriteDOC(0x00, docptr, WritePipeTerm); #ifdef PSYCHO_DEBUG @@ -589,9 +637,6 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4], eccbuf[5]); #endif - - /* Reset the ECC engine */ - WriteDOC(DOC_ECC_RESV, docptr , ECCConf); } /* Commit the Page Program command and wait for ready @@ -601,12 +646,13 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, /* Read the status of the flash device through CDSN Slow IO register see Software Requirement 11.4 item 5.*/ - DoC_Command(docptr, NAND_CMD_STATUS, 0x00); + DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); dummy = ReadDOC(docptr, CDSNSlowIO); DoC_Delay(docptr, 2); if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { printk("Error programming flash\n"); - /* Error in programming */ + /* Error in programming + FIXME: implement Bad Block Replacement (in nftl.c ??) */ *retlen = 0; return -EIO; } @@ -620,31 +666,29 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf) { - volatile char dummy; +#ifndef USE_MEMCPY int i; +#endif + volatile char dummy; struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; unsigned long docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; - /* FIXME: should we restrict the access between 512 to 527 ?? */ - /* Find the chip which is to be used and select it */ if (this->curfloor != mychip->floor) { DoC_SelectFloor(docptr, mychip->floor); DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { + } else if (this->curchip != mychip->chip) { DoC_SelectChip(docptr, mychip->chip); } this->curfloor = mychip->floor; this->curchip = mychip->chip; - /* FIXME: should we disable ECC engine in this way ?? */ - /* disable the ECC engine, FIXME: is this correct ?? */ + /* disable the ECC engine */ WriteDOC (DOC_ECC_RESET, docptr, ECCConf); WriteDOC (DOC_ECC_DIS, docptr, ECCConf); - /* issue the Read2 command to read the Spare Data Area. + /* issue the Read2 command to set the pointer to the Spare Data Area. Polling the Flash Ready bit after issue 3 bytes address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/ DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); @@ -654,10 +698,17 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Read the data out via the internal pipeline through CDSN IO register, see Pipelined Read Operations 11.3 */ dummy = ReadDOC(docptr, ReadPipeInit); +#ifndef USE_MEMCPY for (i = 0; i < len-1; i++) { - buf[i] = ReadDOC(docptr, Mil_CDSN_IO); + /* N.B. you have to increase the source address in this way or the + ECC logic will not work properly */ + buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); } buf[i] = ReadDOC(docptr, LastDataRead); +#else + memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1); +#endif + buf[len - 1] = ReadDOC(docptr, LastDataRead); *retlen = len; @@ -667,32 +718,32 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) { +#ifndef USE_MEMCPY int i; +#endif volatile char dummy; struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; unsigned long docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; /* Find the chip which is to be used and select it */ - if (this->curfloor != mychip->floor) { + if (this->curfloor != mychip->floor) { DoC_SelectFloor(docptr, mychip->floor); DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { + } else if (this->curchip != mychip->chip) { DoC_SelectChip(docptr, mychip->chip); } this->curfloor = mychip->floor; this->curchip = mychip->chip; - /* FIXME: should we disable ECC engine in this way ?? */ - /* disable the ECC engine, FIXME: is this correct ?? */ + /* disable the ECC engine */ WriteDOC (DOC_ECC_RESET, docptr, ECCConf); WriteDOC (DOC_ECC_DIS, docptr, ECCConf); /* Reset the chip, see Software Requirement 11.4 item 1. */ DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); DoC_WaitReady(docptr); - /* issue the Read2 command to read the Spare Data Area. */ + /* issue the Read2 command to set the pointer to the Spare Data Area. */ DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); /* issue the Serial Data In command to initial the Page Program process */ @@ -701,8 +752,15 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Write the data via the internal pipeline through CDSN IO register, see Pipelined Write Operations 11.2 */ - for (i = 0; i < len; i++) - WriteDOC(buf[i], docptr, Mil_CDSN_IO); +#ifndef USE_MEMCPY + for (i = 0; i < len; i++) { + /* N.B. you have to increase the source address in this way or the + ECC logic will not work properly */ + WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); + } +#else + memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); +#endif WriteDOC(0x00, docptr, WritePipeTerm); /* Commit the Page Program command and wait for ready @@ -717,6 +775,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, DoC_Delay(docptr, 2); if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { printk("Error programming oob data\n"); + /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ *retlen = 0; return -EIO; } @@ -743,13 +802,12 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr) if (this->curfloor != mychip->floor) { DoC_SelectFloor(docptr, mychip->floor); DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { + } else if (this->curchip != mychip->chip) { DoC_SelectChip(docptr, mychip->chip); } this->curfloor = mychip->floor; this->curchip = mychip->chip; - + instr->state = MTD_ERASE_PENDING; /* issue the Erase Setup command */ @@ -764,13 +822,16 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr) instr->state = MTD_ERASING; /* Read the status of the flash device through CDSN Slow IO register - see Software Requirement 11.4 item 5.*/ + see Software Requirement 11.4 item 5. + FIXME: it seems that we are not wait long enough, some blocks are not + erased fully */ DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); dummy = ReadDOC(docptr, CDSNSlowIO); DoC_Delay(docptr, 2); if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { - printk("Error Erasing\n"); - /* There was an error */ + printk("Error Erasing at 0x%lx\n", ofs); + /* There was an error + FIXME: implement Bad Block Replacement (in nftl.c ??) */ instr->state = MTD_ERASE_FAILED; } else instr->state = MTD_ERASE_DONE; @@ -787,20 +848,17 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr) * ****************************************************************************/ -static int __init init_doc2001(void) +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define cleanup_doc2001 cleanup_module +#define init_doc2001 init_module +#endif + +int __init init_doc2001(void) { inter_module_register(im_name, THIS_MODULE, &DoCMil_init); return 0; } -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE -#define cleanup_doc2001 cleanup_module -#endif -#define __exit -#endif - - static void __exit cleanup_doc2001(void) { struct mtd_info *mtd; @@ -817,11 +875,9 @@ static void __exit cleanup_doc2001(void) kfree(mtd); } inter_module_unregister(im_name); - } +module_exit(cleanup_doc2001); module_init(init_doc2001); -#if LINUX_VERSION_CODE > 0x20300 -module_exit(cleanup_doc2001); -#endif + diff --git a/drivers/mtd/docecc.c b/drivers/mtd/docecc.c new file mode 100644 index 000000000000..cddc968c0d49 --- /dev/null +++ b/drivers/mtd/docecc.c @@ -0,0 +1,522 @@ +/* + * ECC algorithm for M-systems disk on chip. We use the excellent Reed + * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the + * GNU GPL License. The rest is simply to convert the disk on chip + * syndrom into a standard syndom. + * + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * + * $Id: docecc.c,v 1.1 2000/11/03 12:43:43 dwmw2 Exp $ + * + * 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* need to undef it (from asm/termbits.h) */ +#undef B0 + +#define MM 10 /* Symbol size in bits */ +#define KK (1023-4) /* Number of data symbols per block */ +#define B0 510 /* First root of generator polynomial, alpha form */ +#define PRIM 1 /* power of alpha used to generate roots of generator poly */ +#define NN ((1 << MM) - 1) + +typedef unsigned short dtype; + +/* 1+x^3+x^10 */ +static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 }; + +/* This defines the type used to store an element of the Galois Field + * used by the code. Make sure this is something larger than a char if + * if anything larger than GF(256) is used. + * + * Note: unsigned char will work up to GF(256) but int seems to run + * faster on the Pentium. + */ +typedef int gf; + +/* No legal value in index form represents zero, so + * we need a special value for this purpose + */ +#define A0 (NN) + +/* Compute x % NN, where NN is 2**MM - 1, + * without a slow divide + */ +static inline gf +modnn(int x) +{ + while (x >= NN) { + x -= NN; + x = (x >> MM) + (x & NN); + } + return x; +} + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +#define CLEAR(a,n) {\ +int ci;\ +for(ci=(n)-1;ci >=0;ci--)\ +(a)[ci] = 0;\ +} + +#define COPY(a,b,n) {\ +int ci;\ +for(ci=(n)-1;ci >=0;ci--)\ +(a)[ci] = (b)[ci];\ +} + +#define COPYDOWN(a,b,n) {\ +int ci;\ +for(ci=(n)-1;ci >=0;ci--)\ +(a)[ci] = (b)[ci];\ +} + +#define Ldec 1 + +/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m] + lookup tables: index->polynomial form alpha_to[] contains j=alpha**i; + polynomial form -> index form index_of[j=alpha**i] = i + alpha=2 is the primitive element of GF(2**m) + HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows: + Let @ represent the primitive element commonly called "alpha" that + is the root of the primitive polynomial p(x). Then in GF(2^m), for any + 0 <= i <= 2^m-2, + @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) + where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation + of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for + example the polynomial representation of @^5 would be given by the binary + representation of the integer "alpha_to[5]". + Similarily, index_of[] can be used as follows: + As above, let @ represent the primitive element of GF(2^m) that is + the root of the primitive polynomial p(x). In order to find the power + of @ (alpha) that has the polynomial representation + a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) + we consider the integer "i" whose binary representation with a(0) being LSB + and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry + "index_of[i]". Now, @^index_of[i] is that element whose polynomial + representation is (a(0),a(1),a(2),...,a(m-1)). + NOTE: + The element alpha_to[2^m-1] = 0 always signifying that the + representation of "@^infinity" = 0 is (0,0,0,...,0). + Similarily, the element index_of[0] = A0 always signifying + that the power of alpha which has the polynomial representation + (0,0,...,0) is "infinity". + +*/ + +static void +generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1]) +{ + register int i, mask; + + mask = 1; + Alpha_to[MM] = 0; + for (i = 0; i < MM; i++) { + Alpha_to[i] = mask; + Index_of[Alpha_to[i]] = i; + /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */ + if (Pp[i] != 0) + Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */ + mask <<= 1; /* single left-shift */ + } + Index_of[Alpha_to[MM]] = MM; + /* + * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by + * poly-repr of @^i shifted left one-bit and accounting for any @^MM + * term that may occur when poly-repr of @^i is shifted. + */ + mask >>= 1; + for (i = MM + 1; i < NN; i++) { + if (Alpha_to[i - 1] >= mask) + Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1); + else + Alpha_to[i] = Alpha_to[i - 1] << 1; + Index_of[Alpha_to[i]] = i; + } + Index_of[0] = A0; + Alpha_to[NN] = 0; +} + +/* + * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content + * of the feedback shift register after having processed the data and + * the ECC. + * + * Return number of symbols corrected, or -1 if codeword is illegal + * or uncorrectable. If eras_pos is non-null, the detected error locations + * are written back. NOTE! This array must be at least NN-KK elements long. + * The corrected data are written in eras_val[]. They must be xor with the data + * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] . + * + * First "no_eras" erasures are declared by the calling program. Then, the + * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2). + * If the number of channel errors is not greater than "t_after_eras" the + * transmitted codeword will be recovered. Details of algorithm can be found + * in R. Blahut's "Theory ... of Error-Correcting Codes". + + * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure + * will result. The decoder *could* check for this condition, but it would involve + * extra time on every decoding operation. + * */ +static int +eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1], + gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK], + int no_eras) +{ + int deg_lambda, el, deg_omega; + int i, j, r,k; + gf u,q,tmp,num1,num2,den,discr_r; + gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly + * and syndrome poly */ + gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1]; + gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK]; + int syn_error, count; + + syn_error = 0; + for(i=0;i 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])]; + for (i = 1; i < no_eras; i++) { + u = modnn(PRIM*eras_pos[i]); + for (j = i+1; j > 0; j--) { + tmp = Index_of[lambda[j - 1]]; + if(tmp != A0) + lambda[j] ^= Alpha_to[modnn(u + tmp)]; + } + } +#if DEBUG >= 1 + /* Test code that verifies the erasure locator polynomial just constructed + Needed only for decoder debugging. */ + + /* find roots of the erasure location polynomial */ + for(i=1;i<=no_eras;i++) + reg[i] = Index_of[lambda[i]]; + count = 0; + for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) { + q = 1; + for (j = 1; j <= no_eras; j++) + if (reg[j] != A0) { + reg[j] = modnn(reg[j] + j); + q ^= Alpha_to[reg[j]]; + } + if (q != 0) + continue; + /* store root and error location number indices */ + root[count] = i; + loc[count] = k; + count++; + } + if (count != no_eras) { + printf("\n lambda(x) is WRONG\n"); + count = -1; + goto finish; + } +#if DEBUG >= 2 + printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n"); + for (i = 0; i < count; i++) + printf("%d ", loc[i]); + printf("\n"); +#endif +#endif + } + for(i=0;i 0; j--){ + if (reg[j] != A0) { + reg[j] = modnn(reg[j] + j); + q ^= Alpha_to[reg[j]]; + } + } + if (q != 0) + continue; + /* store root (index-form) and error location number */ + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if(++count == deg_lambda) + break; + } + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -1; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**(NN-KK)). in index form. Also find deg(omega). + */ + deg_omega = 0; + for (i = 0; i < NN-KK;i++){ + tmp = 0; + j = (deg_lambda < i) ? deg_lambda : i; + for(;j >= 0; j--){ + if ((s[i + 1 - j] != A0) && (lambda[j] != A0)) + tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])]; + } + if(tmp != 0) + deg_omega = i; + omega[i] = Index_of[tmp]; + } + omega[NN-KK] = A0; + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count-1; j >=0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != A0) + num1 ^= Alpha_to[modnn(omega[i] + i * root[j])]; + } + num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) { + if(lambda[i+1] != A0) + den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])]; + } + if (den == 0) { +#if DEBUG >= 1 + printf("\n ERROR: denominator = 0\n"); +#endif + /* Convert to dual- basis */ + count = -1; + goto finish; + } + /* Apply error to data */ + if (num1 != 0) { + eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])]; + } else { + eras_val[j] = 0; + } + } + finish: + for(i=0;i> 2) | ((ecc1[2] & 0x0f) << 6); + bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4); + bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2); + + nb_errors = eras_dec_rs(Alpha_to, Index_of, bb, + error_val, error_pos, 0); + if (nb_errors <= 0) + goto the_end; + + /* correct the errors */ + for(i=0;i= NB_DATA && pos < KK) { + nb_errors = -1; + goto the_end; + } + if (pos < NB_DATA) { + /* extract bit position (MSB first) */ + pos = 10 * (NB_DATA - 1 - pos) - 6; + /* now correct the following 10 bits. At most two bytes + can be modified since pos is even */ + index = (pos >> 3) ^ 1; + bitpos = pos & 7; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = error_val[i] >> (2 + bitpos); + parity ^= val; + if (index < SECTOR_SIZE) + sector[index] ^= val; + } + index = ((pos >> 3) + 1) ^ 1; + bitpos = (bitpos + 10) & 7; + if (bitpos == 0) + bitpos = 8; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = error_val[i] << (8 - bitpos); + parity ^= val; + if (index < SECTOR_SIZE) + sector[index] ^= val; + } + } + } + + /* use parity to test extra errors */ + if ((parity & 0xff) != 0) + nb_errors = -1; + + the_end: + kfree(Alpha_to); + kfree(Index_of); + return nb_errors; +} + diff --git a/drivers/mtd/docprobe.c b/drivers/mtd/docprobe.c index b38b4352dbcf..c67a3489abac 100644 --- a/drivers/mtd/docprobe.c +++ b/drivers/mtd/docprobe.c @@ -3,7 +3,7 @@ /* Probe routines common to all DoC devices */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: docprobe.c,v 1.10 2000/07/13 14:23:20 dwmw2 Exp $ */ +/* $Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $ */ @@ -26,6 +26,21 @@ #define DOC_PASSIVE_PROBE */ + +/* DOC_SINGLE_DRIVER: + Millennium driver has been merged into DOC2000 driver. + + The newly-merged driver doesn't appear to work for writing. It's the + same with the DiskOnChip 2000 and the Millennium. If you have a + Millennium and you want write support to work, remove the definition + of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver. + + Otherwise, it's left on in the hope that it'll annoy someone with + a Millennium enough that they go through and work out what the + difference is :) +*/ +#define DOC_SINGLE_DRIVER + #include #include #include @@ -44,21 +59,36 @@ #include /* Where to look for the devices? */ +#ifndef CONFIG_MTD_DOCPROBE_ADDRESS +#define CONFIG_MTD_DOCPROBE_ADDRESS 0 +#endif + + +static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS; +MODULE_PARM(doc_config_location, "l"); + +static unsigned long __initdata doc_locations[] = { #if defined (__alpha__) || defined(__i386__) -static unsigned long __initdata doc_locations[] = { - 0xc8000, 0xca000, 0xcc000, 0xce000, - 0xd0000, 0xd2000, 0xd4000, 0xd6000, - 0xd8000, 0xda000, 0xdc000, 0xde000, - 0xe0000, 0xe2000, 0xe4000, 0xe6000, - 0xe8000, 0xea000, 0xec000, 0xee000, 0 }; +#ifdef CONFIG_MTD_DOCPROBE_HIGH + 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, + 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, + 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, + 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, + 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, +#else /* CONFIG_MTD_DOCPROBE_HIGH */ + 0xc8000, 0xca000, 0xcc000, 0xce000, + 0xd0000, 0xd2000, 0xd4000, 0xd6000, + 0xd8000, 0xda000, 0xdc000, 0xde000, + 0xe0000, 0xe2000, 0xe4000, 0xe6000, + 0xe8000, 0xea000, 0xec000, 0xee000, +#endif /* CONFIG_MTD_DOCPROBE_HIGH */ #elif defined(__ppc__) -static unsigned long __initdata doc_locations[] = { - 0xe4000000, 0}; + 0xe4000000, #else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif - + 0 }; /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ @@ -71,10 +101,13 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr #endif /* Routine copied from the Linux DOC driver */ - - /* Check for 0x55 0xAA signature at beginning of window */ + +#ifdef CONFIG_MTD_DOCPROBE_55AA + /* Check for 0x55 0xAA signature at beginning of window, + this is no longer true once we remove the IPL (for Millennium */ if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa) return 0; +#endif /* CONFIG_MTD_DOCPROBE_55AA */ #ifndef DOC_PASSIVE_PROBE /* It's not possible to cleanly detect the DiskOnChip - the @@ -118,9 +151,10 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr break; default: +#ifndef CONFIG_MTD_DOCPROBE_55AA printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", ChipID, physadr); - +#endif #ifndef DOC_PASSIVE_PROBE /* Put back the contents of the DOCControl register, in case it's not * actually a DiskOnChip. @@ -177,28 +211,35 @@ static void DoC_Probe(unsigned long physadr) this->physadr = physadr; this->ChipID = ChipID; sprintf(namebuf, "with ChipID %2.2X", ChipID); - + switch(ChipID) { case DOC_ChipID_Doc2k: name="2000"; im_funcname = "DoC2k_init"; im_modname = "doc2000"; break; - + case DOC_ChipID_DocMil: name="Millennium"; +#ifdef DOC_SINGLE_DRIVER + im_funcname = "DoC2k_init"; + im_modname = "doc2000"; +#else im_funcname = "DoCMil_init"; im_modname = "doc2001"; +#endif /* DOC_SINGLE_DRIVER */ break; } + if (im_funcname) initroutine = inter_module_get_request(im_funcname, im_modname); + if (initroutine) { (*initroutine)(mtd); inter_module_put(im_funcname); return; } - printk("Cannot find driver for DiskOnChip %s at 0x%X\n", name, physadr); + printk("Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); } iounmap((void *)docptr); } @@ -210,12 +251,9 @@ static void DoC_Probe(unsigned long physadr) * ****************************************************************************/ -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_doc init_module #endif -#define __exit -#endif int __init init_doc(void) { @@ -223,19 +261,22 @@ int __init init_doc(void) printk(KERN_NOTICE "M-Systems DiskOnChip driver. (C) 1999 Machine Vision Holdings, Inc.\n"); #ifdef PRERELEASE - printk(KERN_INFO "$Id: docprobe.c,v 1.10 2000/07/13 14:23:20 dwmw2 Exp $\n"); + printk(KERN_INFO "$Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $\n"); #endif - - for (i=0; doc_locations[i]; i++) { - DoC_Probe(doc_locations[i]); + if (doc_config_location) { + printk("Using configured probe address 0x%lx\n", doc_config_location); + DoC_Probe(doc_config_location); + } else { + for (i=0; doc_locations[i]; i++) { + DoC_Probe(doc_locations[i]); + } } - + /* So it looks like we've been used and we get unloaded */ + MOD_INC_USE_COUNT; + MOD_DEC_USE_COUNT; return 0; } - -#if LINUX_VERSION_CODE > 0x20300 module_init(init_doc); -#endif diff --git a/drivers/mtd/map_ram.c b/drivers/mtd/map_ram.c index c62515406cea..00c009d7fcb9 100644 --- a/drivers/mtd/map_ram.c +++ b/drivers/mtd/map_ram.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $ + * $Id: map_ram.c,v 1.7 2000/12/10 01:39:13 dwmw2 Exp $ */ #include @@ -29,6 +29,7 @@ static const char im_name[] = "map_ram_probe"; * this module is non-zero, i.e. between inter_module_get and * inter_module_put. Keith Owens 29 Oct 2000. */ + static struct mtd_info *map_ram_probe(struct map_info *map) { struct mtd_info *mtd; @@ -59,6 +60,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) memset(mtd, 0, sizeof(*mtd)); + map->im_name = im_name; map->fldrv_destroy = mapram_nop; mtd->priv = map; mtd->name = map->name; @@ -69,9 +71,9 @@ static struct mtd_info *map_ram_probe(struct map_info *map) mtd->read = mapram_read; mtd->write = mapram_write; mtd->sync = mapram_nop; - mtd->im_name = im_name; mtd->flags = MTD_CAP_RAM | MTD_VOLATILE; - + mtd->erasesize = PAGE_SIZE; + return mtd; } @@ -115,6 +117,11 @@ static void mapram_nop(struct mtd_info *mtd) /* Nothing to see here */ } +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define map_ram_init init_module +#define map_ram_exit cleanup_module +#endif + static int __init map_ram_init(void) { inter_module_register(im_name, THIS_MODULE, &map_ram_probe); diff --git a/drivers/mtd/map_rom.c b/drivers/mtd/map_rom.c index d353938e9457..c976c7ecf54f 100644 --- a/drivers/mtd/map_rom.c +++ b/drivers/mtd/map_rom.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $ + * $Id: map_rom.c,v 1.10 2000/12/10 01:39:13 dwmw2 Exp $ */ #include @@ -14,12 +14,19 @@ #include - static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static void maprom_nop (struct mtd_info *); -struct mtd_info *map_rom_probe(struct map_info *); -EXPORT_SYMBOL(map_rom_probe); +static const char im_name[] = "map_rom_probe"; + +/* This routine is made available to other mtd code via + * inter_module_register. It must only be accessed through + * inter_module_get which will bump the use count of this module. The + * addresses passed back in mtd are valid as long as the use count of + * this module is non-zero, i.e. between inter_module_get and + * inter_module_put. Keith Owens 29 Oct 2000. + */ struct mtd_info *map_rom_probe(struct map_info *map) { @@ -31,16 +38,18 @@ struct mtd_info *map_rom_probe(struct map_info *map) memset(mtd, 0, sizeof(*mtd)); + map->im_name = im_name; map->fldrv_destroy = maprom_nop; mtd->priv = map; mtd->name = map->name; mtd->type = MTD_ROM; mtd->size = map->size; mtd->read = maprom_read; + mtd->write = maprom_write; mtd->sync = maprom_nop; mtd->flags = MTD_CAP_ROM; - - MOD_INC_USE_COUNT; + mtd->erasesize = 131072; + return mtd; } @@ -58,3 +67,28 @@ static void maprom_nop(struct mtd_info *mtd) { /* Nothing to see here */ } + +static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + printk(KERN_NOTICE "maprom_write called\n"); + return -EIO; +} + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define map_rom_init init_module +#define map_rom_exit cleanup_module +#endif + +static int __init map_rom_init(void) +{ + inter_module_register(im_name, THIS_MODULE, &map_rom_probe); + return 0; +} + +static void __exit map_rom_exit(void) +{ + inter_module_unregister(im_name); +} + +module_init(map_rom_init); +module_exit(map_rom_exit); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index b728ddcd2c24..0a33ed1cf0e6 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,16 +1,15 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.17 2000/07/13 14:25:54 dwmw2 Exp $ + * $Id: mtdblock.c,v 1.38 2000/11/27 08:50:22 dwmw2 Exp $ + * + * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache */ -#ifdef MTDBLOCK_DEBUG -#define DEBUGLVL debug -#endif - #include #include - +#include +#include #include #define MAJOR_NR MTD_BLOCK_MAJOR @@ -21,62 +20,332 @@ #define DEVICE_OFF(device) #define DEVICE_NO_RANDOM #include - +/* for old kernels... */ +#ifndef QUEUE_EMPTY +#define QUEUE_EMPTY (!CURRENT) +#endif #if LINUX_VERSION_CODE < 0x20300 -#define RQFUNC_ARG void -#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) #else -#define RQFUNC_ARG request_queue_t *q +#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) #endif -#ifdef MTDBLOCK_DEBUG -static int debug = MTDBLOCK_DEBUG; -MODULE_PARM(debug, "i"); +#ifdef CONFIG_DEVFS_FS +#include +static void mtd_notify_add(struct mtd_info* mtd); +static void mtd_notify_remove(struct mtd_info* mtd); +static struct mtd_notifier notifier = { + mtd_notify_add, + mtd_notify_remove, + NULL +}; +static devfs_handle_t devfs_dir_handle = NULL; +static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; #endif -#if 1 -static void mtdblock_end_request(struct request *req, int res) +static struct mtdblk_dev { + struct mtd_info *mtd; /* Locked */ + int count; + struct semaphore cache_sem; + unsigned char *cache_data; + unsigned long cache_offset; + unsigned int cache_size; + enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; +} *mtdblks[MAX_MTD_DEVICES]; + +static spinlock_t mtdblks_lock; + +static int mtd_sizes[MAX_MTD_DEVICES]; +static int mtd_blksizes[MAX_MTD_DEVICES]; + + +/* + * Cache stuff... + * + * Since typical flash erasable sectors are much larger than what Linux's + * buffer cache can handle, we must implement read-modify-write on flash + * sectors for each block write requests. To avoid over-erasing flash sectors + * and to speed things up, we locally cache a whole flash sector while it is + * being written to until a different sector is required. + */ + +static void erase_callback(struct erase_info *done) { - if (end_that_request_first( req, res, "mtdblock" )) - return; - end_that_request_last( req ); + wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; + wake_up(wait_q); } -#endif -static int mtd_sizes[MAX_MTD_DEVICES]; +static int erase_write (struct mtd_info *mtd, unsigned long pos, + int len, const char *buf) +{ + struct erase_info erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + size_t retlen; + int ret; + + /* + * First, let's erase the flash block. + */ + + init_waitqueue_head(&wait_q); + erase.mtd = mtd; + erase.callback = erase_callback; + erase.addr = pos; + erase.len = len; + erase.priv = (u_long)&wait_q; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + ret = MTD_ERASE(mtd, &erase); + if (ret) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " + "on \"%s\" failed\n", + pos, len, mtd->name); + return ret; + } + schedule(); /* Wait for erase to finish. */ + remove_wait_queue(&wait_q, &wait); -/* Keeping a separate list rather than just getting stuff directly out of - the MTD core's mtd_table is perhaps not very nice, but I happen - to dislike the idea of directly accessing mtd_table even more. - dwmw2 31/3/0 -*/ + /* + * Next, writhe data to flash. + */ -static int mtdblock_open(struct inode *inode, struct file *file) + ret = MTD_WRITE (mtd, pos, len, &retlen, buf); + if (ret) + return ret; + if (retlen != len) + return -EIO; + return 0; +} + + +static int write_cached_data (struct mtdblk_dev *mtdblk) +{ + struct mtd_info *mtd = mtdblk->mtd; + int ret; + + if (mtdblk->cache_state != STATE_DIRTY) + return 0; + + DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" " + "at 0x%lx, size 0x%x\n", mtd->name, + mtdblk->cache_offset, mtdblk->cache_size); + + ret = erase_write (mtd, mtdblk->cache_offset, + mtdblk->cache_size, mtdblk->cache_data); + if (ret) + return ret; + + /* + * Here we could argably set the cache state to STATE_CLEAN. + * However this could lead to inconsistency since we will not + * be notified if this content is altered on the flash by other + * means. Let's declare it empty and leave buffering tasks to + * the buffer cache instead. + */ + mtdblk->cache_state = STATE_EMPTY; + return 0; +} + + +static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, + int len, const char *buf) { - struct mtd_info *mtd = NULL; + struct mtd_info *mtd = mtdblk->mtd; + unsigned int sect_size = mtd->erasesize; + size_t retlen; + int ret; + + DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n", + mtd->name, pos, len); + + while (len > 0) { + unsigned long sect_start = (pos/sect_size)*sect_size; + unsigned int offset = pos - sect_start; + unsigned int size = sect_size - offset; + if( size > len ) + size = len; + + if (size == sect_size) { + /* + * We are covering a whole sector. Thus there is no + * need to bother with the cache while it may still be + * useful for other partial writes. + */ + ret = erase_write (mtd, pos, size, buf); + if (ret) + return ret; + } else { + /* Partial sector: need to use the cache */ + + if (mtdblk->cache_state == STATE_DIRTY && + mtdblk->cache_offset != sect_start) { + ret = write_cached_data(mtdblk); + if (ret) + return ret; + } + + if (mtdblk->cache_state == STATE_EMPTY || + mtdblk->cache_offset != sect_start) { + /* fill the cache with the current sector */ + mtdblk->cache_state = STATE_EMPTY; + ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data); + if (ret) + return ret; + if (retlen != sect_size) + return -EIO; + + mtdblk->cache_offset = sect_start; + mtdblk->cache_size = sect_size; + mtdblk->cache_state = STATE_CLEAN; + } + + /* write data to our local cache */ + memcpy (mtdblk->cache_data + offset, buf, size); + mtdblk->cache_state = STATE_DIRTY; + } + + buf += size; + pos += size; + len -= size; + } + + return 0; +} + + +static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, + int len, char *buf) +{ + struct mtd_info *mtd = mtdblk->mtd; + unsigned int sect_size = mtd->erasesize; + size_t retlen; + int ret; + + DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n", + mtd->name, pos, len); + + while (len > 0) { + unsigned long sect_start = (pos/sect_size)*sect_size; + unsigned int offset = pos - sect_start; + unsigned int size = sect_size - offset; + if (size > len) + size = len; + + /* + * Check if the requested data is already cached + * Read the requested amount of data from our internal cache if it + * contains what we want, otherwise we read the data directly + * from flash. + */ + if (mtdblk->cache_state != STATE_EMPTY && + mtdblk->cache_offset == sect_start) { + memcpy (buf, mtdblk->cache_data + offset, size); + } else { + ret = MTD_READ (mtd, pos, size, &retlen, buf); + if (ret) + return ret; + if (retlen != size) + return -EIO; + } + + buf += size; + pos += size; + len -= size; + } + + return 0; +} + + +static int mtdblock_open(struct inode *inode, struct file *file) +{ + struct mtdblk_dev *mtdblk; int dev; - DEBUG(1,"mtdblock_open\n"); + DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); - if (inode == 0) + if (!inode) return -EINVAL; dev = MINOR(inode->i_rdev); + if (dev >= MAX_MTD_DEVICES) + return -EINVAL; MOD_INC_USE_COUNT; - mtd = get_mtd_device(NULL, dev); + spin_lock(&mtdblks_lock); + + /* If it's already open, no need to piss about. */ + if (mtdblks[dev]) { + mtdblks[dev]->count++; + spin_unlock(&mtdblks_lock); + return 0; + } + + /* OK, it's not open. Try to find it */ + + /* First we have to drop the lock, because we have to + to things which might sleep. + */ + spin_unlock(&mtdblks_lock); + + mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); + if (!mtdblk) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + memset(mtdblk, 0, sizeof(*mtdblk)); + mtdblk->count = 1; + mtdblk->mtd = get_mtd_device(NULL, dev); - if (!mtd) { + if (!mtdblk->mtd) { + kfree(mtdblk); MOD_DEC_USE_COUNT; return -ENODEV; } - mtd_sizes[dev] = mtd->size>>9; + init_MUTEX (&mtdblk->cache_sem); + mtdblk->cache_state = STATE_EMPTY; + mtdblk->cache_size = mtdblk->mtd->erasesize; + mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); + if (!mtdblk->cache_data) { + put_mtd_device(mtdblk->mtd); + kfree(mtdblk); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + /* OK, we've created a new one. Add it to the list. */ + + spin_lock(&mtdblks_lock); - DEBUG(1, "ok\n"); + if (mtdblks[dev]) { + /* Another CPU made one at the same time as us. */ + mtdblks[dev]->count++; + spin_unlock(&mtdblks_lock); + put_mtd_device(mtdblk->mtd); + vfree(mtdblk->cache_data); + kfree(mtdblk); + return 0; + } + + mtdblks[dev] = mtdblk; + mtd_sizes[dev] = mtdblk->mtd->size/1024; + mtd_blksizes[dev] = mtdblk->mtd->erasesize; + if (mtd_blksizes[dev] > PAGE_SIZE) + mtd_blksizes[dev] = PAGE_SIZE; + set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE)); + + spin_unlock(&mtdblks_lock); + + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); return 0; } @@ -84,11 +353,11 @@ static int mtdblock_open(struct inode *inode, struct file *file) static release_t mtdblock_release(struct inode *inode, struct file *file) { int dev; - struct mtd_info *mtd; + struct mtdblk_dev *mtdblk; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) struct super_block * sb = get_super(inode->i_rdev); #endif - DEBUG(1, "mtdblock_release\n"); + DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); if (inode == NULL) release_return(-ENODEV); @@ -100,150 +369,189 @@ static release_t mtdblock_release(struct inode *inode, struct file *file) invalidate_buffers(inode->i_rdev); dev = MINOR(inode->i_rdev); - mtd = __get_mtd_device(NULL, dev); - - if (!mtd) { - printk(KERN_WARNING "MTD device is absent on mtd_release!\n"); - MOD_DEC_USE_COUNT; - release_return(-ENODEV); - + mtdblk = mtdblks[dev]; + + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); + + spin_lock(&mtdblks_lock); + if (!--mtdblk->count) { + /* It was the last usage. Free the device */ + mtdblks[dev] = NULL; + spin_unlock(&mtdblks_lock); + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); + put_mtd_device(mtdblk->mtd); + vfree(mtdblk->cache_data); + kfree(mtdblk); + } else { + spin_unlock(&mtdblks_lock); } - - if (mtd->sync) - mtd->sync(mtd); - - put_mtd_device(mtd); - DEBUG(1, "ok\n"); + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); MOD_DEC_USE_COUNT; release_return(0); } -static void mtdblock_request(RQFUNC_ARG) +/* + * This is a special request_fn because it is executed in a process context + * to be able to sleep independently of the caller. The io_request_lock + * is held upon entry and exit. + * The head of our request queue is considered active so there is no need + * to dequeue requests before we are done. + */ +static void handle_mtdblock_request(void) { - struct request *current_request; - unsigned int res = 0; - struct mtd_info *mtd; - - while (1) - { - /* Grab the Request and unlink it from the request list, INIT_REQUEST - will execute a return if we are done. */ - INIT_REQUEST; - current_request = CURRENT; - - if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES) - { - printk("mtd: Unsupported device!\n"); - end_request(0); - continue; - } - - // Grab our MTD structure - - mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev)); - if (!mtd) { - printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV); - end_request(0); - } - - if (current_request->sector << 9 > mtd->size || - (current_request->sector + current_request->nr_sectors) << 9 > mtd->size) - { - printk("mtd: Attempt to read past end of device!\n"); - printk("size: %lx, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors); - end_request(0); - continue; - } - - /* Remove the request we are handling from the request list so nobody messes - with it */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - blkdev_dequeue_request(current_request); - - /* Now drop the lock that the ll_rw_blk functions grabbed for us - and process the request. This is necessary due to the extreme time - we spend processing it. */ - spin_unlock_irq(&io_request_lock); -#endif + struct request *req; + struct mtdblk_dev *mtdblk; + unsigned int res; + + for (;;) { + INIT_REQUEST; + req = CURRENT; + spin_unlock_irq(&io_request_lock); + mtdblk = mtdblks[MINOR(req->rq_dev)]; + res = 0; + + if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES) + panic(__FUNCTION__": minor out of bound"); + + if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) + goto end_req; + + // Handle the request + switch (req->cmd) + { + int err; + + case READ: + down(&mtdblk->cache_sem); + err = do_cached_read (mtdblk, req->sector << 9, + req->current_nr_sectors << 9, + req->buffer); + up(&mtdblk->cache_sem); + if (!err) + res = 1; + break; + + case WRITE: + // Read only device + if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) ) + break; + + // Do the write + down(&mtdblk->cache_sem); + err = do_cached_write (mtdblk, req->sector << 9, + req->current_nr_sectors << 9, + req->buffer); + up(&mtdblk->cache_sem); + if (!err) + res = 1; + break; + } + +end_req: + spin_lock_irq(&io_request_lock); + end_request(res); + } +} - // Handle the request - switch (current_request->cmd) - { - size_t retlen; - - case READ: - if (mtd->read(mtd,current_request->sector<<9, - current_request->nr_sectors << 9, - &retlen, current_request->buffer) == 0) - res = 1; - else - res = 0; - break; - - case WRITE: -//printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector, -// current_request->nr_sectors); - - // Read only device - if ((mtd->flags & MTD_CAP_RAM) == 0) - { - res = 0; - break; - } - - // Do the write - if (mtd->write(mtd,current_request->sector<<9, - current_request->nr_sectors << 9, - &retlen, current_request->buffer) == 0) - res = 1; - else - res = 0; - break; - - // Shouldn't happen - default: - printk("mtd: unknown request\n"); - break; - } - - // Grab the lock and re-thread the item onto the linked list -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - spin_lock_irq(&io_request_lock); - mtdblock_end_request(current_request, res); +static volatile int leaving = 0; +#if LINUX_VERSION_CODE > 0x020300 +static DECLARE_MUTEX_LOCKED(thread_sem); +static DECLARE_WAIT_QUEUE_HEAD(thr_wq); #else - end_request(res); +static struct semaphore thread_sem = MUTEX_LOCKED; +DECLARE_WAIT_QUEUE_HEAD(thr_wq); #endif - } + +int mtdblock_thread(void *dummy) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->session = 1; + tsk->pgrp = 1; + /* we might get involved when memory gets low, so use PF_MEMALLOC */ + tsk->flags |= PF_MEMALLOC; + strcpy(tsk->comm, "mtdblockd"); + tsk->tty = NULL; + spin_lock_irq(&tsk->sigmask_lock); + sigfillset(&tsk->blocked); + recalc_sigpending(tsk); + spin_unlock_irq(&tsk->sigmask_lock); + exit_mm(tsk); + exit_files(tsk); + exit_sighand(tsk); + exit_fs(tsk); + + while (!leaving) { + add_wait_queue(&thr_wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&io_request_lock); + if (QUEUE_EMPTY || QUEUE_PLUGGED) { + spin_unlock_irq(&io_request_lock); + schedule(); + remove_wait_queue(&thr_wq, &wait); + } else { + remove_wait_queue(&thr_wq, &wait); + set_current_state(TASK_RUNNING); + handle_mtdblock_request(); + spin_unlock_irq(&io_request_lock); + } + } + + up(&thread_sem); + return 0; } +#if LINUX_VERSION_CODE < 0x20300 +#define RQFUNC_ARG void +#else +#define RQFUNC_ARG request_queue_t *q +#endif + +static void mtdblock_request(RQFUNC_ARG) +{ + /* Don't do anything, except wake the thread if necessary */ + wake_up(&thr_wq); +} static int mtdblock_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { - struct mtd_info *mtd; + struct mtdblk_dev *mtdblk; - mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); + mtdblk = mtdblks[MINOR(inode->i_rdev)]; - if (!mtd) return -EINVAL; +#ifdef PARANOIA + if (!mtdblk) + BUG(); +#endif switch (cmd) { case BLKGETSIZE: /* Return device size */ - if (!arg) return -EFAULT; - return put_user((mtd->size >> 9), - (long *) arg); + if (!arg) + return -EFAULT; + return put_user((mtdblk->mtd->size >> 9), + (long *) arg)?-EFAULT:0; case BLKFLSBUF: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - if(!capable(CAP_SYS_ADMIN)) return -EACCES; + if(!capable(CAP_SYS_ADMIN)) + return -EACCES; #endif fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); - if (mtd->sync) - mtd->sync(mtd); + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); return 0; default: @@ -251,7 +559,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file, } } - /*}}}*/ #if LINUX_VERSION_CODE < 0x20326 static struct file_operations mtd_fops = { @@ -270,32 +577,69 @@ static struct block_device_operations mtd_fops = }; #endif -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#ifdef CONFIG_DEVFS_FS +/* Notification that a new device has been added. Create the devfs entry for + * it. */ + +static void mtd_notify_add(struct mtd_info* mtd) +{ + char name[8]; + + if (!mtd) + return; + + sprintf(name, "%d", mtd->index); + devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, + DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index, + S_IFBLK | S_IRUGO | S_IWUGO, + &mtd_fops, NULL); +} + +static void mtd_notify_remove(struct mtd_info* mtd) +{ + if (!mtd) + return; + + devfs_unregister(devfs_rw_handle[mtd->index]); +} +#endif + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_mtdblock init_module #define cleanup_mtdblock cleanup_module #endif -#define __exit -#endif - int __init init_mtdblock(void) { int i; + spin_lock_init(&mtdblks_lock); +#ifdef CONFIG_DEVFS_FS + if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops)) + { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_BLOCK_MAJOR); + return -EAGAIN; + } + + devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL); + register_mtd_user(¬ifier); +#else if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", MTD_BLOCK_MAJOR); return -EAGAIN; } +#endif /* We fill it in at open() time. */ for (i=0; i< MAX_MTD_DEVICES; i++) { mtd_sizes[i] = 0; + mtd_blksizes[i] = BLOCK_SIZE; } - + init_waitqueue_head(&thr_wq); /* Allow the block size to default to BLOCK_SIZE. */ - blksize_size[MAJOR_NR] = NULL; + blksize_size[MAJOR_NR] = mtd_blksizes; blk_size[MAJOR_NR] = mtd_sizes; #if LINUX_VERSION_CODE < 0x20320 @@ -303,15 +647,30 @@ int __init init_mtdblock(void) #else blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); #endif + kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); return 0; } static void __exit cleanup_mtdblock(void) { + leaving = 1; + wake_up(&thr_wq); + down(&thread_sem); +#ifdef CONFIG_DEVFS_FS + unregister_mtd_user(¬ifier); + devfs_unregister(devfs_dir_handle); + devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME); +#else unregister_blkdev(MAJOR_NR,DEVICE_NAME); +#endif +#if LINUX_VERSION_CODE < 0x20320 + blk_dev[MAJOR_NR].request_fn = NULL; +#else + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +#endif + blksize_size[MAJOR_NR] = NULL; + blk_size[MAJOR_NR] = NULL; } -#if LINUX_VERSION_CODE > 0x20300 module_init(init_mtdblock); module_exit(cleanup_mtdblock); -#endif diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 0bb15a8fc35a..bccb687c0ae9 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,5 +1,6 @@ /* - * $Id: mtdchar.c,v 1.7 2000/06/30 15:54:19 dwmw2 Exp $ + * Almost: $Id: mtdchar.c,v 1.21 2000/12/09 21:15:12 dwmw2 Exp $ + * (With some of the compatibility for previous kernels taken out) * * Character-device access to raw MTD devices. * @@ -13,6 +14,20 @@ #include #include +#ifdef CONFIG_DEVFS_FS +#include +static void mtd_notify_add(struct mtd_info* mtd); +static void mtd_notify_remove(struct mtd_info* mtd); +static struct mtd_notifier notifier = { + mtd_notify_add, + mtd_notify_remove, + NULL +}; +static devfs_handle_t devfs_dir_handle = NULL; +static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; +static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES]; +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) #else @@ -22,11 +37,11 @@ static int mtd_lseek (struct inode *inode, struct file *file, off_t offset, int struct mtd_info *mtd=(struct mtd_info *)file->private_data; switch (orig) { - case 0: + case 0: /* SEEK_SET */ file->f_pos = offset; break; - case 1: + case 1: /* SEEK_CUR */ file->f_pos += offset; break; @@ -34,11 +49,11 @@ static int mtd_lseek (struct inode *inode, struct file *file, off_t offset, int /* SEEK_END */ file->f_pos =mtd->size + offset; break; - default: + default: return -EINVAL; } - if (file->f_pos < 0) + if (file->f_pos < 0) file->f_pos = 0; else if (file->f_pos >= mtd->size) file->f_pos = mtd->size - 1; @@ -54,7 +69,7 @@ static int mtd_open(struct inode *inode, struct file *file) int devnum = minor >> 1; struct mtd_info *mtd; - DEBUG(0, "MTD_open\n"); + DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); if (devnum >= MAX_MTD_DEVICES) return -ENODEV; @@ -86,7 +101,7 @@ static release_t mtd_close(struct inode *inode, { struct mtd_info *mtd; - DEBUG(0, "MTD_close\n"); + DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); mtd = (struct mtd_info *)file->private_data; @@ -115,7 +130,7 @@ static int mtd_read(struct inode *inode,struct file *file, char *buf, int count) int ret=0; char *kbuf; - DEBUG(0,"MTD_read\n"); + DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); if (FILE_POS + count > mtd->size) count = mtd->size - FILE_POS; @@ -124,7 +139,7 @@ static int mtd_read(struct inode *inode,struct file *file, char *buf, int count) return 0; /* FIXME: Use kiovec in 2.3 or 2.2+rawio, or at - * least split the IO into smaller chunks. + * least split the IO into smaller chunks. */ kbuf = vmalloc(count); @@ -157,7 +172,7 @@ static read_write_t mtd_write(struct inode *inode,struct file *file, const char size_t retlen; int ret=0; - DEBUG(0,"MTD_write\n"); + DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); if (FILE_POS == mtd->size) return -ENOSPC; @@ -208,7 +223,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, int ret = 0; u_long size; - DEBUG(0, "MTD_ioctl\n"); + DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (cmd & IOC_IN) { @@ -222,8 +237,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, switch (cmd) { case MEMGETINFO: - copy_to_user((struct mtd_info *)arg, mtd, - sizeof(struct mtd_info_user)); + if (copy_to_user((struct mtd_info *)arg, mtd, + sizeof(struct mtd_info_user))) + return -EFAULT; break; case MEMERASE: @@ -238,28 +254,23 @@ static int mtd_ioctl(struct inode *inode, struct file *file, init_waitqueue_head(&waitq); memset (erase,0,sizeof(struct erase_info)); - copy_from_user(&erase->addr, (u_long *)arg, - 2 * sizeof(u_long)); + if (copy_from_user(&erase->addr, (u_long *)arg, + 2 * sizeof(u_long))) { + kfree(erase); + return -EFAULT; + } erase->mtd = mtd; erase->callback = mtd_erase_callback; erase->priv = (unsigned long)&waitq; - /* FIXME: Allow INTERRUPTIBLE. Which means - not having the wait_queue head on the stack - - Does it? Why? Who wrote this? Was it my alter - ago - the intelligent one? Or was it the stupid - one, and now I'm being clever I don't know what - it was on about? - - dwmw2. - - It was the intelligent one. If the wq_head is - on the stack, and we leave because we got - interrupted, then the wq_head is no longer - there when the callback routine tries to - wake us up --> BOOM!. - + /* + FIXME: Allow INTERRUPTIBLE. Which means + not having the wait_queue head on the stack. + + If the wq_head is on the stack, and we + leave because we got interrupted, then the + wq_head is no longer there when the + callback routine tries to wake us up. */ current->state = TASK_UNINTERRUPTIBLE; add_wait_queue(&waitq, &wait); @@ -281,7 +292,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, void *databuf; ssize_t retlen; - copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)); + if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + return -EFAULT; if (buf.length > 0x4096) return -EINVAL; @@ -298,11 +310,13 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (!databuf) return -ENOMEM; - copy_from_user(databuf, buf.ptr, buf.length); + if (copy_from_user(databuf, buf.ptr, buf.length)) + return -EFAULT; ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); - copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)); + if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t))) + ret = -EFAULT; kfree(databuf); break; @@ -315,7 +329,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, void *databuf; ssize_t retlen; - copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)); + if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + return -EFAULT; if (buf.length > 0x4096) return -EINVAL; @@ -334,19 +349,42 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); - copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)); - - if (retlen) - copy_to_user(buf.ptr, databuf, retlen); - + if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t))) + ret = -EFAULT; + else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) + ret = -EFAULT; + kfree(databuf); break; } - - - - + case MEMLOCK: + { + unsigned long adrs[2]; + + if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long))) + return -EFAULT; + + if (!mtd->lock) + ret = -EOPNOTSUPP; + else + ret = mtd->lock(mtd, adrs[0], adrs[1]); + } + + case MEMUNLOCK: + { + unsigned long adrs[2]; + + if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long))) + return -EFAULT; + + if (!mtd->unlock) + ret = -EOPNOTSUPP; + else + ret = mtd->unlock(mtd, adrs[0], adrs[1]); + } + + default: printk("Invalid ioctl %x (MEMGETINFO = %x)\n",cmd, MEMGETINFO); ret = -EINVAL; @@ -366,31 +404,84 @@ static struct file_operations mtd_fops = { }; -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#ifdef CONFIG_DEVFS_FS +/* Notification that a new device has been added. Create the devfs entry for + * it. */ + +static void mtd_notify_add(struct mtd_info* mtd) +{ + char name[8]; + + if (!mtd) + return; + + sprintf(name, "%d", mtd->index); + devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, + DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2, + S_IFCHR | S_IRUGO | S_IWUGO, + &mtd_fops, NULL); + + sprintf(name, "%dro", mtd->index); + devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name, + DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1, + S_IFCHR | S_IRUGO | S_IWUGO, + &mtd_fops, NULL); +} + +static void mtd_notify_remove(struct mtd_info* mtd) +{ + if (!mtd) + return; + + devfs_unregister(devfs_rw_handle[mtd->index]); + devfs_unregister(devfs_ro_handle[mtd->index]); +} +#endif + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_mtdchar init_module #define cleanup_mtdchar cleanup_module #endif -#endif mod_init_t init_mtdchar(void) { - - if (register_chrdev(MTD_CHAR_MAJOR,"mtd",&mtd_fops)) { +#ifdef CONFIG_DEVFS_FS + int i; + char name[8]; + struct mtd_info* mtd; + + if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) + { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_CHAR_MAJOR); + return -EAGAIN; + } + + devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); + + register_mtd_user(¬ifier); +#else + if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) + { printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", MTD_CHAR_MAJOR); return -EAGAIN; } +#endif return 0; } mod_exit_t cleanup_mtdchar(void) { - unregister_chrdev(MTD_CHAR_MAJOR,"mtd"); +#ifdef CONFIG_DEVFS_FS + unregister_mtd_user(¬ifier); + devfs_unregister(devfs_dir_handle); + devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +#else + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +#endif } -#if LINUX_VERSION_CODE > 0x20300 module_init(init_mtdchar); module_exit(cleanup_mtdchar); -#endif diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index b3f0157eba4d..8c30838ba4d2 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,15 +1,11 @@ /* - * $Id: mtdcore.c,v 1.13 2000/07/13 14:27:37 dwmw2 Exp $ + * $Id: mtdcore.c,v 1.27 2000/12/10 01:10:09 dwmw2 Exp $ * - * Core registration and callback routines for MTD + * Core registration and callback routines for MTD * drivers and users. * */ -#ifdef MTD_DEBUG -#define DEBUGLVL debug -#endif - #include #include #include @@ -29,67 +25,12 @@ #include -#ifdef MTD_DEBUG -static int debug = MTD_DEBUG; -MODULE_PARM(debug, "i"); -#endif - -/* Init code required for 2.2 kernels */ - -#if LINUX_VERSION_CODE < 0x20300 - -#ifdef CONFIG_MTD_DOC1000 -extern int init_doc1000(void); -#endif -#ifdef CONFIG_MTD_DOCPROBE -extern int init_doc(void); -#endif -#ifdef CONFIG_MTD_PHYSMAP -extern int init_physmap(void); -#endif -#ifdef CONFIG_MTD_RPXLITE -extern int init_rpxlite(void); -#endif -#ifdef CONFIG_MTD_OCTAGON -extern int init_octagon5066(void); -#endif -#ifdef CONFIG_MTD_PNC2000 -extern int init_pnc2000(void); -#endif -#ifdef CONFIG_MTD_VMAX -extern int init_vmax301(void); -#endif -#ifdef CONFIG_MTD_MIXMEM -extern int init_mixmem(void); -#endif -#ifdef CONFIG_MTD_PMC551 -extern int init_pmc551(void); -#endif -#ifdef CONFIG_MTD_NORA -extern int init_nora(void); -#endif -#ifdef CONFIG_FTL -extern int init_ftl(void); -#endif -#ifdef CONFIG_NFTL -extern int init_nftl(void); -#endif -#ifdef CONFIG_MTD_BLOCK -extern int init_mtdblock(void); -#endif -#ifdef CONFIG_MTD_CHAR -extern int init_mtdchar(void); -#endif - -#endif /* LINUX_VERSION_CODE < 0x20300 */ - - static DECLARE_MUTEX(mtd_table_mutex); static struct mtd_info *mtd_table[MAX_MTD_DEVICES]; static struct mtd_notifier *mtd_notifiers = NULL; /** - * add_mtd_device - register an MTD device + * add_mtd_device - register an MTD device * @mtd: pointer to new MTD device info structure * * Add a device to the list of MTD devices present in the system, and @@ -110,6 +51,7 @@ int add_mtd_device(struct mtd_info *mtd) struct mtd_notifier *not=mtd_notifiers; mtd_table[i] = mtd; + mtd->index = i; DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); while (not) { @@ -126,7 +68,7 @@ int add_mtd_device(struct mtd_info *mtd) } /** - * del_mtd_device - unregister an MTD device + * del_mtd_device - unregister an MTD device * @mtd: pointer to MTD device info structure * * Remove a device from the list of MTD devices present in the system, @@ -194,7 +136,7 @@ void register_mtd_user (struct mtd_notifier *new) * @new: pointer to notifier info structure * * Removes a callback function pair from the list of 'users' to be - * notified upon addition or removal of MTD devices. Causes the + * notified upon addition or removal of MTD devices. Causes the * 'remove' callback to be immediately invoked for each MTD device * currently present in the system. */ @@ -232,7 +174,7 @@ int unregister_mtd_user (struct mtd_notifier *old) * @mtd: last known address of the required MTD device * @num: internal device number of the required MTD device * - * Given a number and NULL address, return the num'th entry in the device + * Given a number and NULL address, return the num'th entry in the device * table, if any. Given an address and num == -1, search the device table * for a device with that address and return if it's still present. Given * both, return the num'th driver only if its address matches. Return NULL @@ -313,10 +255,10 @@ static inline int mtd_proc_info (char *buf, int i) { struct mtd_info *this = mtd_table[i]; - if (!this) + if (!this) return 0; - return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size, + return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size, this->name); } @@ -330,7 +272,7 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count { int len = 0, l, i; off_t begin = 0; - + down(&mtd_table_mutex); for (i=0; i< MAX_MTD_DEVICES; i++) { @@ -374,79 +316,13 @@ struct proc_dir_entry mtd_proc_entry = { /*====================================================================*/ /* Init code */ -#if LINUX_VERSION_CODE < 0x20300 - -static inline void init_others(void) -{ - /* Shedloads of calls to init functions of all the - * other drivers and users of MTD, which we can - * ditch in 2.3 because of the sexy new way of - * finding init routines. - */ -#ifdef CONFIG_MTD_DOC1000 - init_doc1000(); -#endif -#ifdef CONFIG_MTD_DOCPROBE - init_doc(); /* This covers both the DiskOnChip 2000 - * and the DiskOnChip Millennium. - * Theoretically all other DiskOnChip - * devices too. */ -#endif -#ifdef CONFIG_MTD_PHYSMAP - init_physmap(); -#endif -#ifdef CONFIG_MTD_RPXLITE - init_rpxlite(); -#endif -#ifdef CONFIG_MTD_OCTAGON - init_octagon5066(); -#endif -#ifdef CONFIG_MTD_PNC2000 - init_pnc2000(); -#endif -#ifdef CONFIG_MTD_VMAX - init_vmax301(); -#endif -#ifdef CONFIGF_MTD_MIXMEM - init_mixmem(); -#endif -#ifdef CONFIG_MTD_PMC551 - init_pmc551(); -#endif -#ifdef CONFIG_MTD_NORA - init_nora(); -#endif -#ifdef CONFIG_MTD_MTDRAM - init_mtdram(); -#endif -#ifdef CONFIG_FTL - init_ftl(); -#endif -#ifdef CONFIG_NFTL - init_nftl(); -#endif -#ifdef CONFIG_MTD_BLOCK - init_mtdblock(); -#endif -#ifdef CONFIG_MTD_CHAR - init_mtdchar(); -#endif -} - -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_mtd init_module #define cleanup_mtd cleanup_module #endif -#endif /* LINUX_VERSION_CODE < 0x20300 */ - mod_init_t init_mtd(void) { - int i; - DEBUG(1, "INIT_MTD:\n"); - for (i=0; i= KERNEL_VERSION(2,2,0) if ((proc_mtd = create_proc_entry( "mtd", 0, 0 ))) @@ -454,12 +330,12 @@ mod_init_t init_mtd(void) #else proc_register_dynamic(&proc_root,&mtd_proc_entry); #endif - #endif -#if LINUX_VERSION_CODE < 0x20300 - init_others(); +#if LINUX_VERSION_CODE < 0x20212 + init_mtd_devices(); #endif + #ifdef CONFIG_PM mtd_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, mtd_pm_callback); #endif @@ -468,13 +344,13 @@ mod_init_t init_mtd(void) mod_exit_t cleanup_mtd(void) { - unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); #ifdef CONFIG_PM if (mtd_pm_dev) { pm_unregister(mtd_pm_dev); mtd_pm_dev = NULL; } #endif + #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) if (proc_mtd) @@ -484,10 +360,8 @@ mod_exit_t cleanup_mtd(void) #endif #endif } - -#if LINUX_VERSION_CODE > 0x20300 + module_init(init_mtd); module_exit(cleanup_mtd); -#endif diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c new file mode 100644 index 000000000000..0135be39ea43 --- /dev/null +++ b/drivers/mtd/mtdpart.c @@ -0,0 +1,228 @@ +/* + * Simple MTD partitioning layer + * + * (C) 2000 Nicolas Pitre + * + * This code is GPL + * + * $Id: mtdpart.c,v 1.7 2000/12/09 23:29:47 dwmw2 Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include + + +/* Our partition linked list */ +static LIST_HEAD(mtd_partitions); + +/* Our partition node structure */ +struct mtd_part { + struct mtd_info mtd; + struct mtd_info *master; + loff_t offset; + int index; + struct list_head list; +}; + +/* + * Given a pointer to the MTD object in the mtd_part structure, we can retrieve + * the pointer to that structure with this macro. + */ +#define PART(x) ((struct mtd_part *)(x)) + + +/* + * MTD methods which simply translate the effective address and pass through + * to the _real_ device. + */ + +static int part_read (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; + return part->master->read (part->master, from + part->offset, + len, retlen, buf); +} + +static int part_write (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; + return part->master->write (part->master, to + part->offset, + len, retlen, buf); +} + +static int part_writev (struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + return part->master->writev (part->master, vecs, count, + to + part->offset, retlen); +} + +static int part_readv (struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen) +{ + struct mtd_part *part = PART(mtd); + return part->master->readv (part->master, vecs, count, + from + part->offset, retlen); +} + +static int part_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (instr->addr >= mtd->size) + return -EINVAL; + instr->addr += part->offset; + return part->master->erase(part->master, instr); +} + +static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->lock(part->master, ofs + part->offset, len); +} + +static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->unlock(part->master, ofs + part->offset, len); +} + + +/* + * This function unregisters and destroy all slave MTD objects which are + * attached to the given master MTD object. + */ + +int del_mtd_partitions(struct mtd_info *master) +{ + struct list_head *node; + struct mtd_part *slave; + + for (node = mtd_partitions.next; + node != &mtd_partitions; + node = node->next) { + slave = list_entry(node, struct mtd_part, list); + if (slave->master == master) { + struct list_head *prev = node->prev; + __list_del(prev, node->next); + del_mtd_device(&slave->mtd); + kfree(slave); + node = prev; + MOD_DEC_USE_COUNT; + } + } + + return 0; +} + + +/* + * This function, given a master MTD object and a partition table, creates + * and registers slave MTD objects which are bound to the master according to + * the partition definitions. + * (Q: should we register the master MTD object as well?) + */ + +int add_mtd_partitions(struct mtd_info *master, + struct mtd_partition *parts, + int nbparts) +{ + struct mtd_part *slave; + u_long cur_offset = 0; + int i; + + for (i = 0; i < nbparts; i++) { + /* allocate the partition structure */ + slave = kmalloc (sizeof(*slave), GFP_KERNEL); + if (!slave) { + printk ("memory allocation error while creating partitions for \"%s\"\n", + master->name); + del_mtd_partitions(master); + return -ENOMEM; + } + list_add(&slave->list, &mtd_partitions); + + /* set up the MTD object for this partition */ + slave->mtd = *master; + slave->mtd.name = parts[i].name; + slave->mtd.size = parts[i].size; + slave->mtd.flags &= ~parts[i].mask_flags; + slave->mtd.read = part_read; + slave->mtd.write = part_write; + if (slave->mtd.writev) + slave->mtd.writev = part_writev; + if (slave->mtd.readv) + slave->mtd.readv = part_readv; + if (slave->mtd.lock) + slave->mtd.lock = part_lock; + if (slave->mtd.unlock) + slave->mtd.unlock = part_unlock; + slave->mtd.erase = part_erase; + slave->master = master; + slave->offset = parts[i].offset; + slave->index = i; + + if (slave->offset == 0) + slave->offset = cur_offset; + if (slave->mtd.size == 0) + slave->mtd.size = master->size - slave->offset; + cur_offset = slave->offset + slave->mtd.size; + + /* let's do some sanity checks */ + if ((slave->mtd.flags & MTD_WRITEABLE) && + (parts[i].offset % master->erasesize)) { + slave->mtd.flags &= ~MTD_WRITEABLE; + printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", + parts[i].name); + } + if ((slave->mtd.flags & MTD_WRITEABLE) && + (parts[i].size % master->erasesize)) { + slave->mtd.flags &= ~MTD_WRITEABLE; + printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", + parts[i].name); + } + if (parts[i].offset >= master->size) { + /* let's register it anyway to preserve ordering */ + slave->offset = 0; + slave->mtd.size = 0; + printk ("mtd: partition \"%s\" is out of reach -- disabled\n", + parts[i].name); + } + if (parts[i].offset + parts[i].size > master->size) { + slave->mtd.size = master->size - parts[i].offset; + printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#lx\n", + parts[i].name, master->name, slave->mtd.size); + } + + /* register our partition */ + add_mtd_device(&slave->mtd); + MOD_INC_USE_COUNT; + } + + return 0; +} + +EXPORT_SYMBOL(add_mtd_partitions); +EXPORT_SYMBOL(del_mtd_partitions); diff --git a/drivers/mtd/nftl.c b/drivers/mtd/nftl.c index e7040863f469..d7e2aeaadc34 100644 --- a/drivers/mtd/nftl.c +++ b/drivers/mtd/nftl.c @@ -1,32 +1,35 @@ - /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftl.c,v 1.36 2000/07/13 14:14:20 dwmw2 Exp $ */ +/* $Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $ */ /* - The contents of this file are distributed under the GNU Public - Licence version 2 ("GPL"). The legal note below refers only to the - _use_ of the code in some jurisdictions, and does not in any way - affect the copying, distribution and modification of this code, - which is permitted under the terms of the GPL. + The contents of this file are distributed under the GNU General + Public License version 2 ("GPL"). The author places no additional + restrictions of any kind on it. However, local legislation in some + countries may restrict the use of the algorithms implemented by this + code in certain circumstances. - Section 0 of the GPL says: + The legal note below refers only to the _use_ of the code in the + affected jurisdictions, and does not in any way affect the copying, + distribution and modification of this code, which are permitted, and + indeed required, under the terms of the GPL. + Section 0 of the GPL says: "Activities other than copying, distribution and modification are not covered by this License; they are outside its scope." You may copy, distribute and modify this code to your hearts' content - it's just that in some jurisdictions, you may only _use_ - it under the terms of the licence below. This puts it in a similar - situation to the ISDN code, which you may need telco approval to - use, and indeed any code which has uses that may be restricted in - law. For example, certain malicious uses of the networking stack - may be illegal, but that doesn't prevent the networking code from - being under GPL. + it under the terms of the patent grant below. This puts it in a + similar situation to the ISDN code, which you may need telco + approval to use, and indeed any code which has uses that may be + restricted in law. For example, certain malicious uses of the + networking stack may be illegal, but that doesn't prevent the + networking code from being under GPL. In fact the ISDN case is worse than this, because modification of - the code automatically invalidates its approval. Modificiation, + the code automatically invalidates its approval. Modification, unlike usage, _is_ one of the rights which is protected by the GPL. Happily, the law in those places where approval is required doesn't actually prevent you from modifying the code - it's just @@ -34,7 +37,7 @@ because usage isn't addressed by the GPL, that's just fine. dwmw2@infradead.org - 6/7/0 + 30/10/0 LEGAL NOTE: The NFTL format is patented by M-Systems. They have granted a licence for its use with their DiskOnChip products: @@ -51,10 +54,6 @@ #define PRERELEASE -#ifdef NFTL_DEBUG -#define DEBUGLVL debug -#endif - #include #include #include @@ -67,42 +66,28 @@ #include #include #include +#include +#ifdef CONFIG_KMOD +#include +#endif #include #include #include -#undef WE_KNOW_WTF_THIS_DOES_NOT_WORK +/* maximum number of loops while examining next block, to have a + chance to detect consistency problems (they should never happen + because of the checks done in the mounting */ + +#define MAX_LOOPS 10000 /* NFTL block device stuff */ #define MAJOR_NR NFTL_MAJOR #define DEVICE_REQUEST nftl_request #define DEVICE_OFF(device) -#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK -#define LOCAL_END_REQUEST -#endif -#include -#include - - -#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK - -static void nftl_end_request(struct request *req, int res) -{ - req->sector += req->current_nr_sectors; - req->nr_sectors -= req->current_nr_sectors; - - if (end_that_request_first( req, res, "nftl" )) - return; - end_that_request_last( req ); -} -#endif - -#ifdef NFTL_DEBUG -static int debug = NFTL_DEBUG; -MODULE_PARM(debug, "i"); -#endif +#include +#include /* Linux-specific block device functions */ @@ -110,11 +95,10 @@ MODULE_PARM(debug, "i"); * encountered, except ... */ -static int nftl_sizes[256]={0,}; +static int nftl_sizes[256] = {0,}; static int nftl_blocksizes[256] = {0,}; /* .. for the Linux partition table handling. */ - struct hd_struct part_table[256] = {{0,0},}; #if LINUX_VERSION_CODE < 0x20328 @@ -123,8 +107,8 @@ static void dummy_init (struct gendisk *crap) #endif static struct gendisk nftl_gendisk = { - NFTL_MAJOR, /* Major number */ - "nftl", /* Major name */ + MAJOR_NR, /* Major number */ + "nftl", /* Major name */ 4, /* Bits to shift to get real from partition */ 15, /* Number of partitions per real */ #if LINUX_VERSION_CODE < 0x20328 @@ -138,281 +122,129 @@ static struct gendisk nftl_gendisk = { NULL /* next */ }; - struct NFTLrecord *NFTLs[MAX_NFTLS] = {NULL}; -static void NFTL_setup(struct mtd_info *mtd, unsigned long ofs, - struct NFTLMediaHeader *hdr) +static void NFTL_setup(struct mtd_info *mtd) { int i; - struct NFTLrecord *thisNFTL; + struct NFTLrecord *nftl; unsigned long temp; int firstfree = -1; - DEBUG(1,"NFTL_setup\n"); + DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n"); - for (i=0; i < MAX_NFTLS; i++) { - if (!NFTLs[i] && firstfree==-1) + for (i = 0; i < MAX_NFTLS; i++) { + if (!NFTLs[i] && firstfree == -1) firstfree = i; - else if (NFTLs[i] && NFTLs[i]->mtd == mtd && - NFTLs[i]->MediaHdr.FirstPhysicalEUN == hdr->FirstPhysicalEUN) { + else if (NFTLs[i] && NFTLs[i]->mtd == mtd) { /* This is a Spare Media Header for an NFTL we've already found */ - DEBUG(1, "Spare Media Header for NFTL %d found at %lx\n",i, ofs); - NFTLs[i]->SpareMediaUnit = ofs / mtd->erasesize; + DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n"); return; } } - - - /* OK, it's a new one. Set up all the data structures. */ -#ifdef PSYCHO_DEBUG - printk("Found new NFTL nftl%c at offset %lx\n",firstfree + 'a', ofs); -#endif - if (hdr->UnitSizeFactor != 0xff) { - printk("Sorry, we don't support UnitSizeFactor of != 1 yet\n"); + if (firstfree == -1) { + printk(KERN_WARNING "No more NFTL slot available\n"); return; - } - - thisNFTL = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); - if (!thisNFTL) { + } + + nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); + if (!nftl) { printk(KERN_WARNING "Out of memory for NFTL data structures\n"); return; } - init_MUTEX(&thisNFTL->mutex); - thisNFTL->EraseSize = mtd->erasesize; - memcpy(&thisNFTL->MediaHdr, hdr, sizeof(*hdr)); - thisNFTL->mtd = mtd; - thisNFTL->MediaUnit = ofs / mtd->erasesize; - thisNFTL->SpareMediaUnit = 0xffff; - thisNFTL->numvunits = le32_to_cpu(thisNFTL->MediaHdr.FormattedSize) / 8192; - thisNFTL->nr_sects = thisNFTL->numvunits * (thisNFTL->EraseSize / 512); - thisNFTL->usecount = 0; - thisNFTL->cylinders = 1024; - thisNFTL->heads = 16; + init_MUTEX(&nftl->mutex); - temp = thisNFTL->cylinders * thisNFTL->heads; - thisNFTL->sectors = thisNFTL->nr_sects / temp; + /* get physical parameters */ + nftl->EraseSize = mtd->erasesize; + nftl->nb_blocks = mtd->size / mtd->erasesize; + nftl->mtd = mtd; - if (thisNFTL->nr_sects % temp) { - - thisNFTL->sectors++; - temp = thisNFTL->cylinders * thisNFTL->sectors; - thisNFTL->heads = thisNFTL->nr_sects / temp; - - if (thisNFTL->nr_sects & temp) { - thisNFTL->heads++; - temp = thisNFTL->heads * thisNFTL->sectors; - - thisNFTL->cylinders = thisNFTL->nr_sects / temp; - } - } - if (thisNFTL->nr_sects != thisNFTL->heads * thisNFTL->cylinders * - thisNFTL->sectors) { - printk(KERN_WARNING "Cannot calculate an NFTL geometry to match size of 0x%lx.\n", thisNFTL->nr_sects); - printk(KERN_WARNING "Using C:%d H:%d S:%d (== %lx sects)\n", - thisNFTL->cylinders, thisNFTL->heads , - thisNFTL->sectors, - (long)thisNFTL->cylinders * (long)thisNFTL->heads * - (long)thisNFTL->sectors ); - - /* Oh no we don't - * thisNFTL->nr_sects = thisNFTL->heads * thisNFTL->cylinders * thisNFTL->sectors; - */ - } - - - thisNFTL->EUNtable = kmalloc( 2 * thisNFTL->numvunits, - GFP_KERNEL); - if (!thisNFTL->EUNtable) { - printk("ENOMEM\n"); - kfree(thisNFTL); + if (NFTL_mount(nftl) < 0) { + printk(KERN_WARNING "Could not mount NFTL device\n"); + kfree(nftl); return; - } - memset(thisNFTL->EUNtable, 0xff, 2 * thisNFTL->numvunits); - - thisNFTL->VirtualUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL); - if (!thisNFTL->VirtualUnitTable) { - printk("ENOMEM\n"); - kfree(thisNFTL->EUNtable); - kfree(thisNFTL); - return; - } - memset(thisNFTL->VirtualUnitTable, 0xff, 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits)); - - thisNFTL->ReplUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL); - if (!thisNFTL->ReplUnitTable) { - printk("ENOMEM\n"); - kfree(thisNFTL->VirtualUnitTable); - kfree(thisNFTL->EUNtable); - kfree(thisNFTL); - return; - } - memset(thisNFTL->ReplUnitTable, 0xff, 2 *le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) ); - - /* Ought to check the media header for bad blocks */ - thisNFTL->lastEUN = le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) + - le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN) - 1; - thisNFTL->numfreeEUNs = 0; + } - /* Scan each physical Erase Unit for validity and to find the - Virtual Erase Unit Chain to which it belongs */ - - for (i=le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); - i <= thisNFTL->lastEUN; i++) { - - union nftl_uci uci; - unsigned long ofs; - size_t retlen; - ofs = i * thisNFTL->EraseSize; - - MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 512 + 8, 8, &retlen, (char *)&uci); - - if (uci.b.EraseMark != cpu_to_le16(0x3c69) || - uci.b.EraseMark1 != cpu_to_le16(0x3c69)) { - printk("EUN %d: EraseMark not 0x3c69 (0x%4.4x 0x%4.4x instead)\n", - i, le16_to_cpu(uci.b.EraseMark), le16_to_cpu(uci.b.EraseMark1)); - thisNFTL->VirtualUnitTable[i] = 0x7fff; - thisNFTL->ReplUnitTable[i] = 0xffff; - continue; - } - - MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 8, 8, &retlen, (u_char *)&uci); - - if (uci.a.VirtUnitNum != uci.a.SpareVirtUnitNum) - printk("EUN %d: VirtualUnitNumber (%x) != SpareVirtualUnitNumber (%x)\n", - i, le16_to_cpu(uci.a.VirtUnitNum), - le16_to_cpu(uci.a.SpareVirtUnitNum)); - - if (uci.a.ReplUnitNum != uci.a.SpareReplUnitNum) - printk("EUN %d: ReplacementUnitNumber (%x) != SpareReplacementUnitNumber (%x)\n", - i, le16_to_cpu(uci.a.ReplUnitNum), - le16_to_cpu(uci.a.SpareReplUnitNum)); - - /* We don't actually _do_ anything about the above, just whinge */ - - thisNFTL->VirtualUnitTable[i] = le16_to_cpu(uci.a.VirtUnitNum); - thisNFTL->ReplUnitTable[i] = le16_to_cpu(uci.a.ReplUnitNum); - - /* if (!(VUN & 0x8000) && VUN < (arraybounds)).. optimises to: */ - if (le16_to_cpu(uci.a.VirtUnitNum) < thisNFTL->numvunits) - thisNFTL->EUNtable[le16_to_cpu(uci.a.VirtUnitNum) & 0x7fff] = i; - - if (uci.a.VirtUnitNum == 0xffff) { - /* Free block */ - thisNFTL->LastFreeEUN = i; - thisNFTL->numfreeEUNs++; - } - - } - NFTLs[firstfree] = thisNFTL; - thisNFTL->LastFreeEUN = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); - - //#define PSYCHO_DEBUG + /* OK, it's a new one. Set up all the data structures. */ #ifdef PSYCHO_DEBUG - for (i=0; i < 10/* thisNFTL->numvunits*/; i++) { - u16 curEUN = thisNFTL->EUNtable[i]; - int sillycount=100; - - printk("Virtual Unit #%d: ",i); - if (!curEUN || curEUN == 0xffff) { - printk("Not present\n"); - continue; - } - printk("%d", curEUN); - - while ((curEUN = thisNFTL->ReplUnitTable[curEUN]) != 0xffff && --sillycount) { - printk(", %d", curEUN & 0xffff); - + printk("Found new NFTL nftl%c\n", firstfree + 'a'); +#endif + + /* linux stuff */ + nftl->usecount = 0; + nftl->cylinders = 1024; + nftl->heads = 16; + + temp = nftl->cylinders * nftl->heads; + nftl->sectors = nftl->nr_sects / temp; + if (nftl->nr_sects % temp) { + nftl->sectors++; + temp = nftl->cylinders * nftl->sectors; + nftl->heads = nftl->nr_sects / temp; + + if (nftl->nr_sects % temp) { + nftl->heads++; + temp = nftl->heads * nftl->sectors; + nftl->cylinders = nftl->nr_sects / temp; } - printk("\n"); } -#endif - /* OK. Now we deal with the fact that we're in the real world. Sometimes - things don't actually happen the way they're supposed to. Find, fix, - and whinge about the most common deviations from spec that we have - been known to encounter. - */ - /* Except that I haven't implemented that bit yet :) */ + if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) { + printk(KERN_WARNING "Cannot calculate an NFTL geometry to " + "match size of 0x%lx.\n", nftl->nr_sects); + printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n", + nftl->cylinders, nftl->heads , nftl->sectors, + (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors ); + /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */ + } + NFTLs[firstfree] = nftl; /* Finally, set up the block device sizes */ - nftl_sizes[firstfree * 16]=thisNFTL->nr_sects; -// nftl_blocksizes[firstfree*16] = 512; - part_table[firstfree * 16].nr_sects = thisNFTL->nr_sects; + nftl_sizes[firstfree * 16] = nftl->nr_sects; + //nftl_blocksizes[firstfree*16] = 512; + part_table[firstfree * 16].nr_sects = nftl->nr_sects; + + /* partition check ... */ #if LINUX_VERSION_CODE < 0x20328 resetup_one_dev(&nftl_gendisk, firstfree); #else - grok_partitions(&nftl_gendisk, firstfree, 1<<4, thisNFTL->nr_sects); + grok_partitions(&nftl_gendisk, firstfree, 1<<4, nftl->nr_sects); #endif - } - static void NFTL_unsetup(int i) { - struct NFTLrecord *thisNFTL = NFTLs[i]; + struct NFTLrecord *nftl = NFTLs[i]; - DEBUG(1, "NFTL_unsetup %d\n", i); + DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i); NFTLs[i] = NULL; - if (thisNFTL->VirtualUnitTable) - kfree(thisNFTL->VirtualUnitTable); - if (thisNFTL->ReplUnitTable) - kfree(thisNFTL->ReplUnitTable); - if (thisNFTL->EUNtable) - kfree(thisNFTL->EUNtable); + if (nftl->ReplUnitTable) + kfree(nftl->ReplUnitTable); + if (nftl->EUNtable) + kfree(nftl->EUNtable); - kfree(thisNFTL); + kfree(nftl); } - - - /* Search the MTD device for NFTL partitions */ static void NFTL_notify_add(struct mtd_info *mtd) { - int i; - unsigned long ofs; - struct NFTLMediaHeader hdr; - - DEBUG(1, "NFTL_notify_add for %s\n", mtd->name); + DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name); if (mtd) { - if (!mtd->read_oob) /* If this MTD doesn't have out-of-band data, - then there's no point continuing */ - { - DEBUG(1, "No OOB data, quitting\n"); + if (!mtd->read_oob) { + /* If this MTD doesn't have out-of-band data, + then there's no point continuing */ + DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n"); return; } - DEBUG(3, "mtd->read = %p,size = %d, erasesize = %d\n", - mtd->read, mtd->size, mtd->erasesize); - for (ofs = 0; ofs < mtd->size ; ofs += mtd->erasesize) { - size_t retlen = 0; - MTD_READ(mtd, ofs, sizeof(hdr), &retlen, (u_char *)&hdr); - - if (retlen < sizeof(hdr)) - { - continue; - } - - if (!strncmp(hdr.DataOrgID, "ANAND", 6)) { - DEBUG(2, "Valid NFTL partition at ofs %ld\n", ofs); - NFTL_setup(mtd, ofs, &hdr); - } - else { - DEBUG(3,"No valid NFTL Partition at ofs %d\n", ofs); - for(i = 0; i < 6; i++) { - DEBUG(3,"%x, ", hdr.DataOrgID[i]); - } - DEBUG(3," = %s\n", hdr.DataOrgID); - DEBUG(3,"%d, %d, %d, %d\n", hdr.NumEraseUnits, hdr.FirstPhysicalEUN, - hdr.FormattedSize, hdr.UnitSizeFactor); + DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n", + mtd->read, mtd->size, mtd->erasesize); - } - } - return; + NFTL_setup(mtd); } } @@ -420,100 +252,101 @@ static void NFTL_notify_remove(struct mtd_info *mtd) { int i; - for (i=0; i< MAX_NFTLS; i++) { + for (i = 0; i < MAX_NFTLS; i++) { if (NFTLs[i] && NFTLs[i]->mtd == mtd) NFTL_unsetup(i); } } - #ifdef CONFIG_NFTL_RW /* Actual NFTL access routines */ - - -static u16 NFTL_findfreeblock( struct NFTLrecord *thisNFTL, int desperate ) +/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used + * when the give Virtual Unit Chain + */ +static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate ) { /* For a given Virtual Unit Chain: find or create a free block and add it to the chain */ /* We're passed the number of the last EUN in the chain, to save us from having to look it up again */ - - u16 pot = thisNFTL->LastFreeEUN; + u16 pot = nftl->LastFreeEUN; int silly = -1; /* Normally, we force a fold to happen before we run out of free blocks completely */ - - if (!desperate && thisNFTL->numfreeEUNs < 2) { - // printk("NFTL_findfreeblock: there are too few free EUNs\n"); + if (!desperate && nftl->numfreeEUNs < 2) { + DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n"); return 0xffff; } /* Scan for a free block */ - do { - if (thisNFTL->VirtualUnitTable[pot] == 0xffff) { - thisNFTL->LastFreeEUN = pot; - thisNFTL->numfreeEUNs--; + if (nftl->ReplUnitTable[pot] == BLOCK_FREE) { + nftl->LastFreeEUN = pot; + nftl->numfreeEUNs--; return pot; } - if (++pot > thisNFTL->lastEUN) - pot = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); + /* This will probably point to the MediaHdr unit itself, + right at the beginning of the partition. But that unit + (and the backup unit too) should have the UCI set + up so that it's not selected for overwriting */ + if (++pot > nftl->lastEUN) + pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN); if (!silly--) { - printk("Tell Dave he fucked up. LastFreeEUN = %d, FirstEUN = %d\n", - thisNFTL->LastFreeEUN, le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN)); + printk("Argh! No free blocks found! LastFreeEUN = %d, " + "FirstEUN = %d\n", nftl->LastFreeEUN, + le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN)); return 0xffff; } - - } while (pot != thisNFTL->LastFreeEUN); + } while (pot != nftl->LastFreeEUN); return 0xffff; } - - - - -static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pendingblock ) +static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock ) { - u16 BlockMap[thisNFTL->EraseSize / 512]; - unsigned char BlockLastState[thisNFTL->EraseSize / 512]; - unsigned char BlockFreeFound[thisNFTL->EraseSize / 512]; - u16 thisEUN; + u16 BlockMap[MAX_SECTORS_PER_UNIT]; + unsigned char BlockLastState[MAX_SECTORS_PER_UNIT]; + unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT]; + unsigned int thisEUN; int block; - int silly = -1; - u16 targetEUN = 0xffff; + int silly; + unsigned int targetEUN; struct nftl_oob oob; int inplace = 1; + size_t retlen; memset(BlockMap, 0xff, sizeof(BlockMap)); memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); - thisEUN = thisNFTL->EUNtable[thisVUC]; + thisEUN = nftl->EUNtable[thisVUC]; - if (thisEUN == 0xffff) { - printk(KERN_WARNING "Trying to fold non-existent Virtual Unit Chain %d!\n", thisVUC); - return 0xffff; + if (thisEUN == BLOCK_NIL) { + printk(KERN_WARNING "Trying to fold non-existent " + "Virtual Unit Chain %d!\n", thisVUC); + return BLOCK_NIL; } /* Scan to find the Erase Unit which holds the actual data for each 512-byte block within the Chain. */ + silly = MAX_LOOPS; + targetEUN = BLOCK_NIL; + while (thisEUN <= nftl->lastEUN ) { + unsigned int status, foldmark; - while( thisEUN <= thisNFTL->lastEUN ) { - size_t retlen; - targetEUN = thisEUN; - - for (block = 0 ; block < thisNFTL->EraseSize / 512; block ++) { - - MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + (block * 512),16 , &retlen, (char *)&oob); - + for (block = 0; block < nftl->EraseSize / 512; block ++) { + MTD_READOOB(nftl->mtd, + (thisEUN * nftl->EraseSize) + (block * 512), + 16 , &retlen, (char *)&oob); if (block == 2) { - if (oob.u.c.WriteInh != 0xffffffff) { - printk("Write Inhibited on EUN %d\n", thisEUN); + foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; + if (foldmark == FOLD_MARK_IN_PROGRESS) { + DEBUG(MTD_DEBUG_LEVEL1, + "Write Inhibited on EUN %d\n", thisEUN); inplace = 0; } else { /* There's no other reason not to do inplace, @@ -522,153 +355,139 @@ static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pe inplace = 1; } } + status = oob.b.Status | oob.b.Status1; + BlockLastState[block] = status; - BlockLastState[block] = (unsigned char) oob.b.Status & 0xff; - - switch(oob.b.Status) { - case __constant_cpu_to_le16(BLOCK_FREE): - BlockFreeFound[block]=1; + switch(status) { + case SECTOR_FREE: + BlockFreeFound[block] = 1; break; - case __constant_cpu_to_le16(BLOCK_USED): + case SECTOR_USED: if (!BlockFreeFound[block]) BlockMap[block] = thisEUN; else - printk(KERN_WARNING "BLOCK_USED found after BLOCK_FREE in Virtual Unit Chain %d for block %d\n", thisVUC, block); + printk(KERN_WARNING + "SECTOR_USED found after SECTOR_FREE " + "in Virtual Unit Chain %d for block %d\n", + thisVUC, block); break; - case __constant_cpu_to_le16(BLOCK_IGNORE): - case __constant_cpu_to_le16(BLOCK_DELETED): + case SECTOR_IGNORE: + case SECTOR_DELETED: break; default: - printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, oob.b.Status); + printk("Unknown status for block %d in EUN %d: %x\n", + block, thisEUN, status); } } if (!silly--) { - printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC); - return 0xffff; + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", + thisVUC); + return BLOCK_NIL; } - thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; + thisEUN = nftl->ReplUnitTable[thisEUN]; } if (inplace) { /* We're being asked to be a fold-in-place. Check - that all blocks are either present or BLOCK_FREE + that all blocks are either present or SECTOR_FREE in the target block. If not, we're going to have to fold out-of-place anyway. */ - - for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) { - - if (BlockLastState[block] != (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff) && + for (block = 0; block < nftl->EraseSize / 512 ; block++) { + if (BlockLastState[block] != SECTOR_FREE && BlockMap[block] != targetEUN) { - DEBUG(1, "Setting inplace to 0. VUC %d, block %d was %x lastEUN, and is in EUN %d (%s) %d\n", - thisVUC, block, BlockLastState[block], BlockMap[block] , BlockMap[block]==targetEUN?"==":"!=", targetEUN); - + DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, " + "block %d was %x lastEUN, " + "and is in EUN %d (%s) %d\n", + thisVUC, block, BlockLastState[block], + BlockMap[block], + BlockMap[block]== targetEUN ? "==" : "!=", + targetEUN); inplace = 0; break; } } - if ( pendingblock >= (thisVUC * (thisNFTL->EraseSize / 512)) && - pendingblock < ((thisVUC + 1)* (thisNFTL->EraseSize / 512)) && - BlockLastState[ pendingblock - (thisVUC * (thisNFTL->EraseSize / 512))] != - (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff)) { - DEBUG(1, "Pending write not free in EUN %d. Folding out of place.\n", targetEUN); + if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) && + pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) && + BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] != + SECTOR_FREE) { + DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. " + "Folding out of place.\n", targetEUN); inplace = 0; } - } if (!inplace) { - DEBUG(1, "Cannot fold Virtual Unit Chain %d in place. Trying out-of-place\n", thisVUC); + DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. " + "Trying out-of-place\n", thisVUC); /* We need to find a targetEUN to fold into. */ - targetEUN = NFTL_findfreeblock(thisNFTL, 1); - if (targetEUN == 0xffff) { - /* Ouch. Now we're screwed. We need to do a - fold-in-place of another chain to make room - for this one. We need a better way of selecting - which chain to fold, because makefreeblock will - only ask us to fold the same one again. - */ - printk(KERN_WARNING"NFTL_findfreeblock(desperate) returns 0xffff.\n"); - return 0xffff; + targetEUN = NFTL_findfreeblock(nftl, 1); + if (targetEUN == BLOCK_NIL) { + /* Ouch. Now we're screwed. We need to do a + fold-in-place of another chain to make room + for this one. We need a better way of selecting + which chain to fold, because makefreeblock will + only ask us to fold the same one again. + */ + printk(KERN_WARNING + "NFTL_findfreeblock(desperate) returns 0xffff.\n"); + return BLOCK_NIL; } - - } - + } else { + /* We put a fold mark in the chain we are folding only if + we fold in place to help the mount check code. If we do + not fold in place, it is possible to find the valid + chain by selecting the longer one */ + oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); + oob.u.c.unused = 0xffffffff; + MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, + 8, &retlen, (char *)&oob.u); + } /* OK. We now know the location of every block in the Virtual Unit Chain, and the Erase Unit into which we are supposed to be copying. Go for it. */ - - DEBUG(1,"Folding chain %d into unit %d\n", thisVUC, targetEUN); - - for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) { + DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN); + for (block = 0; block < nftl->EraseSize / 512 ; block++) { unsigned char movebuf[512]; - struct nftl_oob oob; - size_t retlen; - - memset(&oob, 0xff, sizeof(oob)); + int ret; /* If it's in the target EUN already, or if it's pending write, do nothing */ - if (BlockMap[block] == targetEUN ||(pendingblock == (thisVUC * (thisNFTL->EraseSize / 512) + block))) { - /* Except if it's the first block, in which case we have to - set the UnitNumbers */ - if (block == 0) { - - thisNFTL->mtd->read_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) , - 16, &retlen, (char *)&oob); - - // printk("Setting VirtUnitNum on EUN %d to %x, was %x\n", targetEUN, thisVUC, - // le16_to_cpu(oob.u.a.VirtUnitNum)); - - oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff); - - thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) , - 16, &retlen, (char *)&oob); - } + if (BlockMap[block] == targetEUN || + (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) { continue; } - oob.b.Status = BLOCK_USED; - - switch(block) { - case 0: - oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff); - // printk("Setting VirtUnitNum on EUN %d to %x\n", targetEUN, thisVUC); - - oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; - break; - - case 1: - oob.u.b.WearInfo = cpu_to_le32(3); // We don't use this, but M-Systems' drivers do - oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69); - break; - - case 2: - default: - oob.u.c.WriteInh = 0xffffffff; - oob.u.c.unused = 0xffffffff; - } - if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512), - 512, &retlen, movebuf, (char *)&oob) == -EIO) { - if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512), - 512, &retlen, movebuf, (char *)&oob) != -EIO) - printk("Error went away on retry.\n"); - } - - thisNFTL->mtd->write_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512), - 512, &retlen, movebuf, (char *)&oob); - - - /* FIXME: Add some error checking.... */ - thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512), - 16, &retlen, (char *)&oob); - + /* copy only in non free block (free blocks can only + happen in case of media errors or deleted blocks) */ + if (BlockMap[block] == BLOCK_NIL) + continue; + + ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + + (block * 512), 512, &retlen, movebuf, (char *)&oob); + if (ret < 0) { + ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + + (block * 512), 512, &retlen, + movebuf, (char *)&oob); + if (ret != -EIO) + printk("Error went away on retry.\n"); + } + MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512), + 512, &retlen, movebuf, (char *)&oob); } + + /* add the header so that it is now a valid chain */ + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum + = cpu_to_le16(thisVUC); + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; + + MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8, + 8, &retlen, (char *)&oob.u); /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ @@ -677,78 +496,37 @@ static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pe shouldn't actually lose data in this case. It's just that when we load up on a medium which has duplicate chains, we need to free one of the chains because it's not necessary any more. */ - - - thisEUN = thisNFTL->EUNtable[thisVUC]; + thisEUN = nftl->EUNtable[thisVUC]; + DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n"); - DEBUG(1,"Want to erase\n"); /* For each block in the old chain (except the targetEUN of course), free it and make it available for future use */ - - while( thisEUN <= thisNFTL->lastEUN && thisEUN != targetEUN) { - size_t retlen; - struct erase_info *instr; - u16 EUNtmp; - - instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); - if (!instr) { - printk(KERN_WARNING "Out of memory for struct erase_info\n"); - - EUNtmp = thisEUN; - - thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff; - thisNFTL->VirtualUnitTable[EUNtmp] = 0x7fff; - thisNFTL->ReplUnitTable[EUNtmp] = 0xffff; - } else { - memset(instr, 0, sizeof(struct erase_info)); - instr->addr = thisEUN * thisNFTL->EraseSize; - instr->len = thisNFTL->EraseSize; - - MTD_ERASE(thisNFTL->mtd, instr); - /* This is an async interface. Or will be. At which point - this code will break. */ - -#if 0 - MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob); - - printk("After erasing, EUN %d contains: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", - thisEUN, oob.b.ECCSig[0], - oob.b.ECCSig[1], - oob.b.ECCSig[2], - oob.b.ECCSig[3], - oob.b.ECCSig[4], - oob.b.ECCSig[5]); -#endif - memset(&oob, 0xff, sizeof(oob)); - oob.u.b.WearInfo = cpu_to_le32(3); - oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69); - - MTD_WRITEOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob); - - EUNtmp = thisEUN; - - thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff; - thisNFTL->VirtualUnitTable[EUNtmp] = 0xffff; - thisNFTL->ReplUnitTable[EUNtmp] = 0xffff; - - thisNFTL->numfreeEUNs++; - - } - - // shifted upwards: thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; - + while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) { + unsigned int EUNtmp; + + EUNtmp = nftl->ReplUnitTable[thisEUN]; + + if (NFTL_formatblock(nftl, thisEUN) < 0) { + /* could not erase : mark block as reserved + * FixMe: Update Bad Unit Table on disk + */ + nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; + } else { + /* correctly erased : mark it as free */ + nftl->ReplUnitTable[thisEUN] = BLOCK_FREE; + nftl->numfreeEUNs++; + } + thisEUN = EUNtmp; } /* Make this the new start of chain for thisVUC */ - thisNFTL->VirtualUnitTable[targetEUN] = thisVUC; - thisNFTL->ReplUnitTable[targetEUN] = 0xffff; + nftl->ReplUnitTable[targetEUN] = BLOCK_NIL; + nftl->EUNtable[thisVUC] = targetEUN; - thisNFTL->EUNtable[thisVUC] = targetEUN; return targetEUN; - } -u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock) +u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) { /* This is the part that needs some cleverness applied. For now, I'm doing the minimum applicable to actually @@ -757,23 +535,21 @@ u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock) and we also need to do some assessment of the results when the system loses power half-way through the routine. */ - u16 LongestChain = 0; u16 ChainLength = 0, thislen; u16 chain, EUN; - - for (chain=0; chain < thisNFTL->MediaHdr.FormattedSize / thisNFTL->EraseSize; chain++) { - EUN = thisNFTL->EUNtable[chain]; - + for (chain = 0; chain < nftl->MediaHdr.FormattedSize / nftl->EraseSize; chain++) { + EUN = nftl->EUNtable[chain]; thislen = 0; - while (EUN <= thisNFTL->lastEUN) { + while (EUN <= nftl->lastEUN) { thislen++; - // printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); - EUN = thisNFTL->ReplUnitTable[EUN] & 0x7fff; + //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); + EUN = nftl->ReplUnitTable[EUN] & 0x7fff; if (thislen > 0xff00) { - printk("Endless loop in Virtual Chain %d: Unit %x\n", chain, EUN); + printk("Endless loop in Virtual Chain %d: Unit %x\n", + chain, EUN); } if (thislen > 0xff10) { /* Actually, don't return failure. Just ignore this chain and @@ -781,42 +557,38 @@ u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock) thislen = 0; break; } - } - if (thislen > ChainLength) { - // printk("New longest chain is %d with length %d\n", chain, thislen); + //printk("New longest chain is %d with length %d\n", chain, thislen); ChainLength = thislen; LongestChain = chain; } - } + } if (ChainLength < 2) { - printk(KERN_WARNING "No Virtual Unit Chains available for folding. Failing request\n"); + printk(KERN_WARNING "No Virtual Unit Chains available for folding. " + "Failing request\n"); return 0xffff; } - - return NFTL_foldchain (thisNFTL, LongestChain, pendingblock); + + return NFTL_foldchain (nftl, LongestChain, pendingblock); } /* NFTL_findwriteunit: Return the unit number into which we can write for this block. Make it available if it isn't already */ - -static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block) +static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) { u16 lastEUN; - u16 thisVUC = block / (thisNFTL->EraseSize / 512); - u16 writeEUN; - unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + u16 thisVUC = block / (nftl->EraseSize / 512); + unsigned int writeEUN; + unsigned long blockofs = (block * 512) & (nftl->EraseSize -1); size_t retlen; - int silly = 0x10000, silly2 = 3; + int silly, silly2 = 3; struct nftl_oob oob; - int debug=0; do { - /* Scan the media to find a unit in the VUC which has a free space for the block in question. */ @@ -824,28 +596,30 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block /* This condition catches the 0x[7f]fff cases, as well as being a sanity check for past-end-of-media access */ - lastEUN = 0xffff; - writeEUN = thisNFTL->EUNtable[thisVUC]; - - while(writeEUN <= thisNFTL->lastEUN) { + lastEUN = BLOCK_NIL; + writeEUN = nftl->EUNtable[thisVUC]; + silly = MAX_LOOPS; + while (writeEUN <= nftl->lastEUN) { struct nftl_bci bci; size_t retlen; - + unsigned int status; + lastEUN = writeEUN; + + MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci); - MTD_READOOB(thisNFTL->mtd, (writeEUN * thisNFTL->EraseSize) - + blockofs,8, &retlen, (char *)&bci); - - if (debug) - printk("Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status)); + DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", + block , writeEUN, le16_to_cpu(bci.Status)); - switch(bci.Status) { - case __constant_cpu_to_le16(BLOCK_FREE): + status = bci.Status | bci.Status1; + switch(status) { + case SECTOR_FREE: return writeEUN; - case __constant_cpu_to_le16(BLOCK_DELETED): - case __constant_cpu_to_le16(BLOCK_USED): - case __constant_cpu_to_le16(BLOCK_IGNORE): + case SECTOR_DELETED: + case SECTOR_USED: + case SECTOR_IGNORE: break; default: // Invalid block. Don't use it any more. Must implement. @@ -853,35 +627,35 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block } if (!silly--) { - printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC); + printk(KERN_WARNING + "Infinite loop in Virtual Unit Chain 0x%x\n", + thisVUC); return 0xffff; } /* Skip to next block in chain */ - - writeEUN = thisNFTL->ReplUnitTable[writeEUN] & 0x7fff; + writeEUN = nftl->ReplUnitTable[writeEUN]; } /* OK. We didn't find one in the existing chain, or there is no existing chain. */ /* Try to find an already-free block */ + writeEUN = NFTL_findfreeblock(nftl, 0); - writeEUN = NFTL_findfreeblock(thisNFTL, 0); - - if (writeEUN == 0xffff) { + if (writeEUN == BLOCK_NIL) { /* That didn't work - there were no free blocks just waiting to be picked up. We're going to have to fold a chain to make room. */ /* First remember the start of this chain */ - // u16 startEUN = thisNFTL->EUNtable[thisVUC]; + //u16 startEUN = nftl->EUNtable[thisVUC]; //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); - writeEUN = NFTL_makefreeblock(thisNFTL, block); + writeEUN = NFTL_makefreeblock(nftl, 0xffff); - if (writeEUN == 0xffff) { + if (writeEUN == BLOCK_NIL) { /* Ouch. This should never happen - we should always be able to make some room somehow. If we get here, we've allocated more storage @@ -889,235 +663,223 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block routine is missing something. */ printk(KERN_WARNING "Cannot make free space.\n"); - return 0xffff; + return BLOCK_NIL; } - // printk("Restarting scan\n"); - lastEUN = 0xffff; - // debug = 1; + //printk("Restarting scan\n"); + lastEUN = BLOCK_NIL; continue; -#if 0 - if (startEUN != thisNFTL->EUNtable[thisVUC]) { - /* The fold operation has moved the chain - that we're looking at. Start the scan again. - */ - continue; - } -#endif } /* We've found a free block. Insert it into the chain. */ - if (lastEUN != 0xffff) { - /* Addition to an existing chain. Make the previous - last block in the chain point to this one. - */ - - //printk("Linking EUN %d to EUN %d in VUC %d\n", - // lastEUN, writeEUN, thisVUC); - /* Both in our cache... */ - thisNFTL->ReplUnitTable[lastEUN] = writeEUN; - - - /* ... and on the flash itself */ - MTD_READOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen, - (char *)&oob); + if (lastEUN != BLOCK_NIL) { + thisVUC |= 0x8000; /* It's a replacement block */ + } else { + /* The first block in a new chain */ + nftl->EUNtable[thisVUC] = writeEUN; + } - oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN); + /* set up the actual EUN we're writing into */ + /* Both in our cache... */ + nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; - MTD_WRITEOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen, - (char *)&oob); + /* ... and on the flash itself */ + MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); - thisVUC |= 0x8000; /* It's a replacement block */ - } else { - /* The first block in a new chain */ - thisNFTL->EUNtable[thisVUC] = writeEUN; - } + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); - /* Now set up the actual EUN we're writing into */ + MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + /* we link the new block to the chain only after the + block is ready. It avoids the case where the chain + could point to a free block */ + if (lastEUN != BLOCK_NIL) { /* Both in our cache... */ - thisNFTL->VirtualUnitTable[writeEUN] = thisVUC; - thisNFTL->ReplUnitTable[writeEUN] = 0xffff; - + nftl->ReplUnitTable[lastEUN] = writeEUN; /* ... and on the flash itself */ - MTD_READOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16, - &retlen, (char *)&oob); + MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); - oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum + = cpu_to_le16(writeEUN); - MTD_WRITEOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16, - &retlen, (char *)&oob); + MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); + } return writeEUN; } while (silly2--); - printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", thisVUC); + printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", + thisVUC); return 0xffff; } -static int NFTL_writeblock(struct NFTLrecord *thisNFTL, unsigned block, - char *buffer) +static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer) { u16 writeEUN; - unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); size_t retlen; - u16 eccbuf[8]; - - // if (thisEUN == 0xffff) thisEUN = 0; - - writeEUN = NFTL_findwriteunit(thisNFTL, block); + u8 eccbuf[6]; -// printk("writeblock(%d): Write to Unit %d\n", block, writeEUN); + writeEUN = NFTL_findwriteunit(nftl, block); - if (writeEUN == 0xffff) { - printk(KERN_WARNING "NFTL_writeblock(): Cannot find block to write to\n"); + if (writeEUN == BLOCK_NIL) { + printk(KERN_WARNING + "NFTL_writeblock(): Cannot find block to write to\n"); /* If we _still_ haven't got a block to use, we're screwed */ return 1; } -// printk("Writing block %lx to EUN %x\n",block, writeEUN); - - thisNFTL->mtd->write_ecc(thisNFTL->mtd, - (writeEUN * thisNFTL->EraseSize) + blockofs, - 512, &retlen, (char *)buffer, (char *)eccbuf); - eccbuf[3] = BLOCK_USED; - eccbuf[4] = eccbuf[5] = eccbuf[6] = eccbuf[7] = 0xffff; - - thisNFTL->mtd->write_oob(thisNFTL->mtd, - (writeEUN * thisNFTL->EraseSize) + blockofs, - 16, &retlen, (char *)eccbuf); + MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)eccbuf); + /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ return 0; } - #endif /* CONFIG_NFTL_RW */ -static int NFTL_readblock(struct NFTLrecord *thisNFTL, - unsigned block, char *buffer) +static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) { - u16 lastgoodEUN = 0xffff; - u16 thisEUN = thisNFTL->EUNtable[block / (thisNFTL->EraseSize / 512)]; - unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); - - int silly = -1; + u16 lastgoodEUN; + u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); + unsigned int status; + int silly = MAX_LOOPS; + size_t retlen; + struct nftl_bci bci; + + lastgoodEUN = BLOCK_NIL; + + if (thisEUN != BLOCK_NIL) { + while (thisEUN < nftl->nb_blocks) { + if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else + status = bci.Status | bci.Status1; + + switch (status) { + case SECTOR_FREE: + /* no modification of a sector should follow a free sector */ + goto the_end; + case SECTOR_DELETED: + lastgoodEUN = BLOCK_NIL; + break; + case SECTOR_USED: + lastgoodEUN = thisEUN; + break; + case SECTOR_IGNORE: + break; + default: + printk("Unknown status for block %d in EUN %d: %x\n", + block, thisEUN, status); + break; + } - if (thisEUN == 0xffff) thisEUN = 0; - - while(thisEUN && (thisEUN & 0x7fff) != 0x7fff) { - struct nftl_bci bci; - size_t retlen; - - MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + blockofs,8, &retlen, (char *)&bci); - - switch(bci.Status) { - case __constant_cpu_to_le16(BLOCK_FREE): - thisEUN = 0; - break; - case __constant_cpu_to_le16(BLOCK_USED): - lastgoodEUN = thisEUN; - break; - case __constant_cpu_to_le16(BLOCK_IGNORE): - case __constant_cpu_to_le16(BLOCK_DELETED): - break; - default: - printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, bci.Status); + if (!silly--) { + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", + block / (nftl->EraseSize / 512)); + return 1; + } + thisEUN = nftl->ReplUnitTable[thisEUN]; } + } - if (!silly--) { - printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",block / (thisNFTL->EraseSize / 512)); - return 1; - } - if (thisEUN) - thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; - } - if (lastgoodEUN == 0xffff) { + the_end: + if (lastgoodEUN == BLOCK_NIL) { + /* the requested block is not on the media, return all 0x00 */ memset(buffer, 0, 512); } else { - loff_t ptr = (lastgoodEUN * thisNFTL->EraseSize) + blockofs; + loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; size_t retlen; u_char eccbuf[6]; - thisNFTL->mtd->read_ecc(thisNFTL->mtd, ptr, 512, &retlen, buffer, eccbuf); + if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf)) + return -EIO; } return 0; } - -static int nftl_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { - struct NFTLrecord *thisNFTL; - - thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; + struct NFTLrecord *nftl; - if (!thisNFTL) return -EINVAL; + nftl = NFTLs[MINOR(inode->i_rdev) / 16]; + if (!nftl) return -EINVAL; switch (cmd) { case HDIO_GETGEO: { struct hd_geometry g; - g.heads = thisNFTL->heads; - g.sectors = thisNFTL->sectors; - g.cylinders = thisNFTL->cylinders; + g.heads = nftl->heads; + g.sectors = nftl->sectors; + g.cylinders = nftl->cylinders; g.start = part_table[MINOR(inode->i_rdev)].start_sect; return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; } case BLKGETSIZE: /* Return device size */ - if (!arg) return -EINVAL; + if (!arg) return -EINVAL; return put_user(part_table[MINOR(inode->i_rdev)].nr_sects, (long *) arg); case BLKFLSBUF: - if(!capable(CAP_SYS_ADMIN)) return -EACCES; + if (!capable(CAP_SYS_ADMIN)) return -EACCES; fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); - if (thisNFTL->mtd->sync) - thisNFTL->mtd->sync(thisNFTL->mtd); + if (nftl->mtd->sync) + nftl->mtd->sync(nftl->mtd); return 0; case BLKRRPART: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (thisNFTL->usecount > 1) { - // printk("Use count %d\n", thisNFTL->usecount); - return -EBUSY; - } + if (nftl->usecount > 1) return -EBUSY; #if LINUX_VERSION_CODE < 0x20328 - resetup_one_dev(&nftl_gendisk, MINOR(inode->i_dev) / 16); + resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) / 16); #else - grok_partitions(&nftl_gendisk, MINOR(inode->i_dev) / 16, 1<<4, thisNFTL->nr_sects); + grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) / 16, + 1<<4, nftl->nr_sects); #endif return 0; - - // RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */ + +#if (LINUX_VERSION_CODE < 0x20303) + RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */ +#else + case BLKROSET: + case BLKROGET: + case BLKSSZGET: + return blk_ioctl(inode->i_rdev, cmd, arg); +#endif + default: return -EINVAL; } } - void nftl_request(RQFUNC_ARG) { unsigned int dev, block, nsect; - struct NFTLrecord *thisNFTL; + struct NFTLrecord *nftl; char *buffer; struct request *req; int res; while (1) { INIT_REQUEST; /* blk.h */ - - req = CURRENT; -#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK - blkdev_dequeue_request(req); - spin_unlock_irq(&io_request_lock); -#else req = CURRENT; -#endif - DEBUG(2,"NFTL_request\n"); - DEBUG(3,"NFTL %d request, %lx, %lx", req->cmd, - req->sector, req->current_nr_sectors); + /* We can do this because the generic code knows not to + touch the request at the head of the queue */ + spin_unlock_irq(&io_request_lock); + + DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n"); + DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n", + (req->cmd == READ) ? "Read " : "Write", + req->sector, req->current_nr_sectors); + dev = MINOR(req->rq_dev); block = req->sector; nsect = req->current_nr_sectors; @@ -1125,21 +887,23 @@ void nftl_request(RQFUNC_ARG) res = 1; /* succeed */ if (dev >= MAX_NFTLS * 16) { - printk("fl: bad minor number: device=%s\n", + /* there is no such partition */ + printk("nftl: bad minor number: device = %s\n", kdevname(req->rq_dev)); res = 0; /* fail */ goto repeat; } - thisNFTL = NFTLs[dev / 16]; - DEBUG(3,"Waiting for mutex\n"); - down(&thisNFTL->mutex); - DEBUG(3,"Got mutex\n"); - - if (block + nsect >= part_table[dev].nr_sects) { - printk("nftl%c%d: bad access: block=%d, count=%d\n", + nftl = NFTLs[dev / 16]; + DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n"); + down(&nftl->mutex); + DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n"); + + if (block + nsect > part_table[dev].nr_sects) { + /* access past the end of device */ + printk("nftl%c%d: bad access: block = %d, count = %d\n", (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect); - up(&thisNFTL->mutex); + up(&nftl->mutex); res = 0; /* fail */ goto repeat; } @@ -1147,75 +911,80 @@ void nftl_request(RQFUNC_ARG) block += part_table[dev].start_sect; if (req->cmd == READ) { - DEBUG(2,"NFTL read\n"); - for ( ; nsect > 0; nsect-- , block++, buffer+= 512) { + DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x " + "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors); + + for ( ; nsect > 0; nsect-- , block++, buffer += 512) { /* Read a single sector to req->buffer + (512 * i) */ - - if (NFTL_readblock(thisNFTL, block, buffer)) { - DEBUG(2,"NFTL read request failed\n"); - up(&thisNFTL->mutex); + if (NFTL_readblock(nftl, block, buffer)) { + DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n"); + up(&nftl->mutex); res = 0; goto repeat; } } - DEBUG(2,"NFTL read request completed OK\n"); - up(&thisNFTL->mutex); + + DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n"); + up(&nftl->mutex); goto repeat; - } - else if (req->cmd == WRITE) { - DEBUG(2,"NFTL write request of 0x%x sectors @ %x (req->nr_sectors == %lx\n",nsect, block, req->nr_sectors); + } else if (req->cmd == WRITE) { + DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x " + "(req->nr_sectors == %lx)\n", nsect, block, + req->nr_sectors); #ifdef CONFIG_NFTL_RW - for ( ; nsect > 0; nsect-- , block++, buffer+= 512) { + for ( ; nsect > 0; nsect-- , block++, buffer += 512) { /* Read a single sector to req->buffer + (512 * i) */ - - if (NFTL_writeblock(thisNFTL, block, buffer)) { - DEBUG(1,"NFTL write request failed\n"); - - up(&thisNFTL->mutex); + if (NFTL_writeblock(nftl, block, buffer)) { + DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n"); + up(&nftl->mutex); res = 0; goto repeat; } } - DEBUG(2,"NFTL write request completed OK\n"); + DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n"); #else - res=0; /* Writes always fail */ + res = 0; /* Writes always fail */ #endif /* CONFIG_NFTL_RW */ - up(&thisNFTL->mutex); + up(&nftl->mutex); goto repeat; - } - else { - DEBUG(0,"NFTL ??? request\n"); - up(&thisNFTL->mutex); + } else { + DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n"); + up(&nftl->mutex); res = 0; goto repeat; } repeat: - DEBUG(3,"end_request(%d)\n", res); -#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK + DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res); spin_lock_irq(&io_request_lock); - nftl_end_request(req, res); -#else end_request(res); -#endif } } static int nftl_open(struct inode *ip, struct file *fp) { + int nftlnum = MINOR(ip->i_rdev) / 16; struct NFTLrecord *thisNFTL; - thisNFTL = NFTLs[MINOR(ip->i_rdev) / 16]; + thisNFTL = NFTLs[nftlnum]; - DEBUG(2,"NFTL_open\n"); + DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n"); +#ifdef CONFIG_KMOD + if (!thisNFTL && nftlnum == 0) { + request_module("docprobe"); + thisNFTL = NFTLs[nftlnum]; + } +#endif if (!thisNFTL) { - DEBUG(2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", - MINOR(ip->i_rdev) / 16,ip->i_rdev,ip, fp); + DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", + nftlnum, ip->i_rdev, ip, fp); return -ENODEV; } + #ifndef CONFIG_NFTL_RW if (fp->f_mode & FMODE_WRITE) - return -EROFS; + return -EROFS; #endif /* !CONFIG_NFTL_RW */ + thisNFTL->usecount++; MOD_INC_USE_COUNT; if (!get_mtd_device(thisNFTL->mtd, -1)) { @@ -1233,8 +1002,8 @@ static int nftl_release(struct inode *inode, struct file *fp) thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; - DEBUG(2, "NFTL_release\n"); - + DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); + fsync_dev(inode->i_rdev); if (sb) invalidate_inodes(sb); @@ -1251,19 +1020,19 @@ static int nftl_release(struct inode *inode, struct file *fp) } #if LINUX_VERSION_CODE < 0x20326 static struct file_operations nftl_fops = { - read: block_read, - write: block_write, - ioctl: nftl_ioctl, - open: nftl_open, - release: nftl_release, - fsync: block_fsync, + read: block_read, + write: block_write, + ioctl: nftl_ioctl, + open: nftl_open, + release: nftl_release, + fsync: block_fsync, }; #else static struct block_device_operations nftl_fops = { - open: nftl_open, - release: nftl_release, - ioctl: nftl_ioctl + open: nftl_open, + release: nftl_release, + ioctl: nftl_ioctl }; #endif @@ -1275,39 +1044,39 @@ static struct block_device_operations nftl_fops = * ****************************************************************************/ -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_nftl init_module #define cleanup_nftl cleanup_module #endif -#define __exit -#endif static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL}; - /* static int __init init_nftl(void) */ int __init init_nftl(void) { int i; - printk(KERN_NOTICE "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n"); + printk(KERN_NOTICE + "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n"); #ifdef PRERELEASE - printk(KERN_INFO"$Id: nftl.c,v 1.36 2000/07/13 14:14:20 dwmw2 Exp $\n"); + printk(KERN_INFO"$Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $\n"); #endif - if (register_blkdev(NFTL_MAJOR, "nftl", &nftl_fops)){ - printk("unable to register NFTL block device\n"); + if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){ + printk("unable to register NFTL block device on major %d\n", MAJOR_NR); + return -EBUSY; } else { #if LINUX_VERSION_CODE < 0x20320 - blk_dev[MAJOR_NR].request_fn = nftl_request; + blk_dev[MAJOR_NR].request_fn = nftl_request; #else - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); #endif - for (i=0; i < 256 ; i++) { + /* set block size to 1kB each */ + for (i = 0; i < 256; i++) { nftl_blocksizes[i] = 1024; } - blksize_size[NFTL_MAJOR] = nftl_blocksizes; + blksize_size[MAJOR_NR] = nftl_blocksizes; + nftl_gendisk.next = gendisk_head; gendisk_head = &nftl_gendisk; } @@ -1319,25 +1088,25 @@ int __init init_nftl(void) static void __exit cleanup_nftl(void) { - struct gendisk *gd, **gdp; + struct gendisk *gd, **gdp; - unregister_mtd_user(&nftl_notifier); - - unregister_blkdev(NFTL_MAJOR, "nftl"); + unregister_mtd_user(&nftl_notifier); + unregister_blkdev(MAJOR_NR, "nftl"); + #if LINUX_VERSION_CODE < 0x20320 - blk_dev[MAJOR_NR].request_fn = 0; + blk_dev[MAJOR_NR].request_fn = 0; #else - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); #endif - for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) - if (*gdp == &nftl_gendisk) { - gd = *gdp; *gdp = gd->next; - break; - } - + + /* remove ourself from generic harddisk list + FIXME: why can't I found this partition on /proc/partition */ + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == &nftl_gendisk) { + gd = *gdp; *gdp = gd->next; + break; + } } -#if LINUX_VERSION_CODE > 0x20300 module_init(init_nftl); module_exit(cleanup_nftl); -#endif diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c new file mode 100644 index 000000000000..d1f05930fd5a --- /dev/null +++ b/drivers/mtd/nftlmount.c @@ -0,0 +1,678 @@ +/* + * NFTL mount code with extensive checks + * + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * + * $Id: nftlmount.c,v 1.11 2000/11/17 12:24:09 ollie Exp $ + * + * 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECTORSIZE 512 + +/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the + * various device information of the NFTL partition and Bad Unit Table. Update + * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] + * is used for management of Erase Unit in other routines in nftl.c and nftlmount.c + */ +static int find_boot_record(struct NFTLrecord *nftl) +{ + struct nftl_uci1 h1; + struct nftl_oob oob; + unsigned int block, boot_record_count; + int retlen; + u8 buf[SECTORSIZE]; + struct NFTLMediaHeader *mh = &nftl->MediaHdr; + + nftl->MediaUnit = BLOCK_NIL; + nftl->SpareMediaUnit = BLOCK_NIL; + boot_record_count = 0; + + /* search for a valid boot record */ + for (block = 0; block < nftl->nb_blocks; block++) { + unsigned int erase_mark; + + /* read ANAND header. To be safer with BIOS, also use erase mark as discriminant */ + if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&h1) < 0) + continue; + + erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1)); + if (erase_mark != ERASE_MARK) + continue; + + if (MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, + &retlen, buf, (char *)&oob) < 0) + continue; + + memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); + if (memcmp(mh->DataOrgID, "ANAND", 6) == 0) { + /* first boot record */ + if (boot_record_count == 0) { + unsigned int i; + /* header found : read the bad block table data */ + if (mh->UnitSizeFactor != 0xff) { + printk("Sorry, we don't support UnitSizeFactor " + "of != 1 yet\n"); + goto ReplUnitTable; + } + + nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); + if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) + goto ReplUnitTable; /* small consistency check */ + + nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize; + if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) + goto ReplUnitTable; /* small consistency check */ + + /* FixMe: with bad blocks, the total size available is not FormattedSize any + more !!! */ + nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); + nftl->MediaUnit = block; + + /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ + for (i = 0; i < nftl->nb_blocks; i++) { + if ((i & (SECTORSIZE - 1)) == 0) { + /* read one sector for every SECTORSIZE of blocks */ + if (MTD_READECC(nftl->mtd, block * nftl->EraseSize + + i + SECTORSIZE, SECTORSIZE, + &retlen, buf, (char *)&oob) < 0) + goto ReplUnitTable; + } + /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ + if (buf[i & (SECTORSIZE - 1)] != 0xff) + nftl->ReplUnitTable[i] = BLOCK_RESERVED; + } + + boot_record_count++; + } else if (boot_record_count == 1) { + nftl->SpareMediaUnit = block; + boot_record_count++; + break; + } + } + ReplUnitTable: + } + + if (boot_record_count == 0) { + /* no boot record found */ + return -1; + } else { + return 0; + } +} + +static int memcmpb(void *a, int c, int n) +{ + int i; + for (i = 0; i < n; i++) { + if (c != ((unsigned char *)a)[i]) + return 1; + } + return 0; +} + +/* check_free_sector: check if a free sector is actually FREE, i.e. All 0xff in data and oob area */ +static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, + int check_oob) +{ + int i, retlen; + u8 buf[SECTORSIZE]; + + for (i = 0; i < len; i += SECTORSIZE) { + /* we want to read the sector without ECC check here since a free + sector does not have ECC syndrome on it yet */ + if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0) + return -1; + if (memcmpb(buf, 0xff, SECTORSIZE) != 0) + return -1; + + if (check_oob) { + if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize, + &retlen, buf) < 0) + return -1; + if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0) + return -1; + } + address += SECTORSIZE; + } + + return 0; +} + +/* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and + * Update NFTL metadata. Each erase operation is checked with check_free_sectors + * + * Return: 0 when succeed, -1 on error. + * + * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? + * 2. UnitSizeFactor != 0xFF + */ +int NFTL_formatblock(struct NFTLrecord *nftl, int block) +{ + int retlen; + unsigned int nb_erases, erase_mark; + struct nftl_uci1 uci; + struct erase_info *instr = &nftl->instr; + + /* Read the Unit Control Information #1 for Wear-Leveling */ + if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + goto default_uci1; + + erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1)); + if (erase_mark != ERASE_MARK) { + default_uci1: + uci.EraseMark = cpu_to_le16(ERASE_MARK); + uci.EraseMark1 = cpu_to_le16(ERASE_MARK); + uci.WearInfo = cpu_to_le32(0); + } + + memset(instr, 0, sizeof(struct erase_info)); + + /* XXX: use async erase interface, XXX: test return code */ + instr->addr = block * nftl->EraseSize; + instr->len = nftl->EraseSize; + MTD_ERASE(nftl->mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { + /* could not format, FixMe: We should update the BadUnitTable + both in memory and on disk */ + printk("Error while formatting block %d\n", block); + return -1; + } else { + /* increase and write Wear-Leveling info */ + nb_erases = le32_to_cpu(uci.WearInfo); + nb_erases++; + + /* wrap (almost impossible with current flashs) or free block */ + if (nb_erases == 0) + nb_erases = 1; + + /* check the "freeness" of Erase Unit before updating metadata + * FixMe: is this check really necessary ? since we have check the + * return code after the erase operation. */ + if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) + return -1; + + uci.WearInfo = le32_to_cpu(nb_erases); + if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&uci) < 0) + return -1; + return 0; + } +} + +/* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct. + * Mark as 'IGNORE' each incorrect sector. This check is only done if the chain + * was being folded when NFTL was interrupted. + * + * The check_free_sectors in this function is neceressary. There is a possible + * situation that after writing the Data area, the Block Control Information is + * not updated according (due to power failure or something) which leaves the block + * in an umconsistent state. So we have to check if a block is really FREE in this + * case. */ +static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block) +{ + unsigned int block, i, status; + struct nftl_bci bci; + int sectors_per_block, retlen; + + sectors_per_block = nftl->EraseSize / SECTORSIZE; + block = first_block; + for (;;) { + for (i = 0; i < sectors_per_block; i++) { + if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else + status = bci.Status | bci.Status1; + + switch(status) { + case SECTOR_FREE: + /* verify that the sector is really free. If not, mark + as ignore */ + if (memcmpb(&bci, 0xff, 8) != 0 || + check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE, + SECTORSIZE, 0) != 0) { + printk("Incorrect free sector %d in block %d: " + "marking it as ignored\n", + i, block); + + /* sector not free actually : mark it as SECTOR_IGNORE */ + bci.Status = SECTOR_IGNORE; + bci.Status1 = SECTOR_IGNORE; + MTD_WRITEOOB(nftl->mtd, + block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci); + } + break; + default: + break; + } + } + + /* proceed to next Erase Unit on the chain */ + block = nftl->ReplUnitTable[block]; + if (!(block == BLOCK_NIL || block < nftl->nb_blocks)) + printk("incorrect ReplUnitTable[] : %d\n", block); + if (block == BLOCK_NIL || block >= nftl->nb_blocks) + break; + } +} + +/* calc_chain_lenght: Walk through a Virtual Unit Chain and estimate chain length */ +static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block) +{ + unsigned int length = 0, block = first_block; + + for (;;) { + length++; + /* avoid infinite loops, although this is guaranted not to + happen because of the previous checks */ + if (length >= nftl->nb_blocks) { + printk("nftl: length too long %d !\n", length); + break; + } + + block = nftl->ReplUnitTable[block]; + if (!(block == BLOCK_NIL || block < nftl->nb_blocks)) + printk("incorrect ReplUnitTable[] : %d\n", block); + if (block == BLOCK_NIL || block >= nftl->nb_blocks) + break; + } + return length; +} + +/* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a + * Virtual Unit Chain, i.e. all the units are disconnected. + * + * It is not stricly correct to begin from the first block of the chain because + * if we stop the code, we may see again a valid chain if there was a first_block + * flag in a block inside it. But is it really a problem ? + * + * FixMe: Figure out what the last statesment means. What if power failure when we are + * in the for (;;) loop formatting blocks ?? + */ +static void format_chain(struct NFTLrecord *nftl, unsigned int first_block) +{ + unsigned int block = first_block, block1; + + printk("Formatting chain at block %d\n", first_block); + + for (;;) { + block1 = nftl->ReplUnitTable[block]; + + printk("Formatting block %d\n", block); + if (NFTL_formatblock(nftl, block) < 0) { + /* cannot format !!!! Mark it as Bad Unit, + FixMe: update the BadUnitTable on disk */ + nftl->ReplUnitTable[block] = BLOCK_RESERVED; + } else { + nftl->ReplUnitTable[block] = BLOCK_FREE; + } + + /* goto next block on the chain */ + block = block1; + + if (!(block == BLOCK_NIL || block < nftl->nb_blocks)) + printk("incorrect ReplUnitTable[] : %d\n", block); + if (block == BLOCK_NIL || block >= nftl->nb_blocks) + break; + } +} + +/* check_and_mark_free_block: Verify that a block is free in the NFTL sense (valid erase mark) or + * totally free (only 0xff). + * + * Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the + * following critia: + * 1. */ +static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) +{ + struct nftl_uci1 h1; + unsigned int erase_mark; + int i, retlen; + unsigned char buf[SECTORSIZE]; + + /* check erase mark. */ + if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + + erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1)); + if (erase_mark != ERASE_MARK) { + /* if no erase mark, the block must be totally free. This is + possible in two cases : empty filsystem or interrupted erase (very unlikely) */ + if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0) + return -1; + + /* free block : write erase mark */ + h1.EraseMark = cpu_to_le16(ERASE_MARK); + h1.EraseMark1 = cpu_to_le16(ERASE_MARK); + h1.WearInfo = cpu_to_le32(0); + if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + } else { +#if 0 + /* if erase mark present, need to skip it when doing check */ + for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) { + /* check free sector */ + if (check_free_sectors (nftl, block * nftl->EraseSize + i, + SECTORSIZE, 0) != 0) + return -1; + + if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i, + 16, &retlen, buf) < 0) + return -1; + if (i == SECTORSIZE) { + /* skip erase mark */ + if (memcmpb(buf, 0xff, 8)) + return -1; + } else { + if (memcmpb(buf, 0xff, 16)) + return -1; + } + } +#endif + } + + return 0; +} + +/* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS + * to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2 + * is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted + * for some reason. A clean up/check of the VUC is neceressary in this case. + * + * WARNING: return 0 if read error + */ +static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block) +{ + struct nftl_uci2 uci; + int retlen; + + if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + return 0; + + return le16_to_cpu((uci.FoldMark | uci.FoldMark1)); +} + +int NFTL_mount(struct NFTLrecord *s) +{ + int i; + unsigned int first_logical_block, logical_block, rep_block, nb_erases, erase_mark; + unsigned int block, first_block, is_first_block; + int chain_length, do_format_chain; + struct nftl_uci0 h0; + struct nftl_uci1 h1; + int retlen; + + /* XXX: will be suppressed */ + s->lastEUN = s->nb_blocks - 1; + + /* memory alloc */ + s->EUNtable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL); + s->ReplUnitTable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL); + if (!s->EUNtable || !s->ReplUnitTable) { + fail: + if (s->EUNtable) + kfree(s->EUNtable); + if (s->ReplUnitTable) + kfree(s->ReplUnitTable); + return -1; + } + + /* mark all blocks as potentially containing data */ + for (i = 0; i < s->nb_blocks; i++) { + s->ReplUnitTable[i] = BLOCK_NOTEXPLORED; + } + + /* search for NFTL MediaHeader and Spare NFTL Media Header */ + if (find_boot_record(s) < 0) { + printk("Could not find valid boot record\n"); + goto fail; + } + + /* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */ + for (i = 0; i < s->nb_boot_blocks; i++) + s->ReplUnitTable[i] = BLOCK_RESERVED; + + /* also mark the boot records (NFTL MediaHeader) blocks as reserved */ + if (s->MediaUnit != BLOCK_NIL) + s->ReplUnitTable[s->MediaUnit] = BLOCK_RESERVED; + if (s->SpareMediaUnit != BLOCK_NIL) + s->ReplUnitTable[s->SpareMediaUnit] = BLOCK_RESERVED; + + /* init the logical to physical table */ + for (i = 0; i < s->nb_blocks; i++) { + s->EUNtable[i] = BLOCK_NIL; + } + + /* first pass : explore each block chain */ + first_logical_block = 0; + for (first_block = 0; first_block < s->nb_blocks; first_block++) { + /* if the block was not already explored, we can look at it */ + if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) { + block = first_block; + chain_length = 0; + do_format_chain = 0; + + for (;;) { + /* read the block header. If error, we format the chain */ + if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8, + &retlen, (char *)&h0) < 0 || + MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) { + s->ReplUnitTable[block] = BLOCK_NIL; + do_format_chain = 1; + break; + } + + logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum)); + rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum)); + nb_erases = le32_to_cpu (h1.WearInfo); + erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1)); + + is_first_block = !(logical_block >> 15); + logical_block = logical_block & 0x7fff; + + /* invalid/free block test */ + if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) { + if (chain_length == 0) { + /* if not currently in a chain, we can handle it safely */ + if (check_and_mark_free_block(s, block) < 0) { + /* not really free: format it */ + printk("Formatting block %d\n", block); + if (NFTL_formatblock(s, block) < 0) { + /* could not format: reserve the block */ + s->ReplUnitTable[block] = BLOCK_RESERVED; + } else { + s->ReplUnitTable[block] = BLOCK_FREE; + } + } else { + /* free block: mark it */ + s->ReplUnitTable[block] = BLOCK_FREE; + } + /* directly examine the next block. */ + goto examine_ReplUnitTable; + } else { + /* the block was in a chain : this is bad. We + must format all the chain */ + printk("Block %d: free but referenced in chain %d\n", + block, first_block); + s->ReplUnitTable[block] = BLOCK_NIL; + do_format_chain = 1; + break; + } + } + + /* we accept only first blocks here */ + if (chain_length == 0) { + /* this block is not the first block in chain : + ignore it, it will be included in a chain + later, or marked as not explored */ + if (!is_first_block) + goto examine_ReplUnitTable; + first_logical_block = logical_block; + } else { + if (logical_block != first_logical_block) { + printk("Block %d: incorrect logical block: %d expected: %d\n", + block, logical_block, first_logical_block); + /* the chain is incorrect : we must format it, + but we need to read it completly */ + do_format_chain = 1; + } + if (is_first_block) { + /* we accept that a block is marked as first + block while being last block in a chain + only if the chain is being folded */ + if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS || + rep_block != 0xffff) { + printk("Block %d: incorrectly marked as first block in chain\n", + block); + /* the chain is incorrect : we must format it, + but we need to read it completly */ + do_format_chain = 1; + } else { + printk("Block %d: folding in progress - ignoring first block flag\n", + block); + } + } + } + chain_length++; + if (rep_block == 0xffff) { + /* no more blocks after */ + s->ReplUnitTable[block] = BLOCK_NIL; + break; + } else if (rep_block >= s->nb_blocks) { + printk("Block %d: referencing invalid block %d\n", + block, rep_block); + do_format_chain = 1; + s->ReplUnitTable[block] = BLOCK_NIL; + break; + } else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) { + /* same problem as previous 'is_first_block' test: + we accept that the last block of a chain has + the first_block flag set if folding is in + progress. We handle here the case where the + last block appeared first */ + if (s->ReplUnitTable[rep_block] == BLOCK_NIL && + s->EUNtable[first_logical_block] == rep_block && + get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) { + /* EUNtable[] will be set after */ + printk("Block %d: folding in progress - ignoring first block flag\n", + rep_block); + s->ReplUnitTable[block] = rep_block; + s->EUNtable[first_logical_block] = BLOCK_NIL; + } else { + printk("Block %d: referencing block %d already in another chain\n", + block, rep_block); + /* XXX: should handle correctly fold in progress chains */ + do_format_chain = 1; + s->ReplUnitTable[block] = BLOCK_NIL; + } + break; + } else { + /* this is OK */ + s->ReplUnitTable[block] = rep_block; + block = rep_block; + } + } + + /* the chain was completely explored. Now we can decide + what to do with it */ + if (do_format_chain) { + /* invalid chain : format it */ + format_chain(s, first_block); + } else { + unsigned int first_block1, chain_to_format, chain_length1; + int fold_mark; + + /* valid chain : get foldmark */ + fold_mark = get_fold_mark(s, first_block); + if (fold_mark == 0) { + /* cannot get foldmark : format the chain */ + printk("Could read foldmark at block %d\n", first_block); + format_chain(s, first_block); + } else { + if (fold_mark == FOLD_MARK_IN_PROGRESS) + check_sectors_in_chain(s, first_block); + + /* now handle the case where we find two chains at the + same virtual address : we select the longer one, + because the shorter one is the one which was being + folded if the folding was not done in place */ + first_block1 = s->EUNtable[first_logical_block]; + if (first_block1 != BLOCK_NIL) { + /* XXX: what to do if same length ? */ + chain_length1 = calc_chain_length(s, first_block1); + printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n", + first_block1, chain_length1, first_block, chain_length); + + if (chain_length >= chain_length1) { + chain_to_format = first_block1; + s->EUNtable[first_logical_block] = first_block; + } else { + chain_to_format = first_block; + } + format_chain(s, chain_to_format); + } else { + s->EUNtable[first_logical_block] = first_block; + } + } + } + } + examine_ReplUnitTable: + } + + /* second pass to format unreferenced blocks and init free block count */ + s->numfreeEUNs = 0; + s->LastFreeEUN = BLOCK_NIL; + + for (block = 0; block < s->nb_blocks; block++) { + if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) { + printk("Unreferenced block %d, formatting it\n", block); + if (NFTL_formatblock(s, block) < 0) + s->ReplUnitTable[block] = BLOCK_RESERVED; + else + s->ReplUnitTable[block] = BLOCK_FREE; + } + if (s->ReplUnitTable[block] == BLOCK_FREE) { + s->numfreeEUNs++; + s->LastFreeEUN = block; + } + } + + return 0; +} diff --git a/drivers/mtd/nora.c b/drivers/mtd/nora.c index 9304d7e6230a..48da299fc880 100644 --- a/drivers/mtd/nora.c +++ b/drivers/mtd/nora.c @@ -1,5 +1,5 @@ /* - * $Id: nora.c,v 1.12 2000/07/13 10:32:33 dwmw2 Exp $ + * $Id: nora.c,v 1.17 2000/12/03 19:32:21 dwmw2 Exp $ * * This is so simple I love it. */ @@ -58,19 +58,17 @@ void nora_copy_to(struct map_info *map, unsigned long to, const void *from, ssiz } struct map_info nora_map = { - "NORA", - WINDOW_SIZE, - 2, - nora_read8, - nora_read16, - nora_read32, - nora_copy_from, - nora_write8, - nora_write16, - nora_write32, - nora_copy_to, - 0, - 0 + name: "NORA", + size: WINDOW_SIZE, + buswidth: 2, + read8: nora_read8, + read16: nora_read16, + read32: nora_read32, + copy_from: nora_copy_from, + write8: nora_write8, + write16: nora_write16, + write32: nora_write32, + copy_to: nora_copy_to }; @@ -140,7 +138,7 @@ static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */ { type: MTD_NORFLASH, flags: MTD_CAP_NORFLASH, - size: 0xf00000, + size: 0x900000, erasesize: 0x20000, name: "NORA root filesystem", module: THIS_MODULE, @@ -155,9 +153,9 @@ static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */ { type: MTD_NORFLASH, flags: MTD_CAP_NORFLASH, - size: 0x1000000, + size: 0x1600000, erasesize: 0x20000, - name: "NORA main filesystem", + name: "NORA second filesystem", module: THIS_MODULE, erase: nora_mtd_erase, read: nora_mtd_read, @@ -165,16 +163,15 @@ static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */ suspend: nora_mtd_suspend, resume: nora_mtd_resume, sync: nora_mtd_sync, - priv: (void *)0x1000000 + priv: (void *)0xa00000 } }; -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_nora init_module #define cleanup_nora cleanup_module #endif -#endif int __init init_nora(void) { @@ -186,10 +183,10 @@ int __init init_nora(void) mymtd->module = &__this_module; #endif - add_mtd_device(&nora_mtds[3]); + add_mtd_device(&nora_mtds[2]); add_mtd_device(&nora_mtds[0]); add_mtd_device(&nora_mtds[1]); - add_mtd_device(&nora_mtds[2]); + add_mtd_device(&nora_mtds[3]); return 0; } @@ -199,10 +196,13 @@ int __init init_nora(void) static void __exit cleanup_nora(void) { if (mymtd) { - del_mtd_device(&nora_mtds[2]); + del_mtd_device(&nora_mtds[3]); del_mtd_device(&nora_mtds[1]); del_mtd_device(&nora_mtds[0]); - del_mtd_device(&nora_mtds[3]); + del_mtd_device(&nora_mtds[2]); map_destroy(mymtd); } } + +module_init(init_nora); +module_exit(cleanup_nora); diff --git a/drivers/mtd/octagon-5066.c b/drivers/mtd/octagon-5066.c index b184cd0e7cc8..70e78d7e9c03 100644 --- a/drivers/mtd/octagon-5066.c +++ b/drivers/mtd/octagon-5066.c @@ -1,4 +1,4 @@ -// $Id: octagon-5066.c,v 1.10 2000/07/13 14:04:23 dwmw2 Exp $ +// $Id: octagon-5066.c,v 1.12 2000/11/27 08:50:22 dwmw2 Exp $ /* ###################################################################### Octagon 5066 MTD Driver. @@ -151,32 +151,32 @@ static void oct5066_copy_to(struct map_info *map, unsigned long to, const void * static struct map_info oct5066_map[2] = { { - "Octagon 5066 Socket", - 512 * 1024, - 1, - oct5066_read8, - oct5066_read16, - oct5066_read32, - oct5066_copy_from, - oct5066_write8, - oct5066_write16, - oct5066_write32, - oct5066_copy_to, - 1<<6 + name: "Octagon 5066 Socket", + size: 512 * 1024, + buswidth: 1, + read8: oct5066_read8, + read16: oct5066_read16, + read32: oct5066_read32, + copy_from: oct5066_copy_from, + write8: oct5066_write8, + write16: oct5066_write16, + write32: oct5066_write32, + copy_to: oct5066_copy_to, + map_priv_1: 1<<6 }, { - "Octagon 5066 Internal Flash", - 2 * 1024 * 1024, - 1, - oct5066_read8, - oct5066_read16, - oct5066_read32, - oct5066_copy_from, - oct5066_write8, - oct5066_write16, - oct5066_write32, - oct5066_copy_to, - 2<<6 + name: "Octagon 5066 Internal Flash", + size: 2 * 1024 * 1024, + buswidth: 1, + read8: oct5066_read8, + read16: oct5066_read16, + read32: oct5066_read32, + copy_from: oct5066_copy_from, + write8: oct5066_write8, + write16: oct5066_write16, + write32: oct5066_write32, + copy_to: oct5066_copy_to, + map_priv_1: 2<<6 } }; @@ -213,13 +213,10 @@ static int __init OctProbe() return 0; } -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_oct5066 init_module #define cleanup_oct5066 cleanup_module #endif -#define __exit -#endif void cleanup_oct5066(void) { @@ -284,7 +281,5 @@ int __init init_oct5066(void) return 0; } -#if LINUX_VERSION_CODE > 0x20300 module_init(init_oct5066); module_exit(cleanup_oct5066); -#endif diff --git a/drivers/mtd/physmap.c b/drivers/mtd/physmap.c index 658b8bf4776a..31ac39310fe2 100644 --- a/drivers/mtd/physmap.c +++ b/drivers/mtd/physmap.c @@ -1,5 +1,5 @@ /* - * $Id: physmap.c,v 1.2 2000/07/11 09:42:32 dwmw2 Exp $ + * $Id: physmap.c,v 1.8 2000/11/27 08:50:22 dwmw2 Exp $ * * Normal mappings of chips in physical memory */ @@ -15,6 +15,7 @@ #define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START #define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN +#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH static struct mtd_info *mymtd; @@ -59,32 +60,28 @@ void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, s } struct map_info physmap_map = { - "Physically mapped flash", - WINDOW_SIZE, - 2, - physmap_read8, - physmap_read16, - physmap_read32, - physmap_copy_from, - physmap_write8, - physmap_write16, - physmap_write32, - physmap_copy_to, - 0, - 0 + name: "Physically mapped flash", + size: WINDOW_SIZE, + buswidth: BUSWIDTH, + read8: physmap_read8, + read16: physmap_read16, + read32: physmap_read32, + copy_from: physmap_copy_from, + write8: physmap_write8, + write16: physmap_write16, + write32: physmap_write32, + copy_to: physmap_copy_to }; -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_physmap init_module #define cleanup_physmap cleanup_module #endif -#endif int __init init_physmap(void) { printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_SIZE, WINDOW_ADDR); + physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); if (!physmap_map.map_priv_1) { printk("Failed to ioremap\n"); @@ -99,6 +96,7 @@ int __init init_physmap(void) return 0; } + iounmap((void *)physmap_map.map_priv_1); return -ENXIO; } @@ -113,3 +111,7 @@ static void __exit cleanup_physmap(void) physmap_map.map_priv_1 = 0; } } + +module_init(init_physmap); +module_exit(cleanup_physmap); + diff --git a/drivers/mtd/pnc2000.c b/drivers/mtd/pnc2000.c index 4e4b052a76b9..a93dd354870e 100644 --- a/drivers/mtd/pnc2000.c +++ b/drivers/mtd/pnc2000.c @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: pnc2000.c,v 1.1 2000/07/12 09:34:32 dwmw2 Exp $ + * $Id: pnc2000.c,v 1.4 2000/11/27 08:50:22 dwmw2 Exp $ */ #include @@ -14,6 +14,7 @@ #include #include +#include #define WINDOW_ADDR 0xbf000000 @@ -64,136 +65,60 @@ void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize } struct map_info pnc_map = { - "PNC-2000", - WINDOW_SIZE, - 4, - pnc_read8, - pnc_read16, - pnc_read32, - pnc_copy_from, - pnc_write8, - pnc_write16, - pnc_write32, - pnc_copy_to, - 0, - 0 + name: "PNC-2000", + size: WINDOW_SIZE, + buswidth: 4, + read8: pnc_read8, + read16: pnc_read16, + read32: pnc_read32, + copy_from: pnc_copy_from, + write8: pnc_write8, + write16: pnc_write16, + write32: pnc_write32, + copy_to: pnc_copy_to }; /* * MTD 'PARTITIONING' STUFF */ - -/* - * This is the _real_ MTD device for which all the others are just - * auto-relocating aliases. - */ -static struct mtd_info *mymtd; - -/* - * MTD methods which simply translate the effective address and pass through - * to the _real_ device. - */ - -static int pnc_mtd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - return mymtd->read(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf); -} - -static int pnc_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) -{ - return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf); -} - -static int pnc_mtd_erase (struct mtd_info *mtd, struct erase_info *instr) -{ - instr->addr += (unsigned long)mtd->priv; - return mymtd->erase(mymtd, instr); -} - -static void pnc_mtd_sync (struct mtd_info *mtd) -{ - mymtd->sync(mymtd); -} - -static int pnc_mtd_suspend (struct mtd_info *mtd) -{ - return mymtd->suspend(mymtd); -} - -static void pnc_mtd_resume (struct mtd_info *mtd) -{ - mymtd->resume(mymtd); -} - - -static struct mtd_info pnc_mtds[3] = { /* boot, kernel, fs */ +static struct mtd_partition pnc_partitions[3] = { { - type: MTD_NORFLASH, - flags: MTD_CAP_NORFLASH, - size: 0x20000, - erasesize: 0x20000, name: "PNC-2000 boot firmware", - module: THIS_MODULE, - erase: pnc_mtd_erase, - read: pnc_mtd_read, - write: pnc_mtd_write, - suspend: pnc_mtd_suspend, - resume: pnc_mtd_resume, - sync: pnc_mtd_sync, - priv: (void *)0 + size: 0x20000, + offset: 0 }, { - type: MTD_NORFLASH, - flags: MTD_CAP_NORFLASH, - size: 0x1a0000, - erasesize: 0x20000, name: "PNC-2000 kernel", - module: THIS_MODULE, - erase: pnc_mtd_erase, - read: pnc_mtd_read, - write: pnc_mtd_write, - suspend: pnc_mtd_suspend, - resume: pnc_mtd_resume, - sync: pnc_mtd_sync, - priv: (void *)0x20000 + size: 0x1a0000, + offset: 0x20000 }, { - type: MTD_NORFLASH, - flags: MTD_CAP_NORFLASH, - size: 0x240000, - erasesize: 0x20000, name: "PNC-2000 filesystem", - module: THIS_MODULE, - erase: pnc_mtd_erase, - read: pnc_mtd_read, - write: pnc_mtd_write, - suspend: pnc_mtd_suspend, - resume: pnc_mtd_resume, - sync: pnc_mtd_sync, - priv: (void *)0x1c0000 + size: 0x240000, + offset: 0x1c0000 } }; -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +/* + * This is the master MTD device for which all the others are just + * auto-relocating aliases. + */ +static struct mtd_info *mymtd; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_pnc init_module #define cleanup_pnc cleanup_module #endif -#endif int __init init_pnc(void) { - printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); mymtd = do_cfi_probe(&pnc_map); if (mymtd) { mymtd->module = THIS_MODULE; - - add_mtd_device(&pnc_mtds[0]); /* boot */ - add_mtd_device(&pnc_mtds[1]); /* kernel */ - add_mtd_device(&pnc_mtds[2]); /* file system */ - return 0; + return add_mtd_partitions(mymtd, pnc_partitions, 3); } return -ENXIO; @@ -202,9 +127,10 @@ int __init init_pnc(void) static void __exit cleanup_pnc(void) { if (mymtd) { - del_mtd_device(&pnc_mtds[2]); - del_mtd_device(&pnc_mtds[1]); - del_mtd_device(&pnc_mtds[0]); + del_mtd_partitions(mymtd); map_destroy(mymtd); } } + +module_init(init_pnc); +module_exit(cleanup_pnc); diff --git a/drivers/mtd/rpxlite.c b/drivers/mtd/rpxlite.c index 783c863acf2e..51bcaf8a9e19 100644 --- a/drivers/mtd/rpxlite.c +++ b/drivers/mtd/rpxlite.c @@ -1,7 +1,7 @@ /* - * $Id: rpxlite.c,v 1.2 2000/07/04 12:16:26 dwmw2 Exp $ + * $Id: rpxlite.c,v 1.8 2000/12/09 22:00:31 dwmw2 Exp $ * - * Handle the strange 16-in-32-bit mapping on the RPXLite board + * Handle mapping of the flash on the RPX Lite and CLLF boards */ #include @@ -12,124 +12,74 @@ #include -#define WINDOW_ADDR 0x8000000 -#define WINDOW_SIZE 0x2000000 - -#define MAP_TO_ADR(x) ( ( ( x & ~1 ) << 1 ) | (x&1) ) +#define WINDOW_ADDR 0xfe000000 +#define WINDOW_SIZE 0x800000 static struct mtd_info *mymtd; __u8 rpxlite_read8(struct map_info *map, unsigned long ofs) { - return readb(map->map_priv_1 + MAP_TO_ADR(ofs)); + return readb(map->map_priv_1 * ofs); } __u16 rpxlite_read16(struct map_info *map, unsigned long ofs) { - return readw(map->map_priv_1 + MAP_TO_ADR(ofs)); + return readw(map->map_priv_1 + ofs); } __u32 rpxlite_read32(struct map_info *map, unsigned long ofs) { - return readl(map->map_priv_1 + MAP_TO_ADR(ofs)); + return readl(map->map_priv_1 + ofs); } void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { - if (from & 1) { - *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from)); - from++; - len--; - } - /* Can't do this if it's not aligned */ - if (!((unsigned long)to & 1)) { - unsigned long fromadr = MAP_TO_ADR(from); - - while (len > 1) { - *(__u16 *)to = readw(map->map_priv_1 + fromadr); - to += 2; - fromadr += 4; - from += 2; - len -= 2; - } - } - while(len) { - *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from)); - to++; - from++; - len--; - } + memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); } void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr) { - writeb(d, map->map_priv_1 + MAP_TO_ADR(adr)); + writeb(d, map->map_priv_1 + adr); } void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr) { - writew(d, map->map_priv_1 + MAP_TO_ADR(adr)); + writew(d, map->map_priv_1 + adr); } void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr) { - writel(d, map->map_priv_1 + MAP_TO_ADR(adr)); + writel(d, map->map_priv_1 + adr); } void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { - if (to & 1) { - writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to)); - from++; - len--; - } - /* Can't do this if it's not aligned */ - if (!((unsigned long)from & 1)) { - unsigned long toadr = map->map_priv_1 + MAP_TO_ADR(to); - - while (len > 1) { - writew(*(__u16 *)from, toadr); - from += 2; - toadr += 4; - to += 2; - len -= 2; - } - } - while(len) { - writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to)); - to++; - from++; - len--; - } + memcpy_toio((void *)(map->map_priv_1 + to), from, len); } struct map_info rpxlite_map = { - "RPXLITE", - WINDOW_SIZE, - 2, - rpxlite_read8, - rpxlite_read16, - rpxlite_read32, - rpxlite_copy_from, - rpxlite_write8, - rpxlite_write16, - rpxlite_write32, - rpxlite_copy_to, - 0, - 0 + name: "RPX", + size: WINDOW_SIZE, + buswidth: 4, + read8: rpxlite_read8, + read16: rpxlite_read16, + read32: rpxlite_read32, + copy_from: rpxlite_copy_from, + write8: rpxlite_write8, + write16: rpxlite_write16, + write32: rpxlite_write32, + copy_to: rpxlite_copy_to }; -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_rpxlite init_module #define cleanup_rpxlite cleanup_module #endif -#endif int __init init_rpxlite(void) { - printk(KERN_NOTICE "rpxlite flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 2); + printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); + rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); if (!rpxlite_map.map_priv_1) { printk("Failed to ioremap\n"); @@ -158,3 +108,6 @@ static void __exit cleanup_rpxlite(void) rpxlite_map.map_priv_1 = 0; } } + +module_init(init_rpxlite); +module_exit(cleanup_rpxlite); diff --git a/drivers/mtd/vmax301.c b/drivers/mtd/vmax301.c index 553beaad61fa..e694604e7afe 100644 --- a/drivers/mtd/vmax301.c +++ b/drivers/mtd/vmax301.c @@ -1,4 +1,4 @@ -// $Id: vmax301.c,v 1.13 2000/07/03 10:01:38 dwmw2 Exp $ +// $Id: vmax301.c,v 1.15 2000/11/27 08:50:22 dwmw2 Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. @@ -142,46 +142,43 @@ static void vmax301_copy_to(struct map_info *map, unsigned long to, const void * static struct map_info vmax_map[2] = { { - "VMAX301 Internal Flash", - 3*2*1024*1024, - 1, - vmax301_read8, - vmax301_read16, - vmax301_read32, - vmax301_copy_from, - vmax301_write8, - vmax301_write16, - vmax301_write32, - vmax301_copy_to, - WINDOW_START + WINDOW_LENGTH, - 0xFFFFFFFF + name: "VMAX301 Internal Flash", + size: 3*2*1024*1024, + buswidth: 1, + read8: vmax301_read8, + read16: vmax301_read16, + read32: vmax301_read32, + copy_from: vmax301_copy_from, + write8: vmax301_write8, + write16: vmax301_write16, + write32: vmax301_write32, + copy_to: vmax301_copy_to, + map_priv_1: WINDOW_START + WINDOW_LENGTH, + map_priv_2: 0xFFFFFFFF }, { - "VMAX301 Socket", - 0, - 1, - vmax301_read8, - vmax301_read16, - vmax301_read32, - vmax301_copy_from, - vmax301_write8, - vmax301_write16, - vmax301_write32, - vmax301_copy_to, - WINDOW_START + (3*WINDOW_LENGTH), - 0xFFFFFFFF + name: "VMAX301 Socket", + size: 0, + buswidth: 1, + read8: vmax301_read8, + read16: vmax301_read16, + read32: vmax301_read32, + copy_from: vmax301_copy_from, + write8: vmax301_write8, + write16: vmax301_write16, + write32: vmax301_write32, + copy_to: vmax301_copy_to, + map_priv_1: WINDOW_START + (3*WINDOW_LENGTH), + map_priv_2: 0xFFFFFFFF } }; static struct mtd_info *vmax_mtd[2] = {NULL, NULL}; -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_vmax301 init_module #define cleanup_vmax301 cleanup_module #endif -#define __exit -#endif static void __exit cleanup_vmax301(void) { @@ -237,7 +234,5 @@ int __init init_vmax301(void) return 0; } -#if LINUX_VERSION_CODE > 0x20300 module_init(init_vmax301); module_exit(cleanup_vmax301); -#endif diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index f73af08284fa..19bf1ef0fa70 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -2223,7 +2223,7 @@ static int ace_open(struct net_device *dev) /* * Setup the bottom half rx ring refill handler */ - ap->immediate.next = NULL; + INIT_LIST_HEAD(&ap->immediate.list); ap->immediate.sync = 0; ap->immediate.routine = (void *)(void *)ace_bh; ap->immediate.data = dev; diff --git a/drivers/net/aironet4500_core.c b/drivers/net/aironet4500_core.c index 7d5e67b5c643..5a06600f0dc7 100644 --- a/drivers/net/aironet4500_core.c +++ b/drivers/net/aironet4500_core.c @@ -2868,7 +2868,7 @@ int awc_private_init(struct net_device * dev){ priv->command_semaphore_on = 0; priv->unlock_command_postponed = 0; - priv->immediate_bh.next = NULL; + INIT_LIST_HEAD(&priv->immediate_bh.list); priv->immediate_bh.sync = 0; priv->immediate_bh.routine = (void *)(void *)awc_bh; priv->immediate_bh.data = dev; diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c index c8cab206293e..a70ab61a38c3 100644 --- a/drivers/net/eth16i.c +++ b/drivers/net/eth16i.c @@ -440,8 +440,10 @@ int __init eth16i_probe(struct net_device *dev) { int i; int ioaddr; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + SET_MODULE_OWNER(dev); + if(eth16i_debug > 4) printk(KERN_DEBUG "Probing started for %s\n", cardname); @@ -979,8 +981,6 @@ static int eth16i_open(struct net_device *dev) outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); netif_start_queue(dev); - MOD_INC_USE_COUNT; - return 0; } @@ -1007,8 +1007,6 @@ static int eth16i_close(struct net_device *dev) outb(0x00, ioaddr + CONFIG_REG_1); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/ethertap.c b/drivers/net/ethertap.c index a4cfce6aebeb..5863998cb8e5 100644 --- a/drivers/net/ethertap.c +++ b/drivers/net/ethertap.c @@ -67,6 +67,8 @@ struct net_local int __init ethertap_probe(struct net_device *dev) { + SET_MODULE_OWNER(dev); + memcpy(dev->dev_addr, "\xFE\xFD\x00\x00\x00\x00", 6); if (dev->mem_start & 0xf) ethertap_debug = dev->mem_start & 0x7; @@ -116,13 +118,9 @@ static int ethertap_open(struct net_device *dev) if (ethertap_debug > 2) printk("%s: Doing ethertap_open()...", dev->name); - MOD_INC_USE_COUNT; - lp->nl = netlink_kernel_create(dev->base_addr, ethertap_rx); - if (lp->nl == NULL) { - MOD_DEC_USE_COUNT; + if (lp->nl == NULL) return -ENOBUFS; - } netif_start_queue(dev); return 0; } @@ -324,7 +322,6 @@ static int ethertap_close(struct net_device *dev) sock_release(sk->socket); } - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c index 60cef746f634..b10fe94904b5 100644 --- a/drivers/net/ewrk3.c +++ b/drivers/net/ewrk3.c @@ -350,6 +350,8 @@ int __init ewrk3_probe(struct net_device *dev) int tmp = num_ewrk3s, status = -ENODEV; u_long iobase = dev->base_addr; + SET_MODULE_OWNER(dev); + if ((iobase == 0) && loading_module) { printk("Autoprobing is not supported when loading a module based driver.\n"); status = -EIO; @@ -681,8 +683,6 @@ static int ewrk3_open(struct net_device *dev) return -EINVAL; } - MOD_INC_USE_COUNT; - return status; } @@ -1132,8 +1132,6 @@ static int ewrk3_close(struct net_device *dev) if (!lp->hard_strapped) { free_irq(dev->irq, dev); } - MOD_DEC_USE_COUNT; - return 0; } @@ -1860,15 +1858,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } #ifdef MODULE -static char devicename[9] = -{0,}; -static struct net_device thisEthwrk = -{ - devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0x300, 5, /* I/O address, IRQ */ - 0, 0, 0, NULL, ewrk3_probe}; - +static struct net_device thisEthwrk; static int io = 0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ static int irq = 5; /* or use the insmod io= irq= options */ @@ -1879,6 +1869,7 @@ int init_module(void) { thisEthwrk.base_addr = io; thisEthwrk.irq = irq; + thisEthwrk.init = ewrk3_probe; if (register_netdev(&thisEthwrk) != 0) return -EIO; return 0; diff --git a/drivers/net/fmv18x.c b/drivers/net/fmv18x.c index 9c1423965719..14265574d8cc 100644 --- a/drivers/net/fmv18x.c +++ b/drivers/net/fmv18x.c @@ -134,6 +134,8 @@ int __init fmv18x_probe(struct net_device *dev) int i; int base_addr = dev->base_addr; + SET_MODULE_OWNER(dev); + if (base_addr > 0x1ff) /* Check a single specified location. */ return fmv18x_probe1(dev, base_addr); else if (base_addr != 0) /* Don't probe at all. */ @@ -319,8 +321,6 @@ static int net_open(struct net_device *dev) /* Enable both Tx and Rx interrupts */ outw(0x8182, ioaddr+TX_INTR); - MOD_INC_USE_COUNT; - return 0; } @@ -569,8 +569,6 @@ static int net_close(struct net_device *dev) /* Power-down the chip. Green, green, green! */ outb(0x00, ioaddr + CONFIG_1); - MOD_DEC_USE_COUNT; - /* Set the ethernet adaptor disable IRQ */ outb(0x00, ioaddr + FJ_CONFIG1); diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index dda42edd84bb..d4b8e8e37a44 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -580,6 +580,7 @@ static int __init hamachi_init_one (struct pci_dev *pdev, iounmap((char *)ioaddr); return -ENOMEM; } + SET_MODULE_OWNER(dev); #ifdef TX_CHECKSUM printk("check that skbcopy in ip_queue_xmit isn't happening\n"); @@ -787,12 +788,9 @@ static int hamachi_open(struct net_device *dev) u_int32_t rx_int_var, tx_int_var; u_int16_t fifo_info; - MOD_INC_USE_COUNT; - - if (request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev)) { - MOD_DEC_USE_COUNT; - return -EAGAIN; - } + i = request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev); + if (i) + return i; if (hamachi_debug > 1) printk(KERN_DEBUG "%s: hamachi_open() irq %d.\n", @@ -1754,8 +1752,6 @@ static int hamachi_close(struct net_device *dev) writeb(0x00, ioaddr + LEDCtrl); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 263ae384e083..0e41265fe96f 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -1023,7 +1023,8 @@ static int epp_open(struct net_device *dev) struct baycom_state *bc; struct parport *pp; const struct tq_struct run_bh = { - 0, 0, (void *)(void *)epp_bh, dev + routine: (void *)(void *)epp_bh, + data: dev }; unsigned int i, j; unsigned char tmp[128]; diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 9ccc0d1b4d2c..3ec7b1c1d9f2 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -287,7 +287,7 @@ void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s) /* ---------------------------------------------------------------------- */ -static void inline do_kiss_params(struct hdlcdrv_state *s, +static inline void do_kiss_params(struct hdlcdrv_state *s, unsigned char *data, unsigned long len) { @@ -889,14 +889,7 @@ EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv); /* --------------------------------------------------------------------- */ -#ifdef MODULE - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); - -/* --------------------------------------------------------------------- */ - -int __init init_module(void) +static int __init hdlcdrv_init_driver(void) { printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n"); printk(KERN_INFO "hdlcdrv: version 0.8 compiled " __TIME__ " " __DATE__ "\n"); @@ -905,10 +898,16 @@ int __init init_module(void) /* --------------------------------------------------------------------- */ -void cleanup_module(void) +static void __exit hdlcdrv_cleanup_driver(void) { printk(KERN_INFO "hdlcdrv: cleanup\n"); } -#endif /* MODULE */ +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); +module_init(hdlcdrv_init_driver); +module_exit(hdlcdrv_cleanup_driver); + /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c index 68fb88261a44..9dbade4275b2 100644 --- a/drivers/net/hp-plus.c +++ b/drivers/net/hp-plus.c @@ -120,7 +120,9 @@ static void hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hd int __init hp_plus_probe(struct net_device *dev) { int i; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); if (base_addr > 0x1ff) /* Check a single specified location. */ return hpp_probe1(dev, base_addr); @@ -270,7 +272,6 @@ hpp_open(struct net_device *dev) outw(Perf_Page, ioaddr + HP_PAGING); ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -285,7 +286,6 @@ hpp_close(struct net_device *dev) outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset, ioaddr + HPP_OPTION); - MOD_DEC_USE_COUNT; return 0; } @@ -402,17 +402,9 @@ hpp_mem_block_output(struct net_device *dev, int count, #ifdef MODULE #define MAX_HPP_CARDS 4 /* Max number of HPP cards per module */ -static struct net_device dev_hpp[MAX_HPP_CARDS] = { - { - "", - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, NULL - }, -}; - -static int io[MAX_HPP_CARDS] = { 0, }; -static int irq[MAX_HPP_CARDS] = { 0, }; +static struct net_device dev_hpp[MAX_HPP_CARDS]; +static int io[MAX_HPP_CARDS]; +static int irq[MAX_HPP_CARDS]; MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i"); diff --git a/drivers/net/hp.c b/drivers/net/hp.c index 26b92b8c62c7..60b723bfadee 100644 --- a/drivers/net/hp.c +++ b/drivers/net/hp.c @@ -57,7 +57,7 @@ static unsigned int hppclan_portlist[] __initdata = #define HP_16BSTOP_PG 0xFF /* Same, for 16 bit cards. */ int hp_probe(struct net_device *dev); -int hp_probe1(struct net_device *dev, int ioaddr); +static int hp_probe1(struct net_device *dev, int ioaddr); static int hp_open(struct net_device *dev); static int hp_close(struct net_device *dev); @@ -83,7 +83,9 @@ static char irqmap[16] __initdata= { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0} int __init hp_probe(struct net_device *dev) { int i; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); if (base_addr > 0x1ff) /* Check a single specified location. */ return hp_probe1(dev, base_addr); @@ -97,14 +99,14 @@ int __init hp_probe(struct net_device *dev) return -ENODEV; } -int __init hp_probe1(struct net_device *dev, int ioaddr) +static int __init hp_probe1(struct net_device *dev, int ioaddr) { int i, retval, board_id, wordmode; const char *name; static unsigned version_printed; if (!request_region(ioaddr, HP_IO_EXTENT, dev->name)) - return -ENODEV; + return -EBUSY; /* Check for the HP physical address, 08 00 09 xx xx xx. */ /* This really isn't good enough: we may pick up HP LANCE boards @@ -206,7 +208,6 @@ static int hp_open(struct net_device *dev) { ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -214,7 +215,6 @@ static int hp_close(struct net_device *dev) { ei_close(dev); - MOD_DEC_USE_COUNT; return 0; } @@ -373,17 +373,9 @@ hp_init_card(struct net_device *dev) #ifdef MODULE #define MAX_HP_CARDS 4 /* Max number of HP cards per module */ -static struct net_device dev_hp[MAX_HP_CARDS] = { - { - "", - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, NULL - }, -}; - -static int io[MAX_HP_CARDS] = { 0, }; -static int irq[MAX_HP_CARDS] = { 0, }; +static struct net_device dev_hp[MAX_HP_CARDS]; +static int io[MAX_HP_CARDS]; +static int irq[MAX_HP_CARDS]; MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HP_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HP_CARDS) "i"); diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 74938f954f8c..6a052e543fd5 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -274,6 +274,17 @@ static struct hp100_pci_id hp100_pci_ids[] = { #define HP100_PCI_IDS_SIZE (sizeof(hp100_pci_ids)/sizeof(struct hp100_pci_id)) +#if LINUX_VERSION_CODE >= 0x20400 +static struct pci_device_id hp100_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_COMPEX2, PCI_DEVICE_ID_COMPEX2_100VG, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, hp100_pci_tbl); +#endif /* LINUX_VERSION_CODE >= 0x20400 */ + static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX; static int hp100_mode = 1; @@ -761,6 +772,7 @@ static int __init hp100_probe1( struct net_device *dev, int ioaddr, u_char bus, /* Reset statistics (counters) */ hp100_clear_stats( lp, ioaddr ); + SET_MODULE_OWNER(dev); ether_setup( dev ); /* If busmaster mode is wanted, a dma-capable memory area is needed for @@ -1154,8 +1166,6 @@ static int hp100_open( struct net_device *dev ) return -EAGAIN; } - MOD_INC_USE_COUNT; - dev->trans_start = jiffies; netif_start_queue(dev); @@ -1201,7 +1211,6 @@ static int hp100_close( struct net_device *dev ) printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); #endif - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c index 8f05bb5a0426..5f2052feb539 100644 --- a/drivers/net/ibmlana.c +++ b/drivers/net/ibmlana.c @@ -87,9 +87,7 @@ History: #include #include -#ifdef MODULE #include -#endif #include #include @@ -877,9 +875,6 @@ static int ibmlana_open(struct IBMLANA_NETDEV *dev) dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; -#endif - -#ifdef MODULE MOD_INC_USE_COUNT; #endif @@ -897,7 +892,7 @@ static int ibmlana_close(struct IBMLANA_NETDEV *dev) free_irq(dev->irq, dev); dev->irq = 0; -#ifdef MODULE +#if (LINUX_VERSION_CODE < 0x02032a) MOD_DEC_USE_COUNT; #endif @@ -1043,6 +1038,10 @@ int ibmlana_probe(struct IBMLANA_NETDEV *dev) ibmlana_priv *priv; ibmlana_medium medium; +#if (LINUX_VERSION_CODE >= 0x02032a) + SET_MODULE_OWNER(dev); +#endif + /* can't work without an MCA bus ;-) */ if (MCA_bus == 0) @@ -1192,14 +1191,11 @@ int ibmlana_probe(struct IBMLANA_NETDEV *dev) #define DEVMAX 5 -static struct IBMLANA_NETDEV moddevs[DEVMAX] = { - { init: ibmlana_probe }, { init: ibmlana_probe }, - { init: ibmlana_probe }, { init: ibmlana_probe }, - { init: ibmlana_probe } -}; - -int irq = 0; -int io = 0; +static struct IBMLANA_NETDEV moddevs[DEVMAX]; +static int irq; +static int io; +MODULE_PARM(irq, "i"); +MODULE_PARM(io, "i"); int init_module(void) { @@ -1207,7 +1203,9 @@ int init_module(void) startslot = 0; for (z = 0; z < DEVMAX; z++) { - strcpy(moddevs[z].name, " "); + moddevs[z].init = ibmlana_probe; + moddevs[z].irq = irq; + moddevs[z].base_addr = io; res = register_netdev(moddevs + z); if (res != 0) return (z > 0) ? 0 : -EIO; diff --git a/drivers/net/isa-skeleton.c b/drivers/net/isa-skeleton.c index c39defcd35f5..99093299c4af 100644 --- a/drivers/net/isa-skeleton.c +++ b/drivers/net/isa-skeleton.c @@ -121,8 +121,8 @@ static void net_tx_timeout(struct net_device *dev); /* Example routines you must write ;->. */ #define tx_done(dev) 1 -extern void hardware_send_packet(short ioaddr, char *buf, int length); -extern void chipset_init(struct net_device *dev, int startp); +static void hardware_send_packet(short ioaddr, char *buf, int length); +static void chipset_init(struct net_device *dev, int startp); /* * Check for a network adaptor of this type, and return '0' iff one exists. @@ -135,7 +135,9 @@ int __init netcard_probe(struct net_device *dev) { int i; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); if (base_addr > 0x1ff) /* Check a single specified location. */ return netcard_probe1(dev, base_addr); @@ -360,8 +362,6 @@ net_open(struct net_device *dev) */ netif_start_queue(dev); - MOD_INC_USE_COUNT; - return 0; } @@ -574,8 +574,6 @@ net_close(struct net_device *dev) /* Update the statistics here. */ - MOD_DEC_USE_COUNT; - return 0; } @@ -589,11 +587,8 @@ static struct net_device_stats *net_get_stats(struct net_device *dev) struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; - cli(); /* Update the statistics from the device registers. */ lp->stats.rx_missed_errors = inw(ioaddr+1); - sti(); - return &lp->stats; } @@ -633,7 +628,7 @@ set_multicast_list(struct net_device *dev) #ifdef MODULE -static struct net_device this_device = { init: netcard_probe }; +static struct net_device this_device; static int io = 0x300; static int irq; static int dma; @@ -652,6 +647,7 @@ int init_module(void) this_device.irq = irq; this_device.dma = dma; this_device.mem_start = mem; + this_device.init = netcard_probe; if ((result = register_netdev(&this_device)) != 0) return result; diff --git a/drivers/net/lne390.c b/drivers/net/lne390.c index d524b5ef607d..59ce4f9b7b54 100644 --- a/drivers/net/lne390.c +++ b/drivers/net/lne390.c @@ -50,7 +50,7 @@ static const char *version = #include "8390.h" int lne390_probe(struct net_device *dev); -int lne390_probe1(struct net_device *dev, int ioaddr); +static int lne390_probe1(struct net_device *dev, int ioaddr); static int lne390_open(struct net_device *dev); static int lne390_close(struct net_device *dev); @@ -108,8 +108,10 @@ int __init lne390_probe(struct net_device *dev) unsigned short ioaddr = dev->base_addr; int ret; + SET_MODULE_OWNER(dev); + if (ioaddr > 0x1ff) { /* Check a single specified location. */ - if (!request_region(ioaddr, LNE390_IO_EXTENT, "lne390")) + if (!request_region(ioaddr, LNE390_IO_EXTENT, dev->name)) return -EBUSY; ret = lne390_probe1(dev, ioaddr); if (ret) @@ -128,7 +130,7 @@ int __init lne390_probe(struct net_device *dev) /* EISA spec allows for up to 16 slots, but 8 is typical. */ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { - if (!request_region(ioaddr, LNE390_IO_EXTENT, "lne390")) + if (!request_region(ioaddr, LNE390_IO_EXTENT, dev->name)) continue; if (lne390_probe1(dev, ioaddr) == 0) return 0; @@ -138,7 +140,7 @@ int __init lne390_probe(struct net_device *dev) return -ENODEV; } -int __init lne390_probe1(struct net_device *dev, int ioaddr) +static int __init lne390_probe1(struct net_device *dev, int ioaddr) { int i, revision, ret; unsigned long eisa_id; @@ -195,11 +197,11 @@ int __init lne390_probe1(struct net_device *dev, int ioaddr) } printk(" IRQ %d,", dev->irq); - if (request_irq(dev->irq, ei_interrupt, 0, "lne390", dev)) { + if ((ret = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) { printk (" unable to get IRQ %d.\n", dev->irq); kfree(dev->priv); dev->priv = NULL; - return -EAGAIN; + return ret; } if (dev->mem_start == 0) { @@ -356,7 +358,6 @@ static void lne390_block_output(struct net_device *dev, int count, static int lne390_open(struct net_device *dev) { ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -367,21 +368,12 @@ static int lne390_close(struct net_device *dev) printk("%s: Shutting down ethercard.\n", dev->name); ei_close(dev); - MOD_DEC_USE_COUNT; return 0; } #ifdef MODULE #define MAX_LNE_CARDS 4 /* Max number of LNE390 cards per module */ -static struct net_device dev_lne[MAX_LNE_CARDS] = { - { - "", - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, NULL - }, -}; - +static struct net_device dev_lne[MAX_LNE_CARDS]; static int io[MAX_LNE_CARDS]; static int irq[MAX_LNE_CARDS]; static int mem[MAX_LNE_CARDS]; diff --git a/drivers/net/mvme147.c b/drivers/net/mvme147.c index fd6744284a12..1d2d4c47b3f0 100644 --- a/drivers/net/mvme147.c +++ b/drivers/net/mvme147.c @@ -79,9 +79,11 @@ int __init mvme147lance_probe(struct net_device *dev) u_long address; if (!MACH_IS_MVME147 || called) - return(ENODEV); + return(-ENODEV); called++; + SET_MODULE_OWNER(dev); + dev->priv = kmalloc(sizeof(struct m147lance_private), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; @@ -173,7 +175,6 @@ static int m147lance_open(struct net_device *dev) m147_pcc->lan_cntrl=0; /* clear the interrupts (if any) */ m147_pcc->lan_cntrl=0x08 | 0x04; /* Enable irq 4 */ - MOD_INC_USE_COUNT; return 0; } @@ -182,7 +183,6 @@ static int m147lance_close(struct net_device *dev) /* disable interrupts at boardlevel */ m147_pcc->lan_cntrl=0x0; /* disable interrupts */ lance_close(dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 254b42880b40..34d050f712f7 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -386,6 +386,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, dev = init_etherdev(NULL, sizeof (struct netdev_private)); if (!dev) return -ENOMEM; + SET_MODULE_OWNER(dev); { void *mmio; @@ -559,12 +560,8 @@ static int netdev_open(struct net_device *dev) /* Do we need to reset the chip??? */ - MOD_INC_USE_COUNT; - - if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { - MOD_DEC_USE_COUNT; - return -EAGAIN; - } + i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + if (i) return i; if (debug > 1) printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", @@ -1179,8 +1176,6 @@ static int netdev_close(struct net_device *dev) writel(0x0200, ioaddr + ChipConfig); /* Power down Xcvr. */ #endif - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 976dd578120a..70004107f45c 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -483,7 +483,7 @@ pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent) printk(KERN_INFO "pcnet32_probe_pci: found device %#08x.%#08x\n", ent->vendor, ent->device); ioaddr = pci_resource_start (pdev, 0); - printk(KERN_INFO " ioaddr=%#08lx resource_flags=%#08lx\n", ioaddr, pci_resource_flags (pdev, 0)); + printk(KERN_INFO " ioaddr=%#08lx resource_flags=%#08lx\n", ioaddr, pci_resource_flags (pdev, 0)); if (!ioaddr) { printk (KERN_ERR "no PCI IO resources, aborting\n"); return -ENODEV; @@ -627,29 +627,29 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car /* There is a 16 byte station address PROM at the base address. The first six bytes are the station address. */ for (i = 0; i < 6; i++) - printk( KERN_INFO " %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */ i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ - printk(KERN_INFO"\n tx_start_pt(0x%04x):",i); + printk("\n" KERN_INFO " tx_start_pt(0x%04x):",i); switch(i>>10) { - case 0: printk(KERN_INFO " 20 bytes,"); break; - case 1: printk(KERN_INFO " 64 bytes,"); break; - case 2: printk(KERN_INFO " 128 bytes,"); break; - case 3: printk(KERN_INFO "~220 bytes,"); break; + case 0: printk(" 20 bytes,"); break; + case 1: printk(" 64 bytes,"); break; + case 2: printk(" 128 bytes,"); break; + case 3: printk("~220 bytes,"); break; } i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ - printk(KERN_INFO" BCR18(%x):",i&0xffff); - if (i & (1<<5)) printk(KERN_INFO "BurstWrEn "); - if (i & (1<<6)) printk(KERN_INFO "BurstRdEn "); - if (i & (1<<7)) printk(KERN_INFO "DWordIO "); - if (i & (1<<11)) printk(KERN_INFO"NoUFlow "); + printk(" BCR18(%x):",i&0xffff); + if (i & (1<<5)) printk("BurstWrEn "); + if (i & (1<<6)) printk("BurstRdEn "); + if (i & (1<<7)) printk("DWordIO "); + if (i & (1<<11)) printk("NoUFlow "); i = a->read_bcr(ioaddr, 25); - printk(KERN_INFO "\n SRAMSIZE=0x%04x,",i<<8); + printk("\n" KERN_INFO " SRAMSIZE=0x%04x,",i<<8); i = a->read_bcr(ioaddr, 26); - printk(KERN_INFO " SRAM_BND=0x%04x,",i<<8); + printk(" SRAM_BND=0x%04x,",i<<8); i = a->read_bcr(ioaddr, 27); - if (i & (1<<14)) printk(KERN_INFO "LowLatRx,"); + if (i & (1<<14)) printk("LowLatRx"); } dev->base_addr = ioaddr; @@ -662,7 +662,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car memset(lp, 0, sizeof(*lp)); lp->dma_addr = lp_dma_addr; lp->pci_dev = pdev; - printk(KERN_INFO "pcnet32: pcnet32_private lp=%p lp_dma_addr=%#08x\n", lp, lp_dma_addr); + printk("\n" KERN_INFO "pcnet32: pcnet32_private lp=%p lp_dma_addr=%#08x", lp, lp_dma_addr); spin_lock_init(&lp->lock); @@ -713,7 +713,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car } if (dev->irq >= 2) - printk(KERN_INFO " assigned IRQ %d.\n", dev->irq); + printk(" assigned IRQ %d.\n", dev->irq); else { unsigned long irq_mask = probe_irq_on(); @@ -728,9 +728,9 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car dev->irq = probe_irq_off (irq_mask); if (dev->irq) - printk(KERN_INFO ", probed IRQ %d.\n", dev->irq); + printk(", probed IRQ %d.\n", dev->irq); else { - printk(KERN_ERR ", failed to detect IRQ line.\n"); + printk(", failed to detect IRQ line.\n"); return -ENODEV; } } @@ -978,14 +978,14 @@ pcnet32_tx_timeout (struct net_device *dev) lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", lp->cur_rx); for (i = 0 ; i < RX_RING_SIZE; i++) - printk(KERN_DEBUG "%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", + printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status); for (i = 0 ; i < TX_RING_SIZE; i++) - printk(KERN_DEBUG "%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", + printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", lp->tx_ring[i].base, -lp->tx_ring[i].length, lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status); - printk(KERN_DEBUG "\n"); + printk("\n"); } pcnet32_restart(dev, 0x0042); diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 43c0e7bcdb4e..61cdd84d60d7 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -349,18 +349,18 @@ plip_init_dev(struct net_device *dev, struct parport *pb) nl->nibble = PLIP_NIBBLE_WAIT; /* Initialize task queue structures */ - nl->immediate.next = NULL; + INIT_LIST_HEAD(&nl->immediate.list); nl->immediate.sync = 0; nl->immediate.routine = (void (*)(void *))plip_bh; nl->immediate.data = dev; - nl->deferred.next = NULL; + INIT_LIST_HEAD(&nl->deferred.list); nl->deferred.sync = 0; nl->deferred.routine = (void (*)(void *))plip_kick_bh; nl->deferred.data = dev; if (dev->irq == -1) { - nl->timer.next = NULL; + INIT_LIST_HEAD(&nl->timer.list); nl->timer.sync = 0; nl->timer.routine = (void (*)(void *))plip_timer_bh; nl->timer.data = dev; diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index feb72365193f..8744f6186457 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -785,8 +785,7 @@ int pppoe_sendmsg(struct socket *sock, struct msghdr *m, skb_reserve(skb, dev->hard_header_len); skb->nh.raw = skb->data; - skb->rx_dev = skb->dev = dev; - dev_hold(skb->rx_dev); + skb->dev = dev; skb->priority = sk->priority; skb->protocol = __constant_htons(ETH_P_PPP_SES); @@ -869,11 +868,7 @@ int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) skb->nh.raw = skb->data; - /* Change device of skb, update reference counts */ - if(skb->rx_dev) - dev_put(skb->rx_dev); - skb->rx_dev = skb->dev = dev; - dev_hold(skb->rx_dev); + skb->dev = dev; dev->hard_header(skb, dev, ETH_P_PPP_SES, sk->protinfo.pppox->pppoe_pa.remote, diff --git a/drivers/net/rclanmtl.c b/drivers/net/rclanmtl.c index 2721cb05e9e2..1a5f13672b75 100644 --- a/drivers/net/rclanmtl.c +++ b/drivers/net/rclanmtl.c @@ -1561,7 +1561,7 @@ RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction) { unsigned long off; - unsigned long *pMsg; + PU32 pMsg; PPAB pPab; int i; long timeout = 0; diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index 793af1577f8b..95a7c864e51f 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -210,6 +210,9 @@ sb1000_probe(struct net_device *dev) dev->rmem_end, serial_number, dev->irq); dev = init_etherdev(dev, 0); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); /* Make up a SB1000-specific-data structure. */ dev->priv = kmalloc(sizeof(struct sb1000_private), GFP_KERNEL); @@ -1004,7 +1007,6 @@ sb1000_open(struct net_device *dev) netif_start_queue(dev); - MOD_INC_USE_COUNT; return 0; /* Always succeed */ } @@ -1195,7 +1197,6 @@ static int sb1000_close(struct net_device *dev) dev_kfree_skb(lp->rx_skb[i]); } } - MOD_DEC_USE_COUNT; return 0; } @@ -1205,14 +1206,9 @@ MODULE_DESCRIPTION("General Instruments SB1000 driver"); MODULE_PARM(io, "1-2i"); MODULE_PARM(irq, "i"); -static struct net_device dev_sb1000 = { - "", - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, sb1000_probe }; - -static int io[2] = {0, 0}; -static int irq = 0; +static struct net_device dev_sb1000; +static int io[2]; +static int irq; int init_module(void) @@ -1226,6 +1222,7 @@ init_module(void) printk(KERN_ERR "sb1000: can't register any device cm\n"); return -ENFILE; } + dev_sb1000.init = sb1000_probe; dev_sb1000.base_addr = io[0]; /* rmem_end holds the second I/O address - fv */ dev_sb1000.rmem_end = io[1]; diff --git a/drivers/net/setup.c b/drivers/net/setup.c index a8c9e5e46ff5..823836cbbee8 100644 --- a/drivers/net/setup.c +++ b/drivers/net/setup.c @@ -31,9 +31,7 @@ extern int sdla_c_setup(void); extern int comx_init(void); extern int lmc_setup(void); -extern int abyss_probe(void); extern int madgemc_probe(void); -extern int tms_pci_probe(void); /* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is string of 9 zeros. */ #define __PAD6 "\0\0\0\0\0\0\0\0\0" @@ -115,16 +113,9 @@ struct net_probe pci_probes[] __initdata = { /* * Token Ring Drivers */ -#ifdef CONFIG_ABYSS - {abyss_probe, 0}, -#endif #ifdef CONFIG_MADGEMC {madgemc_probe, 0}, #endif -#ifdef CONFIG_TMSPCI - {tms_pci_probe, 0}, -#endif - {NULL, 0}, }; diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index cc0eae2140ee..72949ff74fa2 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -241,6 +241,7 @@ static int __devinit sis900_probe (struct pci_dev *pci_dev, const struct pci_dev net_dev = init_etherdev(NULL, sizeof(struct sis900_private)); if (!net_dev) return -ENOMEM; + SET_MODULE_OWNER(net_dev); if (!request_region(ioaddr, SIS900_TOTAL_SIZE, net_dev->name)) { printk(KERN_ERR "sis900.c: can't allocate I/O space at 0x%lX\n", ioaddr); @@ -530,8 +531,7 @@ sis900_open(struct net_device *net_dev) struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; u8 revision; - - MOD_INC_USE_COUNT; + int ret; /* Soft reset the chip. */ sis900_reset(net_dev); @@ -541,10 +541,9 @@ sis900_open(struct net_device *net_dev) if (revision == SIS630E_REV || revision == SIS630EA1_REV) sis630e_set_eq(net_dev); - if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) { - MOD_DEC_USE_COUNT; - return -EAGAIN; - } + ret = request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev); + if (ret) + return ret; sis900_init_rxfilter(net_dev); @@ -1279,8 +1278,6 @@ sis900_close(struct net_device *net_dev) /* Green! Put the chip in low-power mode. */ - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c index 51688271ac6e..49823291a751 100644 --- a/drivers/net/sk_mca.c +++ b/drivers/net/sk_mca.c @@ -879,9 +879,6 @@ static int skmca_open(struct SKMCA_NETDEV *dev) dev->interrupt = 0; dev->tbusy = 0; dev->start = 0; -#endif - -#ifdef MODULE MOD_INC_USE_COUNT; #endif @@ -900,7 +897,7 @@ static int skmca_close(struct SKMCA_NETDEV *dev) free_irq(dev->irq, dev); dev->irq = 0; -#ifdef MODULE +#if (LINUX_VERSION_CODE < 0x02032a) MOD_DEC_USE_COUNT; #endif @@ -1087,6 +1084,8 @@ int skmca_probe(struct SKMCA_NETDEV *dev) if (MCA_bus == 0) return -ENODEV; + SET_MODULE_OWNER(dev); + /* start address of 1 --> forced detection */ if (dev->mem_start == 1) diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c index af2050833a54..2b5fb3fc410d 100644 --- a/drivers/net/smc-mca.c +++ b/drivers/net/smc-mca.c @@ -91,7 +91,7 @@ struct smc_mca_adapters_t { char *name; }; -const struct smc_mca_adapters_t smc_mca_adapters[] = { +static const struct smc_mca_adapters_t smc_mca_adapters[] = { { 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" }, { 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" }, { 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" }, @@ -114,13 +114,15 @@ int __init ultramca_probe(struct net_device *dev) int adapter = 0; int tbase = 0; int tirq = 0; - int base_addr = dev ? dev->base_addr : 0; - int irq = dev ? dev->irq : 0; + int base_addr = dev->base_addr; + int irq = dev->irq; if (!MCA_bus) { return -ENODEV; } + SET_MODULE_OWNER(dev); + if (base_addr || irq) { printk(KERN_INFO "Probing for SMC MCA adapter"); if (base_addr) { @@ -340,7 +342,6 @@ static int ultramca_open(struct net_device *dev) */ ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -424,8 +425,6 @@ static int ultramca_close_card(struct net_device *dev) * "just in case"... */ - MOD_DEC_USE_COUNT; - return 0; } @@ -435,18 +434,9 @@ static int ultramca_close_card(struct net_device *dev) #define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */ -static struct net_device dev_ultra[MAX_ULTRAMCA_CARDS] = -{ - { - "", - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, NULL - }, -}; - -static int io[MAX_ULTRAMCA_CARDS] = { 0, }; -static int irq[MAX_ULTRAMCA_CARDS] = { 0, }; +static struct net_device dev_ultra[MAX_ULTRAMCA_CARDS]; +static int io[MAX_ULTRAMCA_CARDS]; +static int irq[MAX_ULTRAMCA_CARDS]; MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i"); diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c index 5e7c7aeae2bb..ebd3ab2b7b38 100644 --- a/drivers/net/smc-ultra.c +++ b/drivers/net/smc-ultra.c @@ -105,7 +105,9 @@ static int ultra_close_card(struct net_device *dev); int __init ultra_probe(struct net_device *dev) { int i; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); if (base_addr > 0x1ff) /* Check a single specified location. */ return ultra_probe1(dev, base_addr); @@ -272,7 +274,6 @@ ultra_open(struct net_device *dev) outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); outb(0xff, dev->base_addr + EN0_ERWCNT); ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -412,8 +413,6 @@ ultra_close_card(struct net_device *dev) /* We should someday disable shared memory and change to 8-bit mode "just in case"... */ - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/smc-ultra32.c b/drivers/net/smc-ultra32.c index ba387049ce94..3978433b5112 100644 --- a/drivers/net/smc-ultra32.c +++ b/drivers/net/smc-ultra32.c @@ -61,7 +61,7 @@ static const char *version = "smc-ultra32.c: 06/97 v1.00\n"; #include "8390.h" int ultra32_probe(struct net_device *dev); -int ultra32_probe1(struct net_device *dev, int ioaddr); +static int ultra32_probe1(struct net_device *dev, int ioaddr); static int ultra32_open(struct net_device *dev); static void ultra32_reset_8390(struct net_device *dev); static void ultra32_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, @@ -105,48 +105,64 @@ static int ultra32_close(struct net_device *dev); int __init ultra32_probe(struct net_device *dev) { - const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"}; - int ioaddr, edge, media; + int ioaddr; if (!EISA_bus) return -ENODEV; + SET_MODULE_OWNER(dev); + /* EISA spec allows for up to 16 slots, but 8 is typical. */ for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000) - if (check_region(ioaddr, ULTRA32_IO_EXTENT) == 0 && - inb(ioaddr + ULTRA32_IDPORT) != 0xff && - inl(ioaddr + ULTRA32_IDPORT) == ULTRA32_ID) { - media = inb(ioaddr + ULTRA32_CFG7) & 0x03; - edge = inb(ioaddr + ULTRA32_CFG5) & 0x08; - printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n", - ioaddr >> 12, ifmap[media], - (edge ? "Edge Triggered" : "Level Sensitive")); if (ultra32_probe1(dev, ioaddr) == 0) - return 0; - } + return 0; + return -ENODEV; } -int __init ultra32_probe1(struct net_device *dev, int ioaddr) +static int __init ultra32_probe1(struct net_device *dev, int ioaddr) { - int i; + int i, edge, media, retval; int checksum = 0; const char *model_name; - static unsigned version_printed = 0; + static unsigned version_printed; /* Values from various config regs. */ - unsigned char idreg = inb(ioaddr + 7); - unsigned char reg4 = inb(ioaddr + 4) & 0x7f; + unsigned char idreg; + unsigned char reg4; + const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"}; + + if (!request_region(ioaddr, ULTRA32_IO_EXTENT, dev->name)) + return -EBUSY; + + if (inb(ioaddr + ULTRA32_IDPORT) == 0xff || + inl(ioaddr + ULTRA32_IDPORT) != ULTRA32_ID) { + retval = -ENODEV; + goto out; + } + + media = inb(ioaddr + ULTRA32_CFG7) & 0x03; + edge = inb(ioaddr + ULTRA32_CFG5) & 0x08; + printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n", + ioaddr >> 12, ifmap[media], + (edge ? "Edge Triggered" : "Level Sensitive")); + + idreg = inb(ioaddr + 7); + reg4 = inb(ioaddr + 4) & 0x7f; /* Check the ID nibble. */ - if ((idreg & 0xf0) != 0x20) /* SMC Ultra */ - return -ENODEV; + if ((idreg & 0xf0) != 0x20) { /* SMC Ultra */ + retval = -ENODEV; + goto out; + } /* Select the station address register set. */ outb(reg4, ioaddr + 4); for (i = 0; i < 8; i++) checksum += inb(ioaddr + 8 + i); - if ((checksum & 0xff) != 0xff) - return -ENODEV; + if ((checksum & 0xff) != 0xff) { + retval = -ENODEV; + goto out; + } if (ei_debug && version_printed++ == 0) printk(version); @@ -175,7 +191,8 @@ int __init ultra32_probe1(struct net_device *dev, int ioaddr) if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) { printk("\nsmc-ultra32: Card RAM is disabled! " "Run EISA config utility.\n"); - return -ENODEV; + retval = -ENODEV; + goto out; } if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0) printk("\nsmc-ultra32: Ignoring Bus-Master enable bit. " @@ -186,7 +203,8 @@ int __init ultra32_probe1(struct net_device *dev, int ioaddr) int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07]; if (irq == 0) { printk(", failed to detect IRQ line.\n"); - return -EAGAIN; + retval = -EAGAIN; + goto out; } dev->irq = irq; } @@ -194,12 +212,10 @@ int __init ultra32_probe1(struct net_device *dev, int ioaddr) /* Allocate dev->priv and fill in 8390 specific dev fields. */ if (ethdev_init(dev)) { printk (", no memory for dev->priv.\n"); - return -ENOMEM; + retval = -ENOMEM; + goto out; } - /* OK, we are certain this is going to work. Setup the device. */ - request_region(ioaddr, ULTRA32_IO_EXTENT, model_name); - /* The 8390 isn't at the base address, so fake the offset */ dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET; @@ -229,15 +245,20 @@ int __init ultra32_probe1(struct net_device *dev, int ioaddr) NS8390_init(dev, 0); return 0; +out: + release_region(ioaddr, ULTRA32_IO_EXTENT); + return retval; } static int ultra32_open(struct net_device *dev) { int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */ int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : SA_SHIRQ; + int retval; - if (request_irq(dev->irq, ei_interrupt, irq_flags, ei_status.name, dev)) - return -EAGAIN; + retval = request_irq(dev->irq, ei_interrupt, irq_flags, dev->name, dev); + if (retval) + return retval; outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */ @@ -248,7 +269,6 @@ static int ultra32_open(struct net_device *dev) outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); outb(0xff, dev->base_addr + EN0_ERWCNT); ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -267,8 +287,6 @@ static int ultra32_close(struct net_device *dev) NS8390_init(dev, 0); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c index 201b259f7869..199553971104 100644 --- a/drivers/net/smc9194.c +++ b/drivers/net/smc9194.c @@ -256,15 +256,7 @@ inline static void smc_tx( struct net_device * dev ); . Test if a given location contains a chip, trying to cause as . little damage as possible if it's not a SMC chip. */ -static int smc_probe( int ioaddr ); - -/* - . this routine initializes the cards hardware, prints out the configuration - . to the system log as well as the vanity message, and handles the setup - . of a device parameter. - . It will give an error if it can't initialize the card. -*/ -static int smc_initcard( struct net_device *, int ioaddr ); +static int smc_probe(struct net_device *dev, int ioaddr); /* . A rather simple routine to print out a packet for debugging purposes. @@ -714,35 +706,20 @@ static void smc_hardware_send_packet( struct net_device * dev ) int __init smc_init(struct net_device *dev) { int i; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); /* try a specific location */ - if (base_addr > 0x1ff) { - int error; - error = smc_probe(base_addr); - if ( 0 == error ) { - return smc_initcard( dev, base_addr ); - } - return error; - } else { - if ( 0 != base_addr ) { - return -ENXIO; - } - } + if (base_addr > 0x1ff) + return smc_probe(dev, base_addr); + else if (base_addr != 0) + return -ENXIO; /* check every ethernet address */ - for (i = 0; smc_portlist[i]; i++) { - int ioaddr = smc_portlist[i]; - - /* check if the area is available */ - if (check_region( ioaddr , SMC_IO_EXTENT)) - continue; - - /* check this specific address */ - if ( smc_probe( ioaddr ) == 0) { - return smc_initcard( dev, ioaddr ); - } - } + for (i = 0; smc_portlist[i]; i++) + if (smc_probe(dev, smc_portlist[i]) == 0) + return 0; /* couldn't find anything */ return -ENODEV; @@ -837,23 +814,53 @@ int __init smc_findirq( int ioaddr ) .--------------------------------------------------------------------- */ -static int __init smc_probe( int ioaddr ) +/*--------------------------------------------------------------- + . Here I do typical initialization tasks. + . + . o Initialize the structure if needed + . o print out my vanity message if not done so already + . o print out what type of hardware is detected + . o print out the ethernet address + . o find the IRQ + . o set up my private data + . o configure the dev structure with my subroutines + . o actually GRAB the irq. + . o GRAB the region + .----------------------------------------------------------------- +*/ +static int __init smc_probe(struct net_device *dev, int ioaddr) { - unsigned int bank; - word revision_register; - word base_address_register; + int i, memory, retval; + static unsigned version_printed; + unsigned int bank; + + const char *version_string; + const char *if_string; + + /* registers */ + word revision_register; + word base_address_register; + word configuration_register; + word memory_info_register; + word memory_cfg_register; + + /* Grab the region so that no one else tries to probe our ioports. */ + if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) + return -EBUSY; /* First, see if the high byte is 0x33 */ bank = inw( ioaddr + BANK_SELECT ); if ( (bank & 0xFF00) != 0x3300 ) { - return -ENODEV; + retval = -ENODEV; + goto err_out; } /* The above MIGHT indicate a device, but I need to write to further test this. */ outw( 0x0, ioaddr + BANK_SELECT ); bank = inw( ioaddr + BANK_SELECT ); if ( (bank & 0xFF00 ) != 0x3300 ) { - return -ENODEV; + retval = -ENODEV; + goto err_out; } /* well, we've already written once, so hopefully another time won't hurt. This time, I need to switch the bank register to bank 1, @@ -866,7 +873,8 @@ static int __init smc_probe( int ioaddr ) ioaddr, base_address_register >> 3 & 0x3E0 ); /* well, the base address register didn't match. Must not have been a SMC chip after all. */ - return -ENODEV; + retval = -ENODEV; + goto err_out; } /* check if the revision register is something that I recognize. @@ -879,46 +887,13 @@ static int __init smc_probe( int ioaddr ) printk(CARDNAME ": IO %x: Unrecognized revision register:" " %x, Contact author. \n", ioaddr, revision_register ); - return -ENODEV; + retval = -ENODEV; + goto err_out; } /* at this point I'll assume that the chip is an SMC9xxx. It might be prudent to check a listing of MAC addresses against the hardware address, or do some other tests. */ - return 0; -} - -/*--------------------------------------------------------------- - . Here I do typical initialization tasks. - . - . o Initialize the structure if needed - . o print out my vanity message if not done so already - . o print out what type of hardware is detected - . o print out the ethernet address - . o find the IRQ - . o set up my private data - . o configure the dev structure with my subroutines - . o actually GRAB the irq. - . o GRAB the region - .----------------------------------------------------------------- -*/ -static int __init smc_initcard(struct net_device *dev, int ioaddr) -{ - int i; - - static unsigned version_printed = 0; - - /* registers */ - word revision_register; - word configuration_register; - word memory_info_register; - word memory_cfg_register; - - const char * version_string; - const char * if_string; - int memory; - - int irqval; if (version_printed++ == 0) printk("%s", version); @@ -956,7 +931,8 @@ static int __init smc_initcard(struct net_device *dev, int ioaddr) version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; if ( !version_string ) { /* I shouldn't get here because this call was done before.... */ - return -ENODEV; + retval = -ENODEV; + goto err_out; } /* is it using AUI or 10BaseT ? */ @@ -1003,7 +979,8 @@ static int __init smc_initcard(struct net_device *dev, int ioaddr) } if (dev->irq == 0 ) { printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); - return -ENODEV; + retval = -ENODEV; + goto err_out; } if (dev->irq == 2) { /* Fixup for users that don't know that IRQ 2 is really IRQ 9, @@ -1014,7 +991,7 @@ static int __init smc_initcard(struct net_device *dev, int ioaddr) /* now, print out the card info, in a short format.. */ - printk(CARDNAME ": %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", + printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, version_string, revision_register & 0xF, ioaddr, dev->irq, if_string, memory ); /* @@ -1029,8 +1006,10 @@ static int __init smc_initcard(struct net_device *dev, int ioaddr) /* Initialize the private structure. */ if (dev->priv == NULL) { dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; + if (dev->priv == NULL) { + retval = -ENOMEM; + goto err_out; + } } /* set the private data to zero by default */ memset(dev->priv, 0, sizeof(struct smc_local)); @@ -1039,16 +1018,15 @@ static int __init smc_initcard(struct net_device *dev, int ioaddr) ether_setup(dev); /* Grab the IRQ */ - irqval = request_irq(dev->irq, &smc_interrupt, 0, CARDNAME, dev); - if (irqval) { - printk(CARDNAME": unable to get IRQ %d (irqval=%d).\n", - dev->irq, irqval); - return -EAGAIN; + retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); + if (retval) { + printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, + dev->irq, retval); + kfree(dev->priv); + dev->priv = NULL; + goto err_out; } - /* Grab the region so that no one else tries to probe our ioports. */ - request_region(ioaddr, SMC_IO_EXTENT, CARDNAME); - dev->open = smc_open; dev->stop = smc_close; dev->hard_start_xmit = smc_wait_to_send_packet; @@ -1058,6 +1036,10 @@ static int __init smc_initcard(struct net_device *dev, int ioaddr) dev->set_multicast_list = smc_set_multicast_list; return 0; + +err_out: + release_region(ioaddr, SMC_IO_EXTENT); + return retval; } #if SMC_DEBUG > 2 @@ -1112,8 +1094,6 @@ static int smc_open(struct net_device *dev) /* clear out all the junk that was put here before... */ memset(dev->priv, 0, sizeof(struct smc_local)); - MOD_INC_USE_COUNT; - /* reset the hardware */ smc_reset( ioaddr ); @@ -1504,7 +1484,6 @@ static int smc_close(struct net_device *dev) smc_shutdown( dev->base_addr ); /* Update the statistics here. */ - MOD_DEC_USE_COUNT; return 0; } @@ -1577,8 +1556,7 @@ static void smc_set_multicast_list(struct net_device *dev) #ifdef MODULE -static struct net_device devSMC9194 = { init: smc_init }; - +static struct net_device devSMC9194; static int io; static int irq; static int ifport; @@ -1599,6 +1577,7 @@ int init_module(void) devSMC9194.base_addr = io; devSMC9194.irq = irq; devSMC9194.if_port = ifport; + devSMC9194.init = smc_init; if ((result = register_netdev(&devSMC9194)) != 0) return result; diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index a6535df79f3f..085270494319 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -390,6 +390,7 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev, dev = init_etherdev(NULL, sizeof(*np)); if (!dev) return -ENOMEM; + SET_MODULE_OWNER(dev); #ifdef USE_IO_OPS ioaddr = pci_resource_start(pdev, 0); @@ -609,12 +610,9 @@ static int netdev_open(struct net_device *dev) /* Do we need to reset the chip??? */ - MOD_INC_USE_COUNT; - - if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { - MOD_DEC_USE_COUNT; - return -EAGAIN; - } + i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + if (i) + return i; if (debug > 1) printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", @@ -1224,8 +1222,6 @@ static int netdev_close(struct net_device *dev) np->tx_skbuff[i] = 0; } - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index bfb0f57dfd00..ba436a12b77c 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -1,4 +1,4 @@ -/* $Id: sunhme.c,v 1.104 2000/11/17 01:40:00 davem Exp $ +/* $Id: sunhme.c,v 1.105 2000/12/05 02:00:36 anton Exp $ * sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching, * auto carrier detecting ethernet driver. Also known as the * "Happy Meal Ethernet" found on SunSwift SBUS cards. @@ -1492,18 +1492,18 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq) /* Shut up the MIF. */ HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ", - hme_read32(hp, &tregs->int_mask))); + hme_read32(hp, tregs + TCVR_IMASK))); hme_write32(hp, tregs + TCVR_IMASK, 0xffff); /* See if we can enable the MIF frame on this card to speak to the DP83840. */ if (hp->happy_flags & HFLAG_FENABLE) { HMD(("use frame old[%08x], ", - hme_read32(hp, &tregs->cfg))); + hme_read32(hp, tregs + TCVR_CFG))); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_BENABLE)); } else { HMD(("use bitbang old[%08x], ", - hme_read32(hp, &tregs->cfg))); + hme_read32(hp, tregs + TCVR_CFG))); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) | TCV_CFG_BENABLE); } diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index 33e98c23e776..e3aae91119e2 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -474,6 +474,7 @@ static int __devinit TLan_probe1(struct pci_dev *pdev, printk(KERN_ERR "TLAN: Could not allocate memory for device.\n"); return -ENOMEM; } + SET_MODULE_OWNER(dev); priv = dev->priv; @@ -814,14 +815,11 @@ static int TLan_Open( struct net_device *dev ) TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; int err; - MOD_INC_USE_COUNT; - priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); if ( err ) { printk(KERN_ERR "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq ); - MOD_DEC_USE_COUNT; return err; } @@ -1098,8 +1096,6 @@ static int TLan_Close(struct net_device *dev) TLan_FreeLists( dev ); TLAN_DBG( TLAN_DEBUG_GNRL, "Device %s closed.\n", dev->name ); - MOD_DEC_USE_COUNT; - return 0; } /* TLan_Close */ diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index e91f3b0cc610..f5d261027c1f 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -52,7 +52,7 @@ const char * const medianame[] = { /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(__sparc_) + || defined(__sparc_) || defined(__ia64__) static int rx_copybreak = 1518; #else static int rx_copybreak = 100; @@ -71,7 +71,7 @@ static int rx_copybreak = 100; ToDo: Non-Intel setting could be better. */ -#if defined(__alpha__) +#if defined(__alpha__) || defined(__ia64__) static int csr0 = 0x01A00000 | 0xE000; #elif defined(__i386__) || defined(__powerpc__) static int csr0 = 0x01A00000 | 0x8000; diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 9d0947b05875..a1d4629af178 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -508,6 +508,7 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, card_idx); goto err_out_free_dma; } + SET_MODULE_OWNER(dev); /* request all PIO and MMIO regions just to make sure * noone else attempts to use any portion of our I/O space */ @@ -694,15 +695,12 @@ static int via_rhine_open(struct net_device *dev) long ioaddr = dev->base_addr; int i; - MOD_INC_USE_COUNT; - /* Reset the chip. */ writew(CmdReset, ioaddr + ChipCmd); - if (request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev)) { - MOD_DEC_USE_COUNT; - return -EBUSY; - } + i = request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev); + if (i) + return i; if (debug > 1) printk(KERN_DEBUG "%s: via_rhine_open() irq %d.\n", @@ -712,7 +710,6 @@ static int via_rhine_open(struct net_device *dev) &np->tx_bufs_dma); if (np->tx_bufs == NULL) { free_irq(dev->irq, dev); - MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -1403,8 +1400,6 @@ static int via_rhine_close(struct net_device *dev) pci_free_consistent(np->pdev, PKT_BUF_SZ * TX_RING_SIZE, np->tx_bufs, np->tx_bufs_dma); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c index 2bf564aba660..e6a2837b846a 100644 --- a/drivers/net/wan/comx-hw-mixcom.c +++ b/drivers/net/wan/comx-hw-mixcom.c @@ -76,7 +76,7 @@ static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"}; struct mixcom_privdata { u16 clock; char channel; - char txbusy; + long txbusy; struct sk_buff *sending; unsigned tx_ptr; struct sk_buff *recving; diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h index b343eb4ca0af..1461d5fb18cb 100644 --- a/drivers/net/wan/comx.h +++ b/drivers/net/wan/comx.h @@ -55,12 +55,12 @@ struct comx_channel { unsigned char line_status; struct timer_list lineup_timer; // against line jitter - int lineup_pending; + long int lineup_pending; unsigned char lineup_delay; #if 0 struct timer_list reset_timer; // for board resetting - int reset_pending; + long reset_pending; int reset_timeout; #endif diff --git a/drivers/net/wan/sdlamain.c b/drivers/net/wan/sdlamain.c index 57f800d03b82..4ab0de121476 100644 --- a/drivers/net/wan/sdlamain.c +++ b/drivers/net/wan/sdlamain.c @@ -106,14 +106,7 @@ static int active; /* number of active cards */ static sdla_t* card_array; /* adapter data space */ /* Task queue element for creating a 'thread' */ -static struct tq_struct sdla_tq = -{ - NULL, /* .next */ - 0, /* .sync */ - &sdla_poll, /* .routine */ - NULL /* .data */ -}; - +static struct tq_struct sdla_tq = { routine: sdla_poll }; /******* Kernel Loadable Module Entry Points ********************************/ diff --git a/drivers/net/wan/x25_asy.h b/drivers/net/wan/x25_asy.h index 5abeceb2039e..c54e2ed18e55 100644 --- a/drivers/net/wan/x25_asy.h +++ b/drivers/net/wan/x25_asy.h @@ -42,7 +42,7 @@ struct x25_asy { int mtu; /* Our mtu (to spot changes!) */ int buffsize; /* Max buffers sizes */ - unsigned int flags; /* Flag values/ mode etc */ + unsigned long flags; /* Flag values/ mode etc */ #define SLF_INUSE 0 /* Channel in use */ #define SLF_ESCAPE 1 /* ESC received */ #define SLF_ERROR 2 /* Parity, etc. error */ diff --git a/drivers/net/wd.c b/drivers/net/wd.c index e46844b598da..26a26506d769 100644 --- a/drivers/net/wd.c +++ b/drivers/net/wd.c @@ -86,7 +86,9 @@ int __init wd_probe(struct net_device *dev) { int i; struct resource *r; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); if (base_addr > 0x1ff) { /* Check a user specified location. */ r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); @@ -96,7 +98,7 @@ int __init wd_probe(struct net_device *dev) if (i != 0) release_resource(r); else - r->name = ei_status.name; + r->name = dev->name; return i; } else if (base_addr != 0) /* Don't probe at all. */ @@ -108,7 +110,7 @@ int __init wd_probe(struct net_device *dev) if (r == NULL) continue; if (wd_probe1(dev, ioaddr) == 0) { - r->name = ei_status.name; + r->name = dev->name; return 0; } release_resource(r); @@ -263,11 +265,12 @@ static int __init wd_probe1(struct net_device *dev, int ioaddr) /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ - if (request_irq(dev->irq, ei_interrupt, 0, model_name, dev)) { + i = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + if (i) { printk (" unable to get IRQ %d.\n", dev->irq); kfree(dev->priv); dev->priv = NULL; - return -EAGAIN; + return i; } /* OK, were are certain this is going to work. Setup the device. */ @@ -324,7 +327,6 @@ wd_open(struct net_device *dev) outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -431,8 +433,6 @@ wd_close(struct net_device *dev) /* And disable the shared memory. */ outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/winbond-840.c b/drivers/net/winbond-840.c index 03f79ca2eea0..2996fb6f9951 100644 --- a/drivers/net/winbond-840.c +++ b/drivers/net/winbond-840.c @@ -367,6 +367,7 @@ static int __devinit w840_probe1 (struct pci_dev *pdev, dev = init_etherdev(NULL, sizeof(*np)); if (!dev) return -ENOMEM; + SET_MODULE_OWNER(dev); #ifdef USE_IO_OPS ioaddr = pci_resource_start(pdev, 0); @@ -623,12 +624,9 @@ static int netdev_open(struct net_device *dev) writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */ - MOD_INC_USE_COUNT; - - if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { - MOD_DEC_USE_COUNT; - return -EAGAIN; - } + i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + if (i) + return i; if (debug > 1) printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n", @@ -1305,8 +1303,6 @@ static int netdev_close(struct net_device *dev) np->tx_skbuff[i] = 0; } - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 4e2d6435e87e..9cda23333a6f 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -305,9 +305,6 @@ struct yellowfin_private { for status. */ struct yellowfin_desc rx_ring[RX_RING_SIZE]; struct yellowfin_desc tx_ring[TX_RING_SIZE*2]; - const char *product_name; - struct net_device *next_module; - void *priv_addr; /* Unaligned address for kfree */ /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ @@ -426,8 +423,6 @@ static int yellowfin_open(struct net_device *dev) printk(KERN_DEBUG "%s: yellowfin_open() irq %d.\n", dev->name, dev->irq); - MOD_INC_USE_COUNT; - yellowfin_init_ring(dev); YF_OUTL(virt_to_bus(yp->rx_ring), ioaddr + RxPtr); @@ -1110,8 +1105,6 @@ static int yellowfin_close(struct net_device *dev) dev->name, bogus_rx); } #endif - MOD_DEC_USE_COUNT; - return 0; } @@ -1230,24 +1223,19 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev, chip_idx = ent->driver_data; flags = chip_info[chip_idx].flags; - dev = init_etherdev(NULL, 0); + dev = init_etherdev(NULL, sizeof(*yp)); if (!dev) { printk (KERN_ERR PFX "cannot allocate ethernet device\n"); return -ENOMEM; } + SET_MODULE_OWNER(dev); - dev->priv = kmalloc(sizeof(*yp) + PRIV_ALIGN, GFP_KERNEL); - if (!dev->priv) - goto err_out_free_netdev; - yp = (void *)(((long)dev->priv + PRIV_ALIGN) & ~PRIV_ALIGN); - memset(yp, 0, sizeof(*yp)); - yp->priv_addr = dev->priv; /* store real addr for kfree */ - dev->priv = yp; /* use aligned addr */ + yp = dev->priv; if (!request_region (pci_resource_start (pdev, 0), YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) { printk (KERN_ERR PFX "cannot obtain I/O port region\n"); - goto err_out_free_priv; + goto err_out_free_netdev; } if (!request_mem_region (pci_resource_start (pdev, 1), YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) { @@ -1255,8 +1243,8 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev, goto err_out_free_pio_region; } - /* XXX check enable_device for failure */ - pci_enable_device (pdev); + if (pci_enable_device (pdev)) + goto err_out_free_mmio_region; pci_set_master (pdev); #ifdef USE_IO_OPS @@ -1264,7 +1252,8 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev, #else real_ioaddr = ioaddr = pci_resource_start (pdev, 1); ioaddr = (long) ioremap(ioaddr, YELLOWFIN_SIZE); - /* XXX check for failure */ + if (!ioaddr) + goto err_out_free_mmio_region; #endif irq = pdev->irq; @@ -1348,10 +1337,10 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev, return 0; +err_out_free_mmio_region: + release_mem_region (pci_resource_start (pdev, 1), YELLOWFIN_SIZE); err_out_free_pio_region: release_region (pci_resource_start (pdev, 0), YELLOWFIN_SIZE); -err_out_free_priv: - kfree (dev->priv); err_out_free_netdev: unregister_netdev (dev); kfree (dev); @@ -1363,23 +1352,18 @@ static void __devexit yellowfin_remove_one (struct pci_dev *pdev) struct net_device *dev = pdev->driver_data; struct yellowfin_private *np; - if (!dev) { - printk (KERN_ERR "remove non-existent device\n"); - return; - } - np = (struct yellowfin_private *) dev->priv; + if (!dev) + BUG(); + np = dev->priv; unregister_netdev (dev); -#ifdef USE_IO_OPS release_region (dev->base_addr, YELLOWFIN_SIZE); -#else - iounmap ((void *) dev->base_addr); release_mem_region (dev->base_addr, YELLOWFIN_SIZE); -#endif - if (np->priv_addr) - kfree (np->priv_addr); +#ifndef USE_IO_OPS + iounmap ((void *) dev->base_addr); +#endif kfree (dev); } @@ -1398,11 +1382,7 @@ static int __init yellowfin_init (void) if (debug) /* Emit version even if no cards detected. */ printk(KERN_INFO "%s", version); - if (pci_register_driver (&yellowfin_driver) > 0) - return 0; - - pci_unregister_driver (&yellowfin_driver); - return -ENODEV; + return pci_module_init (&yellowfin_driver); } diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog index 1314e9f9d0d8..8d3c572ab4fb 100644 --- a/drivers/parport/ChangeLog +++ b/drivers/parport/ChangeLog @@ -1,3 +1,9 @@ +2000-11-21 Tim Waugh + + * parport_pc.c (parport_pc_ecp_write_block_pio): Fix + reverse-to-forward logic. Spotted by Roland Kuck + . + 2000-09-16 Cesar Eduardo Barros * parport_pc.c (sio_via_686a_probe): Handle case diff --git a/drivers/parport/Makefile b/drivers/parport/Makefile index 95a778e96aec..f2f7b1d7069f 100644 --- a/drivers/parport/Makefile +++ b/drivers/parport/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_PARPORT_AMIGA) += parport_amiga.o obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o +obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c new file mode 100644 index 000000000000..8e4b177e324d --- /dev/null +++ b/drivers/parport/parport_gsc.c @@ -0,0 +1,573 @@ +/* + * Low-level parallel-support for PC-style hardware integrated in the + * LASI-Controller (on GSC-Bus) for HP-PARISC Workstations + * + * 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. + * + * by Helge Deller + * + * + * based on parport_pc.c by + * Grant Guenther + * Phil Blundell + * Tim Waugh + * Jose Renau + * David Campbell + * Andrea Arcangeli + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +#undef DEBUG /* undef for production */ + +#ifdef DEBUG +#define DPRINTK printk +#else +#define DPRINTK(stuff...) +#endif + + +/* + * Clear TIMEOUT BIT in EPP MODE + * + * This is also used in SPP detection. + */ +static int clear_epp_timeout(struct parport *pb) +{ + unsigned char r; + + if (!(parport_gsc_read_status(pb) & 0x01)) + return 1; + + /* To clear timeout some chips require double read */ + parport_gsc_read_status(pb); + r = parport_gsc_read_status(pb); + parport_writeb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */ + parport_writeb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */ + r = parport_gsc_read_status(pb); + + return !(r & 0x01); +} + +/* + * Access functions. + * + * Most of these aren't static because they may be used by the + * parport_xxx_yyy macros. extern __inline__ versions of several + * of these are in parport_gsc.h. + */ + +static void parport_gsc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + DPRINTK(__FILE__ ": got IRQ\n"); + parport_generic_irq(irq, (struct parport *) dev_id, regs); +} + +void parport_gsc_write_data(struct parport *p, unsigned char d) +{ + DPRINTK(__FILE__ ": write (0x%02x) %c \n", d, d); + parport_writeb (d, DATA (p)); +} + +unsigned char parport_gsc_read_data(struct parport *p) +{ +#ifdef DEBUG + unsigned char c = parport_readb (DATA (p)); + DPRINTK(__FILE__ ": read (0x%02x) %c\n", c,c); + return c; +#else + return parport_readb (DATA (p)); +#endif +} + +void parport_gsc_write_control(struct parport *p, unsigned char d) +{ + const unsigned char wm = (PARPORT_CONTROL_STROBE | + PARPORT_CONTROL_AUTOFD | + PARPORT_CONTROL_INIT | + PARPORT_CONTROL_SELECT); + + /* Take this out when drivers have adapted to the newer interface. */ + if (d & 0x20) { + printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n", + p->name, p->cad->name); + parport_gsc_data_reverse (p); + } + + __parport_gsc_frob_control (p, wm, d & wm); +} + +unsigned char parport_gsc_read_control(struct parport *p) +{ + const unsigned char wm = (PARPORT_CONTROL_STROBE | + PARPORT_CONTROL_AUTOFD | + PARPORT_CONTROL_INIT | + PARPORT_CONTROL_SELECT); + const struct parport_gsc_private *priv = p->physport->private_data; + return priv->ctr & wm; /* Use soft copy */ +} + +unsigned char parport_gsc_frob_control (struct parport *p, unsigned char mask, + unsigned char val) +{ + const unsigned char wm = (PARPORT_CONTROL_STROBE | + PARPORT_CONTROL_AUTOFD | + PARPORT_CONTROL_INIT | + PARPORT_CONTROL_SELECT); + + /* Take this out when drivers have adapted to the newer interface. */ + if (mask & 0x20) { + printk (KERN_DEBUG "%s (%s): use data_%s for this!\n", + p->name, p->cad->name, + (val & 0x20) ? "reverse" : "forward"); + if (val & 0x20) + parport_gsc_data_reverse (p); + else + parport_gsc_data_forward (p); + } + + /* Restrict mask and val to control lines. */ + mask &= wm; + val &= wm; + + return __parport_gsc_frob_control (p, mask, val); +} + +unsigned char parport_gsc_read_status(struct parport *p) +{ + return parport_readb (STATUS (p)); +} + +void parport_gsc_disable_irq(struct parport *p) +{ + __parport_gsc_frob_control (p, 0x10, 0); +} + +void parport_gsc_enable_irq(struct parport *p) +{ + __parport_gsc_frob_control (p, 0x10, 0x10); +} + +void parport_gsc_data_forward (struct parport *p) +{ + __parport_gsc_frob_control (p, 0x20, 0); +} + +void parport_gsc_data_reverse (struct parport *p) +{ + __parport_gsc_frob_control (p, 0x20, 0x20); +} + +void parport_gsc_init_state(struct pardevice *dev, struct parport_state *s) +{ + s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0); +} + +void parport_gsc_save_state(struct parport *p, struct parport_state *s) +{ + s->u.pc.ctr = parport_readb (CONTROL (p)); +} + +void parport_gsc_restore_state(struct parport *p, struct parport_state *s) +{ + parport_writeb (s->u.pc.ctr, CONTROL (p)); +} + +void parport_gsc_inc_use_count(void) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void parport_gsc_dec_use_count(void) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +struct parport_operations parport_gsc_ops = +{ + write_data: parport_gsc_write_data, + read_data: parport_gsc_read_data, + + write_control: parport_gsc_write_control, + read_control: parport_gsc_read_control, + frob_control: parport_gsc_frob_control, + + read_status: parport_gsc_read_status, + + enable_irq: parport_gsc_enable_irq, + disable_irq: parport_gsc_disable_irq, + + data_forward: parport_gsc_data_forward, + data_reverse: parport_gsc_data_reverse, + + init_state: parport_gsc_init_state, + save_state: parport_gsc_save_state, + restore_state: parport_gsc_restore_state, + + inc_use_count: parport_gsc_inc_use_count, + dec_use_count: parport_gsc_dec_use_count, + + epp_write_data: parport_ieee1284_epp_write_data, + epp_read_data: parport_ieee1284_epp_read_data, + epp_write_addr: parport_ieee1284_epp_write_addr, + epp_read_addr: parport_ieee1284_epp_read_addr, + + ecp_write_data: parport_ieee1284_ecp_write_data, + ecp_read_data: parport_ieee1284_ecp_read_data, + ecp_write_addr: parport_ieee1284_ecp_write_addr, + + compat_write_data: parport_ieee1284_write_compat, + nibble_read_data: parport_ieee1284_read_nibble, + byte_read_data: parport_ieee1284_read_byte, +}; + +/* --- Mode detection ------------------------------------- */ + +/* + * Checks for port existence, all ports support SPP MODE + */ +static int __devinit parport_SPP_supported(struct parport *pb) +{ + unsigned char r, w; + + /* + * first clear an eventually pending EPP timeout + * I (sailer@ife.ee.ethz.ch) have an SMSC chipset + * that does not even respond to SPP cycles if an EPP + * timeout is pending + */ + clear_epp_timeout(pb); + + /* Do a simple read-write test to make sure the port exists. */ + w = 0xc; + parport_writeb (w, CONTROL (pb)); + + /* Is there a control register that we can read from? Some + * ports don't allow reads, so read_control just returns a + * software copy. Some ports _do_ allow reads, so bypass the + * software copy here. In addition, some bits aren't + * writable. */ + r = parport_readb (CONTROL (pb)); + if ((r & 0xf) == w) { + w = 0xe; + parport_writeb (w, CONTROL (pb)); + r = parport_readb (CONTROL (pb)); + parport_writeb (0xc, CONTROL (pb)); + if ((r & 0xf) == w) + return PARPORT_MODE_PCSPP; + } + + /* Try the data register. The data lines aren't tri-stated at + * this stage, so we expect back what we wrote. */ + w = 0xaa; + parport_gsc_write_data (pb, w); + r = parport_gsc_read_data (pb); + if (r == w) { + w = 0x55; + parport_gsc_write_data (pb, w); + r = parport_gsc_read_data (pb); + if (r == w) + return PARPORT_MODE_PCSPP; + } + + return 0; +} + +/* Detect PS/2 support. + * + * Bit 5 (0x20) sets the PS/2 data direction; setting this high + * allows us to read data from the data lines. In theory we would get back + * 0xff but any peripheral attached to the port may drag some or all of the + * lines down to zero. So if we get back anything that isn't the contents + * of the data register we deem PS/2 support to be present. + * + * Some SPP ports have "half PS/2" ability - you can't turn off the line + * drivers, but an external peripheral with sufficiently beefy drivers of + * its own can overpower them and assert its own levels onto the bus, from + * where they can then be read back as normal. Ports with this property + * and the right type of device attached are likely to fail the SPP test, + * (as they will appear to have stuck bits) and so the fact that they might + * be misdetected here is rather academic. + */ + +static int __devinit parport_PS2_supported(struct parport *pb) +{ + int ok = 0; + + clear_epp_timeout(pb); + + /* try to tri-state the buffer */ + parport_gsc_data_reverse (pb); + + parport_gsc_write_data(pb, 0x55); + if (parport_gsc_read_data(pb) != 0x55) ok++; + + parport_gsc_write_data(pb, 0xaa); + if (parport_gsc_read_data(pb) != 0xaa) ok++; + + /* cancel input mode */ + parport_gsc_data_forward (pb); + + if (ok) { + pb->modes |= PARPORT_MODE_TRISTATE; + } else { + struct parport_gsc_private *priv = pb->private_data; + priv->ctr_writable &= ~0x20; + } + + return ok; +} + + +/* --- Initialisation code -------------------------------- */ + +struct parport *__devinit parport_gsc_probe_port (unsigned long base, + unsigned long base_hi, + int irq, int dma, + struct pci_dev *dev) +{ + struct parport_gsc_private *priv; + struct parport_operations *ops; + struct parport tmp; + struct parport *p = &tmp; + + if (check_region(base, 3)) + return NULL; + + priv = kmalloc (sizeof (struct parport_gsc_private), GFP_KERNEL); + if (!priv) { + printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base); + return NULL; + } + ops = kmalloc (sizeof (struct parport_operations), GFP_KERNEL); + if (!ops) { + printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n", + base); + kfree (priv); + return NULL; + } + memcpy (ops, &parport_gsc_ops, sizeof (struct parport_operations)); + priv->ctr = 0xc; + priv->ctr_writable = 0xff; + priv->dma_buf = 0; + priv->dma_handle = 0; + priv->dev = dev; + p->base = base; + p->base_hi = base_hi; + p->irq = irq; + p->dma = dma; + p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT; + p->ops = ops; + p->private_data = priv; + p->physport = p; + if (!parport_SPP_supported (p)) { + /* No port. */ + kfree (priv); + return NULL; + } + parport_PS2_supported (p); + + if (!(p = parport_register_port(base, PARPORT_IRQ_NONE, + PARPORT_DMA_NONE, ops))) { + kfree (priv); + kfree (ops); + return NULL; + } + + p->base_hi = base_hi; + p->modes = tmp.modes; + p->size = (p->modes & PARPORT_MODE_EPP)?8:3; + p->private_data = priv; + + printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base); + p->irq = irq; + if (p->irq == PARPORT_IRQ_AUTO) { + p->irq = PARPORT_IRQ_NONE; + } + if (p->irq != PARPORT_IRQ_NONE) { + printk(", irq %d", p->irq); + + if (p->dma == PARPORT_DMA_AUTO) { + p->dma = PARPORT_DMA_NONE; + } + } + if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq + is mandatory (see above) */ + p->dma = PARPORT_DMA_NONE; + + printk(" ["); +#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}} + { + int f = 0; + printmode(PCSPP); + printmode(TRISTATE); + printmode(COMPAT) + printmode(EPP); +// printmode(ECP); +// printmode(DMA); + } +#undef printmode + printk("]\n"); + parport_proc_register(p); + + request_region (p->base, 3, p->name); + if (p->size > 3) + request_region (p->base + 3, p->size - 3, p->name); + if (p->modes & PARPORT_MODE_ECP) + request_region (p->base_hi, 3, p->name); + + if (p->irq != PARPORT_IRQ_NONE) { + if (request_irq (p->irq, parport_gsc_interrupt, + 0, p->name, p)) { + printk (KERN_WARNING "%s: irq %d in use, " + "resorting to polled operation\n", + p->name, p->irq); + p->irq = PARPORT_IRQ_NONE; + p->dma = PARPORT_DMA_NONE; + } + } + + /* Done probing. Now put the port into a sensible start-up state. */ + + parport_gsc_write_data(p, 0); + parport_gsc_data_forward (p); + + /* Now that we've told the sharing engine about the port, and + found out its characteristics, let the high-level drivers + know about it. */ + parport_announce_port (p); + + return p; +} + + +#define PARPORT_GSC_OFFSET 0x800 + +static int __initdata parport_count; + +static int __init +parport_init_chip(struct hp_device *d, struct pa_iodc_driver *dri) +{ + unsigned long port; + int irq; + + irq = busdevice_alloc_irq(d); + + if (!irq) { + printk("IRQ not found for parallel device at 0x%p\n", d->hpa); + return -ENODEV; + } + + port = ((unsigned long) d->hpa) + PARPORT_GSC_OFFSET; + + /* + some older machines with ASP-chip don't support the enhanced parport modes + */ + if (!pdc_add_valid( (void *)(port+4))) { + /* Initialize bidirectional-mode (0x10) & data-tranfer-mode #1 (0x20) */ + printk("%s: initialize bidirectional-mode.\n", __FUNCTION__); + parport_writeb ( (0x10 + 0x20), port + 4); + } else { + printk("%s: enhanced parport-modes not supported.\n", __FUNCTION__); + } + + if (parport_gsc_probe_port(port, 0, + irq, /* PARPORT_IRQ_NONE */ + PARPORT_DMA_NONE, NULL)) + parport_count++; + + return 0; +} + +static struct pa_iodc_driver parport_drivers_for[] __initdata = { + {HPHW_FIO, 0x0, 0x0, 0x74, 0x0, 0, /* 715/64 */ + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + "parallel device", "HP 7xx - Series", (void *) parport_init_chip}, + { 0 } +}; + +int __init +parport_gsc_init ( void ) +{ + parport_count = 0; + + register_driver(parport_drivers_for); + + return parport_count; +} + +/* Exported symbols. */ +EXPORT_NO_SYMBOLS; + + +#ifdef MODULE + +MODULE_AUTHOR("Helge Deller "); +MODULE_DESCRIPTION("HP-PARISC PC-style parallel port driver"); +MODULE_SUPPORTED_DEVICE("integrated PC-style parallel port"); + +int init_module(void) +{ + return !parport_gsc_init (); +} + +void cleanup_module(void) +{ + struct parport *p = parport_enumerate(), *tmp; + while (p) { + tmp = p->next; + if (p->modes & PARPORT_MODE_PCSPP) { + struct parport_gsc_private *priv = p->private_data; + struct parport_operations *ops = p->ops; + if (p->dma != PARPORT_DMA_NONE) + free_dma(p->dma); + if (p->irq != PARPORT_IRQ_NONE) + free_irq(p->irq, p); + release_region(p->base, 3); + if (p->size > 3) + release_region(p->base + 3, p->size - 3); + if (p->modes & PARPORT_MODE_ECP) + release_region(p->base_hi, 3); + parport_proc_unregister(p); + if (priv->dma_buf) + pci_free_consistent(priv->dev, PAGE_SIZE, + priv->dma_buf, + priv->dma_handle); + kfree (p->private_data); + parport_unregister_port(p); + kfree (ops); /* hope no-one cached it */ + } + p = tmp; + } +} +#endif diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 952eaec41f99..fe6721c34147 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -778,9 +778,13 @@ size_t parport_pc_ecp_write_block_pio (struct parport *port, /* Switch to forward mode if necessary. */ if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) { /* Event 47: Set nInit high. */ - parport_frob_control (port, PARPORT_CONTROL_INIT, 0); + parport_frob_control (port, + PARPORT_CONTROL_INIT + | PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_INIT + | PARPORT_CONTROL_AUTOFD); - /* Event 40: PError goes high. */ + /* Event 49: PError goes high. */ r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, PARPORT_STATUS_PAPEROUT); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index afcc00a892c3..a6dc39f437ae 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -582,8 +582,9 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) pci_write_config_dword(dev, reg+4, ~0); pci_read_config_dword(dev, reg+4, &sz); pci_write_config_dword(dev, reg+4, l); - if (sz) - res->end = res->start + (((unsigned long) ~sz) << 32); + if (~sz) + res->end = res->start + 0xffffffff + + (((unsigned long) ~sz) << 32); #else if (l) { printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index bf9c82c5a742..ace49456b949 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -205,7 +205,7 @@ pbus_assign_resources(struct pci_bus *bus, struct pbus_set_ranges_data *ranges) } } -void __init +void __init pci_assign_unassigned_resources(void) { struct pbus_set_ranges_data ranges; @@ -215,8 +215,8 @@ pci_assign_unassigned_resources(void) for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) { struct pci_bus *b = pci_bus_b(ln); - ranges.io_start = b->resource[0]->start; - ranges.mem_start = b->resource[1]->start; + ranges.io_start = b->resource[0]->start + PCIBIOS_MIN_IO; + ranges.mem_start = b->resource[1]->start + PCIBIOS_MIN_MEM; ranges.io_end = ranges.io_start; ranges.mem_end = ranges.mem_start; ranges.found_vga = 0; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index a2e83d751477..1ca56825458a 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -136,6 +136,7 @@ pdev_sort_resources(struct pci_dev *dev, for (i = 0; i < PCI_NUM_RESOURCES; i++) { struct resource *r; struct resource_list *list, *tmp; + unsigned long r_size; /* PCI-PCI bridges may have I/O ports or memory on the primary bus */ @@ -144,15 +145,23 @@ pdev_sort_resources(struct pci_dev *dev, continue; r = &dev->resource[i]; + r_size = r->end - r->start; + if (!(r->flags & type_mask) || r->parent) continue; + if (!r_size) { + printk(KERN_WARNING "PCI: Ignore bogus resource %d " + "[%lx:%lx] of %s\n", + i, r->start, r->end, dev->name); + continue; + } for (list = head; ; list = list->next) { unsigned long size = 0; struct resource_list *ln = list->next; if (ln) size = ln->res->end - ln->res->start; - if (r->end - r->start > size) { + if (r_size > size) { tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); tmp->next = ln; tmp->res = r; diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c index b8f3664d5b6c..7256544ff54c 100644 --- a/drivers/pnp/isapnp.c +++ b/drivers/pnp/isapnp.c @@ -746,6 +746,25 @@ static void __init isapnp_add_fixed_mem32_resource(struct pci_dev *dev, (*res)->mem32 = mem32; } +/* + * Parse card name for ISA PnP device. + */ + +static void __init +isapnp_parse_name(char *name, unsigned int name_max, unsigned short *size) +{ + if (name[0] == '\0') { + unsigned short size1 = *size >= name_max ? (name_max - 1) : *size; + isapnp_peek(name, size1); + name[size1] = '\0'; + *size -= size1; + + /* clean whitespace from end of string */ + while (size1 > 0 && name[--size1] == ' ') + name[size1] = '\0'; + } +} + /* * Parse resource map for logical device. */ @@ -843,12 +862,7 @@ static int __init isapnp_create_device(struct pci_bus *card, size = 0; break; case _LTAG_ANSISTR: - if (dev->name[0] == '\0') { - unsigned short size1 = size > 47 ? 47 : size; - isapnp_peek(dev->name, size1); - dev->name[size1] = '\0'; - size -= size1; - } + isapnp_parse_name(dev->name, sizeof(dev->name), &size); break; case _LTAG_UNICODESTR: /* silently ignore */ @@ -914,12 +928,7 @@ static void __init isapnp_parse_resource_map(struct pci_bus *card) case _STAG_VENDOR: break; case _LTAG_ANSISTR: - if (card->name[0] == '\0') { - unsigned short size1 = size > 47 ? 47 : size; - isapnp_peek(card->name, size1); - card->name[size1] = '\0'; - size -= size1; - } + isapnp_parse_name(card->name, sizeof(card->name), &size); break; case _LTAG_UNICODESTR: /* silently ignore */ diff --git a/drivers/s390/net/ctc.c b/drivers/s390/net/ctc.c index 4acc6e543622..699eaba377dd 100644 --- a/drivers/s390/net/ctc.c +++ b/drivers/s390/net/ctc.c @@ -1313,7 +1313,7 @@ static int ctc_open(net_device *dev) return -ENOMEM; } init_waitqueue_head(&privptr->channel[i].wait); - privptr->channel[i].tq.next = NULL; + INIT_LIST_HEAD(&privptr->channel[i].tq.list); privptr->channel[i].tq.sync = 0; privptr->channel[i].tq.routine = (void *)(void *)ctc_irq_bh; privptr->channel[i].tq.data = &privptr->channel[i]; diff --git a/drivers/sbus/audio/dmy.c b/drivers/sbus/audio/dmy.c index 560134ba2186..91cc447fb308 100644 --- a/drivers/sbus/audio/dmy.c +++ b/drivers/sbus/audio/dmy.c @@ -547,7 +547,7 @@ static void dummy_start_output(struct sparcaudio_driver *drv, __u8 * buffer, dummy_chip->perchip_info.play.active = 1; /* fake an "interrupt" to deal with this block */ - dummy_chip->tqueue.next = NULL; + INIT_LIST_HEAD(&dummy_chip->tqueue.list); dummy_chip->tqueue.sync = 0; dummy_chip->tqueue.routine = dummy_output_done_task; dummy_chip->tqueue.data = drv; diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c index b305ef12710d..c640c9fe869b 100644 --- a/drivers/sbus/char/aurora.c +++ b/drivers/sbus/char/aurora.c @@ -1,4 +1,4 @@ -/* $Id: aurora.c,v 1.9 2000/11/08 05:33:03 davem Exp $ +/* $Id: aurora.c,v 1.10 2000/12/07 04:35:38 anton Exp $ * linux/drivers/sbus/char/aurora.c -- Aurora multiport driver * * Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro) diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c index 7aa23b8b0bec..5de83158b295 100644 --- a/drivers/sbus/char/sab82532.c +++ b/drivers/sbus/char/sab82532.c @@ -1,4 +1,4 @@ -/* $Id: sab82532.c,v 1.53 2000/11/15 07:28:09 davem Exp $ +/* $Id: sab82532.c,v 1.54 2000/12/07 04:35:39 anton Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -2134,7 +2134,7 @@ static void __init sab82532_kgdb_hook(int line) static inline void __init show_serial_version(void) { - char *revision = "$Revision: 1.53 $"; + char *revision = "$Revision: 1.54 $"; char *version, *p; version = strchr(revision, ' '); diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index c2627d106e1d..6f262b998657 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -30,7 +30,7 @@ CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM -obj-$(CONFIG_SCSI) += scsi_mod.o scsi_syms.o +obj-$(CONFIG_SCSI) += scsi_mod.o obj-$(CONFIG_A4000T_SCSI) += amiga7xx.o 53c7xx.o obj-$(CONFIG_A4091_SCSI) += amiga7xx.o 53c7xx.o @@ -123,6 +123,7 @@ scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o \ scsicam.o scsi_proc.o scsi_error.o \ scsi_obsolete.o scsi_queue.o scsi_lib.o \ scsi_merge.o scsi_dma.o scsi_scan.o \ + scsi_syms.o sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o initio-objs := ini9100u.o i91uscsi.o diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c index 61298e9c084a..cdc3f8989b8a 100644 --- a/drivers/scsi/imm.c +++ b/drivers/scsi/imm.c @@ -51,7 +51,7 @@ typedef struct { mode: IMM_AUTODETECT, \ host: -1, \ cur_cmd: NULL, \ - imm_tq: {0, 0, imm_interrupt, NULL}, \ + imm_tq: { routine: imm_interrupt }, \ jstart: 0, \ failed: 0, \ dp: 0, \ @@ -122,7 +122,14 @@ int imm_detect(Scsi_Host_Template * host) struct Scsi_Host *hreg; int ports; int i, nhosts, try_again; - struct parport *pb = parport_enumerate(); + struct parport *pb; + + /* + * unlock to allow the lowlevel parport driver to probe + * the irqs + */ + spin_unlock_irq(&io_request_lock); + pb = parport_enumerate(); printk("imm: Version %s\n", IMM_VERSION); nhosts = 0; @@ -130,6 +137,7 @@ int imm_detect(Scsi_Host_Template * host) if (!pb) { printk("imm: parport reports no devices.\n"); + spin_lock_irq(&io_request_lock); return 0; } retry_entry: @@ -154,6 +162,7 @@ int imm_detect(Scsi_Host_Template * host) printk(KERN_ERR "imm%d: failed to claim parport because a " "pardevice is owning the port for too longtime!\n", i); + spin_lock_irq(&io_request_lock); return 0; } } @@ -208,12 +217,16 @@ int imm_detect(Scsi_Host_Template * host) nhosts++; } if (nhosts == 0) { - if (try_again == 1) + if (try_again == 1) { + spin_lock_irq(&io_request_lock); return 0; + } try_again = 1; goto retry_entry; - } else + } else { + spin_lock_irq (&io_request_lock); return 1; /* return number of hosts detected */ + } } /* This is to give the imm driver a way to modify the timings (and other diff --git a/drivers/scsi/imm.h b/drivers/scsi/imm.h index 7b8c9df55f87..8c3d65bb89ce 100644 --- a/drivers/scsi/imm.h +++ b/drivers/scsi/imm.h @@ -10,7 +10,7 @@ #ifndef _IMM_H #define _IMM_H -#define IMM_VERSION "2.04 (for Linux 2.4.0)" +#define IMM_VERSION "2.05 (for Linux 2.4.0)" /* * 10 Apr 1998 (Good Friday) - Received EN144302 by email from Iomega. @@ -60,6 +60,9 @@ * added CONFIG_SCSI_IZIP_SLOW_CTR option * [2.03] * Fix kernel panic on scsi timeout. 20Aug00 [2.04] + * + * Avoid io_request_lock problems. + * John Cavan 16Nov00 [2.05] */ /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */ diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index e55b26a0e72d..4d84fabc9b12 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -929,7 +929,7 @@ typedef struct ips_ha { char *ioctl_data; /* IOCTL data area */ u32 ioctl_datasize; /* IOCTL data size */ u32 cmd_in_progress; /* Current command in progress*/ - u32 flags; /* HA flags */ + long flags; /* HA flags */ u8 waitflag; /* are we waiting for cmd */ u8 active; u16 reset_count; /* number of resets */ diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index 661316cacea4..e85391bd1ed4 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -41,7 +41,7 @@ typedef struct { mode: PPA_AUTODETECT, \ host: -1, \ cur_cmd: NULL, \ - ppa_tq: {0, 0, ppa_interrupt, NULL}, \ + ppa_tq: { routine: ppa_interrupt }, \ jstart: 0, \ failed: 0, \ p_busy: 0 \ @@ -111,7 +111,14 @@ int ppa_detect(Scsi_Host_Template * host) struct Scsi_Host *hreg; int ports; int i, nhosts, try_again; - struct parport *pb = parport_enumerate(); + struct parport *pb; + + /* + * unlock to allow the lowlevel parport driver to probe + * the irqs + */ + spin_unlock_irq(&io_request_lock); + pb = parport_enumerate(); printk("ppa: Version %s\n", PPA_VERSION); nhosts = 0; @@ -119,6 +126,7 @@ int ppa_detect(Scsi_Host_Template * host) if (!pb) { printk("ppa: parport reports no devices.\n"); + spin_lock_irq(&io_request_lock); return 0; } retry_entry: @@ -143,6 +151,7 @@ int ppa_detect(Scsi_Host_Template * host) printk(KERN_ERR "ppa%d: failed to claim parport because a " "pardevice is owning the port for too longtime!\n", i); + spin_lock_irq(&io_request_lock); return 0; } } @@ -212,11 +221,14 @@ int ppa_detect(Scsi_Host_Template * host) printk(" cable is marked with \"AutoDetect\", this is what has\n"); printk(" happened.\n"); return 0; + spin_lock_irq(&io_request_lock); } try_again = 1; goto retry_entry; - } else + } else { + spin_lock_irq(&io_request_lock); return 1; /* return number of hosts detected */ + } } /* This is to give the ppa driver a way to modify the timings (and other diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h index 74147c3299a9..0c03366dceb6 100644 --- a/drivers/scsi/ppa.h +++ b/drivers/scsi/ppa.h @@ -10,7 +10,7 @@ #ifndef _PPA_H #define _PPA_H -#define PPA_VERSION "2.05 (for Linux 2.2.x)" +#define PPA_VERSION "2.06 (for Linux 2.2.x)" /* * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu) @@ -58,6 +58,9 @@ * Tim Waugh * [2.04] * Fix kernel panic on scsi timeout, 2000-08-18 [2.05] + * + * Avoid io_request_lock problems. + * John Cavan [2.06] */ /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */ diff --git a/drivers/sound/vwsnd.c b/drivers/sound/vwsnd.c index b79ce2b32817..4db994a45e9d 100644 --- a/drivers/sound/vwsnd.c +++ b/drivers/sound/vwsnd.c @@ -144,6 +144,8 @@ #include #include +#include +#include #include #include #include diff --git a/drivers/sound/ymfpci.c b/drivers/sound/ymfpci.c index e970737d1c41..a8cdc58f2fd3 100644 --- a/drivers/sound/ymfpci.c +++ b/drivers/sound/ymfpci.c @@ -32,6 +32,9 @@ * ? merge ymf_pcm and state * ? pcm interrupt no pointer * ? underused structure members + * - Remove remaining P3 tags (debug messages). + * - Resolve XXX tagged questions. + * - Cannot play 5133Hz. */ #include @@ -59,7 +62,7 @@ static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice); static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice); static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state); -static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance); +static int ymf_state_alloc(ymfpci_t *unit, int nvirt); static LIST_HEAD(ymf_devs); @@ -602,11 +605,9 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) char silence; if ((ypcm = voice->ypcm) == NULL) { -/* P3 */ printk("ymf_pcm_interrupt: voice %d: no ypcm\n", voice->number); return; } if ((state = ypcm->state) == NULL) { -/* P3 */ printk("ymf_pcm_interrupt: voice %d: no state\n", voice->number); ypcm->running = 0; // lock it return; } @@ -628,7 +629,7 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ printk(KERN_ERR "ymfpci%d: %d: runaway: hwptr %d dmasize %d\n", - codec->inst, voice->number, + codec->dev_audio, voice->number, dmabuf->hwptr, dmabuf->dmasize); pos = 0; } @@ -645,7 +646,7 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) if (dmabuf->count == 0) { printk("ymfpci%d: %d: strain: hwptr %d\n", - codec->inst, voice->number, dmabuf->hwptr); + codec->dev_audio, voice->number, dmabuf->hwptr); ymf_playback_trigger(codec, ypcm, 0); } @@ -664,7 +665,7 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) */ printk("ymfpci%d: %d: lost: delta %d" " hwptr %d swptr %d distance %d count %d\n", - codec->inst, voice->number, delta, + codec->dev_audio, voice->number, delta, dmabuf->hwptr, swptr, distance, dmabuf->count); } else { /* @@ -672,7 +673,7 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) */ // printk("ymfpci%d: %d: done: delta %d" // " hwptr %d swptr %d distance %d count %d\n", -// codec->inst, voice->number, delta, +// codec->dev_audio, voice->number, delta, // dmabuf->hwptr, swptr, distance, dmabuf->count); } played = dmabuf->count; @@ -738,7 +739,6 @@ static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd) { if (ypcm->voices[0] == NULL) { -/* P3 */ printk("ymfpci: trigger %d no voice\n", cmd); return -EINVAL; } if (cmd != 0) { @@ -911,7 +911,7 @@ static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state) if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { /* Cannot be unless we leak voices in ymf_release! */ printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n", - codec->inst); + codec->dev_audio); return err; } @@ -1052,7 +1052,7 @@ static void ymf_pcm_free_substream(ymfpci_pcm_t *ypcm) } } -static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance) +static int ymf_state_alloc(ymfpci_t *unit, int nvirt) { ymfpci_pcm_t *ypcm; struct ymf_state *state; @@ -1062,7 +1062,6 @@ static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance) } memset(state, 0, sizeof(struct ymf_state)); - init_waitqueue_head(&state->open_wait); init_waitqueue_head(&state->dmabuf.wait); ypcm = &state->ypcm; @@ -1541,12 +1540,13 @@ static int ymf_ioctl(struct inode *inode, struct file *file, return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_RESET: - /* FIXME: spin_lock ? */ if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); + spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); } #if HAVE_RECORD if (file->f_mode & FMODE_READ) { @@ -1576,9 +1576,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file, case SNDCTL_DSP_SPEED: /* set smaple rate */ if (get_user(val, (int *)arg)) return -EFAULT; - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SPEED %d\n", val); */ if (val >= 8000 && val <= 48000) { - spin_lock_irqsave(&state->unit->reg_lock, flags); if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); } @@ -1587,6 +1585,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file, stop_adc(state); } #endif + spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; state->format.rate = val; ymf_pcm_update_shift(&state->format); @@ -1603,7 +1602,6 @@ static int ymf_ioctl(struct inode *inode, struct file *file, case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ if (get_user(val, (int *)arg)) return -EFAULT; - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_STEREO %d\n", val); */ if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); spin_lock_irqsave(&state->unit->reg_lock, flags); @@ -1625,7 +1623,6 @@ static int ymf_ioctl(struct inode *inode, struct file *file, return 0; case SNDCTL_DSP_GETBLKSIZE: - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETBLKSIZE\n"); */ if (file->f_mode & FMODE_WRITE) { if ((val = prog_dmabuf(state, 0))) return val; @@ -1639,15 +1636,12 @@ static int ymf_ioctl(struct inode *inode, struct file *file, return -EINVAL; case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETFMTS\n"); */ return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); case SNDCTL_DSP_SETFMT: /* Select sample format */ if (get_user(val, (int *)arg)) return -EFAULT; - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETFMT 0x%x\n", val); */ if (val == AFMT_S16_LE || val == AFMT_U8) { - spin_lock_irqsave(&state->unit->reg_lock, flags); if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); } @@ -1656,6 +1650,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file, stop_adc(state); } #endif + spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; state->format.format = val; ymf_pcm_update_shift(&state->format); @@ -1668,22 +1663,24 @@ static int ymf_ioctl(struct inode *inode, struct file *file, return -EFAULT; /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */ if (val != 0) { - spin_lock_irqsave(&state->unit->reg_lock, flags); if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); if (val == 1 || val == 2) { + spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; state->format.voices = val; ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); } } #if HAVE_RECORD if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&state->unit->reg_lock, flags); stop_adc(state); dmabuf->ready = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); } #endif - spin_unlock_irqrestore(&state->unit->reg_lock, flags); } return put_user(state->format.voices, (int *)arg); @@ -1737,7 +1734,6 @@ static int ymf_ioctl(struct inode *inode, struct file *file, return 0; case SNDCTL_DSP_GETOSPACE: - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOSPACE\n"); */ if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) @@ -1768,12 +1764,10 @@ static int ymf_ioctl(struct inode *inode, struct file *file, #endif case SNDCTL_DSP_NONBLOCK: - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_NONBLOCK\n"); */ file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETCAPS: - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETCAPS\n"); */ /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, (int *)arg); */ return put_user(0, (int *)arg); @@ -1826,7 +1820,6 @@ static int ymf_ioctl(struct inode *inode, struct file *file, #endif case SNDCTL_DSP_GETOPTR: - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOPTR\n"); */ if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; spin_lock_irqsave(&state->unit->reg_lock, flags); @@ -1840,7 +1833,6 @@ static int ymf_ioctl(struct inode *inode, struct file *file, return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_SETDUPLEX: /* XXX TODO */ - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETDUPLEX\n"); */ return -EINVAL; #if 0 /* old */ @@ -1871,7 +1863,6 @@ static int ymf_ioctl(struct inode *inode, struct file *file, return -ENOTTY; default: - /* P3 */ printk(KERN_WARNING "ymfpci: unknown ioctl cmd 0x%x\n", cmd); /* * Some programs mix up audio devices and ioctls * or perhaps they expect "universal" ioctls, @@ -1886,7 +1877,7 @@ static int ymf_open(struct inode *inode, struct file *file) { struct list_head *list; ymfpci_t *unit; - int minor, instance; + int minor; struct ymf_state *state; int nvirt; int err; @@ -1903,24 +1894,24 @@ static int ymf_open(struct inode *inode, struct file *file) } else { return -ENXIO; } - instance = (minor >> 4) & 0x0F; nvirt = 0; /* Such is the partitioning of minor */ - /* XXX Semaphore here! */ for (list = ymf_devs.next; list != &ymf_devs; list = list->next) { unit = list_entry(list, ymfpci_t, ymf_devs); - if (unit->inst == instance) + if (((unit->dev_audio ^ minor) & ~0x0F) == 0) break; } if (list == &ymf_devs) return -ENODEV; + down(&unit->open_sem); if (unit->states[nvirt] != NULL) { - /* P3 */ printk("ymfpci%d: busy\n", unit->inst); + up(&unit->open_sem); return -EBUSY; } - if ((err = ymf_state_alloc(unit, nvirt, instance)) != 0) { + if ((err = ymf_state_alloc(unit, nvirt)) != 0) { + up(&unit->open_sem); return err; } state = unit->states[nvirt]; @@ -1940,6 +1931,7 @@ static int ymf_open(struct inode *inode, struct file *file) unit->states[state->virt] = NULL; kfree(state); + up(&unit->open_sem); return err; } @@ -1948,6 +1940,8 @@ static int ymf_open(struct inode *inode, struct file *file) ymfpci_writeb(codec, YDSXGR_TIMERCTRL, (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN)); #endif + up(&unit->open_sem); + /* XXX Is it correct to have MOD_INC_USE_COUNT outside of sem.? */ MOD_INC_USE_COUNT; return 0; @@ -1962,14 +1956,14 @@ static int ymf_release(struct inode *inode, struct file *file) ymfpci_writeb(codec, YDSXGR_TIMERCTRL, 0); #endif - /* XXX Use the semaphore to unrace us with opens */ - if (state != codec->states[state->virt]) { printk(KERN_ERR "ymfpci%d.%d: state mismatch\n", - state->unit->inst, state->virt); + state->unit->dev_audio, state->virt); return -EIO; } + down(&codec->open_sem); + /* * XXX Solve the case of O_NONBLOCK close - don't deallocate here. * Deallocate when unloading the driver and we can wait. @@ -1981,6 +1975,8 @@ static int ymf_release(struct inode *inode, struct file *file) codec->states[state->virt] = NULL; kfree(state); + up(&codec->open_sem); + MOD_DEC_USE_COUNT; return 0; } @@ -2235,7 +2231,6 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97) codec->codec_write = ymfpci_codec_write; if (ac97_probe_codec(codec) == 0) { - /* Alan does not have this printout. P3 */ printk("ymfpci: ac97_probe_codec failed\n"); goto out_kfree; } @@ -2264,7 +2259,6 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97) static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent) { u16 ctrl; - static int ymf_instance; /* = 0 */ ymfpci_t *codec; int err; @@ -2282,13 +2276,13 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi spin_lock_init(&codec->reg_lock); spin_lock_init(&codec->voice_lock); + init_MUTEX(&codec->open_sem); codec->pci = pcidev; - codec->inst = ymf_instance; - pci_read_config_byte(pcidev, PCI_REVISION_ID, (u8 *)&codec->rev); + pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev); codec->reg_area_virt = ioremap(pci_resource_start(pcidev, 0), 0x8000); - printk(KERN_INFO "ymfpci%d: %s at 0x%lx IRQ %d\n", ymf_instance, + printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n", (char *)ent->driver_data, pci_resource_start(pcidev, 0), pcidev->irq); ymfpci_aclink_reset(pcidev); @@ -2306,13 +2300,14 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) { printk(KERN_ERR "ymfpci%d: unable to request IRQ %d\n", - codec->inst, pcidev->irq); + codec->dev_audio, pcidev->irq); goto out_memfree; } /* register /dev/dsp */ if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) { - printk(KERN_ERR "ymfpci%d: unable to register dsp\n", codec->inst); + printk(KERN_ERR "ymfpci%d: unable to register dsp\n", + codec->dev_audio); goto out_free_irq; } @@ -2325,7 +2320,6 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi /* put it into driver list */ list_add_tail(&codec->ymf_devs, &ymf_devs); pci_set_drvdata(pcidev, codec); - ymf_instance++; return 0; diff --git a/drivers/sound/ymfpci.h b/drivers/sound/ymfpci.h index 7ea633ca09b2..6e1a8d5f3967 100644 --- a/drivers/sound/ymfpci.h +++ b/drivers/sound/ymfpci.h @@ -247,7 +247,7 @@ struct ymf_pcm { }; struct ymf_unit { - unsigned int rev; /* PCI revision */ + u8 rev; /* PCI revision */ void *reg_area_virt; void *work_ptr; // + @@ -275,13 +275,13 @@ struct ymf_unit { u16 ac97_features; struct pci_dev *pci; - int inst; /* Unit number (instance) */ spinlock_t reg_lock; spinlock_t voice_lock; /* soundcore stuff */ int dev_audio; + struct semaphore open_sem; struct list_head ymf_devs; struct ymf_state *states[1]; // * @@ -332,10 +332,6 @@ struct ymf_pcm_format { struct ymf_state { struct ymf_unit *unit; /* backpointer */ - /* single open lock mechanism, only used for recording */ - struct semaphore open_sem; - wait_queue_head_t open_wait; - /* virtual channel number */ int virt; // * unused a.t.m. diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 0592297dc4b9..060ee5a69cf6 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1738,7 +1738,7 @@ dbg( "digi_startup: TOP" ); init_waitqueue_head( &priv->dp_flush_wait ); priv->dp_in_close = 0; init_waitqueue_head( &priv->dp_close_wait ); - priv->dp_wakeup_task.next = NULL; + INIT_LIST_HEAD(&priv->dp_wakeup_task.list); priv->dp_wakeup_task.sync = 0; priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock; priv->dp_wakeup_task.data = (void *)(&serial->port[i]); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index b4ee5fdc111d..8d7c9c035ca5 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -49,7 +49,6 @@ */ -#include #include #include #include diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 0d7b4545ae32..0cd2a70b0579 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -34,6 +34,7 @@ #ifndef __LINUX_USB_SERIAL_KEYSPAN_H #define __LINUX_USB_SERIAL_KEYSPAN_H +#include /* Function prototypes for Keyspan serial converter */ static int keyspan_open (struct usb_serial_port *port, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index b614a6d7e5c2..b94c9766a3db 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -742,11 +742,11 @@ static int keyspan_pda_startup (struct usb_serial *serial) if (!priv) return (1); /* error */ init_waitqueue_head(&serial->port[0].write_wait); - priv->wakeup_task.next = NULL; + INIT_LIST_HEAD(&priv->wakeup_task.list); priv->wakeup_task.sync = 0; priv->wakeup_task.routine = (void *)keyspan_pda_wakeup_write; priv->wakeup_task.data = (void *)(&serial->port[0]); - priv->unthrottle_task.next = NULL; + INIT_LIST_HEAD(&priv->unthrottle_task.list); priv->unthrottle_task.sync = 0; priv->unthrottle_task.routine = (void *)keyspan_pda_request_unthrottle; priv->unthrottle_task.data = (void *)(serial); diff --git a/drivers/video/mdacon.c b/drivers/video/mdacon.c index d988d9e39984..98e58b46c628 100644 --- a/drivers/video/mdacon.c +++ b/drivers/video/mdacon.c @@ -77,10 +77,8 @@ static int mda_last_vc = 16; static struct vc_data *mda_display_fg = NULL; -#ifdef MODULE_PARM MODULE_PARM(mda_first_vc, "1-255i"); MODULE_PARM(mda_last_vc, "1-255i"); -#endif /* MDA register values */ @@ -200,11 +198,7 @@ void __init mdacon_setup(char *str, int *ints) } #endif -#ifdef MODULE -static int mda_detect(void) -#else static int __init mda_detect(void) -#endif { int count=0; u16 *p, p_save; @@ -287,11 +281,7 @@ static int __init mda_detect(void) return 1; } -#ifdef MODULE -static void mda_initialize(void) -#else static void __init mda_initialize(void) -#endif { write_mda_b(97, 0x00); /* horizontal total */ write_mda_b(80, 0x01); /* horizontal displayed */ @@ -316,11 +306,7 @@ static void __init mda_initialize(void) outb_p(0x00, mda_gfx_port); } -#ifdef MODULE -static const char *mdacon_startup(void) -#else static const char __init *mdacon_startup(void) -#endif { mda_num_columns = 80; mda_num_lines = 25; @@ -605,11 +591,7 @@ const struct consw mda_con = { con_invert_region: mdacon_invert_region, }; -#ifdef MODULE -void mda_console_init(void) -#else void __init mda_console_init(void) -#endif { if (mda_first_vc > mda_last_vc) return; diff --git a/fs/buffer.c b/fs/buffer.c index dbe463e0502f..8e2a382c3713 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2040,7 +2040,6 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], int pageind; int bhind; int offset; - int sectors = size>>9; unsigned long blocknr; struct kiobuf * iobuf = NULL; struct page * map; @@ -2092,9 +2091,8 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], tmp->b_this_page = tmp; init_buffer(tmp, end_buffer_io_kiobuf, iobuf); - tmp->b_rdev = tmp->b_dev = dev; + tmp->b_dev = dev; tmp->b_blocknr = blocknr; - tmp->b_rsector = blocknr*sectors; tmp->b_state = (1 << BH_Mapped) | (1 << BH_Lock) | (1 << BH_Req); if (rw == WRITE) { @@ -2108,7 +2106,7 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], atomic_inc(&iobuf->io_count); - generic_make_request(rw, tmp); + submit_bh(rw, tmp); /* * Wait for IO if we have got too much */ diff --git a/fs/ncpfs/Config.in b/fs/ncpfs/Config.in index 104f5a3c739f..1f1eb12787f7 100644 --- a/fs/ncpfs/Config.in +++ b/fs/ncpfs/Config.in @@ -7,7 +7,5 @@ dep_mbool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG $CONFI dep_mbool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS $CONFIG_NCP_FS dep_mbool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS $CONFIG_NCP_FS dep_mbool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS $CONFIG_NCP_FS -dep_mbool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR $CONFIG_NCP_FS -dep_mbool ' NDS authentication support' CONFIG_NCPFS_NDS_DOMAINS $CONFIG_NCP_FS dep_mbool ' Use Native Language Support' CONFIG_NCPFS_NLS $CONFIG_NCP_FS dep_mbool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS $CONFIG_NCP_FS diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 4b6afe2e9c61..37c87f8b203c 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -165,7 +165,6 @@ int ncp_ioctl(struct inode *inode, struct file *filp, return 0; } -#ifdef CONFIG_NCPFS_MOUNT_SUBDIR case NCP_IOC_GETROOT: { struct ncp_setroot_ioctl sr; @@ -241,7 +240,6 @@ int ncp_ioctl(struct inode *inode, struct file *filp, return 0; } -#endif /* CONFIG_NCPFS_MOUNT_SUBDIR */ #ifdef CONFIG_NCPFS_PACKET_SIGNING case NCP_IOC_SIGN_INIT: @@ -374,7 +372,6 @@ outrel: } #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ -#ifdef CONFIG_NCPFS_NDS_DOMAINS case NCP_IOC_GETOBJECTNAME: if (current->uid != server->m.mounted_uid) { return -EACCES; @@ -503,7 +500,6 @@ outrel: if (old) ncp_kfree_s(old, oldlen); return 0; } -#endif /* CONFIG_NCPFS_NDS_DOMAINS */ #ifdef CONFIG_NCPFS_NLS /* Here we are select the iocharset and the codepage for NLS. diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 6d07ead736e1..162c89b4b4f4 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -96,16 +96,14 @@ ncp_ClearPhysicalRecord(struct ncp_server *server, __u32 offset, __u32 length); #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ -#ifdef CONFIG_NCPFS_MOUNT_SUBDIR int ncp_mount_subdir(struct ncp_server *, struct nw_info_struct *, __u8, __u8, __u32); -#endif /* CONFIG_NCPFS_MOUNT_SUBDIR */ #ifdef CONFIG_NCPFS_NLS -inline unsigned char ncp__tolower(struct nls_table *, unsigned char); -inline unsigned char ncp__toupper(struct nls_table *, unsigned char); +unsigned char ncp__tolower(struct nls_table *, unsigned char); +unsigned char ncp__toupper(struct nls_table *, unsigned char); int ncp__io2vol(struct ncp_server *, unsigned char *, unsigned int *, const unsigned char *, unsigned int, int); int ncp__vol2io(struct ncp_server *, unsigned char *, unsigned int *, diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 029b673f50dc..f741cb6ddfe2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -594,6 +594,11 @@ nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr) nfs_refresh_inode(inode, fattr); } +struct nfs_find_desc { + struct nfs_fh *fh; + struct nfs_fattr *fattr; +}; + /* * In NFSv3 we can have 64bit inode numbers. In order to support * this, and re-exported directories (also seen in NFSv2) @@ -603,13 +608,16 @@ nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr) static int nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque) { - struct nfs_fattr *fattr = (struct nfs_fattr *)opaque; + struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque; + struct nfs_fh *fh = desc->fh; + struct nfs_fattr *fattr = desc->fattr; if (NFS_FSID(inode) != fattr->fsid) return 0; if (NFS_FILEID(inode) != fattr->fileid) return 0; - + if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0) + return 0; return 1; } @@ -640,8 +648,6 @@ nfs_inode_is_stale(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fat * the vfs read_inode function because there is no way to pass the * file handle or current attributes into the read_inode function. * - * We provide a special check for NetApp .snapshot directories to avoid - * inode aliasing problems. All snapshot inodes are anonymous (unhashed). */ struct inode * nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle, @@ -652,41 +658,16 @@ nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle, dprintk("NFS: nfs_fhget(%s/%s fileid=%Ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, (long long)fattr->fileid); - -#ifdef CONFIG_NFS_SNAPSHOT - /* - * Check for NetApp snapshot dentries, and get an - * unhashed inode to avoid aliasing problems. - */ - if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) || - (dentry->d_name.len == 9 && - memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) { - struct inode *inode = new_inode(sb); - if (!inode) - goto out; - inode->i_ino = nfs_fattr_to_ino_t(fattr); - nfs_read_inode(inode); - nfs_fill_inode(inode, fhandle, fattr); - inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT; - dprintk("NFS: nfs_fhget(snapshot ino=%ld)\n", inode->i_ino); - out: - return inode; - } -#endif return __nfs_fhget(sb, fhandle, fattr); } /* * Look up the inode by super block and fattr->fileid. - * - * Note carefully the special handling of busy inodes (i_count > 1). - * With the kernel 2.1.xx dcache all inodes except hard links must - * have i_count == 1 after iget(). Otherwise, it indicates that the - * server has reused a fileid (i_ino) and we have a stale inode. */ static struct inode * __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) { + struct nfs_find_desc desc = { fh, fattr }; struct inode *inode = NULL; unsigned long ino; @@ -700,29 +681,7 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ino = nfs_fattr_to_ino_t(fattr); - while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) { - - /* - * Check for busy inodes, and attempt to get rid of any - * unused local references. If successful, we release the - * inode and try again. - * - * Note that the busy test uses the values in the fattr, - * as the inode may have become a different object. - * (We can probably handle modes changes here, too.) - */ - if (!nfs_inode_is_stale(inode, fh, fattr)) - break; - - dprintk("__nfs_fhget: inode (%x/%Ld) still busy, i_count=%d\n", - inode->i_dev, (long long)NFS_FILEID(inode), - atomic_read(&inode->i_count)); - nfs_zap_caches(inode); - remove_inode_hash(inode); - iput(inode); - } - - if (!inode) + if (!(inode = iget4(sb, ino, nfs_find_actor, &desc))) goto out_no_inode; nfs_fill_inode(inode, fh, fattr); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 0e850ba3e33b..e0f7313be780 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -116,8 +116,8 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) lock_kernel(); result = NFS_PROTO(inode)->read(inode, cred, &fattr, flags, offset, rsize, buffer, &eof); - unlock_kernel(); nfs_refresh_inode(inode, &fattr); + unlock_kernel(); /* * Even if we had a partial success we can't mark the page diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 96509df07dd5..e46ef0b25fc7 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -43,7 +43,7 @@ static struct file_operations proc_file_operations = { #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif -/* 4K page size but our output routines use some slack for overruns */ +/* buffer size is one page but our output routines use some slack for overruns */ #define PROC_BLOCK_SIZE (PAGE_SIZE - 1024) static ssize_t diff --git a/fs/readdir.c b/fs/readdir.c index cd8f7ad3d4d9..16eba638327b 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -325,4 +325,3 @@ out_putf: out: return error; } - diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c index 9e6b8222b1ff..cb1e697fc131 100644 --- a/fs/smbfs/sock.c +++ b/fs/smbfs/sock.c @@ -163,7 +163,7 @@ smb_data_ready(struct sock *sk, int len) found_data(sk); return; } - job->cb.next = NULL; + INIT_LIST_HEAD(&job->cb.list); job->cb.sync = 0; job->cb.routine = smb_data_callback; job->cb.data = job; diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index a655468204c8..b98102522d0d 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -26,7 +26,7 @@ static inline pte_t ptep_get_and_clear(pte_t *ptep) return pte; } -static inline void ptep_clear_wrprotect(pte_t *ptep) +static inline void ptep_set_wrprotect(pte_t *ptep) { pte_t old_pte = *ptep; set_pte(ptep, pte_wrprotect(old_pte)); diff --git a/include/asm-i386/apic.h b/include/asm-i386/apic.h index 733ec4cc0597..abb627a41280 100644 --- a/include/asm-i386/apic.h +++ b/include/asm-i386/apic.h @@ -10,7 +10,7 @@ #ifdef CONFIG_X86_LOCAL_APIC #if APIC_DEBUG -#define Dprintk(x...) printk(##x) +#define Dprintk(x...) printk(x) #else #define Dprintk(x...) #endif diff --git a/include/asm-i386/msr.h b/include/asm-i386/msr.h index 515a8dc5ccb2..72bf155e92ca 100644 --- a/include/asm-i386/msr.h +++ b/include/asm-i386/msr.h @@ -30,3 +30,7 @@ : "=a" (low), "=d" (high) \ : "c" (counter)) +/* symbolic names for some interesting MSRs */ +#define MSR_IA32_PLATFORM_ID 0x17 +#define MSR_IA32_UCODE_WRITE 0x79 +#define MSR_IA32_UCODE_REV 0x8B diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index 1fc0a0b9ac1d..5d7d5717a63c 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -283,7 +283,7 @@ static inline pte_t pte_mkwrite(pte_t pte) { (pte).pte_low |= _PAGE_RW; return p static inline int ptep_test_and_clear_dirty(pte_t *ptep) { return test_and_clear_bit(_PAGE_BIT_DIRTY, ptep); } static inline int ptep_test_and_clear_young(pte_t *ptep) { return test_and_clear_bit(_PAGE_BIT_ACCESSED, ptep); } -static inline void ptep_clear_wrprotect(pte_t *ptep) { clear_bit(_PAGE_BIT_RW, ptep); } +static inline void ptep_set_wrprotect(pte_t *ptep) { clear_bit(_PAGE_BIT_RW, ptep); } static inline void ptep_mkdirty(pte_t *ptep) { set_bit(_PAGE_BIT_RW, ptep); } /* diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 9e8e8c5effde..a03dd00275ef 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -464,7 +464,8 @@ struct microcode { unsigned int bits[500]; }; -#define MICROCODE_IOCFREE _IO('6',0) /* because it is for P6 */ +/* '6' because it used to be for P6 only (but now covers Pentium 4 as well) */ +#define MICROCODE_IOCFREE _IO('6',0) /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ extern inline void rep_nop(void) diff --git a/include/asm-sparc/hdreg.h b/include/asm-sparc/hdreg.h index 1c321c3e7d2c..8df7a4693fb6 100644 --- a/include/asm-sparc/hdreg.h +++ b/include/asm-sparc/hdreg.h @@ -1,4 +1,4 @@ -/* $Id: hdreg.h,v 1.1 2000/01/21 04:56:27 zaitcev Exp $ +/* $Id: hdreg.h,v 1.2 2000/12/05 00:56:36 anton Exp $ * hdreg.h: SPARC PCI specific IDE glue. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -8,6 +8,6 @@ #ifndef __SPARC_HDREG_H #define __SPARC_HDREG_H -typedef unsigned int ide_ioreg_t; +typedef unsigned long ide_ioreg_t; #endif /* __SPARC_HDREG_H */ diff --git a/include/asm-sparc/processor.h b/include/asm-sparc/processor.h index 278c15c801f9..41b96727ad85 100644 --- a/include/asm-sparc/processor.h +++ b/include/asm-sparc/processor.h @@ -1,4 +1,4 @@ -/* $Id: processor.h,v 1.77 2000/01/21 11:39:17 jj Exp $ +/* $Id: processor.h,v 1.78 2000/11/30 08:37:31 anton Exp $ * include/asm-sparc/processor.h * * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) @@ -90,6 +90,7 @@ struct thread_struct { #define SPARC_FLAG_KTHREAD 0x1 /* task is a kernel thread */ #define SPARC_FLAG_UNALIGNED 0x2 /* is allowed to do unaligned accesses */ +#define SPARC_FLAG_MMAPSHARED 0x4 /* task wants a shared mmap */ #define INIT_MMAP { &init_mm, (0), (0), \ NULL, __pgprot(0x0) , VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL } diff --git a/include/asm-sparc64/pgalloc.h b/include/asm-sparc64/pgalloc.h index 23603f287510..45e1f1828a4d 100644 --- a/include/asm-sparc64/pgalloc.h +++ b/include/asm-sparc64/pgalloc.h @@ -1,4 +1,4 @@ -/* $Id: pgalloc.h,v 1.13 2000/11/06 06:59:04 davem Exp $ */ +/* $Id: pgalloc.h,v 1.14 2000/12/09 04:15:24 anton Exp $ */ #ifndef _SPARC64_PGALLOC_H #define _SPARC64_PGALLOC_H @@ -18,10 +18,15 @@ #define flush_cache_page(vma, page) \ flush_cache_mm((vma)->vm_mm) -/* These operations are unnecessary on the SpitFire since D-CACHE is write-through. */ -#define flush_icache_range(start, end) do { } while (0) +/* This is unnecessary on the SpitFire since D-CACHE is write-through. */ #define flush_page_to_ram(page) do { } while (0) +/* + * icache doesnt snoop local stores and we don't use block commit stores + * (which invalidate icache lines) during module load, so we need this. + */ +extern void flush_icache_range(unsigned long start, unsigned long end); + extern void __flush_dcache_page(void *addr, int flush_icache); #define flush_dcache_page(page) \ do { if ((page)->mapping && !(page)->mapping->i_mmap && !(page)->mapping->i_mmap_shared) \ diff --git a/include/asm-sparc64/processor.h b/include/asm-sparc64/processor.h index eae8b7bdb62b..562cf4ba8237 100644 --- a/include/asm-sparc64/processor.h +++ b/include/asm-sparc64/processor.h @@ -1,4 +1,4 @@ -/* $Id: processor.h,v 1.65 2000/08/09 00:00:17 davem Exp $ +/* $Id: processor.h,v 1.66 2000/11/29 05:56:12 anton Exp $ * include/asm-sparc64/processor.h * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -80,6 +80,7 @@ struct thread_struct { #define SPARC_FLAG_32BIT 0x04 /* task is older 32-bit binary */ #define SPARC_FLAG_NEWCHILD 0x08 /* task is just-spawned child process */ #define SPARC_FLAG_PERFCTR 0x10 /* task has performance counters active */ +#define SPARC_FLAG_MMAPSHARED 0x20 /* task wants a shared mmap */ #define FAULT_CODE_WRITE 0x01 /* Write access, implies D-TLB */ #define FAULT_CODE_DTLB 0x02 /* Miss happened in D-TLB */ diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index 4d5ada52b6e0..d169bf7f22c9 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -257,7 +257,7 @@ enum { #define ATM_ATMOPT_CLP 1 /* set CLP bit */ -typedef struct { unsigned int bits; } atm_vcc_flags_t; +typedef struct { unsigned long bits; } atm_vcc_flags_t; struct atm_vcc { diff --git a/include/linux/module.h b/include/linux/module.h index 61cbe14d80e5..7f416d7fb9b7 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -168,6 +168,7 @@ struct module_info * Keith Owens 28 Oct 2000. */ +#ifdef __KERNEL__ #define HAVE_INTER_MODULE extern void inter_module_register(const char *, struct module *, const void *); extern void inter_module_unregister(const char *); @@ -183,6 +184,7 @@ struct inter_module_entry { }; extern int try_inc_mod_count(struct module *mod); +#endif /* __KERNEL__ */ #if defined(MODULE) && !defined(__GENKSYMS__) diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 0ed87cdb4fa5..696938aa1853 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -2,7 +2,7 @@ /* Linux driver for Disk-On-Chip 2000 */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: doc2000.h,v 1.8 2000/07/10 15:46:29 dwmw2 Exp $ */ +/* $Id: doc2000.h,v 1.12 2000/11/03 12:43:43 dwmw2 Exp $ */ #ifndef __MTD_DOC2000_H__ #define __MTD_DOC2000_H__ @@ -44,16 +44,24 @@ * Others use readb/writeb */ #if defined(__arm__) -#define ReadDOC(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+(DoC_##reg<<2)))) -#define WriteDOC(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+(DoC_##reg<<2)) = (__u32)d} while(0) +#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+(reg<<2)))) +#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+(reg<<2)) = (__u32)d} while(0) #elif defined(__ppc__) -#define ReadDOC(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+(DoC_##reg<<1)))) -#define WriteDOC(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+(DoC_##reg<<1)) = (__u16)d} while(0) +#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+(reg<<1)))) +#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+(reg<<1)) = (__u16)d} while(0) #else -#define ReadDOC(adr, reg) readb(((unsigned long)adr) + DoC_##reg) -#define WriteDOC(d, adr, reg) writeb(d, ((unsigned long)adr) + DoC_##reg) +#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + reg) +#define WriteDOC_(d, adr, reg) writeb(d, ((unsigned long)adr) + reg) #endif +#if defined(__i386__) +#define USE_MEMCPY +#endif + +/* These are provided to directly use the DoC_xxx defines */ +#define ReadDOC(adr, reg) ReadDOC_(adr,DoC_##reg) +#define WriteDOC(d, adr, reg) WriteDOC_(d,adr,DoC_##reg) + #define DOC_MODE_RESET 0 #define DOC_MODE_NORMAL 1 #define DOC_MODE_RESERVED1 2 @@ -80,9 +88,10 @@ #define DOC_TOGGLE_BIT 0x04 #define DOC_ECC_RESV 0x02 #define DOC_ECC_IGNORE 0x01 + /* We have to also set the reserved bit 1 for enable */ #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV) -#define DOC_ECC_DIS (DOC_ECC_IGNORE | DOC_ECC_RESV) +#define DOC_ECC_DIS (DOC_ECC_RESV) struct Nand { char floor, chip; @@ -97,15 +106,23 @@ struct Nand { #define MAX_FLOORS_MIL 4 #define MAX_CHIPS_MIL 1 +#define ADDR_COLUMN 1 +#define ADDR_PAGE 2 +#define ADDR_COLUMN_PAGE 3 + struct DiskOnChip { unsigned long physadr; unsigned long virtadr; unsigned long totlen; char ChipID; /* Type of DiskOnChip */ + int ioreg; unsigned long mfr; /* Flash IDs - only one type of flash per device */ unsigned long id; int chipshift; + char page256; + char pageadrlen; + unsigned long erasesize; int curfloor; int curchip; @@ -115,5 +132,6 @@ struct DiskOnChip { struct mtd_info *nextdoc; }; +int doc_decode_ecc(unsigned char sector[512], unsigned char ecc1[6]); #endif /* __MTD_DOC2000_H__ */ diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index ebb41c973a8b..7051698000d9 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -1,6 +1,6 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.5 2000/06/26 16:18:58 dwmw2 Exp $ */ +/* $Id: map.h,v 1.10 2000/12/04 13:18:33 dwmw2 Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ @@ -42,6 +42,8 @@ struct map_info { void (*write16)(struct map_info *, __u16, unsigned long); void (*write32)(struct map_info *, __u32, unsigned long); void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t); + + void (*set_vpp)(int); /* We put these two here rather than a single void *map_priv, because we want mappers to be able to have quickly-accessible cache for the 'currently-mapped page' without the _extra_ @@ -54,6 +56,7 @@ struct map_info { const char *im_name; }; +#ifdef CONFIG_MODULES /* * Probe for the contents of a map device and make an MTD structure * if anything is recognised. Doesn't register it because the calling @@ -78,6 +81,18 @@ static inline struct mtd_info *do_map_probe(struct map_info *map, const char *fu #define do_jedec_probe(x) do_map_probe(x, "jedec_probe", "jedec_probe") #define do_ram_probe(x) do_map_probe(x, "map_ram_probe", "map_ram") #define do_rom_probe(x) do_map_probe(x, "map_rom_probe", "map_rom") +#else + /* without module support, call probe function directly */ +extern struct mtd_info *cfi_probe(struct map_info *); +extern struct mtd_info *jedec_probe(struct map_info *); +extern struct mtd_info *map_ram_probe(struct map_info *); +extern struct mtd_info *map_rom_probe(struct map_info *); + +#define do_cfi_probe(x) cfi_probe(x) +#define do_jedec_probe(x) jedec_probe(x) +#define do_ram_probe(x) map_ram_probe(x) +#define do_rom_probe(x) map_rom_probe(x) +#endif /* * Destroy an MTD device which was created for a map device. @@ -92,5 +107,7 @@ static inline void map_destroy(struct mtd_info *mtd) kfree(mtd); } +#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(1); } while(0) +#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(0); } while(0) #endif /* __LINUX_MTD_MAP_H__ */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index a7bc521c2196..708f4f203091 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -1,5 +1,5 @@ -/* $Id: mtd.h,v 1.17 2000/07/04 07:24:49 jgg Exp $ */ +/* $Id: mtd.h,v 1.26 2000/10/30 17:18:04 sjhill Exp $ */ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ @@ -10,6 +10,7 @@ #include #include #include +#include #endif /* __KERNEL__ */ @@ -61,7 +62,8 @@ struct mtd_oob_buf { // Types of automatic ECC/Checksum available #define MTD_ECC_NONE 0 // No automatic ECC available -#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip +#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip +#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices struct mtd_info_user { u_char type; @@ -78,6 +80,8 @@ struct mtd_info_user { #define MEMERASE _IOW('M', 2, struct erase_info_user) #define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) #define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) #ifndef __KERNEL__ @@ -123,6 +127,7 @@ struct mtd_info { // Kernel-only stuff starts here. char *name; + int index; u_long bank_size; @@ -144,9 +149,22 @@ struct mtd_info { int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); - + + /* iovec-based read/write methods. We need these especially for NAND flash, + with its limited number of write cycles per erase. + NB: The 'count' parameter is the number of _vectors_, each of + which contains an (ofs, len) tuple. + */ + int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen); + int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); + + /* Sync */ void (*sync) (struct mtd_info *mtd); + /* Chip-supported device locking */ + int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); + int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); + /* Power Management functions */ int (*suspend) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd); @@ -198,20 +216,32 @@ extern int unregister_mtd_user (struct mtd_notifier *old); #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg) #define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args) #define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args) +#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args) +#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args) +#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args) +#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args) #define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args) #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args) #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0) #endif /* MTDC */ -/* Debugging macros */ - -#ifdef DEBUGLVL -#define DEBUG(n, args...) if (DEBUGLVL>(n)) printk(KERN_DEBUG args) -#else +/* + * Debugging macro and defines + */ +#define MTD_DEBUG_LEVEL0 (0) /* Quiet */ +#define MTD_DEBUG_LEVEL1 (1) /* Audible */ +#define MTD_DEBUG_LEVEL2 (2) /* Loud */ +#define MTD_DEBUG_LEVEL3 (3) /* Noisy */ + +#ifdef CONFIG_MTD_DEBUG +#define DEBUG(n, args...) \ + if (n <= CONFIG_MTD_DEBUG_VERBOSE) { \ + printk(KERN_INFO args); \ + } +#else /* CONFIG_MTD_DEBUG */ #define DEBUG(n, args...) -#endif +#endif /* CONFIG_MTD_DEBUG */ #endif /* __KERNEL__ */ - #endif /* __MTD_MTD_H__ */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 79ca1843c176..8c678ab9761a 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1,31 +1,154 @@ +/* + * linux/include/linux/mtd/nand.h + * + * Copyright (c) 2000 David Woodhouse + * Steven J. Hill + * + * $Id: nand.h,v 1.8 2000/10/30 17:16:17 sjhill Exp $ + * + * 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. + * + * Info: + * Contains standard defines and IDs for NAND flash devices + * + * Changelog: + * 01-31-2000 DMW Created + * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers + * so it can be used by other NAND flash device + * drivers. I also changed the copyright since none + * of the original contents of this file are specific + * to DoC devices. David can whack me with a baseball + * bat later if I did something naughty. + * 10-11-2000 SJH Added private NAND flash structure for driver + * 10-24-2000 SJH Added prototype for 'nand_scan' function + */ +#ifndef __LINUX_MTD_NAND_H +#define __LINUX_MTD_NAND_H -/* Defines for NAND flash devices */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse */ -/* $Id: nand.h,v 1.2 1999/08/17 22:57:08 dwmw2 Exp $ */ +#include +#include -#ifndef __MTD_NAND_H__ -#define __MTD_NAND_H__ - -#define NAND_CMD_READ0 0 -#define NAND_CMD_READ1 1 -#define NAND_CMD_PAGEPROG 0x10 -#define NAND_CMD_READOOB 0x50 -#define NAND_CMD_ERASE1 0x60 -#define NAND_CMD_STATUS 0x70 -#define NAND_CMD_SEQIN 0x80 -#define NAND_CMD_READID 0x90 -#define NAND_CMD_ERASE2 0xd0 -#define NAND_CMD_RESET 0xff - -#define NAND_MFR_TOSHIBA 0x98 -#define NAND_MFR_SAMSUNG 0xec - - -#endif /* __MTD_NAND_H__ */ +/* + * Searches for a NAND device + */ +extern int nand_scan (struct mtd_info *mtd); +/* + * Standard NAND flash commands + */ +#define NAND_CMD_READ0 0 +#define NAND_CMD_READ1 1 +#define NAND_CMD_PAGEPROG 0x10 +#define NAND_CMD_READOOB 0x50 +#define NAND_CMD_ERASE1 0x60 +#define NAND_CMD_STATUS 0x70 +#define NAND_CMD_SEQIN 0x80 +#define NAND_CMD_READID 0x90 +#define NAND_CMD_ERASE2 0xd0 +#define NAND_CMD_RESET 0xff +/* + * Enumeration for NAND flash chip state + */ +typedef enum { + FL_READY, + FL_READING, + FL_WRITING, + FL_ERASING, + FL_SYNCING +} nand_state_t; +/* + * NAND Private Flash Chip Data + * + * Structure overview: + * + * IO_ADDR - address to access the 8 I/O lines to the flash device + * + * CTRL_ADDR - address where ALE, CLE and CE control bits are accessed + * + * CLE - location in control word for Command Latch Enable bit + * + * ALE - location in control word for Address Latch Enable bit + * + * NCE - location in control word for nChip Enable bit + * + * chip_lock - spinlock used to protect access to this structure + * + * wq - wait queue to sleep on if a NAND operation is in progress + * + * state - give the current state of the NAND device + * + * page_shift - number of address bits in a page (column address bits) + * + * data_buf - data buffer passed to/from MTD user modules + * + * ecc_code_buf - used only for holding calculated or read ECCs for + * a page read or written when ECC is in use + * + * reserved - padding to make structure fall on word boundary if + * when ECC is in use + */ +struct nand_chip { + unsigned long IO_ADDR; + unsigned long CTRL_ADDR; + unsigned int CLE; + unsigned int ALE; + unsigned int NCE; + spinlock_t chip_lock; + wait_queue_head_t wq; + nand_state_t state; + int page_shift; + u_char *data_buf; +#ifdef CONFIG_MTD_NAND_ECC + u_char ecc_code_buf[6]; + u_char reserved[2]; +#endif +}; +/* + * NAND Flash Manufacturer ID Codes + */ +#define NAND_MFR_TOSHIBA 0x98 +#define NAND_MFR_SAMSUNG 0xec +/* + * NAND Flash Device ID Structure + * + * Structure overview: + * + * name - Complete name of device + * + * manufacture_id - manufacturer ID code of device. + * + * model_id - model ID code of device. + * + * chipshift - total number of address bits for the device which + * is used to calculate address offsets and the total + * number of bytes the device is capable of. + * + * page256 - denotes if flash device has 256 byte pages or not. + * + * pageadrlen - number of bytes minus one needed to hold the + * complete address into the flash array. Keep in + * mind that when a read or write is done to a + * specific address, the address is input serially + * 8 bits at a time. This structure member is used + * by the read/write routines as a loop index for + * shifting the address out 8 bits at a time. + * + * erasesize - size of an erase block in the flash device. + */ +struct nand_flash_dev { + char * name; + int manufacture_id; + int model_id; + int chipshift; + char page256; + char pageadrlen; + unsigned long erasesize; +}; +#endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/nand_ids.h b/include/linux/mtd/nand_ids.h new file mode 100644 index 000000000000..0918b8c1e92d --- /dev/null +++ b/include/linux/mtd/nand_ids.h @@ -0,0 +1,52 @@ +/* + * linux/include/linux/mtd/nand_ids.h + * + * Copyright (c) 2000 David Woodhouse + * Steven J. Hill + * + * $Id: nand_ids.h,v 1.1 2000/10/13 16:16:26 mdeans Exp $ + * + * 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. + * + * Info: + * Contains standard defines and IDs for NAND flash devices + * + * Changelog: + * 01-31-2000 DMW Created + * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers + * so it can be used by other NAND flash device + * drivers. I also changed the copyright since none + * of the original contents of this file are specific + * to DoC devices. David can whack me with a baseball + * bat later if I did something naughty. + * 10-11-2000 SJH Added private NAND flash structure for driver + * 2000-10-13 BE Moved out of 'nand.h' - avoids duplication. + */ + +#ifndef __LINUX_MTD_NAND_IDS_H +#define __LINUX_MTD_NAND_IDS_H + +static struct nand_flash_dev nand_flash_ids[] = { + {"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000}, + {"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22, 0, 2, 0x2000}, + {"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24, 0, 2, 0x4000}, + {"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25, 0, 2, 0x4000}, + {"Toshiba TH58512FT", NAND_MFR_TOSHIBA, 0x76, 26, 0, 3, 0x4000}, + {"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22, 0, 2, 0x2000}, + {"Toshiba TC58V64AFT/DC", NAND_MFR_TOSHIBA, 0xe6, 23, 0, 2, 0x2000}, + {"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21, 1, 2, 0x1000}, + {"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000}, + {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000}, + {"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24, 0, 2, 0x4000}, + {"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25, 0, 2, 0x4000}, + {"Samsung unknown 64Mb", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000}, + {"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22, 0, 2, 0x2000}, + {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0xe5, 22, 0, 2, 0x2000}, + {"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23, 0, 2, 0x2000}, + {"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000}, + {NULL,} +}; + +#endif /* __LINUX_MTD_NAND_IDS_H */ diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index 153fa5c6a76e..ae00ceb0ba2a 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -2,18 +2,21 @@ /* Defines for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftl.h,v 1.6 2000/03/31 15:12:20 dwmw2 Exp $ */ +/* $Id: nftl.h,v 1.9 2000/11/07 05:48:49 ollie Exp $ */ #ifndef __MTD_NFTL_H__ #define __MTD_NFTL_H__ +#ifndef __BOOT__ #include +#endif /* Block Control Information */ struct nftl_bci { unsigned char ECCSig[6]; - __u16 Status; + __u8 Status; + __u8 Status1; }__attribute__((packed)); /* Unit Control Information */ @@ -32,7 +35,8 @@ struct nftl_uci1 { } __attribute__((packed)); struct nftl_uci2 { - __u32 WriteInh; + __u16 FoldMark; + __u16 FoldMark1; __u32 unused; } __attribute__((packed)); @@ -60,10 +64,12 @@ struct NFTLMediaHeader { #define MAX_ERASE_ZONES (8192 - 512) #define ERASE_MARK 0x3c69 -#define BLOCK_FREE 0xffff -#define BLOCK_USED 0x5555 -#define BLOCK_IGNORE 0x1111 -#define BLOCK_DELETED 0x0000 +#define SECTOR_FREE 0xff +#define SECTOR_USED 0x55 +#define SECTOR_IGNORE 0x11 +#define SECTOR_DELETED 0x00 + +#define FOLD_MARK_IN_PROGRESS 0x5555 #define ZONE_GOOD 0xff #define ZONE_BAD_ORIGINAL 0 @@ -71,6 +77,11 @@ struct NFTLMediaHeader { #ifdef __KERNEL__ +/* these info are used in ReplUnitTable */ +#define BLOCK_NIL 0xffff /* last block of a chain */ +#define BLOCK_FREE 0xfffe /* free block */ +#define BLOCK_NOTEXPLORED 0xfffd /* non explored block, only used during mounting */ +#define BLOCK_RESERVED 0xfffc /* bios block or bad block */ struct NFTLrecord { struct mtd_info *mtd; @@ -83,18 +94,27 @@ struct NFTLrecord { unsigned char sectors; unsigned short cylinders; __u16 numvunits; - __u16 lastEUN; + __u16 lastEUN; /* should be suppressed */ __u16 numfreeEUNs; - __u16 LastFreeEUN; /* To speed up finding a free EUN */ + __u16 LastFreeEUN; /* To speed up finding a free EUN */ __u32 long nr_sects; int head,sect,cyl; - __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ - __u16 *VirtualUnitTable; /* [numEUNs]: VirtualUnitNumber for each */ - __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ + __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ + __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ + unsigned int nb_blocks; /* number of physical blocks */ + unsigned int nb_boot_blocks; /* number of blocks used by the bios */ + struct erase_info instr; }; +int NFTL_mount(struct NFTLrecord *s); +int NFTL_formatblock(struct NFTLrecord *s, int block); + +#ifndef NFTL_MAJOR #define NFTL_MAJOR 93 +#endif + #define MAX_NFTLS 16 +#define MAX_SECTORS_PER_UNIT 32 #endif /* __KERNEL__ */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h new file mode 100644 index 000000000000..a92023886008 --- /dev/null +++ b/include/linux/mtd/partitions.h @@ -0,0 +1,50 @@ +/* + * MTD partitioning layer definitions + * + * (C) 2000 Nicolas Pitre + * + * This code is GPL + * + * $Id: partitions.h,v 1.3 2000/11/10 23:35:12 nico Exp $ + */ + +#ifndef MTD_PARTITIONS_H +#define MTD_PARTITIONS_H + +#include + + +/* + * Partition definition structure: + * + * An array of struct partition is passed along with a MTD object to + * add_mtd_partitions() to create them. + * + * For each partition, these fields are available: + * name: string that will be used to label the partition's MTD device. + * size: the partition size; if 0, the partition will extend to the end of the + * master MTD device. + * offset: absolute starting position within the master MTD device; if 0, + * partition will start where the previous one ended. + * mask_flags: contains flags that have to be masked (removed) from the + * master MTD flag set for the corresponding MTD partition. + * For example, to force a read-only partition, simply adding + * MTD_WRITEABLE to the mask_flags will do the trick. + * + * Note: writeable partitions require their size and offset be + * erasesize aligned. + */ + +struct mtd_partition { + char *name; /* identifier string */ + u_long size; /* partition size */ + u_long offset; /* offset within the master MTD space */ + u_long mask_flags; /* master MTD flags to mask out for this partition */ +}; + + +int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int); +int del_mtd_partitions(struct mtd_info *); + +#endif + diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a7330e249407..cc8abab1f95c 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -270,7 +270,7 @@ static inline int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) - return 0; + return NFS_STALE(inode) ? -ESTALE : 0; return __nfs_revalidate_inode(server, inode); } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 07d821239a4c..8c260c63d598 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -165,8 +165,7 @@ struct mdk_rdev_s mddev_t *mddev; /* RAID array if running */ unsigned long last_events; /* IO event timestamp */ - struct inode *inode; /* Lock inode */ - struct file filp; /* Lock file */ + struct block_device *bdev; /* block device handle */ mdp_super_t *sb; unsigned long sb_offset; @@ -206,6 +205,7 @@ struct mddev_s struct semaphore reconfig_sem; struct semaphore recovery_sem; struct semaphore resync_sem; + atomic_t active; atomic_t recovery_active; /* blocks scheduled, but not written */ md_wait_queue_head_t recovery_wait; diff --git a/include/linux/raid/raid1.h b/include/linux/raid/raid1.h index aa17b8472ae1..a9a9d3e8edb8 100644 --- a/include/linux/raid/raid1.h +++ b/include/linux/raid/raid1.h @@ -7,7 +7,6 @@ struct mirror_info { int number; int raid_disk; kdev_t dev; - int next; int sect_limit; int head_position; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c67f8c85abf6..17e48d0e937e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -65,8 +65,7 @@ struct sk_buff { struct sk_buff_head * list; /* List we are on */ struct sock *sk; /* Socket we are owned by */ struct timeval stamp; /* Time we arrived */ - struct net_device *dev; /* Device we arrived on/are leaving by */ - struct net_device *rx_dev; + struct net_device *dev; /* Device we arrived on/are leaving by */ /* Transport layer header */ union @@ -110,8 +109,7 @@ struct sk_buff { unsigned int len; /* Length of actual data */ unsigned int csum; /* Checksum */ volatile char used; /* Data moved to user and not MSG_PEEK */ - unsigned char is_clone, /* We are a clone */ - cloned, /* head may be cloned (check refcnt to be sure). */ + unsigned char cloned, /* head may be cloned (check refcnt to be sure). */ pkt_type, /* Packet class */ ip_summed; /* Driver fed us an IP checksum */ __u32 priority; /* Packet queueing priority */ diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 8ba581f5ba38..8bb2d1c5d10d 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -65,7 +65,7 @@ #elif (DEBUG_SPINLOCKS < 2) typedef struct { - volatile unsigned int lock; + volatile unsigned long lock; } spinlock_t; #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } @@ -80,7 +80,7 @@ typedef struct { #else /* (DEBUG_SPINLOCKS >= 2) */ typedef struct { - volatile unsigned int lock; + volatile unsigned long lock; volatile unsigned int babble; const char *module; } spinlock_t; diff --git a/include/net/sock.h b/include/net/sock.h index 4b3a82bef1dc..8550282cbf45 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1169,6 +1169,7 @@ static inline int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) } #endif /* CONFIG_FILTER */ + skb->dev = NULL; skb_set_owner_r(skb, sk); skb_queue_tail(&sk->receive_queue, skb); if (!sk->dead) diff --git a/kernel/Makefile b/kernel/Makefile index 8f4c218f332e..311d66cb85cf 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,6 +26,13 @@ ifeq ($(CONFIG_PM),y) OX_OBJS += pm.o endif +ifneq ($(CONFIG_IA64),y) +# According to Alan Modra , the -fno-omit-frame-pointer is +# needed for x86 only. Why this used to be enabled for all architectures is beyond +# me. I suspect most platforms don't need this, but until we know that for sure +# I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k +# to get a correct value for the wait-channel (WCHAN in ps). --davidm CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer +endif include $(TOPDIR)/Rules.make diff --git a/kernel/fork.c b/kernel/fork.c index 68f72370946f..bf3e36cfb797 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -542,7 +542,7 @@ static inline void copy_flags(unsigned long clone_flags, struct task_struct *p) * arch/ia64/kernel/process.c. */ int do_fork(unsigned long clone_flags, unsigned long stack_start, - struct pt_regs *regs, unsigned long stack_top) + struct pt_regs *regs, unsigned long stack_size) { int retval = -ENOMEM; struct task_struct *p; @@ -637,7 +637,7 @@ int do_fork(unsigned long clone_flags, unsigned long stack_start, goto bad_fork_cleanup_fs; if (copy_mm(clone_flags, p)) goto bad_fork_cleanup_sighand; - retval = copy_thread(0, clone_flags, stack_start, stack_top, p, regs); + retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); if (retval) goto bad_fork_cleanup_sighand; p->semundo = NULL; diff --git a/mm/memory.c b/mm/memory.c index fda0940e07cd..13dad21a06ca 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -227,7 +227,7 @@ skip_copy_pte_range: address = (address + PMD_SIZE) & PMD_MASK; /* If it's a COW mapping, write protect it both in the parent and the child */ if (cow) { - ptep_clear_wrprotect(src_pte); + ptep_set_wrprotect(src_pte); pte = *src_pte; } @@ -887,45 +887,6 @@ bad_wp_page: return -1; } -/* - * This function zeroes out partial mmap'ed pages at truncation time.. - */ -static void partial_clear(struct vm_area_struct *vma, unsigned long address) -{ - unsigned int offset; - struct page *page; - pgd_t *page_dir; - pmd_t *page_middle; - pte_t *page_table, pte; - - page_dir = pgd_offset(vma->vm_mm, address); - if (pgd_none(*page_dir)) - return; - if (pgd_bad(*page_dir)) { - pgd_ERROR(*page_dir); - pgd_clear(page_dir); - return; - } - page_middle = pmd_offset(page_dir, address); - if (pmd_none(*page_middle)) - return; - if (pmd_bad(*page_middle)) { - pmd_ERROR(*page_middle); - pmd_clear(page_middle); - return; - } - page_table = pte_offset(page_middle, address); - pte = *page_table; - if (!pte_present(pte)) - return; - flush_cache_page(vma, address); - page = pte_page(pte); - if ((!VALID_PAGE(page)) || PageReserved(page)) - return; - offset = address & ~PAGE_MASK; - memclear_highpage_flush(page, offset, PAGE_SIZE - offset); -} - static void vmtruncate_list(struct vm_area_struct *mpnt, unsigned long pgoff, unsigned long partial) { @@ -953,10 +914,6 @@ static void vmtruncate_list(struct vm_area_struct *mpnt, /* Ok, partially affected.. */ start += diff << PAGE_SHIFT; len = (len - diff) << PAGE_SHIFT; - if (start & ~PAGE_MASK) { - partial_clear(mpnt, start); - start = (start + ~PAGE_MASK) & PAGE_MASK; - } flush_cache_range(mm, start, end); zap_page_range(mm, start, len); flush_tlb_range(mm, start, end); diff --git a/mm/mmap.c b/mm/mmap.c index da649f2a240d..648cc520875d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -354,11 +354,11 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon lock_vma_mappings(vma); spin_lock(&mm->page_table_lock); __insert_vm_struct(mm, vma); + unlock_vma_mappings(vma); if (correct_wcount) atomic_inc(&file->f_dentry->d_inode->i_writecount); merge_segments(mm, vma->vm_start, vma->vm_end); spin_unlock(&mm->page_table_lock); - unlock_vma_mappings(vma); mm->total_vm += len >> PAGE_SHIFT; if (flags & VM_LOCKED) { @@ -858,9 +858,9 @@ unsigned long do_brk(unsigned long addr, unsigned long len) lock_vma_mappings(vma); spin_lock(&mm->page_table_lock); __insert_vm_struct(mm, vma); + unlock_vma_mappings(vma); merge_segments(mm, vma->vm_start, vma->vm_end); spin_unlock(&mm->page_table_lock); - unlock_vma_mappings(vma); mm->total_vm += len >> PAGE_SHIFT; if (flags & VM_LOCKED) { @@ -1034,20 +1034,23 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l avl_remove(mpnt, &mm->mmap_avl); prev->vm_end = mpnt->vm_end; prev->vm_next = mpnt->vm_next; + mm->map_count--; if (mpnt->vm_ops && mpnt->vm_ops->close) { mpnt->vm_pgoff += (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; mpnt->vm_start = mpnt->vm_end; spin_unlock(&mm->page_table_lock); - unlock_vma_mappings(mpnt); mpnt->vm_ops->close(mpnt); - lock_vma_mappings(mpnt); - spin_lock(&mm->page_table_lock); - } - mm->map_count--; + } else + spin_unlock(&mm->page_table_lock); + + lock_vma_mappings(mpnt); __remove_shared_vm_struct(mpnt); + unlock_vma_mappings(mpnt); if (mpnt->vm_file) fput(mpnt->vm_file); kmem_cache_free(vm_area_cachep, mpnt); mpnt = prev; + + spin_lock(&mm->page_table_lock); } } diff --git a/mm/mremap.c b/mm/mremap.c index 764cfabb8ea4..bdbcf4841e58 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -144,9 +144,9 @@ static inline unsigned long move_vma(struct vm_area_struct * vma, lock_vma_mappings(vma); spin_lock(¤t->mm->page_table_lock); __insert_vm_struct(current->mm, new_vma); + unlock_vma_mappings(vma); merge_segments(current->mm, new_vma->vm_start, new_vma->vm_end); spin_unlock(¤t->mm->page_table_lock); - unlock_vma_mappings(vma); do_munmap(current->mm, addr, old_len); current->mm->total_vm += new_len >> PAGE_SHIFT; if (new_vma->vm_flags & VM_LOCKED) { diff --git a/net/atm/proc.c b/net/atm/proc.c index 4a016d08becd..e8d3170bef0c 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -220,7 +220,7 @@ static void vc_info(struct atm_vcc *vcc,char *buf) default: here += sprintf(here,"%3d",vcc->family); } - here += sprintf(here," %04x %5d %7d/%7d %7d/%7d\n",vcc->flags.bits, + here += sprintf(here," %04x %5ld %7d/%7d %7d/%7d\n",vcc->flags.bits, vcc->reply, atomic_read(&vcc->tx_inuse),vcc->sk->sndbuf, atomic_read(&vcc->rx_inuse),vcc->sk->rcvbuf); diff --git a/net/core/dev.c b/net/core/dev.c index c49ae2ff694c..cf4dcf8cd022 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -623,7 +623,7 @@ void netdev_state_change(struct net_device *dev) void dev_load(const char *name) { - if (!__dev_get_by_name(name) && capable(CAP_SYS_MODULE)) + if (!dev_get(name) && capable(CAP_SYS_MODULE)) request_module(name); } @@ -881,8 +881,6 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) skb2->h.raw = skb2->nh.raw; skb2->pkt_type = PACKET_OUTGOING; - skb2->rx_dev = skb->dev; - dev_hold(skb2->rx_dev); ptype->func(skb2, skb->dev, ptype); } } @@ -1135,10 +1133,7 @@ int netif_rx(struct sk_buff *skb) goto drop; enqueue: - if (skb->rx_dev) - dev_put(skb->rx_dev); - skb->rx_dev = skb->dev; - dev_hold(skb->rx_dev); + dev_hold(skb->dev); __skb_queue_tail(&queue->input_pkt_queue,skb); __cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ); local_irq_restore(flags); @@ -1212,11 +1207,11 @@ static int deliver_to_old_ones(struct packet_type *pt, struct sk_buff *skb, int */ static __inline__ void skb_bond(struct sk_buff *skb) { - struct net_device *dev = skb->rx_dev; + struct net_device *dev = skb->dev; if (dev->master) { dev_hold(dev->master); - skb->dev = skb->rx_dev = dev->master; + skb->dev = dev->master; dev_put(dev); } } @@ -1326,6 +1321,7 @@ static void net_rx_action(struct softirq_action *h) for (;;) { struct sk_buff *skb; + struct net_device *rx_dev; local_irq_disable(); skb = __skb_dequeue(&queue->input_pkt_queue); @@ -1336,10 +1332,13 @@ static void net_rx_action(struct softirq_action *h) skb_bond(skb); + rx_dev = skb->dev; + #ifdef CONFIG_NET_FASTROUTE if (skb->pkt_type == PACKET_FASTROUTE) { netdev_rx_stat[this_cpu].fastroute_deferred_out++; dev_queue_xmit(skb); + dev_put(rx_dev); continue; } #endif @@ -1375,6 +1374,7 @@ static void net_rx_action(struct softirq_action *h) if (skb->dev->br_port != NULL && br_handle_frame_hook != NULL) { handle_bridge(skb, pt_prev); + dev_put(rx_dev); continue; } #endif @@ -1405,6 +1405,8 @@ static void net_rx_action(struct softirq_action *h) kfree_skb(skb); } + dev_put(rx_dev); + if (bugdet-- < 0 || jiffies - start_time > 1) goto softnet_break; @@ -2313,6 +2315,12 @@ int register_netdevice(struct net_device *dev) #endif if (dev_boot_phase) { +#ifdef CONFIG_NET_DIVERT + ret = alloc_divert_blk(dev); + if (ret) + return ret; +#endif /* CONFIG_NET_DIVERT */ + /* This is NOT bug, but I am not sure, that all the devices, initialized before netdev module is started are sane. @@ -2338,12 +2346,6 @@ int register_netdevice(struct net_device *dev) dev_hold(dev); write_unlock_bh(&dev_base_lock); -#ifdef CONFIG_NET_DIVERT - ret = alloc_divert_blk(dev); - if (ret) - return ret; -#endif /* CONFIG_NET_DIVERT */ - /* * Default initial state at registry is that the * device is present. @@ -2354,6 +2356,12 @@ int register_netdevice(struct net_device *dev) return 0; } +#ifdef CONFIG_NET_DIVERT + ret = alloc_divert_blk(dev); + if (ret) + return ret; +#endif /* CONFIG_NET_DIVERT */ + dev->iflink = -1; /* Init, if this function is available */ @@ -2393,12 +2401,6 @@ int register_netdevice(struct net_device *dev) dev->deadbeaf = 0; write_unlock_bh(&dev_base_lock); -#ifdef CONFIG_NET_DIVERT - ret = alloc_divert_blk(dev); - if (ret) - return ret; -#endif /* CONFIG_NET_DIVERT */ - /* Notify protocols, that a new device appeared. */ notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); diff --git a/net/core/dv.c b/net/core/dv.c index 4df7747b87a0..0e5b3e671620 100644 --- a/net/core/dv.c +++ b/net/core/dv.c @@ -62,7 +62,7 @@ int alloc_divert_blk(struct net_device *dev) if (dev->divert == NULL) { printk(KERN_DEBUG "divert: unable to allocate divert_blk for %s\n", dev->name); - return -EFAULT; + return -ENOMEM; } else { memset(dev->divert, 0, sizeof(struct divert_blk)); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ca7433f9bf9b..c5dcecfb358e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4,7 +4,7 @@ * Authors: Alan Cox * Florian La Roche * - * Version: $Id: skbuff.c,v 1.73 2000/05/22 07:29:44 davem Exp $ + * Version: $Id: skbuff.c,v 1.75 2000/12/08 17:15:53 davem Exp $ * * Fixes: * Alan Cox : Fixed the worst of the load balancer bugs. @@ -202,7 +202,6 @@ struct sk_buff *alloc_skb(unsigned int size,int gfp_mask) /* Set up other state */ skb->len = 0; - skb->is_clone = 0; skb->cloned = 0; atomic_set(&skb->users, 1); @@ -233,7 +232,6 @@ static inline void skb_headerinit(void *p, kmem_cache_t *cache, skb->ip_summed = 0; skb->security = 0; /* By default packets are insecure */ skb->dst = NULL; - skb->rx_dev = NULL; #ifdef CONFIG_NETFILTER skb->nfmark = skb->nfcache = 0; skb->nfct = NULL; @@ -287,10 +285,6 @@ void __kfree_skb(struct sk_buff *skb) #ifdef CONFIG_NETFILTER nf_conntrack_put(skb->nfct); #endif -#ifdef CONFIG_NET - if(skb->rx_dev) - dev_put(skb->rx_dev); -#endif skb_headerinit(skb, NULL, 0); /* clean state */ kfree_skbmem(skb); } @@ -325,12 +319,10 @@ struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask) skb->cloned = 1; dst_clone(n->dst); - n->rx_dev = NULL; n->cloned = 1; n->next = n->prev = NULL; n->list = NULL; n->sk = NULL; - n->is_clone = 1; atomic_set(&n->users, 1); n->destructor = NULL; #ifdef CONFIG_NETFILTER @@ -349,7 +341,6 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->list=NULL; new->sk=NULL; new->dev=old->dev; - new->rx_dev=NULL; new->priority=old->priority; new->protocol=old->protocol; new->dst=dst_clone(old->dst); @@ -358,7 +349,6 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->mac.raw=old->mac.raw+offset; memcpy(new->cb, old->cb, sizeof(old->cb)); new->used=old->used; - new->is_clone=0; atomic_set(&new->users, 1); new->pkt_type=old->pkt_type; new->stamp=old->stamp; diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index 4754cd850122..361729458ada 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -78,9 +78,9 @@ extern int decnet_log_martians; static void dn_log_martian(struct sk_buff *skb, const char *msg) { if (decnet_log_martians && net_ratelimit()) { - char *devname = skb->rx_dev ? skb->rx_dev->name : "???"; + char *devname = skb->dev ? skb->dev->name : "???"; struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; - printk(KERN_INFO "DECnet: Martian packet (%s) rx_dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port); + printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port); } } @@ -782,7 +782,7 @@ free_out: int dn_nsp_rx(struct sk_buff *skb) { - return NF_HOOK(PF_DECnet, NF_DN_LOCAL_IN, skb, skb->rx_dev, NULL, dn_nsp_rx_packet); + return NF_HOOK(PF_DECnet, NF_DN_LOCAL_IN, skb, skb->dev, NULL, dn_nsp_rx_packet); } /* diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 20ec07acc922..70646fc11d2a 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -526,6 +526,7 @@ static int dn_forward(struct sk_buff *skb) { struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; struct dst_entry *dst = skb->dst; + struct net_device *dev = skb->dev; struct neighbour *neigh; int err = -EINVAL; @@ -551,7 +552,7 @@ static int dn_forward(struct sk_buff *skb) else cb->rt_flags &= ~DN_RT_F_IE; - return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, skb->rx_dev, skb->dev, neigh->output); + return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, dev, skb->dev, neigh->output); error: @@ -985,7 +986,6 @@ int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg) } skb->protocol = __constant_htons(ETH_P_DNA_RT); skb->dev = dev; - skb->rx_dev = dev; cb->src = src; cb->dst = dst; local_bh_disable(); @@ -1002,7 +1002,6 @@ int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg) if (skb->dev) dev_put(skb->dev); skb->dev = NULL; - skb->rx_dev = NULL; if (err) goto out_free; skb->dst = &rt->u.dst; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 276f7bd4f0f1..6b980e3f34bc 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1,7 +1,7 @@ /* * NET3 IP device support routines. * - * Version: $Id: devinet.c,v 1.38 2000/08/19 23:22:56 davem Exp $ + * Version: $Id: devinet.c,v 1.39 2000/12/10 22:24:11 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 121a1ac7a38b..7091bf82ca52 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -5,7 +5,7 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.52 2000/11/28 13:32:54 davem Exp $ + * Version: $Id: ip_fragment.c,v 1.53 2000/12/08 17:15:53 davem Exp $ * * Authors: Fred N. van Kempen * Alan Cox @@ -83,7 +83,7 @@ struct ipq { atomic_t refcnt; struct timer_list timer; /* when will this queue expire? */ struct ipq **pprev; - struct net_device *dev; /* Device - for icmp replies */ + int iif; /* Device index - for icmp replies */ }; /* Hash table. */ @@ -255,8 +255,13 @@ static void ip_expire(unsigned long arg) IP_INC_STATS_BH(IpReasmFails); if ((qp->last_in&FIRST_IN) && qp->fragments != NULL) { + struct sk_buff *head = qp->fragments; + /* Send an ICMP "Fragment Reassembly Timeout" message. */ - icmp_send(qp->fragments, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); + if ((head->dev = dev_get_by_index(qp->iif)) != NULL) { + icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); + dev_put(head->dev); + } } out: spin_unlock(&qp->lock); @@ -480,7 +485,8 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb) else qp->fragments = skb; - qp->dev = skb->dev; + qp->iif = skb->dev->ifindex; + skb->dev = NULL; qp->meat += skb->len; atomic_add(skb->truesize, &ip_frag_mem); if (offset == 0) @@ -499,7 +505,7 @@ err: * of bits on input. Until the new skb data handling is in I'm not going * to touch this with a bargepole. */ -static struct sk_buff *ip_frag_reasm(struct ipq *qp) +static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev) { struct sk_buff *skb; struct iphdr *iph; @@ -546,7 +552,7 @@ static struct sk_buff *ip_frag_reasm(struct ipq *qp) skb->dst = dst_clone(head->dst); skb->pkt_type = head->pkt_type; skb->protocol = head->protocol; - skb->dev = qp->dev; + skb->dev = dev; /* * Clearly bogus, because security markings of the individual @@ -595,6 +601,7 @@ struct sk_buff *ip_defrag(struct sk_buff *skb) { struct iphdr *iph = skb->nh.iph; struct ipq *qp; + struct net_device *dev; IP_INC_STATS_BH(IpReasmReqds); @@ -602,6 +609,8 @@ struct sk_buff *ip_defrag(struct sk_buff *skb) if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh) ip_evictor(); + dev = skb->dev; + /* Lookup (or create) queue header */ if ((qp = ip_find(iph)) != NULL) { struct sk_buff *ret = NULL; @@ -612,7 +621,7 @@ struct sk_buff *ip_defrag(struct sk_buff *skb) if (qp->last_in == (FIRST_IN|LAST_IN) && qp->meat == qp->len) - ret = ip_frag_reasm(qp); + ret = ip_frag_reasm(qp, dev); spin_unlock(&qp->lock); ipq_put(qp); diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 873030d0a1e4..be5df4c26d9f 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) module. * - * Version: $Id: ip_input.c,v 1.50 2000/10/24 22:54:26 davem Exp $ + * Version: $Id: ip_input.c,v 1.51 2000/12/08 17:15:53 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -225,12 +225,6 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) nf_debug_ip_local_deliver(skb); #endif /*CONFIG_NETFILTER_DEBUG*/ - /* Free rx_dev before enqueueing to sockets */ - if (skb->rx_dev) { - dev_put(skb->rx_dev); - skb->rx_dev = NULL; - } - /* Point into the IP datagram, just past the header. */ skb->h.raw = skb->nh.raw + iph->ihl*4; diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c index 73fd4eaf73d5..9c8d493b5917 100644 --- a/net/ipv4/netfilter/ip_queue.c +++ b/net/ipv4/netfilter/ip_queue.c @@ -400,13 +400,6 @@ static struct sk_buff *netlink_build_message(ipq_queue_element_t *e, int *errp) if (e->info->outdev) strcpy(pm->outdev_name, e->info->outdev->name); else pm->outdev_name[0] = '\0'; pm->hw_protocol = e->skb->protocol; - if (e->skb->rx_dev) { - pm->hw_type = e->skb->rx_dev->type; - if (e->skb->rx_dev->hard_header_parse) - pm->hw_addrlen = - e->skb->rx_dev->hard_header_parse(e->skb, - pm->hw_addr); - } if (data_len) memcpy(pm->payload, e->skb->data, data_len); nlh->nlmsg_len = skb->tail - old_tail; diff --git a/net/ipv4/netfilter/ipt_MIRROR.c b/net/ipv4/netfilter/ipt_MIRROR.c index cb5362dc2a19..9449c5128691 100644 --- a/net/ipv4/netfilter/ipt_MIRROR.c +++ b/net/ipv4/netfilter/ipt_MIRROR.c @@ -50,7 +50,7 @@ static int route_mirror(struct sk_buff *skb) /* check if the interface we are leaving by is the same as the one we arrived on */ - if (skb->rx_dev == rt->u.dst.dev) { + if (skb->dev == rt->u.dst.dev) { /* Drop old route. */ dst_release(skb->dst); skb->dst = &rt->u.dst; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2cbb20fcfad2..c2cc4815bc2a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.221 2000/11/28 17:04:10 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.222 2000/12/08 17:15:53 davem Exp $ * * IPv4 specific functions * @@ -1649,6 +1649,8 @@ process: if (sk->state == TCP_TIME_WAIT) goto do_time_wait; + skb->dev = NULL; + bh_lock_sock(sk); ret = 0; if (!sk->lock.users) { diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 6c6ae227f53d..c4e3f6f1a99a 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -6,7 +6,7 @@ * Pedro Roque * Ian P. Morris * - * $Id: ip6_input.c,v 1.17 2000/02/27 19:42:53 davem Exp $ + * $Id: ip6_input.c,v 1.18 2000/12/08 17:15:54 davem Exp $ * * Based in linux/net/ipv4/ip_input.c * @@ -146,11 +146,6 @@ static inline int ip6_input_finish(struct sk_buff *skb) } len = skb->tail - skb->h.raw; - if (skb->rx_dev) { - dev_put(skb->rx_dev); - skb->rx_dev = NULL; - } - raw_sk = raw_v6_htable[nexthdr&(MAX_INET_PROTOS-1)]; if (raw_sk) raw_sk = ipv6_raw_deliver(skb, nexthdr, len); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 75eb9f5594b8..0529aa480ef7 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: reassembly.c,v 1.20 2000/11/28 13:48:03 davem Exp $ + * $Id: reassembly.c,v 1.22 2000/12/08 17:41:54 davem Exp $ * * Based on: net/ipv4/ip_fragment.c * @@ -78,7 +78,6 @@ struct frag_queue struct sk_buff *fragments; int len; int meat; - struct net_device *dev; int iif; __u8 last_in; /* has first/last segment arrived? */ #define COMPLETE 4 @@ -476,8 +475,8 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, else fq->fragments = skb; - fq->dev = skb->dev; fq->iif = skb->dev->ifindex; + skb->dev = NULL; fq->meat += skb->len; atomic_add(skb->truesize, &ip6_frag_mem); @@ -507,7 +506,8 @@ err: * queue is eligible for reassembly i.e. it is not COMPLETE, * the last and the first frames arrived and all the bits are here. */ -static u8* ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in) +static u8 *ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in, + struct net_device *dev) { struct sk_buff *fp, *head = fq->fragments; struct sk_buff *skb; @@ -541,7 +541,7 @@ static u8* ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in) skb->mac.raw = skb->data; skb->nh.ipv6h = (struct ipv6hdr *) skb->data; - skb->dev = fq->dev; + skb->dev = dev; skb->protocol = __constant_htons(ETH_P_IPV6); skb->pkt_type = head->pkt_type; FRAG6_CB(skb)->h = FRAG6_CB(head)->h; @@ -579,6 +579,7 @@ u8* ipv6_reassembly(struct sk_buff **skbp, __u8 *nhptr) { struct sk_buff *skb = *skbp; struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw); + struct net_device *dev = skb->dev; struct frag_queue *fq; struct ipv6hdr *hdr; @@ -616,7 +617,7 @@ u8* ipv6_reassembly(struct sk_buff **skbp, __u8 *nhptr) if (fq->last_in == (FIRST_IN|LAST_IN) && fq->meat == fq->len) - ret = ip6_frag_reasm(fq, skbp); + ret = ip6_frag_reasm(fq, skbp, dev); spin_unlock(&fq->lock); fq_put(fq); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index b78e56d15c8d..1b0684987038 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.127 2000/11/28 17:04:10 davem Exp $ + * $Id: tcp_ipv6.c,v 1.128 2000/12/08 17:15:54 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -1576,6 +1576,8 @@ process: if(sk->state == TCP_TIME_WAIT) goto do_time_wait; + skb->dev = NULL; + bh_lock_sock(sk); ret = 0; if (!sk->lock.users) { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 6b4c77854e2b..576d6400e8c5 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -5,7 +5,7 @@ * * PACKET - implements raw packet sockets. * - * Version: $Id: af_packet.c,v 1.46 2000/10/24 21:26:19 davem Exp $ + * Version: $Id: af_packet.c,v 1.47 2000/12/08 17:15:54 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -264,11 +264,6 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, struct strncpy(spkt->spkt_device, dev->name, sizeof(spkt->spkt_device)); spkt->spkt_protocol = skb->protocol; - if (skb->rx_dev) { - dev_put(skb->rx_dev); - skb->rx_dev = NULL; - } - /* * Charge the memory to the socket. This is done specifically * to prevent sockets using all the memory up. @@ -482,17 +477,13 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packe if (dev->hard_header_parse) sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr); - if (skb->rx_dev) { - dev_put(skb->rx_dev); - skb->rx_dev = NULL; - } - #ifdef CONFIG_FILTER if (skb->len > snaplen) __skb_trim(skb, snaplen); #endif skb_set_owner_r(skb, sk); + skb->dev = NULL; spin_lock(&sk->receive_queue.lock); po->stats.tp_packets++; __skb_queue_tail(&sk->receive_queue, skb); diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 0242b12e5c5c..8d42109d8819 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -520,6 +520,7 @@ static struct sock *x25_make_new(struct sock *osk) sk->state = TCP_ESTABLISHED; sk->sleep = osk->sleep; sk->zapped = osk->zapped; + sk->backlog_rcv = osk->backlog_rcv; x25->t21 = osk->protinfo.x25->t21; x25->t22 = osk->protinfo.x25->t22; @@ -867,7 +868,7 @@ static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct return -EINVAL; /* we currently don't support segmented records at the user interface */ - if (!(msg->msg_flags & MSG_EOR)) + if (!(msg->msg_flags & (MSG_EOR|MSG_OOB))) return -EINVAL; if (sk->zapped) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index eb4c33cc144f..77dba8e220c6 100644 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -2,10 +2,15 @@ ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## ## Copyright (C) 2000 Tim Waugh ## +## ## +## #define enhancements by Armin Kuster ## +## Copyright (c) 2000 MontaVista Software, Inc. ## ## ## ## This software falls under the GNU General Public License. ## ## Please read the COPYING file for more information ## +# w.o. 03-11-2000: added the '-filelist' option. + # # This will read a 'c' file and scan for embedded comments in the # style of gnome comments (+minor extensions - see below). @@ -150,6 +155,8 @@ $output_mode = "man"; $blankline = $blankline_man; $modulename = "API Documentation"; $function_only = 0; +$filelist = ''; + while ($ARGV[0] =~ m/^-(.*)/) { $cmd = shift @ARGV; if ($cmd eq "-html") { @@ -186,6 +193,8 @@ while ($ARGV[0] =~ m/^-(.*)/) { $verbose = 1; } elsif (($cmd eq "-h") || ($cmd eq "--help")) { usage(); + } elsif ($cmd eq '-filelist') { + $filelist = shift @ARGV; } } @@ -663,8 +672,14 @@ sub dump_function { $prototype =~ s/^extern+ //; $prototype =~ s/^inline+ //; $prototype =~ s/^__inline__+ //; - - if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ s/^#define+ //; #ak added + + if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || @@ -781,13 +796,28 @@ $section_default = "Description"; # default section $section_intro = "Introduction"; $section = $section_default; -$lineno = 0; +if( $filelist ne '' ) { + open(FLIST,"<$filelist") or die "Can't open file list $filelist"; + while() { + chop; + process_file($_); + } +} + foreach $file (@ARGV) { chomp($file); + process_file($file); +} + +sub process_file($) { + my ($file) = @_; + if (!open(IN,"<$file")) { print STDERR "Error: Cannot open file $file\n"; - next; + return; } + + $lineno = 0; while () { $lineno++; @@ -877,7 +907,7 @@ foreach $file (@ARGV) { elsif (/([^\{]*)/) { $prototype .= $1; } - if (/\{/) { + if (/\{/ || /\#/) { # added for #define AK $prototype =~ s@/\*.*?\*/@@gos; # strip comments. $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. $prototype =~ s@^ +@@gos; # strip leading spaces -- 2.39.5