From 53b3988d474435254a3b053a68bb24ce9e439295 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 15 Jun 2000 01:55:58 +0000 Subject: [PATCH] Merge with 2.3.99-pre9. --- CREDITS | 43 +- Documentation/Configure.help | 18 +- Documentation/DocBook/Makefile | 17 +- Documentation/DocBook/kernel-api.tmpl | 5 + Documentation/DocBook/kernel-hacking.tmpl | 1316 ++++++++++ Documentation/DocBook/kernel-locking.tmpl | 1221 +++++++++ Documentation/DocBook/parportbook.tmpl | 4070 ++++++++++++++++------------- Documentation/filesystems/cramfs.txt | 4 +- Documentation/kbuild/config-language.txt | 2 +- Documentation/kernel-doc-nano-HOWTO.txt | 128 + Documentation/networking/8139too.txt | 33 +- Documentation/pci.txt | 10 + Documentation/s390/DASD | 2 +- Documentation/usb/ov511.txt | 52 +- Documentation/usb/usb-serial.txt | 25 + Documentation/video4linux/bttv/CARDLIST | 4 + MAINTAINERS | 15 + Makefile | 6 +- arch/alpha/kernel/osf_sys.c | 7 +- arch/arm/Makefile | 2 +- arch/arm/def-configs/ebsa110 | 41 +- arch/arm/def-configs/footbridge | 152 +- arch/arm/def-configs/rpc | 199 +- arch/arm/kernel/Makefile | 2 +- arch/arm/kernel/iic.c | 248 -- arch/arm/kernel/traps.c | 6 +- arch/arm/lib/Makefile | 1 + arch/i386/config.in | 1 - arch/i386/defconfig | 8 +- arch/i386/kernel/acpi.c | 2 +- arch/i386/kernel/apic.c | 5 - arch/i386/kernel/apm.c | 50 +- arch/i386/kernel/i8259.c | 2 +- arch/i386/kernel/io_apic.c | 32 +- arch/i386/kernel/irq.c | 8 +- arch/i386/kernel/ldt.c | 1 + arch/i386/kernel/mpparse.c | 15 +- arch/i386/kernel/pci-i386.c | 22 +- arch/i386/kernel/pci-irq.c | 3 +- arch/i386/kernel/pci-pc.c | 11 +- arch/i386/kernel/setup.c | 58 +- arch/i386/kernel/traps.c | 2 +- arch/i386/mm/init.c | 54 +- arch/ia64/ia32/sys_ia32.c | 4 +- arch/mips/mm/umap.c | 3 +- arch/mips64/defconfig | 1 - arch/mips64/defconfig-ip22 | 1 - arch/mips64/defconfig-ip27 | 1 - arch/ppc/8260_io/enet.c | 4 +- arch/ppc/8260_io/uart.c | 12 +- arch/ppc/configs/common_defconfig | 16 +- arch/ppc/defconfig | 15 +- arch/ppc/kernel/chrp_pci.c | 37 +- arch/ppc/kernel/chrp_setup.c | 39 +- arch/ppc/kernel/chrp_time.c | 9 +- arch/ppc/kernel/entry.S | 89 +- arch/ppc/kernel/head.S | 3 +- arch/ppc/kernel/ppc_ksyms.c | 3 + arch/ppc/kernel/ptrace.c | 837 +++--- arch/ppc/kernel/setup.c | 7 +- arch/ppc/kernel/time.c | 24 +- arch/ppc/kernel/traps.c | 9 +- arch/ppc/mbxboot/Makefile | 75 +- arch/ppc/mbxboot/embed_config.c | 13 +- arch/ppc/mbxboot/gzimage.c | 8 + arch/ppc/mbxboot/head_8260.S | 11 +- arch/ppc/mbxboot/misc.c | 9 + arch/ppc/mbxboot/rdimage.c | 8 + arch/ppc/{ => mbxboot}/vmlinux.lds | 16 +- arch/ppc/mm/fault.c | 93 +- arch/ppc/mm/init.c | 10 +- arch/ppc/treeboot/mkevimg | 2 +- arch/ppc/treeboot/mkirimg | 6 +- arch/ppc/vmlinux.lds | 8 +- arch/sh/config.in | 15 +- arch/sh/defconfig | 14 +- arch/sh/kernel/Makefile | 10 +- arch/sh/kernel/entry.S | 17 +- arch/sh/kernel/io_generic.c | 99 + arch/sh/kernel/io_se.c | 221 ++ arch/sh/kernel/irq.c | 10 +- arch/sh/kernel/irq_ipr.c | 171 ++ arch/sh/kernel/irq_onchip.c | 298 --- arch/sh/kernel/ptrace.c | 36 +- arch/sh/kernel/setup_se.c | 123 + arch/sh/kernel/signal.c | 4 +- arch/sh/kernel/time.c | 54 +- arch/sh/lib/checksum.S | 153 +- arch/sh/mm/cache.c | 345 +-- arch/sh/mm/fault.c | 81 +- arch/sparc/config.in | 7 +- arch/sparc/kernel/Makefile | 4 +- arch/sparc/kernel/entry.S | 2 +- arch/sparc/kernel/head.S | 2 +- arch/sparc/kernel/irq.c | 2 +- arch/sparc/kernel/process.c | 2 +- arch/sparc/kernel/setup.c | 2 +- arch/sparc/kernel/signal.c | 2 +- arch/sparc/kernel/sparc_ksyms.c | 2 +- arch/sparc/kernel/sun4d_irq.c | 2 +- arch/sparc/kernel/sys_sunos.c | 7 +- arch/sparc/kernel/time.c | 2 +- arch/sparc/kernel/traps.c | 2 +- arch/sparc/lib/rwsem.S | 2 +- arch/sparc/mm/btfixup.c | 2 +- arch/sparc/mm/hypersparc.S | 2 +- arch/sparc/mm/init.c | 2 +- arch/sparc/mm/srmmu.c | 2 +- arch/sparc/mm/sun4c.c | 2 +- arch/sparc/mm/swift.S | 2 +- arch/sparc/mm/tsunami.S | 2 +- arch/sparc/mm/viking.S | 2 +- arch/sparc64/config.in | 7 +- arch/sparc64/kernel/Makefile | 2 +- arch/sparc64/kernel/head.S | 2 +- arch/sparc64/kernel/ioctl32.c | 40 +- arch/sparc64/kernel/irq.c | 2 +- arch/sparc64/kernel/process.c | 2 +- arch/sparc64/kernel/setup.c | 2 +- arch/sparc64/kernel/sparc64_ksyms.c | 2 +- arch/sparc64/kernel/sys_sparc32.c | 9 +- arch/sparc64/kernel/sys_sunos32.c | 7 +- arch/sparc64/kernel/systbls.S | 6 +- arch/sparc64/kernel/time.c | 2 +- arch/sparc64/kernel/traps.c | 2 +- arch/sparc64/kernel/ttable.S | 2 +- arch/sparc64/lib/debuglocks.c | 2 +- arch/sparc64/mm/init.c | 2 +- arch/sparc64/mm/ultra.S | 2 +- arch/sparc64/prom/misc.c | 2 +- arch/sparc64/solaris/misc.c | 2 +- drivers/acorn/net/Makefile | 8 +- drivers/acorn/net/ether1.c | 226 +- drivers/acorn/net/ether3.c | 304 +-- drivers/acorn/net/etherh.c | 512 ++-- drivers/atm/eni.c | 2 +- drivers/block/Config.in | 10 +- drivers/block/DAC960.c | 262 +- drivers/block/DAC960.h | 184 +- drivers/block/elevator.c | 5 +- drivers/block/floppy.c | 195 +- drivers/block/loop.c | 70 +- drivers/block/xor.c | 1 + drivers/char/Config.in | 6 +- drivers/char/Makefile | 52 +- drivers/char/bttv.c | 623 ++--- drivers/char/bttv.h | 18 +- drivers/char/epca.c | 3 +- drivers/char/generic_serial.c | 35 +- drivers/char/lp.c | 8 +- drivers/char/mem.c | 31 +- drivers/char/msp3400.c | 16 +- drivers/char/rio/func.h | 2 + drivers/char/rio/host.h | 8 +- drivers/char/rio/linux_compat.h | 1 - drivers/char/rio/rio_linux.c | 285 +- drivers/char/rio/rio_linux.h | 27 +- drivers/char/rio/rioboot.c | 111 +- drivers/char/rio/riocmd.c | 1 - drivers/char/rio/rioctrl.c | 4 + drivers/char/rio/rioinit.c | 2 +- drivers/char/rio/riotty.c | 38 +- drivers/char/rio/unixrup.h | 2 +- drivers/char/rtc.c | 7 +- drivers/char/sh-sci.c | 31 +- drivers/char/sh-sci.h | 25 +- drivers/char/sx.c | 10 +- drivers/char/tda7432.c | 505 ++++ drivers/char/tda8425.c | 1 - drivers/char/tda985x.c | 58 +- drivers/char/tda9875.c | 371 +++ drivers/char/tea6420.c | 268 ++ drivers/char/tty_io.c | 8 +- drivers/char/videodev.c | 6 +- drivers/ide/Config.in | 9 + drivers/ide/aec62xx.c | 43 +- drivers/ide/alim15x3.c | 10 +- drivers/ide/amd7409.c | 51 +- drivers/ide/cmd64x.c | 34 +- drivers/ide/hd.c | 9 + drivers/ide/hpt34x.c | 17 +- drivers/ide/hpt366.c | 30 +- drivers/ide/icside.c | 156 +- drivers/ide/ide-disk.c | 11 +- drivers/ide/ide-features.c | 66 +- drivers/ide/ide-pci.c | 20 +- drivers/ide/ide-pmac.c | 243 +- drivers/ide/ide-probe.c | 66 +- drivers/ide/ide.c | 224 +- drivers/ide/pdc202xx.c | 243 +- drivers/ide/piix.c | 107 +- drivers/ide/qd6580.c | 252 +- drivers/ide/sis5513.c | 16 +- drivers/ide/via82cxxx.c | 48 +- drivers/isdn/avmb1/kcapi.c | 2 +- drivers/macintosh/Makefile | 30 +- drivers/macintosh/adb.c | 6 + drivers/macintosh/mac_keyb.c | 64 +- drivers/macintosh/macserial.c | 233 +- drivers/macintosh/macserial.h | 2 +- drivers/macintosh/mediabay.c | 112 +- drivers/macintosh/nvram.c | 13 +- drivers/macintosh/via-pmu.c | 49 +- drivers/net/3c59x.c | 2 +- drivers/net/8139too.c | 107 +- drivers/net/Config.in | 5 +- drivers/net/Makefile | 2 + drivers/net/Space.c | 22 - drivers/net/acenic.c | 2 +- drivers/net/am79c961a.c | 235 +- drivers/net/arcnet/arc-rimi.c | 5 +- drivers/net/arcnet/com20020-isa.c | 5 +- drivers/net/arcnet/com20020-pci.c | 4 +- drivers/net/arcnet/com90io.c | 5 +- drivers/net/arcnet/com90xx.c | 5 +- drivers/net/eepro100.c | 2 +- drivers/net/epic100.c | 2 +- drivers/net/hp100.c | 16 +- drivers/net/ncr885e.c | 2 +- drivers/net/ne2k-pci.c | 2 +- drivers/net/pcnet32.c | 4 +- drivers/net/ppp_synctty.c | 12 +- drivers/net/pppoe.c | 83 +- drivers/net/setup.c | 23 +- drivers/net/sis900.c | 3 +- drivers/net/slhc.c | 7 - drivers/net/starfire.c | 2 +- drivers/net/stnic.c | 302 +++ drivers/net/tulip/tulip_core.c | 2 +- drivers/net/via-rhine.c | 2 +- drivers/net/wan/comx-hw-comx.c | 12 - drivers/net/wan/comx-hw-locomx.c | 8 - drivers/net/wan/comx-hw-mixcom.c | 10 - drivers/net/wan/comx-proto-fr.c | 9 - drivers/net/wan/comx-proto-lapb.c | 6 - drivers/net/wan/comx.c | 127 +- drivers/net/wan/lmc/lmc_main.c | 2 +- drivers/net/wavelan.c | 1 + drivers/net/wavelan.p.h | 2 + drivers/net/yellowfin.c | 2 +- drivers/parport/ChangeLog | 40 + drivers/parport/daisy.c | 12 +- drivers/parport/ieee1284.c | 2 +- drivers/parport/ieee1284_ops.c | 4 + drivers/parport/parport_pc.c | 93 +- drivers/parport/share.c | 33 +- drivers/pci/pci.ids | 7 +- drivers/pcmcia/yenta.c | 2 +- drivers/s390/block/dasd_proc.c | 58 +- drivers/scsi/BusLogic.c | 3 +- drivers/scsi/aha152x.c | 31 +- drivers/scsi/megaraid.c | 2 +- drivers/scsi/megaraid.h | 1 + drivers/sound/ac97_codec.c | 6 + drivers/sound/cmpci.c | 2 +- drivers/sound/dmasound/dmasound_awacs.c | 513 ++-- drivers/sound/emu10k1/main.c | 2 +- drivers/sound/es1370.c | 2 +- drivers/sound/es1371.c | 2 +- drivers/sound/esssolo1.c | 2 +- drivers/sound/sb_card.c | 15 +- drivers/sound/sonicvibes.c | 2 +- drivers/sound/trident.c | 29 +- drivers/sound/via82cxxx_audio.c | 2 +- drivers/usb/Config.in | 2 +- drivers/usb/audio.c | 129 +- drivers/usb/hub.c | 4 +- drivers/usb/input.c | 8 +- drivers/usb/ov511.c | 743 +++--- drivers/usb/ov511.h | 139 +- drivers/usb/pegasus.c | 50 +- drivers/usb/printer.c | 45 +- drivers/usb/serial/Makefile | 22 +- drivers/usb/serial/digi_acceleport.c | 1164 ++++++--- drivers/usb/serial/ftdi_sio.c | 6 - drivers/usb/serial/keyspan_pda.c | 4 - drivers/usb/serial/omninet.c | 6 - drivers/usb/serial/usbserial.c | 4 + drivers/usb/serial/visor.c | 7 - drivers/usb/serial/whiteheat.c | 6 - drivers/usb/uhci.c | 31 +- drivers/usb/usb-ohci.c | 160 +- drivers/usb/usb-ohci.h | 2 +- drivers/usb/usb-storage.c | 356 ++- drivers/usb/usb-storage.h | 14 +- drivers/usb/usb-uhci.c | 87 +- drivers/usb/usb-uhci.h | 4 +- drivers/usb/wacom.c | 2 +- drivers/video/cyber2000fb.c | 2 + drivers/video/cyber2000fb.h | 1 + drivers/video/riva/fbdev.c | 4 +- fs/Makefile | 14 +- fs/affs/namei.c | 2 - fs/autofs/dir.c | 27 +- fs/autofs/dirhash.c | 4 +- fs/autofs4/root.c | 66 +- fs/bfs/dir.c | 6 - fs/binfmt_aout.c | 4 +- fs/buffer.c | 16 +- fs/coda/cache.c | 44 +- fs/coda/dir.c | 10 +- fs/coda/psdev.c | 2 + fs/cramfs/inode.c | 57 +- fs/dcache.c | 123 +- fs/devfs/base.c | 822 +++--- fs/devfs/util.c | 73 +- fs/exec.c | 69 +- fs/ext2/balloc.c | 2 +- fs/ext2/namei.c | 2 - fs/hfs/balloc.c | 2 + fs/hfs/catalog.c | 8 +- fs/hfs/dir.c | 2 - fs/hfs/file_hdr.c | 2 +- fs/hfs/hfs.h | 16 +- fs/hfs/hfs_btree.h | 4 +- fs/hfs/mdb.c | 2 +- fs/hfs/part_tbl.c | 2 +- fs/hpfs/namei.c | 2 - fs/inode.c | 4 +- fs/lockd/clntproc.c | 36 +- fs/lockd/svclock.c | 6 +- fs/minix/namei.c | 2 - fs/msdos/namei.c | 2 - fs/namei.c | 10 +- fs/ncpfs/dir.c | 25 +- fs/nfs/dir.c | 656 +++-- fs/nfs/inode.c | 29 +- fs/nfs/nfs2xdr.c | 7 - fs/nfs/nfs3proc.c | 18 +- fs/nfs/proc.c | 18 +- fs/nfs/read.c | 21 +- fs/nfs/write.c | 63 +- fs/nfsd/nfscache.c | 7 +- fs/nfsd/nfsfh.c | 8 +- fs/openpromfs/inode.c | 3 +- fs/partitions/check.c | 3 + fs/pipe.c | 3 +- fs/proc/base.c | 70 +- fs/proc/generic.c | 11 +- fs/proc/kcore.c | 17 +- fs/proc/proc_devtree.c | 1 - fs/proc/root.c | 29 - fs/qnx4/namei.c | 2 - fs/ramfs/inode.c | 62 +- fs/readdir.c | 47 + fs/smbfs/dir.c | 13 +- fs/smbfs/file.c | 23 +- fs/smbfs/proc.c | 45 - fs/super.c | 102 +- fs/sysv/namei.c | 2 - fs/udf/namei.c | 30 +- fs/ufs/namei.c | 2 - fs/umsdos/Makefile | 2 +- fs/umsdos/check.c | 229 -- fs/umsdos/dir.c | 6 +- fs/umsdos/inode.c | 8 - fs/umsdos/ioctl.c | 4 + fs/umsdos/namei.c | 19 +- fs/umsdos/rdir.c | 2 + fs/vfat/namei.c | 21 +- include/asm-alpha/ide.h | 3 +- include/asm-arm/arch-ebsa285/irq.h | 13 +- include/asm-arm/arch-l7200/dma.h | 23 + include/asm-arm/arch-l7200/hardware.h | 49 + include/asm-arm/arch-l7200/ide.h | 27 + include/asm-arm/arch-l7200/io.h | 210 ++ include/asm-arm/arch-l7200/irq.h | 66 + include/asm-arm/arch-l7200/irqs.h | 45 + include/asm-arm/arch-l7200/memory.h | 44 + include/asm-arm/arch-l7200/param.h | 23 + include/asm-arm/arch-l7200/processor.h | 27 + include/asm-arm/arch-l7200/serial_l7200.h | 101 + include/asm-arm/arch-l7200/system.h | 30 + include/asm-arm/arch-l7200/time.h | 68 + include/asm-arm/arch-l7200/timex.h | 20 + include/asm-arm/arch-l7200/uncompress.h | 19 + include/asm-arm/arch-l7200/vmalloc.h | 16 + include/asm-arm/hardirq.h | 14 +- include/asm-arm/proc-armo/semaphore.h | 75 - include/asm-arm/proc-armv/locks.h | 64 +- include/asm-arm/softirq.h | 8 +- include/asm-arm/string.h | 8 +- include/asm-arm/unistd.h | 4 +- include/asm-i386/apicdef.h | 3 +- include/asm-i386/hw_irq.h | 36 +- include/asm-i386/ide.h | 1 + include/asm-i386/uaccess.h | 22 +- include/asm-ia64/ide.h | 1 + include/asm-ppc/bitops.h | 2 +- include/asm-ppc/cpm_8260.h | 19 +- include/asm-ppc/init.h | 6 + include/asm-ppc/page.h | 3 + include/asm-ppc/siginfo.h | 2 +- include/asm-ppc/string.h | 10 + include/asm-sh/cache.h | 4 - include/asm-sh/checksum.h | 37 +- include/asm-sh/hitachi_se.h | 53 + include/asm-sh/ide.h | 11 +- include/asm-sh/io.h | 116 +- include/asm-sh/irq.h | 72 +- include/asm-sh/pgtable.h | 6 +- include/asm-sh/smc37c93x.h | 190 ++ include/asm-sh/system.h | 24 +- include/asm-sh/unistd.h | 6 +- include/asm-sparc/bitops.h | 2 +- include/asm-sparc/ide.h | 5 +- include/asm-sparc/irq.h | 2 +- include/asm-sparc/pgalloc.h | 2 +- include/asm-sparc/system.h | 2 +- include/asm-sparc/winmacro.h | 2 +- include/asm-sparc64/delay.h | 2 +- include/asm-sparc64/ide.h | 5 +- include/asm-sparc64/irq.h | 2 +- include/asm-sparc64/oplib.h | 2 +- include/asm-sparc64/processor.h | 2 +- include/asm-sparc64/system.h | 2 +- include/asm-sparc64/timer.h | 2 +- include/asm-sparc64/unistd.h | 6 +- include/linux/coda.h | 6 +- include/linux/dcache.h | 9 +- include/linux/elevator.h | 8 +- include/linux/fb.h | 3 + include/linux/fs.h | 9 +- include/linux/hdreg.h | 15 +- include/linux/hdsmart.h | 263 +- include/linux/ide.h | 52 +- include/linux/if_pppox.h | 5 +- include/linux/lvm.h | 6 +- include/linux/mount.h | 1 + include/linux/netfilter_ipv6.h | 10 + include/linux/netfilter_ipv6/ip6_tables.h | 452 ++++ include/linux/nfs_fs.h | 13 +- include/linux/nfs_mount.h | 7 - include/linux/nfs_page.h | 2 +- include/linux/nfs_xdr.h | 9 +- include/linux/pci_ids.h | 22 +- include/linux/proc_fs.h | 3 - include/linux/smb_fs.h | 1 - include/linux/smb_fs_i.h | 1 + include/linux/sunrpc/auth.h | 5 + include/linux/umsdos_fs.p | 8 - include/linux/usbdevice_fs.h | 2 + include/linux/vmalloc.h | 50 +- include/linux/wait.h | 2 +- include/linux/wrapper.h | 1 - include/net/slhc.h | 6 - init/main.c | 12 +- ipc/shm.c | 10 +- ipc/util.c | 14 +- kernel/exec_domain.c | 24 +- kernel/fork.c | 2 +- kernel/ksyms.c | 5 + mm/filemap.c | 65 +- mm/highmem.c | 6 +- mm/memory.c | 6 +- mm/slab.c | 2 +- mm/swap_state.c | 2 +- mm/swapfile.c | 2 +- mm/vmalloc.c | 40 +- mm/vmscan.c | 42 +- net/Makefile | 9 +- net/core/dev.c | 1 - net/core/skbuff.c | 10 +- net/ipv4/ip_gre.c | 2 +- net/ipv4/ipip.c | 4 +- net/ipv6/Config.in | 4 + net/ipv6/netfilter/Config.in | 49 + net/ipv6/netfilter/Makefile | 180 ++ net/ipv6/netfilter/ip6_tables.c | 1795 +++++++++++++ net/ipv6/netfilter/ip6t_MARK.c | 66 + net/ipv6/netfilter/ip6t_limit.c | 135 + net/ipv6/netfilter/ip6t_mark.c | 50 + net/ipv6/netfilter/ip6table_filter.c | 183 ++ net/sunrpc/auth.c | 12 +- net/unix/af_unix.c | 2 +- scripts/cramfs/mkcramfs.c | 22 +- scripts/header.tk | 106 +- scripts/mkdep.c | 14 - scripts/tail.tk | 10 + scripts/tkgen.c | 31 +- scripts/tkparse.c | 4 +- 481 files changed, 20715 insertions(+), 10344 deletions(-) create mode 100644 Documentation/DocBook/kernel-hacking.tmpl create mode 100644 Documentation/DocBook/kernel-locking.tmpl rewrite Documentation/DocBook/parportbook.tmpl (87%) create mode 100644 Documentation/kernel-doc-nano-HOWTO.txt delete mode 100644 arch/arm/kernel/iic.c rewrite arch/ppc/kernel/ptrace.c (70%) create mode 100644 arch/ppc/mbxboot/gzimage.c create mode 100644 arch/ppc/mbxboot/rdimage.c copy arch/ppc/{ => mbxboot}/vmlinux.lds (92%) create mode 100644 arch/sh/kernel/io_generic.c create mode 100644 arch/sh/kernel/io_se.c create mode 100644 arch/sh/kernel/irq_ipr.c delete mode 100644 arch/sh/kernel/irq_onchip.c create mode 100644 arch/sh/kernel/setup_se.c create mode 100644 drivers/char/tda7432.c create mode 100644 drivers/char/tda9875.c create mode 100644 drivers/char/tea6420.c create mode 100644 drivers/net/stnic.c delete mode 100644 fs/umsdos/check.c create mode 100644 include/asm-arm/arch-l7200/dma.h create mode 100644 include/asm-arm/arch-l7200/hardware.h create mode 100644 include/asm-arm/arch-l7200/ide.h create mode 100644 include/asm-arm/arch-l7200/io.h create mode 100644 include/asm-arm/arch-l7200/irq.h create mode 100644 include/asm-arm/arch-l7200/irqs.h create mode 100644 include/asm-arm/arch-l7200/memory.h create mode 100644 include/asm-arm/arch-l7200/param.h create mode 100644 include/asm-arm/arch-l7200/processor.h create mode 100644 include/asm-arm/arch-l7200/serial_l7200.h create mode 100644 include/asm-arm/arch-l7200/system.h create mode 100644 include/asm-arm/arch-l7200/time.h create mode 100644 include/asm-arm/arch-l7200/timex.h create mode 100644 include/asm-arm/arch-l7200/uncompress.h create mode 100644 include/asm-arm/arch-l7200/vmalloc.h delete mode 100644 include/asm-arm/proc-armo/semaphore.h create mode 100644 include/asm-sh/hitachi_se.h create mode 100644 include/asm-sh/smc37c93x.h rewrite include/linux/hdsmart.h (78%) create mode 100644 include/linux/netfilter_ipv6/ip6_tables.h delete mode 100644 include/net/slhc.h create mode 100644 net/ipv6/netfilter/Config.in create mode 100644 net/ipv6/netfilter/Makefile create mode 100644 net/ipv6/netfilter/ip6_tables.c create mode 100644 net/ipv6/netfilter/ip6t_MARK.c create mode 100644 net/ipv6/netfilter/ip6t_limit.c create mode 100644 net/ipv6/netfilter/ip6t_mark.c create mode 100644 net/ipv6/netfilter/ip6table_filter.c diff --git a/CREDITS b/CREDITS index f881963d7cf..868858e1404 100644 --- a/CREDITS +++ b/CREDITS @@ -251,6 +251,14 @@ S: Bouwensstraat 22 S: 6369 BG Simpelveld S: The Netherlands +N: Peter Berger +E: pberger@brimson.com +W: http://www.brimson.com +D: Author/maintainer of Digi AccelePort USB driver +S: 1549 Hiironen Rd. +S: Brimson, MN 55602 +S: USA + N: Hennus Bergman E: hennus@cybercomm.nl W: http://www.cybercomm.nl/~hennus/ @@ -318,6 +326,13 @@ P: 1024/04880A44 72E5 7031 4414 2EB6 F6B4 4CBD 1181 7032 0488 0A44 D: IEEE 1394 subsystem rewrite and maintainer D: Texas Instruments PCILynx IEEE 1394 driver +N: Al Borchers +E: alborchers@steinerpoint.com +D: Author/maintainer of Digi AccelePort USB driver +S: 4912 Zenith Ave. S. +S: Minneapolis, MN 55410 +S: USA + N: Marc Boucher E: marc@mbsi.ca P: CA 67 A5 1A 38 CE B6 F2 D5 83 51 03 D2 9C 30 9E CE D2 DD 65 @@ -942,14 +957,20 @@ D: Selection mechanism N: Andre Hedrick E: andre@linux-ide.org +E: ahedrick@atipa.com E: andre@suse.com +W: http://www.linux-ide.org/ D: Random SMP kernel hacker... D: Uniform Multi-Platform E-IDE driver D: Active-ATA-Chipset maddness.......... D: Ultra DMA 66/33 D: ATA-Smart Kernel Daemon -S: 580 Second Street, Suite 2 -S: Oakland, CA +S: Linux ATA Development (LAD) +S: Concord, CA +S: Atipa Linux Solutions +S: 6000 Connecticut Kansas City, MO 64120 +S: SuSE Linux, Inc. +S: 580 Second Street, Suite 210 Oakland, CA 94607 S: USA N: Jochen Hein @@ -1607,6 +1628,16 @@ D: XF86_Mach8 D: XF86_8514 D: cfdisk (curses based disk partitioning program) +N: Claudio S. Matsuoka +E: claudio@conectiva.com +E: claudio@helllabs.org +W: http://helllabs.org/~claudio +D: OV511 driver hacks +S: Conectiva S.A. +S: R. Tocantins 89 +S: 80050-430 Curitiba PR +S: Brazil + N: Heinz Mauelshagen E: mge@EZ-Darmstadt.Telekom.de D: Logical Volume Manager @@ -1614,6 +1645,14 @@ S: Bartningstr. 12 S: 64289 Darmstadt S: Germany +N: Mark W. McClelland +E: mmcclelland@delphi.com +E: mark@alpha.dyndns.org +W: http://alpha.dyndns.org/ov511/ +D: OV511 driver +S: (address available on request) +S: USA + N: Mike McLagan E: mike.mclagan@linux.org W: http://www.invlogic.com/~mmclagan diff --git a/Documentation/Configure.help b/Documentation/Configure.help index f65e265dc87..4d7b936a3d1 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -1496,7 +1496,7 @@ CONFIG_MD_RAID0 If unsure, say Y. -DANGEROUS! RAID-1/RAID-5 code +RAID-1/RAID-5 code (DANGEROUS) CONFIG_RAID15_DANGEROUS This new RAID1/RAID5 code has been freshly merged, and has not seen enough testing yet. While there are no known bugs in it, it might @@ -9779,6 +9779,12 @@ USB ZyXEL omni.net LCD Plus Driver CONFIG_USB_SERIAL_OMNINET Say Y here if you want to use a ZyXEL omni.net LCD ISDN TA. +USB Digi International AccelePort USB Serial Driver +CONFIG_USB_SERIAL_DIGI_ACCELEPORT + Say Y here if you want to use a Digi AccelePort USB 4 device, + a 4 port USB serial converter. The Digi Acceleport USB 2 and + 8 are not yet supported by this driver. + USB Printer support CONFIG_USB_PRINTER Say Y here if you want to connect a USB printer to your computer's @@ -10430,7 +10436,7 @@ CONFIG_NFSD Provide NFSv3 server support CONFIG_NFSD_V3 If you would like to include the NFSv3 server as well as the NFSv2 - server, say Y here. In unsure, say Y. + server, say Y here. If unsure, say Y. OS/2 HPFS file system support CONFIG_HPFS_FS @@ -12462,14 +12468,6 @@ CONFIG_APM_DISPLAY_BLANK backlight at all, or it might print a lot of errors to the console, especially if you are using gpm. -Ignore multiple suspend/standby events -CONFIG_APM_IGNORE_MULTIPLE_SUSPEND - This option is necessary on the IBM Thinkpad 560, but should work on - all other laptops. When the APM BIOS returns multiple suspend or - standby events while one is already being processed they will be - ignored. Without this the Thinkpad 560 has troubles with the user - level daemon apmd, and with the PCMCIA package pcmcia-cs. - Ignore multiple suspend/resume cycles CONFIG_APM_IGNORE_SUSPEND_BOUNCE This option is necessary on the Dell Inspiron 3200 and others, but diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 2079bae183d..8a40bfade13 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -1,4 +1,4 @@ -BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml kernel-api.sgml parportbook.sgml +BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml kernel-api.sgml parportbook.sgml kernel-hacking.sgml kernel-locking.sgml PS := $(patsubst %.sgml, %.ps, $(BOOKS)) PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) @@ -18,6 +18,9 @@ db2ps db2pdf: (echo "*** You need to install DocBook stylesheets ***"; \ exit 1) +%.eps: %.fig + -fig2dev -Leps $< $@ + $(TOPDIR)/scripts/docproc: $(MAKE) -C $(TOPDIR)/scripts docproc @@ -51,6 +54,7 @@ kernel-api.sgml: kernel-api.tmpl $(TOPDIR)/drivers/sound/sound_firmware.c \ $(TOPDIR)/drivers/net/wan/syncppp.c \ $(TOPDIR)/drivers/net/wan/z85230.c \ + $(TOPDIR)/fs/devfs/base.c \ $(TOPDIR)/kernel/pm.c \ $(TOPDIR)/kernel/ksyms.c \ $(TOPDIR)/net/netsyms.c \ @@ -66,12 +70,15 @@ TEX := $(patsubst %.sgml, %.tex, $(BOOKS)) LOG := $(patsubst %.sgml, %.log, $(BOOKS)) clean: - $(RM) core *~ - $(RM) $(BOOKS) - $(RM) $(DVI) $(AUX) $(TEX) $(LOG) + -$(RM) core *~ + -$(RM) $(BOOKS) + -$(RM) $(DVI) $(AUX) $(TEX) $(LOG) + -$(RM) parport-share.eps parport-multi.eps parport-structure.eps mrproper: clean - $(RM) $(PS) $(PDF) + -$(RM) $(PS) $(PDF) + +parportbook.ps: parport-share.eps parport-multi.eps parport-structure.eps %.ps : %.sgml db2ps db2ps $< diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 3f886e702ec..688cdf351cd 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -101,6 +101,11 @@ + + The Device File System +!Efs/devfs/base.c + + Power Management !Ekernel/pm.c diff --git a/Documentation/DocBook/kernel-hacking.tmpl b/Documentation/DocBook/kernel-hacking.tmpl new file mode 100644 index 00000000000..8d87787ad67 --- /dev/null +++ b/Documentation/DocBook/kernel-hacking.tmpl @@ -0,0 +1,1316 @@ + + + + + Unreliable Guide To Hacking The Linux Kernel + + + + Paul + Rusty + Russell + +
+ rusty@linuxcare.com +
+
+
+
+ + + 2000 + Paul Russell + + + + + This documentation 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 + + + + For more details see the file COPYING in the source + distribution of Linux. + + + + + This is the first release of this document as part of the kernel tarball. + + +
+ + + + + Introduction + + Welcome, gentle reader, to Rusty's Unreliable Guide to Linux + Kernel Hacking. This document describes the common routines and + general requirements for kernel code: its goal is to serve as a + primer for Linux kernel development for experienced C + programmers. I avoid implementation details: that's what the + code is for, and I ignore whole tracts of useful routines. + + + Before you read this, please understand that I never wanted to + write this document, being grossly under-qualified, but I always + wanted to read it, and this was the only way. I hope it will + grow into a compendium of best practice, common starting points + and random information. + + + + + The Players + + + At any time each of the CPUs in a system can be: + + + + + + not associated with any process, serving a hardware interrupt; + + + + + + not associated with any process, serving a softirq, tasklet or bh; + + + + + + running in kernel space, associated with a process; + + + + + + running a process in user space. + + + + + + There is a strict ordering between these: other than the last + category (userspace) each can only be pre-empted by those above. + For example, while a softirq is running on a CPU, no other + softirq will pre-empt it, but a hardware interrupt can. However, + any other CPUs in the system execute independently. + + + + We'll see a number of ways that the user context can block + interrupts, to become truly non-preemptable. + + + + User Context + + + User context is when you are coming in from a system call or + other trap: you can sleep, and you own the CPU (except for + interrupts) until you call schedule(). + In other words, user context (unlike userspace) is not pre-emptable. + + + + + You are always in user context on module load and unload, + and on operations on the block device layer. + + + + + In user context, the current pointer (indicating + the task we are currently executing) is valid, and + in_interrupt() + (include/asm/hardirq.h) is false + . + + + + + Beware that if you have interrupts or bottom halves disabled + (see below), in_interrupt() will return a + false positive. + + + + + + Hardware Interrupts (Hard IRQs) + + + Timer ticks, network cards and + keyboard are examples of real + hardware which produce interrupts at any time. The kernel runs + interrupt handlers, which services the hardware. The kernel + guarantees that this handler is never re-entered: if another + interrupt arrives, it is queued (or dropped). Because it + disables interrupts, this handler has to be fast: frequently it + simply acknowledges the interrupt, marks a `software interrupt' + for execution and exits. + + + + You can tell you are in a hardware interrupt, because + in_irq() returns true. + + + + Beware that this will return a false positive if interrupts are disabled + (see below). + + + + + + Software Interrupt Context: Bottom Halves, Tasklets, softirqs + + + Whenever a system call is about to return to userspace, or a + hardware interrupt handler exits, any `software interrupts' + which are marked pending (usually by hardware interrupts) are + run (kernel/softirq.c). + + + + Much of the real interrupt handling work is done here. Early in + the transition to SMP, there were only `bottom + halves' (BHs), which didn't take advantage of multiple CPUs. Shortly + after we switched from wind-up computers made of match-sticks and snot, + we abandoned this limitation. + + + + include/linux/interrupt.h lists the + different BH's. No matter how many CPUs you have, no two BHs will run at + the same time. This made the transition to SMP simpler, but sucks hard for + scalable performance. A very important bottom half is the timer + BH (include/linux/timer.h): you + can register to have it call functions for you in a given length of time. + + + + 2.3.43 introduced softirqs, and re-implemented the (now + deprecated) BHs underneath them. Softirqs are fully-SMP + versions of BHs: they can run on as many CPUs at once as + required. This means they need to deal with any races in shared + data using their own locks. A bitmask is used to keep track of + which are enabled, so the 32 available softirqs should not be + used up lightly. (Yes, people will + notice). + + + + tasklets (include/linux/interrupt.h) + are like softirqs, except they are dynamically-registrable (meaning you + can have as many as you want), and they also guarantee that any tasklet + will only run on one CPU at any time, although different tasklets can + run simultaneously (unlike different BHs). + + + + The name `tasklet' is misleading: they have nothing to do with `tasks', + and probably more to do with some bad vodka Alexey Kuznetsov had at the + time. + + + + + You can tell you are in a softirq (or bottom half, or tasklet) + using the in_softirq() macro + (include/asm/softirq.h). + + + + Beware that this will return a false positive if a bh lock (see below) + is held. + + + + + + + Some Basic Rules + + + + No memory protection + + + If you corrupt memory, whether in user context or + interrupt context, the whole machine will crash. Are you + sure you can't do what you want in userspace? + + + + + + No floating point or MMX + + + The FPU context is not saved; even in user + context the FPU state probably won't + correspond with the current process: you would mess with some + user process' FPU state. If you really want + to do this, you would have to explicitly save/restore the full + FPU state (and avoid context switches). It + is generally a bad idea; use fixed point arithmetic first. + + + + + + A rigid stack limit + + + The kernel stack is about 6K in 2.2 (for most + architectures: it's about 14K on the Alpha), and shared + with interrupts so you can't use it all. Avoid deep + recursion and huge local arrays on the stack (allocate + them dynamically instead). + + + + + + The Linux kernel is portable + + + Let's keep it that way. Your code should be 64-bit clean, + and endian-independent. You should also minimize CPU + specific stuff, e.g. inline assembly should be cleanly + encapsulated and minimized to ease porting. Generally it + should be restricted to the architecture-dependent part of + the kernel tree. + + + + + + + + ioctls: Not writing a new system call + + + A system call generally looks like this + + + +asmlinkage int sys_mycall(int arg) +{ + return 0; +} + + + + First, in most cases you don't want to create a new system call. + You create a character device and implement an appropriate ioctl + for it. This is much more flexible than system calls, doesn't have + to be entered in every architecture's + include/asm/unistd.h and + arch/kernel/entry.S file, and is much more + likely to be accepted by Linus. + + + + Inside the ioctl you're in user context to a process. When a + error occurs you return a negated errno (see + include/linux/errno.h), + otherwise you return 0. + + + + After you slept you should check if a signal occurred: the + Unix/Linux way of handling signals is to temporarily exit the + system call with the -ERESTARTSYS error. The + system call entry code will switch back to user context, process + the signal handler and then your system call will be restarted + (unless the user disabled that). So you should be prepared to + process the restart, e.g. if you're in the middle of manipulating + some data structure. + + + +if (signal_pending()) + return -ERESTARTSYS; + + + + If you're doing longer computations: first think userspace. If you + really want to do it in kernel you should + regularly check if you need to give up the CPU (remember there is + cooperative multitasking per CPU). Idiom: + + + +if (current->need_resched) + schedule(); /* Will sleep */ + + + + A short note on interface design: the UNIX system call motto is + "Provide mechanism not policy". + + + + + Recipes for Deadlock + + + You cannot call any routines which may sleep, unless: + + + + + You are in user context. + + + + + + You do not own any spinlocks. + + + + + + You have interrupts enabled (actually, Andi Kleen says + that the scheduling code will enable them for you, but + that's probably not what you wanted). + + + + + + Note that some functions may sleep implicitly: common ones are + the user space access functions (*_user) and memory allocation + functions without GFP_ATOMIC. + + + + You will eventually lock up your box if you break these rules. + + + + Really. + + + + + Common Routines + + + + <function>printk()</function> + <filename class=headerfile>include/linux/kernel.h</filename> + + + + printk() feeds kernel messages to the + console, dmesg, and the syslog daemon. It is useful for debugging + and reporting errors, and can be used inside interrupt context, + but use with caution: a machine which has its console flooded with + printk messages is unusable. It uses a format string mostly + compatible with ANSI C printf, and C string concatenation to give + it a first "priority" argument: + + + +printk(KERN_INFO "i = %u\n", i); + + + + See include/linux/kernel.h; + for other KERN_ values; these are interpreted by syslog as the + level. Special case: for printing an IP address use + + + +__u32 ipaddress; +printk(KERN_INFO "my ip: %d.%d.%d.%d\n", NIPQUAD(ipaddress)); + + + + printk() internally uses a 1K buffer and does + not catch overruns. Make sure that will be enough. + + + + + You will know when you are a real kernel hacker + when you start typoing printf as printk in your user programs :) + + + + + + + + Another sidenote: the original Unix Version 6 sources had a + comment on top of its printf function: "Printf should not be + used for chit-chat". You should follow that advice. + + + + + + + <function>copy_[to/from]_user()</function> + / + <function>get_user()</function> + / + <function>put_user()</function> + <filename class=headerfile>include/asm/uaccess.h</filename> + + + + [SLEEPS] + + + + put_user() and get_user() + are used to get and put single values (such as an int, char, or + long) from and to userspace. A pointer into userspace should + never be simply dereferenced: data should be copied using these + routines. Both return -EFAULT or 0. + + + copy_to_user() and + copy_from_user() are more general: they copy + an arbitrary amount of data to and from userspace. + + + Unlike put_user() and + get_user(), they return the amount of + uncopied data (ie. 0 still means + success). + + + [Yes, this moronic interface makes me cringe. Please submit a + patch and become my hero --RR.] + + + The functions may sleep implicitly. This should never be called + outside user context (it makes no sense), with interrupts + disabled, or a spinlock held. + + + + + <function>kmalloc()</function>/<function>kfree()</function> + <filename class=headerfile>include/linux/slab.h</filename> + + + [MAY SLEEP: SEE BELOW] + + + + These routines are used to dynamically request pointer-aligned + chunks of memory, like malloc and free do in userspace, but + kmalloc() takes an extra flag word. + Important values: + + + + + + + GFP_KERNEL + + + + + May sleep and swap to free memory. Only allowed in user + context, but is the most reliable way to allocate memory. + + + + + + + + GFP_ATOMIC + + + + + Don't sleep. Less reliable than GFP_KERNEL, + but may be called from interrupt context. You should + really have a good out-of-memory + error-handling strategy. + + + + + + + + GFP_DMA + + + + + Allocate ISA DMA lower than 16MB. If you don't know what that + is you don't need it. Very unreliable. + + + + + + + If you see a kmem_grow: Called nonatomically from int + warning message you called a memory allocation function + from interrupt context without GFP_ATOMIC. + You should really fix that. Run, don't walk. + + + + If you are allocating at least PAGE_SIZE + (include/asm/page.h) bytes, + consider using __get_free_pages() + + (include/linux/mm.h). It + takes an order argument (0 for page sized, 1 for double page, 2 + for four pages etc.) and the same memory priority flag word as + above. + + + + If you are allocating more than a page worth of bytes you can use + vmalloc(). It'll allocate virtual memory in + the kernel map. This block is not contiguous in physical memory, + but the MMU makes it look like it is for you + (so it'll only look contiguous to the CPUs, not to external device + drivers). If you really need large physically contiguous memory + for some weird device, you have a problem: it is poorly supported + in Linux because after some time memory fragmentation in a running + kernel makes it hard. The best way is to allocate the block early + in the boot process. + + + + Before inventing your own cache of often-used objects consider + using a slab cache in + include/linux/slab.h + + + + + <function>current</function> + <filename class=headerfile>include/asm/current.h</filename> + + + This global variable (really a macro) contains a pointer to + the current task structure, so is only valid in user context. + For example, when a process makes a system call, this will + point to the task structure of the calling process. It is + not NULL in interrupt context. + + + + + <function>local_irq_save()</function>/<function>local_irq_restore()</function> + <filename class=headerfile>include/asm/system.h</filename> + + + + These routines disable hard interrupts on the local CPU, and + restore them. They are reentrant; saving the previous state in + their one unsigned long flags argument. If you + know that interrupts are enabled, you can simply use + local_irq_disable() and + local_irq_enable(). + + + + + <function>local_bh_disable()</function>/<function>local_bh_enable()</function> + <filename class=headerfile>include/asm/softirq.h</filename> + + + These routines disable soft interrupts on the local CPU, and + restore them. They are reentrant; if soft interrupts were + disabled before, they will still be disabled after this pair + of functions has been called. They prevent softirqs, tasklets + and bottom halves from running on the current CPU. + + + + + <function>smp_processor_id</function>()/<function>cpu_[number/logical]_map()</function> + <filename class=headerfile>include/asm/smp.h</filename> + + + smp_processor_id() returns the current + processor number, between 0 and NR_CPUS (the + maximum number of CPUs supported by Linux, currently 32). These + values are not necessarily continuous: to get a number between 0 + and smp_num_cpus() (the number of actual + processors in this machine), the + cpu_number_map() function is used to map the + processor id to a logical number. + cpu_logical_map() does the reverse. + + + + + <type>__init</type>/<type>__exit</type>/<type>__initdata</type> + <filename class=headerfile>include/linux/init.h</filename> + + + After boot, the kernel frees up a special section; functions + marked with __init and data structures marked with + __initdata are dropped after boot is complete (within + modules this directive is currently ignored). __exit + is used to declare a function which is only required on exit: the + function will be dropped if this file is not compiled as a module. + See the header file for use. + + + + + + <function>__initcall()</function>/<function>module_init()</function> + <filename class=headerfile>include/linux/init.h</filename> + + Many parts of the kernel are well served as a module + (dynamically-loadable parts of the kernel). Using the + module_init() and + module_exit() macros it is easy to write code + without #ifdefs which can operate both as a module or built into + the kernel. + + + + The module_init() macro defines which + function is to be called at module insertion time (if the file is + compiled as a module), or at boot time: if the file is not + compiled as a module the module_init() macro + becomes equivalent to __initcall(), which + through linker magic ensures that the function is called on boot. + + + + The function can return a negative error number to cause + module loading to fail (unfortunately, this has no effect if + the module is compiled into the kernel). For modules, this is + called in user context, with interrupts enabled, and the + kernel lock held, so it can sleep. + + + + + <function>module_exit()</function> + <filename class=headerfile>include/linux/init.h</filename> + + + This macro defines the function to be called at module removal + time (or never, in the case of the file compiled into the + kernel). It will only be called if the module usage count has + reached zero. This function can also sleep, but cannot fail: + everything must be cleaned up by the time it returns. + + + + + <function>MOD_INC_USE_COUNT</function>/<function>MOD_DEC_USE_COUNT</function> + <filename class=headerfile>include/linux/module.h</filename> + + + These manipulate the module usage count, to protect against + removal (a module also can't be removed if another module uses + one of its exported symbols: see below). Every reference to + the module from user context should be reflected by this + counter (e.g. for every data structure or socket) before the + function sleeps. To quote Tim Waugh: + + + +/* THIS IS BAD */ +foo_open (...) +{ + stuff.. + if (fail) + return -EBUSY; + sleep.. (might get unloaded here) + stuff.. + MOD_INC_USE_COUNT; + return 0; +} + +/* THIS IS GOOD / +foo_open (...) +{ + MOD_INC_USE_COUNT; + stuff.. + if (fail) { + MOD_DEC_USE_COUNT; + return -EBUSY; + } + sleep.. (safe now) + stuff.. + return 0; +} + + + + + + Wait Queues + <filename class=headerfile>include/linux/wait.h</filename> + + + [SLEEPS] + + + + A wait queue is used to wait for someone to wake you up when a + certain condition is true. They must be used carefully to ensure + there is no race condition. You declare a + wait_queue_head_t, and then processes which want to + wait for that condition declare a wait_queue_t + referring to themselves, and place that in the queue. + + + + Declaring + + + You declare a wait_queue_head_t using the + DECLARE_WAIT_QUEUE_HEAD() macro, or using the + init_waitqueue_head() routine in your + initialization code. + + + + + Queuing + + + Placing yourself in the waitqueue is fairly complex, because you + must put yourself in the queue before checking the condition. + There is a macro to do this: + wait_event_interruptible() + + include/linux/sched.h The + first argument is the wait queue head, and the second is an + expression which is evaluated; the macro returns + 0 when this expression is true, or + -ERESTARTSYS if a signal is received. + The wait_event() version ignores signals. + + + + + Waking Up Queued Tasks + + + Call wake_up() + + include/linux/sched.h;, + which will wake up every process in the queue. The exception is + if one has TASK_EXCLUSIVE set, in which case + the remainder of the queue will not be woken. + + + + + + Atomic Operations + + + Certain operations are guaranteed atomic on all platforms. The + first class of operations work on atomic_t + + include/asm/atomic.h; this + contains a signed integer (at least 32 bits long), and you must use + these functions to manipulate or read atomic_t variables. + atomic_read() and + atomic_set() get and set the counter, + atomic_add(), + atomic_sub(), + atomic_inc(), + atomic_dec(), and + atomic_dec_and_test() (returns + true if it was decremented to zero). + + + + Yes. It returns true (i.e. != 0) if the + atomic variable is zero. + + + + Note that these functions are slower than normal arithmetic, and + so should not be used unnecessarily. On some platforms they + are much slower, like 32-bit Sparc where they use a spinlock. + + + + The second class of atomic operations is atomic bit operations, + defined in + + include/asm/bitops.h. These + operations generally take a pointer to the bit pattern, and a bit + number: 0 is the least significant bit. + set_bit(), clear_bit() + and change_bit() set, clear, and flip the + given bit. test_and_set_bit(), + test_and_clear_bit() and + test_and_change_bit() do the same thing, + except return true if the bit was previously set; these are + particularly useful for very simple locking. + + + + It is possible to call these operations with bit indices greater + than 31. The resulting behavior is strange on big-endian + platforms though so it is a good idea not to do this. + + + + + Symbols + + + Within the kernel proper, the normal linking rules apply + (ie. unless a symbol is declared to be file scope with the + static keyword, it can be used anywhere in the + kernel). However, for modules, a special exported symbol table is + kept which limits the entry points to the kernel proper. Modules + can also export symbols. + + + + <function>EXPORT_SYMBOL()</function> + <filename class=headerfile>include/linux/module.h</filename> + + + This is the classic method of exporting a symbol, and it works + for both modules and non-modules. In the kernel all these + declarations are often bundled into a single file to help + genksyms (which searches source files for these declarations). + See the comment on genksyms and Makefiles below. + + + + + <function>EXPORT_SYMTAB</function> + + + For convenience, a module usually exports all non-file-scope + symbols (ie. all those not declared static). If this + is defined before + + include/linux/module.h is + included, then only symbols explicit exported with + EXPORT_SYMBOL() will be exported. + + + + + + Routines and Conventions + + + Double-linked lists + <filename class=headerfile>include/linux/list.h</filename> + + + There are three sets of linked-list routines in the kernel + headers, but this one seems to be winning out (and Linus has + used it). If you don't have some particular pressing need for + a single list, it's a good choice. In fact, I don't care + whether it's a good choice or not, just use it so we can get + rid of the others. + + + + + Return Conventions + + + For code called in user context, it's very common to defy C + convention, and return 0 for success, + and a negative error number + (eg. -EFAULT) for failure. This can be + unintuitive at first, but it's fairly widespread in the networking + code, for example. + + + + The filesystem code uses ERR_PTR() + + include/linux/fs.h; to + encode a negative error number into a pointer, and + IS_ERR() and PTR_ERR() + to get it back out again: avoids a separate pointer parameter for + the error number. Icky, but in a good way. + + + + + Breaking Compilation + + + Linus and the other developers sometimes change function or + structure names in development kernels; this is not done just to + keep everyone on their toes: it reflects a fundamental change + (eg. can no longer be called with interrupts on, or does extra + checks, or doesn't do checks which were caught before). Usually + this is accompanied by a fairly complete note to the linux-kernel + mailing list; search the archive. Simply doing a global replace + on the file usually makes things worse. + + + + + Initializing structure members + + + The preferred method of initializing structures is to use the + gcc Labeled Elements extension, eg: + + +static struct block_device_operations opt_fops = { + open: opt_open, + release: opt_release, + ioctl: opt_ioctl, + check_media_change: opt_media_change, +}; + + + + This makes it easy to grep for, and makes it clear which + structure fields are set. You should do this because it looks + cool. + + + + + GNU Extensions + + + GNU Extensions are explicitly allowed in the Linux kernel. + Note that some of the more complex ones are not very well + supported, due to lack of general use, but the following are + considered standard (see the GCC info page section "C + Extensions" for more details - Yes, really the info page, the + man page is only a short summary of the stuff in info): + + + + + Inline functions + + + + + Statement expressions (ie. the ({ and }) constructs). + + + + + Declaring attributes of a function / variable / type + (__attribute__) + + + + + Labeled elements + + + + + typeof + + + + + Zero length arrays + + + + + Macro varargs + + + + + Arithmetic on void pointers + + + + + Non-Constant initializers + + + + + Assembler Instructions (not outside arch/ and include/asm/) + + + + + Function names as strings (__FUNCTION__) + + + + + __builtin_constant_p() + + + + + + Be wary when using long long in the kernel, the code gcc generates for + it is horrible and worse: division and multiplication does not work + on i386 because the GCC runtime functions for it are missing from + the kernel environment. + + + + + + + C++ + + + Using C++ in the kernel is usually a bad idea, because the + kernel does not provide the necessary runtime environment + and the include files are not tested for it. It is still + possible, but not recommended. If you really want to do + this, forget about exceptions at least. + + + + + #if + + + It is generally considered cleaner to use macros in header files + (or at the top of .c files) to abstract away functions rather than + using `#if' pre-processor statements throughout the source code. + + + + + + Putting Your Stuff in the Kernel + + + In order to get your stuff into shape for official inclusion, or + even to make a neat patch, there's administrative work to be + done: + + + + + Figure out whose pond you've been pissing in. Look at the top of + the source files, inside the MAINTAINERS + file, and last of all in the CREDITS file. + You should coordinate with this person to make sure you're not + duplicating effort, or trying something that's already been + rejected. + + + + Make sure you put your name and EMail address at the top of + any files you create or mangle significantly. This is the + first place people will look when they find a bug, or when + they want to make a change. + + + + + + Usually you want a configuration option for your kernel hack. + Edit Config.in in the appropriate directory + (but under arch/ it's called + config.in). The Config Language used is not + bash, even though it looks like bash; the safe way is to use only + the constructs that you already see in + Config.in files (see + Documentation/kbuild/config-language.txt). + It's good to run "make xconfig" at least once to test (because + it's the only one with a static parser). + + + + Variables which can be Y or N use bool followed by a + tagline and the config define name (which must start with + CONFIG_). The tristate function is the same, but + allows the answer M (which defines + CONFIG_foo_MODULE in your source, instead of + CONFIG_FOO) if CONFIG_MODULES + is enabled. + + + + You may well want to make your CONFIG option only visible if + CONFIG_EXPERIMENTAL is enabled: this serves as a + warning to users. There many other fancy things you can do: see + the the various Config.in files for ideas. + + + + + + Edit the Makefile: the CONFIG variables are + exported here so you can conditionalize compilation with `ifeq'. + If your file exports symbols then add the names to + MX_OBJS or OX_OBJS instead + of M_OBJS or O_OBJS, so + that genksyms will find them. + + + + + + Document your option in Documentation/Configure.help. Mention + incompatibilities and issues here. Definitely + end your description with if in doubt, say N + (or, occasionally, `Y'); this is for people who have no + idea what you are talking about. + + + + + + Put yourself in CREDITS if you've done + something noteworthy, usually beyond a single file (your name + should be at the top of the source files anyway). + MAINTAINERS means you want to be consulted + when changes are made to a subsystem, and hear about bugs; it + implies a more-than-passing commitment to some part of the code. + + + + + + + Kernel Cantrips + + + Some favorites from browsing the source. Feel free to add to this + list. + + + + include/linux/brlock.h: + + +extern inline void br_read_lock (enum brlock_indices idx) +{ + /* + * This causes a link-time bug message if an + * invalid index is used: + */ + if (idx >= __BR_END) + __br_lock_usage_bug(); + + read_lock(&__brlock_array[smp_processor_id()][idx]); +} + + + + include/linux/fs.h: + + +/* + * Kernel pointers have redundant information, so we can use a + * scheme where we can return either an error code or a dentry + * pointer with the same return value. + * + * This should be a per-architecture thing, to allow different + * error and pointer decisions. + */ + #define ERR_PTR(err) ((void *)((long)(err))) + #define PTR_ERR(ptr) ((long)(ptr)) + #define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) + + + + include/asm-i386/uaccess.h: + + + +#define copy_to_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_to_user((to),(from),(n)) : \ + __generic_copy_to_user((to),(from),(n))) + + + + arch/sparc/kernel/head.S: + + + +/* + * Sun people can't spell worth damn. "compatability" indeed. + * At least we *know* we can't spell, and use a spell-checker. + */ + +/* Uh, actually Linus it is I who cannot spell. Too much murky + * Sparc assembly will do this to ya. + */ +C_LABEL(cputypvar): + .asciz "compatability" + +/* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */ + .align 4 +C_LABEL(cputypvar_sun4m): + .asciz "compatible" + + + + arch/sparc/lib/checksum.S: + + + + /* Sun, you just can't beat me, you just can't. Stop trying, + * give up. I'm serious, I am going to kick the living shit + * out of you, game over, lights out. + */ + + + + + Thanks + + + Thanks to Andi Kleen for the idea, answering my questions, fixing + my mistakes, filling content, etc. Philipp Rumpf for more spelling + and clarity fixes, and some excellent non-obvious points. Werner + Almesberger for giving me a great summary of + disable_irq(), and Jes Sorensen and Andrea + Arcangeli added caveats. Michael Elizabeth Chastain for checking + and adding to the Configure section. Telsa Gwynne for teaching me DocBook. + + +
+ diff --git a/Documentation/DocBook/kernel-locking.tmpl b/Documentation/DocBook/kernel-locking.tmpl new file mode 100644 index 00000000000..397e7e95440 --- /dev/null +++ b/Documentation/DocBook/kernel-locking.tmpl @@ -0,0 +1,1221 @@ + + + + + Unreliable Guide To Locking + + + + Paul + Rusty + Russell + +
+ rusty@linuxcare.com +
+
+
+
+ + + 2000 + Paul Russell + + + + + This documentation 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 + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + Introduction + + Welcome, to Rusty's Remarkably Unreliable Guide to Kernel + Locking issues. This document describes the locking systems in + the Linux Kernel as we approach 2.4. + + + It looks like SMP + is here to stay; so everyone hacking on the kernel + these days needs to know the fundamentals of concurrency and locking + for SMP. + + + + The Problem With Concurrency + + (Skip this if you know what a Race Condition is). + + + In a normal program, you can increment a counter like so: + + + very_important_count++; + + + + This is what they would expect to happen: + + + + Expected Results + + + + + + Instance 1 + Instance 2 + + + + + + read very_important_count (5) + + + + add 1 (6) + + + + write very_important_count (6) + + + + + read very_important_count (6) + + + + add 1 (7) + + + + write very_important_count (7) + + + + +
+ + + This is what might happen: + + + + Possible Results + + + + + Instance 1 + Instance 2 + + + + + + read very_important_count (5) + + + + + read very_important_count (5) + + + add 1 (6) + + + + + add 1 (5) + + + write very_important_count (6) + + + + + write very_important_count (6) + + + +
+ + + This overlap, where what actually happens depends on the + relative timing of multiple tasks, is called a race condition. + The piece of code containing the concurrency issue is called a + critical region. And especially since Linux starting running + on SMP machines, they became one of the major issues in kernel + design and implementation. + + + The solution is to recognize when these simultaneous accesses + occur, and use locks to make sure that only one instance can + enter the critical region at any time. There are many + friendly primitives in the Linux kernel to help you do this. + And then there are the unfriendly primitives, but I'll pretend + they don't exist. + +
+
+ + + Two Main Types of Kernel Locks: Spinlocks and Semaphores + + + There are two main types of kernel locks. The fundamental type + is the spinlock + (include/asm/spinlock.h), + which is a very simple single-holder lock: if you can't get the + spinlock, you keep trying (spinning) until you can. Spinlocks are + very small and fast, and can be used anywhere. + + + The second type is a semaphore + (include/asm/semaphore.h): it + can have more than one holder at any time (the number decided at + initialization time), although it is most commonly used as a + single-holder lock (a mutex). If you can't get a semaphore, + your task will put itself on the queue, and be woken up when the + semaphore is released. This means the CPU will do something + else while you are waiting, but there are many cases when you + simply can't sleep, and so have to use a spinlock instead. + + + + Locks and Uniprocessor Kernels + + + For kernels compiled without CONFIG_SMP, spinlocks + do not exist at all. This is an excellent design decision: when + no-one else can run at the same time, there is no reason to + have a lock at all. + + + + You should always test your locking code with CONFIG_SMP + enabled, even if you don't have an SMP test box, because it + will still catch some (simple) kinds of deadlock. + + + + Semaphores still exist, because they are required for + synchronization between user + contexts, as we will see below. + + + + + Read/Write Lock Variants + + + Both spinlocks and semaphores have read/write variants: + rwlock_t and struct rw_semaphore. + These divide users into two classes: the readers and the writers. If + you are only reading the data, you can get a read lock, but to write to + the data you need the write lock. Many people can hold a read lock, + but a writer must be sole holder. + + + + This means much smoother locking if your code divides up + neatly along reader/writer lines. All the discussions below + also apply to read/write variants. + + + + + Locking Only In User Context + + + If you have a data structure which is only ever accessed from + user context, then you can use a simple semaphore + (linux/asm/semaphore.h) to protect it. This + is the most trivial case: you initialize the semaphore to the number + of resources available (usually 1), and call + down_interruptible() to grab the semaphore, and + up() to release it. There is also a + down(), which should be avoided, because it + will not return if a signal is received. + + + + Example: linux/net/core/netfilter.c allows + registration of new setsockopt() and + getsockopt() calls, with + nf_register_sockopt(). Registration and + de-registration are only done on module load and unload (and boot + time, where there is no concurrency), and the list of registrations + is only consulted for an unknown setsockopt() + or getsockopt() system call. The + nf_sockopt_mutex is perfect to protect this, + especially since the setsockopt and getsockopt calls may well + sleep. + + + + + Locking Between User Context and BHs + + + If a bottom half shares + data with user context, you have two problems. Firstly, the current + user context can be interrupted by a bottom half, and secondly, the + critical region could be entered from another CPU. This is where + spin_lock_bh() + (include/linux/spinlock.h) is + used. It disables bottom halves on that CPU, then grabs the lock. + spin_unlock_bh() does the reverse. + + + + This works perfectly for UP + as well: the spin lock vanishes, and this macro + simply becomes local_bh_disable() + (include/asm/softirq.h), which + protects you from the bottom half being run. + + + + + Locking Between User Context and Tasklets/Soft IRQs + + + This is exactly the same as above, because + local_bh_disable() actually disables all + softirqs and tasklets + on that CPU as well. It should really be + called `local_softirq_disable()', but the name has been preserved + for historical reasons. Similarly, + spin_lock_bh() would now be called + spin_lock_softirq() in a perfect world. + + + + + Locking Between Bottom Halves + + + Sometimes a bottom half might want to share data with + another bottom half (especially remember that timers are run + off a bottom half). + + + + The Same BH + + + Since a bottom half is never run on two CPUs at once, you + don't need to worry about your bottom half being run twice + at once, even on SMP. + + + + + Different BHs + + + Since only one bottom half ever runs at a time once, you + don't need to worry about race conditions with other bottom + halves. Beware that things might change under you, however, + if someone changes your bottom half to a tasklet. If you + want to make your code future-proof, pretend you're already + running from a tasklet (see below), and doing the extra + locking. Of course, if it's five years before that happens, + you're gonna look like a damn fool. + + + + + + Locking Between Tasklets + + + Sometimes a tasklet might want to share data with another + tasklet, or a bottom half. + + + + The Same Tasklet + + Since a tasklet is never run on two CPUs at once, you don't + need to worry about your tasklet being reentrant (running + twice at once), even on SMP. + + + + + Different Tasklets + + If another tasklet (or bottom half, such as a timer) wants + to share data with your tasklet, you will both need to use + spin_lock() and + spin_unlock() calls. + spin_lock_bh() is + unnecessary here, as you are already in a a tasklet, and + none will be run on the same CPU. + + + + + + Locking Between Softirqs + + + Often a softirq might + want to share data with itself, a tasklet, or a bottom half. + + + + The Same Softirq + + + The same softirq can run on the other CPUs: you can use a + per-CPU array (see ) for better + performance. If you're going so far as to use a softirq, + you probably care about scalable performance enough + to justify the extra complexity. + + + + You'll need to use spin_lock() and + spin_unlock() for shared data. + + + + + Different Softirqs + + + You'll need to use spin_lock() and + spin_unlock() for shared data, whether it + be a timer (which can be running on a different CPU), bottom half, + tasklet or the same or another softirq. + + + + + + + Hard IRQ Context + + + Hardware interrupts usually communicate with a bottom half, + tasklet or softirq. Frequently this involved putting work in a + queue, which the BH/softirq will take out. + + + + Locking Between Hard IRQ and Softirqs/Tasklets/BHs + + + If a hardware irq handler shares data with a softirq, you have + two concerns. Firstly, the softirq processing can be + interrupted by a hardware interrupt, and secondly, the + critical region could be entered by a hardware interrupt on + another CPU. This is where spin_lock_irq() is + used. It is defined to disable interrupts on that cpu, then grab + the lock. spin_unlock_irq() does the reverse. + + + + This works perfectly for UP as well: the spin lock vanishes, + and this macro simply becomes local_irq_disable() + (include/asm/smp.h), which + protects you from the softirq/tasklet/BH being run. + + + + spin_lock_irqsave() + (include/linux/spinlock.h) is a variant + which saves whether interrupts were on or off in a flags word, + which is passed to spin_lock_irqrestore(). This + means that the same code can be used inside an hard irq handler (where + interrupts are already off) and in softirqs (where the irq + disabling is required). + + + + + + Common Techniques + + + This section lists some common dilemmas and the standard + solutions used in the Linux kernel code. If you use these, + people will find your code simpler to understand. + + + + If I could give you one piece of advice: never sleep with anyone + crazier than yourself. But if I had to give you advice on + locking: keep it simple. + + + + Lock data, not code. + + + + Be reluctant to introduce new locks. + + + + Strangely enough, this is the exact reverse of my advice when + you have slept with someone crazier than yourself. + + + + No Writers in Interrupt Context + + + There is a fairly common case where an interrupt handler needs + access to a critical region, but does not need write access. + In this case, you do not need to use + read_lock_irq(), but only + read_lock() everywhere (since if an interrupt + occurs, the irq handler will only try to grab a read lock, which + won't deadlock). You will still need to use + write_lock_irq(). + + + + Similar logic applies to locking between softirqs/tasklets/BHs + which never need a write lock, and user context: + read_lock() and + write_lock_bh(). + + + + + Deadlock: Simple and Advanced + + + There is a coding bug where a piece of code tries to grab a + spinlock twice: it will spin forever, waiting for the lock to + be released (spinlocks and writelocks are not re-entrant in + Linux). This is trivial to diagnose: not a + stay-up-five-nights-talk-to-fluffy-code-bunnies kind of + problem. + + + + For a slightly more complex case, imagine you have a region + shared by a bottom half and user context. If you use a + spin_lock() call to protect it, it is + possible that the user context will be interrupted by the bottom + half while it holds the lock, and the bottom half will then spin + forever trying to get the same lock. + + + + Both of these are called deadlock, and as shown above, it can + occur even with a single CPU (although not on UP compiles, + since spinlocks vanish on kernel compiles with + CONFIG_SMP=n. You'll still get data corruption + in the second example). + + + + This complete lockup is easy to diagnose: on SMP boxes the + watchdog timer or compiling with DEBUG_SPINLOCKS set + (include/linux/spinlock.h) will show this up + immediately when it happens. + + + + A more complex problem is the so-called `deadly embrace', + involving two or more locks. Say you have a hash table: each + entry in the table is a spinlock, and a chain of hashed + objects. Inside a softirq handler, you sometimes want to + alter an object from one place in the hash to another: you + grab the spinlock of the old hash chain and the spinlock of + the new hash chain, and delete the object from the old one, + and insert it in the new one. + + + + There are two problems here. First, if your code ever + tries to move the object to the same chain, it will deadlock + with itself as it tries to lock it twice. Secondly, if the + same softirq on another CPU is trying to move another object + in the reverse direction, the following could happen: + + + + Consequences + + + + + + CPU 1 + CPU 2 + + + + + + Grab lock A -> OK + Grab lock B -> OK + + + Grab lock B -> spin + Grab lock A -> spin + + + +
+ + + The two CPUs will spin forever, waiting for the other to give up + their lock. It will look, smell, and feel like a crash. + + + + Preventing Deadlock + + + Textbooks will tell you that if you always lock in the same + order, you will never get this kind of deadlock. Practice + will tell you that this approach doesn't scale: when I + create a new lock, I don't understand enough of the kernel + to figure out where in the 5000 lock hierarchy it will fit. + + + + The best locks are encapsulated: they never get exposed in + headers, and are never held around calls to non-trivial + functions outside the same file. You can read through this + code and see that it will never deadlock, because it never + tries to grab another lock while it has that one. People + using your code don't even need to know you are using a + lock. + + + + A classic problem here is when you provide callbacks or + hooks: if you call these with the lock held, you risk simple + deadlock, or a deadly embrace (who knows what the callback + will do?). Remember, the other programmers are out to get + you, so don't do this. + + + + + Overzealous Prevention Of Deadlocks + + + Deadlocks are problematic, but not as bad as data + corruption. Code which grabs a read lock, searches a list, + fails to find what it wants, drops the read lock, grabs a + write lock and inserts the object has a race condition. + + + + If you don't see why, please stay the fuck away from my code. + + +
+ + + Per-CPU Data + + + A great technique for avoiding locking which is used fairly + widely is to duplicate information for each CPU. For example, + if you wanted to keep a count of a common condition, you could + use a spin lock and a single counter. Nice and simple. + + + + If that was too slow [it's probably not], you could instead + use a counter for each CPU [don't], then none of them need an + exclusive lock [you're wasting your time here]. To make sure + the CPUs don't have to synchronize caches all the time, align + the counters to cache boundaries by appending + `__cacheline_aligned' to the declaration + (include/linux/cache.h). + [Can't you think of anything better to do?] + + + + They will need a read lock to access their own counters, + however. That way you can use a write lock to grant exclusive + access to all of them at once, to tally them up. + + + + + Big Reader Locks + + + A classic example of per-CPU information is Ingo's `big + reader' locks + (linux/include/brlock.h). These + use the Per-CPU Data techniques described above to create a lock which + is very fast to get a read lock, but agonizingly slow for a write + lock. + + + + Fortunately, there are a limited number of these locks + available: you have to go through a strict interview process + to get one. + + + + + Avoiding Locks: Read And Write Ordering + + + Sometimes it is possible to avoid locking. Consider the + following case from the 2.2 firewall code, which inserted an + element into a single linked list in user context: + + + + new->next = i->next; + i->next = new; + + + + Here the author (Alan Cox, who knows what he's doing) assumes + that the pointer assignments are atomic. This is important, + because networking packets would traverse this list on bottom + halves without a lock. Depending on their exact timing, they + would either see the new element in the list with a valid + next pointer, or it would not be in the + list yet. + + + + Of course, the writes must be in this + order, otherwise the new element appears in the list with an + invalid next pointer, and any other + CPU iterating at the wrong time will jump through it into garbage. + Because modern CPUs reorder, Alan's code actually read as follows: + + + + new->next = i->next; + wmb(); + i->next = new; + + + + The wmb() is a write memory barrier + (include/asm/system.h): neither + the compiler nor the CPU will allow any writes to memory after the + wmb() to be visible to other hardware + before any of the writes before the wmb(). + + + + As i386 does not do write reordering, this bug would never + show up on that platform. On other SMP platforms, however, it + will. + + + + There is also rmb() for read ordering: to ensure + any previous variable reads occur before following reads. The simple + mb() macro combines both + rmb() and wmb(). + + + + Dropping or gaining a spinlock, and any atomic operation are + all defined to act as memory barriers (ie. as per the + mb() macro). + + + + There is a similar, but unrelated, problem with code like the + following: + + + + if (!(ctrack->status & IPS_CONFIRMED)) { + spin_lock_bh(&ip_conntrack_lock); + if (!(ctrack->status & IPS_CONFIRMED)) { + clean_from_lists(h->ctrack); + h->ctrack->status |= IPS_CONFIRMED; + } + spin_unlock_bh(&ip_conntrack_lock); + } + + + + In this case, the author has tried to be tricky: knowing that + the CONFIRMED bit is set and never reset in the status word, + you can test it outside the lock, and frequently avoid + grabbing the lock at all. However, the compiler could cache + the value in a register, rather than rereading it once the + lock is obtained, creating a subtle race. The way to get + around this is to declare the status field `volatile', or use + a temporary volatile pointer to achieve the same effect in + this one place. + + + + + Avoiding Locks: Atomic Operations + + + There are a number of atomic operations defined in + include/asm/atomic.h: these + are guaranteed to be seen atomically from all CPUs in the system, thus + avoiding races. If your shared data consists of a single counter, say, + these operations might be simpler than using spinlocks (although for + anything non-trivial using spinlocks is clearer). + + + + Note that the atomic operations are defined to act as both + read and write barriers on all platforms. + + + + + Protecting A Collection of Objects: Reference Counts + + + Locking a collection of objects is fairly easy: you get a + single spinlock, and you make sure you grab it before + searching, adding or deleting an object. + + + + The purpose of this lock is not to protect the individual + objects: you might have a separate lock inside each one for + that. It is to protect the data structure + containing the objects from race conditions. Often + the same lock is used to protect the contents of all the + objects as well, for simplicity, but they are inherently + orthogonal (and many other big words designed to confuse). + + + + Changing this to a read-write lock will often help markedly if + reads are far more common that writes. If not, there is + another approach you can use to reduce the time the lock is + held: reference counts. + + + + In this approach, an object has an owner, who sets the + reference count to one. Whenever you get a pointer to the + object, you increment the reference count (a `get' operation). + Whenever you relinquish a pointer, you decrement the reference + count (a `put' operation). When the owner wants to destroy + it, they mark it dead, and do a put. + + + + Whoever drops the reference count to zero (usually implemented + with atomic_dec_and_test()) actually cleans + up and frees the object. + + + + This means that you are guaranteed that the object won't + vanish underneath you, even though you no longer have a lock + for the collection. + + + + Here's some skeleton code: + + + + void create_foo(struct foo *x) + { + atomic_set(&x->use, 1); + spin_lock_bh(&list_lock); + ... insert in list ... + spin_unlock_bh(&list_lock); + } + + struct foo *get_foo(int desc) + { + struct foo *ret; + + spin_lock_bh(&list_lock); + ... find in list ... + if (ret) atomic_inc(&ret->use); + spin_unlock_bh(&list_lock); + + return ret; + } + + void put_foo(struct foo *x) + { + if (atomic_dec_and_test(&x->use)) + kfree(foo); + } + + void destroy_foo(struct foo *x) + { + spin_lock_bh(&list_lock); + ... remove from list ... + spin_unlock_bh(&list_lock); + + put_foo(x); + } + + + + Macros To Help You + + There are a set of debugging macros tucked inside + include/linux/netfilter_ipv4/lockhelp.h + and listhelp.h: these are very + useful for ensuring that locks are held in the right places to protect + infrastructure. + + + + + + Things Which Sleep + + + You can never call the following routines while holding a + spinlock, as they may sleep: + + + + + + Accesses to + userspace: + + + + + copy_from_user() + + + + + copy_to_user() + + + + + get_user() + + + + + put_user() + + + + + + + + kmalloc(GFP_KERNEL) + + + + + + printk(), which can be called from + user context, interestingly enough. + + + + + + + The Fucked Up Sparc + + + Alan Cox says the irq disable/enable is in the register + window on a sparc. Andi Kleen says when you do + restore_flags in a different function you mess up all the + register windows. + + + + So never pass the flags word set by + spin_lock_irqsave() and brethren to another + function (unless it's declared inline. Usually no-one + does this, but now you've been warned. Dave Miller can never do + anything in a straightforward manner (I can say that, because I have + pictures of him and a certain PowerPC maintainer in a compromising + position). + + + + + Racing Timers: A Kernel Pastime + + + Timers can produce their own special problems with races. + Consider a collection of objects (list, hash, etc) where each + object has a timer which is due to destroy it. + + + + If you want to destroy the entire collection (say on module + removal), you might do the following: + + + + /* THIS CODE BAD BAD BAD BAD: IF IT WAS ANY WORSE IT WOULD USE + HUNGARIAN NOTATION */ + spin_lock_bh(&list_lock); + + while (list) { + struct foo *next = list->next; + del_timer(&list->timer); + kfree(list); + list = next; + } + + spin_unlock_bh(&list_lock); + + + + Sooner or later, this will crash on SMP, because a timer can + have just gone off before the spin_lock_bh(), + and it will only get the lock after we + spin_unlock_bh(), and then try to free + the element (which has already been freed!). + + + + This can be avoided by checking the result of + del_timer(): if it returns + 1, the timer has been deleted. + If 0, it means (in this + case) that it is currently running, so we can do: + + + + retry: + spin_lock_bh(&list_lock); + + while (list) { + struct foo *next = list->next; + if (!del_timer(&list->timer)) { + /* Give timer a chance to delete this */ + spin_unlock_bh(&list_lock); + goto retry; + } + kfree(list); + list = next; + } + + spin_unlock_bh(&list_lock); + + + + Another common problem is deleting timers which restart + themselves (by calling add_timer() at the end + of their timer function). Because this is a fairly common case + which is prone to races, the function del_timer_sync() + (include/linux/timer.h) is + provided to handle this case. It returns the number of times the timer + had to be deleted before we finally stopped it from adding itself back + in. + + +
+ + + Further reading + + + + + Documentation/spinlocks.txt: + Linus Torvalds' spinlocking tutorial in the kernel sources. + + + + + + Unix Systems for Modern Architectures: Symmetric + Multiprocessing and Caching for Kernel Programmers: + + + + Curt Schimmel's very good introduction to kernel level + locking (not written for Linux, but nearly everything + applies). The book is expensive, but really worth every + penny to understand SMP locking. [ISBN: 0201633388] + + + + + + + Thanks + + + Thanks to Telsa Gwynne for DocBooking, neatening and adding + style. + + + + Thanks to Martin Pool, Philipp Rumpf, Stephen Rothwell, Paul + Mackerras, Ruedi Aschwanden, Alan Cox for proofreading, + correcting, flaming, commenting. + + + + Thanks to the cabal for having no influence on this document. + + + + + Glossary + + + bh + + + Bottom Half: for historical reasons, functions with + `_bh' in them often now refer to any software interrupt, e.g. + spin_lock_bh() blocks any software interrupt + on the current CPU. Bottom halves are deprecated, and will + eventually be replaced by tasklets. Only one bottom half will be + running at any time. + + + + + + Hardware Interrupt / Hardware IRQ + + + Hardware interrupt request. in_irq() returns + true in a hardware interrupt handler (it + also returns true when interrupts are blocked). + + + + + + Interrupt Context + + + Not user context: processing a hardware irq or software irq. + Indicated by the in_interrupt() macro + returning true (although it also + returns true when interrupts or BHs are blocked). + + + + + + SMP + + + Symmetric Multi-Processor: kernels compiled for multiple-CPU + machines. (CONFIG_SMP=y). + + + + + + softirq + + + Strictly speaking, one of up to 32 enumerated software + interrupts which can run on multiple CPUs at once. + Sometimes used to refer to tasklets and bottom halves as + well (ie. all software interrupts). + + + + + + Software Interrupt / Software IRQ + + + Software interrupt handler. in_irq() returns + false; in_softirq() + returns true. Tasklets, softirqs and + bottom halves all fall into the category of `software interrupts'. + + + + + + tasklet + + + A dynamically-registrable software interrupt, + which is guaranteed to only run on one CPU at a time. + + + + + + UP + + + Uni-Processor: Non-SMP. (CONFIG_SMP=n). + + + + + + User Context + + + The kernel executing on behalf of a particular + process or kernel thread (given by the current() + macro.) Not to be confused with userspace. Can be interrupted by + software or hardware interrupts. + + + + + + Userspace + + + A process executing its own code outside the kernel. + + + + + +
+ diff --git a/Documentation/DocBook/parportbook.tmpl b/Documentation/DocBook/parportbook.tmpl dissimilarity index 87% index 1644748addd..bf4d77873c0 100644 --- a/Documentation/DocBook/parportbook.tmpl +++ b/Documentation/DocBook/parportbook.tmpl @@ -1,1747 +1,2323 @@ - - - - - The Parallel Port Subsystem - - - - Tim - Waugh - -
- twaugh@redhat.com -
-
-
-
- - - 1999-2000 - Tim Waugh - - - - - This documentation 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 - - - - For more details see the file COPYING in the source - distribution of Linux. - - -
- - - - -Design goals - - -The problems - - - - - - -The first parallel port support for Linux came with the line -printer driver, lp. The printer driver is a -character special device, and (in Linux 2.0) had support for writing, -via write, and configuration and statistics -reporting via ioctl. - -The printer driver could be used on any computer that had an IBM -PC-compatible parallel port. Because some architectures have parallel -ports that aren't really the same as PC-style ports, other variants of -the printer driver were written in order to support Amiga and Atari -parallel ports. - -When the Iomega Zip drive was released, and a driver written for -it, a problem became apparent. The Zip drive is a parallel port -device that provides a parallel port of its own---it is designed to -sit between a computer and an attached printer, with the printer -plugged into the Zip drive, and the Zip drive plugged into the -computer. - -The problem was that, although printers and Zip drives were both -supported, for any given port only one could be used at a time. Only -one of the two drivers could be present in the kernel at once. This -was because of the fact that both drivers wanted to drive the same -hardware---the parallel port. When the printer driver initialised, it -would call the check_region function to make sure -that the IO region associated with the parallel port was free, and -then it would call request_region to allocate it. -The Zip drive used the same mechanism. Whichever driver initialised -first would gain exclusive control of the parallel port. - -The only way around this problem at the time was to make sure -that both drivers were available as loadable kernel modules. To use -the printer, load the printer driver module; then for the Zip drive, -unload the printer driver module and load the Zip driver -module. - -The net effect was that printing a document that was stored on a Zip -drive was a bit of an ordeal, at least if the Zip drive and printer -shared a parallel port. A better solution was needed. - -Zip drives are not the only devices that presented problems for -Linux. There are other devices with pass-through ports, for example -parallel port CD-ROM drives. There are also printers that report -their status textually rather than using simple error pins: sending a -command to the printer can cause it to report the number of pages that -it has ever printed, or how much free memory it has, or whether it is -running out of toner, and so on. The printer driver didn't originally -offer any facility for reading back this information (although Carsten -Gross added nibble mode readback support for kernel 2.2). - - - -The IEEE has issued a standards document called IEEE 1284, which -documents existing practice for parallel port communications in a -variety of modes. Those modes are: compatibility, -reverse nibble, reverse byte, ECP and EPP. Newer devices often use -the more advanced modes of transfer (ECP and EPP). In Linux 2.0, the -printer driver only supported compatibility mode -(i.e. normal printer protocol) and reverse nibble mode. - - - - -The solutions - - - -The parport code in Linux 2.2 was designed -to meet these problems of architectural differences in parallel ports, -of port-sharing between devices with pass-through ports, and of lack -of support for IEEE 1284 transfer modes. - - - -There are two layers to the -parport subsystem, only one of which deals -directly with the hardware. The other layer deals with sharing and -IEEE 1284 transfer modes. In this way, parallel support for a -particular architecture comes in the form of a module which registers -itself with the generic sharing layer. - - - -The sharing model provided by the parport -subsystem is one of exclusive access. A device driver, such as the -printer driver, must ask the parport layer for -access to the port, and can only use the port once access has been -granted. When it has finished a transaction, it can -tell the parport layer that it may release the -port for other device drivers to use. - - - -Devices with pass-through ports all manage to share a parallel -port with other devices in generally the same way. The device has a -latch for each of the pins on its pass-through port. The normal state -of affairs is pass-through mode, with the device copying the signal -lines between its host port and its pass-through port. When the -device sees a special signal from the host port, it latches the -pass-through port so that devices further downstream don't get -confused by the pass-through device's conversation with the host -parallel port: the device connected to the pass-through port (and any -devices connected in turn to it) are effectively cut off from the -computer. When the pass-through device has completed its transaction -with the computer, it enables the pass-through port again. - - - - - - - -This technique relies on certain special signals -being invisible to devices that aren't watching for them. This tends -to mean only changing the data signals and leaving the control signals -alone. IEEE 1284.3 documents a standard protocol for daisy-chaining -devices together with parallel ports. - - - -Support for standard transfer modes are provided as operations -that can be performed on a port, along with operations for setting the -data lines, or the control lines, or reading the status lines. These -operations appear to the device driver as function pointers; more -later. - - - - - - -Standard transfer modes - - - - -The standard transfer modes in use over the -parallel port are defined by a document called IEEE -1284. It really just codifies existing practice and documents -protocols (and variations on protocols) that have been in common use -for quite some time. - -The original definitions of which pin did what were set out by -Centronics Data Computer Corporation, but only the printer-side -interface signals were specified. - -By the early 1980s, IBM's host-side implementation had become -the most widely used. New printers emerged that claimed Centronics -compatibility, but although compatible with Centronics they differed -from one another in a number of ways. - -As a result of this, when IEEE 1284 was published in 1994, all -that it could really do was document the various protocols that are -used for printers (there are about six variations on a theme). - -In addition to the protocol used to talk to -Centronics-compatible printers, IEEE 1284 defined other protocols that -are used for unidirectional peripheral-to-host transfers (reverse -nibble and reverse byte) and for fast bidirectional transfers (ECP and -EPP). - - - - -Structure - - - - - - - - - - - - -Sharing core - - - -At the core of the parport subsystem is the -sharing mechanism (see drivers/parport/share.c). -This module, parport, is responsible for -keeping track of which ports there are in the system, which device -drivers might be interested in new ports, and whether or not each port -is available for use (or if not, which driver is currently using -it). - - - - -Parports and their overrides - - -The generic parport sharing code doesn't -directly handle the parallel port hardware. That is done instead by -low-level parport drivers. The -function of a low-level parport driver is to -detect parallel ports, register them with the sharing code, and -provide a list of access functions for each port. - -The most basic access functions that must be provided are ones -for examining the status lines, for setting the control lines, and for -setting the data lines. There are also access functions for setting -the direction of the data lines; normally they are in the -forward direction (that is, the computer drives them), -but some ports allow switching to reverse mode (driven -by the peripheral). There is an access function for examining the -data lines once in reverse mode. - - - - -IEEE 1284 transfer modes - - -Stacked on top of the sharing mechanism, but still in the -parport module, are functions for transferring -data. They are provided for the device drivers to use, and are very -much like library routines. Since these transfer functions are -provided by the generic parport core they must -use the lowest common denominator set of access -functions: they can set the control lines, examine the status lines, -and use the data lines. With some parallel ports the data lines can -only be set and not examined, and with other ports accessing the data -register causes control line activity; with these types of situations, -the IEEE 1284 transfer functions make a best effort attempt to do the -right thing. In some cases, it is not physically possible to use -particular IEEE 1284 transfer modes. - -The low-level parport drivers also provide -IEEE 1284 transfer functions, as names in the access function list. -The low-level driver can just name the generic IEEE 1284 transfer -functions for this. Some parallel ports can do IEEE 1284 transfers in -hardware; for those ports, the low-level driver can provide functions -to utilise that feature. - - - - - - - - -Pardevices and parport_drivers - -When a parallel port device driver (such as -lp) initialises it tells the sharing layer about -itself using parport_register_driver. The -information is put into a struct -parport_driver, which is put into a linked list. The -information in a struct parport_driver really -just amounts to some function pointers to callbacks in the parallel -port device driver. - -During its initialisation, a low-level port driver tells the -sharing layer about all the ports that it has found (using -parport_register_port), and the sharing layer -creates a struct parport for each of them. -Each struct parport contains (among other -things) a pointer to a struct -parport_operations, which is a list of function pointers -for the various operations that can be performed on a port. You can -think of a struct parport as a parallel port -object, if object-orientated programming -is your thing. The parport structures are -chained in a linked list, whose head is portlist -(in drivers/parport/share.c). - -Once the port has been registered, the low-level port driver -announces it. The parport_announce_port function -walks down the list of parallel port device drivers -(struct parport_drivers) calling the -attach function of each. - -Similarly, a low-level port driver can undo the effect of -registering a port with the -parport_unregister_port function, and device -drivers are notified using the detach -callback. - -Device drivers can undo the effect of registering themselves -with the parport_unregister_driver -function. - - - - - - -The IEEE 1284.3 API - -The ability to daisy-chain devices is very useful, but if every -device does it in a different way it could lead to lots of -complications for device driver writers. Fortunately, the IEEE are -standardising it in IEEE 1284.3, which covers daisy-chain devices and -port multiplexors. - -At the time of writing, IEEE 1284.3 has not been published, but -the draft specifies the on-the-wire protocol for daisy-chaining and -multiplexing, and also suggests a programming interface for using it. -That interface (or most of it) has been implemented in the -parport code in Linux. - -At initialisation of the parallel port bus, daisy-chained -devices are assigned addresses starting from zero. There can only be -four devices with daisy-chain addresses, plus one device on the end -that doesn't know about daisy-chaining and thinks it's connected -directly to a computer. - -Another way of connecting more parallel port devices is to use a -multiplexor. The idea is to have a device that is connected directly -to a parallel port on a computer, but has a number of parallel ports -on the other side for other peripherals to connect to (two or four -ports are allowed). The multiplexor switches control to different -ports under software control---it is, in effect, a programmable -printer switch. - -Combining the ability of daisy-chaining five devices together -with the ability to multiplex one parallel port between four gives the -potential to have twenty peripherals connected to the same parallel -port! - -In addition, of course, a single computer can have multiple -parallel ports. So, each parallel port peripheral in the system can -be identified with three numbers, or co-ordinates: the parallel port, -the multiplexed port, and the daisy-chain address. - - - - - - - - - - - - - - - -Each device in the system is numbered at initialisation (by -parport_daisy_init). You can convert between -this device number and its co-ordinates with -parport_device_num and -parport_device_coords. - - - int parport_device_num - int parport - int mux - int daisy - - - - int parport_device_coords - int devnum - int *parport - int *mux - int *daisy - - -Any parallel port peripheral will be connected directly or -indirectly to a parallel port on the system, but it won't have a -daisy-chain address if it does not know about daisy-chaining, and it -won't be connected through a multiplexor port if there is no -multiplexor. The special co-ordinate value -1 is -used to indicate these cases. - -Two functions are provided for finding devices based on their -IEEE 1284 Device ID: parport_find_device and -parport_find_class. - - - int parport_find_device - const char *mfg - const char *mdl - int from - - - - int parport_find_class - parport_device_class cls - int from - - -These functions take a device number (in addition to some other -things), and return another device number. They walk through the list -of detected devices until they find one that matches the requirements, -and then return that device number (or -1 if -there are no more such devices). They start their search at the -device after the one in the list with the number given (at -from+1, in other words). - - - - - - -Device driver's view - - - - - - - - - -This section is written from the point of view of the device -driver programmer, who might be writing a driver for a printer or a -scanner or else anything that plugs into the parallel port. It -explains how to use the parport interface to find -parallel ports, use them, and share them with other device -drivers. - -We'll start out with a description of the various functions that -can be called, and then look at a reasonably simple example of their -use: the printer driver. - -The interactions between the device driver and the -parport layer are as follows. First, the device -driver registers its existence with parport, in -order to get told about any parallel ports that have been (or will be) -detected. When it gets told about a parallel port, it then tells -parport that it wants to drive a device on that -port. Thereafter it can claim exclusive access to the port in order -to talk to its device. - -So, the first thing for the device driver to do is tell -parport that it wants to know what parallel ports -are on the system. To do this, it uses the -parport_register_device function: - - - - - - int parport_register_driver - struct parport_driver *driver - - -In other words, the device driver passes pointers to a couple of -functions to parport, and -parport calls attach for -each port that's detected (and detach for each -port that disappears -- yes, this can happen). - -The next thing that happens is that the device driver tells -parport that it thinks there's a device on the -port that it can drive. This typically will happen in the driver's -attach function, and is done with -parport_register_device: - - - struct pardevice *parport_register_device - struct parport *port - const char *name - int (*pf) - void * - void (*kf) - void * - void (*irq_func) - int, void *, struct pt_regs * - int flags - void *handle - - -The port comes from the parameter supplied -to the attach function when it is called, or -alternatively can be found from the list of detected parallel ports -directly with the (now deprecated) -parport_enumerate function. - -The next three parameters, pf, -kf, and irq_func, are -more function pointers. These callback functions get called under -various circumstances, and are always given the -handle as one of their parameters. - -The preemption callback, pf, is called -when the driver has claimed access to the port but another device -driver wants access. If the driver is willing to let the port go, it -should return zero and the port will be released on its behalf. There -is no need to call parport_release. If -pf gets called at a bad time for letting the -port go, it should return non-zero and no action will be taken. It is -good manners for the driver to try to release the port at the earliest -opportunity after its preemption callback is called. - -The kick callback, kf, is -called when the port can be claimed for exclusive access; that is, -parport_claim is guaranteed to succeed inside the -kick callback. If the driver wants to claim the port -it should do so; otherwise, it need not take any action. - -The irq_func callback is called, -predictably, when a parallel port interrupt is generated. But it is -not the only code that hooks on the interrupt. The sequence is this: -the lowlevel driver is the one that has done -request_irq; it then does whatever -hardware-specific things it needs to do to the parallel port hardware -(for PC-style ports, there is nothing special to do); it then tells -the IEEE 1284 code about the interrupt, which may involve reacting to -an IEEE 1284 event, depending on the current IEEE 1284 phase; and -finally the irq_func function is called. - -None of the callback functions are allowed to block. - -The flags are for telling -parport any requirements or hints that are -useful. The only useful value here (other than -0, which is the usual value) is -PARPORT_DEV_EXCL. The point of that flag is to -request exclusive access at all times---once a driver has successfully -called parport_register_device with that flag, no -other device drivers will be able to register devices on that port -(until the successful driver deregisters its device, of -course). - -The PARPORT_DEV_EXCL flag is for preventing -port sharing, and so should only be used when sharing the port with -other device drivers is impossible and would lead to incorrect -behaviour. Use it sparingly! - -Devices can also be registered by device drivers based on their -device numbers (the same device numbers as in the previous -section). - -The parport_open function is similar to -parport_register_device, and -parport_close is the equivalent of -parport_unregister_device. The difference is -that parport_open takes a device number rather -than a pointer to a struct parport. - - - struct pardevice *parport_open - int devnum - int (*pf) - void * - int (*kf) - void * - int (*irqf) - int, void *, struct pt_regs * - int flags - void *handle - - - - void parport_close - struct pardevice *dev - - - - struct pardevice *parport_register_device - struct parport *port - const char *name - int (*pf) - void * - int (*kf) - void * - int (*irqf) - int, void *, struct pt_regs * - int flags - void *handle - - - - void parport_unregister_device - struct pardevice *dev - - -The intended use of these functions is during driver -initialisation while the driver looks for devices that it supports, as -demonstrated by the following code fragment: - - - - -Once your device driver has registered its device and been -handed a pointer to a struct pardevice, the -next thing you are likely to want to do is communicate with the device -you think is there. To do that you'll need to claim access to the -port. - - - int parport_claim - struct pardevice *dev - - - - int parport_claim_or_block - struct pardevice *dev - - - - void parport_release - struct pardevice *dev - - -To claim access to the port, use -parport_claim or -parport_claim_or_block. The first of these will -not block, and so can be used from interrupt context. If -parport_claim succeeds it will return zero and -the port is available to use. It may fail (returning non-zero) if the -port is in use by another driver and that driver is not willing to -relinquish control of the port. - -The other function, parport_claim_or_block, -will block if necessary to wait for the port to be free. If it slept, -it returns 1; if it succeeded without needing to -sleep it returns 0. If it fails it will return a -negative error code. - -When you have finished communicating with the device, you can -give up access to the port so that other drivers can communicate with -their devices. The parport_release function -cannot fail, but it should not be called without the port claimed. -Similarly, you should not try to claim the port if you already have it -claimed. - -You may find that although there are convenient points for your -driver to relinquish the parallel port and allow other drivers to talk -to their devices, it would be preferable to keep hold of the port. -The printer driver only needs the port when there is data to print, -for example, but a network driver (such as PLIP) could be sent a -remote packet at any time. With PLIP, it is no huge catastrophe if a -network packet is dropped, since it will likely be sent again, so it -is possible for that kind of driver to share the port with other -(pass-through) devices. - -The parport_yield and -parport_yield_blocking functions are for marking -points in the driver at which other drivers may claim the port and use -their devices. Yielding the port is similar to releasing it and -reclaiming it, but it more efficient because nothing is done if there -are no other devices needing the port. In fact, nothing is done even -if there are other devices waiting but the current device is still -within its timeslice. The default timeslice is half a -second, but it can be adjusted via a /proc -entry. - - - int parport_yield - struct pardevice *dev - - - - int parport_yield_blocking - struct pardevice *dev - - -The first of these, parport_yield, will not -block but as a result may fail. The return value for -parport_yield is the same as for -parport_claim. The blocking version, -parport_yield_blocking, has the same return code -as parport_claim_or_block. - -Once the port has been claimed, the device driver can use the -functions in the struct parport_operations -pointer in the struct parport it has a -pointer to. For example: - - -ops->write_data (port, d); -]]> - -Some of these operations have shortcuts. For -instance, parport_write_data is equivalent to the -above, but may be a little bit faster (it's a macro that in some cases -can avoid needing to indirect through port and -ops). - - - - -Port drivers - - - -To recap, then: - - - - - -The device driver registers itself with parport. - - - - - -A low-level driver finds a parallel port and registers it with -parport (these first two things can happen in -either order). This registration creates a struct -parport which is linked onto a list of known ports. - - - - - -parport calls the attach -function of each registered device driver, passing it the pointer to -the new struct parport. - - - - - -The device driver gets a handle from parport, for -use with -parport_claim/release. This -handle takes the form of a pointer to a struct -pardevice, representing a particular device on the -parallel port, and is acquired using -parport_register_device. - - - - - -The device driver claims the port using -parport_claim (or -function_claim_or_block). - - - - - -Then it goes ahead and uses the port. When finished it releases the -port. - - - - - -The purpose of the low-level drivers, then, is to detect -parallel ports and provide methods of accessing them -(i.e. implementing the operations in struct -parport_operations). - - - - - - -A more complete description of which operation is supposed to do -what is available in -Documentation/parport-lowlevel.txt. - - - - -The printer driver - - - - -The printer driver, lp is a character -special device driver and a parport client. As a -character special device driver it registers a struct -file_operations using -register_chrdev, with pointers filled in for -write, ioctl, -open and -release. As a client of -parport, it registers a struct -parport_driver using -parport_register_driver, so that -parport knows to call -lp_attach when a new parallel port is discovered -(and lp_detach when it goes away). - -The parallel port console functionality is also implemented in -lp.c, but that won't be covered here (it's quite -simple though). - -The initialisation of the driver is quite easy to understand -(see lp_init). The lp_table -is an array of structures that contain information about a specific -device (the struct pardevice associated with -it, for example). That array is initialised to sensible values first -of all. - -Next, the printer driver calls -register_chrdev passing it a pointer to -lp_fops, which contains function pointers for the -printer driver's implementation of open, -write, and so on. This part is the same as for -any character special device driver. - -After successfully registering itself as a character special -device driver, the printer driver registers itself as a -parport client using -parport_register_driver. It passes a pointer to -this structure: - - - - -The lp_detach function is not very -interesting (it does nothing); the interesting bit is -lp_attach. What goes on here depends on whether -the user supplied any parameters. The possibilities are: no -parameters supplied, in which case the printer driver uses every port -that is detected; the user supplied the parameter auto, -in which case only ports on which the device ID string indicates a -printer is present are used; or the user supplied a list of parallel -port numbers to try, in which case only those are used. - -For each port that the printer driver wants to use (see -lp_register), it calls -parport_register_device and stores the resulting -struct pardevice pointer in the -lp_table. If the user told it to do so, it then -resets the printer. - -The other interesting piece of the printer driver, from the -point of view of parport, is -lp_write. In this function, the user space -process has data that it wants printed, and the printer driver hands -it off to the parport code to deal with. - -The parport functions it uses that we have -not seen yet are parport_negotiate, -parport_set_timeout, and -parport_write. These functions are part of the -IEEE 1284 implementation. - -The way the IEEE 1284 protocol works is that the host tells the -peripheral what transfer mode it would like to use, and the peripheral -either accepts that mode or rejects it; if the mode is rejected, the -host can try again with a different mode. This is the negotation -phase. Once the peripheral has accepted a particular transfer mode, -data transfer can begin that mode. - -The particular transfer mode that the printer driver wants to -use is named in IEEE 1284 as compatibility mode, and -the function to request a particular mode is called -parport_negotiate. - - - int parport_negotiate - struct parport *port - int mode - - -The modes parameter is a symbolic -constant representing an IEEE 1284 mode; in this instance, it is -IEEE1284_MODE_COMPAT. (Compatibility mode is -slightly different to the other modes---rather than being specifically -requested, it is the default until another mode is selected.) - -Back to lp_write then. First, access to -the parallel port is secured with -parport_claim_or_block. At this point the driver -might sleep, waiting for another driver (perhaps a Zip drive driver, -for instance) to let the port go. Next, it goes to compatibility mode -using parport_negotiate. - -The main work is done in the write-loop. In particular, the -line that hands the data over to parport -reads: - - - - -The parport_write function writes data to -the peripheral using the currently selected transfer mode -(compatibility mode, in this case). It returns the number of bytes -successfully written: - - - ssize_t parport_write - struct parport *port - const void *buf - size_t len - - - - ssize_t parport_read - struct parport *port - void *buf - size_t len - - -(parport_read does what it sounds like, but -only works for modes in which reverse transfer is possible. Of -course, parport_write only works in modes in -which forward transfer is possible, too.) - -The buf pointer should be to kernel space -memory, and obviously the len parameter -specifies the amount of data to transfer. - -In fact what parport_write does is call the -appropriate block transfer function from the struct -parport_operations: - - - - -The transfer code in parport will tolerate -a data transfer stall only for so long, and this timeout can be -specified with parport_set_timeout, which returns -the previous timeout: - - - long parport_set_timeout - struct pardevice *dev - long inactivity - - -This timeout is specific to the device, and is restored on -parport_claim. - - - - -User-level device drivers - - - -Introduction to ppdev - -The printer is accessible through /dev/lp0; -in the same way, the parallel port itself is accessible through -/dev/parport0. The difference is in the level of -control that you have over the wires in the parallel port -cable. - -With the printer driver, a user-space program (such as the -printer spooler) can send bytes in printer protocol. -Briefly, this means that for each byte, the eight data lines are set -up, then a strobe line tells the printer to look at the -data lines, and the printer sets an acknowledgement -line to say that it got the byte. The printer driver also allows the -user-space program to read bytes in nibble mode, which -is a way of transferring data from the peripheral to the computer half -a byte at a time (and so it's quite slow). - -In contrast, the ppdev driver (accessed via -/dev/parport0) allows you to: - - - - - -examine status lines, - - - - - -set control lines, - - - - - -set/examine data lines (and control the direction of the data lines), - - - - - -wait for an interrupt (triggered by one of the status lines), - - - - - -find out how many new interrupts have occurred, - - - - - -set up a response to an interrupt, - - - - - -use IEEE 1284 negotiation (for telling peripheral which transfer mode, -to use) - - - - - -transfer data using a specified IEEE 1284 mode. - - - - - - - - -User-level or kernel-level driver? - -The decision of whether to choose to write a kernel-level device -driver or a user-level device driver depends on several factors. One -of the main ones from a practical point of view is speed: kernel-level -device drivers get to run faster because they are not preemptable, -unlike user-level applications. - -Another factor is ease of development. It is in general easier -to write a user-level driver because (a) one wrong move does not -result in a crashed machine, (b) you have access to user libraries -(such as the C library), and (c) debugging is easier. - - - - -Programming interface - -The ppdev interface is largely the same as -that of other character special devices, in that it supports -open, close, -read, write, and -ioctl. - - -Starting and stopping: <function>open</function> and -<function>close</function> - -The device node /dev/parport0 represents -any device that is connected to parport0, the -first parallel port in the system. Each time the device node is -opened, it represents (to the process doing the opening) a different -device. It can be opened more than once, but only one instance can -actually be in control of the parallel port at any time. A process -that has opened /dev/parport0 shares the parallel -port in the same way as any other device driver. A user-land driver -may be sharing the parallel port with in-kernel device drivers as well -as other user-land drivers. - - - -Control: <function>ioctl</function> - -Most of the control is done, naturally enough, via the -ioctl call. Using ioctl, -the user-land driver can control both the ppdev -driver in the kernel and the physical parallel port itself. The -ioctl call takes as parameters a file descriptor -(the one returned from opening the device node), a command, and -optionally (a pointer to) some data. - - -PPCLAIM - - -Claims access to the port. As a user-land device driver writer, -you will need to do this before you are able to actually change the -state of the parallel port in any way. Note that some operations only -affect the ppdev driver and not the port, such as -PPSETMODE; they can be performed while access to -the port is not claimed. - - - -PPEXCL - - -Instructs the kernel driver to forbid any sharing of the port -with other drivers, i.e. it requests exclusivity. The -PPEXCL command is only valid when the port is not -already claimed for use, and it may mean that the next -PPCLAIM ioctl will fail: -some other driver may already have registered itself on that -port. - -Most device drivers don't need exclusive access to the port. -It's only provided in case it is really needed, for example for -devices where access to the port is required for extensive periods of -time (many seconds). - -Note that the PPEXCL -ioctl doesn't actually claim the port there and -then---action is deferred until the PPCLAIM -ioctl is performed. - - - -PPRELEASE - - -Releases the port. Releasing the port undoes the effect of -claiming the port. It allows other device drivers to talk to their -devices (assuming that there are any). - - - -PPYIELD - - -Yields the port to another driver. This -ioctl is a kind of short-hand for releasing the -port and immediately reclaiming it. It gives other drivers a chance -to talk to their devices, but afterwards claims the port back. An -example of using this would be in a user-land printer driver: once a -few characters have been written we could give the port to another -device driver for a while, but if we still have characters to send to -the printer we would want the port back as soon as possible. - -It is important not to claim the parallel port for too long, as -other device drivers will have no time to service their devices. If -your device does not allow for parallel port sharing at all, it is -better to claim the parallel port exclusively (see -PPEXCL). - - - -PPNEGOT - - -Performs IEEE 1284 negotiation into a particular mode. Briefly, -negotiation is the method by which the host and the peripheral decide -on a protocol to use when transferring data. - -An IEEE 1284 compliant device will start out in compatibility -mode, and then the host can negotiate to another mode (such as -ECP). - -The ioctl parameter should be a pointer to -an int; values for this are in -parport.h and include: - - -IEEE1284_MODE_COMPAT -IEEE1284_MODE_NIBBLE -IEEE1284_MODE_BYTE -IEEE1284_MODE_EPP -IEEE1284_MODE_ECP - - -The PPNEGOT ioctl -actually does two things: it performs the on-the-wire negotiation, and -it sets the behaviour of subsequent -read/write calls so that -they use that mode (but see PPSETMODE). - - - -PPSETMODE - - -Sets which IEEE 1284 protocol to use for the -read and write calls. - -The ioctl parameter should be a pointer to -an int. - - - -PPGETTIME - - -Retrieves the time-out value. The read and -write calls will time out if the peripheral -doesn't respond quickly enough. The PPGETTIME -ioctl retrieves the length of time that the -peripheral is allowed to have before giving up. - -The ioctl parameter should be a pointer to -a struct timeval. - - - -PPSETTIME - - -Sets the time-out. The ioctl parameter -should be a pointer to a struct -timeval. - - - -PPWCONTROL - - -Sets the control lines. The ioctl -parameter is a pointer to an unsigned char, the bitwise -OR of the control line values in -parport.h. - - - -PPRCONTROL - - -Returns the last value written to the control register, in the -form of an unsigned char: each bit corresponds to a -control line (although some are unused). The -ioctl parameter should be a pointer to an -unsigned char. - -This doesn't actually touch the hardware; the last value written -is remembered in software. This is because some parallel port -hardware does not offer read access to the control register. - -The control lines bits are defined in -parport.h: - - -PARPORT_CONTROL_STROBE -PARPORT_CONTROL_AUTOFD -PARPORT_CONTROL_SELECT -PARPORT_CONTROL_INIT - - - - -PPFCONTROL - - -Frobs the control lines. Since a common operation is to change -one of the control signals while leaving the others alone, it would be -quite inefficient for the user-land driver to have to use -PPRCONTROL, make the change, and then use -PPWCONTROL. Of course, each driver could -remember what state the control lines are supposed to be in (they are -never changed by anything else), but in order to provide -PPRCONTROL, ppdev must -remember the state of the control lines anyway. - -The PPFCONTROL ioctl -is for frobbing control lines, and is like -PPWCONTROL but acts on a restricted set of -control lines. The ioctl parameter is a pointer -to a struct ppdev_frob_struct: - - - - - -The mask and -val fields are bitwise ORs of control line -names (such as in PPWCONTROL). The operation -performed by PPFCONTROL is: - - - - - -In other words, the signals named in -mask are set to the values in -val. - - - -PPRSTATUS - - -Returns an unsigned char containing bits set for -each status line that is set (for instance, -PARPORT_STATUS_BUSY). The -ioctl parameter should be a pointer to an -unsigned char. - - - -PPDATADIR - - -Controls the data line drivers. Normally the computer's -parallel port will drive the data lines, but for byte-wide transfers -from the peripheral to the host it is useful to turn off those drivers -and let the peripheral drive the signals. (If the drivers on the -computer's parallel port are left on when this happens, the port might -be damaged.) - -This is only needed in conjunction with -PPWDATA or PPRDATA. - -The ioctl parameter is a pointer to an -int. If the int is zero, the drivers are -turned on (forward direction); if non-zero, the drivers are turned off -(reverse direction). - - - -PPWDATA - - -Sets the data lines (if in forward mode). The -ioctl parameter is a pointer to an unsigned -char. - - - -PPRDATA - - -Reads the data lines (if in reverse mode). The -ioctl parameter is a pointer to an unsigned -char. - - - -PPCLRIRQ - - -Clears the interrupt count. The ppdev -driver keeps a count of interrupts as they are triggered. -PPCLRIRQ stores this count in an -int, a pointer to which is passed in as the -ioctl parameter. - -In addition, the interrupt count is reset to zero. - - - -PPWCTLONIRQ - - -Set a trigger response. Afterwards when an interrupt is -triggered, the interrupt handler will set the control lines as -requested. The ioctl parameter is a pointer to -an unsigned char, which is interpreted in the same way as -for PPWCONTROL. - -The reason for this ioctl is simply speed. -Without this ioctl, responding to an interrupt -would start in the interrupt handler, switch context to the user-land -driver via poll or select, -and then switch context back to the kernel in order to handle -PPWCONTROL. Doing the whole lot in the interrupt -handler is a lot faster. - - - - - - - - - - -Transferring data: <function>read</function> and -<function>write</function> - -Transferring data using read and -write is straightforward. The data is -transferring using the current IEEE 1284 mode (see the -PPSETMODE ioctl). For modes -which can only transfer data in one direction, only the appropriate -function will work, of course. - - - -Waiting for events: <function>poll</function> and -<function>select</function> - -The ppdev driver provides user-land device -drivers with the ability to wait for interrupts, and this is done -using poll (and select, -which is implemented in terms of poll). - -When a user-land device driver wants to wait for an interrupt, -it sleeps with poll. When the interrupt arrives, -ppdev wakes it up (with a read -event, although strictly speaking there is nothing to actually -read). - - - - - - -Examples - -Presented here are two demonstrations of how to write a simple -printer driver for ppdev. Firstly we will use -the write function, and after that we will drive -the control and data lines directly. - -The first thing to do is to actually open the device. - - - -Here name should be something along the lines -of "/dev/parport0". (If you don't have any -/dev/parport files, you can make them with -mknod; they are character special device nodes with -major 99.) - -In order to do anything with the port we need to claim access to -it. - - - -Our printer driver will copy its input (from -stdin) to the printer, and it can do that it one of -two ways. The first way is to hand it all off to the kernel driver, -with the knowledge that the protocol that the printer speaks is IEEE -1284's compatibility mode. - - 0) { - int written = write_printer (fd, ptr, got); - - if (written < 0) { - perror ("write"); - close (fd); - return 1; - } - - ptr += written; - got -= written; - } - } -]]> - -The write_printer function is not pictured -above. This is because the main loop that is shown can be used for -both methods of driving the printer. Here is one implementation of -write_printer: - - - -We hand the data to the kernel-level driver (using -write) and it handles the printer -protocol. - -Now let's do it the hard way! In this particular example there -is no practical reason to do anything other than just call -write, because we know that the printer talks an -IEEE 1284 protocol. On the other hand, this particular example does -not even need a user-land driver since there is already a kernel-level -one; for the purpose of this discussion, try to imagine that the -printer speaks a protocol that is not already implemented under -Linux. - -So, here is the alternative implementation of -write_printer (for brevity, error checking has -been omitted): - - - -To show a bit more of the ppdev interface, -here is a small piece of code that is intended to mimic the printer's -side of printer protocol. - - 1) - fprintf (stderr, "Arghh! Missed %d interrupt%s!\n", - irqc - 1, irqc == 2 ? "s" : ""); - - /* Ack it. */ - ioctl (fd, PPWCONTROL, &acking); - usleep (2); - ioctl (fd, PPWCONTROL, &busy); - - putchar (ch); - } -]]> - - - - -
\ No newline at end of file + + + + + + The Linux 2.4 Parallel Port Subsystem + + + + Tim + Waugh + +
+ twaugh@redhat.com +
+
+
+
+ + + 1999-2000 + Tim Waugh + + + + + This documentation 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 + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Design goals + + + The problems + + + The first parallel port support for Linux came with the line + printer driver, lp. The printer driver is a + character special device, and (in Linux 2.0) had support for + writing, via write, and configuration and + statistics reporting via ioctl. + + + + The printer driver could be used on any computer that had an IBM + PC-compatible parallel port. Because some architectures have + parallel ports that aren't really the same as PC-style ports, + other variants of the printer driver were written in order to + support Amiga and Atari parallel ports. + + + + When the Iomega Zip drive was released, and a driver written for + it, a problem became apparent. The Zip drive is a parallel port + device that provides a parallel port of its own---it is designed + to sit between a computer and an attached printer, with the + printer plugged into the Zip drive, and the Zip drive plugged into + the computer. + + + + The problem was that, although printers and Zip drives were both + supported, for any given port only one could be used at a time. + Only one of the two drivers could be present in the kernel at + once. This was because of the fact that both drivers wanted to + drive the same hardware---the parallel port. When the printer + driver initialised, it would call the + check_region function to make sure that the + IO region associated with the parallel port was free, and then it + would call request_region to allocate it. + The Zip drive used the same mechanism. Whichever driver + initialised first would gain exclusive control of the parallel + port. + + + + The only way around this problem at the time was to make sure that + both drivers were available as loadable kernel modules. To use + the printer, load the printer driver module; then for the Zip + drive, unload the printer driver module and load the Zip driver + module. + + + + The net effect was that printing a document that was stored on a + Zip drive was a bit of an ordeal, at least if the Zip drive and + printer shared a parallel port. A better solution was + needed. + + + + Zip drives are not the only devices that presented problems for + Linux. There are other devices with pass-through ports, for + example parallel port CD-ROM drives. There are also printers that + report their status textually rather than using simple error pins: + sending a command to the printer can cause it to report the number + of pages that it has ever printed, or how much free memory it has, + or whether it is running out of toner, and so on. The printer + driver didn't originally offer any facility for reading back this + information (although Carsten Gross added nibble mode readback + support for kernel 2.2). + + + + The IEEE has issued a standards document called IEEE 1284, which + documents existing practice for parallel port communications in a + variety of modes. Those modes are: compatibility, + reverse nibble, reverse byte, ECP and EPP. Newer devices often + use the more advanced modes of transfer (ECP and EPP). In Linux + 2.0, the printer driver only supported compatibility + mode (i.e. normal printer protocol) and reverse nibble + mode. + + + + + + The solutions + + + + + The parport code in Linux 2.2 was designed to + meet these problems of architectural differences in parallel + ports, of port-sharing between devices with pass-through ports, + and of lack of support for IEEE 1284 transfer modes. + + + + + + There are two layers to the parport + subsystem, only one of which deals directly with the hardware. + The other layer deals with sharing and IEEE 1284 transfer modes. + In this way, parallel support for a particular architecture comes + in the form of a module which registers itself with the generic + sharing layer. + + + + + + The sharing model provided by the parport + subsystem is one of exclusive access. A device driver, such as + the printer driver, must ask the parport + layer for access to the port, and can only use the port once + access has been granted. When it has finished a + transaction, it can tell the + parport layer that it may release the port + for other device drivers to use. + + + + + + Devices with pass-through ports all manage to share a parallel + port with other devices in generally the same way. The device has + a latch for each of the pins on its pass-through port. The normal + state of affairs is pass-through mode, with the device copying the + signal lines between its host port and its pass-through port. + When the device sees a special signal from the host port, it + latches the pass-through port so that devices further downstream + don't get confused by the pass-through device's conversation with + the host parallel port: the device connected to the pass-through + port (and any devices connected in turn to it) are effectively cut + off from the computer. When the pass-through device has completed + its transaction with the computer, it enables the pass-through + port again. + + + + + + + + + + + + + This technique relies on certain special signals + being invisible to devices that aren't watching for them. This + tends to mean only changing the data signals and leaving the + control signals alone. IEEE 1284.3 documents a standard protocol + for daisy-chaining devices together with parallel ports. + + + + + + Support for standard transfer modes are provided as operations + that can be performed on a port, along with operations for setting + the data lines, or the control lines, or reading the status lines. + These operations appear to the device driver as function pointers; + more later. + + + + + + + + Standard transfer modes + + + + + + The standard transfer modes in use over the parallel + port are defined by a document called IEEE 1284. It + really just codifies existing practice and documents protocols (and + variations on protocols) that have been in common use for quite + some time. + + + + The original definitions of which pin did what were set out by + Centronics Data Computer Corporation, but only the printer-side + interface signals were specified. + + + + By the early 1980s, IBM's host-side implementation had become the + most widely used. New printers emerged that claimed Centronics + compatibility, but although compatible with Centronics they + differed from one another in a number of ways. + + + + As a result of this, when IEEE 1284 was published in 1994, all that + it could really do was document the various protocols that are used + for printers (there are about six variations on a theme). + + + + In addition to the protocol used to talk to Centronics-compatible + printers, IEEE 1284 defined other protocols that are used for + unidirectional peripheral-to-host transfers (reverse nibble and + reverse byte) and for fast bidirectional transfers (ECP and + EPP). + + + + + + Structure + + + + + + + + + + + + + + Sharing core + + + At the core of the parport subsystem is the + sharing mechanism (see + drivers/parport/share.c). This module, + parport, is responsible for keeping track of + which ports there are in the system, which device drivers might be + interested in new ports, and whether or not each port is available + for use (or if not, which driver is currently using it). + + + + + + Parports and their overrides + + + The generic parport sharing code doesn't + directly handle the parallel port hardware. That is done instead + by low-level parport drivers. + The function of a low-level parport driver is + to detect parallel ports, register them with the sharing code, and + provide a list of access functions for each port. + + + + The most basic access functions that must be provided are ones for + examining the status lines, for setting the control lines, and for + setting the data lines. There are also access functions for + setting the direction of the data lines; normally they are in the + forward direction (that is, the computer drives + them), but some ports allow switching to reverse + mode (driven by the peripheral). There is an access function for + examining the data lines once in reverse mode. + + + + + + IEEE 1284 transfer modes + + + Stacked on top of the sharing mechanism, but still in the + parport module, are functions for + transferring data. They are provided for the device drivers to + use, and are very much like library routines. Since these + transfer functions are provided by the generic + parport core they must use the lowest + common denominator set of access functions: they can set + the control lines, examine the status lines, and use the data + lines. With some parallel ports the data lines can only be set + and not examined, and with other ports accessing the data register + causes control line activity; with these types of situations, the + IEEE 1284 transfer functions make a best effort attempt to do the + right thing. In some cases, it is not physically possible to use + particular IEEE 1284 transfer modes. + + + + The low-level parport drivers also provide + IEEE 1284 transfer functions, as names in the access function + list. The low-level driver can just name the generic IEEE 1284 + transfer functions for this. Some parallel ports can do IEEE 1284 + transfers in hardware; for those ports, the low-level driver can + provide functions to utilise that feature. + + + + + + + + Pardevices and parport_drivers + + + When a parallel port device driver (such as + lp) initialises it tells the sharing layer + about itself using parport_register_driver. + The information is put into a struct + parport_driver, which is put into a linked list. The + information in a struct parport_driver + really just amounts to some function pointers to callbacks in the + parallel port device driver. + + + + During its initialisation, a low-level port driver tells the + sharing layer about all the ports that it has found (using + parport_register_port), and the sharing layer + creates a struct parport for each of + them. Each struct parport contains + (among other things) a pointer to a struct + parport_operations, which is a list of function + pointers for the various operations that can be performed on a + port. You can think of a struct parport + as a parallel port object, if + object-orientated programming is your thing. The + parport structures are chained in a + linked list, whose head is portlist (in + drivers/parport/share.c). + + + + Once the port has been registered, the low-level port driver + announces it. The parport_announce_port + function walks down the list of parallel port device drivers + (struct parport_drivers) calling the + attach function of each. + + + + Similarly, a low-level port driver can undo the effect of + registering a port with the + parport_unregister_port function, and device + drivers are notified using the detach + callback. + + + + Device drivers can undo the effect of registering themselves with + the parport_unregister_driver + function. + + + + + + + + The IEEE 1284.3 API + + + The ability to daisy-chain devices is very useful, but if every + device does it in a different way it could lead to lots of + complications for device driver writers. Fortunately, the IEEE + are standardising it in IEEE 1284.3, which covers daisy-chain + devices and port multiplexors. + + + + At the time of writing, IEEE 1284.3 has not been published, but + the draft specifies the on-the-wire protocol for daisy-chaining + and multiplexing, and also suggests a programming interface for + using it. That interface (or most of it) has been implemented in + the parport code in Linux. + + + + At initialisation of the parallel port bus, + daisy-chained devices are assigned addresses starting from zero. + There can only be four devices with daisy-chain addresses, plus + one device on the end that doesn't know about daisy-chaining and + thinks it's connected directly to a computer. + + + + Another way of connecting more parallel port devices is to use a + multiplexor. The idea is to have a device that is connected + directly to a parallel port on a computer, but has a number of + parallel ports on the other side for other peripherals to connect + to (two or four ports are allowed). The multiplexor switches + control to different ports under software control---it is, in + effect, a programmable printer switch. + + + + Combining the ability of daisy-chaining five devices together with + the ability to multiplex one parallel port between four gives the + potential to have twenty peripherals connected to the same + parallel port! + + + + In addition, of course, a single computer can have multiple + parallel ports. So, each parallel port peripheral in the system + can be identified with three numbers, or co-ordinates: the + parallel port, the multiplexed port, and the daisy-chain + address. + + + + + + + + + + + + + Each device in the system is numbered at initialisation (by + parport_daisy_init). You can convert between + this device number and its co-ordinates with + parport_device_num and + parport_device_coords. + + + + +#include <parport.h> + + + int parport_device_num + int parport + int mux + int daisy + + + + + + int parport_device_coords + int devnum + int *parport + int *mux + int *daisy + + + + + Any parallel port peripheral will be connected directly or + indirectly to a parallel port on the system, but it won't have a + daisy-chain address if it does not know about daisy-chaining, and + it won't be connected through a multiplexor port if there is no + multiplexor. The special co-ordinate value + -1 is used to indicate these cases. + + + + Two functions are provided for finding devices based on their IEEE + 1284 Device ID: parport_find_device and + parport_find_class. + + + + +#include <parport.h> + + + int parport_find_device + const char *mfg + const char *mdl + int from + + + + + + int parport_find_class + parport_device_class cls + int from + + + + + These functions take a device number (in addition to some other + things), and return another device number. They walk through the + list of detected devices until they find one that matches the + requirements, and then return that device number (or + -1 if there are no more such devices). They + start their search at the device after the one in the list with + the number given (at from+1, in other + words). + + + + + + + + Device driver's view + + + + + + + + + + + This section is written from the point of view of the device driver + programmer, who might be writing a driver for a printer or a + scanner or else anything that plugs into the parallel port. It + explains how to use the parport interface to + find parallel ports, use them, and share them with other device + drivers. + + + + We'll start out with a description of the various functions that + can be called, and then look at a reasonably simple example of + their use: the printer driver. + + + + The interactions between the device driver and the + parport layer are as follows. First, the + device driver registers its existence with + parport, in order to get told about any + parallel ports that have been (or will be) detected. When it gets + told about a parallel port, it then tells + parport that it wants to drive a device on + that port. Thereafter it can claim exclusive access to the port in + order to talk to its device. + + + + So, the first thing for the device driver to do is tell + parport that it wants to know what parallel + ports are on the system. To do this, it uses the + parport_register_device function: + + + + +#include <parport.h> + +struct parport_driver { + const char *name; + void (*attach) (struct parport *); + void (*detach) (struct parport *); + struct parport_driver *next; +}; + + + + int parport_register_driver + struct parport_driver *driver + + + + + In other words, the device driver passes pointers to a couple of + functions to parport, and + parport calls attach for + each port that's detected (and detach for each + port that disappears---yes, this can happen). + + + + The next thing that happens is that the device driver tells + parport that it thinks there's a device on the + port that it can drive. This typically will happen in the driver's + attach function, and is done with + parport_register_device: + + + + +#include <parport.h> + + + struct pardevice *parport_register_device + struct parport *port + const char *name + int (*pf) + void * + void (*kf) + void * + void (*irq_func) + int, void *, struct pt_regs * + int flags + void *handle + + + + + The port comes from the parameter supplied + to the attach function when it is called, or + alternatively can be found from the list of detected parallel ports + directly with the (now deprecated) + parport_enumerate function. + + + + The next three parameters, pf, + kf, and irq_func, are + more function pointers. These callback functions get called under + various circumstances, and are always given the + handle as one of their parameters. + + + + The preemption callback, pf, is called when + the driver has claimed access to the port but another device driver + wants access. If the driver is willing to let the port go, it + should return zero and the port will be released on its behalf. + There is no need to call parport_release. If + pf gets called at a bad time for letting the + port go, it should return non-zero and no action will be taken. It + is good manners for the driver to try to release the port at the + earliest opportunity after its preemption callback is + called. + + + + The kick callback, kf, is + called when the port can be claimed for exclusive access; that is, + parport_claim is guaranteed to succeed inside + the kick callback. If the driver wants to claim the + port it should do so; otherwise, it need not take any + action. + + + + The irq_func callback is called, + predictably, when a parallel port interrupt is generated. But it + is not the only code that hooks on the interrupt. The sequence is + this: the lowlevel driver is the one that has done + request_irq; it then does whatever + hardware-specific things it needs to do to the parallel port + hardware (for PC-style ports, there is nothing special to do); it + then tells the IEEE 1284 code about the interrupt, which may + involve reacting to an IEEE 1284 event, depending on the current + IEEE 1284 phase; and finally the irq_func + function is called. + + + + None of the callback functions are allowed to block. + + + + The flags are for telling + parport any requirements or hints that are + useful. The only useful value here (other than + 0, which is the usual value) is + PARPORT_DEV_EXCL. The point of that flag is + to request exclusive access at all times---once a driver has + successfully called parport_register_device + with that flag, no other device drivers will be able to register + devices on that port (until the successful driver deregisters its + device, of course). + + + + The PARPORT_DEV_EXCL flag is for preventing + port sharing, and so should only be used when sharing the port with + other device drivers is impossible and would lead to incorrect + behaviour. Use it sparingly! + + + + Devices can also be registered by device drivers based on their + device numbers (the same device numbers as in the previous + section). + + + + The parport_open function is similar to + parport_register_device, and + parport_close is the equivalent of + parport_unregister_device. The difference is + that parport_open takes a device number rather + than a pointer to a struct parport. + + + + +#include <parport.h> + + + struct pardevice *parport_open + int devnum + int (*pf) + void * + int (*kf) + void * + int (*irqf) + int, void *, struct pt_regs * + int flags + void *handle + + + + + + void parport_close + struct pardevice *dev + + + + + + struct pardevice *parport_register_device + struct parport *port + const char *name + int (*pf) + void * + int (*kf) + void * + int (*irqf) + int, void *, struct pt_regs * + int flags + void *handle + + + + + + void parport_unregister_device + struct pardevice *dev + + + + + The intended use of these functions is during driver initialisation + while the driver looks for devices that it supports, as + demonstrated by the following code fragment: + + + + + + + Once your device driver has registered its device and been handed a + pointer to a struct pardevice, the next + thing you are likely to want to do is communicate with the device + you think is there. To do that you'll need to claim access to the + port. + + + + +#include <parport.h> + + + int parport_claim + struct pardevice *dev + + + + + + int parport_claim_or_block + struct pardevice *dev + + + + + + void parport_release + struct pardevice *dev + + + + + To claim access to the port, use parport_claim + or parport_claim_or_block. The first of these + will not block, and so can be used from interrupt context. If + parport_claim succeeds it will return zero and + the port is available to use. It may fail (returning non-zero) if + the port is in use by another driver and that driver is not willing + to relinquish control of the port. + + + + The other function, parport_claim_or_block, + will block if necessary to wait for the port to be free. If it + slept, it returns 1; if it succeeded without + needing to sleep it returns 0. If it fails it + will return a negative error code. + + + + When you have finished communicating with the device, you can give + up access to the port so that other drivers can communicate with + their devices. The parport_release function + cannot fail, but it should not be called without the port claimed. + Similarly, you should not try to claim the port if you already have + it claimed. + + + + You may find that although there are convenient points for your + driver to relinquish the parallel port and allow other drivers to + talk to their devices, it would be preferable to keep hold of the + port. The printer driver only needs the port when there is data to + print, for example, but a network driver (such as PLIP) could be + sent a remote packet at any time. With PLIP, it is no huge + catastrophe if a network packet is dropped, since it will likely be + sent again, so it is possible for that kind of driver to share the + port with other (pass-through) devices. + + + + The parport_yield and + parport_yield_blocking functions are for + marking points in the driver at which other drivers may claim the + port and use their devices. Yielding the port is similar to + releasing it and reclaiming it, but is more efficient because + nothing is done if there are no other devices needing the port. In + fact, nothing is done even if there are other devices waiting but + the current device is still within its timeslice. + The default timeslice is half a second, but it can be adjusted via + a /proc entry. + + + + +#include <parport.h> + + + int parport_yield + struct pardevice *dev + + + + + + int parport_yield_blocking + struct pardevice *dev + + + + + The first of these, parport_yield, will not + block but as a result may fail. The return value for + parport_yield is the same as for + parport_claim. The blocking version, + parport_yield_blocking, has the same return + code as parport_claim_or_block. + + + + Once the port has been claimed, the device driver can use the + functions in the struct parport_operations + pointer in the struct parport it has a + pointer to. For example: + + + + ops->write_data (port, d); + ]]> + + + Some of these operations have shortcuts. For + instance, parport_write_data is equivalent to + the above, but may be a little bit faster (it's a macro that in + some cases can avoid needing to indirect through + port and ops). + + + + + + Port drivers + + + + + To recap, then: + + + + + + The device driver registers itself with parport. + + + + + + A low-level driver finds a parallel port and registers it with + parport (these first two things can happen + in either order). This registration creates a struct + parport which is linked onto a list of known ports. + + + + + + parport calls the + attach function of each registered device + driver, passing it the pointer to the new struct + parport. + + + + + + The device driver gets a handle from + parport, for use with + parport_claim/release. + This handle takes the form of a pointer to a struct + pardevice, representing a particular device on the + parallel port, and is acquired using + parport_register_device. + + + + + + The device driver claims the port using + parport_claim (or + function_claim_or_block). + + + + + + Then it goes ahead and uses the port. When finished it releases + the port. + + + + + + + The purpose of the low-level drivers, then, is to detect parallel + ports and provide methods of accessing them (i.e. implementing the + operations in struct + parport_operations). + + + + + A more complete description of which operation is supposed to do + what is available in + Documentation/parport-lowlevel.txt. + + + + + + The printer driver + + + + + + The printer driver, lp is a character special + device driver and a parport client. As a + character special device driver it registers a struct + file_operations using + register_chrdev, with pointers filled in for + write, ioctl, + open and + release. As a client of + parport, it registers a struct + parport_driver using + parport_register_driver, so that + parport knows to call + lp_attach when a new parallel port is + discovered (and lp_detach when it goes + away). + + + + The parallel port console functionality is also implemented in + drivers/char/lp.c, but that won't be covered + here (it's quite simple though). + + + + The initialisation of the driver is quite easy to understand (see + lp_init). The lp_table is + an array of structures that contain information about a specific + device (the struct pardevice associated + with it, for example). That array is initialised to sensible + values first of all. + + + + Next, the printer driver calls register_chrdev + passing it a pointer to lp_fops, which contains + function pointers for the printer driver's implementation of + open, write, and so on. + This part is the same as for any character special device + driver. + + + + After successfully registering itself as a character special device + driver, the printer driver registers itself as a + parport client using + parport_register_driver. It passes a pointer + to this structure: + + + + + + + The lp_detach function is not very interesting + (it does nothing); the interesting bit is + lp_attach. What goes on here depends on + whether the user supplied any parameters. The possibilities are: + no parameters supplied, in which case the printer driver uses every + port that is detected; the user supplied the parameter + auto, in which case only ports on which the device + ID string indicates a printer is present are used; or the user + supplied a list of parallel port numbers to try, in which case only + those are used. + + + + For each port that the printer driver wants to use (see + lp_register), it calls + parport_register_device and stores the + resulting struct pardevice pointer in the + lp_table. If the user told it to do so, it then + resets the printer. + + + + The other interesting piece of the printer driver, from the point + of view of parport, is + lp_write. In this function, the user space + process has data that it wants printed, and the printer driver + hands it off to the parport code to deal with. + + + + The parport functions it uses that we have not + seen yet are parport_negotiate, + parport_set_timeout, and + parport_write. These functions are part of + the IEEE 1284 implementation. + + + + The way the IEEE 1284 protocol works is that the host tells the + peripheral what transfer mode it would like to use, and the + peripheral either accepts that mode or rejects it; if the mode is + rejected, the host can try again with a different mode. This is + the negotation phase. Once the peripheral has accepted a + particular transfer mode, data transfer can begin that mode. + + + + The particular transfer mode that the printer driver wants to use + is named in IEEE 1284 as compatibility mode, and the + function to request a particular mode is called + parport_negotiate. + + + + +#include <parport.h> + + + int parport_negotiate + struct parport *port + int mode + + + + + The modes parameter is a symbolic constant + representing an IEEE 1284 mode; in this instance, it is + IEEE1284_MODE_COMPAT. (Compatibility mode is + slightly different to the other modes---rather than being + specifically requested, it is the default until another mode is + selected.) + + + + Back to lp_write then. First, access to the + parallel port is secured with + parport_claim_or_block. At this point the + driver might sleep, waiting for another driver (perhaps a Zip drive + driver, for instance) to let the port go. Next, it goes to + compatibility mode using parport_negotiate. + + + + The main work is done in the write-loop. In particular, the line + that hands the data over to parport reads: + + + + + + + The parport_write function writes data to the + peripheral using the currently selected transfer mode + (compatibility mode, in this case). It returns the number of bytes + successfully written: + + + + +#include <parport.h> + + + ssize_t parport_write + struct parport *port + const void *buf + size_t len + + + + + + ssize_t parport_read + struct parport *port + void *buf + size_t len + + + + + (parport_read does what it sounds like, but + only works for modes in which reverse transfer is possible. Of + course, parport_write only works in modes in + which forward transfer is possible, too.) + + + + The buf pointer should be to kernel space + memory, and obviously the len parameter + specifies the amount of data to transfer. + + + + In fact what parport_write does is call the + appropriate block transfer function from the struct + parport_operations: + + + + + + + The transfer code in parport will tolerate a + data transfer stall only for so long, and this timeout can be + specified with parport_set_timeout, which + returns the previous timeout: + + + + +#include <parport.h> + + + long parport_set_timeout + struct pardevice *dev + long inactivity + + + + + This timeout is specific to the device, and is restored on + parport_claim. + + + + The next function to look at is the one that allows processes to + read from /dev/lp0: + lp_read. It's short, like + lp_write. + + + + The semantics of reading from a line printer device are as follows: + + + + + + Switch to reverse nibble mode. + + + + + + Try to read data from the peripheral using reverse nibble mode, + until either the user-provided buffer is full or the peripheral + indicates that there is no more data. + + + + + + If there was data, stop, and return it. + + + + + + Otherwise, we tried to read data and there was none. If the user + opened the device node with the O_NONBLOCK + flag, return. Otherwise wait until an interrupt occurs on the + port (or a timeout elapses). + + + + + + + + User-level device drivers + + + + Introduction to ppdev + + + The printer is accessible through /dev/lp0; + in the same way, the parallel port itself is accessible through + /dev/parport0. The difference is in the + level of control that you have over the wires in the parallel port + cable. + + + + With the printer driver, a user-space program (such as the printer + spooler) can send bytes in printer protocol. + Briefly, this means that for each byte, the eight data lines are + set up, then a strobe line tells the printer to + look at the data lines, and the printer sets an + acknowledgement line to say that it got the byte. + The printer driver also allows the user-space program to read + bytes in nibble mode, which is a way of + transferring data from the peripheral to the computer half a byte + at a time (and so it's quite slow). + + + + In contrast, the ppdev driver (accessed via + /dev/parport0) allows you to: + + + + + + + examine status lines, + + + + + + set control lines, + + + + + + set/examine data lines (and control the direction of the data + lines), + + + + + + wait for an interrupt (triggered by one of the status lines), + + + + + + find out how many new interrupts have occurred, + + + + + + set up a response to an interrupt, + + + + + + use IEEE 1284 negotiation (for telling peripheral which transfer + mode, to use) + + + + + + transfer data using a specified IEEE 1284 mode. + + + + + + + + + User-level or kernel-level driver? + + + The decision of whether to choose to write a kernel-level device + driver or a user-level device driver depends on several factors. + One of the main ones from a practical point of view is speed: + kernel-level device drivers get to run faster because they are not + preemptable, unlike user-level applications. + + + + Another factor is ease of development. It is in general easier to + write a user-level driver because (a) one wrong move does not + result in a crashed machine, (b) you have access to user libraries + (such as the C library), and (c) debugging is easier. + + + + + + Programming interface + + + The ppdev interface is largely the same as that + of other character special devices, in that it supports + open, close, + read, write, and + ioctl. The constants for the + ioctl commands are in + include/linux/ppdev.h. + + + + + Starting and stopping: <function>open</function> and + <function>close</function> + + + + The device node /dev/parport0 represents any + device that is connected to parport0, the + first parallel port in the system. Each time the device node is + opened, it represents (to the process doing the opening) a + different device. It can be opened more than once, but only one + instance can actually be in control of the parallel port at any + time. A process that has opened + /dev/parport0 shares the parallel port in + the same way as any other device driver. A user-land driver may + be sharing the parallel port with in-kernel device drivers as + well as other user-land drivers. + + + + + Control: <function>ioctl</function> + + + Most of the control is done, naturally enough, via the + ioctl call. Using + ioctl, the user-land driver can control both + the ppdev driver in the kernel and the + physical parallel port itself. The ioctl + call takes as parameters a file descriptor (the one returned from + opening the device node), a command, and optionally (a pointer + to) some data. + + + + PPCLAIM + + + + Claims access to the port. As a user-land device driver + writer, you will need to do this before you are able to + actually change the state of the parallel port in any way. + Note that some operations only affect the + ppdev driver and not the port, such as + PPSETMODE; they can be performed while + access to the port is not claimed. + + + + + PPEXCL + + + + Instructs the kernel driver to forbid any sharing of the port + with other drivers, i.e. it requests exclusivity. The + PPEXCL command is only valid when the + port is not already claimed for use, and it may mean that the + next PPCLAIM ioctl + will fail: some other driver may already have registered + itself on that port. + + + + Most device drivers don't need exclusive access to the port. + It's only provided in case it is really needed, for example + for devices where access to the port is required for extensive + periods of time (many seconds). + + + + Note that the PPEXCL + ioctl doesn't actually claim the port + there and then---action is deferred until the + PPCLAIM ioctl is + performed. + + + + + PPRELEASE + + + + Releases the port. Releasing the port undoes the effect of + claiming the port. It allows other device drivers to talk to + their devices (assuming that there are any). + + + + + PPYIELD + + + + Yields the port to another driver. This + ioctl is a kind of short-hand for + releasing the port and immediately reclaiming it. It gives + other drivers a chance to talk to their devices, but + afterwards claims the port back. An example of using this + would be in a user-land printer driver: once a few characters + have been written we could give the port to another device + driver for a while, but if we still have characters to send to + the printer we would want the port back as soon as possible. + + + + It is important not to claim the parallel port for too long, + as other device drivers will have no time to service their + devices. If your device does not allow for parallel port + sharing at all, it is better to claim the parallel port + exclusively (see PPEXCL). + + + + + PPNEGOT + + + + Performs IEEE 1284 negotiation into a particular mode. + Briefly, negotiation is the method by which the host and the + peripheral decide on a protocol to use when transferring data. + + + + An IEEE 1284 compliant device will start out in compatibility + mode, and then the host can negotiate to another mode (such as + ECP). + + + + The ioctl parameter should be a pointer + to an int; values for this are in + incluce/linux/parport.h and include: + + + + + IEEE1284_MODE_COMPAT + + IEEE1284_MODE_NIBBLE + + IEEE1284_MODE_BYTE + + IEEE1284_MODE_EPP + + IEEE1284_MODE_ECP + + + + The PPNEGOT ioctl + actually does two things: it performs the on-the-wire + negotiation, and it sets the behaviour of subsequent + read/write calls so + that they use that mode (but see + PPSETMODE). + + + + + PPSETMODE + + + + Sets which IEEE 1284 protocol to use for the + read and write + calls. + + + + The ioctl parameter should be a pointer + to an int. + + + + + PPGETTIME + + + + Retrieves the time-out value. The read + and write calls will time out if the + peripheral doesn't respond quickly enough. The + PPGETTIME ioctl + retrieves the length of time that the peripheral is allowed to + have before giving up. + + + + The ioctl parameter should be a pointer + to a struct timeval. + + + + + PPSETTIME + + + + Sets the time-out. The ioctl parameter + should be a pointer to a struct + timeval. + + + + + PPWCONTROL + + + + Sets the control lines. The ioctl + parameter is a pointer to an unsigned char, the + bitwise OR of the control line values in + include/linux/parport.h. + + + + + PPRCONTROL + + + + Returns the last value written to the control register, in the + form of an unsigned char: each bit corresponds to + a control line (although some are unused). The + ioctl parameter should be a pointer to an + unsigned char. + + + + This doesn't actually touch the hardware; the last value + written is remembered in software. This is because some + parallel port hardware does not offer read access to the + control register. + + + + The control lines bits are defined in + include/linux/parport.h: + + + + + PARPORT_CONTROL_STROBE + + PARPORT_CONTROL_AUTOFD + + PARPORT_CONTROL_SELECT + + PARPORT_CONTROL_INIT + + + + + PPFCONTROL + + + + Frobs the control lines. Since a common operation is to + change one of the control signals while leaving the others + alone, it would be quite inefficient for the user-land driver + to have to use PPRCONTROL, make the + change, and then use PPWCONTROL. Of + course, each driver could remember what state the control + lines are supposed to be in (they are never changed by + anything else), but in order to provide + PPRCONTROL, ppdev + must remember the state of the control lines anyway. + + + + The PPFCONTROL ioctl + is for frobbing control lines, and is like + PPWCONTROL but acts on a restricted set + of control lines. The ioctl parameter is + a pointer to a struct + ppdev_frob_struct: + + + + + + + + The mask and + val fields are bitwise ORs of + control line names (such as in + PPWCONTROL). The operation performed by + PPFCONTROL is: + + + + + + + + In other words, the signals named in + mask are set to the values in + val. + + + + + PPRSTATUS + + + + Returns an unsigned char containing bits set for + each status line that is set (for instance, + PARPORT_STATUS_BUSY). The + ioctl parameter should be a pointer to an + unsigned char. + + + + + PPDATADIR + + + + Controls the data line drivers. Normally the computer's + parallel port will drive the data lines, but for byte-wide + transfers from the peripheral to the host it is useful to turn + off those drivers and let the peripheral drive the + signals. (If the drivers on the computer's parallel port are + left on when this happens, the port might be damaged.) + + + + This is only needed in conjunction with + PPWDATA or + PPRDATA. + + + + The ioctl parameter is a pointer to an + int. If the int is zero, the + drivers are turned on (forward direction); if non-zero, the + drivers are turned off (reverse direction). + + + + + PPWDATA + + + + Sets the data lines (if in forward mode). The + ioctl parameter is a pointer to an + unsigned char. + + + + + PPRDATA + + + + Reads the data lines (if in reverse mode). The + ioctl parameter is a pointer to an + unsigned char. + + + + + PPCLRIRQ + + + + Clears the interrupt count. The ppdev + driver keeps a count of interrupts as they are triggered. + PPCLRIRQ stores this count in an + int, a pointer to which is passed in as the + ioctl parameter. + + + + In addition, the interrupt count is reset to zero. + + + + + PPWCTLONIRQ + + + + Set a trigger response. Afterwards when an interrupt is + triggered, the interrupt handler will set the control lines as + requested. The ioctl parameter is a + pointer to an unsigned char, which is interpreted + in the same way as for PPWCONTROL. + + + + The reason for this ioctl is simply + speed. Without this ioctl, responding to + an interrupt would start in the interrupt handler, switch + context to the user-land driver via poll + or select, and then switch context back + to the kernel in order to handle + PPWCONTROL. Doing the whole lot in the + interrupt handler is a lot faster. + + + + + + + + + + + + Transferring data: <function>read</function> and + <function>write</function> + + + Transferring data using read and + write is straightforward. The data is + transferring using the current IEEE 1284 mode (see the + PPSETMODE ioctl). For + modes which can only transfer data in one direction, only the + appropriate function will work, of course. + + + + + Waiting for events: <function>poll</function> and + <function>select</function> + + + The ppdev driver provides user-land device + drivers with the ability to wait for interrupts, and this is done + using poll (and select, + which is implemented in terms of poll). + + + + When a user-land device driver wants to wait for an interrupt, it + sleeps with poll. When the interrupt + arrives, ppdev wakes it up (with a + read event, although strictly speaking there is + nothing to actually read). + + + + + + + + Examples + + + Presented here are two demonstrations of how to write a simple + printer driver for ppdev. Firstly we will + use the write function, and after that we + will drive the control and data lines directly. + + + + The first thing to do is to actually open the device. + + + + + + Here name should be something along the lines + of "/dev/parport0". (If you don't have any + /dev/parport files, you can make them with + mknod; they are character special device nodes + with major 99.) + + + + In order to do anything with the port we need to claim access to + it. + + + + + + Our printer driver will copy its input (from + stdin) to the printer, and it can do that it + one of two ways. The first way is to hand it all off to the + kernel driver, with the knowledge that the protocol that the + printer speaks is IEEE 1284's compatibility + mode. + + + 0) { + int written = write_printer (fd, ptr, got); + + if (written < 0) { + perror ("write"); + close (fd); + return 1; + } + + ptr += written; + got -= written; + } + } + ]]> + + + The write_printer function is not pictured + above. This is because the main loop that is shown can be used + for both methods of driving the printer. Here is one + implementation of write_printer: + + + + + + We hand the data to the kernel-level driver (using + write) and it handles the printer + protocol. + + + + Now let's do it the hard way! In this particular example there is + no practical reason to do anything other than just call + write, because we know that the printer talks + an IEEE 1284 protocol. On the other hand, this particular example + does not even need a user-land driver since there is already a + kernel-level one; for the purpose of this discussion, try to + imagine that the printer speaks a protocol that is not already + implemented under Linux. + + + + So, here is the alternative implementation of + write_printer (for brevity, error checking + has been omitted): + + + + + + To show a bit more of the ppdev interface, + here is a small piece of code that is intended to mimic the + printer's side of printer protocol. + + + 1) + fprintf (stderr, "Arghh! Missed %d interrupt%s!\n", + irqc - 1, irqc == 2 ? "s" : ""); + + /* Ack it. */ + ioctl (fd, PPWCONTROL, &acking); + usleep (2); + ioctl (fd, PPWCONTROL, &busy); + + putchar (ch); + } + ]]> + + + And here is an example (with no error checking at all) to show how + to read data from the port, using ECP mode, with optional + negotiation to ECP mode first. + + + + + + + + + + + API reference + + +!Fdrivers/parport/daisy.c parport_device_num +!Fdrivers/parport/daisy.c parport_device_coords +!Fdrivers/parport/daisy.c parport_find_device +!Fdrivers/parport/daisy.c parport_find_class +!Fdrivers/parport/share.c parport_register_driver +!Fdrivers/parport/share.c parport_unregister_driver +!Fdrivers/parport/share.c parport_register_device +!Fdrivers/parport/share.c parport_unregister_device +!Fdrivers/parport/daisy.c parport_open +!Fdrivers/parport/daisy.c parport_close +!Fdrivers/parport/share.c parport_claim +!Fdrivers/parport/share.c parport_claim_or_block +!Fdrivers/parport/share.c parport_release +!Finclude/linux/parport.h parport_yield +!Finclude/linux/parport.h parport_yield_blocking +!Fdrivers/parport/ieee1284.c parport_negotiate +!Fdrivers/parport/ieee1284.c parport_write +!Fdrivers/parport/ieee1284.c parport_read +!Fdrivers/parport/ieee1284.c parport_set_timeout + + + + + + The Linux 2.2 Parallel Port Subsystem + + + + Although the interface described in this document is largely new + with the 2.4 kernel, the sharing mechanism is available in the 2.2 + kernel as well. The functions available in 2.2 are: + + + + + + parport_register_device + + + + + + parport_unregister_device + + + + + + parport_claim + + + + + + parport_claim_or_block + + + + + + parport_release + + + + + + parport_yield + + + + + + parport_yield_blocking + + + + + + In addition, negotiation to reverse nibble mode is supported: + + + + + int parport_ieee1284_nibble_mode_ok + struct parport *port + unsigned char mode + + + + + The only valid values for mode are 0 (for + reverse nibble mode) and 4 (for Device ID in reverse nibble mode). + + + + This function is obsoleted by + parport_negotiate in Linux 2.4, and has been + removed. + + +
+ + + + + + diff --git a/Documentation/filesystems/cramfs.txt b/Documentation/filesystems/cramfs.txt index 9bbee161384..2bac0331346 100644 --- a/Documentation/filesystems/cramfs.txt +++ b/Documentation/filesystems/cramfs.txt @@ -27,8 +27,8 @@ Only the low 8 bits of gid are stored. The current version of mkcramfs simply truncates to 8 bits, which is a potential security issue. -Hard links are not supported, but symlinks are. (See also the TODO -comment in mkcramfs.c at the nlink test.) +Hard links are supported, but hard linked files +will still have a link count of 1 in the cramfs image. Cramfs directories have no `.' or `..' entries. Directories (like every other file on cramfs) always have a link count of 1. (There's diff --git a/Documentation/kbuild/config-language.txt b/Documentation/kbuild/config-language.txt index c446e7ac326..b37dafb7e5e 100644 --- a/Documentation/kbuild/config-language.txt +++ b/Documentation/kbuild/config-language.txt @@ -193,7 +193,7 @@ output files. Configure: implemented Menuconfig: implemented -Xconfig: does not display, but writes to output files +Xconfig: implemented mconfig: implemented Example: diff --git a/Documentation/kernel-doc-nano-HOWTO.txt b/Documentation/kernel-doc-nano-HOWTO.txt new file mode 100644 index 00000000000..585d557abac --- /dev/null +++ b/Documentation/kernel-doc-nano-HOWTO.txt @@ -0,0 +1,128 @@ +kernel-doc nano-HOWTO +===================== + +Many places in the source tree have extractable documentation in the +form of block comments above functions. The components of this system +are: + +- scripts/kernel-doc + + This is a perl script that hunts for the block comments and can mark + them up directly into DocBook, man, text, and HTML. (No, not + texinfo.) + +- Documentation/DocBook/*.tmpl + + These are SGML template files, which are normal SGML files with + special place-holders for where the extracted documentation should + go. + +- scripts/docproc.c + + This is a program for converting SGML template files into SGML + files. It invokes kernel-doc, giving it the list of functions that + are to be documented. + +- scripts/gen-all-syms + + This is a script that lists the EXPORT_SYMBOL symbols in a list of C + files. + +- scripts/docgen + + This script invokes docproc, telling it which functions are to be + documented (this list comes from gen-all-syms). + +- Makefile + + The targets 'sgmldocs', 'psdocs', and 'pdfdocs' are used to build + DocBook files, PostScript files, and PDF files in + Documentation/DocBook. + +- Documentation/DocBook/Makefile + + This is where C files are associated with SGML templates. + + +How to extract the documentation +-------------------------------- + +If you just want to read the ready-made books on the various +subsystems (see Documentation/DocBook/*.tmpl), just type 'make +psdocs', or 'make pdfdocs', depending on your preference. If you +would rather read a different format, you can type 'make sgmldocs' and +then use DocBook tools to convert Documentation/DocBook/*.sgml to a +format of your choice (for example, 'db2html ...'). + +If you want to see man pages instead, you can do this: + +$ cd linux +$ scripts/kernel-doc -man $(find -name '*.c') | split-man.pl /tmp/man + +Here is split-man.pl: + +--> +#!/usr/bin/perl + +if ($#ARGV < 0) { + die "where do I put the results?\n"; +} + +mkdir $ARGV[0],0777 or die "Can't create $ARGV[0]: $!\n"; +$state = 0; +while () { + if (/^\.TH \"[^\"]*\" 4 \"([^\"]*)\"/) { + if ($state == 1) { close OUT } + $state = 1; + $fn = "$ARGV[0]/$1.4"; + print STDERR "Creating $fn\n"; + open OUT, ">$fn" or die "can't open $fn: $!\n"; + print OUT $_; + } elsif ($state != 0) { + print OUT $_; + } +} + +close OUT; +<-- + +If you just want to view the documentation for one function in one +file, you can do this: + +$ scripts/kernel-doc -man -function fn file | nroff -man | less + +or this: + +$ scripts/kernel-doc -text -function fn file + + +How to add extractable documentation to your source files +--------------------------------------------------------- + +The format of the block comment is like this: + +/** + * function_name(:)? (- short description)? +(* @parameterx: (description of parameter x)?)* +(* a blank line)? + * (Description:)? (Description of function)? + * (section header: (section description)? )* +(*)?*/ + +The short function description cannot be multiline, but the other +descriptions can be. + +All descriptive text is further processed, scanning for the following special +patterns, which are highlighted appropriately. + +'funcname()' - function +'$ENVVAR' - environment variable +'&struct_name' - name of a structure (up to two words including 'struct') +'@parameter' - name of a parameter +'%CONST' - name of a constant. + +Take a look around the source tree for examples. + +Tim. +*/ + diff --git a/Documentation/networking/8139too.txt b/Documentation/networking/8139too.txt index 92427b67957..879bf6d2f2f 100644 --- a/Documentation/networking/8139too.txt +++ b/Documentation/networking/8139too.txt @@ -132,6 +132,20 @@ And thanks to every supporter free software. +Submitting Bug Reports +---------------------- +Obtain and compile the modified rtl8139-diag source code from the +8139too driver Web site. This diagnostics programs, originally +from Donald Becker, has been modified to display all registers +on your RTL8139 chip, not just the first 0x80. + +If possible, send the output of a working and broken driver with + rtl8139-diag -mmmaaavvveefN > my-output-file.txt + +Send "lspci -vvv" or "cat /proc/pci" output for PCI information. + + + Known Bugs / Errata / To-Do --------------------------- The following issues are known, and are actively being pursued. Patches @@ -149,7 +163,7 @@ It is included only for enterprising hackers willing to help fix it. 4) Sparc64 platform not tested at all. -5) Identify and fix "rx wedge" when ping flooded. +5) Identify and fix "rx wedge" when ping flooded. (WIP) 7) N-Way auto-negotiation is known to fail in some cases. This problem also occurs in the rtl8139 driver in kernels 2.2.x/2.3.x. Solution: @@ -158,17 +172,30 @@ manually perform autonegotiation in case the network or card cannot do it automatically. (patches welcome) 8) Much improved command line / module parameter setup. (patches and -suggestions welcome) +suggestions welcome) (WIP) 9) Better documentation. (patches welcome) -10) User-mode (or maybe optional /proc) diagnostics program. +10) (rtl8139-diag modified from Becker version, DONE) +User-mode (or maybe optional /proc) diagnostics program. + +11) RTL8139C support untested. Change History -------------- +Version 0.9.5 - May 17, 2000 + +* Improved chip version recognition +* Continue banging away at receiver hang problem +* Use spin_lock_irq in another spot +* Don't print anything on pci_enable_device, it does so for us +* Disable buggy NWay code +* Define TxConfig bitmasks + + Version 0.9.4.1 - April 27, 2000 - third public beta release * Replace several "magic numbers" with symbolic constants diff --git a/Documentation/pci.txt b/Documentation/pci.txt index 63e035a9390..b96f6c48fec 100644 --- a/Documentation/pci.txt +++ b/Documentation/pci.txt @@ -89,6 +89,16 @@ Please mark the initialization and cleanup functions where appropriate function otherwise. __devexit The same for __exit. +Tips: + The module_init()/module_exit() functions (and all initialization + functions called only from these) should be marked __init/exit. + The struct pci_driver shouldn't be marked with any of these tags. + The ID table array should be marked __devinitdata. + The probe() and remove() functions (and all initialization + functions called only from these) should be marked __devinit/exit. + If you are sure the driver is not a hotplug driver then use only + __init/exit __initdata/exitdata. + 2. How to find PCI devices manually (the old style) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/s390/DASD b/Documentation/s390/DASD index b1cd0558a2a..6f8f9b527a9 100644 --- a/Documentation/s390/DASD +++ b/Documentation/s390/DASD @@ -22,7 +22,7 @@ The driver currently supports ECKD-devices and there are stubs for support of the FBA and CKD architectures. For the FBA architecture only some smart data structures are missing to make the support complete. -We performed our testing on 3380 and 3390 type disksof different +We performed our testing on 3380 and 3390 type disks of different sizes, under VM and on the bare hardware (LPAR), using internal disks of the multiprise as well as a RAMAC virtual array. Disks exported by an Enterprise Storage Server (Seascape) should work fine as well. diff --git a/Documentation/usb/ov511.txt b/Documentation/usb/ov511.txt index 58efa7ccfee..d57cc2e236b 100644 --- a/Documentation/usb/ov511.txt +++ b/Documentation/usb/ov511.txt @@ -6,11 +6,8 @@ Author: Mark McClelland Homepage: http://alpha.dyndns.org/ov511 NEW IN THIS VERSION: - o Improvements to sensor detection code - o Added "i2c_detect_tries" and "aperture" parameters - o proc filesystem status support - o read() fixed partially - o code cleanups and minor fixes + o 352x288 mode + o force_rgb parameter for apps that expect RGB instead of BGR INTRODUCTION: @@ -139,7 +136,7 @@ MODULE PARAMETERS: or so lines higher than the red component. This is only apparent in images with white objects on black backgrounds at 640x480. Setting this to 1 will realign the color planes correctly. NOTE: This is still - experimental and very buggy. + experimental and very buggy. You will likely need a fast (500 Mhz) CPU. NAME: snapshot TYPE: integer (boolean) @@ -148,8 +145,41 @@ MODULE PARAMETERS: button is pressed. Note that this does not yet work with most apps, including xawtv and vidcat. NOTE: See the section "TODO" for more info. + NAME: sensor + TYPE: integer ([0, 1, 3]) + DEFAULT: [varies] + DESC: If you know that your camera sensor is not detected correctly, set this + parameter. This is a global option for all attached OV511 cameras. You + will probably never need to set this, but if you do, valid values are: + 0 for OV7620 + 1 for OV7620AE + 3 for OV7610 + + NAME: i2c_detect_tries + TYPE: integer (don't set it insanely high!) + DEFAULT: 5 + DESC: This is the number of times the driver will try to sync and detect the + internal i2c bus (which connects the OV511 and sensor). If you are + getting intermittant detection failures ("Failed to read sensor ID...") + you should increase this by a modest amount. If setting it to 20 or so + doesn't fix things, look elsewhere for the cause of the problem. + + NAME: aperture + TYPE: integer (0 - 15) + DEFAULT: [varies by sensor] + DESC: For legal values, see the OV7610/7620 specs under register Common F. + This setting affects the upper nybble of that reg (bits 4-7). This is + for if you want to play with the camera's pixel saturation. + + NAME: force_rgb + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Force image to be read in RGB instead of BGR. This option allow + programs that expect RGB data (e.g. gqcam) to work with this driver. If + your colors look VERY wrong, you may want to change this. + WORKING FEATURES: - o Color streaming/capture at 640x480 and 320x240 + o Color streaming/capture at 640x480, 352x288, and 320x240 o YUV420 color o Monochrome o Setting/getting of saturation, contrast and brightness (no hue yet; only @@ -158,7 +188,7 @@ WORKING FEATURES: EXPERIMENTAL FEATURES: o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and - corrupted frames. + corrupted frames. If you have a very fast CPU, you can try it. o Snapshot mode (only works with some read() based apps; see below for more) o read() support @@ -197,6 +227,6 @@ CREDITS: The code is based in no small part on the CPiA driver by Johannes Erdfelt, Randy Dunlap, and others. Big thanks to them for their pioneering work on that -and the USB stack. Thanks to Bret Wallach for getting camera reg IO , ISOC, and -image capture working. Thanks to Orion Sky Lawlor and Kevin Moore for their -work as well. +and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and +image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio +Matsuoka for their work as well. diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt index 193ab6ddce8..00248f36e22 100644 --- a/Documentation/usb/usb-serial.txt +++ b/Documentation/usb/usb-serial.txt @@ -59,6 +59,13 @@ Current status: properly enumerated, assigned a port, and then communication _should_ be possible. The driver cleans up properly when the device is removed, or the connection is canceled on the Visor. + + NOTE: + This means that in order to talk to the Visor, the sync button must be + pressed BEFORE trying to get any program to communicate to the Visor. + This goes against the current documentation for pilot-xfer and other + packages, but is the only way that it will work due to the hardware + in the Visor. When the device is connected, try talking to it on the second port (this is usually /dev/ttyUSB1 if you do not have any other usb-serial @@ -107,6 +114,24 @@ ZyXEL omni.net lcd plus ISDN TA author at omninet@kroah.com +Digi AccelePort Driver + + This driver supports the Digi AccelePort USB 4 device, a 4 port + USB serial converter. The driver does NOT yet support the Digi + AccelePort USB 2 or 8. + + The driver supports open, close, read, write, termios settings (baud + rate, word size, parity, stop bits, hardware/software flow control, + CREAD), DTR/RTS, and TIOCMGET/SET/BIS/BIC ioctls. It has not been + thoroughly tested, but it seems to be working reasonable well. There + is more work to do, including flow control, ioctls, and support for + the Digi AccelePort USB 2 and 8. + + Please contact Peter Berger (pberger@brimson.com) or Al Borchers + (alborchers@steinerpoint.com) for questions or problems with this + driver. + + Generic Serial driver If your device is not one of the above listed devices, compatible with diff --git a/Documentation/video4linux/bttv/CARDLIST b/Documentation/video4linux/bttv/CARDLIST index e41e60edfb7..02ec58baac5 100644 --- a/Documentation/video4linux/bttv/CARDLIST +++ b/Documentation/video4linux/bttv/CARDLIST @@ -38,6 +38,10 @@ bttv.o card=36 - Typhoon TView TV/FM Tuner card=37 - PixelView PlayTV pro card=38 - TView99 CPH063 + card=39 - Pinnacle PCTV Rave + card=40 - STB2 + card=41 - AVerMedia TVPhone 98 + card=42 - ProVideo PV951 tuner.o type=0 - Temic PAL diff --git a/MAINTAINERS b/MAINTAINERS index 56f7268549d..764f931192e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -482,6 +482,7 @@ S: Supported IDE DRIVER [GENERAL] P: Andre Hedrick M: andre@linux-ide.org +M: ahedrick@atipa.com M: andre@suse.com L: linux-kernel@vger.rutgers.edu W: http://www.kernel.org/pub/linux/kernel/people/hedrick/ @@ -1139,6 +1140,13 @@ M: weissg@vienna.at L: linux-usb@suse.com S: Maintained +USB OV511 DRIVER +P: Mark McClelland +M: mmcclelland@delphi.com +L: linux-usb@suse.com +W: http://alpha.dyndns.org/ov511/ +S: Maintained + USB PEGASUS DRIVER P: Petko Manolov M: petkan@spct.net @@ -1151,6 +1159,13 @@ M: vojtech@suse.cz L: linux-usb@suse.com S: Supported +USB SERIAL DIGI ACCELEPORT DRIVER +P: Peter Berger and Al Borchers +M: pberger@brimson.com +M: alborchers@steinerpoint.com +L: linux-usb@suse.com +S: Supported + USB SERIAL DRIVER P: Greg Kroah-Hartman M: greg@kroah.com diff --git a/Makefile b/Makefile index e74645c25bd..d706803a347 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 3 SUBLEVEL = 99 -EXTRAVERSION = -pre8 +EXTRAVERSION = -pre9 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -78,10 +78,6 @@ endif CPPFLAGS := -D__KERNEL__ -I$(HPATH) -ifdef CONFIG_SMP -CPPFLAGS += -D__SMP__ -endif - CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 398de179c3e..238b4004f3a 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -42,7 +42,6 @@ #include #include -extern long do_sys_mount(char *, char *, char *, int, void *); extern int do_pipe(int *); extern asmlinkage int sys_swapon(const char *specialfile, int swap_flags); @@ -377,7 +376,7 @@ static int osf_ufs_mount(char *dirname, struct ufs_args *args, int flags) retval = PTR_ERR(devname); if (IS_ERR(devname)) goto out; - retval = do_sys_mount(devname, dirname, "ext2", flags, NULL); + retval = do_mount(devname, dirname, "ext2", flags, NULL); putname(devname); out: return retval; @@ -396,7 +395,7 @@ static int osf_cdfs_mount(char *dirname, struct cdfs_args *args, int flags) retval = PTR_ERR(devname); if (IS_ERR(devname)) goto out; - retval = do_sys_mount(devname, dirname, "iso9660", flags, NULL); + retval = do_mount(devname, dirname, "iso9660", flags, NULL); putname(devname); out: return retval; @@ -409,7 +408,7 @@ static int osf_procfs_mount(char *dirname, struct procfs_args *args, int flags) if (copy_from_user(&tmp, args, sizeof(tmp))) return -EFAULT; - return do_sys_mount("", dirname, "proc", flags, NULL); + return do_mount("", dirname, "proc", flags, NULL); } asmlinkage int osf_mount(unsigned long typenr, char *path, int flag, void *data) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 3988131acb0..b49cebfded6 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -182,7 +182,7 @@ ifeq ($(CONFIG_ARCH_ACORN),y) SUBDIRS += drivers/acorn DRIVERS += drivers/acorn/block/acorn-block.a DRIVERS += drivers/acorn/char/acorn-char.o -DRIVERS += drivers/acorn/net/acorn-net.a +DRIVERS += drivers/acorn/net/acorn-net.o DRIVERS += drivers/acorn/scsi/acorn-scsi.a endif diff --git a/arch/arm/def-configs/ebsa110 b/arch/arm/def-configs/ebsa110 index 417ad00ffff..42529e789a4 100644 --- a/arch/arm/def-configs/ebsa110 +++ b/arch/arm/def-configs/ebsa110 @@ -10,7 +10,7 @@ CONFIG_UID16=y CONFIG_EXPERIMENTAL=y # -# System and processor type +# System and Processor Type # # CONFIG_ARCH_ARC is not set # CONFIG_ARCH_A5K is not set @@ -25,6 +25,8 @@ CONFIG_CPU_SA110=y # CONFIG_PCI is not set # CONFIG_ISA is not set # CONFIG_ISA_DMA is not set +# CONFIG_SBUS is not set +# CONFIG_PCMCIA is not set # # Loadable module support @@ -69,7 +71,6 @@ CONFIG_LEDS=y # I2O device support # # CONFIG_I2O is not set -# CONFIG_I2O_PCI is not set # CONFIG_I2O_BLOCK is not set # CONFIG_I2O_LAN is not set # CONFIG_I2O_SCSI is not set @@ -87,13 +88,19 @@ CONFIG_LEDS=y # CONFIG_BLK_DEV_FD is not set # CONFIG_BLK_DEV_XD is not set # CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set # # Additional Block Devices # # CONFIG_BLK_DEV_LOOP is not set CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_LVM is not set # CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_RAID15_DANGEROUS is not set CONFIG_BLK_DEV_RAM=y # CONFIG_BLK_DEV_INITRD is not set @@ -124,7 +131,7 @@ CONFIG_PRINTER=m # Mice # # CONFIG_BUSMOUSE is not set -CONFIG_MOUSE=y +# CONFIG_MOUSE is not set # CONFIG_PSMOUSE is not set # CONFIG_82C710_MOUSE is not set # CONFIG_PC110_PAD is not set @@ -141,6 +148,7 @@ CONFIG_MOUSE=y CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set # CONFIG_WDT is not set +# CONFIG_WDTPCI is not set CONFIG_SOFT_WATCHDOG=y # CONFIG_PCWATCHDOG is not set # CONFIG_ACQUIRE_WDT is not set @@ -173,6 +181,7 @@ CONFIG_NETLINK=y CONFIG_RTNETLINK=y # CONFIG_NETLINK_DEV is not set CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -222,6 +231,7 @@ CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y # CONFIG_IP_NF_TARGET_MIRROR is not set CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=y CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_MANGLE=y @@ -302,10 +312,12 @@ CONFIG_ARM_AM79C961A=y # CONFIG_HIPPI is not set # CONFIG_PLIP is not set CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set CONFIG_PPP_ASYNC=y # CONFIG_PPP_SYNC_TTY is not set CONFIG_PPP_DEFLATE=m CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m # CONFIG_SLIP is not set # @@ -342,9 +354,10 @@ CONFIG_PPP_BSDCOMP=m # File systems # # CONFIG_QUOTA is not set -CONFIG_AUTOFS_FS=y +# CONFIG_AUTOFS_FS is not set CONFIG_AUTOFS4_FS=y # CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set @@ -354,10 +367,12 @@ CONFIG_AUTOFS4_FS=y # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set -CONFIG_MINIX_FS=y +# CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y # CONFIG_DEVFS_FS is not set @@ -365,23 +380,39 @@ CONFIG_PROC_FS=y # CONFIG_DEVFS_DEBUG is not set # CONFIG_DEVPTS_FS is not set # CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set # CONFIG_ROMFS_FS is not set # CONFIG_EXT2_FS is not set # CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set # CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set # # Network File Systems # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set # # Partition Types diff --git a/arch/arm/def-configs/footbridge b/arch/arm/def-configs/footbridge index acfd9de1758..ffe655f5db2 100644 --- a/arch/arm/def-configs/footbridge +++ b/arch/arm/def-configs/footbridge @@ -10,7 +10,7 @@ CONFIG_UID16=y CONFIG_EXPERIMENTAL=y # -# System and processor type +# System and Processor Type # # CONFIG_ARCH_ARC is not set # CONFIG_ARCH_A5K is not set @@ -19,6 +19,10 @@ CONFIG_EXPERIMENTAL=y CONFIG_FOOTBRIDGE=y CONFIG_HOST_FOOTBRIDGE=y # CONFIG_ADDIN_FOOTBRIDGE is not set + +# +# Footbridge Implementations +# CONFIG_ARCH_EBSA285=y # CONFIG_ARCH_CATS is not set CONFIG_ARCH_NETWINDER=y @@ -30,7 +34,10 @@ CONFIG_CPU_32v4=y CONFIG_CPU_SA110=y CONFIG_PCI=y CONFIG_PCI_NAMES=y +CONFIG_ISA=y CONFIG_ISA_DMA=y +# CONFIG_SBUS is not set +# CONFIG_PCMCIA is not set CONFIG_ALIGNMENT_TRAP=y # @@ -137,7 +144,11 @@ CONFIG_PARIDE_ON26=m # CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_LVM is not set # CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_RAID15_DANGEROUS is not set CONFIG_BLK_DEV_RAM=y # CONFIG_BLK_DEV_INITRD is not set @@ -202,6 +213,7 @@ CONFIG_PSMOUSE=y CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set # CONFIG_WDT is not set +# CONFIG_WDTPCI is not set CONFIG_SOFT_WATCHDOG=y # CONFIG_PCWATCHDOG is not set # CONFIG_ACQUIRE_WDT is not set @@ -242,6 +254,7 @@ CONFIG_VIDEO_DEV=y # CONFIG_VIDEO_PMS is not set # CONFIG_VIDEO_BWQCAM is not set # CONFIG_VIDEO_CQCAM is not set +# CONFIG_VIDEO_CPIA is not set # CONFIG_VIDEO_SAA5249 is not set # CONFIG_TUNER_3036 is not set # CONFIG_VIDEO_STRADIS is not set @@ -262,56 +275,6 @@ CONFIG_VIDEO_CYBERPRO=m # CONFIG_AGP is not set # -# USB support -# -CONFIG_USB=m - -# -# USB Controllers -# -# CONFIG_USB_UHCI is not set -# CONFIG_USB_UHCI_ALT is not set -CONFIG_USB_OHCI=m - -# -# Miscellaneous USB options -# -# CONFIG_USB_DEVICEFS is not set - -# -# USB Devices -# -CONFIG_USB_PRINTER=m -CONFIG_USB_SCANNER=m -CONFIG_USB_AUDIO=m -# CONFIG_USB_ACM is not set -# CONFIG_USB_SERIAL is not set -# CONFIG_USB_CPIA is not set -# CONFIG_USB_IBMCAM is not set -# CONFIG_USB_OV511 is not set -# CONFIG_USB_DC2XX is not set -# CONFIG_USB_STORAGE is not set -# CONFIG_USB_USS720 is not set -# CONFIG_USB_DABUSB is not set -# CONFIG_USB_PLUSB is not set -# CONFIG_USB_PEGASUS is not set -# CONFIG_USB_RIO500 is not set -# CONFIG_USB_DSBR is not set - -# -# USB HID -# -# CONFIG_USB_HID is not set -CONFIG_USB_KBD=m -CONFIG_USB_MOUSE=m -# CONFIG_USB_WACOM is not set -# CONFIG_USB_WMFORCE is not set -# CONFIG_INPUT_KEYBDEV is not set -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_EVDEV is not set - -# # Console drivers # CONFIG_VGA_CONSOLE=y @@ -522,10 +485,12 @@ CONFIG_NE2K_PCI=y # CONFIG_HIPPI is not set # CONFIG_PLIP is not set CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set CONFIG_PPP_ASYNC=m # CONFIG_PPP_SYNC_TTY is not set CONFIG_PPP_DEFLATE=m CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m CONFIG_SLIP=m CONFIG_SLIP_COMPRESSED=y CONFIG_SLIP_SMART=y @@ -588,9 +553,10 @@ CONFIG_BLK_DEV_IDEDMA=y CONFIG_IDEDMA_PCI_EXPERIMENTAL=y # CONFIG_IDEDMA_PCI_WIP is not set # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set -# CONFIG_BLK_DEV_AEC6210 is not set -# CONFIG_AEC6210_TUNING is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_AEC62XX_TUNING is not set # CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD7409 is not set # CONFIG_AMD7409_OVERRIDE is not set # CONFIG_BLK_DEV_CMD64X is not set @@ -610,6 +576,7 @@ CONFIG_BLK_DEV_PDC202XX=y # CONFIG_BLK_DEV_SIS5513 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_VIA82CXXX_TUNING is not set CONFIG_BLK_DEV_SL82C105=y # CONFIG_IDE_CHIPSETS is not set CONFIG_IDEDMA_AUTO=y @@ -625,6 +592,7 @@ CONFIG_BLK_DEV_IDE_MODES=y # CONFIG_SOUND=m # CONFIG_SOUND_CMPCI is not set +# CONFIG_SOUND_EMU10K1 is not set # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set # CONFIG_SOUND_ESSSOLO1 is not set @@ -633,6 +601,7 @@ CONFIG_SOUND=m # CONFIG_SOUND_TRIDENT is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_VIA82CXXX is not set CONFIG_SOUND_OSS=m # CONFIG_SOUND_TRACEINIT is not set # CONFIG_SOUND_DMAP is not set @@ -643,6 +612,7 @@ CONFIG_SOUND_ADLIB=m # CONFIG_SOUND_CS4232 is not set # CONFIG_SOUND_SSCAPE is not set # CONFIG_SOUND_GUS is not set +# CONFIG_SOUND_ICH is not set # CONFIG_SOUND_VMIDI is not set # CONFIG_SOUND_TRIX is not set # CONFIG_SOUND_MSS is not set @@ -657,7 +627,6 @@ CONFIG_SOUND_SB=m # CONFIG_SOUND_AWE32_SYNTH is not set # CONFIG_SOUND_WAVEFRONT is not set # CONFIG_SOUND_MAUI is not set -# CONFIG_SOUND_VIA82CXXX is not set # CONFIG_SOUND_YM3812 is not set # CONFIG_SOUND_OPL3SA1 is not set # CONFIG_SOUND_OPL3SA2 is not set @@ -670,8 +639,8 @@ CONFIG_SOUND_WAVEARTIST=m # File systems # # CONFIG_QUOTA is not set -CONFIG_AUTOFS_FS=y -# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=y CONFIG_ADFS_FS=m # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set @@ -683,10 +652,12 @@ CONFIG_MSDOS_FS=m CONFIG_VFAT_FS=m # CONFIG_EFS_FS is not set # CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set CONFIG_ISO9660_FS=m CONFIG_JOLIET=y # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y # CONFIG_DEVFS_FS is not set @@ -694,17 +665,22 @@ CONFIG_PROC_FS=y # CONFIG_DEVFS_DEBUG is not set CONFIG_DEVPTS_FS=y # CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set # CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set # # Network File Systems # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y CONFIG_NFSD=m # CONFIG_NFSD_V3 is not set @@ -712,6 +688,16 @@ CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set # # Partition Types @@ -767,6 +753,57 @@ CONFIG_NLS_ISO8859_15=m # CONFIG_NLS_KOI8_R is not set # +# USB support +# +CONFIG_USB=m +CONFIG_USB_DEBUG=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +CONFIG_USB_OHCI=m + +# +# USB Devices +# +CONFIG_USB_PRINTER=m +CONFIG_USB_SCANNER=m +CONFIG_USB_AUDIO=m +# CONFIG_USB_ACM is not set +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_USS720 is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_PLUSB is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_DSBR is not set + +# +# USB HID +# +# CONFIG_USB_HID is not set +CONFIG_USB_KBD=m +CONFIG_USB_MOUSE=m +# CONFIG_USB_WACOM is not set +# CONFIG_USB_WMFORCE is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# # Kernel hacking # CONFIG_FRAME_POINTER=y @@ -774,4 +811,5 @@ CONFIG_DEBUG_ERRORS=y CONFIG_DEBUG_USER=y # CONFIG_DEBUG_INFO is not set CONFIG_MAGIC_SYSRQ=y -# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_DC21285_PORT is not set diff --git a/arch/arm/def-configs/rpc b/arch/arm/def-configs/rpc index a22e1d99258..d55bdf89f2b 100644 --- a/arch/arm/def-configs/rpc +++ b/arch/arm/def-configs/rpc @@ -2,6 +2,7 @@ # Automatically generated make config: don't edit # CONFIG_ARM=y +CONFIG_UID16=y # # Code maturity level options @@ -9,7 +10,7 @@ CONFIG_ARM=y CONFIG_EXPERIMENTAL=y # -# System and processor type +# System and Processor Type # # CONFIG_ARCH_ARC is not set # CONFIG_ARCH_A5K is not set @@ -23,7 +24,11 @@ CONFIG_CPU_32v3=y CONFIG_CPU_ARM6=y CONFIG_CPU_ARM7=y CONFIG_CPU_SA110=y +# CONFIG_PCI is not set +# CONFIG_ISA is not set # CONFIG_ISA_DMA is not set +# CONFIG_SBUS is not set +# CONFIG_PCMCIA is not set # CONFIG_ALIGNMENT_TRAP is not set # @@ -47,10 +52,14 @@ CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_ARTHUR is not set + +# +# Parallel port support +# CONFIG_PARPORT=y CONFIG_PARPORT_PC=y # CONFIG_PARPORT_PC_FIFO is not set -# CONFIG_PARPORT_PC_PCMCIA is not set +CONFIG_PARPORT_PC_SUPERIO=y # CONFIG_PARPORT_ARC is not set # CONFIG_PARPORT_AMIGA is not set # CONFIG_PARPORT_MFC3 is not set @@ -63,7 +72,6 @@ CONFIG_PARPORT_PC=y # I2O device support # # CONFIG_I2O is not set -# CONFIG_I2O_PCI is not set # CONFIG_I2O_BLOCK is not set # CONFIG_I2O_LAN is not set # CONFIG_I2O_SCSI is not set @@ -79,44 +87,23 @@ CONFIG_PARPORT_PC=y # Block devices # CONFIG_BLK_DEV_FD=y -CONFIG_BLK_DEV_IDE=y - -# -# Please see Documentation/ide.txt for help/info on IDE drives -# -# CONFIG_BLK_DEV_HD_IDE is not set -CONFIG_BLK_DEV_IDEDISK=y -CONFIG_IDEDISK_MULTI_MODE=y -CONFIG_BLK_DEV_IDECD=y -# CONFIG_BLK_DEV_IDETAPE is not set -# CONFIG_BLK_DEV_IDEFLOPPY is not set -# CONFIG_BLK_DEV_IDESCSI is not set - -# -# IDE chipset support/bugfixes -# -# CONFIG_BLK_DEV_CMD640 is not set -CONFIG_BLK_DEV_IDE_ICSIDE=y -CONFIG_BLK_DEV_IDEDMA_ICS=y -CONFIG_IDEDMA_ICS_AUTO=y -CONFIG_BLK_DEV_IDE_RAPIDE=y -CONFIG_BLK_DEV_IDEDMA=y -CONFIG_IDEDMA_AUTO=y -# CONFIG_IDE_CHIPSETS is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set # # Additional Block Devices # CONFIG_BLK_DEV_LOOP=m # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_LVM is not set # CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_RAID15_DANGEROUS is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y -# CONFIG_BLK_DEV_XD is not set -CONFIG_PARIDE_PARPORT=y -# CONFIG_PARIDE is not set -# CONFIG_BLK_DEV_IDE_MODES is not set -# CONFIG_BLK_DEV_HD is not set # # Acorn-specific block devices @@ -139,6 +126,17 @@ CONFIG_PRINTER=m # CONFIG_PPDEV is not set # +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_PHILIPSPAR is not set +# CONFIG_I2C_ELV is not set +# CONFIG_I2C_VELLEMAN is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_CHARDEV=y + +# # Mice # CONFIG_BUSMOUSE=y @@ -177,20 +175,10 @@ CONFIG_MOUSE=y # CONFIG_FTAPE is not set # CONFIG_DRM is not set # CONFIG_DRM_TDFX is not set - -# -# PCMCIA character device support -# -# CONFIG_PCMCIA_SERIAL_CS is not set # CONFIG_AGP is not set CONFIG_RPCMOUSE=y # -# Support for USB -# -# CONFIG_USB is not set - -# # Console drivers # CONFIG_FB=y @@ -201,10 +189,6 @@ CONFIG_FB=y CONFIG_FB=y CONFIG_DUMMY_CONSOLE=y CONFIG_FB_ACORN=y -# CONFIG_FB_MATROX is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_3DFX is not set # CONFIG_FB_VIRTUAL is not set CONFIG_FBCON_ADVANCED=y CONFIG_FBCON_MFB=y @@ -222,6 +206,7 @@ CONFIG_FBCON_CFB32=y # CONFIG_FBCON_MAC is not set # CONFIG_FBCON_VGA_PLANES is not set # CONFIG_FBCON_VGA is not set +# CONFIG_FBCON_HGA is not set # CONFIG_FBCON_FONTWIDTH8_ONLY is not set CONFIG_FBCON_FONTS=y # CONFIG_FONT_8x8 is not set @@ -298,6 +283,7 @@ CONFIG_NETDEVICES=y # # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set +# CONFIG_BONDING is not set # CONFIG_EQUALIZER is not set # CONFIG_NET_SB1000 is not set @@ -312,12 +298,10 @@ CONFIG_ARM_ETHERH=y # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set -# CONFIG_RTL8139 is not set -# CONFIG_DM9102 is not set # CONFIG_AT1700 is not set # CONFIG_DEPCA is not set # CONFIG_NET_ISA is not set -# CONFIG_NET_EISA is not set +# CONFIG_NET_PCI is not set # CONFIG_NET_POCKET is not set # @@ -330,10 +314,12 @@ CONFIG_ARM_ETHERH=y # CONFIG_HIPPI is not set # CONFIG_PLIP is not set CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set # CONFIG_PPP_ASYNC is not set # CONFIG_PPP_SYNC_TTY is not set # CONFIG_PPP_DEFLATE is not set # CONFIG_PPP_BSDCOMP is not set +CONFIG_PPPOE=m # CONFIG_SLIP is not set # @@ -342,7 +328,7 @@ CONFIG_PPP=m # CONFIG_NET_RADIO is not set # -# Token Ring driver support +# Token Ring devices # # CONFIG_TR is not set # CONFIG_NET_FC is not set @@ -355,9 +341,42 @@ CONFIG_PPP=m # CONFIG_WAN is not set # -# PCMCIA network device support +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices # -# CONFIG_NET_PCMCIA is not set +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +# CONFIG_BLK_DEV_IDECS is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +CONFIG_BLK_DEV_IDE_ICSIDE=y +CONFIG_BLK_DEV_IDEDMA_ICS=y +CONFIG_IDEDMA_ICS_AUTO=y +CONFIG_BLK_DEV_IDEDMA=y +CONFIG_BLK_DEV_IDE_RAPIDE=y +# CONFIG_IDE_CHIPSETS is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_IDE_MODES is not set # # SCSI support @@ -368,9 +387,11 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=y -# CONFIG_CHR_DEV_ST is not set +CONFIG_SD_EXTRA_DEVS=8 +CONFIG_CHR_DEV_ST=m CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_SR_EXTRA_DEVS=2 CONFIG_CHR_DEV_SG=y # @@ -390,12 +411,12 @@ CONFIG_SCSI_LOGGING=y # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set # CONFIG_SCSI_AIC7XXX is not set -# CONFIG_SCSI_IPS is not set # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set # CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_DTC3280 is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_DMA is not set @@ -403,6 +424,8 @@ CONFIG_SCSI_LOGGING=y # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set CONFIG_SCSI_PPA=m CONFIG_SCSI_IMM=m # CONFIG_SCSI_IZIP_EPP16 is not set @@ -410,15 +433,14 @@ CONFIG_SCSI_IMM=m # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set # CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set -# CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_T128 is not set # CONFIG_SCSI_U14_34F is not set -# CONFIG_SCSI_ULTRASTOR is not set # CONFIG_SCSI_DEBUG is not set CONFIG_SCSI_ACORNSCSI_3=m CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y @@ -439,6 +461,7 @@ CONFIG_SCSI_OAK1=m # CONFIG_SOUND=m # CONFIG_SOUND_CMPCI is not set +# CONFIG_SOUND_EMU10K1 is not set # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set # CONFIG_SOUND_ESSSOLO1 is not set @@ -447,12 +470,18 @@ CONFIG_SOUND=m # CONFIG_SOUND_TRIDENT is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_VIA82CXXX is not set CONFIG_SOUND_OSS=m +# CONFIG_SOUND_TRACEINIT is not set +# CONFIG_SOUND_DMAP is not set # CONFIG_SOUND_AD1816 is not set # CONFIG_SOUND_SGALAXY is not set +# CONFIG_SOUND_ADLIB is not set +# CONFIG_SOUND_ACI_MIXER is not set # CONFIG_SOUND_CS4232 is not set # CONFIG_SOUND_SSCAPE is not set # CONFIG_SOUND_GUS is not set +# CONFIG_SOUND_ICH is not set # CONFIG_SOUND_VMIDI is not set # CONFIG_SOUND_TRIX is not set # CONFIG_SOUND_MSS is not set @@ -460,30 +489,29 @@ CONFIG_SOUND_OSS=m # CONFIG_SOUND_NM256 is not set # CONFIG_SOUND_MAD16 is not set # CONFIG_SOUND_PAS is not set +# CONFIG_PAS_JOYSTICK is not set # CONFIG_SOUND_PSS is not set -# CONFIG_SOUND_SOFTOSS is not set +CONFIG_SOUND_SOFTOSS=m # CONFIG_SOUND_SB is not set +# CONFIG_SOUND_AWE32_SYNTH is not set # CONFIG_SOUND_WAVEFRONT is not set # CONFIG_SOUND_MAUI is not set -# CONFIG_SOUND_VIA82CXXX is not set # CONFIG_SOUND_YM3812 is not set # CONFIG_SOUND_OPL3SA1 is not set # CONFIG_SOUND_OPL3SA2 is not set # CONFIG_SOUND_UART6850 is not set +# CONFIG_SOUND_AEDSP16 is not set CONFIG_SOUND_VIDC=m # CONFIG_SOUND_WAVEARTIST is not set # -# Additional low level sound drivers -# -# CONFIG_LOWLEVEL_SOUND is not set - -# -# Filesystems +# File systems # # CONFIG_QUOTA is not set # CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=m CONFIG_ADFS_FS=y +# CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set @@ -492,35 +520,66 @@ CONFIG_ADFS_FS=y # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y CONFIG_JOLIET=y # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set # CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set # CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set # # Network File Systems # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set # CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set # # Partition Types # CONFIG_PARTITION_ADVANCED=y +CONFIG_ACORN_PARTITION=y +CONFIG_ACORN_PARTITION_ICS=y +CONFIG_ACORN_PARTITION_ADFS=y +CONFIG_ACORN_PARTITION_POWERTEC=y +CONFIG_ACORN_PARTITION_RISCIX=y CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +# CONFIG_ATARI_PARTITION is not set CONFIG_MAC_PARTITION=y CONFIG_MSDOS_PARTITION=y CONFIG_BSD_DISKLABEL=y @@ -528,13 +587,6 @@ CONFIG_SOLARIS_X86_PARTITION=y # CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_SGI_PARTITION=y CONFIG_SUN_PARTITION=y -CONFIG_AMIGA_PARTITION=y -# CONFIG_ATARI_PARTITION is not set -CONFIG_ACORN_PARTITION=y -CONFIG_ACORN_PARTITION_ADFS=y -CONFIG_ACORN_PARTITION_ICS=y -CONFIG_ACORN_PARTITION_POWERTEC=y -CONFIG_ACORN_PARTITION_RISCIX=y CONFIG_NLS=y # @@ -570,6 +622,11 @@ CONFIG_NLS_ISO8859_9=m CONFIG_NLS_KOI8_R=m # +# USB support +# +# CONFIG_USB is not set + +# # Kernel hacking # CONFIG_FRAME_POINTER=y diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index d8f14a756d3..a129ca2f63a 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -34,7 +34,7 @@ obj- := export-objs := armksyms.o dma.o ecard.o hw-footbridge.o leds-$(MACHINE).o -obj-$(CONFIG_ARCH_ACORN) += dma.o ecard.o iic.o fiq.o time-acorn.o +obj-$(CONFIG_ARCH_ACORN) += dma.o ecard.o fiq.o time-acorn.o obj-$(CONFIG_DEBUG_LL) += debug-$(PROCESSOR).o obj-$(CONFIG_MODULES) += armksyms.o obj-$(CONFIG_LEDS) += leds-$(MACHINE).o diff --git a/arch/arm/kernel/iic.c b/arch/arm/kernel/iic.c deleted file mode 100644 index c9a672a321c..00000000000 --- a/arch/arm/kernel/iic.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * linux/arch/arm/kernel/iic.c - * - * Copyright (C) 1995, 1996 Russell King - * - * IIC is used to get the current time from the CMOS rtc. - */ - -#include -#include - -#include -#include -#include -#include - -#define FORCE_ONES 0xdc - -/* - * if delay loop has been calibrated then us that, - * else use IOC timer 1. - */ -static void iic_delay(void) -{ - extern unsigned long loops_per_sec; - if (loops_per_sec != (1 << 12)) { - udelay(100); /* was 10 */ - return; - } else { - unsigned long flags; - save_flags_cli(flags); - - outb(254, IOC_T1LTCHL); - outb(255, IOC_T1LTCHH); - outb(0, IOC_T1GO); - outb(1<<6, IOC_IRQCLRA); /* clear T1 irq */ - outb(10, IOC_T1LTCHL); /* was 4 */ - outb(0, IOC_T1LTCHH); - outb(0, IOC_T1GO); - while ((inb(IOC_IRQSTATA) & (1<<6)) == 0); - restore_flags(flags); - } -} - -#define IIC_INIT() dat = (inb(IOC_CONTROL) | FORCE_ONES) & ~3 -#define IIC_SET_DAT outb(dat|=1, IOC_CONTROL); -#define IIC_CLR_DAT outb(dat&=~1, IOC_CONTROL); -#define IIC_SET_CLK outb(dat|=2, IOC_CONTROL); -#define IIC_CLR_CLK outb(dat&=~2, IOC_CONTROL); -#define IIC_DELAY iic_delay(); -#define IIC_READ_DATA() (inb(IOC_CONTROL) & 1) - -static inline void iic_set_lines(int clk, int dat) -{ - int old; - - old = inb(IOC_CONTROL) | FORCE_ONES; - - old &= ~3; - - if (clk) - old |= 2; - if (dat) - old |= 1; - - outb(old, IOC_CONTROL); - - iic_delay(); -} - -static inline unsigned int iic_read_data(void) -{ - return inb(IOC_CONTROL) & 1; -} - -/* - * C: ==~~_ - * D: =~~__ - */ -static inline void iic_start(void) -{ - unsigned int dat; - - IIC_INIT(); - - IIC_SET_DAT - IIC_DELAY - IIC_SET_CLK - IIC_DELAY - - IIC_CLR_DAT - IIC_DELAY - IIC_CLR_CLK - IIC_DELAY -} - -/* - * C: __~~ - * D: =__~ - */ -static inline void iic_stop(void) -{ - unsigned int dat; - - IIC_INIT(); - - IIC_CLR_DAT - IIC_DELAY - IIC_SET_CLK - IIC_DELAY - IIC_SET_DAT - IIC_DELAY -} - -/* - * C: __~_ - * D: =___ - */ -static inline void iic_acknowledge(void) -{ - unsigned int dat; - - IIC_INIT(); - - IIC_CLR_DAT - IIC_DELAY - IIC_SET_CLK - IIC_DELAY - IIC_CLR_CLK - IIC_DELAY -} - -/* - * C: __~_ - * D: =~H~ - */ -static inline int iic_is_acknowledged(void) -{ - unsigned int dat, ack_bit; - - IIC_INIT(); - - IIC_SET_DAT - IIC_DELAY - IIC_SET_CLK - IIC_DELAY - - ack_bit = IIC_READ_DATA(); - - IIC_CLR_CLK - IIC_DELAY - - return ack_bit == 0; -} - -/* - * C: _~__~__~__~__~__~__~__~_ - * D: =DDXDDXDDXDDXDDXDDXDDXDD - */ -static void iic_sendbyte(unsigned int b) -{ - unsigned int dat, i; - - IIC_INIT(); - - for (i = 0; i < 8; i++) { - if (b & 128) - IIC_SET_DAT - else - IIC_CLR_DAT - IIC_DELAY - - IIC_SET_CLK - IIC_DELAY - IIC_CLR_CLK - IIC_DELAY - - b <<= 1; - } -} - -/* - * C: __~_~_~_~_~_~_~_~_ - * D: =~HHHHHHHHHHHHHHHH - */ -static unsigned char iic_recvbyte(void) -{ - unsigned int dat, i, in; - - IIC_INIT(); - - IIC_SET_DAT - IIC_DELAY - - in = 0; - for (i = 0; i < 8; i++) { - IIC_SET_CLK - IIC_DELAY - - in = (in << 1) | IIC_READ_DATA(); - - IIC_CLR_CLK - IIC_DELAY - } - - return in; -} - -int iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len) -{ - int i, err = -EIO; - - iic_start(); - iic_sendbyte(addr & 0xfe); - if (!iic_is_acknowledged()) - goto error; - - iic_sendbyte(loc); - if (!iic_is_acknowledged()) - goto error; - - if (addr & 1) { - iic_stop(); - iic_start(); - iic_sendbyte(addr|1); - if (!iic_is_acknowledged()) - goto error; - - for (i = 0; i < len - 1; i++) { - buf[i] = iic_recvbyte(); - iic_acknowledge(); - } - buf[i] = iic_recvbyte(); - } else { - for (i = 0; i < len; i++) { - iic_sendbyte(buf[i]); - - if (!iic_is_acknowledged()) - goto error; - } - } - - err = 0; -error: - iic_stop(); - - return err; -} diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 18d9931eefe..1d692dd3503 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -399,13 +399,13 @@ void __bug(const char *file, int line, void *data) printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line); if (data) printk(KERN_CRIT"extra data = %p\n", data); - *(int *)0 = 0; + BUG(); } void __readwrite_bug(const char *fn) { printk("%s called, but not implemented", fn); - *(int *)0 = 0; + BUG(); } void __pte_error(const char *file, int line, unsigned long val) @@ -436,7 +436,7 @@ void abort(void) printk(KERN_CRIT "abort() called from %p! (Please " "report to rmk@arm.linux.org.uk)\n", lr); - *(int *)0 = 0; + BUG(); /* if that doesn't kill us, halt */ panic("Oops failed to kill thread"); diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 5f48f951abf..e932164bf8b 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -20,6 +20,7 @@ L_OBJS_rpc := io-acorn.o floppydma.o L_OBJS_clps7500 := io-acorn.o L_OBJS_ebsa110 := io-ebsa110.o L_OBJS_footbridge := io-footbridge.o +L_OBJS_l7200 := io-acorn.o L_OBJS_nexuspci := io-footbridge.o L_OBJS_sa1100 := io-footbridge.o L_OBJS_shark := io-shark.o diff --git a/arch/i386/config.in b/arch/i386/config.in index 8f4782ca80f..1208a6b82a6 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -180,7 +180,6 @@ if [ "$CONFIG_APM" != "n" ]; then bool ' Enable PM at boot time' CONFIG_APM_DO_ENABLE bool ' Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE bool ' Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK - bool ' Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS diff --git a/arch/i386/defconfig b/arch/i386/defconfig index f618cac4bb7..e00ac753a26 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -111,7 +111,6 @@ CONFIG_BLK_DEV_FD=y # CONFIG_BLK_DEV_MD is not set # CONFIG_MD_LINEAR is not set # CONFIG_MD_RAID0 is not set -# CONFIG_RAID15_DANGEROUS is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set @@ -169,6 +168,13 @@ CONFIG_BLK_DEV_IDE=y # CONFIG_BLK_DEV_HD is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set # CONFIG_BLK_DEV_IDECS is not set CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c index d9dabac2f41..2e220bc06ec 100644 --- a/arch/i386/kernel/acpi.c +++ b/arch/i386/kernel/acpi.c @@ -829,7 +829,7 @@ const static struct pci_device_id acpi_pci_tbl[] = {0x8086, 0x7113, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_INTEL_PIIX4}, {0x1106, 0x3040, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VIA_586}, {0x1106, 0x3057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VIA_686A}, - {0,}, /* terminate list */ + {0,} /* terminate list */ }; static int __init acpi_probe(struct pci_dev *dev, diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index bf3d3c0af6c..eab365e260e 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -30,11 +30,6 @@ int prof_multiplier[NR_CPUS] = { 1, }; int prof_old_multiplier[NR_CPUS] = { 1, }; int prof_counter[NR_CPUS] = { 1, }; -/* - * IA s/w dev Vol 3, Section 7.4 - */ -#define APIC_DEFAULT_PHYS_BASE 0xfee00000 - int get_maxlvt(void) { unsigned int v, ver, maxlvt; diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 99e2587565a..b1e0e8b7c61 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -132,6 +132,9 @@ * 1.13: Changes for new pm_ interfaces (Andy Henroid * ). * Modularize the code. + * Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS + * is now the way life works). + * Fix thinko in suspend() (wrong return). * * APM 1.1 Reference: * @@ -308,9 +311,7 @@ static int clock_slowed = 0; #endif static int suspends_pending = 0; static int standbys_pending = 0; -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND static int waiting_for_resume = 0; -#endif #ifdef CONFIG_APM_RTC_IS_GMT # define clock_cmos_diff 0 @@ -866,22 +867,22 @@ static void reinit_timer(void) static int suspend(void) { int err; - int ret; struct apm_user *as; get_time_diff(); err = apm_set_power_state(APM_STATE_SUSPEND); reinit_timer(); set_time(); - ret = (err == APM_SUCCESS) || (err == APM_NO_ERROR); - if (!ret) + if (err == APM_NO_ERROR) + err = APM_SUCCESS; + if (err != APM_SUCCESS) apm_error("suspend", err); for (as = user_list; as != NULL; as = as->next) { as->suspend_wait = 0; - as->suspend_result = (ret ? 0 : -EIO); + as->suspend_result = ((err == APM_SUCCESS) ? 0 : -EIO); } wake_up_interruptible(&apm_suspend_waitqueue); - return ret; + return err; } static void standby(void) @@ -962,14 +963,7 @@ static void check_events(void) switch (event) { case APM_SYS_STANDBY: case APM_USER_STANDBY: -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND - if (waiting_for_resume) - break; -#endif if (send_event(event, NULL)) { -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND - waiting_for_resume = 1; -#endif if (standbys_pending <= 0) standby(); } @@ -986,14 +980,18 @@ static void check_events(void) if (ignore_bounce) break; #endif -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + /* + * If we are already processing a SUSPEND, + * then further SUSPEND events from the BIOS + * will be ignored. We also return here to + * cope with the fact that the Thinkpads keep + * sending a SUSPEND event until something else + * happens! + */ if (waiting_for_resume) - break; -#endif + return; if (send_event(event, NULL)) { -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND waiting_for_resume = 1; -#endif if (suspends_pending <= 0) (void) suspend(); } @@ -1002,9 +1000,7 @@ static void check_events(void) case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: case APM_STANDBY_RESUME: -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND waiting_for_resume = 0; -#endif #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE last_resume = jiffies; ignore_bounce = 1; @@ -1036,8 +1032,10 @@ static void apm_event_handler(void) int err; if ((standbys_pending > 0) || (suspends_pending > 0)) { - if ((apm_bios_info.version > 0x100) && (pending_count-- < 0)) { + if ((apm_bios_info.version > 0x100) && (pending_count-- <= 0)) { pending_count = 4; + if (debug) + printk(KERN_DEBUG "apm: setting state busy\n"); err = apm_set_power_state(APM_STATE_BUSY); if (err) apm_error("busy", err); @@ -1097,7 +1095,7 @@ static void apm_mainloop(void) static int check_apm_user(struct apm_user *as, const char *func) { if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { - printk(KERN_ERR "apm: %s passed bad filp", func); + printk(KERN_ERR "apm: %s passed bad filp\n", func); return 1; } return 0; @@ -1200,7 +1198,7 @@ static int do_ioctl(struct inode * inode, struct file *filp, } else if (!send_event(APM_USER_SUSPEND, as)) return -EAGAIN; if (suspends_pending <= 0) { - if (!suspend()) + if (suspend() != APM_SUCCESS) return -EIO; } else { as->suspend_wait = 1; @@ -1251,7 +1249,7 @@ static int do_release(struct inode * inode, struct file * filp) as1 = as1->next) ; if (as1 == NULL) - printk(KERN_ERR "apm: filp not in user list"); + printk(KERN_ERR "apm: filp not in user list\n"); else as1->next = as->next; } @@ -1268,7 +1266,7 @@ static int do_open(struct inode * inode, struct file * filp) as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL); if (as == NULL) { - printk(KERN_ERR "apm: cannot allocate struct of size %d bytes", + printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n", sizeof(*as)); MOD_DEC_USE_COUNT; return -ENOMEM; diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index 334a75f88ac..e88fa90229b 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -456,7 +456,7 @@ void __init init_IRQ(void) * IRQ0 must be given a fixed assignment and initialized, * because it's used before the IO-APIC is set up. */ - set_intr_gate(IRQ0_TRAP_VECTOR, interrupt[0]); + set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); /* * The reschedule interrupt is a CPU-to-CPU reschedule-helper diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index dfee6e4d55f..7bf275c14b8 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -48,6 +48,11 @@ struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; /* MP IRQ source entries */ int mp_irq_entries = 0; +#if CONFIG_SMP +# define TARGET_CPUS cpu_online_map +#else +# define TARGET_CPUS 0x01 +#endif /* * Rough estimation of how many shared IRQs there are, can * be changed anytime. @@ -89,7 +94,7 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin) entry->pin = pin; } -#define __DO_ACTION(name,R,ACTION, FINAL) \ +#define __DO_ACTION(R, ACTION, FINAL) \ \ { \ int pin; \ @@ -112,8 +117,8 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin) #define DO_ACTION(name,R,ACTION, FINAL) \ \ -static void name##_IO_APIC_irq(unsigned int irq) \ -__DO_ACTION(name,R,ACTION, FINAL) + static void name##_IO_APIC_irq (unsigned int irq) \ + __DO_ACTION(R, ACTION, FINAL) DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic))/* mask = 1 */ DO_ACTION( __unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ @@ -542,25 +547,26 @@ static inline int IO_APIC_irq_trigger(int irq) return 0; } -int irq_vector[NR_IRQS] = { IRQ0_TRAP_VECTOR , 0 }; +int irq_vector[NR_IRQS] = { FIRST_DEVICE_VECTOR , 0 }; static int __init assign_irq_vector(int irq) { - static int current_vector = IRQ0_TRAP_VECTOR, offset = 0; + static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; if (IO_APIC_VECTOR(irq) > 0) return IO_APIC_VECTOR(irq); - if (current_vector == 0xFF) - panic("ran out of interrupt sources!"); next: current_vector += 8; if (current_vector == SYSCALL_VECTOR) goto next; - if (current_vector > 0xFF) { + if (current_vector > FIRST_SYSTEM_VECTOR) { offset++; - current_vector = IRQ0_TRAP_VECTOR + offset; + current_vector = FIRST_DEVICE_VECTOR + offset; } + if (current_vector == FIRST_SYSTEM_VECTOR) + panic("ran out of interrupt sources!"); + IO_APIC_VECTOR(irq) = current_vector; return current_vector; } @@ -587,7 +593,7 @@ void __init setup_IO_APIC_irqs(void) entry.delivery_mode = dest_LowestPrio; entry.dest_mode = 1; /* logical delivery */ entry.mask = 0; /* enable IRQ */ - entry.dest.logical.logical_dest = APIC_ALL_CPUS; + entry.dest.logical.logical_dest = TARGET_CPUS; idx = find_irq_entry(apic,pin,mp_INT); if (idx == -1) { @@ -605,7 +611,7 @@ void __init setup_IO_APIC_irqs(void) if (irq_trigger(idx)) { entry.trigger = 1; entry.mask = 1; - entry.dest.logical.logical_dest = APIC_ALL_CPUS; + entry.dest.logical.logical_dest = TARGET_CPUS; } irq = pin_2_irq(idx, apic, pin); @@ -660,7 +666,7 @@ void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) */ entry.dest_mode = 1; /* logical delivery */ entry.mask = 0; /* unmask IRQ now */ - entry.dest.logical.logical_dest = APIC_ALL_CPUS; + entry.dest.logical.logical_dest = TARGET_CPUS; entry.delivery_mode = dest_LowestPrio; entry.polarity = 0; entry.trigger = 0; @@ -1167,7 +1173,7 @@ static void set_ioapic_affinity (unsigned int irq, unsigned long mask) mask = mask << 24; spin_lock_irqsave(&ioapic_lock, flags); - __DO_ACTION( target, 1, = mask, ) + __DO_ACTION(1, = mask, ) spin_unlock_irqrestore(&ioapic_lock, flags); } diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 79155a3262b..fb0fe277c51 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -1117,7 +1117,8 @@ static void register_irq_proc (unsigned int irq) struct proc_dir_entry *entry; char name [MAX_NAMELEN]; - if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type)) + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) return; memset(name, 0, MAX_NAMELEN); @@ -1158,10 +1159,7 @@ void init_irq_proc (void) /* * Create entries for all existing IRQs. */ - for (i = 0; i < NR_IRQS; i++) { - if (irq_desc[i].handler == &no_irq_type) - continue; + for (i = 0; i < NR_IRQS; i++) register_irq_proc(i); - } } diff --git a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c index 1c359b4f4f2..e3b5c2f75a6 100644 --- a/arch/i386/kernel/ldt.c +++ b/arch/i386/kernel/ldt.c @@ -93,6 +93,7 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) mm->segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); if (!mm->segments) goto out_unlock; + memset(mm->segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE); if (atomic_read(&mm->mm_users) > 1) printk(KERN_WARNING "LDT allocated for cloned task!\n"); diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 5df83a10a2f..9455d2e3a79 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -50,11 +50,6 @@ static unsigned int num_processors = 0; unsigned long phys_cpu_present_map = 0; /* - * IA s/w dev Vol 3, Section 7.4 - */ -#define APIC_DEFAULT_PHYS_BASE 0xfee00000 - -/* * Intel MP BIOS table parsing routines: */ @@ -65,10 +60,12 @@ unsigned long phys_cpu_present_map = 0; static int __init mpf_checksum(unsigned char *mp, int len) { - int sum=0; - while(len--) - sum+=*mp++; - return sum&0xFF; + int sum = 0; + + while (len--) + sum += *mp++; + + return sum & 0xFF; } /* diff --git a/arch/i386/kernel/pci-i386.c b/arch/i386/kernel/pci-i386.c index afb90ab125a..53349bd0a66 100644 --- a/arch/i386/kernel/pci-i386.c +++ b/arch/i386/kernel/pci-i386.c @@ -121,6 +121,19 @@ pcibios_update_resource(struct pci_dev *dev, struct resource *root, } } +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ void pcibios_align_resource(void *data, struct resource *res, unsigned long size) { @@ -129,17 +142,16 @@ pcibios_align_resource(void *data, struct resource *res, unsigned long size) if (res->flags & IORESOURCE_IO) { unsigned long start = res->start; - /* We need to avoid collisions with `mirrored' VGA ports - and other strange ISA hardware, so we always want the - addresses kilobyte aligned. */ if (size > 0x100) { printk(KERN_ERR "PCI: I/O Region %s/%d too large" " (%ld bytes)\n", dev->slot_name, dev->resource - res, size); } - start = (start + 1024 - 1) & ~(1024 - 1); - res->start = start; + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } } } diff --git a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c index d678801b69e..cd16c78a5ff 100644 --- a/arch/i386/kernel/pci-irq.c +++ b/arch/i386/kernel/pci-irq.c @@ -222,6 +222,7 @@ static struct irq_router pirq_routers[] = { { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82440MX_1, pirq_piix_get, pirq_piix_set }, { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set }, { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set }, @@ -345,7 +346,7 @@ int pcibios_lookup_irq(struct pci_dev *dev, int assign) if (!irq) { DBG(" ... failed\n"); - if (newirq && mask == (1 << newirq)) { + if (assign && newirq && mask == (1 << newirq)) { msg = "Guessed"; irq = newirq; } else diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c index d1f41ff0df1..404a49868cc 100644 --- a/arch/i386/kernel/pci-pc.c +++ b/arch/i386/kernel/pci-pc.c @@ -844,15 +844,15 @@ static void __init pci_fixup_i450gx(struct pci_dev *d) pcibios_last_bus = -1; } -static void __init pci_fixup_rcc(struct pci_dev *d) +static void __init pci_fixup_serverworks(struct pci_dev *d) { /* - * RCC host bridges -- Find and scan all secondary buses. + * ServerWorks host bridges -- Find and scan all secondary buses. * Register 0x44 contains first, 0x45 last bus number routed there. */ u8 busno; pci_read_config_byte(d, 0x44, &busno); - printk("PCI: RCC host bridge: secondary bus %02x\n", busno); + printk("PCI: ServerWorks host bridge: secondary bus %02x\n", busno); pci_scan_bus(busno, pci_root_ops, NULL); pcibios_last_bus = -1; } @@ -928,8 +928,9 @@ static void __init pci_fixup_latency(struct pci_dev *d) struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_HE, pci_fixup_rcc }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_LE, pci_fixup_rcc }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HE, pci_fixup_serverworks }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE, pci_fixup_serverworks }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CMIC_HE, pci_fixup_serverworks }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_6010, pci_fixup_compaq }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash }, diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index e3e043b2372..358d9f91764 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -406,6 +406,32 @@ void __init add_memory_region(unsigned long start, e820.nr_map++; } /* add_memory_region */ +#define E820_DEBUG 1 + +static void __init print_e820_map(void) +{ + int i; + + for (i = 0; i < e820.nr_map; i++) { + printk(" e820: %016Lx @ %016Lx ", + e820.map[i].size, e820.map[i].addr); + switch (e820.map[i].type) { + case E820_RAM: printk("(usable)\n"); + break; + case E820_RESERVED: + printk("(reserved)\n"); + break; + case E820_ACPI: + printk("(ACPI data)\n"); + break; + case E820_NVS: + printk("(ACPI NVS)\n"); + break; + default: printk("type %lu\n", e820.map[i].type); + break; + } + } +} /* * Do NOT EVER look at the BIOS memory size location. @@ -415,11 +441,6 @@ void __init add_memory_region(unsigned long start, void __init setup_memory_region(void) { -#define E820_DEBUG 1 -#ifdef E820_DEBUG - int i; -#endif - /* * If we're lucky and live on a modern system, the setup code * will have given us a memory map that we can use to properly @@ -439,27 +460,6 @@ void __init setup_memory_region(void) if (e820.nr_map > E820MAX) e820.nr_map = E820MAX; memcpy(e820.map, E820_MAP, e820.nr_map * sizeof e820.map[0]); -#ifdef E820_DEBUG - for (i=0; i < e820.nr_map; i++) { - printk("e820: %08x @ %08x ", (int)e820.map[i].size, - (int)e820.map[i].addr); - switch (e820.map[i].type) { - case E820_RAM: printk("(usable)\n"); - break; - case E820_RESERVED: - printk("(reserved)\n"); - break; - case E820_ACPI: - printk("(ACPI data)\n"); - break; - case E820_NVS: - printk("(ACPI NVS)\n"); - break; - default: printk("type %lu\n", e820.map[i].type); - break; - } - } -#endif } else { /* otherwise fake a memory map; one section from 0k->640k, @@ -472,6 +472,8 @@ void __init setup_memory_region(void) add_memory_region(0, LOWMEMSIZE(), E820_RAM); add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM); } + printk("BIOS-provided physical RAM map:\n"); + print_e820_map(); } /* setup_memory_region */ @@ -538,6 +540,10 @@ static inline void parse_mem_cmdline (char ** cmdline_p) } *to = '\0'; *cmdline_p = command_line; + if (usermem) { + printk("user-defined physical RAM map:\n"); + print_e820_map(); + } } void __init setup_arch(char **cmdline_p) diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index e25f50cfd20..7fb5ebc6114 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -790,7 +790,7 @@ cobalt_init(void) * On normal SMP PC this is used only with SMP, but we have to * use it and set it up here to start the Cobalt clock */ - set_fixmap(FIX_APIC_BASE, APIC_PHYS_BASE); + set_fixmap(FIX_APIC_BASE, APIC_DEFAULT_PHYS_BASE); printk("Local APIC ID %lx\n", apic_read(APIC_ID)); printk("Local APIC Version %lx\n", apic_read(APIC_LVR)); diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index c801285eb18..af72d158103 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -242,8 +242,18 @@ static inline void set_pte_phys (unsigned long vaddr, pte_t *pte; pgd = swapper_pg_dir + __pgd_offset(vaddr); + if (pgd_none(*pgd)) { + printk("PAE BUG #00!\n"); + return; + } pmd = pmd_offset(pgd, vaddr); + if (pmd_none(*pmd)) { + printk("PAE BUG #01!\n"); + return; + } pte = pte_offset(pmd, vaddr); + if (pte_val(*pte)) + pte_ERROR(*pte); pgprot_val(prot) = pgprot_val(PAGE_KERNEL) | pgprot_val(flags); set_pte(pte, mk_pte_phys(phys, prot)); @@ -271,53 +281,65 @@ static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t pmd_t *pmd; pte_t *pte; int i, j; + unsigned long vaddr; - i = __pgd_offset(start); - j = __pmd_offset(start); + vaddr = start; + i = __pgd_offset(vaddr); + j = __pmd_offset(vaddr); pgd = pgd_base + i; - for ( ; (i < PTRS_PER_PGD) && (start != end); pgd++, i++) { + for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) { #if CONFIG_X86_PAE if (pgd_none(*pgd)) { pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); set_pgd(pgd, __pgd(__pa(pmd) + 0x1)); - if (pmd != pmd_offset(pgd, start)) - BUG(); + if (pmd != pmd_offset(pgd, 0)) + printk("PAE BUG #02!\n"); } - pmd = pmd_offset(pgd, start); + pmd = pmd_offset(pgd, vaddr); #else pmd = (pmd_t *)pgd; #endif - for (; (j < PTRS_PER_PMD) && start; pmd++, j++) { + for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) { if (pmd_none(*pmd)) { pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte))); if (pte != pte_offset(pmd, 0)) BUG(); } - start += PMD_SIZE; + vaddr += PMD_SIZE; } j = 0; } } -static void __init pagetable_init(void) +static void __init pagetable_init (void) { + unsigned long vaddr, end; pgd_t *pgd, *pgd_base; + int i, j, k; pmd_t *pmd; pte_t *pte; - int i, j, k; - unsigned long vaddr, end; - end = (unsigned long)__va(max_low_pfn*PAGE_SIZE) - 1; + /* + * This can be zero as well - no problem, in that case we exit + * the loops anyway due to the PTRS_PER_* conditions. + */ + end = (unsigned long)__va(max_low_pfn*PAGE_SIZE); - i = __pgd_offset(PAGE_OFFSET); pgd_base = swapper_pg_dir; +#if CONFIG_X86_PAE + for (i = 0; i < PTRS_PER_PGD; i++) { + pgd = pgd_base + i; + __pgd_clear(pgd); + } +#endif + i = __pgd_offset(PAGE_OFFSET); pgd = pgd_base + i; for (; i < PTRS_PER_PGD; pgd++, i++) { vaddr = i*PGDIR_SIZE; - if (vaddr >= end) + if (end && (vaddr >= end)) break; #if CONFIG_X86_PAE pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); @@ -329,7 +351,7 @@ static void __init pagetable_init(void) BUG(); for (j = 0; j < PTRS_PER_PMD; pmd++, j++) { vaddr = i*PGDIR_SIZE + j*PMD_SIZE; - if (vaddr >= end) + if (end && (vaddr >= end)) break; if (cpu_has_pse) { unsigned long __pe; @@ -354,7 +376,7 @@ static void __init pagetable_init(void) for (k = 0; k < PTRS_PER_PTE; pte++, k++) { vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE; - if (vaddr >= end) + if (end && (vaddr >= end)) break; *pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL); } diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 2077abe939b..dae2f2265ef 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -2735,8 +2735,6 @@ copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) extern asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, unsigned long new_flags, void *data); -extern long do_sys_mount(char * dev_name, char * dir_name, char * type, - unsigned long new_flags, void *data); #define SMBFS_NAME "smbfs" #define NCPFS_NAME "ncpfs" @@ -2784,7 +2782,7 @@ sys32_mount(char *dev_name, char *dir_name, char *type, do_smb_super_data_conv((void *)data_page); else panic("The problem is here..."); - err = do_sys_mount((char *)dev_page, (char *)dir_page, + err = do_mount((char *)dev_page, (char *)dir_page, (char *)type_page, new_flags, (void *)data_page); if(data_page) diff --git a/arch/mips/mm/umap.c b/arch/mips/mm/umap.c index 2024e9dcea2..003866ac439 100644 --- a/arch/mips/mm/umap.c +++ b/arch/mips/mm/umap.c @@ -108,7 +108,8 @@ remove_mapping (struct task_struct *task, unsigned long start, unsigned long end void *vmalloc_uncached (unsigned long size) { - return vmalloc_prot (size, PAGE_KERNEL_UNCACHED); + return __vmalloc (size, GFP_KERNEL | __GFP_HIGHMEM, + PAGE_KERNEL_UNCACHED); } static inline void free_pte(pte_t page) diff --git a/arch/mips64/defconfig b/arch/mips64/defconfig index 042baa4b5ce..b5e6bc0e887 100644 --- a/arch/mips64/defconfig +++ b/arch/mips64/defconfig @@ -77,7 +77,6 @@ CONFIG_PCI_NAMES=y # CONFIG_BLK_DEV_MD is not set # CONFIG_MD_LINEAR is not set # CONFIG_MD_RAID0 is not set -# CONFIG_RAID15_DANGEROUS is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set diff --git a/arch/mips64/defconfig-ip22 b/arch/mips64/defconfig-ip22 index e9a442e43a1..85e7ace0e65 100644 --- a/arch/mips64/defconfig-ip22 +++ b/arch/mips64/defconfig-ip22 @@ -67,7 +67,6 @@ CONFIG_BINFMT_ELF=y # CONFIG_BLK_DEV_MD is not set # CONFIG_MD_LINEAR is not set # CONFIG_MD_RAID0 is not set -# CONFIG_RAID15_DANGEROUS is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set diff --git a/arch/mips64/defconfig-ip27 b/arch/mips64/defconfig-ip27 index 042baa4b5ce..b5e6bc0e887 100644 --- a/arch/mips64/defconfig-ip27 +++ b/arch/mips64/defconfig-ip27 @@ -77,7 +77,6 @@ CONFIG_PCI_NAMES=y # CONFIG_BLK_DEV_MD is not set # CONFIG_MD_LINEAR is not set # CONFIG_MD_RAID0 is not set -# CONFIG_RAID15_DANGEROUS is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set diff --git a/arch/ppc/8260_io/enet.c b/arch/ppc/8260_io/enet.c index 82ed8f9ec01..4a7c20df35a 100644 --- a/arch/ppc/8260_io/enet.c +++ b/arch/ppc/8260_io/enet.c @@ -689,8 +689,8 @@ int __init scc_enet_init(void) cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; cep->cur_rx = cep->rx_bd_base; - ep->sen_genscc.scc_rfcr = SCC_EB; - ep->sen_genscc.scc_tfcr = SCC_EB; + ep->sen_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; + ep->sen_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; /* Set maximum bytes per receive buffer. * This appears to be an Ethernet frame size, not the buffer diff --git a/arch/ppc/8260_io/uart.c b/arch/ppc/8260_io/uart.c index 9e93bcdd542..c039a452c0d 100644 --- a/arch/ppc/8260_io/uart.c +++ b/arch/ppc/8260_io/uart.c @@ -2536,8 +2536,8 @@ int __init rs_8xx_init(void) /* Set up the uart parameters in the * parameter ram. */ - up->smc_rfcr = SMC_EB; - up->smc_tfcr = SMC_EB; + up->smc_rfcr = CPMFCR_GBL | CPMFCR_EB; + up->smc_tfcr = CPMFCR_GBL | CPMFCR_EB; /* Set this to 1 for now, so we get single * character interrupts. Using idle charater @@ -2579,8 +2579,8 @@ int __init rs_8xx_init(void) /* Set up the uart parameters in the * parameter ram. */ - sup->scc_genscc.scc_rfcr = SMC_EB; - sup->scc_genscc.scc_tfcr = SMC_EB; + sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; + sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; /* Set this to 1 for now, so we get single * character interrupts. Using idle charater @@ -2741,8 +2741,8 @@ static int __init serial_console_setup(struct console *co, char *options) */ up->smc_rbase = dp_addr; /* Base of receive buffer desc. */ up->smc_tbase = dp_addr+sizeof(cbd_t); /* Base of xmt buffer desc. */ - up->smc_rfcr = SMC_EB; - up->smc_tfcr = SMC_EB; + up->smc_rfcr = CPMFCR_GBL | CPMFCR_EB; + up->smc_tfcr = CPMFCR_GBL | CPMFCR_EB; /* Set this to 1 for now, so we get single character interrupts. */ diff --git a/arch/ppc/configs/common_defconfig b/arch/ppc/configs/common_defconfig index caf15d27d93..051e964023c 100644 --- a/arch/ppc/configs/common_defconfig +++ b/arch/ppc/configs/common_defconfig @@ -15,10 +15,11 @@ CONFIG_PPC=y CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_PPC64 is not set -# CONFIG_82xx is not set +# CONFIG_8260 is not set # CONFIG_8xx is not set CONFIG_ALL_PPC=y # CONFIG_GEMINI is not set +# CONFIG_EST8260 is not set # CONFIG_APUS is not set # CONFIG_SMP is not set CONFIG_ALTIVEC=y @@ -159,10 +160,10 @@ CONFIG_BLK_DEV_IDESCSI=y # CONFIG_BLK_DEV_RZ1000 is not set CONFIG_BLK_DEV_IDEPCI=y # CONFIG_IDEPCI_SHARE_IRQ is not set -# CONFIG_BLK_DEV_IDEDMA_PCI is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_OFFBOARD is not set -# CONFIG_IDEDMA_PCI_AUTO is not set -# CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDEDMA_PCI_AUTO=y +CONFIG_BLK_DEV_IDEDMA=y CONFIG_IDEDMA_PCI_EXPERIMENTAL=y # CONFIG_IDEDMA_PCI_WIP is not set # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set @@ -377,6 +378,11 @@ CONFIG_PPP=y # CONFIG_HAMRADIO is not set # +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# # ISDN subsystem # # CONFIG_ISDN is not set @@ -607,6 +613,7 @@ CONFIG_SOUND=y CONFIG_DMASOUND_AWACS=y CONFIG_DMASOUND=y # CONFIG_SOUND_CMPCI is not set +# CONFIG_SOUND_EMU10K1 is not set # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set # CONFIG_SOUND_ESSSOLO1 is not set @@ -688,3 +695,4 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 CONFIG_MAGIC_SYSRQ=y # CONFIG_KGDB is not set CONFIG_XMON=y + diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index c93118bae96..a14aa41ab74 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -15,10 +15,11 @@ CONFIG_PPC=y CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_PPC64 is not set -# CONFIG_82xx is not set +# CONFIG_8260 is not set # CONFIG_8xx is not set CONFIG_ALL_PPC=y # CONFIG_GEMINI is not set +# CONFIG_EST8260 is not set # CONFIG_APUS is not set # CONFIG_SMP is not set CONFIG_ALTIVEC=y @@ -159,10 +160,10 @@ CONFIG_BLK_DEV_IDESCSI=y # CONFIG_BLK_DEV_RZ1000 is not set CONFIG_BLK_DEV_IDEPCI=y # CONFIG_IDEPCI_SHARE_IRQ is not set -# CONFIG_BLK_DEV_IDEDMA_PCI is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_OFFBOARD is not set -# CONFIG_IDEDMA_PCI_AUTO is not set -# CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDEDMA_PCI_AUTO=y +CONFIG_BLK_DEV_IDEDMA=y CONFIG_IDEDMA_PCI_EXPERIMENTAL=y # CONFIG_IDEDMA_PCI_WIP is not set # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set @@ -377,6 +378,11 @@ CONFIG_PPP=y # CONFIG_HAMRADIO is not set # +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# # ISDN subsystem # # CONFIG_ISDN is not set @@ -608,6 +614,7 @@ CONFIG_SOUND=y CONFIG_DMASOUND_AWACS=y CONFIG_DMASOUND=y # CONFIG_SOUND_CMPCI is not set +# CONFIG_SOUND_EMU10K1 is not set # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set # CONFIG_SOUND_ESSSOLO1 is not set diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index a2fbe5f14f7..e609906cb7b 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "pci.h" @@ -31,7 +32,7 @@ volatile struct Hydra *Hydra = NULL; * limit the bus number to 3 bits */ -int gg2_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, +int __chrp gg2_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { if (bus > 7) { @@ -42,7 +43,7 @@ int gg2_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int gg2_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, +int __chrp gg2_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { if (bus > 7) { @@ -54,7 +55,7 @@ int gg2_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, } -int gg2_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, +int __chrp gg2_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { if (bus > 7) { @@ -65,7 +66,7 @@ int gg2_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int gg2_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, +int __chrp gg2_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { if (bus > 7) @@ -74,7 +75,7 @@ int gg2_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int gg2_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, +int __chrp gg2_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { if (bus > 7) @@ -83,7 +84,7 @@ int gg2_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int gg2_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, +int __chrp gg2_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { if (bus > 7) @@ -98,7 +99,7 @@ int gg2_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, | (((o) & ~3) << 24)) unsigned int python_busnr = 0; -int python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, +int __chrp python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { if (bus > python_busnr) { @@ -110,7 +111,7 @@ int python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int python_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, +int __chrp python_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { if (bus > python_busnr) { @@ -123,7 +124,7 @@ int python_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, } -int python_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, +int __chrp python_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { if (bus > python_busnr) { @@ -135,7 +136,7 @@ int python_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int python_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, +int __chrp python_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { if (bus > python_busnr) @@ -145,7 +146,7 @@ int python_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int python_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, +int __chrp python_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { if (bus > python_busnr) @@ -156,7 +157,7 @@ int python_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int python_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, +int __chrp python_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { if (bus > python_busnr) @@ -167,7 +168,7 @@ int python_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, } -int rtas_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, +int __chrp rtas_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16); @@ -176,7 +177,7 @@ int rtas_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int rtas_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, +int __chrp rtas_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16); @@ -186,7 +187,7 @@ int rtas_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, } -int rtas_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, +int __chrp rtas_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16); @@ -195,7 +196,7 @@ int rtas_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int rtas_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, +int __chrp rtas_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16); @@ -204,7 +205,7 @@ int rtas_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int rtas_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, +int __chrp rtas_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16); @@ -213,7 +214,7 @@ int rtas_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -int rtas_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, +int __chrp rtas_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16); diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 3b541c26f3d..d8c22e1a613 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "time.h" #include "local_irq.h" @@ -112,7 +113,7 @@ static const char *gg2_cachemodes[4] = { "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" }; -int +int __chrp chrp_get_cpuinfo(char *buffer) { int i, len, sdramen; @@ -306,7 +307,7 @@ chrp_setup_arch(void) } } -void +void __chrp chrp_event_scan(void) { unsigned char log[1024]; @@ -317,7 +318,7 @@ chrp_event_scan(void) ppc_md.heartbeat_count = ppc_md.heartbeat_reset; } -void +void __chrp chrp_restart(char *cmd) { printk("RTAS system-reboot returned %d\n", @@ -325,7 +326,7 @@ chrp_restart(char *cmd) for (;;); } -void +void __chrp chrp_power_off(void) { /* allow power on only with power button press */ @@ -334,13 +335,13 @@ chrp_power_off(void) for (;;); } -void +void __chrp chrp_halt(void) { chrp_power_off(); } -u_int +u_int __chrp chrp_irq_cannonicalize(u_int irq) { if (irq == 2) @@ -353,7 +354,7 @@ chrp_irq_cannonicalize(u_int irq) } } -int chrp_get_irq( struct pt_regs *regs ) +int __chrp chrp_get_irq( struct pt_regs *regs ) { int irq; @@ -383,7 +384,7 @@ int chrp_get_irq( struct pt_regs *regs ) return irq; } -void chrp_post_irq(struct pt_regs* regs, int irq) +void __chrp chrp_post_irq(struct pt_regs* regs, int irq) { /* * If it's an i8259 irq then we've already done the @@ -445,7 +446,7 @@ int chrp_ide_ports_known = 0; ide_ioreg_t chrp_ide_regbase[MAX_HWIFS]; ide_ioreg_t chrp_idedma_regbase; -void +void __chrp chrp_ide_probe(void) { struct pci_dev *pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, NULL); @@ -460,19 +461,19 @@ chrp_ide_probe(void) } } -void +void __chrp chrp_ide_insw(ide_ioreg_t port, void *buf, int ns) { ide_insw(port+_IO_BASE, buf, ns); } -void +void __chrp chrp_ide_outsw(ide_ioreg_t port, void *buf, int ns) { ide_outsw(port+_IO_BASE, buf, ns); } -int +int __chrp chrp_ide_default_irq(ide_ioreg_t base) { if (chrp_ide_ports_known == 0) @@ -480,7 +481,7 @@ chrp_ide_default_irq(ide_ioreg_t base) return chrp_ide_irq; } -ide_ioreg_t +ide_ioreg_t __chrp chrp_ide_default_io_base(int index) { if (chrp_ide_ports_known == 0) @@ -488,13 +489,13 @@ chrp_ide_default_io_base(int index) return chrp_ide_regbase[index]; } -int +int __chrp chrp_ide_check_region(ide_ioreg_t from, unsigned int extent) { return check_region(from, extent); } -void +void __chrp chrp_ide_request_region(ide_ioreg_t from, unsigned int extent, const char *name) @@ -502,20 +503,20 @@ chrp_ide_request_region(ide_ioreg_t from, request_region(from, extent, name); } -void +void __chrp chrp_ide_release_region(ide_ioreg_t from, unsigned int extent) { release_region(from, extent); } -void +void __chrp chrp_ide_fix_driveid(struct hd_driveid *id) { ppc_generic_ide_fix_driveid(id); } -void +void __chrp chrp_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) { ide_ioreg_t reg = data_port; @@ -629,7 +630,7 @@ void __init if ( ppc_md.progress ) ppc_md.progress("Linux/PPC "UTS_RELEASE"\n", 0x0); } -void +void __chrp chrp_progress(char *s, unsigned short hex) { extern unsigned int rtas_data; diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index c692b54d08b..6d275e517b3 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "time.h" static int nvram_as1 = NVRAM_AS1; @@ -45,7 +46,7 @@ void __init chrp_time_init(void) nvram_data = base + 1; } -int chrp_cmos_clock_read(int addr) +int __chrp chrp_cmos_clock_read(int addr) { if (nvram_as1 != 0) outb(addr>>8, nvram_as1); @@ -53,7 +54,7 @@ int chrp_cmos_clock_read(int addr) return (inb(nvram_data)); } -void chrp_cmos_clock_write(unsigned long val, int addr) +void __chrp chrp_cmos_clock_write(unsigned long val, int addr) { if (nvram_as1 != 0) outb(addr>>8, nvram_as1); @@ -65,7 +66,7 @@ void chrp_cmos_clock_write(unsigned long val, int addr) /* * Set the hardware clock. -- Cort */ -int chrp_set_rtc_time(unsigned long nowtime) +int __chrp chrp_set_rtc_time(unsigned long nowtime) { unsigned char save_control, save_freq_select; struct rtc_time tm; @@ -111,7 +112,7 @@ int chrp_set_rtc_time(unsigned long nowtime) return 0; } -unsigned long chrp_get_rtc_time(void) +unsigned long __chrp chrp_get_rtc_time(void) { unsigned int year, mon, day, hour, min, sec; int i; diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index cc647a58b61..ad467894f7a 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -291,25 +291,9 @@ ret_from_intercept: * -- Cort */ cmpi 0,r3,0 - bne ret_from_except - /* - * If we're returning from user mode we do things differently - * -- Cort - */ - lwz r3,_MSR(r1) - andi. r3,r3,MSR_PR - beq+ 10f - b 8f - + beq restore .globl ret_from_except ret_from_except: -0: /* disable interrupts */ - lis r30,int_control@h - ori r30,r30,int_control@l - lwz r30,0(r30) - mtlr r30 - blrl - lwz r5,_MSR(r1) andi. r5,r5,MSR_EE beq 2f @@ -341,65 +325,58 @@ lost_irq_ret: bl do_softirq .globl do_bottom_half_ret do_bottom_half_ret: -2: /* disable interrupts */ - lis r30,int_control@h - ori r30,r30,int_control@l - lwz r30,0(r30) - mtlr r30 - blrl - lwz r3,_MSR(r1) /* Returning to user mode? */ +2: lwz r3,_MSR(r1) /* Returning to user mode? */ andi. r3,r3,MSR_PR - beq+ 10f /* if so, check need_resched and signals */ + beq+ restore /* if so, check need_resched and signals */ + .globl ret_to_user_hook +ret_to_user_hook: + nop lwz r3,NEED_RESCHED(r2) cmpi 0,r3,0 /* check need_resched flag */ beq+ 7f bl schedule - b 0b 7: lwz r5,SIGPENDING(r2) /* Check for pending unblocked signals */ cmpwi 0,r5,0 - beq+ 8f + beq+ restore li r3,0 addi r4,r1,STACK_FRAME_OVERHEAD bl do_signal .globl do_signal_ret do_signal_ret: - b 0b -8: /* - * We need to hard disable here even if RTL is active since - * being interrupted after here trashes the SPRG2 - * -- Cort - */ - mfmsr r0 /* Get current interrupt state */ - rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ - mtmsr r0 /* Update machine state */ - - addi r4,r1,INT_FRAME_SIZE /* size of frame */ - stw r4,THREAD+KSP(r2) /* save kernel stack pointer */ - tophys(r3,r1) - mtspr SPRG2,r3 /* phys exception stack pointer */ - b 11f -10: /* make sure we hard disable here, even if rtl is active -- Cort */ - mfmsr r0 /* Get current interrupt state */ - rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ - sync /* Some chip revs have problems here... */ - mtmsr r0 /* Update machine state */ -11: - lwz r2,_CTR(r1) +restore: + lwz r3,_CTR(r1) lwz r0,_LINK(r1) - mtctr r2 + mtctr r3 mtlr r0 - lwz r2,_XER(r1) - lwz r0,_CCR(r1) - mtspr XER,r2 - mtcrf 0xFF,r0 + lwz r3,_XER(r1) + mtspr XER,r3 REST_10GPRS(3, r1) REST_10GPRS(13, r1) REST_8GPRS(23, r1) REST_GPR(31, r1) - lwz r2,_NIP(r1) /* Restore environment */ + + /* make sure we hard disable here, even if rtl is active, to protect + * SRR[01] and SPRG2 -- Cort + */ + mfmsr r0 /* Get current interrupt state */ + rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + sync /* Some chip revs have problems here... */ + mtmsr r0 /* Update machine state */ + + /* if returning to user mode, set new sprg2 and save kernel SP */ lwz r0,_MSR(r1) - mtspr SRR0,r2 mtspr SRR1,r0 + andi. r0,r0,MSR_PR + beq+ 1f + addi r0,r1,INT_FRAME_SIZE /* size of frame */ + stw r0,THREAD+KSP(r2) /* save kernel stack pointer */ + tophys(r2,r1) + mtspr SPRG2,r2 /* phys exception stack pointer */ +1: + lwz r2,_CCR(r1) + mtcrf 0xFF,r2 + lwz r2,_NIP(r1) + mtspr SRR0,r2 lwz r0,GPR0(r1) lwz r2,GPR2(r1) lwz r1,GPR1(r1) diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 6700806bd15..f88c5383d26 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -1459,7 +1459,8 @@ start_here: /* * Set up the segment registers for a new context. */ -_GLOBAL(set_context) + .globl set_context +set_context: rlwinm r3,r3,4,8,27 /* VSID = context << 4 */ addis r3,r3,0x6000 /* Set Ks, Ku bits */ li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 9b7d2be3148..6d7f2aff789 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -58,6 +58,7 @@ long long __ashrdi3(long long, int); long long __ashldi3(long long, int); long long __lshrdi3(long long, int); int abs(int); +extern unsigned long ret_to_user_hook; EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(do_signal); @@ -284,3 +285,5 @@ EXPORT_SYMBOL(debugger_sstep); EXPORT_SYMBOL(debugger_iabr_match); EXPORT_SYMBOL(debugger_dabr_match); EXPORT_SYMBOL(debugger_fault_handler); + +EXPORT_SYMBOL(ret_to_user_hook); diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c dissimilarity index 70% index fe0bfcea2a0..3ccc8f51869 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -1,522 +1,315 @@ -/* - * linux/arch/ppc/kernel/ptrace.c - * - * PowerPC version - * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) - * - * Derived from "arch/m68k/kernel/ptrace.c" - * Copyright (C) 1994 by Hamish Macdonald - * Taken from linux/kernel/ptrace.c and modified for M680x0. - * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds - * - * Modified by Cort Dougan (cort@cs.nmt.edu) - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file README.legal in the main directory of - * this archive for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * Set of msr bits that gdb can change on behalf of a process. - */ -#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) - -/* - * does not yet catch signals sent when the child dies. - * in exit.c or in signal.c. - */ - -/* - * Get contents of register REGNO in task TASK. - */ -static inline long get_reg(struct task_struct *task, int regno) -{ - if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) - return ((unsigned long *)task->thread.regs)[regno]; - return (0); -} - -/* - * Write contents of register REGNO in task TASK. - */ -static inline int put_reg(struct task_struct *task, int regno, - unsigned long data) -{ - if (regno <= PT_MQ) { - if (regno == PT_MSR) - data = (data & MSR_DEBUGCHANGE) - | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); - ((unsigned long *)task->thread.regs)[regno] = data; - return 0; - } - return -1; -} - -static inline void -set_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->thread.regs; - regs->msr |= MSR_SE; -} - -static inline void -clear_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->thread.regs; - regs->msr &= ~MSR_SE; -} - -#if 0 -/* - * This routine gets a long from any process space by following the page - * tables. NOTE! You should check that the long isn't on a page boundary, - * and that it is in the task area before calling this: this routine does - * no checking. - * - */ -static unsigned long get_long(struct task_struct * tsk, - struct vm_area_struct * vma, unsigned long addr) -{ - pgd_t * pgdir; - pmd_t * pgmiddle; - pte_t * pgtable; - unsigned long page; - -repeat: - pgdir = pgd_offset(vma->vm_mm, addr); - if (pgd_none(*pgdir)) { - handle_mm_fault(tsk->mm, vma, addr, 0); - goto repeat; - } - if (pgd_bad(*pgdir)) { - printk("ptrace[1]: bad page directory %lx\n", pgd_val(*pgdir)); - pgd_clear(pgdir); - return 0; - } - pgmiddle = pmd_offset(pgdir,addr); - if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk->mm, vma, addr, 0); - goto repeat; - } - if (pmd_bad(*pgmiddle)) { - printk("ptrace[3]: bad pmd %lx\n", pmd_val(*pgmiddle)); - pmd_clear(pgmiddle); - return 0; - } - pgtable = pte_offset(pgmiddle, addr); - if (!pte_present(*pgtable)) { - handle_mm_fault(tsk->mm, vma, addr, 0); - goto repeat; - } - page = pte_page(*pgtable); -/* this is a hack for non-kernel-mapped video buffers and similar */ - if (MAP_NR(page) >= max_mapnr) - return 0; - page += addr & ~PAGE_MASK; - return *(unsigned long *) page; -} - -/* - * This routine puts a long into any process space by following the page - * tables. NOTE! You should check that the long isn't on a page boundary, - * and that it is in the task area before calling this: this routine does - * no checking. - * - * Now keeps R/W state of page so that a text page stays readonly - * even if a debugger scribbles breakpoints into it. -M.U- - */ -static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, - unsigned long addr, unsigned long data) -{ - pgd_t *pgdir; - pmd_t *pgmiddle; - pte_t *pgtable; - unsigned long page; - -repeat: - pgdir = pgd_offset(vma->vm_mm, addr); - if (!pgd_present(*pgdir)) { - handle_mm_fault(tsk->mm, vma, addr, 1); - goto repeat; - } - if (pgd_bad(*pgdir)) { - printk("ptrace[2]: bad page directory %lx\n", pgd_val(*pgdir)); - pgd_clear(pgdir); - return; - } - pgmiddle = pmd_offset(pgdir,addr); - if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk->mm, vma, addr, 1); - goto repeat; - } - if (pmd_bad(*pgmiddle)) { - printk("ptrace[4]: bad pmd %lx\n", pmd_val(*pgmiddle)); - pmd_clear(pgmiddle); - return; - } - pgtable = pte_offset(pgmiddle, addr); - if (!pte_present(*pgtable)) { - handle_mm_fault(tsk->mm, vma, addr, 1); - goto repeat; - } - page = pte_page(*pgtable); - if (!pte_write(*pgtable)) { - handle_mm_fault(tsk->mm, vma, addr, 1); - goto repeat; - } -/* this is a hack for non-kernel-mapped video buffers and similar */ - if (MAP_NR(page) < max_mapnr) { - unsigned long phys_addr = page + (addr & ~PAGE_MASK); - *(unsigned long *) phys_addr = data; - flush_icache_range(phys_addr, phys_addr+4); - } -/* we're bypassing pagetables, so we have to set the dirty bit ourselves */ -/* this should also re-instate whatever read-only mode there was before */ - set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); - flush_tlb_all(); -} - -/* - * This routine checks the page boundaries, and that the offset is - * within the task area. It then calls get_long() to read a long. - */ -static int read_long(struct task_struct * tsk, unsigned long addr, - unsigned long * result) -{ - struct vm_area_struct * vma = find_extend_vma(tsk->mm, addr); - - if (!vma) - return -EIO; - if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { - unsigned long low,high; - struct vm_area_struct * vma_low = vma; - - if (addr + sizeof(long) >= vma->vm_end) { - vma_low = vma->vm_next; - if (!vma_low || vma_low->vm_start != vma->vm_end) - return -EIO; - } - high = get_long(tsk, vma,addr & ~(sizeof(long)-1)); - low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1)); - switch (addr & (sizeof(long)-1)) { - case 3: - low >>= 8; - low |= high << 24; - break; - case 2: - low >>= 16; - low |= high << 16; - break; - case 1: - low >>= 24; - low |= high << 8; - break; - } - *result = low; - } else - *result = get_long(tsk, vma,addr); - return 0; -} - -/* - * This routine checks the page boundaries, and that the offset is - * within the task area. It then calls put_long() to write a long. - */ -static int write_long(struct task_struct * tsk, unsigned long addr, - unsigned long data) -{ - struct vm_area_struct * vma = find_extend_vma(tsk->mm, addr); - - if (!vma) - return -EIO; - if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { - unsigned long low,high; - struct vm_area_struct * vma_low = vma; - - if (addr + sizeof(long) >= vma->vm_end) { - vma_low = vma->vm_next; - if (!vma_low || vma_low->vm_start != vma->vm_end) - return -EIO; - } - high = get_long(tsk, vma,addr & ~(sizeof(long)-1)); - low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1)); - switch (addr & (sizeof(long)-1)) { - case 0: /* shouldn't happen, but safety first */ - high = data; - break; - case 3: - low &= 0x000000ff; - low |= data << 8; - high &= ~0xff; - high |= data >> 24; - break; - case 2: - low &= 0x0000ffff; - low |= data << 16; - high &= ~0xffff; - high |= data >> 16; - break; - case 1: - low &= 0x00ffffff; - low |= data << 24; - high &= ~0xffffff; - high |= data >> 8; - break; - } - put_long(tsk, vma,addr & ~(sizeof(long)-1),high); - put_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1),low); - } else - put_long(tsk, vma,addr,data); - return 0; -} -#endif - -asmlinkage int sys_ptrace(long request, long pid, long addr, long data) -{ - struct task_struct *child; - int ret = -EPERM; - unsigned long flags; - - lock_kernel(); - if (request == PTRACE_TRACEME) { - /* are we already being traced? */ - if (current->flags & PF_PTRACED) - goto out; - /* set the ptrace bit in the process flags. */ - current->flags |= PF_PTRACED; - ret = 0; - goto out; - } - if (pid == 1) /* you may not mess with init */ - goto out; - ret = -ESRCH; - read_lock(&tasklist_lock); - child = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!!! */ - if ( !child ) - goto out; - ret = -EPERM; - if (request == PTRACE_ATTACH) { - if (child == current) - goto out; - if ((!child->dumpable || - (current->uid != child->euid) || - (current->uid != child->uid) || - (current->uid != child->suid) || - (current->gid != child->egid) || - (current->gid != child->gid) || - (current->gid != child->sgid) || - (!cap_issubset(child->cap_permitted, current->cap_permitted))) - && !capable(CAP_SYS_PTRACE)) - goto out; - /* the same process cannot be attached many times */ - if (child->flags & PF_PTRACED) - goto out; - child->flags |= PF_PTRACED; - - write_lock_irqsave(&tasklist_lock, flags); - if (child->p_pptr != current) { - REMOVE_LINKS(child); - child->p_pptr = current; - SET_LINKS(child); - } - write_unlock_irqrestore(&tasklist_lock, flags); - - send_sig(SIGSTOP, child, 1); - ret = 0; - goto out; - } - ret = -ESRCH; - if (!(child->flags & PF_PTRACED)) - goto out; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out; - } - if (child->p_pptr != current) - goto out; - - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - goto out; - ret = put_user(tmp,(unsigned long *) data); - goto out; - } - /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: { - unsigned long tmp; - - if ((addr & 3) || addr < 0 || addr > (PT_FPSCR << 2)) { - ret = -EIO; - goto out; - } - - ret = verify_area(VERIFY_WRITE, (void *) data, - sizeof(long)); - if (ret) - goto out; - tmp = 0; /* Default return condition */ - addr = addr >> 2; /* temporary hack. */ - if (addr < PT_FPR0) { - tmp = get_reg(child, addr); - } - else if (addr >= PT_FPR0 && addr <= PT_FPSCR) { - if (child->thread.regs->msr & MSR_FP) - giveup_fpu(child); - tmp = ((long *)child->thread.fpr)[addr - PT_FPR0]; - } - else - ret = -EIO; - if (!ret) - put_user(tmp,(unsigned long *) data); - goto out; - } - - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - goto out; - ret = -EIO; - goto out; - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret = -EIO; - if ((addr & 3) || addr < 0 || addr >= ((PT_FPR0 + 64) << 2)) - goto out; - - addr = addr >> 2; /* temporary hack. */ - - if (addr == PT_ORIG_R3) - goto out; - if (addr < PT_FPR0) { - if (put_reg(child, addr, data)) - goto out; - ret = 0; - goto out; - } - if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { - if (child->thread.regs->msr & MSR_FP) - giveup_fpu(child); - ((long *)child->thread.fpr)[addr - PT_FPR0] = data; - ret = 0; - goto out; - } - goto out; - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - ret = -EIO; - if ((unsigned long) data >= _NSIG) - goto out; - if (request == PTRACE_SYSCALL) - child->flags |= PF_TRACESYS; - else - child->flags &= ~PF_TRACESYS; - child->exit_code = data; - wake_up_process(child); - /* make sure the single step bit is not set. */ - clear_single_step(child); - ret = 0; - goto out; - } - -/* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: { - ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ - goto out; - wake_up_process(child); - child->exit_code = SIGKILL; - /* make sure the single step bit is not set. */ - clear_single_step(child); - goto out; - } - - case PTRACE_SINGLESTEP: { /* set the trap flag. */ - ret = -EIO; - if ((unsigned long) data >= _NSIG) - goto out; - child->flags &= ~PF_TRACESYS; - set_single_step(child); - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; - goto out; - } - - case PTRACE_DETACH: { /* detach a process that was attached. */ - ret = -EIO; - if ((unsigned long) data >= _NSIG) - goto out; - child->flags &= ~(PF_PTRACED|PF_TRACESYS); - wake_up_process(child); - child->exit_code = data; - write_lock_irqsave(&tasklist_lock, flags); - REMOVE_LINKS(child); - child->p_pptr = child->p_opptr; - SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); - /* make sure the single step bit is not set. */ - clear_single_step(child); - ret = 0; - goto out; - } - - default: - ret = -EIO; - goto out; - } -out: - unlock_kernel(); - return ret; -} - -asmlinkage void syscall_trace(void) -{ - if ((current->flags & (PF_PTRACED|PF_TRACESYS)) - != (PF_PTRACED|PF_TRACESYS)) - goto out; - current->exit_code = SIGTRAP; - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; - } -out: -} +/* + * linux/arch/ppc/kernel/ptrace.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + if (regno <= PT_MQ) { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) + | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr |= MSR_SE; +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr &= ~MSR_SE; +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->flags & PF_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->flags |= PF_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) + && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->flags & PF_PTRACED) + goto out_tsk; + child->flags |= PF_PTRACED; + + write_lock_irq(&tasklist_lock); + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irq(&tasklist_lock); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->flags & PF_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + /* XXX this will need fixing for 64-bit */ + case PTRACE_PEEKUSR: { + unsigned long index, tmp; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR) + break; + + if (addr < PT_FPR0) { + tmp = get_reg(child, (int) index); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; + } + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + /* write the word at location addr in the USER area */ + /* XXX this will need fixing for 64-bit */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR) + break; + + if (addr == PT_ORIG_R3) + break; + if (addr < PT_FPR0) { + ret = put_reg(child, addr, data); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->flags |= PF_TRACESYS; + else + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->flags &= ~PF_TRACESYS; + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->flags &= ~(PF_PTRACED|PF_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void syscall_trace(void) +{ + if ((current->flags & (PF_PTRACED|PF_TRACESYS)) + != (PF_PTRACED|PF_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 49a8da13937..67895b87dca 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -776,8 +776,11 @@ void ppc_generic_ide_fix_driveid(struct hd_driveid *id) id->word127 = __le16_to_cpu(id->word127); id->dlf = __le16_to_cpu(id->dlf); id->csfo = __le16_to_cpu(id->csfo); - for (i = 0; i < 30; i++) - id->words130_159[i] = __le16_to_cpu(id->words130_159[i]); + for (i = 0; i < 26; i++) + id->words130_155[i] = __le16_to_cpu(id->words130_155[i]); + id->word156 = __le16_to_cpu(id->word156); + for (i = 0; i < 4; i++) + id->words157_159[i] = __le16_to_cpu(id->words157_159[i]); for (i = 0; i < 96; i++) id->words160_255[i] = __le16_to_cpu(id->words160_255[i]); } diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index b821249d960..3877d11dce2 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -41,10 +41,7 @@ #include #include #include -/* Fixme - Why is this here? - Corey */ -#ifdef CONFIG_8xx #include -#endif #include #include "time.h" @@ -62,6 +59,7 @@ time_t last_rtc_update = 0; unsigned decrementer_count; /* count value for 1e6/HZ microseconds */ unsigned count_period_num; /* 1 decrementer count equals */ unsigned count_period_den; /* count_period_num / count_period_den us */ +unsigned long last_tb; /* * timer_interrupt - gets called when the decrementer overflows, @@ -103,6 +101,8 @@ int timer_interrupt(struct pt_regs * regs) */ while ((d = get_dec()) == dval) ; + asm volatile("mftb %0" : "=r" (last_tb) ); + /* * Don't play catchup between the call to time_init() * and sti() in init/main.c. @@ -149,20 +149,21 @@ int timer_interrupt(struct pt_regs * regs) */ void do_gettimeofday(struct timeval *tv) { - unsigned long flags; + unsigned long flags, diff; save_flags(flags); cli(); *tv = xtime; /* XXX we don't seem to have the decrementers synced properly yet */ #ifndef CONFIG_SMP - tv->tv_usec += (decrementer_count - get_dec()) - * count_period_num / count_period_den; - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; - } + asm volatile("mftb %0" : "=r" (diff) ); + diff -= last_tb; + + tv->tv_usec += diff * count_period_num / count_period_den; + tv->tv_sec += tv->tv_usec / 1000000; + tv->tv_usec = tv->tv_usec % 1000000; #endif + restore_flags(flags); } @@ -334,6 +335,3 @@ void to_tm(int tim, struct rtc_time * tm) */ GregorianDay(tm); } - - - diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 28a5a5035e7..0701f7118e3 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -91,7 +91,7 @@ MachineCheckException(struct pt_regs *regs) { #if defined(CONFIG_8xx) && defined(CONFIG_PCI) /* the qspan pci read routines can cause machine checks -- Cort */ - bad_page_fault(regs,regs->dar); + bad_page_fault(regs, regs->dar); return; #endif #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) @@ -214,8 +214,6 @@ AlignmentException(struct pt_regs *regs) { int fixed; - if (regs->msr & MSR_FP) - giveup_fpu(current); fixed = fix_alignment(regs); if (fixed == 1) { regs->nip += 4; /* skip over emulated instruction */ @@ -223,7 +221,10 @@ AlignmentException(struct pt_regs *regs) } if (fixed == -EFAULT) { /* fixed == -EFAULT means the operand address was bad */ - bad_page_fault(regs, regs->dar); + if (user_mode(regs)) + force_sig(SIGSEGV, current); + else + bad_page_fault(regs, regs->dar); return; } _exception(SIGBUS, regs); diff --git a/arch/ppc/mbxboot/Makefile b/arch/ppc/mbxboot/Makefile index 9dd5d64f36c..53611aa582f 100644 --- a/arch/ppc/mbxboot/Makefile +++ b/arch/ppc/mbxboot/Makefile @@ -28,14 +28,14 @@ ISZ = 0 TFTPIMAGE=/tftpboot/zImage.embedded ifdef CONFIG_8xx -ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00180000 -OBJECTS := head.o misc.o ../coffboot/zlib.o m8xx_tty.o +ZLINKFLAGS = -T vmlinux.lds -Ttext 0x00180000 +OBJECTS := head.o misc.o ../coffboot/zlib.o m8xx_tty.o gzimage.o rdimage.o CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin -DCONFIG_8xx endif ifdef CONFIG_8260 -ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00400000 -OBJECTS := head_8260.o misc.o ../coffboot/zlib.o m8260_tty.o embed_config.o +ZLINKFLAGS = -T vmlinux.lds -Ttext 0x00400000 +OBJECTS := head_8260.o misc.o ../coffboot/zlib.o m8260_tty.o embed_config.o gzimage.o rdimage.o CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin -DCONFIG_8260 endif @@ -61,21 +61,32 @@ endif all: zImage zvmlinux.initrd: zvmlinux - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp1 $(OBJECTS) +# +# Build the boot loader images +# + $(OBJCOPY) $(OBJCOPY_ARGS) -R .gzimage gzimage.o $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp1 zvmlinux.initrd1 - $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd1 initrd` \ - -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd1 initrd` \ - -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd1 image` \ - -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd1 image` \ - -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + --add-section=.gzimage=../coffboot/vmlinux.gz \ + --set-section-flags=.gzimage=alloc,load,readonly,data \ + gzimage.o + $(OBJCOPY) $(OBJCOPY_ARGS) -R .rdimage rdimage.o $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp $@ + --add-section=.rdimage=ramdisk.image.gz \ + --set-section-flags=.rdimage=alloc,load,readonly,data \ + rdimage.o + $(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) +# +# Compute the sizes/offsets for the final image, and rebuild with these values. +# + $(CC) $(CFLAGS) \ + -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd .rdimage` \ + -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd .rdimage` \ + -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd .gzimage` \ + -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd .gzimage` \ + -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment $@ + $(OBJDUMP) -h $@ zImage: zvmlinux ln -sf zvmlinux zImage @@ -85,23 +96,27 @@ zImage.initrd: zvmlinux.initrd zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # -# build the boot loader image and then compute the offset into it -# for the kernel image +# Build the boot loader images # - $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.tmp $@ # -# then with the offset rebuild the bootloader so we know where the kernel is + $(OBJCOPY) $(OBJCOPY_ARGS) -R .gzimage gzimage.o + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=.gzimage=../coffboot/vmlinux.gz \ + --set-section-flags=.gzimage=alloc,load,readonly,data \ + gzimage.o + $(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) +# +# Compute the sizes/offsets for the final image, and rebuild with these values. # - $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ - -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux image` \ - -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux image` \ + $(CC) $(CFLAGS) \ + -DINITRD_OFFSET=0 \ + -DINITRD_SIZE=0 \ + -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux .gzimage` \ + -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux .gzimage` \ -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.tmp $@ - rm zvmlinux.tmp + $(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment $@ + $(OBJDUMP) -h $@ znetboot : zImage cp zImage $(TFTPIMAGE) diff --git a/arch/ppc/mbxboot/embed_config.c b/arch/ppc/mbxboot/embed_config.c index 1d76be70c5a..048f8bcfe52 100644 --- a/arch/ppc/mbxboot/embed_config.c +++ b/arch/ppc/mbxboot/embed_config.c @@ -236,12 +236,12 @@ embed_config(bd_t *bd) * here for those people that may load the kernel with * a JTAG/COP tool and not the rom monitor. */ - bd->bi_baudrate = 115200; - bd->bi_intfreq = 200; - bd->bi_busfreq = 66; - bd->bi_cpmfreq = 66; - bd->bi_brgfreq = 33; - bd->bi_memsize = 16 * 1024 * 1024; + bd->bi_baudrate = 19200; + bd->bi_intfreq = 165; + bd->bi_busfreq = 33; + bd->bi_cpmfreq = 132; + bd->bi_brgfreq = bd->bi_cpmfreq / 2; /* BRGCLK = (CPM*2/4) */ + bd->bi_memsize = 16 * 1024 * 1024; #endif cp = (u_char *)def_enet_addr; @@ -250,3 +250,4 @@ embed_config(bd_t *bd) } } #endif /* EST8260 */ + diff --git a/arch/ppc/mbxboot/gzimage.c b/arch/ppc/mbxboot/gzimage.c new file mode 100644 index 00000000000..11ce5be8ea4 --- /dev/null +++ b/arch/ppc/mbxboot/gzimage.c @@ -0,0 +1,8 @@ +/* + * gzimage.c + * + * Dummy file to allow a compressed zImage to be added + * into a linker section, accessed by the boot coode + */ + +char dummy_for_gzimage; diff --git a/arch/ppc/mbxboot/head_8260.S b/arch/ppc/mbxboot/head_8260.S index 79377a2ac66..0895ce02596 100644 --- a/arch/ppc/mbxboot/head_8260.S +++ b/arch/ppc/mbxboot/head_8260.S @@ -9,10 +9,17 @@ * $Id: head.S,v 1.33 1999/09/08 01:06:58 cort Exp $ * * Boot loader philosophy: + * * ROM loads us to some arbitrary location - * Move the boot code to the link address (8M) + * ROM loads these registers: + * + * R3 = Pointer to the board configuration data + * R5 = Pointer to Open Firmware data + * + * ROM jumps to start/start_ + * Move the boot code to the link address (4 MB) * Call decompress_kernel() - * Relocate the initrd, zimage and residual data to 8M + * Relocate the initrd, zimage and residual data to 4 MB * Decompress the kernel to 0 * Jump to the kernel entry * -- Cort diff --git a/arch/ppc/mbxboot/misc.c b/arch/ppc/mbxboot/misc.c index 683f5349101..d5a44df1ef9 100644 --- a/arch/ppc/mbxboot/misc.c +++ b/arch/ppc/mbxboot/misc.c @@ -25,6 +25,15 @@ #endif /* + * The following references are needed to cause the linker to pull in the + * gzimage.o and rdimage.o files. These object files are special, + * since they get placed into the .gzimage and .rdimage ELF sections + * of the zvmlinux and zvmlinux.initrd files. + */ +extern char dummy_for_gzimage; +extern char dummy_for_rdimage; + +/* * Please send me load/board info and such data for hardware not * listed here so I can keep track since things are getting tricky * with the different load addrs with different firmware. This will diff --git a/arch/ppc/mbxboot/rdimage.c b/arch/ppc/mbxboot/rdimage.c new file mode 100644 index 00000000000..e40fd1e2e94 --- /dev/null +++ b/arch/ppc/mbxboot/rdimage.c @@ -0,0 +1,8 @@ +/* + * rdimage.c + * + * Dummy file to allow a compressed initrd to be added + * into a linker section, accessed by the boot coode + */ + +char dummy_for_rdimage; diff --git a/arch/ppc/vmlinux.lds b/arch/ppc/mbxboot/vmlinux.lds similarity index 92% copy from arch/ppc/vmlinux.lds copy to arch/ppc/mbxboot/vmlinux.lds index 707c4ad2feb..2bf2c87b348 100644 --- a/arch/ppc/vmlinux.lds +++ b/arch/ppc/mbxboot/vmlinux.lds @@ -134,5 +134,19 @@ SECTIONS } _end = . ; PROVIDE (end = .); -} + /* + * For loader only: Put the zImage after everything else + */ + _gzstart = . ; + .gzimage : { *(.gzimage) } + _gzend = . ; + + /* + * For loader only: Put the initrd after zImage + */ + _rdstart = . ; + .rdimage : { *(.rdimage) } + _rdend = . ; + +} diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index 076ee56e57f..7a899252faf 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -62,10 +62,21 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, { struct vm_area_struct * vma; struct mm_struct *mm = current->mm; + siginfo_t info; + int code = SEGV_MAPERR; #if defined(CONFIG_4xx) int is_write = error_code & ESR_DST; #else int is_write = error_code & 0x02000000; + + /* + * Fortunately the bit assignments in SRR1 for an instruction + * fault and DSISR for a data fault are mostly the same for the + * bits we are interested in. But there are some bits which + * indicate errors in DSISR but can validly be set in SRR1. + */ + if (regs->trap == 0x400) + error_code &= 0x48200000; #endif /* CONFIG_4xx */ #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) @@ -82,16 +93,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, #endif /* !CONFIG_4xx */ #endif /* CONFIG_XMON || CONFIG_KGDB */ - if (in_interrupt()) { - static int complained; - if (complained < 20) { - ++complained; - printk("page fault in interrupt handler, addr=%lx\n", - address); - show_regs(regs); - } - } - if (current == NULL || mm == NULL) { + if (in_interrupt() || mm == NULL) { bad_page_fault(regs, address); return; } @@ -107,10 +109,12 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, goto bad_area; good_area: + code = SEGV_ACCERR; #if defined(CONFIG_6xx) if (error_code & 0x95700000) /* an error such as lwarx to I/O controller space, address matching DABR, eciwx, etc. */ + goto bad_area; #endif /* CONFIG_6xx */ #if defined(CONFIG_8xx) /* The MPC8xx seems to always set 0x80000000, which is @@ -119,9 +123,8 @@ good_area: */ if (error_code & 0x10000000) /* Guarded storage error. */ -#endif /* CONFIG_8xx */ goto bad_area; - +#endif /* CONFIG_8xx */ /* a write */ if (is_write) { @@ -135,8 +138,25 @@ good_area: if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } - if (!handle_mm_fault(mm, vma, address, is_write)) - goto bad_area; + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + switch (handle_mm_fault(mm, vma, address, is_write)) { + case 1: + current->min_flt++; + break; + case 2: + current->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + up(&mm->mmap_sem); /* * keep track of tlb+htab misses that are good addrs but @@ -147,22 +167,55 @@ good_area: return; bad_area: - up(&mm->mmap_sem); pte_errors++; + + /* User mode accesses cause a SIGSEGV */ + if (user_mode(regs)) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = code; + info.si_addr = (void *) address; + force_sig_info(SIGSEGV, &info, current); + return; + } + + bad_page_fault(regs, address); + return; + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); bad_page_fault(regs, address); + return; + +do_sigbus: + up(&mm->mmap_sem); + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info (SIGBUS, &info, current); + if (!user_mode(regs)) + bad_page_fault(regs, address); } +/* + * bad_page_fault is called when we have a bad access from the kernel. + * It is called from do_page_fault above and from some of the procedures + * in traps.c. + */ void bad_page_fault(struct pt_regs *regs, unsigned long address) { unsigned long fixup; - if (user_mode(regs)) { - force_sig(SIGSEGV, current); - return; - } - /* Are we prepared to handle this fault? */ if ((fixup = search_exception_table(regs->nip)) != 0) { regs->nip = fixup; diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index 80fb7575e7c..fc3acdd5c25 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -83,6 +83,7 @@ extern char _start[], _end[]; extern char etext[], _stext[]; extern char __init_begin, __init_end; extern char __prep_begin, __prep_end; +extern char __chrp_begin, __chrp_end; extern char __pmac_begin, __pmac_end; extern char __apus_begin, __apus_end; extern char __openfirmware_begin, __openfirmware_end; @@ -777,7 +778,7 @@ void __init free_initmem(void) unsigned long a; unsigned long num_freed_pages = 0, num_prep_pages = 0, num_pmac_pages = 0, num_openfirmware_pages = 0, - num_apus_pages = 0; + num_apus_pages = 0, num_chrp_pages = 0; #define FREESEC(START,END,CNT) do { \ a = (unsigned long)(&START); \ for (; a < (unsigned long)(&END); a += PAGE_SIZE) { \ @@ -794,6 +795,7 @@ void __init free_initmem(void) case _MACH_Pmac: FREESEC(__apus_begin,__apus_end,num_apus_pages); FREESEC(__prep_begin,__prep_end,num_prep_pages); + FREESEC(__chrp_begin,__chrp_end,num_chrp_pages); break; case _MACH_chrp: FREESEC(__apus_begin,__apus_end,num_apus_pages); @@ -803,20 +805,24 @@ void __init free_initmem(void) case _MACH_prep: FREESEC(__apus_begin,__apus_end,num_apus_pages); FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); + FREESEC(__chrp_begin,__chrp_end,num_chrp_pages); break; case _MACH_mbx: FREESEC(__apus_begin,__apus_end,num_apus_pages); FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); FREESEC(__prep_begin,__prep_end,num_prep_pages); + FREESEC(__chrp_begin,__chrp_end,num_chrp_pages); break; case _MACH_apus: FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); FREESEC(__prep_begin,__prep_end,num_prep_pages); + FREESEC(__chrp_begin,__chrp_end,num_chrp_pages); break; case _MACH_gemini: FREESEC(__apus_begin,__apus_end,num_apus_pages); FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); FREESEC(__prep_begin,__prep_end,num_prep_pages); + FREESEC(__chrp_begin,__chrp_end,num_chrp_pages); break; } @@ -829,6 +835,8 @@ void __init free_initmem(void) if ( num_prep_pages ) printk(" %ldk prep", PGTOKB(num_prep_pages)); + if ( num_chrp_pages ) + printk(" %ldk chrp", PGTOKB(num_chrp_pages)); if ( num_pmac_pages ) printk(" %ldk pmac", PGTOKB(num_pmac_pages)); if ( num_openfirmware_pages ) diff --git a/arch/ppc/treeboot/mkevimg b/arch/ppc/treeboot/mkevimg index 76f849bb708..68eb4dd3f4e 100644 --- a/arch/ppc/treeboot/mkevimg +++ b/arch/ppc/treeboot/mkevimg @@ -1,4 +1,4 @@ -#!/usr/local/bin/perl +#!/usr/bin/perl # # Copyright (c) 1998-1999 TiVo, Inc. diff --git a/arch/ppc/treeboot/mkirimg b/arch/ppc/treeboot/mkirimg index e8aa24e3d1c..17a6f23281b 100644 --- a/arch/ppc/treeboot/mkirimg +++ b/arch/ppc/treeboot/mkirimg @@ -1,4 +1,4 @@ -#!/usr/local/bin/perl +#!/usr/bin/perl # # Copyright (c) 1998-1999 TiVo, Inc. # Original ELF parsing code. @@ -332,7 +332,7 @@ require 'elf.pl'; syswrite(OUTPUT, $ibuf, $initialOffset); if ($imageFound) { - $testN = pack ("I2", $bss_addr + $bss_size, $image_size); + $testN = pack ("N2", $bss_addr + $bss_size, $image_size); syswrite(OUTPUT, $testN, length($testN)); printf("Updated symbol \"imageSect_start\" to 0x%08x\n", $bss_addr + $bss_size); @@ -342,7 +342,7 @@ require 'elf.pl'; } if ($initrdFound) { - $testN = pack ("I2", $bss_addr + $bss_size + $image_size, $initrd_size); + $testN = pack ("N2", $bss_addr + $bss_size + $image_size, $initrd_size); syswrite(OUTPUT, $testN, length($testN)); printf("Updated symbol \"initrdSect_start\" to 0x%08x\n", $bss_addr + $bss_size + $image_size); diff --git a/arch/ppc/vmlinux.lds b/arch/ppc/vmlinux.lds index 707c4ad2feb..7bfdc4efbf8 100644 --- a/arch/ppc/vmlinux.lds +++ b/arch/ppc/vmlinux.lds @@ -104,6 +104,13 @@ SECTIONS __prep_end = .; . = ALIGN(4096); + __chrp_begin = .; + .text.chrp : { *(.text.chrp) } + .data.chrp : { *(.data.chrp) } + . = ALIGN(4096); + __chrp_end = .; + + . = ALIGN(4096); __apus_begin = .; .text.apus : { *(.text.apus) } .data.apus : { *(.data.apus) } @@ -135,4 +142,3 @@ SECTIONS _end = . ; PROVIDE (end = .); } - diff --git a/arch/sh/config.in b/arch/sh/config.in index 2ee913c3c22..ef521e0d4ec 100644 --- a/arch/sh/config.in +++ b/arch/sh/config.in @@ -15,6 +15,10 @@ endmenu mainmenu_option next_comment comment 'Processor type and features' +choice 'SuperH system type' \ + "Generic CONFIG_SH_GENERIC \ + SolutionEngine CONFIG_SH_SOLUTION_ENGINE" Generic + choice 'Processor type' \ "SH7708 CONFIG_CPU_SUBTYPE_SH7708 \ SH7709 CONFIG_CPU_SUBTYPE_SH7709 \ @@ -32,7 +36,12 @@ if [ "$CONFIG_CPU_SUBTYPE_SH7750" = "y" ]; then define_bool CONFIG_CPU_SH4 y fi bool 'Little Endian' CONFIG_LITTLE_ENDIAN -hex 'Physical memory start address' CONFIG_MEMORY_START 08000000 +if [ "$CONFIG_SH_SOLUTION_ENGINE" = "y" ]; then + define_hex CONFIG_MEMORY_START 0c000000 +else + hex 'Physical memory start address' CONFIG_MEMORY_START 08000000 + hex 'I/O port offset address' CONFIG_IOPORT_START ba000000 +fi endmenu mainmenu_option next_comment @@ -52,7 +61,9 @@ define_bool CONFIG_SBUS n bool 'Networking support' CONFIG_NET -bool 'Directy Connected Compact Flash support' CONFIG_CF_ENABLER +if [ "$CONFIG_SH_SOLUTION_ENGINE" != "y" ]; then + bool 'Directly Connected Compact Flash support' CONFIG_CF_ENABLER +fi bool 'PCI support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then diff --git a/arch/sh/defconfig b/arch/sh/defconfig index f2ba9193571..6e1a2ba2dad 100644 --- a/arch/sh/defconfig +++ b/arch/sh/defconfig @@ -12,6 +12,8 @@ CONFIG_UID16=y # # Processor type and features # +CONFIG_SH_GENERIC=y +# CONFIG_SH_SOLUTION_ENGINE is not set CONFIG_CPU_SUBTYPE_SH7708=y # CONFIG_CPU_SUBTYPE_SH7709 is not set # CONFIG_CPU_SUBTYPE_SH7750 is not set @@ -19,6 +21,7 @@ CONFIG_CPU_SH3=y # CONFIG_CPU_SH4 is not set CONFIG_LITTLE_ENDIAN=y CONFIG_MEMORY_START=0c000000 +CONFIG_IOPORT_START=ba000000 # # Loadable module support @@ -62,9 +65,10 @@ CONFIG_BINFMT_ELF=y # # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_LVM is not set # CONFIG_BLK_DEV_MD is not set # CONFIG_MD_LINEAR is not set -# CONFIG_MD_STRIPED is not set +# CONFIG_MD_RAID0 is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y @@ -85,6 +89,13 @@ CONFIG_BLK_DEV_IDE=y # CONFIG_BLK_DEV_HD is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set # CONFIG_BLK_DEV_IDECS is not set # CONFIG_BLK_DEV_IDECD is not set # CONFIG_BLK_DEV_IDETAPE is not set @@ -137,6 +148,7 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index 266aafe1ffa..cb14a2b501f 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile @@ -10,7 +10,7 @@ $(CC) $(AFLAGS) -traditional -c $< -o $*.o O_TARGET := kernel.o -O_OBJS := process.o signal.o entry.o traps.o irq.o irq_onchip.o \ +O_OBJS := process.o signal.o entry.o traps.o irq.o irq_ipr.o \ ptrace.o setup.o time.o sys_sh.o semaphore.o pci-sh.o \ irq_imask.o OX_OBJS := sh_ksyms.o @@ -20,6 +20,14 @@ ifdef CONFIG_CF_ENABLER O_OBJS += cf-enabler.o endif +ifdef CONFIG_SH_GENERIC +O_OBJS += io_generic.o +endif + +ifdef CONFIG_SH_SOLUTION_ENGINE +O_OBJS += setup_se.o io_se.o +endif + ifdef CONFIG_CPU_SH4 O_OBJS += fpu.o endif diff --git a/arch/sh/kernel/entry.S b/arch/sh/kernel/entry.S index 00372811cdf..dcb306cac0f 100644 --- a/arch/sh/kernel/entry.S +++ b/arch/sh/kernel/entry.S @@ -282,7 +282,7 @@ system_call: mov $r0, $r9 #endif ! New Syscall ABI - sub $r9, $r8 + add #-0x40, $r8 shlr2 $r8 shll8 $r8 shll8 $r8 @@ -492,7 +492,16 @@ restore_all: mov.l 2f, $k1 and $k1, $k2 ! Mask orignal SR value or $k0, $k2 ! Inherit current FD-bit - or $g_imask, $k2 ! Inherit the IMASK-bits + ! + mov $k3, $k0 ! Calculate IMASK-bits + shlr2 $k0 + and #0x3c, $k0 + cmp/eq #0x3c, $k0 + bf/s 7f + mov $g_imask, $k0 + shll2 $k0 + ! +7: or $k0, $k2 ! Set the IMASK-bits ldc $k2, $ssr ! #if defined(__SH4__) @@ -507,7 +516,7 @@ restore_all: ! There's the case we don't get FPU now stc $sr, $k2 tst $k1, $k2 - bt 7f + bt 8f ! We need to grab FPU here xor $k1, $k2 ldc $k2, $sr ! Grab FPU @@ -519,7 +528,7 @@ restore_all: ! ! Restoring FPU... ! -7: mov.l 3f, $k1 +8: mov.l 3f, $k1 lds $k1, $fpscr fmov.s @$r15+, $fr0 fmov.s @$r15+, $fr1 diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c new file mode 100644 index 00000000000..2465bc28c31 --- /dev/null +++ b/arch/sh/kernel/io_generic.c @@ -0,0 +1,99 @@ +/* $Id: io_generic.c,v 1.3 2000/05/07 23:31:58 gniibe Exp $ + * + * linux/arch/sh/kernel/io_generic.c + * + * Copyright (C) 2000 Niibe Yutaka + * + * Generic I/O routine. + * + */ + +#include +#include + +#define PORT2ADDR(x) (CONFIG_IOPORT_START+(x)) + +static inline void delay(void) +{ + ctrl_inw(0xa0000000); +} + +unsigned long inb(unsigned int port) +{ + return *(volatile unsigned char*)PORT2ADDR(port); +} + +unsigned long inb_p(unsigned int port) +{ + unsigned long v = *(volatile unsigned char*)PORT2ADDR(port); + + delay(); + return v; +} + +unsigned long inw(unsigned int port) +{ + return *(volatile unsigned short*)PORT2ADDR(port); +} + +unsigned long inl(unsigned int port) +{ + return *(volatile unsigned long*)PORT2ADDR(port); +} + +void insb(unsigned int port, void *buffer, unsigned long count) +{ + unsigned char *buf=buffer; + while(count--) *buf++=inb(port); +} + +void insw(unsigned int port, void *buffer, unsigned long count) +{ + unsigned short *buf=buffer; + while(count--) *buf++=inw(port); +} + +void insl(unsigned int port, void *buffer, unsigned long count) +{ + unsigned long *buf=buffer; + while(count--) *buf++=inl(port); +} + +void outb(unsigned long b, unsigned int port) +{ + *(volatile unsigned char*)PORT2ADDR(port) = b; +} + +void outb_p(unsigned long b, unsigned int port) +{ + *(volatile unsigned char*)PORT2ADDR(port) = b; + delay(); +} + +void outw(unsigned long b, unsigned int port) +{ + *(volatile unsigned short*)PORT2ADDR(port) = b; +} + +void outl(unsigned long b, unsigned int port) +{ + *(volatile unsigned long*)PORT2ADDR(port) = b; +} + +void outsb(unsigned int port, const void *buffer, unsigned long count) +{ + const unsigned char *buf=buffer; + while(count--) outb(*buf++, port); +} + +void outsw(unsigned int port, const void *buffer, unsigned long count) +{ + const unsigned short *buf=buffer; + while(count--) outw(*buf++, port); +} + +void outsl(unsigned int port, const void *buffer, unsigned long count) +{ + const unsigned long *buf=buffer; + while(count--) outl(*buf++, port); +} diff --git a/arch/sh/kernel/io_se.c b/arch/sh/kernel/io_se.c new file mode 100644 index 00000000000..1726980b6ec --- /dev/null +++ b/arch/sh/kernel/io_se.c @@ -0,0 +1,221 @@ +/* $Id: io_se.c,v 1.4 2000/05/07 23:31:58 gniibe Exp $ + * + * linux/arch/sh/kernel/io_se.c + * + * Copyright (C) 2000 Kazumoto Kojima + * + * I/O routine for Hitachi SolutionEngine. + * + */ +#include +#include +#include +#include + +/* SH pcmcia io window base, start and end. */ +int sh_pcic_io_wbase = 0xb8400000; +int sh_pcic_io_start; +int sh_pcic_io_stop; +int sh_pcic_io_type; +int sh_pcic_io_dummy; + +static inline void delay(void) +{ + ctrl_inw(0xa0000000); +} + +/* MS7750 requires special versions of in*, out* routines, since + PC-like io ports are located at upper half byte of 16-bit word which + can be accessed only with 16-bit wide. */ + +static inline volatile __u16 * +port2adr(unsigned int port) +{ + if (port >= 0x2000) + return (volatile __u16 *) (PA_MRSHPC + (port - 0x2000)); + else if (port >= 0x1000) + return (volatile __u16 *) (PA_83902 + (port << 1)); + else if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop) + return (volatile __u16 *) (sh_pcic_io_wbase + (port &~ 1)); + else + return (volatile __u16 *) (PA_SUPERIO + (port << 1)); +} + +static inline int +shifted_port(unsigned int port) +{ + /* For IDE registers, value is not shifted */ + if ((0x1f0 <= port && port < 0x1f8) || port == 0x3f6) + return 0; + else + return 1; +} + +#define maybebadio(name,port) \ + printk("bad PC-like io %s for port 0x%x at 0x%08x\n", \ + #name, (port), (__u32) __builtin_return_address(0)) + +unsigned long inb(unsigned int port) +{ + if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop) + return *(__u8 *) (sh_pcic_io_wbase + 0x40000 + port); + else if (shifted_port(port)) + return (*port2adr(port) >> 8); + else + return (*port2adr(port))&0xff; +} + +unsigned long inb_p(unsigned int port) +{ + unsigned long v; + + if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop) + v = *(__u8 *) (sh_pcic_io_wbase + 0x40000 + port); + else if (shifted_port(port)) + v = (*port2adr(port) >> 8); + else + v = (*port2adr(port))&0xff; + delay(); + return v; +} + +unsigned long inw(unsigned int port) +{ + if (port >= 0x2000 || + (sh_pcic_io_start <= port && port <= sh_pcic_io_stop)) + return *port2adr(port); + else + maybebadio(inw, port); + return 0; +} + +unsigned long inl(unsigned int port) +{ + maybebadio(inl, port); + return 0; +} + +void outb(unsigned long value, unsigned int port) +{ + if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop) + *(__u8 *)(sh_pcic_io_wbase + port) = value; + else if (shifted_port(port)) + *(port2adr(port)) = value << 8; + else + *(port2adr(port)) = value; +} + +void outb_p(unsigned long value, unsigned int port) +{ + if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop) + *(__u8 *)(sh_pcic_io_wbase + port) = value; + else if (shifted_port(port)) + *(port2adr(port)) = value << 8; + else + *(port2adr(port)) = value; + delay(); +} + +void outw(unsigned long value, unsigned int port) +{ + if (port >= 0x2000 || + (sh_pcic_io_start <= port && port <= sh_pcic_io_stop)) + *port2adr(port) = value; + else + maybebadio(outw, port); +} + +void outl(unsigned long value, unsigned int port) +{ + maybebadio(outl, port); +} + +void insb(unsigned int port, void *addr, unsigned long count) +{ + volatile __u16 *p = port2adr(port); + + if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop) { + volatile __u8 *bp = (__u8 *) (sh_pcic_io_wbase + 0x40000 + port); + while (count--) + *((__u8 *) addr)++ = *bp; + } else if (shifted_port(port)) { + while (count--) + *((__u8 *) addr)++ = *p >> 8; + } else { + while (count--) + *((__u8 *) addr)++ = *p; + } +} + +void insw(unsigned int port, void *addr, unsigned long count) +{ + volatile __u16 *p = port2adr(port); + while (count--) + *((__u16 *) addr)++ = *p; +} + +void insl(unsigned int port, void *addr, unsigned long count) +{ + maybebadio(insl, port); +} + +void outsb(unsigned int port, const void *addr, unsigned long count) +{ + volatile __u16 *p = port2adr(port); + + if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop) { + volatile __u8 *bp = (__u8 *) (sh_pcic_io_wbase + port); + while (count--) + *bp = *((__u8 *) addr)++; + } else if (shifted_port(port)) { + while (count--) + *p = *((__u8 *) addr)++ << 8; + } else { + while (count--) + *p = *((__u8 *) addr)++; + } +} + +void outsw(unsigned int port, const void *addr, unsigned long count) +{ + volatile __u16 *p = port2adr(port); + while (count--) + *p = *((__u16 *) addr)++; +} + +void outsl(unsigned int port, const void *addr, unsigned long count) +{ + maybebadio(outsw, port); +} + +/* Map ISA bus address to the real address. Only for PCMCIA. */ + +/* ISA page descriptor. */ +static __u32 sh_isa_memmap[256]; + +int +sh_isa_mmap(__u32 start, __u32 length, __u32 offset) +{ + int idx; + + if (start >= 0x100000 || (start & 0xfff) || (length != 0x1000)) + return -1; + + idx = start >> 12; + sh_isa_memmap[idx] = 0xb8000000 + (offset &~ 0xfff); +#if 0 + printk("sh_isa_mmap: start %x len %x offset %x (idx %x paddr %x)\n", + start, length, offset, idx, sh_isa_memmap[idx]); +#endif + return 0; +} + +unsigned long +sh_isa_slot(unsigned long offset) +{ + int idx; + + idx = (offset >> 12) & 0xff; + offset &= 0xfff; + return sh_isa_memmap[idx] + offset; +} diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index c989796d7c0..b7d01e9a25b 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -13,6 +13,7 @@ * Naturally it's not a 1:1 relation, but there are similarities. */ +#include #include #include #include @@ -300,7 +301,7 @@ asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, desc->handler->end(irq); spin_unlock(&irq_controller_lock); -#if 1 +#if 0 __sti(); #endif if (softirq_state[cpu].active&softirq_state[cpu].mask) @@ -513,3 +514,10 @@ int setup_irq(unsigned int irq, struct irqaction * new) spin_unlock_irqrestore(&irq_controller_lock,flags); return 0; } + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL) + +void init_irq_proc(void) +{ +} +#endif diff --git a/arch/sh/kernel/irq_ipr.c b/arch/sh/kernel/irq_ipr.c new file mode 100644 index 00000000000..8b6b0f5ee17 --- /dev/null +++ b/arch/sh/kernel/irq_ipr.c @@ -0,0 +1,171 @@ +/* $Id: irq_ipr.c,v 1.6 2000/05/14 08:41:25 gniibe Exp $ + * + * linux/arch/sh/kernel/irq_ipr.c + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * + * Interrupt handling for IPR-based IRQ. + * + * Supported system: + * On-chip supporting modules (TMU, RTC, etc.). + * On-chip supporting modules for SH7709/SH7709A/SH7729. + * Hitachi SolutionEngine external I/O: + * MS7709SE01, MS7709ASE01, and MS7750SE01 + * + */ + +#include +#include +#include + +#include +#include + +struct ipr_data { + unsigned int addr; /* Address of Interrupt Priority Register */ + int shift; /* Shifts of the 16-bit data */ + int priority; /* The priority */ +}; +static struct ipr_data ipr_data[NR_IRQS]; + +void set_ipr_data(unsigned int irq, unsigned int addr, int pos, int priority) +{ + ipr_data[irq].addr = addr; + ipr_data[irq].shift = pos*4; /* POSition (0-3) x 4 means shift */ + ipr_data[irq].priority = priority; +} + +static void enable_ipr_irq(unsigned int irq); +void disable_ipr_irq(unsigned int irq); + +/* shutdown is same as "disable" */ +#define shutdown_ipr_irq disable_ipr_irq + +static void mask_and_ack_ipr(unsigned int); +static void end_ipr_irq(unsigned int irq); + +static unsigned int startup_ipr_irq(unsigned int irq) +{ + enable_ipr_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type ipr_irq_type = { + "IPR-based-IRQ", + startup_ipr_irq, + shutdown_ipr_irq, + enable_ipr_irq, + disable_ipr_irq, + mask_and_ack_ipr, + end_ipr_irq +}; + +void disable_ipr_irq(unsigned int irq) +{ + unsigned long val, flags; + unsigned int addr = ipr_data[irq].addr; + unsigned short mask = 0xffff ^ (0x0f << ipr_data[irq].shift); + + /* Set the priority in IPR to 0 */ + save_and_cli(flags); + val = ctrl_inw(addr); + val &= mask; + ctrl_outw(val, addr); + restore_flags(flags); +} + +static void enable_ipr_irq(unsigned int irq) +{ + unsigned long val, flags; + unsigned int addr = ipr_data[irq].addr; + int priority = ipr_data[irq].priority; + unsigned short value = (priority << ipr_data[irq].shift); + + /* Set priority in IPR back to original value */ + save_and_cli(flags); + val = ctrl_inw(addr); + val |= value; + ctrl_outw(val, addr); + restore_flags(flags); +} + +void make_ipr_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &ipr_irq_type; + disable_ipr_irq(irq); +} + +static void mask_and_ack_ipr(unsigned int irq) +{ + disable_ipr_irq(irq); + +#ifdef CONFIG_CPU_SUBTYPE_SH7709 + /* This is needed when we use edge triggered setting */ + /* XXX: Is it really needed? */ + if (IRQ0_IRQ <= irq && irq <= IRQ5_IRQ) { + /* Clear external interrupt request */ + int a = ctrl_inb(INTC_IRR0); + a &= ~(1 << (irq - IRQ0_IRQ)); + ctrl_outb(a, INTC_IRR0); + } +#endif +} + +static void end_ipr_irq(unsigned int irq) +{ + enable_ipr_irq(irq); +} + +void __init init_IRQ(void) +{ + int i; + + for (i = TIMER_IRQ; i < NR_IRQS; i++) { + irq_desc[i].handler = &ipr_irq_type; + } + + set_ipr_data(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY); + set_ipr_data(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY); + +#ifdef CONFIG_CPU_SUBTYPE_SH7709 + /* + * Initialize the Interrupt Controller (INTC) + * registers to their power on values + */ +#if 0 + /* + * XXX: I think that this is the job of boot loader. -- gniibe + * + * When Takeshi released new boot loader following setting + * will be removed shortly. + */ + ctrl_outb(0, INTC_IRR0); + ctrl_outb(0, INTC_IRR1); + ctrl_outb(0, INTC_IRR2); + + ctrl_outw(0, INTC_ICR0); + ctrl_outw(0, INTC_ICR1);/* Really? 0x4000?*/ + ctrl_outw(0, INTC_ICR2); + ctrl_outw(0, INTC_INTER); + ctrl_outw(0, INTC_IPRA); + ctrl_outw(0, INTC_IPRB); + ctrl_outw(0, INTC_IPRC); + ctrl_outw(0, INTC_IPRD); + ctrl_outw(0, INTC_IPRE); +#endif + + /* + * Enable external irq (INTC IRQ mode). + * You should set corresponding bits of PFC to "00" + * to enable these interrupts. + */ + set_ipr_data(IRQ0_IRQ, IRQ0_IRP_ADDR, IRQ0_IRP_POS, IRQ0_PRIORITY); + set_ipr_data(IRQ1_IRQ, IRQ1_IRP_ADDR, IRQ1_IRP_POS, IRQ1_PRIORITY); + set_ipr_data(IRQ2_IRQ, IRQ2_IRP_ADDR, IRQ2_IRP_POS, IRQ2_PRIORITY); + set_ipr_data(IRQ3_IRQ, IRQ3_IRP_ADDR, IRQ3_IRP_POS, IRQ3_PRIORITY); + set_ipr_data(IRQ4_IRQ, IRQ4_IRP_ADDR, IRQ4_IRP_POS, IRQ4_PRIORITY); + set_ipr_data(IRQ5_IRQ, IRQ5_IRP_ADDR, IRQ5_IRP_POS, IRQ5_PRIORITY); +#endif /* CONFIG_CPU_SUBTYPE_SH7709 */ +} diff --git a/arch/sh/kernel/irq_onchip.c b/arch/sh/kernel/irq_onchip.c deleted file mode 100644 index 36dce33fbbc..00000000000 --- a/arch/sh/kernel/irq_onchip.c +++ /dev/null @@ -1,298 +0,0 @@ -/* $Id: irq_onchip.c,v 1.7 2000-01-09 15:55:55+09 gniibe Exp $ - * - * linux/arch/sh/kernel/irq_onchip.c - * - * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi - * - * Interrupt handling for on-chip supporting modules (TMU, RTC, etc.). - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -struct ipr_data { - int offset; - int priority; -}; -static struct ipr_data ipr_data[NR_IRQS-TIMER_IRQ]; - -void set_ipr_data(unsigned int irq, int offset, int priority) -{ - ipr_data[irq-TIMER_IRQ].offset = offset; - ipr_data[irq-TIMER_IRQ].priority = priority; -} - -static void enable_onChip_irq(unsigned int irq); -void disable_onChip_irq(unsigned int irq); - -/* shutdown is same as "disable" */ -#define shutdown_onChip_irq disable_onChip_irq - -static void mask_and_ack_onChip(unsigned int); -static void end_onChip_irq(unsigned int irq); - -static unsigned int startup_onChip_irq(unsigned int irq) -{ - enable_onChip_irq(irq); - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type onChip_irq_type = { - "On-Chip-IPR", - startup_onChip_irq, - shutdown_onChip_irq, - enable_onChip_irq, - disable_onChip_irq, - mask_and_ack_onChip, - end_onChip_irq -}; - -/* - * These have to be protected by the irq controller spinlock - * before being called. - * - * - * IPRA 15-12 11-8 7-4 3-0 - * IPRB 15-12 11-8 7-4 3-0 - * IPRC 15-12 11-8 7-4 3-0 - * - */ -#if defined(__sh3__) -#define INTC_IPR 0xfffffee2UL /* Word access */ -#define INTC_SIZE 0x2 -#elif defined(__SH4__) -#define INTC_IPR 0xffd00004UL /* Word access */ -#define INTC_SIZE 0x4 -#endif - -void disable_onChip_irq(unsigned int irq) -{ - unsigned long val, flags; - /* Set priority in IPR to 0 */ - int offset = ipr_data[irq-TIMER_IRQ].offset; - unsigned long intc_ipr_address = INTC_IPR + (offset/16*INTC_SIZE); - unsigned short mask = 0xffff ^ (0xf << (offset%16)); - - save_and_cli(flags); - val = ctrl_inw(intc_ipr_address); - val &= mask; - ctrl_outw(val, intc_ipr_address); - restore_flags(flags); -} - -static void enable_onChip_irq(unsigned int irq) -{ - unsigned long val, flags; - /* Set priority in IPR back to original value */ - int offset = ipr_data[irq-TIMER_IRQ].offset; - int priority = ipr_data[irq-TIMER_IRQ].priority; - unsigned long intc_ipr_address = INTC_IPR + (offset/16*INTC_SIZE); - unsigned short value = (priority << (offset%16)); - - save_and_cli(flags); - val = ctrl_inw(intc_ipr_address); - val |= value; - ctrl_outw(val, intc_ipr_address); - restore_flags(flags); -} - -void make_onChip_irq(unsigned int irq) -{ - disable_irq_nosync(irq); - irq_desc[irq].handler = &onChip_irq_type; - enable_irq(irq); -} - -static void mask_and_ack_onChip(unsigned int irq) -{ - disable_onChip_irq(irq); -} - -static void end_onChip_irq(unsigned int irq) -{ - enable_onChip_irq(irq); -} - - -#ifdef CONFIG_CPU_SUBTYPE_SH7709 -/* - * SH7707/SH7709/SH7709A/SH7729 Extended on-chip I/O - */ - -#define INTC_IRR0 0xa4000004UL -#define INTC_IRR1 0xa4000006UL -#define INTC_IRR2 0xa4000008UL - -#define INTC_ICR0 0xfffffee0 -#define INTC_ICR1 0xa4000010 -#define INTC_ICR2 0xa4000012 -#define INTC_INTER 0xa4000014 -#define INTC_IPRA 0xfffffee2 -#define INTC_IPRB 0xfffffee4 -#define INTC_IPRC 0xa4000016 -#define INTC_IPRD 0xa4000018 -#define INTC_IPRE 0xa400001a - -#define IRQ0_IRQ 32 -#define IRQ1_IRQ 33 -#define IRQ2_IRQ 34 -#define IRQ3_IRQ 35 -#define IRQ4_IRQ 36 -#define IRQ5_IRQ 37 - -#define IRQ0_IRP_OFFSET 32 -#define IRQ1_IRP_OFFSET 36 -#define IRQ2_IRP_OFFSET 40 -#define IRQ3_IRP_OFFSET 44 -#define IRQ4_IRP_OFFSET 48 -#define IRQ5_IRP_OFFSET 52 - -#define IRQ0_PRIORITY 1 -#define IRQ1_PRIORITY 1 -#define IRQ2_PRIORITY 1 -#define IRQ3_PRIORITY 1 -#define IRQ4_PRIORITY 1 -#define IRQ5_PRIORITY 1 - -static void enable_onChip2_irq(unsigned int irq); -void disable_onChip2_irq(unsigned int irq); - -/* shutdown is same as "disable" */ -#define shutdown_onChip2_irq disable_onChip2_irq - -static void mask_and_ack_onChip2(unsigned int); -static void end_onChip2_irq(unsigned int irq); - -static unsigned int startup_onChip2_irq(unsigned int irq) -{ - enable_onChip2_irq(irq); - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type onChip2_irq_type = { - "Extended-IPR", - startup_onChip2_irq, - shutdown_onChip2_irq, - enable_onChip2_irq, - disable_onChip2_irq, - mask_and_ack_onChip2, - end_onChip2_irq -}; - -void disable_onChip2_irq(unsigned int irq) -{ - unsigned long val, flags; - /* Set priority in IPR to 0 */ - int offset = ipr_data[irq-TIMER_IRQ].offset - 32; - unsigned long intc_ipr_address = INTC_IPRC + (offset/16*INTC_SIZE); - unsigned short mask = 0xffff ^ (0xf << (offset%16)); - - save_and_cli(flags); - val = ctrl_inw(intc_ipr_address); - val &= mask; - ctrl_outw(val, intc_ipr_address); - restore_flags(flags); -} - -static void enable_onChip2_irq(unsigned int irq) -{ - unsigned long val, flags; - /* Set priority in IPR back to original value */ - int offset = ipr_data[irq-TIMER_IRQ].offset - 32; - int priority = ipr_data[irq-TIMER_IRQ].priority; - unsigned long intc_ipr_address = INTC_IPRC + (offset/16*INTC_SIZE); - unsigned short value = (priority << (offset%16)); - - save_and_cli(flags); - val = ctrl_inw(intc_ipr_address); - val |= value; - ctrl_outw(val, intc_ipr_address); - restore_flags(flags); -} - -static void mask_and_ack_onChip2(unsigned int irq) -{ - disable_onChip2_irq(irq); - if (IRQ0_IRQ <= irq && irq <= IRQ5_IRQ) { - /* Clear external interrupt request */ - int a = ctrl_inb(INTC_IRR0); - a &= ~(1 << (irq - IRQ0_IRQ)); - ctrl_outb(a, INTC_IRR0); - } -} - -static void end_onChip2_irq(unsigned int irq) -{ - enable_onChip2_irq(irq); -} -#endif /* CONFIG_CPU_SUBTYPE_SH7709 */ - -void __init init_IRQ(void) -{ - int i; - - for (i = TIMER_IRQ; i < NR_IRQS; i++) { - irq_desc[i].handler = &onChip_irq_type; - } - -#ifdef CONFIG_CPU_SUBTYPE_SH7709 - - /* - * Initialize the Interrupt Controller (INTC) - * registers to their power on values - */ - - ctrl_outb(0, INTC_IRR0); - ctrl_outb(0, INTC_IRR1); - ctrl_outb(0, INTC_IRR2); - - ctrl_outw(0, INTC_ICR0); - ctrl_outw(0, INTC_ICR1); - ctrl_outw(0, INTC_ICR2); - ctrl_outw(0, INTC_INTER); - ctrl_outw(0, INTC_IPRA); - ctrl_outw(0, INTC_IPRB); - ctrl_outw(0, INTC_IPRC); - ctrl_outw(0, INTC_IPRD); - ctrl_outw(0, INTC_IPRE); - - for (i = IRQ0_IRQ; i < NR_IRQS; i++) { - irq_desc[i].handler = &onChip2_irq_type; - } - - /* - * Enable external irq(INTC IRQ mode). - * You should set corresponding bits of PFC to "00" - * to enable these interrupts. - */ - set_ipr_data(IRQ0_IRQ, IRQ0_IRP_OFFSET, IRQ0_PRIORITY); - set_ipr_data(IRQ1_IRQ, IRQ1_IRP_OFFSET, IRQ1_PRIORITY); - set_ipr_data(IRQ2_IRQ, IRQ2_IRP_OFFSET, IRQ2_PRIORITY); - set_ipr_data(IRQ3_IRQ, IRQ3_IRP_OFFSET, IRQ3_PRIORITY); - set_ipr_data(IRQ4_IRQ, IRQ4_IRP_OFFSET, IRQ4_PRIORITY); - set_ipr_data(IRQ5_IRQ, IRQ5_IRP_OFFSET, IRQ5_PRIORITY); -#endif /* CONFIG_CPU_SUBTYPE_SH7709 */ -} diff --git a/arch/sh/kernel/ptrace.c b/arch/sh/kernel/ptrace.c index d4a1556b905..73601b358b5 100644 --- a/arch/sh/kernel/ptrace.c +++ b/arch/sh/kernel/ptrace.c @@ -1,4 +1,4 @@ -/* $Id: ptrace.c,v 1.4 2000/03/22 13:59:01 gniibe Exp $ +/* $Id: ptrace.c,v 1.5 2000/05/09 01:42:21 gniibe Exp $ * * linux/arch/sh/kernel/ptrace.c * @@ -137,7 +137,6 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child, *tsk = current; struct user * dummy = NULL; - unsigned long flags; int ret; lock_kernel(); @@ -154,15 +153,19 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!!! */ + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); if (!child) goto out; + ret = -EPERM; if (pid == 1) /* you may not mess with init */ - goto out; + goto out_tsk; + if (request == PTRACE_ATTACH) { if (child == tsk) - goto out; + goto out_tsk; if ((!child->dumpable || (tsk->uid != child->euid) || (tsk->uid != child->suid) || @@ -171,34 +174,33 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) (tsk->gid != child->sgid) || (!cap_issubset(child->cap_permitted, tsk->cap_permitted)) || (tsk->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) - goto out; + goto out_tsk; /* the same process cannot be attached many times */ if (child->flags & PF_PTRACED) - goto out; + goto out_tsk; child->flags |= PF_PTRACED; - write_lock_irqsave(&tasklist_lock, flags); + write_lock_irq(&tasklist_lock); if (child->p_pptr != tsk) { REMOVE_LINKS(child); child->p_pptr = tsk; SET_LINKS(child); } - write_unlock_irqrestore(&tasklist_lock, flags); + write_unlock_irq(&tasklist_lock); send_sig(SIGSTOP, child, 1); ret = 0; - goto out; + goto out_tsk; } ret = -ESRCH; if (!(child->flags & PF_PTRACED)) - goto out; + goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) - goto out; + goto out_tsk; } if (child->p_pptr != tsk) - goto out; - + goto out_tsk; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ @@ -357,11 +359,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) break; child->flags &= ~(PF_PTRACED|PF_TRACESYS); child->exit_code = data; - write_lock_irqsave(&tasklist_lock, flags); + write_lock_irq(&tasklist_lock); REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); + write_unlock_irq(&tasklist_lock); wake_up_process(child); ret = 0; break; @@ -371,6 +373,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = -EIO; break; } +out_tsk: + free_task_struct(child); out: unlock_kernel(); return ret; diff --git a/arch/sh/kernel/setup_se.c b/arch/sh/kernel/setup_se.c new file mode 100644 index 00000000000..bcb9b64140a --- /dev/null +++ b/arch/sh/kernel/setup_se.c @@ -0,0 +1,123 @@ +/* $Id: setup_se.c,v 1.6 2000/05/14 08:41:25 gniibe Exp $ + * + * linux/arch/sh/kernel/setup_se.c + * + * Copyright (C) 2000 Kazumoto Kojima + * + * Hitachi SolutionEngine Support. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Configure the Super I/O chip + */ +static void __init smsc_config(int index, int data) +{ + outb_p(index, INDEX_PORT); + outb_p(data, DATA_PORT); +} + +static void __init init_smsc(void) +{ + outb_p(CONFIG_ENTER, CONFIG_PORT); + outb_p(CONFIG_ENTER, CONFIG_PORT); + + /* FDC */ + smsc_config(CURRENT_LDN_INDEX, LDN_FDC); + smsc_config(ACTIVATE_INDEX, 0x01); + smsc_config(IRQ_SELECT_INDEX, 6); /* IRQ6 */ + + /* IDE1 */ + smsc_config(CURRENT_LDN_INDEX, LDN_IDE1); + smsc_config(ACTIVATE_INDEX, 0x01); + smsc_config(IRQ_SELECT_INDEX, 14); /* IRQ14 */ + + /* AUXIO (GPIO): to use IDE1 */ + smsc_config(CURRENT_LDN_INDEX, LDN_AUXIO); + smsc_config(GPIO46_INDEX, 0x00); /* nIOROP */ + smsc_config(GPIO47_INDEX, 0x00); /* nIOWOP */ + + /* COM1 */ + smsc_config(CURRENT_LDN_INDEX, LDN_COM1); + smsc_config(ACTIVATE_INDEX, 0x01); + smsc_config(IO_BASE_HI_INDEX, 0x03); + smsc_config(IO_BASE_LO_INDEX, 0xf8); + smsc_config(IRQ_SELECT_INDEX, 3); /* IRQ3 */ + + /* RTC */ + smsc_config(CURRENT_LDN_INDEX, LDN_RTC); + smsc_config(ACTIVATE_INDEX, 0x01); + smsc_config(IRQ_SELECT_INDEX, 8); /* IRQ8 */ + + /* XXX: COM2, PARPORT, KBD, and MOUSE will come here... */ + outb_p(CONFIG_EXIT, CONFIG_PORT); +} + +/* + * Initialize IRQ setting + */ +static void __init init_se_IRQ(void) +{ + int i; + + /* + * Super I/O (Just mimic PC): + * 1: keyboard + * 3: serial 0 + * 4: serial 1 + * 5: printer + * 6: floppy + * 8: rtc + * 12: mouse + * 14: ide0 + */ + set_ipr_data(14, BCR_ILCRA, 2, 0x0f-14); + set_ipr_data(12, BCR_ILCRA, 1, 0x0f-12); + set_ipr_data( 8, BCR_ILCRB, 1, 0x0f- 8); + set_ipr_data( 6, BCR_ILCRC, 3, 0x0f- 6); + set_ipr_data( 5, BCR_ILCRC, 2, 0x0f- 5); + set_ipr_data( 4, BCR_ILCRC, 1, 0x0f- 4); + set_ipr_data( 3, BCR_ILCRC, 0, 0x0f- 3); + set_ipr_data( 1, BCR_ILCRD, 3, 0x0f- 1); + + set_ipr_data(10, BCR_ILCRD, 1, 0x0f-10); /* LAN */ + + set_ipr_data( 0, BCR_ILCRE, 3, 0x0f- 0); /* PCIRQ3 */ + set_ipr_data(11, BCR_ILCRE, 2, 0x0f-11); /* PCIRQ2 */ + set_ipr_data( 9, BCR_ILCRE, 1, 0x0f- 9); /* PCIRQ1 */ + set_ipr_data( 7, BCR_ILCRE, 0, 0x0f- 7); /* PCIRQ0 */ + + /* #2, #13 are allocated for SLOT IRQ #1 and #2 (for now) */ + /* NOTE: #2 and #13 are not used on PC */ + set_ipr_data(13, BCR_ILCRG, 1, 0x0f-13); /* SLOTIRQ2 */ + set_ipr_data( 2, BCR_ILCRG, 0, 0x0f- 2); /* SLOTIRQ1 */ + + for (i = 0; i < 15; i++) { + make_ipr_irq(i); + } +} + +/* + * Initialize the board + */ +int __init setup_se(void) +{ + init_se_IRQ(); + init_smsc(); + /* XXX: RTC setting comes here */ + + printk(KERN_INFO "Hitach SolutionEngine Setup...done\n"); + return 0; +} + +module_init(setup_se); diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index 8751d45f547..774359a1be7 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -165,11 +165,11 @@ static inline int save_sigcontext_fpu(struct sigcontext *sc) unsigned long flags; if (!tsk->used_math) { - sc->sc_ownedfp = 0; + __copy_to_user(&sc->sc_ownedfp, 0, sizeof(int)); return 0; } - sc->sc_ownedfp = 1; + __copy_to_user(&sc->sc_ownedfp, 1, sizeof(int)); /* This will cause a "finit" to be triggered by the next attempted FPU operation by the 'current' process. diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index 3e9cd8d2801..de61501841e 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c @@ -47,9 +47,6 @@ #define RCR2_RESET 0x02 /* Reset bit */ #define RCR2_START 0x01 /* Start bit */ -#define RTC_IRQ 22 -#define RTC_IPR_OFFSET 0 - #if defined(__sh3__) #define TMU_TOCR 0xfffffe90 /* Byte access */ #define TMU_TSTR 0xfffffe92 /* Byte access */ @@ -204,13 +201,6 @@ static long last_rtc_update = 0; static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { do_timer(regs); -#ifdef TAKESHI - { - unsigned long what_is_this=0xa4000124; - - ctrl_outb(ctrl_inb(what_is_this)+1,what_is_this); - } -#endif #if 0 if (!user_mode(regs)) sh_do_profile(regs->pc); @@ -393,23 +383,23 @@ static struct irqaction irq1 = { rtc_interrupt, SA_INTERRUPT, 0, "rtc", NULL, N void __init time_init(void) { - unsigned int cpu_clock, master_clock, module_clock; - unsigned short ifc, pfc; + unsigned int cpu_clock, master_clock, bus_clock, module_clock; + unsigned short frqcr, ifc, pfc; unsigned long interval; #if defined(__sh3__) static int ifc_table[] = { 1, 2, 4, 1, 3, 1, 1, 1 }; static int pfc_table[] = { 1, 2, 4, 1, 3, 6, 1, 1 }; + static int stc_table[] = { 1, 2, 3, 4, 6, 8, 1, 1 }; #elif defined(__SH4__) static int ifc_table[] = { 1, 2, 3, 4, 6, 8, 1, 1 }; +#define bfc_table ifc_table /* Same */ static int pfc_table[] = { 2, 3, 4, 6, 8, 2, 2, 2 }; #endif xtime.tv_sec = get_rtc_time(); xtime.tv_usec = 0; - set_ipr_data(TIMER_IRQ, TIMER_IPR_OFFSET, TIMER_PRIORITY); setup_irq(TIMER_IRQ, &irq0); - set_ipr_data(RTC_IRQ, RTC_IPR_OFFSET, TIMER_PRIORITY); setup_irq(RTC_IRQ, &irq1); /* Check how fast it is.. */ @@ -420,23 +410,37 @@ void __init time_init(void) (cpu_clock / 1000000), (cpu_clock % 1000000)/10000); #if defined(__sh3__) { - unsigned short tmp; - tmp = (ctrl_inw(FRQCR) & 0x000c) >> 2; - tmp |= (ctrl_inw(FRQCR) & 0x4000) >> 12; - ifc = ifc_table[tmp & 0x0007]; - tmp = ctrl_inw(FRQCR) & 0x0003; - tmp |= (ctrl_inw(FRQCR) & 0x2000) >> 11; - pfc = pfc_table[ctrl_inw(FRQCR) & 0x0007]; + unsigned short tmp, stc; + frqcr = ctrl_inw(FRQCR); + tmp = (frqcr & 0x8000) >> 13; + tmp |= (frqcr & 0x0030) >> 4; + stc = stc_table[tmp]; + tmp = (frqcr & 0x4000) >> 12; + tmp |= (frqcr & 0x000c) >> 2; + ifc = ifc_table[tmp]; + tmp = (frqcr & 0x2000) >> 11; + tmp |= frqcr & 0x0003; + pfc = pfc_table[tmp]; + master_clock = cpu_clock; + bus_clock = master_clock/pfc; } #elif defined(__SH4__) - ifc = ifc_table[(ctrl_inw(FRQCR)>> 6) & 0x0007]; - pfc = pfc_table[ctrl_inw(FRQCR) & 0x0007]; + { + unsigned short bfc; + frqcr = ctrl_inw(FRQCR); + ifc = ifc_table[(frqcr>> 6) & 0x0007]; + bfc = bfc_table[(frqcr>> 3) & 0x0007]; + pfc = pfc_table[frqcr & 0x0007]; + master_clock = cpu_clock * ifc; + bus_clock = master_clock/bfc; + } #endif - master_clock = cpu_clock * ifc; + printk("Bus clock: %d.%02dMHz\n", + (bus_clock/1000000), (bus_clock % 1000000)/10000); module_clock = master_clock/pfc; printk("Module clock: %d.%02dMHz\n", (module_clock/1000000), (module_clock % 1000000)/10000); - interval = (module_clock/400); + interval = (module_clock/(HZ*4)); printk("Interval = %ld\n", interval); diff --git a/arch/sh/lib/checksum.S b/arch/sh/lib/checksum.S index 3471084b9e2..b34a65383df 100644 --- a/arch/sh/lib/checksum.S +++ b/arch/sh/lib/checksum.S @@ -1,4 +1,4 @@ -/* $Id: checksum.S,v 1.2 1999/10/29 13:06:55 gniibe Exp $ +/* $Id: checksum.S,v 1.4 2000/05/14 08:41:26 gniibe Exp $ * * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket @@ -49,95 +49,99 @@ ENTRY(csum_partial) * Fortunately, it is easy to convert 2-byte alignment to 4-byte * alignment for the unrolled loop. */ - mov r5,r1 - mov r4,r0 - tst #2,r0 ! Check alignment. + mov $r5, $r1 + mov $r4, $r0 + tst #2, $r0 ! Check alignment. bt 2f ! Jump if alignment is ok. ! - add #-2,r5 ! Alignment uses up two bytes. - cmp/pz r5 ! + add #-2, $r5 ! Alignment uses up two bytes. + cmp/pz $r5 ! bt/s 1f ! Jump if we had at least two bytes. clrt bra 6f - add #2,r5 ! r5 was < 2. Deal with it. + add #2, $r5 ! $r5 was < 2. Deal with it. 1: - mov.w @r4+,r0 - extu.w r0,r0 - addc r0,r6 + mov.w @$r4+, $r0 + extu.w $r0, $r0 + addc $r0, $r6 bf 2f - add #1,r6 + add #1, $r6 2: - mov #-5,r0 - shld r0,r5 - tst r5,r5 + mov #-5, $r0 + shld $r0, $r5 + tst $r5, $r5 bt/s 4f ! if it's =0, go to 4f clrt + .align 2 3: - mov.l @r4+,r0 - addc r0,r6 - mov.l @r4+,r0 - addc r0,r6 - mov.l @r4+,r0 - addc r0,r6 - mov.l @r4+,r0 - addc r0,r6 - mov.l @r4+,r0 - addc r0,r6 - mov.l @r4+,r0 - addc r0,r6 - mov.l @r4+,r0 - addc r0,r6 - mov.l @r4+,r0 - addc r0,r6 - movt r0 - dt r5 + mov.l @$r4+, $r0 + mov.l @$r4+, $r2 + mov.l @$r4+, $r3 + addc $r0, $r6 + mov.l @$r4+, $r0 + addc $r2, $r6 + mov.l @$r4+, $r2 + addc $r3, $r6 + mov.l @$r4+, $r3 + addc $r0, $r6 + mov.l @$r4+, $r0 + addc $r2, $r6 + mov.l @$r4+, $r2 + addc $r3, $r6 + addc $r0, $r6 + addc $r2, $r6 + movt $r0 + dt $r5 bf/s 3b - cmp/eq #1,r0 - mov #0,r0 - addc r0,r6 + cmp/eq #1, $r0 + ! here, we know $r5==0 + addc $r5, $r6 ! add carry to $r6 4: - mov r1,r5 - mov #0x1c,r0 - and r0,r5 - tst r5,r5 + mov $r1, $r0 + and #0x1c, $r0 + tst $r0, $r0 bt/s 6f - clrt - shlr2 r5 + mov $r0, $r5 + shlr2 $r5 + mov #0, $r2 5: - mov.l @r4+,r0 - addc r0,r6 - movt r0 - dt r5 + addc $r2, $r6 + mov.l @$r4+, $r2 + movt $r0 + dt $r5 bf/s 5b - cmp/eq #1,r0 - mov #0,r0 - addc r0,r6 + cmp/eq #1, $r0 + addc $r2, $r6 + addc $r5, $r6 ! $r5==0 here, so it means add carry-bit 6: - mov r1,r5 - mov #3,r0 - and r0,r5 - tst r5,r5 + mov $r1, $r5 + mov #3, $r0 + and $r0, $r5 + tst $r5, $r5 bt 9f ! if it's =0 go to 9f - mov #2,r1 - cmp/hs r1,r5 + mov #2, $r1 + cmp/hs $r1, $r5 bf 7f - mov.w @r4+,r0 - extu.w r0,r0 - cmp/eq r1,r5 + mov.w @r4+, $r0 + extu.w $r0, $r0 + cmp/eq $r1, $r5 bt/s 8f clrt - shll16 r0 - addc r0,r6 + shll16 $r0 + addc $r0, $r6 7: - mov.b @r4+,r0 - extu.b r0,r0 + mov.b @$r4+, $r0 + extu.b $r0, $r0 +#ifndef __LITTLE_ENDIAN__ + shll8 $r0 +#endif 8: - addc r0,r6 - mov #0,r0 - addc r0,r6 + addc $r0, $r6 + mov #0, $r0 + addc $r0, $r6 9: rts - mov r6,r0 + mov $r6, $r0 /* unsigned int csum_partial_copy_generic (const char *src, char *dst, int len, @@ -167,6 +171,16 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst, int len, .long 9999b, 6002f ; \ .previous +! +! r4: const char *SRC +! r5: char *DST +! r6: int LEN +! r7: int SUM +! +! on stack: +! int *SRC_ERR_PTR +! int *DST_ERR_PTR +! ENTRY(csum_partial_copy_generic) mov.l r5,@-r15 mov.l r6,@-r15 @@ -179,7 +193,7 @@ ENTRY(csum_partial_copy_generic) bt/s 1f clrt bra 4f - add #2,r6 ! ecx was < 2. Deal with it. + add #2,r6 ! $r6 was < 2. Deal with it. SRC(1: mov.w @r4+,r0 ) DST( mov.w r0,@r5 ) add #2,r5 @@ -273,6 +287,9 @@ DST( mov.w r0,@r5 ) SRC(5: mov.b @r4+,r0 ) DST( mov.b r0,@r5 ) extu.b r0,r0 +#ifndef __LITTLE_ENDIAN__ + shll8 r0 +#endif 6: addc r0,r7 mov #0,r0 addc r0,r7 @@ -299,7 +316,7 @@ DST( mov.b r0,@r5 ) mov.l 8000f,r0 jmp @r0 nop - .balign 4 + .align 2 8000: .long 5000b 6002: @@ -309,7 +326,7 @@ DST( mov.b r0,@r5 ) mov.l 8001f,r0 jmp @r0 nop - .balign 4 + .align 2 8001: .long 5000b .previous diff --git a/arch/sh/mm/cache.c b/arch/sh/mm/cache.c index 5c28ea0397c..895681fc6fb 100644 --- a/arch/sh/mm/cache.c +++ b/arch/sh/mm/cache.c @@ -49,7 +49,7 @@ static struct _cache_system_info cache_system_info; #elif defined(__SH4__) #define CCR 0xff00001c /* Address of Cache Control Register */ #define CCR_CACHE_VAL 0x00000105 /* 8k+16k-byte cache,P1-wb,enable */ -#define CCR_CACHE_INIT 0x0000090d /* 8k+16k-byte cache,CF,P1-wb,enable */ +#define CCR_CACHE_INIT 0x0000090d /* ICI,ICE(8k), OCI,P1-wb,OCE(16k) */ #define CCR_CACHE_ENABLE 0x00000101 #define CACHE_IC_ADDRESS_ARRAY 0xf0000000 @@ -60,143 +60,41 @@ static struct _cache_system_info cache_system_info; #define CACHE_OC_WAY_SHIFT 13 #define CACHE_IC_WAY_SHIFT 13 #define CACHE_OC_ENTRY_SHIFT 5 +#define CACHE_IC_ENTRY_SHIFT 5 #define CACHE_OC_ENTRY_MASK 0x3fe0 +#define CACHE_OC_ENTRY_PHYS_MASK 0x0fe0 #define CACHE_IC_ENTRY_MASK 0x1fe0 +#define CACHE_IC_NUM_ENTRIES 256 #define CACHE_OC_NUM_ENTRIES 512 #define CACHE_OC_NUM_WAYS 1 #define CACHE_IC_NUM_WAYS 1 #endif -/* Write back caches to memory (if needed) and invalidates the caches */ -void cache_flush_area(unsigned long start, unsigned long end) -{ - unsigned long flags; - unsigned long addr, data, v, p; - - start &= ~(L1_CACHE_BYTES-1); - save_and_cli(flags); - jump_to_P2(); - - for (v = start; v < end; v+=L1_CACHE_BYTES) { - p = __pa(v); - addr = CACHE_IC_ADDRESS_ARRAY | - (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; - data = (v&0xfffffc00); /* U=0, V=0 */ - ctrl_outl(data,addr); -#if CACHE_IC_ADDRESS_ARRAY != CACHE_OC_ADDRESS_ARRAY - asm volatile("ocbp %0" - : /* no output */ - : "m" (__m(v))); -#endif - } - back_to_P1(); - restore_flags(flags); -} - -/* Purge (just invalidate, no write back) the caches */ -/* This is expected to work well.. but.. - - On SH7708S, the write-back cache is written back on "purge". - (it's not expected, though). - - It seems that we have no way to just purge (with no write back action) - the cache line. */ -void cache_purge_area(unsigned long start, unsigned long end) -{ - unsigned long flags; - unsigned long addr, data, v, p, j; - - start &= ~(L1_CACHE_BYTES-1); - save_and_cli(flags); - jump_to_P2(); - - for (v = start; v < end; v+=L1_CACHE_BYTES) { - p = __pa(v); - for (j=0; jvm_mm, addr, addr+PAGE_SIZE); } -void __flush_page_to_ram(unsigned long page) -{ /* Page is in physical address */ - /* XXX: for the time being... */ - flush_cache_all(); +/* + * After accessing the memory from kernel space (P1-area), we need to + * write back the cache line, to avoid "alias" issues. + * + * We search the D-cache to see if we have the entries corresponding to + * the page, and if found, write back them. + */ +void flush_page_to_ram(struct page *pg) +{ + unsigned long phys, addr, data, i; + + /* Physical address of this page */ + phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; + + jump_to_P2(); + /* Loop all the D-cache */ + for (i=0; i extern void die(const char *,struct pt_regs *,long); +static void __flush_tlb_page(struct mm_struct *mm, unsigned long page); /* * Ugly, ugly, but the goto's result in better assembly.. @@ -167,12 +168,17 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - { - int fault = handle_mm_fault(mm, vma, address, writeaccess); - if (fault < 0) - goto out_of_memory; - if (!fault) - goto do_sigbus; + switch (handle_mm_fault(mm, vma, address, writeaccess)) { + case 1: + tsk->min_flt++; + break; + case 2: + tsk->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; } up(&mm->mmap_sem); @@ -209,18 +215,20 @@ no_context: printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); else printk(KERN_ALERT "Unable to handle kernel paging request"); - printk(" at virtual address %08lx\n",address); + printk(" at virtual address %08lx\n", address); printk(KERN_ALERT "pc = %08lx\n", regs->pc); - asm volatile("mov.l %1,%0" + asm volatile("mov.l %1, %0" : "=r" (page) : "m" (__m(MMU_TTB))); - page = ((unsigned long *) page)[address >> 22]; - printk(KERN_ALERT "*pde = %08lx\n", page); - if (page & _PAGE_PRESENT) { - page &= PAGE_MASK; - address &= 0x003ff000; - page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; - printk(KERN_ALERT "*pte = %08lx\n", page); + if (page) { + page = ((unsigned long *) page)[address >> 22]; + printk(KERN_ALERT "*pde = %08lx\n", page); + if (page & _PAGE_PRESENT) { + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; + printk(KERN_ALERT "*pte = %08lx\n", page); + } } die("Oops", regs, writeaccess); do_exit(SIGKILL); @@ -261,18 +269,24 @@ void update_mmu_cache(struct vm_area_struct * vma, unsigned long pteaddr; save_and_cli(flags); +#if defined(__SH4__) /* - * We don't need to set PTEH register. - * It's automatically set by the hardware. + * ITLB is not affected by "ldtlb" instruction. + * So, we need to flush the entry by ourselves. */ + __flush_tlb_page(vma->vm_mm, address&PAGE_MASK); +#endif + + /* Set PTEH register */ + pteaddr = (address & MMU_VPN_MASK) | + (vma->vm_mm->context & MMU_CONTEXT_ASID_MASK); + ctrl_outl(pteaddr, MMU_PTEH); + + /* Set PTEL register */ pteval = pte_val(pte); pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */ pteval |= _PAGE_FLAGS_HARDWARE_DEFAULT; /* add default flags */ - /* Set PTEL register */ ctrl_outl(pteval, MMU_PTEL); - /* Set PTEH register */ - pteaddr = (address & MMU_VPN_MASK) | (vma->vm_mm->context & MMU_CONTEXT_ASID_MASK); - ctrl_outl(pteaddr, MMU_PTEH); /* Load the TLB */ asm volatile("ldtlb": /* no output */ : /* no input */ : "memory"); @@ -306,20 +320,6 @@ static void __flush_tlb_page(struct mm_struct *mm, unsigned long page) addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT; data = page | asid; /* VALID bit is off */ ctrl_outl(data, addr); -#if 0 /* Not need when using ASSOC. ??? */ - { - int i; - for (i=0; i<4; i++) { - addr = MMU_ITLB_ADDRESS_ARRAY | (i<<8); - data = ctrl_inl(addr); - data &= ~0x300; - if (data == (page | asid)) { - ctrl_outl(data, addr); - break; - } - } - } -#endif back_to_P1(); #endif if (saved_asid != MMU_NO_ASID) @@ -383,9 +383,16 @@ void flush_tlb_all(void) { unsigned long flags, status; + /* + * Flush all the TLB. + * + * Write to the MMU control register's bit: + * TF-bit for SH-3, TI-bit for SH-4. + * It's same position, bit #2. + */ save_and_cli(flags); status = ctrl_inl(MMUCR); - status |= 0x04; /* Set TF-bit to flush */ - ctrl_outl(status,MMUCR); + status |= 0x04; + ctrl_outl(status, MMUCR); restore_flags(flags); } diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 279afb38f9c..9faf12053f9 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.92 2000/03/29 11:56:48 davem Exp $ +# $Id: config.in,v 1.93 2000/05/22 08:12:19 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -186,6 +186,11 @@ if [ "$CONFIG_NET" = "y" ]; then if [ "$CONFIG_NETDEVICES" = "y" ]; then tristate ' Dummy net driver support' CONFIG_DUMMY tristate ' Bonding driver support' CONFIG_BONDING + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_NETLINK" = "y" ]; then + tristate ' Ethertap network tap (EXPERIMENTAL)' CONFIG_ETHERTAP + fi + fi tristate ' PPP (point-to-point) support' CONFIG_PPP if [ ! "$CONFIG_PPP" = "n" ]; then dep_tristate ' PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index fe83b081ac7..f63f542bad5 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.53 2000/03/31 04:06:19 davem Exp $ +# $Id: Makefile,v 1.54 2000/05/12 23:51:24 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -59,13 +59,11 @@ check_asm: dummy @echo "#ifndef CONFIG_SMP" >> asm_offsets.h @echo "" >> asm_offsets.h @echo "#include " > tmp.c - @echo "#undef __SMP__" >> tmp.c @echo "#undef CONFIG_SMP" >> tmp.c @echo "#include " >> tmp.c $(CPP) $(CPPFLAGS) tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c @echo "#include " >> check_asm.c - @echo "#undef __SMP__" >> check_asm.c @echo "#undef CONFIG_SMP" >> check_asm.c @echo "#include " >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 8141d0e2497..61518872f27 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.163 1999/11/19 04:11:24 davem Exp $ +/* $Id: entry.S,v 1.164 2000/05/09 17:40:13 davem Exp $ * arch/sparc/kernel/entry.S: Sparc trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index 30df8a0f2d6..713dff79911 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.102 2000/01/29 01:08:54 anton Exp $ +/* $Id: head.S,v 1.103 2000/05/09 17:40:13 davem Exp $ * head.S: The initial boot code for the Sparc port of Linux. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index 5eaa8beda8e..77a6f47696c 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.102 2000/02/25 05:44:35 davem Exp $ +/* $Id: irq.c,v 1.103 2000/05/09 17:40:13 davem Exp $ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the * Sparc the IRQ's are basically 'cast in stone' * and you are supposed to probe the prom's device diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 96cd44aea56..e18d91e0edc 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.146 2000/03/01 02:53:27 davem Exp $ +/* $Id: process.c,v 1.147 2000/05/09 17:40:13 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 25ea5cb8dc7..5398a93818a 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.117 2000/03/27 12:14:54 davem Exp $ +/* $Id: setup.c,v 1.118 2000/05/09 17:40:13 davem Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index 41a8a68b75e..e75d5d00096 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.102 2000/04/08 02:11:36 davem Exp $ +/* $Id: signal.c,v 1.103 2000/05/09 17:40:13 davem Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index d4944b5027c..186f1282520 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.96 2000/03/16 09:12:49 jj Exp $ +/* $Id: sparc_ksyms.c,v 1.97 2000/05/09 17:40:13 davem Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 7d8fab67f95..192a6bb037c 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -1,4 +1,4 @@ -/* $Id: sun4d_irq.c,v 1.24 1999/12/27 06:08:34 anton Exp $ +/* $Id: sun4d_irq.c,v 1.25 2000/05/09 17:40:13 davem Exp $ * arch/sparc/kernel/sun4d_irq.c: * SS1000/SC2000 interrupt handling. * diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index c889af1d891..0dba9b1ca77 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.122 2000/04/27 02:49:03 davem Exp $ +/* $Id: sys_sunos.c,v 1.123 2000/05/22 07:29:39 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -634,7 +634,6 @@ struct sunos_nfs_mount_args { extern dev_t get_unnamed_dev(void); extern void put_unnamed_dev(dev_t); -extern asmlinkage long do_sys_mount(char *, char *, char *, int, void *); extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); extern asmlinkage int sys_socket(int family, int type, int protocol); extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); @@ -754,7 +753,7 @@ static int sunos_nfs_mount(char *dir_name, int linux_flags, void *data) linux_nfs_mount.hostname [255] = 0; putname (the_name); - return do_sys_mount ("", dir_name, "nfs", linux_flags, &linux_nfs_mount); + return do_mount ("", dir_name, "nfs", linux_flags, &linux_nfs_mount); } asmlinkage int @@ -814,7 +813,7 @@ sunos_mount(char *type, char *dir, int flags, void *data) ret = PTR_ERR(dev_fname); if (IS_ERR(dev_fname)) goto out2; - ret = do_sys_mount(dev_fname, dir_page, type_page, linux_flags, NULL); + ret = do_mount(dev_fname, dir_page, type_page, linux_flags, NULL); if (dev_fname) putname(dev_fname); out2: diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index 31fcc6166ad..05bb225d78e 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.54 2000/04/13 08:14:30 anton Exp $ +/* $Id: time.c,v 1.55 2000/05/09 17:40:13 davem Exp $ * linux/arch/sparc/kernel/time.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c index 4717d9bd392..ca74c09fcec 100644 --- a/arch/sparc/kernel/traps.c +++ b/arch/sparc/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.61 2000/01/21 11:38:41 jj Exp $ +/* $Id: traps.c,v 1.62 2000/05/09 17:40:13 davem Exp $ * arch/sparc/kernel/traps.c * * Copyright 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/lib/rwsem.S b/arch/sparc/lib/rwsem.S index ebbfd325505..98b757cb67c 100644 --- a/arch/sparc/lib/rwsem.S +++ b/arch/sparc/lib/rwsem.S @@ -1,4 +1,4 @@ -/* $Id: rwsem.S,v 1.4 2000/02/13 07:59:39 anton Exp $ +/* $Id: rwsem.S,v 1.5 2000/05/09 17:40:13 davem Exp $ * Assembly part of rw semaphores. * * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c index 72b8cff3db6..3447b839ead 100644 --- a/arch/sparc/mm/btfixup.c +++ b/arch/sparc/mm/btfixup.c @@ -1,4 +1,4 @@ -/* $Id: btfixup.c,v 1.9 1999/12/27 06:30:02 anton Exp $ +/* $Id: btfixup.c,v 1.10 2000/05/09 17:40:13 davem Exp $ * btfixup.c: Boot time code fixup and relocator, so that * we can get rid of most indirect calls to achieve single * image sun4c and srmmu kernel. diff --git a/arch/sparc/mm/hypersparc.S b/arch/sparc/mm/hypersparc.S index 5c1c0143dcc..10812273b5b 100644 --- a/arch/sparc/mm/hypersparc.S +++ b/arch/sparc/mm/hypersparc.S @@ -1,4 +1,4 @@ -/* $Id: hypersparc.S,v 1.14 1999/08/14 03:51:47 anton Exp $ +/* $Id: hypersparc.S,v 1.15 2000/05/09 17:40:13 davem Exp $ * hypersparc.S: High speed Hypersparc mmu/cache operations. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index 87e30eda126..3f350004675 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.84 2000/03/15 23:26:26 anton Exp $ +/* $Id: init.c,v 1.85 2000/05/09 17:40:13 davem Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 1fa50e946d9..0b46e7d25fe 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.208 2000/02/14 04:52:33 jj Exp $ +/* $Id: srmmu.c,v 1.209 2000/05/09 17:40:13 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index 9671e7ee771..b4b9a82768e 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.191 2000/04/08 02:11:41 davem Exp $ +/* $Id: sun4c.c,v 1.192 2000/05/09 17:40:13 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/mm/swift.S b/arch/sparc/mm/swift.S index f8e2635bca8..2d952f5ad31 100644 --- a/arch/sparc/mm/swift.S +++ b/arch/sparc/mm/swift.S @@ -1,4 +1,4 @@ -/* $Id: swift.S,v 1.4 2000/02/12 03:08:47 zaitcev Exp $ +/* $Id: swift.S,v 1.5 2000/05/09 17:40:13 davem Exp $ * swift.S: MicroSparc-II mmu/cache operations. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) diff --git a/arch/sparc/mm/tsunami.S b/arch/sparc/mm/tsunami.S index 932713eef66..1eb8fd6da20 100644 --- a/arch/sparc/mm/tsunami.S +++ b/arch/sparc/mm/tsunami.S @@ -1,4 +1,4 @@ -/* $Id: tsunami.S,v 1.3 1999/10/09 05:32:19 zaitcev Exp $ +/* $Id: tsunami.S,v 1.4 2000/05/09 17:40:13 davem Exp $ * tsunami.S: High speed MicroSparc-I mmu/cache operations. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S index 4e74f34e4ce..4e083225799 100644 --- a/arch/sparc/mm/viking.S +++ b/arch/sparc/mm/viking.S @@ -1,4 +1,4 @@ -/* $Id: viking.S,v 1.15 2000/01/15 00:51:36 anton Exp $ +/* $Id: viking.S,v 1.16 2000/05/09 17:40:13 davem Exp $ * viking.S: High speed Viking cache/mmu operations * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 4c53b12a16a..d27061c0367 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.109 2000/05/02 06:35:59 davem Exp $ +# $Id: config.in,v 1.112 2000/05/22 08:12:19 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -204,6 +204,11 @@ if [ "$CONFIG_NET" = "y" ]; then if [ "$CONFIG_NETDEVICES" = "y" ]; then tristate ' Dummy net driver support' CONFIG_DUMMY tristate ' Bonding driver support' CONFIG_BONDING + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_NETLINK" = "y" ]; then + tristate ' Ethertap network tap (EXPERIMENTAL)' CONFIG_ETHERTAP + fi + fi tristate ' PPP (point-to-point) support' CONFIG_PPP if [ ! "$CONFIG_PPP" = "n" ]; then dep_tristate ' PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index b57e662da6b..edb6b05718a 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.53 2000/03/31 04:06:22 davem Exp $ +# $Id: Makefile,v 1.54 2000/05/12 23:51:24 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index b3fade43ee4..5e855ad2c78 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.64 2000/03/06 22:33:42 davem Exp $ +/* $Id: head.S,v 1.65 2000/05/09 17:40:13 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 4eb22ea4738..6c00ba2f69c 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.89 2000/05/06 10:38:42 davem Exp $ +/* $Id: ioctl32.c,v 1.90 2000/05/22 07:29:39 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) @@ -2400,39 +2400,6 @@ static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioc return err; } -typedef struct blkelv_ioctl32_arg_s { - u32 queue_ID; - int read_latency; - int write_latency; - int max_bomb_segments; -} blkelv_ioctl32_arg_t; - -static int do_blkelv_ioctl(unsigned int fd, unsigned int cmd, blkelv_ioctl32_arg_t *arg) -{ - blkelv_ioctl_arg_t b; - int err; - mm_segment_t old_fs = get_fs(); - - if (cmd == BLKELVSET) { - err = get_user((long)b.queue_ID, &arg->queue_ID); - err |= __get_user(b.read_latency, &arg->read_latency); - err |= __get_user(b.write_latency, &arg->write_latency); - err |= __get_user(b.max_bomb_segments, &arg->max_bomb_segments); - if (err) return err; - } - set_fs (KERNEL_DS); - err = sys_ioctl(fd, cmd, (unsigned long)&b); - set_fs (old_fs); - if (cmd == BLKELVGET && !err) { - err = put_user((long)b.queue_ID, &arg->queue_ID); - err |= __put_user(b.read_latency, &arg->read_latency); - err |= __put_user(b.write_latency, &arg->write_latency); - err |= __put_user(b.max_bomb_segments, &arg->max_bomb_segments); - if (err) return err; - } - return err; -} - static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) { return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg); @@ -2994,6 +2961,9 @@ COMPATIBLE_IOCTL(LV_SET_ACCESS) COMPATIBLE_IOCTL(LV_SET_STATUS) COMPATIBLE_IOCTL(LV_SET_ALLOCATION) #endif /* LVM */ +/* elevator */ +COMPATIBLE_IOCTL(BLKELVGET) +COMPATIBLE_IOCTL(BLKELVSET) /* And these ioctls need translation */ HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) @@ -3040,8 +3010,6 @@ HANDLE_IOCTL(0x1260, broken_blkgetsize) HANDLE_IOCTL(BLKFRAGET, w_long) HANDLE_IOCTL(BLKSECTGET, w_long) HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans) -HANDLE_IOCTL(BLKELVGET, do_blkelv_ioctl) -HANDLE_IOCTL(BLKELVSET, do_blkelv_ioctl) HANDLE_IOCTL(FBIOPUTCMAP32, fbiogetputcmap) HANDLE_IOCTL(FBIOGETCMAP32, fbiogetputcmap) HANDLE_IOCTL(FBIOSCURSOR32, fbiogscursor) diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 16ec61db093..eb6f789f668 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.86 2000/04/15 06:02:50 davem Exp $ +/* $Id: irq.c,v 1.87 2000/05/09 17:40:13 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index f691e9ca572..060d1c477b9 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.106 2000/04/15 06:02:50 davem Exp $ +/* $Id: process.c,v 1.107 2000/05/09 17:40:14 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 824097fa0d7..0f3e5c0691d 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.53 2000/03/15 14:42:52 jj Exp $ +/* $Id: setup.c,v 1.54 2000/05/09 17:40:14 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 7bf31c1045b..12a87244704 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.83 2000/04/19 08:38:25 davem Exp $ +/* $Id: sparc64_ksyms.c,v 1.84 2000/05/09 17:40:14 davem Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 657ceacaa92..12592b747d6 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.146 2000/05/09 04:48:34 davem Exp $ +/* $Id: sys_sparc32.c,v 1.147 2000/05/22 07:29:40 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -1742,9 +1742,6 @@ static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) return 0; } -extern long do_sys_mount(char * dev_page, char * dir_page, char * type_page, - unsigned long new_flags, char * data_page); - #define SMBFS_NAME "smbfs" #define NCPFS_NAME "ncpfs" @@ -1784,7 +1781,7 @@ asmlinkage int sys32_mount(char *dev_name, char *dir_name, char *type, unsigned goto dev_out; if (!is_smb && !is_ncp) { - err = do_sys_mount((char*)dev_page, (char*)dir_page, + err = do_mount((char*)dev_page, (char*)dir_page, (char*)type_page, new_flags, (char*)data_page); } else { if (is_ncp) @@ -1792,7 +1789,7 @@ asmlinkage int sys32_mount(char *dev_name, char *dir_name, char *type, unsigned else do_smb_super_data_conv((void *)data_page); - err = do_sys_mount((char*)dev_page, (char*)dir_page, + err = do_mount((char*)dev_page, (char*)dir_page, (char*)type_page, new_flags, (char*)data_page); } free_page(dir_page); diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 8fbb1637464..7aeb4781faf 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.46 2000/04/27 02:49:03 davem Exp $ +/* $Id: sys_sunos32.c,v 1.47 2000/05/22 07:29:40 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -598,7 +598,6 @@ struct sunos_nfs_mount_args { char *netname; /* server's netname */ }; -extern long do_sys_mount(const char *, const char *, char *, int, void *); extern dev_t get_unnamed_dev(void); extern void put_unnamed_dev(dev_t); extern asmlinkage int sys_mount(char *, char *, char *, unsigned long, void *); @@ -727,7 +726,7 @@ static int sunos_nfs_mount(char *dir_name, int linux_flags, void *data) linux_nfs_mount.hostname [255] = 0; putname (the_name); - return do_sys_mount ("", dir_name, "nfs", linux_flags, &linux_nfs_mount); + return do_mount ("", dir_name, "nfs", linux_flags, &linux_nfs_mount); } /* XXXXXXXXXXXXXXXXXXXX */ @@ -787,7 +786,7 @@ sunos_mount(char *type, char *dir, int flags, void *data) ret = PTR_ERR(dev_fname); if (IS_ERR(dev_fname)) goto out2; - ret = do_sys_mount(dev_fname, dir_page, type_page, linux_flags, NULL); + ret = do_mount(dev_fname, dir_page, type_page, linux_flags, NULL); if (dev_fname) putname(dev_fname); out2: diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index eb02486f5a5..9c20a275218 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.72 2000/04/13 07:30:34 jj Exp $ +/* $Id: systbls.S,v 1.73 2000/05/10 14:23:39 jj Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -83,7 +83,7 @@ sys_call_table: /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_chown, sys_mknod /*15*/ .word sys_chmod, sys_lchown, sparc_brk, sys_perfctr, sys_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid, sys_getuid -/*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall +/*25*/ .word sys_nis_syscall, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall /*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice .word sys_nis_syscall, sys_sync, sys_kill, sys_newstat, sys_sendfile /*40*/ .word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_nis_syscall @@ -124,7 +124,7 @@ sys_call_table: .word sys_ipc, sys_nis_syscall, sys_clone, sys_nis_syscall, sys_adjtimex /*220*/ .word sys_nis_syscall, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid .word sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid -/*230*/ .word sys_select, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall +/*230*/ .word sys_select, sys_nis_syscall, sys_nis_syscall, sys_stime, sys_nis_syscall .word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall /*240*/ .word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler .word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index c630356e127..6a84d4c29bc 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.25 2000/04/13 05:29:44 davem Exp $ +/* $Id: time.c,v 1.26 2000/05/09 17:40:14 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index cf20ffc1a39..7b5d3261457 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.65 2000/01/21 11:39:01 jj Exp $ +/* $Id: traps.c,v 1.66 2000/05/09 17:40:14 davem Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 027d2e124de..b1081b9fe84 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.30 1999/12/01 23:52:03 davem Exp $ +/* $Id: ttable.S,v 1.31 2000/05/09 17:40:14 davem Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/lib/debuglocks.c b/arch/sparc64/lib/debuglocks.c index d1516a40ba1..f8ebf129f1e 100644 --- a/arch/sparc64/lib/debuglocks.c +++ b/arch/sparc64/lib/debuglocks.c @@ -1,4 +1,4 @@ -/* $Id: debuglocks.c,v 1.4 2000/01/31 04:59:10 davem Exp $ +/* $Id: debuglocks.c,v 1.5 2000/05/09 17:40:14 davem Exp $ * debuglocks.c: Debugging versions of SMP locking primitives. * * Copyright (C) 1998 David S. Miller (davem@redhat.com) diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 997e1a1b5b8..88ac931039a 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.151 2000/04/26 17:09:32 davem Exp $ +/* $Id: init.c,v 1.152 2000/05/09 17:40:14 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S index a12bfb4d01e..90cc898ff42 100644 --- a/arch/sparc64/mm/ultra.S +++ b/arch/sparc64/mm/ultra.S @@ -1,4 +1,4 @@ -/* $Id: ultra.S,v 1.42 2000/05/05 18:47:41 davem Exp $ +/* $Id: ultra.S,v 1.43 2000/05/09 17:40:14 davem Exp $ * ultra.S: Don't expand these all over the place... * * Copyright (C) 1997, 2000 David S. Miller (davem@redhat.com) diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c index 90c77ea744a..a9ee22cd7e8 100644 --- a/arch/sparc64/prom/misc.c +++ b/arch/sparc64/prom/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.17 2000/04/15 06:02:50 davem Exp $ +/* $Id: misc.c,v 1.18 2000/05/09 17:40:14 davem Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index 56f1280f8ff..eaa8fed3298 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.26 2000/04/14 09:59:02 davem Exp $ +/* $Id: misc.c,v 1.27 2000/05/09 17:40:14 davem Exp $ * misc.c: Miscelaneous syscall emulation for Solaris * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) diff --git a/drivers/acorn/net/Makefile b/drivers/acorn/net/Makefile index 27811e92ff4..095a43cef8e 100644 --- a/drivers/acorn/net/Makefile +++ b/drivers/acorn/net/Makefile @@ -3,7 +3,7 @@ # Makefile for the Acorn ethercard network device drivers # -L_TARGET := acorn-net.a +O_TARGET := acorn-net.o MOD_LIST_NAME := ACORN_NET_MODULES obj-y := @@ -11,11 +11,11 @@ obj-m := obj-n := obj- := -obj-$(CONFIG_ARM_ETHER1) += ether1.o -obj-$(CONFIG_ARM_ETHER3) += ether3.o obj-$(CONFIG_ARM_ETHERH) += etherh.o +obj-$(CONFIG_ARM_ETHER3) += ether3.o +obj-$(CONFIG_ARM_ETHER1) += ether1.o -L_OBJS := $(obj-y) +O_OBJS := $(obj-y) M_OBJS := $(obj-m) include $(TOPDIR)/Rules.make diff --git a/drivers/acorn/net/ether1.c b/drivers/acorn/net/ether1.c index 65673b7f443..d2a5ca052ab 100644 --- a/drivers/acorn/net/ether1.c +++ b/drivers/acorn/net/ether1.c @@ -29,6 +29,7 @@ * TDR time-distance. * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1 * 1.06 RMK 10/02/2000 Updated for 2.3.43 + * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8 */ #include @@ -74,7 +75,7 @@ static void ether1_setmulticastlist(struct net_device *dev); static void ether1_timeout(struct net_device *dev); /* ------------------------------------------------------------------------- */ -static char *version = "ether1 ethernet driver (c) 2000 Russell King v1.06\n"; +static char *version = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; #define BUS_16 16 #define BUS_8 8 @@ -620,95 +621,6 @@ ether1_init_for_open (struct net_device *dev) return failures ? 1 : 0; } -static int __init -ether1_probe1(struct net_device *dev) -{ - static unsigned int version_printed = 0; - struct ether1_priv *priv; - int i; - - if (!dev->priv) - dev->priv = kmalloc (sizeof (struct ether1_priv), GFP_KERNEL); - - if (!dev->priv) - return 1; - - priv = (struct ether1_priv *)dev->priv; - memset (priv, 0, sizeof (struct ether1_priv)); - - if ((priv->bus_type = ether1_reset (dev)) == 0) { - kfree (dev->priv); - return 1; - } - - if (net_debug && version_printed++ == 0) - printk (KERN_INFO "%s", version); - - request_region (dev->base_addr, 16, "ether1"); - request_region (dev->base_addr + 0x800, 4096, "ether1(ram)"); - - printk (KERN_INFO "%s: ether1 at %lx, IRQ%d, ether address ", - dev->name, dev->base_addr, dev->irq); - - for (i = 0; i < 6; i++) - printk (i==0?" %02x":i==5?":%02x\n":":%02x", dev->dev_addr[i]); - - if (ether1_init_2 (dev)) { - kfree (dev->priv); - return 1; - } - - dev->open = ether1_open; - dev->stop = ether1_close; - dev->hard_start_xmit = ether1_sendpacket; - dev->get_stats = ether1_getstats; - dev->set_multicast_list = ether1_setmulticastlist; - dev->tx_timeout = ether1_timeout; - dev->watchdog_timeo = 5 * HZ / 100; - - /* Fill in the fields of the device structure with ethernet values */ - ether_setup (dev); - - return 0; -} - -/* ------------------------------------------------------------------------- */ - -static void __init -ether1_addr(struct net_device *dev) -{ - int i; - - for (i = 0; i < 6; i++) - dev->dev_addr[i] = inb (IDPROM_ADDRESS + i); -} - -int __init -ether1_probe(struct net_device *dev) -{ -#ifndef MODULE - struct expansion_card *ec; - - if (!dev) - return ENODEV; - - ecard_startfind (); - if ((ec = ecard_find (0, ether1_cids)) == NULL) - return ENODEV; - - dev->base_addr = ecard_address (ec, ECARD_IOC, ECARD_FAST); - dev->irq = ec->irq; - - ecard_claim (ec); - -#endif - ether1_addr (dev); - - if (ether1_probe1 (dev) == 0) - return 0; - return ENODEV; -} - /* ------------------------------------------------------------------------- */ static int @@ -1087,66 +999,118 @@ ether1_setmulticastlist (struct net_device *dev) /* ------------------------------------------------------------------------- */ -#ifdef MODULE +static void __init ether1_banner(void) +{ + static unsigned int version_printed = 0; -static struct ether_dev { - struct expansion_card *ec; - char name[9]; - struct net_device dev; -} ether_devs[MAX_ECARDS]; + if (net_debug && version_printed++ == 0) + printk (KERN_INFO "%s", version); +} -int -init_module (void) +static struct net_device * __init ether1_init_one(struct expansion_card *ec) { - struct expansion_card *ec; - int i, ret = -ENODEV; - - memset(ether_devs, 0, sizeof(ether_devs)); + struct net_device *dev; + struct ether1_priv *priv; + int i; - ecard_startfind (); - ec = ecard_find(0, ether1_cids); - i = 0; + ether1_banner(); - while (ec && i < MAX_ECARDS) { - ecard_claim(ec); + ecard_claim(ec); + + dev = init_etherdev(NULL, sizeof(struct ether1_priv)); + if (!dev) + goto out; - ether_devs[i].ec = ec; - ether_devs[i].dev.irq = ec->irq; - ether_devs[i].dev.base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); - ether_devs[i].dev.init = ether1_probe; - ether_devs[i].dev.name = ether_devs[i].name; + dev->base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); + dev->irq = ec->irq; - ret = register_netdev(ðer_devs[i].dev); + /* + * these will not fail - the nature of the bus ensures this + */ + request_region(dev->base_addr, 16, dev->name); + request_region(dev->base_addr + 0x800, 4096, dev->name); - if (ret) { - ecard_release(ec); - ether_devs[i].ec = NULL; - break; - } + priv = (struct ether1_priv *)dev->priv; + if ((priv->bus_type = ether1_reset(dev)) == 0) + goto free_dev; - i += 1; - ec = ecard_find(0, ether1_cids); + printk(KERN_INFO "%s: ether1 at %lx, IRQ%d, ether address ", + dev->name, dev->base_addr, dev->irq); + + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = inb(IDPROM_ADDRESS + i); + printk (i==0?" %02x":i==5?":%02x\n":":%02x", dev->dev_addr[i]); } - return i != 0 ? 0 : ret; + if (ether1_init_2(dev)) + goto free_dev; + + dev->open = ether1_open; + dev->stop = ether1_close; + dev->hard_start_xmit = ether1_sendpacket; + dev->get_stats = ether1_getstats; + dev->set_multicast_list = ether1_setmulticastlist; + dev->tx_timeout = ether1_timeout; + dev->watchdog_timeo = 5 * HZ / 100; + return 0; + +free_dev: + release_region(dev->base_addr, 16); + release_region(dev->base_addr + 0x800, 4096); + unregister_netdev(dev); + kfree(dev); +out: + ecard_release(ec); + return dev; } -void -cleanup_module (void) +static struct expansion_card *e_card[MAX_ECARDS]; +static struct net_device *e_dev[MAX_ECARDS]; + +static int __init ether1_init(void) { - int i; + int i, ret = -ENODEV; + + ecard_startfind(); for (i = 0; i < MAX_ECARDS; i++) { - if (ether_devs[i].ec) { - unregister_netdev(ðer_devs[i].dev); + struct expansion_card *ec; + struct net_device *dev; - release_region(ether_devs[i].dev.base_addr, 16); - release_region(ether_devs[i].dev.base_addr + 0x800, 4096); + ec = ecard_find(0, ether1_cids); + if (!ec) + break; - ecard_release(ether_devs[i].ec); + dev = ether1_init_one(ec); + if (!dev) + break; + + e_card[i] = ec; + e_dev[i] = dev; + ret = 0; + } + + return ret; +} - ether_devs[i].ec = NULL; +static void __exit ether1_exit(void) +{ + int i; + + for (i = 0; i < MAX_ECARDS; i++) { + if (e_dev[i]) { + unregister_netdev(e_dev[i]); + release_region(e_dev[i]->base_addr, 16); + release_region(e_dev[i]->base_addr + 0x800, 4096); + kfree(e_dev[i]); + e_dev[i] = NULL; + } + if (e_card[i]) { + ecard_release(e_card[i]); + e_card[i] = NULL; } } } -#endif /* MODULE */ + +module_init(ether1_init); +module_exit(ether1_exit); diff --git a/drivers/acorn/net/ether3.c b/drivers/acorn/net/ether3.c index 03cc064c4ef..11e3824810c 100644 --- a/drivers/acorn/net/ether3.c +++ b/drivers/acorn/net/ether3.c @@ -37,9 +37,10 @@ * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy * hardware. * 1.16 RMK 10/02/2000 Updated for 2.3.43 + * 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8 */ -static char *version = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.16\n"; +static char *version = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; #include #include @@ -78,7 +79,6 @@ static const card_ids __init ether3_cids[] = { static void ether3_setmulticastlist(struct net_device *dev); static int ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt); static void ether3_tx(struct net_device *dev, struct dev_priv *priv); -static int ether3_probe1 (struct net_device *dev); static int ether3_open (struct net_device *dev); static int ether3_sendpacket (struct sk_buff *skb, struct net_device *dev); static void ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs); @@ -407,132 +407,6 @@ ether3_probe_bus_16(struct net_device *dev, int val) } /* - * This is the real probe routine. - */ -static int __init -ether3_probe1(struct net_device *dev) -{ - static unsigned version_printed = 0; - struct dev_priv *priv; - unsigned int i, bus_type, error = ENODEV; - const char *name = "ether3"; - - if (net_debug && version_printed++ == 0) - printk(version); - - if (!dev->priv) { - dev->priv = kmalloc(sizeof (struct dev_priv), GFP_KERNEL); - if (!dev->priv) { - printk(KERN_ERR "ether3_probe1: no memory\n"); - return -ENOMEM; - } - } - - priv = (struct dev_priv *) dev->priv; - memset(priv, 0, sizeof(struct dev_priv)); - - request_region(dev->base_addr, 128, name); - - /* Reset card... - */ - ether3_outb(0x80, REG_CONFIG2 + 1); - bus_type = BUS_UNKNOWN; - udelay(4); - - /* Test using Receive Pointer (16-bit register) to find out - * how the ether3 is connected to the bus... - */ - if (ether3_probe_bus_8(dev, 0x100) && - ether3_probe_bus_8(dev, 0x201)) - bus_type = BUS_8; - - if (bus_type == BUS_UNKNOWN && - ether3_probe_bus_16(dev, 0x101) && - ether3_probe_bus_16(dev, 0x201)) - bus_type = BUS_16; - - switch (bus_type) { - case BUS_UNKNOWN: - printk(KERN_ERR "%s: unable to identify bus width\n", dev->name); - goto failed; - - case BUS_8: - printk(KERN_ERR "%s: %s found, but is an unsupported " - "8-bit card\n", dev->name, name); - goto failed; - - default: - break; - } - - printk("%s: %s at %lx, IRQ%d, ether address ", - dev->name, name, dev->base_addr, dev->irq); - for (i = 0; i < 6; i++) - printk(i == 5 ? "%2.2x\n" : "%2.2x:", dev->dev_addr[i]); - - if (ether3_init_2(dev)) - goto failed; - - dev->open = ether3_open; - dev->stop = ether3_close; - dev->hard_start_xmit = ether3_sendpacket; - dev->get_stats = ether3_getstats; - dev->set_multicast_list = ether3_setmulticastlist; - dev->tx_timeout = ether3_timeout; - dev->watchdog_timeo = 5 * HZ / 100; - - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); - - return 0; - -failed: - kfree(dev->priv); - dev->priv = NULL; - release_region(dev->base_addr, 128); - return error; -} - -static void __init -ether3_get_dev(struct net_device *dev, struct expansion_card *ec) -{ - ecard_claim(ec); - - dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); - dev->irq = ec->irq; - - if (ec->cid.manufacturer == MANU_ANT && - ec->cid.product == PROD_ANT_ETHERB) { - dev->base_addr += 0x200; - } - - ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); - ec->irqmask = 0xf0; - - ether3_addr(dev->dev_addr, ec); -} - -#ifndef MODULE -int __init -ether3_probe(struct net_device *dev) -{ - struct expansion_card *ec; - - if (!dev) - return ENODEV; - - ecard_startfind(); - - if ((ec = ecard_find(0, ether3_cids)) == NULL) - return ENODEV; - - ether3_get_dev(dev, ec); - - return ether3_probe1(dev); -} -#endif - -/* * Open/initialize the board. This is called (in the current kernel) * sometime after booting when the 'ifconfig' program is run. * @@ -903,63 +777,163 @@ ether3_tx(struct net_device *dev, struct dev_priv *priv) } } -#ifdef MODULE +static void __init ether3_banner(void) +{ + static unsigned version_printed = 0; -static struct ether_dev { - struct expansion_card *ec; - char name[9]; - struct net_device dev; -} ether_devs[MAX_ECARDS]; + if (net_debug && version_printed++ == 0) + printk(version); +} -int -init_module(void) +static const char * __init +ether3_get_dev(struct net_device *dev, struct expansion_card *ec) { - struct expansion_card *ec; - int i, ret = -ENODEV; + const char *name = "ether3"; + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + dev->irq = ec->irq; + + if (ec->cid.manufacturer == MANU_ANT && + ec->cid.product == PROD_ANT_ETHERB) { + dev->base_addr += 0x200; + name = "etherb"; + } - memset(ether_devs, 0, sizeof(ether_devs)); + ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); + ec->irqmask = 0xf0; - ecard_startfind (); - ec = ecard_find(0, ether3_cids); - i = 0; + ether3_addr(dev->dev_addr, ec); - while (ec && i < MAX_ECARDS) { - ecard_claim(ec); + return name; +} - ether_devs[i].ec = ec; - ether_devs[i].dev.init = ether3_probe1; - ether_devs[i].dev.name = ether_devs[i].name; - ether3_get_dev(ðer_devs[i].dev, ec); +static struct net_device * __init ether3_init_one(struct expansion_card *ec) +{ + struct net_device *dev; + struct dev_priv *priv; + const char *name; + int i, bus_type; - ret = register_netdev(ðer_devs[i].dev); + ether3_banner(); - if (ret) { - ecard_release(ec); - ether_devs[i].ec = NULL; - } else - i += 1; + ecard_claim(ec); - ec = ecard_find(0, ether3_cids); + dev = init_etherdev(NULL, sizeof(struct dev_priv)); + if (!dev) + goto out; + + name = ether3_get_dev(dev, ec); + + /* + * this will not fail - the nature of the bus ensures this + */ + request_region(dev->base_addr, 128, dev->name); + + priv = (struct dev_priv *) dev->priv; + + /* Reset card... + */ + ether3_outb(0x80, REG_CONFIG2 + 1); + bus_type = BUS_UNKNOWN; + udelay(4); + + /* Test using Receive Pointer (16-bit register) to find out + * how the ether3 is connected to the bus... + */ + if (ether3_probe_bus_8(dev, 0x100) && + ether3_probe_bus_8(dev, 0x201)) + bus_type = BUS_8; + + if (bus_type == BUS_UNKNOWN && + ether3_probe_bus_16(dev, 0x101) && + ether3_probe_bus_16(dev, 0x201)) + bus_type = BUS_16; + + switch (bus_type) { + case BUS_UNKNOWN: + printk(KERN_ERR "%s: unable to identify bus width\n", dev->name); + goto failed; + + case BUS_8: + printk(KERN_ERR "%s: %s found, but is an unsupported " + "8-bit card\n", dev->name, name); + goto failed; + + default: + break; } - return i != 0 ? 0 : ret; + printk("%s: %s at %lx, IRQ%d, ether address ", + dev->name, name, dev->base_addr, dev->irq); + for (i = 0; i < 6; i++) + printk(i == 5 ? "%2.2x\n" : "%2.2x:", dev->dev_addr[i]); + + if (ether3_init_2(dev)) + goto failed; + + dev->open = ether3_open; + dev->stop = ether3_close; + dev->hard_start_xmit = ether3_sendpacket; + dev->get_stats = ether3_getstats; + dev->set_multicast_list = ether3_setmulticastlist; + dev->tx_timeout = ether3_timeout; + dev->watchdog_timeo = 5 * HZ / 100; + return 0; + +failed: + release_region(dev->base_addr, 128); + unregister_netdev(dev); + kfree(dev); +out: + ecard_release(ec); + return NULL; } -void -cleanup_module(void) +static struct expansion_card *e_card[MAX_ECARDS]; +static struct net_device *e_dev[MAX_ECARDS]; + +static int ether3_init(void) { - int i; + int i, ret = -ENODEV; + + ecard_startfind(); for (i = 0; i < MAX_ECARDS; i++) { - if (ether_devs[i].ec) { - unregister_netdev(ðer_devs[i].dev); + struct net_device *dev; + struct expansion_card *ec; - release_region(ether_devs[i].dev.base_addr, 128); + ec = ecard_find(0, ether3_cids); + if (!ec) + break; - ecard_release(ether_devs[i].ec); + dev = ether3_init_one(ec); + if (!dev) + break; + + e_card[i] = ec; + e_dev[i] = dev; + ret = 0; + } + + return ret; +} - ether_devs[i].ec = NULL; +static void ether3_exit(void) +{ + int i; + + for (i = 0; i < MAX_ECARDS; i++) { + if (e_dev[i]) { + unregister_netdev(e_dev[i]); + release_region(e_dev[i]->base_addr, 128); + kfree(e_dev[i]); + e_dev[i] = NULL; + } + if (e_card[i]) { + ecard_release(e_card[i]); + e_card[i] = NULL; } } } -#endif /* MODULE */ + +module_init(ether3_init); +module_exit(ether3_exit); diff --git a/drivers/acorn/net/etherh.c b/drivers/acorn/net/etherh.c index 71dcbbdbca1..96f51c68eba 100644 --- a/drivers/acorn/net/etherh.c +++ b/drivers/acorn/net/etherh.c @@ -14,6 +14,7 @@ * 23-11-1997 RMK 1.04 Added media autodetection * 16-04-1998 RMK 1.05 Improved media autodetection * 10-02-2000 RMK 1.06 Updated for 2.3.43 + * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8 * * Insmod Module Parameters * ------------------------ @@ -62,7 +63,8 @@ static const card_ids __init etherh_cids[] = { MODULE_AUTHOR("Russell King"); MODULE_DESCRIPTION("i3 EtherH driver"); -static char *version = "etherh [500/600/600A] ethernet driver (c) 2000 R.M.King v1.06\n"; +static char *version __initdata = + "etherh [500/600/600A] ethernet driver (c) 2000 R.M.King v1.07\n"; #define ETHERH500_DATAPORT 0x200 /* MEMC */ #define ETHERH500_NS8390 0x000 /* MEMC */ @@ -81,34 +83,10 @@ static char *version = "etherh [500/600/600A] ethernet driver (c) 2000 R.M.King /* --------------------------------------------------------------------------- */ -/* - * Read the ethernet address string from the on board rom. - * This is an ascii string... - */ -static int __init -etherh_addr(char *addr, struct expansion_card *ec) -{ - struct in_chunk_dir cd; - char *s; - - if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { - int i; - for (i = 0; i < 6; i++) { - addr[i] = simple_strtoul(s + 1, &s, 0x10); - if (*s != (i == 5? ')' : ':')) - break; - } - if (i == 6) - return 0; - } - return ENODEV; -} - static void etherh_setif(struct net_device *dev) { - unsigned long addr; - unsigned long flags; + unsigned long addr, flags; save_flags_cli(flags); @@ -118,19 +96,27 @@ etherh_setif(struct net_device *dev) case PROD_I3_ETHERLAN600A: addr = dev->base_addr + EN0_RCNTHI; - if (ei_status.interface_num) /* 10b2 */ + switch (dev->if_port) { + case IF_PORT_10BASE2: outb((inb(addr) & 0xf8) | 1, addr); - else /* 10bT */ + break; + case IF_PORT_10BASET: outb((inb(addr) & 0xf8), addr); + break; + } break; case PROD_I3_ETHERLAN500: addr = dev->rmem_start; - if (ei_status.interface_num) /* 10b2 */ + switch (dev->if_port) { + case IF_PORT_10BASE2: outb(inb(addr) & ~ETHERH_CP_IF, addr); - else /* 10bT */ + break; + case IF_PORT_10BASET: outb(inb(addr) | ETHERH_CP_IF, addr); + break; + } break; default: @@ -143,22 +129,30 @@ etherh_setif(struct net_device *dev) static int etherh_getifstat(struct net_device *dev) { - int stat; + int stat = 0; switch (dev->mem_end) { case PROD_I3_ETHERLAN600: case PROD_I3_ETHERLAN600A: - if (ei_status.interface_num) /* 10b2 */ + switch (dev->if_port) { + case IF_PORT_10BASE2: stat = 1; - else /* 10bT */ + break; + case IF_PORT_10BASET: stat = inb(dev->base_addr+EN0_RCNTHI) & 4; + break; + } break; case PROD_I3_ETHERLAN500: - if (ei_status.interface_num) /* 10b2 */ + switch (dev->if_port) { + case IF_PORT_10BASE2: stat = 1; - else /* 10bT */ + break; + case IF_PORT_10BASET: stat = inb(dev->rmem_start) & ETHERH_CP_HEARTBEAT; + break; + } break; default: @@ -170,14 +164,54 @@ etherh_getifstat(struct net_device *dev) } /* - * Reset the 8390 (hard reset) + * Configure the interface. Note that we ignore the other + * parts of ifmap, since its mostly meaningless for this driver. + */ +static int etherh_set_config(struct net_device *dev, struct ifmap *map) +{ + switch (map->port) { + case IF_PORT_10BASE2: + case IF_PORT_10BASET: + /* + * If the user explicitly sets the interface + * media type, turn off automedia detection. + */ + dev->flags &= ~IFF_AUTOMEDIA; + dev->if_port = map->port; + break; + + default: + return -EINVAL; + } + + etherh_setif(dev); + + return 0; +} + +/* + * Reset the 8390 (hard reset). Note that we can't actually do this. */ static void etherh_reset(struct net_device *dev) { outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, dev->base_addr); - etherh_setif(dev); + /* + * See if we need to change the interface type. + * Note that we use 'interface_num' as a flag + * to indicate that we need to change the media. + */ + if (dev->flags & IFF_AUTOMEDIA && ei_status.interface_num) { + ei_status.interface_num = 0; + + if (dev->if_port == IF_PORT_10BASET) + dev->if_port = IF_PORT_10BASE2; + else + dev->if_port = IF_PORT_10BASET; + + etherh_setif(dev); + } } /* @@ -332,8 +366,31 @@ etherh_open(struct net_device *dev) return -EAGAIN; } + /* + * Make sure that we aren't going to change the + * media type on the next reset - we are about to + * do automedia manually now. + */ + ei_status.interface_num = 0; + + /* + * If we are doing automedia detection, do it now. + * This is more reliable than the 8390's detection. + */ + if (dev->flags & IFF_AUTOMEDIA) { + dev->if_port = IF_PORT_10BASET; + etherh_setif(dev); + mdelay(1); + if (!etherh_getifstat(dev)) { + dev->if_port = IF_PORT_10BASE2; + etherh_setif(dev); + } + } else + etherh_setif(dev); + etherh_reset(dev); ei_open(dev); + return 0; } @@ -350,281 +407,266 @@ etherh_close(struct net_device *dev) return 0; } +static void etherh_irq_enable(ecard_t *ec, int irqnr) +{ + unsigned int ctrl_addr = (unsigned int)ec->irq_data; + outb(inb(ctrl_addr) | ETHERH_CP_IE, ctrl_addr); +} + +static void etherh_irq_disable(ecard_t *ec, int irqnr) +{ + unsigned int ctrl_addr = (unsigned int)ec->irq_data; + outb(inb(ctrl_addr) & ~ETHERH_CP_IE, ctrl_addr); +} + +static expansioncard_ops_t etherh_ops = { + etherh_irq_enable, + etherh_irq_disable, + NULL, + NULL, + NULL, + NULL +}; + /* - * This is the real probe routine. + * Initialisation */ -static int __init -etherh_probe1(struct net_device *dev) + +static void __init etherh_banner(void) { static int version_printed; - unsigned int addr, i, reg0, tmp; - const char *dev_type; - const char *if_type; - const char *name = "etherh"; - - addr = dev->base_addr; if (net_debug && version_printed++ == 0) printk(version); +} - switch (dev->mem_end) { - case PROD_I3_ETHERLAN500: - dev_type = "500"; - break; - case PROD_I3_ETHERLAN600: - dev_type = "600"; - break; - case PROD_I3_ETHERLAN600A: - dev_type = "600A"; - break; - default: - dev_type = ""; - } +static int __init etherh_check_presence(struct net_device *dev) +{ + unsigned int addr = dev->base_addr, reg0, tmp; - reg0 = inb (addr); + reg0 = inb(addr); if (reg0 == 0xff) { if (net_debug & DEBUG_INIT) - printk("%s: %s error: NS8390 command register wrong\n", - dev->name, name); + printk("%s: etherh error: NS8390 command register wrong\n", + dev->name); return -ENODEV; } - outb (E8390_NODMA | E8390_PAGE1 | E8390_STOP, addr + E8390_CMD); - tmp = inb (addr + 13); - outb (0xff, addr + 13); - outb (E8390_NODMA | E8390_PAGE0, addr + E8390_CMD); - inb (addr + EN0_COUNTER0); - if (inb (addr + EN0_COUNTER0) != 0) { + outb(E8390_NODMA | E8390_PAGE1 | E8390_STOP, addr + E8390_CMD); + tmp = inb(addr + 13); + outb(0xff, addr + 13); + outb(E8390_NODMA | E8390_PAGE0, addr + E8390_CMD); + inb(addr + EN0_COUNTER0); + if (inb(addr + EN0_COUNTER0) != 0) { if (net_debug & DEBUG_INIT) - printk("%s: %s error: NS8390 not found\n", - dev->name, name); - outb (reg0, addr); - outb (tmp, addr + 13); + printk("%s: etherh error: NS8390 not found\n", + dev->name); + outb(reg0, addr); + outb(tmp, addr + 13); return -ENODEV; } - if (ethdev_init(dev)) - return -ENOMEM; - - request_region(addr, 16, name); - - printk("%s: %s %s at %lx, IRQ%d, ether address ", - dev->name, name, dev_type, dev->base_addr, dev->irq); - - for (i = 0; i < 6; i++) - printk (i == 5 ? "%2.2x " : "%2.2x:", dev->dev_addr[i]); - - ei_status.name = name; - ei_status.word16 = 1; - ei_status.tx_start_page = ETHERH_TX_START_PAGE; - ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES; - ei_status.stop_page = ETHERH_STOP_PAGE; - ei_status.reset_8390 = etherh_reset; - ei_status.block_input = etherh_block_input; - ei_status.block_output = etherh_block_output; - ei_status.get_8390_hdr = etherh_get_header; - dev->open = etherh_open; - dev->stop = etherh_close; - - /* select 10bT */ - ei_status.interface_num = 0; - if_type = "10BaseT"; - etherh_setif(dev); - mdelay(1); - if (!etherh_getifstat(dev)) { - if_type = "10Base2"; - ei_status.interface_num = 1; - etherh_setif(dev); - } - if (!etherh_getifstat(dev)) - if_type = "UNKNOWN"; - - printk("%s\n", if_type); - - etherh_reset(dev); - NS8390_init (dev, 0); return 0; } -static void etherh_irq_enable(ecard_t *ec, int irqnr) +/* + * Read the ethernet address string from the on board rom. + * This is an ascii string... + */ +static int __init etherh_addr(char *addr, struct expansion_card *ec) { - unsigned int ctrl_addr = (unsigned int)ec->irq_data; - outb(inb(ctrl_addr) | ETHERH_CP_IE, ctrl_addr); + struct in_chunk_dir cd; + char *s; + + if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { + int i; + for (i = 0; i < 6; i++) { + addr[i] = simple_strtoul(s + 1, &s, 0x10); + if (*s != (i == 5? ')' : ':')) + break; + } + if (i == 6) + return 0; + } + return ENODEV; } -static void etherh_irq_disable(ecard_t *ec, int irqnr) +static struct net_device * __init etherh_init_one(struct expansion_card *ec) { - unsigned int ctrl_addr = (unsigned int)ec->irq_data; - outb(inb(ctrl_addr) & ~ETHERH_CP_IE, ctrl_addr); -} + struct net_device *dev; + const char *dev_type; + int i; -static expansioncard_ops_t etherh_ops = { - etherh_irq_enable, - etherh_irq_disable, - NULL, - NULL, - NULL, - NULL -}; + etherh_banner(); -static void __init -etherh_initdev(ecard_t *ec, struct net_device *dev) -{ - ecard_claim (ec); + ecard_claim(ec); - dev->irq = ec->irq; - dev->mem_end = ec->cid.product; + dev = init_etherdev(NULL, 0); + if (!dev) + goto out; + + etherh_addr(dev->dev_addr, ec); + + dev->open = etherh_open; + dev->stop = etherh_close; + dev->set_config = etherh_set_config; + dev->irq = ec->irq; + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + dev->mem_end = ec->cid.product; + ec->ops = ðerh_ops; switch (ec->cid.product) { case PROD_I3_ETHERLAN500: - dev->base_addr = ecard_address (ec, ECARD_MEMC, 0) + ETHERH500_NS8390; - dev->mem_start = dev->base_addr + ETHERH500_DATAPORT; + dev->base_addr += ETHERH500_NS8390; + dev->mem_start = dev->base_addr + ETHERH500_DATAPORT; dev->rmem_start = (unsigned long) - ec->irq_data = (void *)ecard_address (ec, ECARD_IOC, ECARD_FAST) + ec->irq_data = (void *)ecard_address (ec, ECARD_IOC, ECARD_FAST) + ETHERH500_CTRLPORT; break; case PROD_I3_ETHERLAN600: case PROD_I3_ETHERLAN600A: - dev->base_addr = ecard_address (ec, ECARD_MEMC, 0) + ETHERH600_NS8390; + dev->base_addr += ETHERH600_NS8390; dev->mem_start = dev->base_addr + ETHERH600_DATAPORT; - ec->irq_data = (void *)(dev->base_addr + ETHERH600_CTRLPORT); + ec->irq_data = (void *)(dev->base_addr + ETHERH600_CTRLPORT); break; default: - printk ("%s: etherh error: unknown card type\n", dev->name); + printk("%s: etherh error: unknown card type %x\n", + dev->name, ec->cid.product); + goto out; } - ec->ops = ðerh_ops; - etherh_addr(dev->dev_addr, ec); -} + if (!request_region(dev->base_addr, 16, dev->name)) + goto region_not_free; -#ifndef MODULE -int __init -etherh_probe(struct net_device *dev) -{ - if (!dev) - return ENODEV; + if (etherh_check_presence(dev) || ethdev_init(dev)) + goto release; - if (!dev->base_addr || dev->base_addr == 0xffe0) { - struct expansion_card *ec; + switch (ec->cid.product) { + case PROD_I3_ETHERLAN500: + dev_type = "500"; + break; - ecard_startfind(); + case PROD_I3_ETHERLAN600: + dev_type = "600"; + break; - if ((ec = ecard_find (0, etherh_cids)) == NULL) - return ENODEV; + case PROD_I3_ETHERLAN600A: + dev_type = "600A"; + break; - etherh_initdev(ec, dev); + default: + dev_type = "unknown"; + break; } - return etherh_probe1(dev); -} -#endif -#ifdef MODULE -#define MAX_ETHERH_CARDS 2 + printk("%s: etherh %s at %lx, IRQ%d, ether address ", + dev->name, dev_type, dev->base_addr, dev->irq); -static int io[MAX_ETHERH_CARDS]; -static int irq[MAX_ETHERH_CARDS]; -static char ethernames[MAX_ETHERH_CARDS][9]; -static struct net_device *my_ethers[MAX_ETHERH_CARDS]; -static struct expansion_card *ec[MAX_ETHERH_CARDS]; + for (i = 0; i < 6; i++) + printk(i == 5 ? "%2.2x\n" : "%2.2x:", dev->dev_addr[i]); -static int -init_all_cards(void) -{ - struct net_device *dev = NULL; - int i, found = 0; + /* + * Unfortunately, ethdev_init eventually calls + * ether_setup, which re-writes dev->flags. + */ + switch (ec->cid.product) { + case PROD_I3_ETHERLAN500: + dev->if_port = IF_PORT_UNKNOWN; + break; - for (i = 0; i < MAX_ETHERH_CARDS; i++) { - my_ethers[i] = NULL; - ec[i] = NULL; - strcpy (ethernames[i], " "); + case PROD_I3_ETHERLAN600: + case PROD_I3_ETHERLAN600A: + dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; + dev->if_port = IF_PORT_10BASET; + break; } - ecard_startfind(); - - for (i = 0; i < MAX_ETHERH_CARDS; i++) { - if (!dev) - dev = (struct net_device *)kmalloc (sizeof (struct net_device), GFP_KERNEL); - if (dev) - memset (dev, 0, sizeof (struct net_device)); - - if (!io[i]) { - if ((ec[i] = ecard_find (0, etherh_cids)) == NULL) - continue; - - if (!dev) - return -ENOMEM; - - etherh_initdev (ec[i], dev); - } else { - ec[i] = NULL; - if (!dev) - return -ENOMEM; - dev->base_addr = io[i]; - dev->irq = irq[i]; - } - - dev->init = etherh_probe1; - dev->name = ethernames[i]; + ei_status.name = dev->name; + ei_status.word16 = 1; + ei_status.tx_start_page = ETHERH_TX_START_PAGE; + ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES; + ei_status.stop_page = ETHERH_STOP_PAGE; - my_ethers[i] = dev; + ei_status.reset_8390 = etherh_reset; + ei_status.block_input = etherh_block_input; + ei_status.block_output = etherh_block_output; + ei_status.get_8390_hdr = etherh_get_header; + ei_status.interface_num = 0; - if (register_netdev(dev) != 0) { - printk(KERN_ERR "No etherh card found at %08lX\n", - dev->base_addr); - if (ec[i]) { - ecard_release(ec[i]); - ec[i] = NULL; - } - continue; - } - found ++; - dev = NULL; - } + etherh_reset(dev); + NS8390_init(dev, 0); + return dev; + +release: + release_region(dev->base_addr, 16); +region_not_free: + unregister_netdev(dev); + kfree(dev); +out: + ecard_release(ec); + return NULL; +} - if (dev) - kfree (dev); +#define MAX_ETHERH_CARDS 2 - return found ? 0 : -ENODEV; -} +static struct net_device *e_dev[MAX_ETHERH_CARDS]; +static struct expansion_card *e_card[MAX_ETHERH_CARDS]; -int -init_module(void) +static int __init etherh_init(void) { - int ret; + int i, ret = -ENODEV; - if (load_8390_module(__FILE__)) + if (load_8390_module("etherh.c")) return -ENOSYS; lock_8390_module(); - ret = init_all_cards(); + ecard_startfind(); - if (ret) { - unlock_8390_module(); + for (i = 0; i < MAX_ECARDS; i++) { + struct expansion_card *ec; + struct net_device *dev; + + ec = ecard_find(0, etherh_cids); + if (!ec) + break; + + dev = etherh_init_one(ec); + if (!dev) + break; + + e_card[i] = ec; + e_dev[i] = dev; + ret = 0; } + if (ret) + unlock_8390_module(); + return ret; } -void -cleanup_module(void) +static void __exit etherh_exit(void) { int i; + for (i = 0; i < MAX_ETHERH_CARDS; i++) { - if (my_ethers[i]) { - unregister_netdev(my_ethers[i]); - release_region (my_ethers[i]->base_addr, 16); - kfree (my_ethers[i]); - my_ethers[i] = NULL; + if (e_dev[i]) { + unregister_netdev(e_dev[i]); + release_region(e_dev[i]->base_addr, 16); + kfree(e_dev[i]); + e_dev[i] = NULL; } - if (ec[i]) { - ec[i]->ops = NULL; - ecard_release(ec[i]); - ec[i] = NULL; + if (e_card[i]) { + e_card[i]->ops = NULL; + ecard_release(e_card[i]); + e_card[i] = NULL; } } unlock_8390_module(); } -#endif /* MODULE */ + +module_init(etherh_init); +module_exit(etherh_exit); diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index df6ae2eda18..c4d52547006 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -2283,7 +2283,7 @@ static struct pci_device_id eni_pci_tbl[] __devinitdata = { 0, 0, 0 /* FPGA */ }, { PCI_VENDOR_ID_EF, PCI_DEVICE_ID_EF_ATM_ASIC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 /* ASIC */ }, - { 0 } + { 0, } }; MODULE_DEVICE_TABLE(pci,eni_pci_tbl); diff --git a/drivers/block/Config.in b/drivers/block/Config.in index 679545b7c71..37f0669d6db 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -49,10 +49,12 @@ fi bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD -bool ' DANGEROUS! RAID1/RAID5 code' CONFIG_RAID15_DANGEROUS -if [ "$CONFIG_RAID15_DANGEROUS" = "y" ]; then - dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD - dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' RAID-1/RAID-5 code (DANGEROUS)' CONFIG_RAID15_DANGEROUS + if [ "$CONFIG_RAID15_DANGEROUS" = "y" ]; then + dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD + dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD + fi fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 04004d7fb28..c6f8531f476 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -19,8 +19,9 @@ */ -#define DAC960_DriverVersion "2.3.4" -#define DAC960_DriverDate "23 September 1999" +#define DAC960_DriverVersion "2.3.5" +#define DAC960_DriverDate "23 January 2000" + #include @@ -110,7 +111,7 @@ static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller) DAC960_Announce("***** DAC960 RAID Driver Version " DAC960_DriverVersion " of " DAC960_DriverDate " *****\n", Controller); - DAC960_Announce("Copyright 1998-1999 by Leonard N. Zubkoff " + DAC960_Announce("Copyright 1998-2000 by Leonard N. Zubkoff " "\n", Controller); } @@ -184,6 +185,23 @@ static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command) /* + DAC960_WaitForCommand waits for a wake_up on Controller's Command Wait Queue. +*/ + +static void DAC960_WaitForCommand(DAC960_Controller_T *Controller) +{ + DECLARE_WAITQUEUE(WaitQueueEntry, current); + add_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry); + current->state = TASK_UNINTERRUPTIBLE; + spin_unlock(&io_request_lock); + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry); + spin_lock_irq(&io_request_lock); +} + + +/* DAC960_QueueCommand queues Command. */ @@ -307,6 +325,62 @@ static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller, /* + DAC960_ReportErrorStatus reports Controller BIOS Messages passed through + the Error Status Register when the driver performs the BIOS handshaking. + It returns true for fatal errors and false otherwise. +*/ + +static boolean DAC960_ReportErrorStatus(DAC960_Controller_T *Controller, + unsigned char ErrorStatus, + unsigned char Parameter0, + unsigned char Parameter1) +{ + switch (ErrorStatus) + { + case 0x00: + DAC960_Notice("Physical Drive %d:%d Not Responding\n", + Controller, Parameter1, Parameter0); + break; + case 0x08: + if (Controller->DriveSpinUpMessageDisplayed) break; + DAC960_Notice("Spinning Up Drives\n", Controller); + Controller->DriveSpinUpMessageDisplayed = true; + break; + case 0x30: + DAC960_Notice("Configuration Checksum Error\n", Controller); + break; + case 0x60: + DAC960_Notice("Mirror Race Recovery Failed\n", Controller); + break; + case 0x70: + DAC960_Notice("Mirror Race Recovery In Progress\n", Controller); + break; + case 0x90: + DAC960_Notice("Physical Drive %d:%d COD Mismatch\n", + Controller, Parameter1, Parameter0); + break; + case 0xA0: + DAC960_Notice("Logical Drive Installation Aborted\n", Controller); + break; + case 0xB0: + DAC960_Notice("Mirror Race On A Critical Logical Drive\n", Controller); + break; + case 0xD0: + DAC960_Notice("New Controller Configuration Found\n", Controller); + break; + case 0xF0: + DAC960_Error("Fatal Memory Parity Error for Controller at\n", Controller); + return true; + default: + DAC960_Error("Unknown Initialization Error %02X for Controller at\n", + Controller, ErrorStatus); + return true; + } + return false; +} + + +/* DAC960_EnableMemoryMailboxInterface enables the Memory Mailbox Interface. */ @@ -372,7 +446,7 @@ static boolean DAC960_EnableMemoryMailboxInterface(DAC960_Controller_T case DAC960_V5_Controller: while (--TimeoutCounter >= 0) { - if (DAC960_V5_HardwareMailboxEmptyP(ControllerBaseAddress)) + if (!DAC960_V5_HardwareMailboxFullP(ControllerBaseAddress)) break; udelay(10); } @@ -469,6 +543,9 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) unsigned long BaseAddress1 = pci_resource_start (PCI_Device, 1); unsigned short SubsystemVendorID, SubsystemDeviceID; int CommandIdentifier; + unsigned char ErrorStatus, Parameter0, Parameter1; + void *BaseAddress; + if (pci_enable_device(PCI_Device)) goto Ignore; @@ -517,24 +594,6 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) Controller->Function = Function; sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber); /* - Acquire shared access to the IRQ Channel. - */ - if (IRQ_Channel == 0) - { - DAC960_Error("IRQ Channel %d illegal for Controller at\n", - Controller, IRQ_Channel); - goto Failure; - } - strcpy(Controller->FullModelName, "DAC960"); - if (request_irq(IRQ_Channel, DAC960_InterruptHandler, - SA_SHIRQ, Controller->FullModelName, Controller) < 0) - { - DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n", - Controller, IRQ_Channel); - goto Failure; - } - Controller->IRQ_Channel = IRQ_Channel; - /* Map the Controller Register Window. */ if (MemoryWindowSize < PAGE_SIZE) @@ -549,34 +608,87 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) "Controller at\n", Controller); goto Failure; } + BaseAddress = Controller->BaseAddress; switch (ControllerType) { case DAC960_V5_Controller: - DAC960_V5_DisableInterrupts(Controller->BaseAddress); + DAC960_V5_DisableInterrupts(BaseAddress); + DAC960_V5_AcknowledgeHardwareMailboxStatus(BaseAddress); + udelay(1000); + while (DAC960_V5_InitializationInProgressP(BaseAddress)) + { + if (DAC960_V5_ReadErrorStatus(BaseAddress, &ErrorStatus, + &Parameter0, &Parameter1) && + DAC960_ReportErrorStatus(Controller, ErrorStatus, + Parameter0, Parameter1)) + goto Failure; + udelay(10); + } if (!DAC960_EnableMemoryMailboxInterface(Controller)) { DAC960_Error("Unable to Enable Memory Mailbox Interface " "for Controller at\n", Controller); goto Failure; } - DAC960_V5_EnableInterrupts(Controller->BaseAddress); + DAC960_V5_EnableInterrupts(BaseAddress); break; case DAC960_V4_Controller: - DAC960_V4_DisableInterrupts(Controller->BaseAddress); + DAC960_V4_DisableInterrupts(BaseAddress); + DAC960_V4_AcknowledgeHardwareMailboxStatus(BaseAddress); + udelay(1000); + while (DAC960_V4_InitializationInProgressP(BaseAddress)) + { + if (DAC960_V4_ReadErrorStatus(BaseAddress, &ErrorStatus, + &Parameter0, &Parameter1) && + DAC960_ReportErrorStatus(Controller, ErrorStatus, + Parameter0, Parameter1)) + goto Failure; + udelay(10); + } if (!DAC960_EnableMemoryMailboxInterface(Controller)) { DAC960_Error("Unable to Enable Memory Mailbox Interface " "for Controller at\n", Controller); goto Failure; } - DAC960_V4_EnableInterrupts(Controller->BaseAddress); + DAC960_V4_EnableInterrupts(BaseAddress); break; case DAC960_V3_Controller: request_region(Controller->IO_Address, 0x80, Controller->FullModelName); - DAC960_V3_EnableInterrupts(Controller->BaseAddress); + DAC960_V3_DisableInterrupts(BaseAddress); + DAC960_V3_AcknowledgeStatus(BaseAddress); + udelay(1000); + while (DAC960_V3_InitializationInProgressP(BaseAddress)) + { + if (DAC960_V3_ReadErrorStatus(BaseAddress, &ErrorStatus, + &Parameter0, &Parameter1) && + DAC960_ReportErrorStatus(Controller, ErrorStatus, + Parameter0, Parameter1)) + goto Failure; + udelay(10); + } + DAC960_V3_EnableInterrupts(BaseAddress); break; } + /* + Acquire shared access to the IRQ Channel. + */ + if (IRQ_Channel == 0) + { + DAC960_Error("IRQ Channel %d illegal for Controller at\n", + Controller, IRQ_Channel); + goto Failure; + } + strcpy(Controller->FullModelName, "DAC960"); + if (request_irq(IRQ_Channel, DAC960_InterruptHandler, + SA_SHIRQ, Controller->FullModelName, Controller) < 0) + { + DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n", + Controller, IRQ_Channel); + goto Failure; + } + Controller->IRQ_Channel = IRQ_Channel; DAC960_ActiveControllerCount++; for (CommandIdentifier = 0; CommandIdentifier < DAC960_MaxChannels; @@ -597,11 +709,11 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) "0x%X PCI Address 0x%X\n", Controller, Bus, Device, Function, IO_Address, PCI_Address); if (Controller == NULL) break; - if (Controller->IRQ_Channel > 0) - free_irq(IRQ_Channel, Controller); if (Controller->MemoryMappedAddress != NULL) iounmap(Controller->MemoryMappedAddress); DAC960_Controllers[Controller->ControllerNumber] = NULL; + if (Controller->IRQ_Channel > 0) + free_irq(IRQ_Channel, Controller); Ignore: kfree(Controller); } @@ -1346,9 +1458,7 @@ static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller, Command = DAC960_AllocateCommand(Controller); if (Command != NULL) break; if (!WaitForCommand) return false; - spin_unlock(&io_request_lock); - sleep_on(&Controller->CommandWaitQueue); - spin_lock_irq(&io_request_lock); + DAC960_WaitForCommand(Controller); } DAC960_ClearCommand(Command); if (Request->cmd == READ) @@ -1900,6 +2010,20 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) } else if (NewEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress) Controller->NeedConsistencyCheckProgress = true; + if (CommandType != DAC960_MonitoringCommand && + Controller->RebuildFlagPending) + { + DAC960_Enquiry_T *Enquiry = (DAC960_Enquiry_T *) + Bus_to_Virtual(Command->CommandMailbox.Type3.BusAddress); + Enquiry->RebuildFlag = Controller->PendingRebuildFlag; + Controller->RebuildFlagPending = false; + } + else if (CommandType == DAC960_MonitoringCommand && + NewEnquiry->RebuildFlag > DAC960_BackgroundCheckInProgress) + { + Controller->PendingRebuildFlag = NewEnquiry->RebuildFlag; + Controller->RebuildFlagPending = true; + } } else if (CommandOpcode == DAC960_PerformEventLogOperation) { @@ -2083,6 +2207,9 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) Controller->RebuildProgress.LogicalDriveSize; unsigned int BlocksCompleted = LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks; + if (CommandStatus == DAC960_NoRebuildOrCheckInProgress && + Controller->LastRebuildStatus == DAC960_NormalCompletion) + CommandStatus = DAC960_RebuildSuccessful; switch (CommandStatus) { case DAC960_NormalCompletion: @@ -2110,13 +2237,28 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) "Failure of Drive Being Rebuilt\n", Controller); break; case DAC960_NoRebuildOrCheckInProgress: - if (Controller->LastRebuildStatus != DAC960_NormalCompletion) - break; + break; case DAC960_RebuildSuccessful: DAC960_Progress("Rebuild Completed Successfully\n", Controller); break; + case DAC960_RebuildSuccessfullyTerminated: + DAC960_Progress("Rebuild Successfully Terminated\n", Controller); + break; } Controller->LastRebuildStatus = CommandStatus; + if (CommandType != DAC960_MonitoringCommand && + Controller->RebuildStatusPending) + { + Command->CommandStatus = Controller->PendingRebuildStatus; + Controller->RebuildStatusPending = false; + } + else if (CommandType == DAC960_MonitoringCommand && + CommandStatus != DAC960_NormalCompletion && + CommandStatus != DAC960_NoRebuildOrCheckInProgress) + { + Controller->PendingRebuildStatus = CommandStatus; + Controller->RebuildStatusPending = true; + } } else if (CommandOpcode == DAC960_RebuildStat) { @@ -2331,7 +2473,7 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) if (CommandType == DAC960_QueuedCommand) { DAC960_KernelCommand_T *KernelCommand = Command->KernelCommand; - KernelCommand->CommandStatus = CommandStatus; + KernelCommand->CommandStatus = Command->CommandStatus; Command->KernelCommand = NULL; if (CommandOpcode == DAC960_DCDB) Controller->DirectCommandActive[KernelCommand->DCDB->Channel] @@ -2352,9 +2494,12 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) return; } /* - Deallocate the Command, and wake up any processes waiting on a free Command. + Deallocate the Command. */ DAC960_DeallocateCommand(Command); + /* + Wake up any processes waiting on a free Command. + */ wake_up(&Controller->CommandWaitQueue); } @@ -2761,19 +2906,14 @@ static int DAC960_UserIOCTL(Inode_T *Inode, File_T *File, } if (CommandOpcode == DAC960_DCDB) { - while (true) - { - DAC960_AcquireControllerLock(Controller, &ProcessorFlags); - if (!Controller->DirectCommandActive[DCDB.Channel] - [DCDB.TargetID]) - Command = DAC960_AllocateCommand(Controller); - if (Command != NULL) - Controller->DirectCommandActive[DCDB.Channel] - [DCDB.TargetID] = true; - DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); - if (Command != NULL) break; - sleep_on(&Controller->CommandWaitQueue); - } + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + while (Controller->DirectCommandActive[DCDB.Channel] + [DCDB.TargetID] || + (Command = DAC960_AllocateCommand(Controller)) == NULL) + DAC960_WaitForCommand(Controller); + Controller->DirectCommandActive[DCDB.Channel] + [DCDB.TargetID] = true; + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); DAC960_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox, @@ -2783,14 +2923,10 @@ static int DAC960_UserIOCTL(Inode_T *Inode, File_T *File, } else { - while (true) - { - DAC960_AcquireControllerLock(Controller, &ProcessorFlags); - Command = DAC960_AllocateCommand(Controller); - DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); - if (Command != NULL) break; - sleep_on(&Controller->CommandWaitQueue); - } + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + while ((Command = DAC960_AllocateCommand(Controller)) == NULL) + DAC960_WaitForCommand(Controller); + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); DAC960_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox, @@ -3194,14 +3330,10 @@ static boolean DAC960_ExecuteUserCommand(DAC960_Controller_T *Controller, DAC960_CommandMailbox_T *CommandMailbox; ProcessorFlags_T ProcessorFlags; unsigned char Channel, TargetID, LogicalDriveNumber; - while (true) - { - DAC960_AcquireControllerLock(Controller, &ProcessorFlags); - Command = DAC960_AllocateCommand(Controller); - DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); - if (Command != NULL) break; - sleep_on(&Controller->CommandWaitQueue); - } + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + while ((Command = DAC960_AllocateCommand(Controller)) == NULL) + DAC960_WaitForCommand(Controller); + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); Controller->UserStatusLength = 0; DAC960_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; diff --git a/drivers/block/DAC960.h b/drivers/block/DAC960.h index e93448faba1..237a8deefb2 100644 --- a/drivers/block/DAC960.h +++ b/drivers/block/DAC960.h @@ -55,17 +55,17 @@ typedef enum { false, true } __attribute__ ((packed)) boolean; /* - Define a 32 bit I/O Address data type. + Define a 32/64 bit I/O Address data type. */ -typedef unsigned int DAC960_IO_Address_T; +typedef unsigned long DAC960_IO_Address_T; /* - Define a 32 bit PCI Bus Address data type. + Define a 32/64 bit PCI Bus Address data type. */ -typedef unsigned int DAC960_PCI_Address_T; +typedef unsigned long DAC960_PCI_Address_T; /* @@ -196,6 +196,7 @@ typedef unsigned char DAC960_CommandIdentifier_T; #define DAC960_RebuildFailed_BadBlocksOnOther 0x0003 /* Consistency */ #define DAC960_RebuildFailed_NewDriveFailed 0x0004 /* Consistency */ #define DAC960_RebuildSuccessful 0x0100 /* Consistency */ +#define DAC960_RebuildSuccessfullyTerminated 0x0107 /* Consistency */ #define DAC960_AddCapacityInProgress 0x0004 /* Consistency */ #define DAC960_AddCapacityFailedOrSuspended 0x00F4 /* Consistency */ #define DAC960_Config2ChecksumError 0x0002 /* Configuration */ @@ -513,7 +514,7 @@ typedef struct DAC960_DeviceState unsigned char SynchronousMultiplier; /* Byte 4 */ unsigned char SynchronousOffset:5; /* Byte 5 Bits 0-4 */ unsigned char :3; /* Byte 5 Bits 5-7 */ - unsigned long DiskSize __attribute__ ((packed)); /* Bytes 6-9 */ + unsigned int DiskSize __attribute__ ((packed)); /* Bytes 6-9 */ } DAC960_DeviceState_T; @@ -1110,7 +1111,6 @@ static char typedef struct buffer_head BufferHeader_T; typedef struct file File_T; -typedef struct file_operations FileOperations_T; typedef struct gendisk GenericDiskInfo_T; typedef struct hd_geometry DiskGeometry_T; typedef struct hd_struct DiskPartition_T; @@ -1212,6 +1212,7 @@ typedef struct DAC960_Controller unsigned char LogicalDriveCount; unsigned char GeometryTranslationHeads; unsigned char GeometryTranslationSectors; + unsigned char PendingRebuildFlag; unsigned short ControllerQueueDepth; unsigned short DriverQueueDepth; unsigned short MaxBlocksPerCommand; @@ -1247,6 +1248,9 @@ typedef struct DAC960_Controller boolean NeedRebuildProgress; boolean NeedConsistencyCheckProgress; boolean EphemeralProgressMessage; + boolean RebuildFlagPending; + boolean RebuildStatusPending; + boolean DriveSpinUpMessageDisplayed; Timer_T MonitoringTimer; GenericDiskInfo_T GenericDiskInfo; DAC960_Command_T *FreeCommands; @@ -1265,6 +1269,7 @@ typedef struct DAC960_Controller DAC960_EventLogEntry_T EventLogEntry; DAC960_RebuildProgress_T RebuildProgress; DAC960_CommandStatus_T LastRebuildStatus; + DAC960_CommandStatus_T PendingRebuildStatus; DAC960_LogicalDriveInformation_T LogicalDriveInformation[2][DAC960_MaxLogicalDrives]; DAC960_LogicalDriveState_T LogicalDriveInitialState[DAC960_MaxLogicalDrives]; @@ -1389,7 +1394,8 @@ typedef enum DAC960_V5_MailboxRegister11Offset = 0x5B, DAC960_V5_MailboxRegister12Offset = 0x5C, DAC960_V5_StatusCommandIdentifierRegOffset = 0x5D, - DAC960_V5_StatusRegisterOffset = 0x5E + DAC960_V5_StatusRegisterOffset = 0x5E, + DAC960_V5_ErrorStatusRegisterOffset = 0x63 } DAC960_V5_RegisterOffsets_T; @@ -1411,7 +1417,8 @@ typedef union DAC960_V5_InboundDoorBellRegister } Write; struct { boolean HardwareMailboxEmpty:1; /* Bit 0 */ - unsigned char :7; /* Bits 1-7 */ + boolean InitializationNotInProgress:1; /* Bit 1 */ + unsigned char :6; /* Bits 2-7 */ } Read; } DAC960_V5_InboundDoorBellRegister_T; @@ -1455,6 +1462,22 @@ DAC960_V5_InterruptMaskRegister_T; /* + Define the structure of the DAC960 V5 Error Status Register. +*/ + +typedef union DAC960_V5_ErrorStatusRegister +{ + unsigned char All; + struct { + unsigned int :2; /* Bits 0-1 */ + boolean ErrorStatusPending:1; /* Bit 2 */ + unsigned int :5; /* Bits 3-7 */ + } Bits; +} +DAC960_V5_ErrorStatusRegister_T; + + +/* Define inline functions to provide an abstraction for reading and writing the DAC960 V5 Controller Interface Registers. */ @@ -1510,12 +1533,21 @@ void DAC960_V5_MemoryMailboxNewCommand(void *ControllerBaseAddress) } static inline -boolean DAC960_V5_HardwareMailboxEmptyP(void *ControllerBaseAddress) +boolean DAC960_V5_HardwareMailboxFullP(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); + return !InboundDoorBellRegister.Read.HardwareMailboxEmpty; +} + +static inline +boolean DAC960_V5_InitializationInProgressP(void *ControllerBaseAddress) { DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); - return InboundDoorBellRegister.Read.HardwareMailboxEmpty; + return !InboundDoorBellRegister.Read.InitializationNotInProgress; } static inline @@ -1571,7 +1603,7 @@ static inline void DAC960_V5_EnableInterrupts(void *ControllerBaseAddress) { DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister; - InterruptMaskRegister.All = 0; + InterruptMaskRegister.All = 0xFF; InterruptMaskRegister.Bits.DisableInterrupts = false; writeb(InterruptMaskRegister.All, ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset); @@ -1581,7 +1613,7 @@ static inline void DAC960_V5_DisableInterrupts(void *ControllerBaseAddress) { DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister; - InterruptMaskRegister.All = 0; + InterruptMaskRegister.All = 0xFF; InterruptMaskRegister.Bits.DisableInterrupts = true; writeb(InterruptMaskRegister.All, ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset); @@ -1603,7 +1635,9 @@ void DAC960_V5_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox, NextCommandMailbox->Words[1] = CommandMailbox->Words[1]; NextCommandMailbox->Words[2] = CommandMailbox->Words[2]; NextCommandMailbox->Words[3] = CommandMailbox->Words[3]; + wmb(); NextCommandMailbox->Words[0] = CommandMailbox->Words[0]; + mb(); } static inline @@ -1633,6 +1667,26 @@ DAC960_V5_ReadStatusRegister(void *ControllerBaseAddress) return readw(ControllerBaseAddress + DAC960_V5_StatusRegisterOffset); } +static inline boolean +DAC960_V5_ReadErrorStatus(void *ControllerBaseAddress, + unsigned char *ErrorStatus, + unsigned char *Parameter0, + unsigned char *Parameter1) +{ + DAC960_V5_ErrorStatusRegister_T ErrorStatusRegister; + ErrorStatusRegister.All = + readb(ControllerBaseAddress + DAC960_V5_ErrorStatusRegisterOffset); + if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false; + ErrorStatusRegister.Bits.ErrorStatusPending = false; + *ErrorStatus = ErrorStatusRegister.All; + *Parameter0 = + readb(ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset); + *Parameter1 = + readb(ControllerBaseAddress + DAC960_V5_CommandIdentifierRegisterOffset); + writeb(0xFF, ControllerBaseAddress + DAC960_V5_ErrorStatusRegisterOffset); + return true; +} + static inline void DAC960_V5_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller) { @@ -1691,7 +1745,8 @@ typedef enum DAC960_V4_MailboxRegister11Offset = 0x100B, DAC960_V4_MailboxRegister12Offset = 0x100C, DAC960_V4_StatusCommandIdentifierRegOffset = 0x1018, - DAC960_V4_StatusRegisterOffset = 0x101A + DAC960_V4_StatusRegisterOffset = 0x101A, + DAC960_V4_ErrorStatusRegisterOffset = 0x103F } DAC960_V4_RegisterOffsets_T; @@ -1713,7 +1768,8 @@ typedef union DAC960_V4_InboundDoorBellRegister } Write; struct { boolean HardwareMailboxFull:1; /* Bit 0 */ - unsigned int :31; /* Bits 1-31 */ + boolean InitializationInProgress:1; /* Bit 1 */ + unsigned int :30; /* Bits 2-31 */ } Read; } DAC960_V4_InboundDoorBellRegister_T; @@ -1758,6 +1814,22 @@ DAC960_V4_InterruptMaskRegister_T; /* + Define the structure of the DAC960 V4 Error Status Register. +*/ + +typedef union DAC960_V4_ErrorStatusRegister +{ + unsigned char All; + struct { + unsigned int :2; /* Bits 0-1 */ + boolean ErrorStatusPending:1; /* Bit 2 */ + unsigned int :5; /* Bits 3-7 */ + } Bits; +} +DAC960_V4_ErrorStatusRegister_T; + + +/* Define inline functions to provide an abstraction for reading and writing the DAC960 V4 Controller Interface Registers. */ @@ -1822,6 +1894,15 @@ boolean DAC960_V4_HardwareMailboxFullP(void *ControllerBaseAddress) } static inline +boolean DAC960_V4_InitializationInProgressP(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.InitializationInProgress; +} + +static inline void DAC960_V4_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) { DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; @@ -1910,7 +1991,9 @@ void DAC960_V4_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox, NextCommandMailbox->Words[1] = CommandMailbox->Words[1]; NextCommandMailbox->Words[2] = CommandMailbox->Words[2]; NextCommandMailbox->Words[3] = CommandMailbox->Words[3]; + wmb(); NextCommandMailbox->Words[0] = CommandMailbox->Words[0]; + mb(); } static inline @@ -1940,11 +2023,31 @@ DAC960_V4_ReadStatusRegister(void *ControllerBaseAddress) return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset); } +static inline boolean +DAC960_V4_ReadErrorStatus(void *ControllerBaseAddress, + unsigned char *ErrorStatus, + unsigned char *Parameter0, + unsigned char *Parameter1) +{ + DAC960_V4_ErrorStatusRegister_T ErrorStatusRegister; + ErrorStatusRegister.All = + readb(ControllerBaseAddress + DAC960_V4_ErrorStatusRegisterOffset); + if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false; + ErrorStatusRegister.Bits.ErrorStatusPending = false; + *ErrorStatus = ErrorStatusRegister.All; + *Parameter0 = + readb(ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset); + *Parameter1 = + readb(ControllerBaseAddress + DAC960_V4_CommandIdentifierRegisterOffset); + writeb(0, ControllerBaseAddress + DAC960_V4_ErrorStatusRegisterOffset); + return true; +} + static inline void DAC960_V4_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller) { void *ControllerBaseAddress = Controller->BaseAddress; - writel(0xAABBFFFF, + writel(0x743C485E, ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset); writel((unsigned long) Controller->FirstCommandMailbox, ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); @@ -1962,7 +2065,7 @@ void DAC960_V4_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller, { void *ControllerBaseAddress = Controller->BaseAddress; if (readl(ControllerBaseAddress - + DAC960_V4_CommandOpcodeRegisterOffset) != 0xAABBFFFF) + + DAC960_V4_CommandOpcodeRegisterOffset) != 0x743C485E) return; *MemoryMailboxAddress = (void *) readl(ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); @@ -1996,6 +2099,7 @@ typedef enum DAC960_V3_MailboxRegister12Offset = 0x0C, DAC960_V3_StatusCommandIdentifierRegOffset = 0x0D, DAC960_V3_StatusRegisterOffset = 0x0E, + DAC960_V3_ErrorStatusRegisterOffset = 0x3F, DAC960_V3_InboundDoorBellRegisterOffset = 0x40, DAC960_V3_OutboundDoorBellRegisterOffset = 0x41, DAC960_V3_InterruptEnableRegisterOffset = 0x43 @@ -2019,7 +2123,8 @@ typedef union DAC960_V3_InboundDoorBellRegister } Write; struct { boolean MailboxFull:1; /* Bit 0 */ - unsigned char :7; /* Bits 1-7 */ + boolean InitializationInProgress:1; /* Bit 1 */ + unsigned char :6; /* Bits 2-7 */ } Read; } DAC960_V3_InboundDoorBellRegister_T; @@ -2060,6 +2165,22 @@ DAC960_V3_InterruptEnableRegister_T; /* + Define the structure of the DAC960 V3 Error Status Register. +*/ + +typedef union DAC960_V3_ErrorStatusRegister +{ + unsigned char All; + struct { + unsigned int :2; /* Bits 0-1 */ + boolean ErrorStatusPending:1; /* Bit 2 */ + unsigned int :5; /* Bits 3-7 */ + } Bits; +} +DAC960_V3_ErrorStatusRegister_T; + + +/* Define inline functions to provide an abstraction for reading and writing the DAC960 V3 Controller Interface Registers. */ @@ -2114,6 +2235,15 @@ boolean DAC960_V3_MailboxFullP(void *ControllerBaseAddress) } static inline +boolean DAC960_V3_InitializationInProgressP(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.InitializationInProgress; +} + +static inline void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress) { DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister; @@ -2188,6 +2318,26 @@ DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress) return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset); } +static inline boolean +DAC960_V3_ReadErrorStatus(void *ControllerBaseAddress, + unsigned char *ErrorStatus, + unsigned char *Parameter0, + unsigned char *Parameter1) +{ + DAC960_V3_ErrorStatusRegister_T ErrorStatusRegister; + ErrorStatusRegister.All = + readb(ControllerBaseAddress + DAC960_V3_ErrorStatusRegisterOffset); + if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false; + ErrorStatusRegister.Bits.ErrorStatusPending = false; + *ErrorStatus = ErrorStatusRegister.All; + *Parameter0 = + readb(ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset); + *Parameter1 = + readb(ControllerBaseAddress + DAC960_V3_CommandIdentifierRegisterOffset); + writeb(0, ControllerBaseAddress + DAC960_V3_ErrorStatusRegisterOffset); + return true; +} + /* Define compatibility macros between Linux 2.0 and Linux 2.1. diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 11532f8fd90..26f02abe3dd 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -105,7 +105,7 @@ int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg) int ret; blkelv_ioctl_arg_t output; - output.queue_ID = elevator; + output.queue_ID = elevator->queue_ID; output.read_latency = elevator->read_latency; output.write_latency = elevator->write_latency; output.max_bomb_segments = elevator->max_bomb_segments; @@ -146,5 +146,8 @@ int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg) void elevator_init(elevator_t * elevator) { + static unsigned int queue_ID; + *elevator = ELEVATOR_DEFAULTS; + elevator->queue_ID = queue_ID++; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index df797a33bb1..4d919b0cfd6 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -1,5 +1,5 @@ /* - * linux/kernel/floppy.c + * linux/drivers/block/floppy.c * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1993, 1994 Alain Knaff @@ -96,6 +96,12 @@ * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent * features to asm/floppy.h. */ + +/* + * 1998/05/07 -- Russell King -- More portability cleanups; moved definition of + * interrupt and dma channel to asm/floppy.h. Cleaned up some formatting & + * use of '0' for NULL. + */ /* * 1998/06/07 -- Alan Cox -- Merged the 2.0.34 fixes for resource allocation @@ -123,19 +129,6 @@ /* do print messages for unexpected interrupts */ static int print_unex=1; #include - -/* the following is the mask of allowed drives. By default units 2 and - * 3 of both floppy controllers are disabled, because switching on the - * motor of these drives causes system hangs on some PCI computers. drive - * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if - * a drive is allowed. */ - -static int FLOPPY_IRQ=6; -static int FLOPPY_DMA=2; -static int allowed_drive_mask = 0x33; -static int irqdma_allocated = 0; - - #include #include #include @@ -177,6 +170,8 @@ static int slow_floppy = 0; #include #include +static int FLOPPY_IRQ=6; +static int FLOPPY_DMA=2; static int can_use_virtual_dma=2; /* ======= * can use virtual DMA: @@ -186,7 +181,7 @@ static int can_use_virtual_dma=2; * but fall back on virtual DMA when not enough memory available */ -static int use_virtual_dma=0; +static int use_virtual_dma; /* ======= * use virtual DMA * 0 using hard DMA @@ -205,8 +200,22 @@ static void register_devfs_entries (int drive) __init; static devfs_handle_t devfs_handle = NULL; #define K_64 0x10000 /* 64KB */ + +/* the following is the mask of allowed drives. By default units 2 and + * 3 of both floppy controllers are disabled, because switching on the + * motor of these drives causes system hangs on some PCI computers. drive + * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if + * a drive is allowed. + * + * NOTE: This must come before we include the arch floppy header because + * some ports reference this variable from there. -DaveM + */ + +static int allowed_drive_mask = 0x33; + #include +static int irqdma_allocated = 0; #define MAJOR_NR FLOPPY_MAJOR @@ -231,9 +240,9 @@ static devfs_handle_t devfs_handle = NULL; static inline void fallback_on_nodma_alloc(char **addr, size_t l) { #ifdef FLOPPY_CAN_FALLBACK_ON_NODMA - if(*addr) + if (*addr) return; /* we have the memory */ - if(can_use_virtual_dma != 2) + if (can_use_virtual_dma != 2) return; /* no fallback allowed */ printk("DMA memory shortage. Temporarily falling back on virtual DMA\n"); *addr = (char *) nodma_mem_alloc(l); @@ -393,6 +402,16 @@ static struct floppy_raw_cmd *raw_cmd, default_raw_cmd; * 'options'. Other parameters should be self-explanatory (see also * setfdprm(8)). */ +/* + Size + | Sectors per track + | | Head + | | | Tracks + | | | | Stretch + | | | | | Gap 1 size + | | | | | | Data rate, | 0x40 for perp + | | | | | | | Spec1 (stepping rate, head unload + | | | | | | | | /fmt gap (gap2) */ static struct floppy_struct floppy_type[32] = { { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */ @@ -403,7 +422,7 @@ static struct floppy_struct floppy_type[32] = { { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */ { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */ - { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120"}, /* 9 3.12MB 3.5" */ + { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */ { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */ { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */ @@ -491,8 +510,8 @@ static struct format_descr format_req; * not contain a 64k byte boundary crossing, or data will be * corrupted/lost. */ -static char *floppy_track_buffer=0; -static int max_buffer_sectors=0; +static char *floppy_track_buffer; +static int max_buffer_sectors; static int *errors; typedef void (*done_f)(int); @@ -503,7 +522,7 @@ static struct cont_t { void (*error)(void); /* this is called to tally an error */ done_f done; /* this is called to say if the operation has * succeeded/failed */ -} *cont=NULL; +} *cont; static void floppy_ready(void); static void floppy_start(void); @@ -551,14 +570,13 @@ static struct floppy_struct *_floppy = floppy_type; static unsigned char current_drive = 0; static long current_count_sectors = 0; static unsigned char sector_t; /* sector in track */ -static unsigned char in_sector_offset; /* offset within physical sector, - * expressed in units of 512 bytes */ +static unsigned char in_sector_offset; /* offset within physical sector, + * expressed in units of 512 bytes */ #ifndef fd_eject #define fd_eject(x) -EINVAL #endif - #ifdef DEBUGT static long unsigned debugtimer; #endif @@ -602,10 +620,10 @@ static void is_alive(const char *message) #define OLOGSIZE 20 static void (*lasthandler)(void) = NULL; -static unsigned long interruptjiffies=0; -static unsigned long resultjiffies=0; -static int resultsize=0; -static unsigned long lastredo=0; +static unsigned long interruptjiffies; +static unsigned long resultjiffies; +static int resultsize; +static unsigned long lastredo; static struct output_log { unsigned char data; @@ -613,7 +631,7 @@ static struct output_log { unsigned long jiffies; } output_log[OLOGSIZE]; -static int output_log_pos=0; +static int output_log_pos; #endif #define CURRENTD -1 @@ -640,7 +658,7 @@ static void reschedule_timeout(int drive, const char *message, int marg) static int maximum(int a, int b) { - if(a > b) + if (a > b) return a; else return b; @@ -649,7 +667,7 @@ static int maximum(int a, int b) static int minimum(int a, int b) { - if(a < b) + if (a < b) return a; else return b; @@ -697,11 +715,11 @@ static int disk_change(int drive) if (jiffies - UDRS->select_date < UDP->select_delay) DPRINT("WARNING disk change called early\n"); if (!(FDCS->dor & (0x10 << UNIT(drive))) || - (FDCS->dor & 3) != UNIT(drive) || - fdc != FDC(drive)){ + (FDCS->dor & 3) != UNIT(drive) || + fdc != FDC(drive)){ DPRINT("probing disk change on unselected drive\n"); DPRINT("drive=%d fdc=%d dor=%x\n",drive, FDC(drive), - FDCS->dor); + (unsigned int)FDCS->dor); } #endif @@ -791,7 +809,7 @@ static void twaddle(void) { if (DP->select_delay) return; - fd_outb(FDCS->dor & ~(0x10<dor & ~(0x10<dor, FD_DOR); DRS->select_date = jiffies; } @@ -1034,7 +1052,7 @@ static int wait_for_completion(unsigned long delay, timeout_fn function) } static spinlock_t floppy_hlt_lock = SPIN_LOCK_UNLOCKED; -static int hlt_disabled=0; +static int hlt_disabled; static void floppy_disable_hlt(void) { unsigned long flags; @@ -1090,7 +1108,7 @@ static void setup_DMA(void) f=claim_dma_lock(); fd_disable_dma(FLOPPY_DMA); #ifdef fd_dma_setup - if(fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length, + if (fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length, (raw_cmd->flags & FD_RAW_READ)? DMA_MODE_READ : DMA_MODE_WRITE, FDCS->address) < 0) { @@ -1122,7 +1140,7 @@ static void show_floppy(void); static int wait_til_ready(void) { int counter, status; - if(FDCS->reset) + if (FDCS->reset) return -1; for (counter = 0; counter < 10000; counter++) { status = fd_inb(FD_STATUS); @@ -1186,7 +1204,7 @@ static int result(void) else break; } - if(!initialising) { + if (!initialising) { DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n", fdc, status, i); show_floppy(); @@ -1200,7 +1218,7 @@ static int result(void) static int need_more_output(void) { int status; - if( (status = wait_til_ready()) < 0) + if ((status = wait_til_ready()) < 0) return -1; if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY) return MORE_OUTPUT; @@ -1251,7 +1269,7 @@ static int fdc_configure(void) { /* Turn on FIFO */ output_byte(FD_CONFIGURE); - if(need_more_output() != MORE_OUTPUT) + if (need_more_output() != MORE_OUTPUT) return 0; output_byte(0); output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf)); @@ -1306,7 +1324,7 @@ static void fdc_specify(void) /* chose the default rate table, not the one * where 1 = 2 Mbps */ output_byte(FD_DRIVESPEC); - if(need_more_output() == MORE_OUTPUT) { + if (need_more_output() == MORE_OUTPUT) { output_byte(UNIT(current_drive)); output_byte(0xc0); } @@ -1747,14 +1765,14 @@ void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs) do_print = !handler && print_unex && !initialising; inr = result(); - if(do_print) + if (do_print) print_result("unexpected interrupt", inr); if (inr == 0){ int max_sensei = 4; do { output_byte(FD_SENSEI); inr = result(); - if(do_print) + if (do_print) print_result("sensei", inr); max_sensei--; } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2 && max_sensei); @@ -2327,31 +2345,31 @@ static void rw_interrupt(void) nr_sectors = 0; CODE2SIZE; - if(ST1 & ST1_EOC) + if (ST1 & ST1_EOC) eoc = 1; else eoc = 0; - if(COMMAND & 0x80) + if (COMMAND & 0x80) heads = 2; else heads = 1; - nr_sectors = (((R_TRACK-TRACK) * heads + + nr_sectors = (((R_TRACK-TRACK) * heads + R_HEAD-HEAD) * SECT_PER_TRACK + - R_SECTOR-SECTOR + eoc) << SIZECODE >> 2; + R_SECTOR-SECTOR + eoc) << SIZECODE >> 2; #ifdef FLOPPY_SANITY_CHECK if (nr_sectors / ssize > - (in_sector_offset + current_count_sectors + ssize - 1)/ssize) { + (in_sector_offset + current_count_sectors + ssize - 1) / ssize) { DPRINT("long rw: %x instead of %lx\n", nr_sectors, current_count_sectors); printk("rs=%d s=%d\n", R_SECTOR, SECTOR); printk("rh=%d h=%d\n", R_HEAD, HEAD); printk("rt=%d t=%d\n", R_TRACK, TRACK); printk("heads=%d eoc=%d\n", heads, eoc); - printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, - sector_t, ssize); + printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, + sector_t, ssize); printk("in_sector_offset=%d\n", in_sector_offset); } #endif @@ -2549,22 +2567,24 @@ static inline int check_dma_crossing(char *start, * does not work with MT, hence we can only transfer one head at * a time */ -static void virtualdmabug_workaround(void) { +static void virtualdmabug_workaround(void) +{ int hard_sectors, end_sector; + if(CT(COMMAND) == FD_WRITE) { COMMAND &= ~0x80; /* switch off multiple track mode */ - + hard_sectors = raw_cmd->length >> (7 + SIZECODE); end_sector = SECTOR + hard_sectors - 1; #ifdef FLOPPY_SANITY_CHECK if(end_sector > SECT_PER_TRACK) { - printk("too many sectors %d > %d\n", - end_sector, SECT_PER_TRACK); + printk("too many sectors %d > %d\n", + end_sector, SECT_PER_TRACK); return; } #endif SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points - * to end of transfer */ + * to end of transfer */ } } @@ -2605,7 +2625,7 @@ static int make_raw_rw_request(void) TRACK = CURRENT->sector / max_sector; sector_t = CURRENT->sector % max_sector; if (_floppy->track && TRACK >= _floppy->track) { - if(CURRENT->current_nr_sectors & 1) { + if (CURRENT->current_nr_sectors & 1) { current_count_sectors = 1; return 1; } else @@ -2937,7 +2957,7 @@ static void process_fd_request(void) static void do_fd_request(request_queue_t * q) { - if(usage_count == 0) { + if (usage_count == 0) { printk("warning: usage count=0, CURRENT=%p exiting\n", CURRENT); printk("sect=%ld cmd=%d\n", CURRENT->sector, CURRENT->cmd); return; @@ -3059,7 +3079,7 @@ static void raw_cmd_done(int flag) raw_cmd->flags |= FD_RAW_HARDFAILURE; } else { raw_cmd->reply_count = inr; - if(raw_cmd->reply_count > MAX_REPLIES) + if (raw_cmd->reply_count > MAX_REPLIES) raw_cmd->reply_count=0; for (i=0; i< raw_cmd->reply_count; i++) raw_cmd->reply[i] = reply_buffer[i]; @@ -3384,7 +3404,7 @@ static int get_floppy_geometry(int drive, int type, struct floppy_struct **g) process_fd_request(); *g = current_type[drive]; } - if(!*g) + if (!*g) return -ENODEV; return 0; } @@ -3422,8 +3442,8 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, /* convert compatibility eject ioctls into floppy eject ioctl. * We do this in order to provide a means to eject floppy disks before * installing the new fdutils package */ - if(cmd == CDROMEJECT || /* CD-ROM eject */ - cmd == 0x6470 /* SunOS floppy eject */) { + if (cmd == CDROMEJECT || /* CD-ROM eject */ + cmd == 0x6470 /* SunOS floppy eject */) { DPRINT("obsolete eject ioctl\n"); DPRINT("please use floppycontrol --eject\n"); cmd = FDEJECT; @@ -3469,7 +3489,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, switch (cmd) { case FDEJECT: - if(UDRS->fd_ref != 1) + if (UDRS->fd_ref != 1) /* somebody else has this drive open */ return -EBUSY; LOCK_FDC(drive,1); @@ -3510,9 +3530,9 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, CALL(poll_drive(1, FD_RAW_NEED_DISK)); ret = UDRS->flags; process_fd_request(); - if(ret & FD_VERIFY) + if (ret & FD_VERIFY) return -ENODEV; - if(!(ret & FD_DISK_WRITABLE)) + if (!(ret & FD_DISK_WRITABLE)) return -EROFS; return 0; case FDFMTTRK: @@ -3707,7 +3727,7 @@ static int floppy_open(struct inode * inode, struct file * filp) INFBOUND(try, 16); tmp= (char *)fd_dma_mem_alloc(1024*try); } - if(!tmp && !floppy_track_buffer) { + if (!tmp && !floppy_track_buffer) { fallback_on_nodma_alloc(&tmp, 2048 * try); } if (!tmp && !floppy_track_buffer) { @@ -3715,7 +3735,7 @@ static int floppy_open(struct inode * inode, struct file * filp) RETERR(ENXIO); } if (floppy_track_buffer) { - if(tmp) + if (tmp) fd_dma_mem_free((unsigned long)tmp,try*1024); } else { buffer_min = buffer_max = -1; @@ -3789,9 +3809,9 @@ static int floppy_revalidate(kdev_t dev) int cf; if (UTESTF(FD_DISK_CHANGED) || - UTESTF(FD_VERIFY) || - test_bit(drive, &fake_change) || - NO_GEOM){ + UTESTF(FD_VERIFY) || + test_bit(drive, &fake_change) || + NO_GEOM){ lock_fdc(drive,0); cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY); if (!(cf || test_bit(drive, &fake_change) || NO_GEOM)){ @@ -3894,13 +3914,13 @@ static char __init get_fdc_version(void) return FDC_UNKNOWN; } - if(!fdc_configure()) { + if (!fdc_configure()) { printk(KERN_INFO "FDC %d is an 82072\n",fdc); return FDC_82072; /* 82072 doesn't know CONFIGURE */ } output_byte(FD_PERPENDICULAR); - if(need_more_output() == MORE_OUTPUT) { + if (need_more_output() == MORE_OUTPUT) { output_byte(0); } else { printk(KERN_INFO "FDC %d is an 82072A\n", fdc); @@ -4039,7 +4059,8 @@ static struct param_table { { "unexpected_interrupts", 0, &print_unex, 1, 0 }, { "no_unexpected_interrupts", 0, &print_unex, 0, 0 }, - { "L40SX", 0, &print_unex, 0, 0 } }; + { "L40SX", 0, &print_unex, 0, 0 } +}; static int __init floppy_setup(char *str) { @@ -4055,11 +4076,11 @@ static int __init floppy_setup(char *str) param = ints[1]; else param = config_params[i].def_param; - if(config_params[i].fn) + if (config_params[i].fn) config_params[i]. fn(ints,param, config_params[i].param2); - if(config_params[i].var) { + if (config_params[i].var) { DPRINT("%s=%d\n", str, param); *config_params[i].var = param; } @@ -4088,7 +4109,7 @@ int __init floppy_init(void) int i,unit,drive; - raw_cmd = 0; + raw_cmd = NULL; devfs_handle = devfs_mk_dir (NULL, "floppy", 0, NULL); if (devfs_register_blkdev(MAJOR_NR,"fd",&floppy_fops)) { @@ -4176,7 +4197,7 @@ int __init floppy_init(void) FDCS->address = -1; continue; } - if(can_use_virtual_dma == 2 && FDCS->version < FDC_82072A) + if (can_use_virtual_dma == 2 && FDCS->version < FDC_82072A) can_use_virtual_dma = 0; have_no_fdc = 0; @@ -4194,13 +4215,13 @@ int __init floppy_init(void) if (have_no_fdc) { DPRINT("no floppy controllers found\n"); - floppy_tq.routine = (void *)(void *) empty; + floppy_tq.routine = (void *)(void *) empty; mark_bh(IMMEDIATE_BH); schedule(); - if (usage_count) - floppy_release_irq_and_dma(); + if (usage_count) + floppy_release_irq_and_dma(); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - devfs_unregister_blkdev(MAJOR_NR,"fd"); + devfs_unregister_blkdev(MAJOR_NR,"fd"); } for (drive = 0; drive < N_DRIVE; drive++) { @@ -4327,7 +4348,7 @@ static void floppy_release_irq_and_dma(void) if (floppy_track_buffer && max_buffer_sectors) { tmpsize = max_buffer_sectors*1024; tmpaddr = (unsigned long)floppy_track_buffer; - floppy_track_buffer = 0; + floppy_track_buffer = NULL; max_buffer_sectors = 0; buffer_min = buffer_max = -1; fd_dma_mem_free(tmpaddr, tmpsize); @@ -4360,7 +4381,7 @@ static void floppy_release_irq_and_dma(void) #ifdef MODULE -char *floppy=NULL; +char *floppy; static void __init parse_floppy_cfg_string(char *cfg) { @@ -4368,11 +4389,11 @@ static void __init parse_floppy_cfg_string(char *cfg) while(*cfg) { for(ptr = cfg;*cfg && *cfg != ' ' && *cfg != '\t'; cfg++); - if(*cfg) { + if (*cfg) { *cfg = '\0'; cfg++; } - if(*ptr) + if (*ptr) floppy_setup(ptr); } } @@ -4381,7 +4402,7 @@ int init_module(void) { printk(KERN_INFO "inserting floppy driver for " UTS_RELEASE "\n"); - if(floppy) + if (floppy) parse_floppy_cfg_string(floppy); return floppy_init(); } @@ -4414,7 +4435,7 @@ __setup ("floppy=", floppy_setup); void floppy_eject(void) { int dummy; - if(have_no_fdc) + if (have_no_fdc) return; if(floppy_grab_irq_and_dma()==0) { diff --git a/drivers/block/loop.c b/drivers/block/loop.c index bacb62fba9a..9d518eaf9c3 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -289,7 +289,8 @@ repeat: if (lo->lo_flags & LO_FLAGS_READ_ONLY) goto error_out; } else if (current_request->cmd != READ) { - printk(KERN_ERR "unknown loop device command (%d)?!?", current_request->cmd); + printk(KERN_ERR "unknown loop device command (%d)?!?", + current_request->cmd); goto error_out; } @@ -423,8 +424,28 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) /* Backed by a block device - don't need to hold onto a file structure */ lo->lo_backing_file = NULL; + + if (error) + goto out_putf; } else if (S_ISREG(inode->i_mode)) { struct address_space_operations *aops; + + aops = inode->i_mapping->a_ops; + /* + * If we can't read - sorry. If we only can't write - well, + * it's going to be read-only. + */ + error = -EINVAL; + if (!aops->readpage) + goto out_putf; + + if (!aops->prepare_write || !aops->commit_write) + lo->lo_flags |= LO_FLAGS_READ_ONLY; + + error = get_write_access(inode); + if (error) + goto out_putf; + /* Backed by a regular file - we need to hold onto a file structure for this file. Friggin' NFS can't live without it on write and for reading we use do_generic_file_read(), @@ -437,35 +458,23 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) error = -ENFILE; lo->lo_backing_file = get_empty_filp(); - if (lo->lo_backing_file) { - lo->lo_backing_file->f_mode = file->f_mode; - lo->lo_backing_file->f_pos = file->f_pos; - lo->lo_backing_file->f_flags = file->f_flags; - lo->lo_backing_file->f_owner = file->f_owner; - lo->lo_backing_file->f_dentry = file->f_dentry; - lo->lo_backing_file->f_vfsmnt = file->f_vfsmnt; - lo->lo_backing_file->f_op = file->f_op; - lo->lo_backing_file->private_data = file->private_data; - file_moveto(lo->lo_backing_file, file); - - error = get_write_access(inode); - if (error) { - put_filp(lo->lo_backing_file); - lo->lo_backing_file = NULL; - } + if (lo->lo_backing_file == NULL) { + put_write_access(inode); + goto out_putf; } - aops = inode->i_mapping->a_ops; - /* - * If we can't read - sorry. If we only can't write - well, - * it's going to be read-only. - */ - if (!aops->readpage) - error = -EINVAL; - else if (!aops->prepare_write || !aops->commit_write) - lo->lo_flags |= LO_FLAGS_READ_ONLY; + + lo->lo_backing_file->f_mode = file->f_mode; + lo->lo_backing_file->f_pos = file->f_pos; + lo->lo_backing_file->f_flags = file->f_flags; + lo->lo_backing_file->f_owner = file->f_owner; + lo->lo_backing_file->f_dentry = file->f_dentry; + lo->lo_backing_file->f_vfsmnt = mntget(file->f_vfsmnt); + lo->lo_backing_file->f_op = file->f_op; + lo->lo_backing_file->private_data = file->private_data; + file_moveto(lo->lo_backing_file, file); + + error = 0; } - if (error) - goto out_putf; if (IS_RDONLY (inode) || is_read_only(lo->lo_device)) lo->lo_flags |= LO_FLAGS_READ_ONLY; @@ -477,9 +486,9 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) lo->ioctl = NULL; figure_loop_size(lo); -out_putf: + out_putf: fput(file); -out: + out: if (error) MOD_DEC_USE_COUNT; return error; @@ -530,6 +539,7 @@ static int loop_clr_fd(struct loop_device *lo, kdev_t dev) lo->lo_dentry = NULL; if (lo->lo_backing_file != NULL) { + put_write_access(lo->lo_backing_file->f_dentry->d_inode); fput(lo->lo_backing_file); lo->lo_backing_file = NULL; } else { diff --git a/drivers/block/xor.c b/drivers/block/xor.c index ca1bb15649f..9f54be5a287 100644 --- a/drivers/block/xor.c +++ b/drivers/block/xor.c @@ -15,6 +15,7 @@ * (for example /usr/src/linux/COPYING); if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #define BH_TRACE 0 #include #include diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 9df21affa0b..792832a306e 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -50,6 +50,10 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then bool ' Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS fi tristate ' Specialix SX (and SI) card support' CONFIG_SX + tristate ' Specialix RIO system support' CONFIG_RIO + if [ "$CONFIG_RIO" != "n" ]; then + bool ' Support really old RIO/PCI cards' CONFIG_RIO_OLDPCI + fi bool ' Stallion multiport serial support' CONFIG_STALDRV if [ "$CONFIG_STALDRV" = "y" ]; then tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION @@ -201,7 +205,7 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then fi comment 'Video Adapters' if [ "$CONFIG_I2C_ALGOBIT" = "y" -o "$CONFIG_I2C_ALGOBIT" = "m" ]; then - dep_tristate ' BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C_ALGOBIT + dep_tristate ' BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C_ALGOBIT $CONFIG_SOUND fi dep_tristate ' Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV if [ "$CONFIG_ALL_PPC" = "y" ]; then diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 22deb4fada1..f75dabd180c 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -136,41 +136,38 @@ obj-$(CONFIG_SPECIALIX) += specialix.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o +# If either SX or RIO is in the kernel, generic_serial goes in the +# kernel, and the module is no longer required. The "in kernel" case +# is last to be able to override the module case.... This is an +# example of the new "makefile automatically figures out the +# dependencies".... -- REW -# If either is in the kernel, generic_serial goes in the kernel, and -# the module is no longer required. The "in kernel" case is last to be -# able to override the module case.... This is an example of the new -# "makefile automatically figures out the dependencies".... -- REW +GS=n +ifeq ($(CONFIG_RIO),m) + M_OBJS += generic_serial.o + MOD_SUB_DIRS += rio + GS = m +endif -GS = n ifeq ($(CONFIG_SX),m) GS = m + M_OBJS += sx.o endif -ifeq ($(CONFIG_RIO),m) - GS = m -endif -ifeq ($(CONFIG_SX),y) - GS = y -endif -ifeq ($(CONFIG_RIO),y) - GS = y -endif -obj-$(GS) += generic_serial.o - - - ifeq ($(CONFIG_RIO),y) -obj-y += rio/rio.o generic_serial.o -SUB_DIRS += rio -MOD_SUB_DIRS += rio -else - ifeq ($(CONFIG_RIO),m) - obj-m += generic_serial.o + L_OBJS += rio/rio.o generic_serial.o + SUB_DIRS += rio MOD_SUB_DIRS += rio - endif + GS = y endif +ifeq ($(CONFIG_SX),y) + L_OBJS += sx.o + GS = y +endif + +obj-$(GS) += generic_serial.o + obj-$(CONFIG_ATIXL_BUSMOUSE) += atixlmouse.o obj-$(CONFIG_LOGIBUSMOUSE) += logibusmouse.o obj-$(CONFIG_PRINTER) += lp.o @@ -205,7 +202,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_MIXCOMWD) += mixcomwd.o obj-$(CONFIG_AMIGAMOUSE) += amigamouse.o obj-$(CONFIG_ATARIMOUSE) += atarimouse.o -obj-$(CONFIG_ADBMOUSE) += adbmouse.o busmouse.o +obj-$(CONFIG_ADBMOUSE) += adbmouse.o obj-$(CONFIG_PC110_PAD) += pc110pad.o obj-$(CONFIG_WDT) += wdt.o obj-$(CONFIG_WDTPCI) += wdt_pci.o @@ -232,7 +229,8 @@ else endif endif -obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tda8425.o tda985x.o tea6300.o +obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o \ + tda7432.o tda8425.o tda985x.o tda9875.o tea6300.o tea6420.o ifeq ($(CONFIG_VIDEO_BT848),y) L_TUNERS=y else diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c index f55f992d8c2..b17efd42963 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -43,19 +43,13 @@ #include #include #include - -#ifdef LOCK_I2C_BUS -# error INSTALL ERROR -# error gcc uses the old, obsolete i2c.h include file. Please install the \ - new i2c stack. Please install it by patching the kernel, otherwise \ - gcc will not find the new header files. -#endif +#include #include "bttv.h" #include "tuner.h" -#define DEBUG(x) /* Debug driver */ -#define IDEBUG(x) /* Debug interrupt handler */ +#define DEBUG(x) /* Debug driver */ +#define IDEBUG(x) /* Debug interrupt handler */ #define MIN(a,b) (((a)>(b))?(b):(a)) #define MAX(a,b) (((a)>(b))?(a):(b)) @@ -100,7 +94,7 @@ static int triton1=0; static unsigned long remap[BTTV_MAX]; static unsigned int radio[BTTV_MAX]; static unsigned int card[BTTV_MAX] = { 0, 0, 0, 0 }; -static unsigned int pll[BTTV_MAX] = { 0, 0, 0, 0}; +static unsigned int pll[BTTV_MAX] = { -1, -1, -1, -1}; static unsigned int fieldnr = 0; static unsigned int verbose = 1; static unsigned int debug = 0; @@ -473,7 +467,7 @@ static struct i2c_client i2c_client_template = { NULL }; -static int init_bttv_i2c(struct bttv *btv) +static int __init init_bttv_i2c(struct bttv *btv) { /* i2c bit_adapter */ memcpy(&btv->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); @@ -495,7 +489,7 @@ static int init_bttv_i2c(struct bttv *btv) } /* read I2C */ -static int I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) +static int __init I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) { unsigned char buffer = 0; @@ -520,7 +514,7 @@ static int I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) } /* write I2C */ -static int I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, +static int __init I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, unsigned char b2, int both) { unsigned char buffer[2]; @@ -537,7 +531,7 @@ static int I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, } /* read EEPROM */ -static void readee(struct bttv *btv, unsigned char *eedata, int addr) +static void __init readee(struct bttv *btv, unsigned char *eedata, int addr) { int i; @@ -564,7 +558,7 @@ static struct HAUPPAUGE_TUNER int id; char *name; } -hauppauge_tuner[] = +hauppauge_tuner[] __initdata = { { TUNER_ABSENT, "" }, { TUNER_ABSENT, "External" }, @@ -612,8 +606,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "Temic 4046FM5" }, }; -static void -hauppauge_eeprom(struct bttv *btv) +static void __init hauppauge_eeprom(struct bttv *btv) { if (eeprom_data[9] < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { @@ -622,10 +615,11 @@ hauppauge_eeprom(struct bttv *btv) printk("bttv%d: Hauppauge eeprom: tuner=%s (%d)\n",btv->nr, hauppauge_tuner[eeprom_data[9]].name,btv->tuner_type); } + + return; } -static void -hauppauge_boot_msp34xx(struct bttv *btv) +static void __init hauppauge_boot_msp34xx(struct bttv *btv) { int i; @@ -661,7 +655,7 @@ hauppauge_boot_msp34xx(struct bttv *btv) /* This is basically the same procedure as * used by Alessandro Rubini in his pxc200 * driver, but using BTTV functions */ -static void init_PXC200(struct bttv *btv) +static void __init init_PXC200(struct bttv *btv) { static const int vals[] = { 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, @@ -711,21 +705,25 @@ static struct CARD { unsigned id; int cardnr; char *name; -} cards[] = { +} cards[] __initdata = { { 0x00011002, BTTV_HAUPPAUGE878, "ATI TV Wonder" }, - { 0x00031461, BTTV_AVERMEDIA98, "AVerMedia TVPhone98" }, + { 0x00011461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, + { 0x00031461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, + { 0x00041461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, { 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" }, { 0x1118153b, BTTV_TERRATVALUE, "Terratec TV Value" }, + { 0x1200bd11, BTTV_PINNACLERAVE, "Pinnacle PCTV Rave" }, { 0x13eb0070, BTTV_HAUPPAUGE878, "Hauppauge WinTV" }, { 0x14610002, BTTV_AVERMEDIA98, "Avermedia TVCapture 98" }, { 0x18501851, BTTV_CHRONOS_VS2, "Chronos Video Shuttle II" }, { 0x18521852, BTTV_TYPHOON_TVIEW, "Typhoon TView TV/FM Tuner" }, + { 0x263610b4, BTTV_STB2, "STB TV PCI FM, P/N 6000704" }, { 0x3000144f, BTTV_MAGICTVIEW063, "TView 99 (CPH063)" }, { 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" }, { 0x3002144f, BTTV_MAGICTVIEW061, "Askey Magic TView" }, { 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master" }, + { 0x402010fc, 0 /* no tvcards entry yet */, "I-O Data Co. GV-BCV3/PCI" }, { 0x6606217d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, - { 0x1200bd11, BTTV_PINNACLERAVE, "Pinnacle PCTV Rave" }, { 0, -1, NULL } }; @@ -747,6 +745,13 @@ struct tvcard int tda9840:1; int tda985x:1; int tea63xx:1; + int tea64xx:1; + int tda7432:1; + int tda9875:1; + + /* other settings */ + int pll; + int tuner_type; }; static struct tvcard tvcards[] = @@ -754,149 +759,161 @@ static struct tvcard tvcards[] = /* 0x00 */ { " *** UNKNOWN *** ", 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "MIRO PCTV", 4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Hauppauge old", 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, - 1,1,0,1,0 }, + 1,1,0,1,0,0,0,1, PLL_NONE, -1 }, { "STB", 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0, - 0,1,1,1,1 }, + 0,1,1,1,1,0,0,1, PLL_NONE, -1 }, { "Intel", - 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, - 1,1,1,1,0 }, + 3, 1, 0, -1, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Diamond DTV2000", 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "AVerMedia TVPhone", 3, 1, 0, 3,15, { 2, 3, 1, 1}, {12, 4,11,11, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "MATRIX-Vision MV-Delta", 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0},{0 }, 0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, /* 0x08 */ { "Fly Video II", 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1}, { 0, 0xc00, 0x800, 0x400, 0xc00, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "TurboTV", 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Hauppauge new (bt878)", 4, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0, - 1,1,0,1,0 }, + 1,1,0,1,0,0,0,1, PLL_28, -1 }, { "MIRO PCTV pro", 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "ADS Technologies Channel Surfer TV", 3, 1, 2, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "AVerMedia TVCapture 98", 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, { "Aimslab VHX", 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Zoltrix TV-Max", 3, 1, 0, 2,15, { 2, 3, 1, 1}, {0 , 0, 1 , 0, 10},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, /* 0x10 */ { "Pixelview PlayTV (bt878)", - 3, 1, 0, 2, 0x01e000, { 2, 0, 1, 1}, + 3, 1, 0, 2, 0x01fe00, { 2, 0, 1, 1}, { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 },0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, { "Leadtek WinView 601", 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "AVEC Intercapture", 3, 2, 0, 2, 0, {2, 3, 1, 1}, {1, 0, 0, 0, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "LifeView FlyKit w/o Tuner", 3, 1, -1, -1, 0x8dff00, { 2, 3, 1, 1}, { 0 },0, - 0,0,0,0,0 }, + 0,0,0,0,0,0,0,1, PLL_NONE, -1 }, { "CEI Raffles Card", 3, 3, 0, 2, 0, {2, 3, 1, 1}, {0, 0, 0, 0 ,0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Lucky Star Image World ConferenceTV", - 3, 1, 0, 2, 16777215, { 2, 3, 1, 1}, { 131072, 1, 1638400, 3, 4},0, - 1,1,1,1,0 }, + 3, 1, 0, 2, 0x00fffe07, { 2, 3, 1, 1}, { 131072, 1, 1638400, 3, 4},0, + 1,1,1,1,0,0,0,1, PLL_28, TUNER_PHILIPS_PAL_I }, { "Phoebe Tv Master + FM", 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1},{0, 1, 0x800, 0x400, 0xc00, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Modular Technology MM205 PCTV, bt878", 2, 1, 0, -1, 7, { 2, 3 }, { 0, 0, 0, 0, 0 },0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, /* 0x18 */ { "Askey/Typhoon/Anubis Magic TView CPH051/061 (bt878)", 3, 1, 0, 2, 0xe00, { 2, 3, 1, 1}, {0x400, 0x400, 0x400, 0x400, 0},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, { "Terratec/Vobis TV-Boostar", 3, 1, 0, 2, 16777215 , { 2, 3, 1, 1}, { 131072, 1, 1638400, 3,4},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Newer Hauppauge WinCam (bt878)", 4, 1, 0, 3, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "MAXI TV Video PCI2", 3, 1, 0, 2, 0xffff, { 2, 3, 1, 1}, { 0, 1, 2, 3, 0xc00},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, TUNER_PHILIPS_SECAM }, { "Terratec TerraTV+", 3, 1, 0, 2, 0x70000, { 2, 3, 1, 1}, { 0x20000, 0x30000, 0x00000, 0x10000, 0x40000},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Imagenation PXC200", 5, 1, -1, 4, 0, { 2, 3, 1, 0, 0}, { 0 }, 0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "FlyVideo 98", 3, 1, 0, 2, 0x8dff00, {2, 3, 1, 1}, { 0, 0x8dff00, 0x8df700, 0x8de700, 0x8dff00, 0 },0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "iProTV", 3, 1, 0, 2, 1, { 2, 3, 1, 1}, { 1, 0, 0, 0, 0 },0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, /* 0x20 */ { "Intel Create and Share PCI", 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 4, 4, 4, 4},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Terratec TerraTValue", - 3, 1, 0, 2, 0xf00, { 2, 3, 1, 1}, { 0x500, 0, 0x300, 0x900, 0x900},0, - 1,1,1,1,0 }, + 3, 1, 0, 2, 0xffff00, { 2, 3, 1, 1}, + { 0x500, 0, 0x300, 0x900, 0x900},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, { "Leadtek WinFast 2000", 3, 1, 0, 2, 0xfff000, { 2, 3, 1, 1,0}, { 0x621000,0x620100,0x621100,0x620000,0xE210000,0x620000},0, - 1,1,1,1,1 }, + 1,1,1,1,1,0,0,1, PLL_28, -1 }, { "Chronos Video Shuttle II", 3, 3, 0, 2, 0x1800, { 2, 3, 1, 1}, { 0, 0, 0x1000, 0x1000, 0x0800},0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, { "Typhoon TView TV/FM Tuner", 3, 3, 0, 2, 0x1800, { 2, 3, 1, 1}, { 0, 0x800, 0, 0, 0x1800, 0 },0, - 1,1,1,1,0 }, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, { "PixelView PlayTV pro", 3, 1, 0, 2, 0xff, { 2, 3, 1, 1 }, - { 0x21, 0x20, 0x24, 0x2c, 0x29, 0x29 }, 0 }, + { 0x21, 0x20, 0x24, 0x2c, 0x29, 0x29 }, 0, + 0,0,0,0,0,0,0,1, PLL_28, -1 }, { "TView99 CPH063", - 3, 1, 0, 2, 0x551e00, { 2, 0, 1, 1}, - { 0x551400, 0x551200, 0, 0, 0x551200 }, 0,1,1,1,1,0 }, + 3, 1, 0, 2, 0x551e00, { 2, 3, 1, 1}, + { 0x551400, 0x551200, 0, 0, 0, 0x551200 }, 0, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, { "Pinnacle PCTV Rave", 3, 1, 0, 2, 0x03000F, { 2, 3, 1, 1}, { 2, 0, 0, 0, 1},0, - 1,1,1,1,0 }, - + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + + /* 0x28 */ + { "STB2", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0, + 0,1,1,1,0,1,1,1, PLL_NONE, -1 }, + { "AVerMedia TVPhone 98", + 3, 4, 0, 2, 4, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0, + 1,1,1,1,0,0,0,1, PLL_28, 5 }, + { "ProVideo PV951", /* pic16c54 */ + 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0},0, + 0,0,0,0,0,0,0,0, PLL_28, 1 }, }; #define TVCARDS (sizeof(tvcards)/sizeof(struct tvcard)) -static void -dump_eeprom(struct bttv *btv,int addr) +static void __init dump_eeprom(struct bttv *btv,int addr) { int i; @@ -913,8 +930,7 @@ dump_eeprom(struct bttv *btv,int addr) } } -static int -idcard_eeprom(struct bttv *btv) +static int __init idcard_eeprom(struct bttv *btv) { unsigned id; int i,n; @@ -1477,6 +1493,10 @@ static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int unsigned char lmask, rmask, *p; int W, l, r; int i; + + if (debug) + printk("bttv clip: %dx%d+%d+%d\n",w,h,x,y); + /* bitmap is fixed width, 128 bytes (1024 pixels represented) */ if (x<0) { @@ -1497,10 +1517,10 @@ static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int w=1024-x; l=x>>3; - r=(x+w)>>3; + r=(x+w-1)>>3; W=r-l-1; lmask=lmaskt[x&7]; - rmask=rmaskt[(x+w)&7]; + rmask=rmaskt[(x+w-1)&7]; p=clipmap+128*y+l; if (W>0) @@ -1716,8 +1736,7 @@ static void bt848_set_geo(struct bttv *btv, struct tvnorm *tvn; unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&btv->s_lock, flags); tvn=&tvnorms[btv->win.norm]; @@ -1771,7 +1790,7 @@ static void bt848_set_geo(struct bttv *btv, btwrite(format, BT848_COLOR_FMT); btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); - restore_flags(flags); + spin_unlock_irqrestore(&btv->s_lock, flags); } @@ -1835,6 +1854,7 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) { unsigned int *ro, *re; unsigned int *vbuf; + unsigned long flags; if(btv->fbuffer==NULL) { @@ -1870,7 +1890,7 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) if (debug) printk("bttv%d: cap vgrab: queue %d (%d:%dx%d)\n", btv->nr,mp->frame,mp->format,mp->width,mp->height); - cli(); + spin_lock_irqsave(&btv->s_lock, flags); btv->gbuf[mp->frame].stat = GBUFFER_GRABBING; btv->gbuf[mp->frame].fmt = palette2fmt[mp->format]; btv->gbuf[mp->frame].width = mp->width; @@ -1885,12 +1905,13 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) #endif if (btv->gq_in == btv->gq_out) { + btv->gq_start = 1; btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); } btv->gqueue[btv->gq_in++] = mp->frame; btv->gq_in = btv->gq_in % MAX_GBUFFERS; - sti(); + spin_unlock_irqrestore(&btv->s_lock, flags); btor(3, BT848_CAP_CTL); btor(3, BT848_GPIO_DMA_CTL); return 0; @@ -1911,23 +1932,25 @@ static long bttv_read(struct video_device *v, char *buf, unsigned long count, in todo=count; while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) { + unsigned long flags; + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) return -EFAULT; todo-=q; buf+=q; - cli(); + spin_lock_irqsave(&btv->s_lock, flags); if (todo && q==VBIBUF_SIZE-btv->vbip) { if(nonblock) { - sti(); + spin_unlock_irqrestore(&btv->s_lock, flags); if(count==todo) return -EWOULDBLOCK; return count-todo; } + spin_unlock_irqrestore(&btv->s_lock, flags); interruptible_sleep_on(&btv->vbiq); - sti(); if(signal_pending(current)) { if(todo==count) @@ -1935,7 +1958,8 @@ static long bttv_read(struct video_device *v, char *buf, unsigned long count, in else return count-todo; } - } + } else + spin_unlock_irqrestore(&btv->s_lock, flags); } if (todo) { @@ -1958,7 +1982,7 @@ static void bt848_restart(struct bttv *btv) { if (verbose) printk("bttv%d: resetting chip\n",btv->nr); - btwrite(0xfffffUL, BT848_INT_STAT); + btwrite(~0x0UL, BT848_INT_STAT); btand(~15, BT848_GPIO_DMA_CTL); btwrite(0, BT848_SRESET); btwrite(virt_to_bus(btv->risc_jmp+2), @@ -1984,6 +2008,8 @@ static int bttv_open(struct video_device *dev, int flags) int i,ret; ret = -EBUSY; + + MOD_INC_USE_COUNT; down(&btv->lock); if (btv->user) goto out_unlock; @@ -2005,11 +2031,11 @@ static int bttv_open(struct video_device *dev, int flags) set_pll(btv); btv->user++; up(&btv->lock); - MOD_INC_USE_COUNT; return 0; out_unlock: up(&btv->lock); + MOD_DEC_USE_COUNT; return ret; } @@ -2033,14 +2059,15 @@ static void bttv_close(struct video_device *dev) btread(BT848_I2C); /* This fixes the PCI posting delay */ - /* - * This is sucky but right now I can't find a good way to - * be sure its safe to free the buffer. We wait 5-6 fields - * which is more than sufficient to be sure. - */ - - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ/10); /* Wait 1/10th of a second */ + if (-1 != btv->gq_grab) { + /* + * This is sucky but right now I can't find a good way to + * be sure its safe to free the buffer. We wait 5-6 fields + * which is more than sufficient to be sure. + */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/10); /* Wait 1/10th of a second */ + } /* * We have allowed it to drain. @@ -2726,6 +2753,8 @@ static long vbi_read(struct video_device *v, char *buf, unsigned long count, todo=count; while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) { + unsigned long flags; + if (btv->needs_restart) { down(&btv->lock); bt848_restart(btv); @@ -2736,18 +2765,18 @@ static long vbi_read(struct video_device *v, char *buf, unsigned long count, todo-=q; buf+=q; - cli(); + spin_lock_irqsave(&btv->s_lock, flags); if (todo && q==VBIBUF_SIZE-btv->vbip) { if(nonblock) { - sti(); + spin_unlock_irqrestore(&btv->s_lock, flags); if(count==todo) return -EWOULDBLOCK; return count-todo; } + spin_unlock_irqrestore(&btv->s_lock, flags); interruptible_sleep_on(&btv->vbiq); - sti(); if(signal_pending(current)) { if(todo==count) @@ -2755,7 +2784,8 @@ static long vbi_read(struct video_device *v, char *buf, unsigned long count, else return count-todo; } - } + } else + spin_unlock_irqrestore(&btv->s_lock, flags); } if (todo) { @@ -2784,6 +2814,8 @@ static int vbi_open(struct video_device *dev, int flags) { struct bttv *btv=(struct bttv *)(dev-2); + MOD_INC_USE_COUNT; + down(&btv->lock); if (btv->needs_restart) bt848_restart(btv); @@ -2792,7 +2824,6 @@ static int vbi_open(struct video_device *dev, int flags) bt848_set_risc_jmps(btv,-1); up(&btv->lock); - MOD_INC_USE_COUNT; return 0; } @@ -2864,6 +2895,7 @@ static int radio_open(struct video_device *dev, int flags) struct bttv *btv = (struct bttv *)(dev-1); unsigned long v; + MOD_INC_USE_COUNT; down(&btv->lock); if (btv->user) goto busy_unlock; @@ -2876,11 +2908,11 @@ static int radio_open(struct video_device *dev, int flags) bt848_muxsel(btv,0); up(&btv->lock); - MOD_INC_USE_COUNT; return 0; busy_unlock: up(&btv->lock); + MOD_DEC_USE_COUNT; return -EBUSY; } @@ -2986,7 +3018,7 @@ static struct video_device radio_template= #define TRITON_PEER_CONCURRENCY (1<<3) -static void handle_chipset(void) +static void __init handle_chipset(void) { struct pci_dev *dev = NULL; @@ -3018,7 +3050,7 @@ static void handle_chipset(void) /* can tda9855.c handle this too maybe? */ -static void init_tda9840(struct bttv *btv) +static void __init init_tda9840(struct bttv *btv) { /* Horrible Hack */ I2CWrite(btv, I2C_TDA9840, TDA9840_SW, 0x2a, 1); /* sound mode switching */ @@ -3034,17 +3066,16 @@ static void init_tda9840(struct bttv *btv) /* Figure out card and tuner type */ -static void idcard(int i) +static void __init idcard(struct bttv *btv) { - struct bttv *btv = &bttvs[i]; int type,eeprom = 0; btwrite(0, BT848_GPIO_OUT_EN); - DEBUG(printk(KERN_DEBUG "bttv%d: GPIO: 0x%08x\n", i, btread(BT848_GPIO_DATA))); + DEBUG(printk(KERN_DEBUG "bttv%d: GPIO: 0x%08x\n", btv->nr, btread(BT848_GPIO_DATA))); /* Default the card to the user-selected one. */ - if (card[i] >= 0 && card[i] < TVCARDS) - btv->type=card[i]; + if (card[btv->nr] >= 0 && card[btv->nr] < TVCARDS) + btv->type=card[btv->nr]; /* If we were asked to auto-detect, then do so! */ if (btv->type == BTTV_UNKNOWN) { @@ -3063,7 +3094,7 @@ static void idcard(int i) btv->type=BTTV_HAUPPAUGE; } - /* STB cards have a eeprom @ 0xae */ + /* STB cards have a eeprom @ 0xae (old bt848) */ } else if (I2CRead(btv, I2C_STBEE, "eeprom")>=0) { btv->type=BTTV_STB; } @@ -3078,20 +3109,20 @@ static void idcard(int i) } /* print which board we have found */ - printk(KERN_INFO "bttv%d: model: ",btv->nr); - sprintf(btv->video_dev.name,"BT%d%s(%.22s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", tvcards[btv->type].name); - printk("%s\n",btv->video_dev.name); + printk(KERN_INFO "bttv%d: model: %s\n",btv->nr,btv->video_dev.name); + /* board specific initialisations */ if (btv->type == BTTV_MIRO || btv->type == BTTV_MIROPRO) { /* auto detect tuner for MIRO cards */ btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7; } if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) { + /* pick up some config infos from the eeprom */ if (0xa0 != eeprom) { eeprom = 0xa0; readee(btv,eeprom_data,0xa0); @@ -3099,32 +3130,42 @@ static void idcard(int i) hauppauge_eeprom(btv); hauppauge_boot_msp34xx(btv); } - if (btv->type == BTTV_MAXI) { - /* PHILIPS FI1216MK2 tuner (PAL/SECAM) */ - btv->tuner_type=TUNER_PHILIPS_SECAM; - } - if (btv->type == BTTV_PXC200) init_PXC200(btv); - - if (btv->type == BTTV_CONFERENCETV) - btv->tuner_type = 1; - - if (btv->type == BTTV_HAUPPAUGE878 || - btv->type == BTTV_CONFERENCETV || - btv->type == BTTV_PIXVIEWPLAYTV || - btv->type == BTTV_AVERMEDIA98 || - btv->type == BTTV_MAGICTVIEW061 || - btv->type == BTTV_MAGICTVIEW063 || - btv->type == BTTV_CHRONOS_VS2 || - btv->type == BTTV_TYPHOON_TVIEW || - btv->type == BTTV_PXELVWPLTVPRO || - btv->type == BTTV_WINFAST2000) { - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - } - if (btv->tuner_type != -1) + + /* pll configuration */ + if (!(btv->id==848 && btv->revision==0x11)) { + /* defaults from card list */ + if (PLL_28 == tvcards[btv->type].pll) { + btv->pll.pll_ifreq=28636363; + btv->pll.pll_crystal=BT848_IFORM_XT0; + } + /* insmod options can override */ + switch (pll[btv->nr]) { + case 0: /* none */ + btv->pll.pll_crystal = 0; + btv->pll.pll_ifreq = 0; + btv->pll.pll_ofreq = 0; + break; + case 1: /* 28 MHz */ + btv->pll.pll_ifreq = 28636363; + btv->pll.pll_ofreq = 0; + btv->pll.pll_crystal=BT848_IFORM_XT0; + break; + case 2: /* 35 MHz */ + btv->pll.pll_ifreq = 35468950; + btv->pll.pll_ofreq = 0; + btv->pll.pll_crystal=BT848_IFORM_XT1; + break; + } + } + + + /* tuner configuration */ + if (-1 != tvcards[btv->type].tuner_type) + btv->tuner_type = tvcards[btv->type].tuner_type; + if (btv->tuner_type != -1) call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); /* try to detect audio/fader chips */ @@ -3154,12 +3195,28 @@ static void idcard(int i) request_module("tda985x"); } - if (tvcards[btv->type].tea63xx /* && - I2CRead(btv, I2C_TEA6300, "TEA63xx") >= 0 */) { + if (tvcards[btv->type].tda9875 && + I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) { + if (autoload) + request_module("tda9875"); + } + + if (tvcards[btv->type].tda7432 && + I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) { + if (autoload) + request_module("tda7432"); + } + + if (tvcards[btv->type].tea63xx) { if (autoload) request_module("tea6300"); } + if (tvcards[btv->type].tea64xx) { + if (autoload) + request_module("tea6420"); + } + if (tvcards[btv->type].tuner != -1) { if (autoload) request_module("tuner"); @@ -3171,6 +3228,10 @@ static void idcard(int i) static void bt848_set_risc_jmps(struct bttv *btv, int flags) { + unsigned long irq_flags; + + spin_lock_irqsave(&btv->s_lock, irq_flags); + if (-1 == flags) { /* defaults */ flags = 0; @@ -3242,7 +3303,11 @@ static void bt848_set_risc_jmps(struct bttv *btv, int flags) btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_jmp+12)); } - btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); + if (btv->gq_start) { + btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); + } else { + btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); + } btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp)); /* enable cpaturing and DMA */ @@ -3253,18 +3318,17 @@ static void bt848_set_risc_jmps(struct bttv *btv, int flags) bt848_dma(btv, 3); else bt848_dma(btv, 0); + + spin_unlock_irqrestore(&btv->s_lock, irq_flags); } -static int -init_video_dev(struct bttv *btv) +static int __init init_video_dev(struct bttv *btv) { - int num = btv - bttvs; - memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); - idcard(num); + idcard(btv); if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0) return -1; @@ -3273,7 +3337,7 @@ init_video_dev(struct bttv *btv) video_unregister_device(&btv->video_dev); return -1; } - if (radio[num]) + if (radio[btv->nr]) { if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO)<0) { @@ -3285,9 +3349,8 @@ init_video_dev(struct bttv *btv) return 1; } -static int init_bt848(int i) +static int __init init_bt848(struct bttv *btv) { - struct bttv *btv = &bttvs[i]; int j; btv->user=0; @@ -3297,14 +3360,14 @@ static int init_bt848(int i) * might help to make a new card work */ if (verbose >= 2) printk("bttv%d: gpio: out_enable=0x%x, data=0x%x, in=0x%x\n", - i, + btv->nr, btread(BT848_GPIO_OUT_EN), btread(BT848_GPIO_DATA), btread(BT848_GPIO_REG_INP)); /* reset the bt848 */ btwrite(0, BT848_SRESET); - DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n",i,(unsigned long) btv->bt848_mem)); + DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n", btv->nr, (unsigned long) btv->bt848_mem)); /* not registered yet */ btv->video_dev.minor = -1; @@ -3412,7 +3475,7 @@ static int init_bt848(int i) btwrite(0x00, BT848_O_SCLOOP); /* clear interrupt status */ - btwrite(0xfffffUL, BT848_INT_STAT); + btwrite(~0x0UL, BT848_INT_STAT); /* set interrupt mask */ btwrite(btv->triton1| @@ -3452,7 +3515,6 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) astat=stat&btread(BT848_INT_MASK); if (!astat) return; - btwrite(astat,BT848_INT_STAT); IDEBUG(printk ("bttv%d: astat=%08x\n", btv->nr, astat)); IDEBUG(printk ("bttv%d: stat=%08x\n", btv->nr, stat)); @@ -3561,6 +3623,7 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) } if (stat&(8<<28)) { + btv->gq_start = 0; btv->gq_grab = btv->gqueue[btv->gq_out++]; btv->gq_out = btv->gq_out % MAX_GBUFFERS; if (debug) @@ -3613,7 +3676,9 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) { IDEBUG(printk ("bttv%d: IRQ_I2CDONE\n", btv->nr)); } - + + btwrite(astat,BT848_INT_STAT); + count++; if (count > 10) printk (KERN_WARNING "bttv%d: irq loop %d\n", @@ -3633,7 +3698,76 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) * Scan for a Bt848 card, request the irq and map the io memory */ -int configure_bt848(struct pci_dev *dev, int bttv_num) +static void __init bttv_remove(struct pci_dev *pci_dev) +{ + u8 command; + int j; + struct bttv *btv = pci_dev->driver_data; + + /* unregister i2c_bus */ + i2c_bit_del_bus(&btv->i2c_adap); + + /* turn off all capturing, DMA and IRQs */ + btand(~15, BT848_GPIO_DMA_CTL); + + /* first disable interrupts before unmapping the memory! */ + btwrite(0, BT848_INT_MASK); + btwrite(~0x0UL,BT848_INT_STAT); + btwrite(0x0, BT848_GPIO_OUT_EN); + + /* disable PCI bus-mastering */ + pci_read_config_byte(btv->dev, PCI_COMMAND, &command); + /* Should this be &=~ ?? */ + command&=~PCI_COMMAND_MASTER; + pci_write_config_byte(btv->dev, PCI_COMMAND, command); + + /* unmap and free memory */ + for (j = 0; j < gbuffers; j++) + if (btv->gbuf[j].risc) + kfree(btv->gbuf[j].risc); + if (btv->gbuf) + kfree((void *) btv->gbuf); + + if (btv->risc_scr_odd) + kfree((void *) btv->risc_scr_odd); + + if (btv->risc_scr_even) + kfree((void *) btv->risc_scr_even); + + DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%p.\n", btv->risc_jmp)); + if (btv->risc_jmp) + kfree((void *) btv->risc_jmp); + + DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%p.\n", btv->vbibuf)); + if (btv->vbibuf) + vfree((void *) btv->vbibuf); + + free_irq(btv->irq,btv); + DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem)); + if (btv->bt848_mem) + iounmap(btv->bt848_mem); + + if(btv->video_dev.minor!=-1) + video_unregister_device(&btv->video_dev); + if(btv->vbi_dev.minor!=-1) + video_unregister_device(&btv->vbi_dev); + if (radio[btv->nr] && btv->radio_dev.minor != -1) + video_unregister_device(&btv->radio_dev); + + release_mem_region(btv->bt848_adr, + pci_resource_len(btv->dev,0)); + /* wake up any waiting processes + because shutdown flag is set, no new processes (in this queue) + are expected + */ + btv->shutdown=1; + wake_up(&btv->gpioq); + + return; +} + + +static int __init bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { int result; unsigned char command; @@ -3642,6 +3776,8 @@ int configure_bt848(struct pci_dev *dev, int bttv_num) unsigned int cmd; #endif + printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num); + btv=&bttvs[bttv_num]; btv->dev=dev; btv->nr = bttv_num; @@ -3657,15 +3793,15 @@ int configure_bt848(struct pci_dev *dev, int bttv_num) btv->vbip=VBIBUF_SIZE; init_waitqueue_head(&btv->gpioq); + btv->s_lock = SPIN_LOCK_UNLOCKED; btv->shutdown=0; btv->id=dev->device; btv->irq=dev->irq; - btv->bt848_adr=pci_resource_start(dev, 0); - + btv->bt848_adr=pci_resource_start(dev, 0); if (pci_enable_device(dev)) return -EIO; - if (!request_mem_region(pci_resource_start(dev,0), + if (!request_mem_region(btv->bt848_adr, pci_resource_len(dev,0), "bttv")) { return -EBUSY; @@ -3689,29 +3825,7 @@ int configure_bt848(struct pci_dev *dev, int bttv_num) cmd = (cmd | PCI_COMMAND_MEMORY ); pci_write_config_dword(dev, PCI_COMMAND, cmd); #endif - - btv->pll.pll_crystal = 0; - btv->pll.pll_ifreq = 0; - btv->pll.pll_ofreq = 0; - btv->pll.pll_current = 0; - if (!(btv->id==848 && btv->revision==0x11)) { - switch (pll[btv->nr]) { - case 0: - /* off */ - break; - case 1: - /* 28 MHz crystal installed */ - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - break; - case 2: - /* 35 MHz crystal installed */ - btv->pll.pll_ifreq=35468950; - btv->pll.pll_crystal=BT848_IFORM_XT1; - break; - } - } - + #ifdef __sparc__ btv->bt848_mem=(unsigned char *)btv->bt848_adr; #else @@ -3751,119 +3865,53 @@ int configure_bt848(struct pci_dev *dev, int bttv_num) if (!(command&BT878_EN_TBFX)) { printk("bttv: 430FX compatibility could not be enabled\n"); + free_irq(btv->irq,btv); result = -1; goto fail; } } + + dev->driver_data = btv; + + if(init_bt848(btv) < 0) { + bttv_remove(dev); + return -EIO; + } + + bttv_num++; + return 0; fail: - release_mem_region(pci_resource_start(btv->dev,0), + release_mem_region(btv->bt848_adr, pci_resource_len(btv->dev,0)); return result; } -static int find_bt848(void) -{ - struct pci_dev *dev; - int result=0; - - bttv_num=0; - - pci_for_each_dev(dev) { - if (dev->vendor == PCI_VENDOR_ID_BROOKTREE) - if ((dev->device == PCI_DEVICE_ID_BT848)|| - (dev->device == PCI_DEVICE_ID_BT849)|| - (dev->device == PCI_DEVICE_ID_BT878)|| - (dev->device == PCI_DEVICE_ID_BT879)) - result=configure_bt848(dev,bttv_num++); - if (result) - return result; - } - if(bttv_num) - printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num); - return bttv_num; -} - -static void release_bttv(void) -{ - u8 command; - int i,j; - struct bttv *btv; - - for (i=0;ii2c_adap); - - /* turn off all capturing, DMA and IRQs */ - btand(~15, BT848_GPIO_DMA_CTL); - - /* first disable interrupts before unmapping the memory! */ - btwrite(0, BT848_INT_MASK); - btwrite(0xffffffffUL,BT848_INT_STAT); - btwrite(0x0, BT848_GPIO_OUT_EN); - - /* disable PCI bus-mastering */ - pci_read_config_byte(btv->dev, PCI_COMMAND, &command); - /* Should this be &=~ ?? */ - command&=~PCI_COMMAND_MASTER; - pci_write_config_byte(btv->dev, PCI_COMMAND, command); - - /* unmap and free memory */ - for (j = 0; j < gbuffers; j++) - if (btv->gbuf[j].risc) - kfree(btv->gbuf[j].risc); - if (btv->gbuf) - kfree((void *) btv->gbuf); - - if (btv->risc_scr_odd) - kfree((void *) btv->risc_scr_odd); - - if (btv->risc_scr_even) - kfree((void *) btv->risc_scr_even); - - DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%p.\n", btv->risc_jmp)); - if (btv->risc_jmp) - kfree((void *) btv->risc_jmp); - - DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%p.\n", btv->vbibuf)); - if (btv->vbibuf) - vfree((void *) btv->vbibuf); - - - free_irq(btv->irq,btv); - DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem)); - if (btv->bt848_mem) - iounmap(btv->bt848_mem); +static struct pci_device_id bttv_pci_tbl[] __initdata = { + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; - if(btv->video_dev.minor!=-1) - video_unregister_device(&btv->video_dev); - if(btv->vbi_dev.minor!=-1) - video_unregister_device(&btv->vbi_dev); - if (radio[btv->nr] && btv->radio_dev.minor != -1) - video_unregister_device(&btv->radio_dev); +MODULE_DEVICE_TABLE(pci, bttv_pci_tbl); - release_mem_region(pci_resource_start(btv->dev,0), - pci_resource_len(btv->dev,0)); - /* wake up any waiting processes - because shutdown flag is set, no new processes (in this queue) - are expected - */ - btv->shutdown=1; - wake_up(&btv->gpioq); - } -} +static struct pci_driver bttv_pci_driver = { + name:"bttv", + id_table:bttv_pci_tbl, + probe:bttv_probe, + remove:bttv_remove, +}; -#ifdef MODULE -int init_module(void) -#else -int init_bttv_cards(struct video_init *unused) -#endif +static int __init bttv_init_module(void) { - int i; + bttv_num = 0; printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n", (BTTV_VERSION_CODE >> 16) & 0xff, @@ -3878,31 +3926,18 @@ int init_bttv_cards(struct video_init *unused) gbuffers,gbufsize/1024,gbuffers*gbufsize/1024); handle_chipset(); - if (find_bt848()<=0) - return -EIO; - /* initialize Bt848s */ - for (i=0; i #include @@ -143,6 +143,8 @@ struct bttv { int tuner_type; int channel; + + spinlock_t s_lock; unsigned int nr; unsigned short id; @@ -183,7 +185,7 @@ struct bttv { struct bttv_gbuf *gbuf; int gqueue[MAX_GBUFFERS]; - int gq_in,gq_out,gq_grab; + int gq_in,gq_out,gq_grab,gq_start; char *fbuffer; struct bttv_pll_info pll; @@ -272,7 +274,13 @@ extern __inline__ void io_st_le32(volatile unsigned *addr, unsigned val) #define BTTV_PXELVWPLTVPRO 0x25 #define BTTV_MAGICTVIEW063 0x26 #define BTTV_PINNACLERAVE 0x27 +#define BTTV_STB2 0x28 +#define BTTV_AVPHONE98 0x29 +#define BTTV_PV951 0x2a +#define PLL_NONE 0 +#define PLL_28 1 +#define PLL_35 2 #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 @@ -289,9 +297,11 @@ extern __inline__ void io_st_le32(volatile unsigned *addr, unsigned val) #define TEA6300 0x04 #define I2C_TSA5522 0xc2 +#define I2C_TDA7432 0x8a +#define I2C_TDA8425 0x82 #define I2C_TDA9840 0x84 #define I2C_TDA9850 0xb6 -#define I2C_TDA8425 0x82 +#define I2C_TDA9875 0xb0 #define I2C_HAUPEE 0xa0 #define I2C_STBEE 0xae #define I2C_VHX 0xc0 diff --git a/drivers/char/epca.c b/drivers/char/epca.c index dd94223bc07..9238ae3eb21 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -4080,9 +4080,10 @@ static struct pci_device_id epca_pci_tbl[] __initdata = { { PCI_VENDOR_DIGI, PCI_DEVICE_XEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xem }, { PCI_VENDOR_DIGI, PCI_DEVICE_CX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_cx }, { PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xrj }, - { 0, }, /* terminate list */ + { 0, } }; +MODULE_DEVICE_TABLE(pci, epca_pci_tbl); int __init init_PCI (void) { /* Begin init_PCI */ diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 212525df759..dfe89b9707b 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -52,6 +52,10 @@ int gs_debug = 0; #define RS_EVENT_WRITE_WAKEUP 1 +#ifdef MODULE +MODULE_PARM(gs_debug, "i"); +#endif + #ifdef DEBUG static void my_hd (unsigned char *addr, int len) { @@ -209,12 +213,9 @@ int gs_write(struct tty_struct * tty, int from_user, if (!port || !port->xmit_buf || !tmp_buf) return -EIO; - /* printk ("from_user = %d.\n", from_user); */ save_flags(flags); if (from_user) { - /* printk ("Going into the semaphore\n"); */ down(&tmp_buf_sem); - /* printk ("got out of the semaphore\n"); */ while (1) { c = count; @@ -363,19 +364,14 @@ static int gs_wait_tx_flushed (void * ptr, int timeout) func_exit(); return -EINVAL; /* This is an error which we don't know how to handle. */ } - gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 1\n"); rcib = gs_real_chars_in_buffer(port->tty); - gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 2\n"); - if(rcib <= 0) { gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n"); func_exit(); return rv; } - gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 3\n"); - /* stop trying: now + twice the time it would normally take + seconds */ end_jiffies = jiffies; if (timeout != MAX_SCHEDULE_TIMEOUT) @@ -520,11 +516,10 @@ void gs_hangup(struct tty_struct *tty) func_enter (); tty = port->tty; - if (!tty) return; + if (!tty) + return; gs_shutdown_port (port); - - /* gs_flush_buffer (tty); */ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE |GS_ACTIVE); port->tty = NULL; port->count = 0; @@ -576,7 +571,6 @@ int block_til_ready(void *port_, struct file * filp) else return -ERESTARTSYS; } - gs_dprintk (GS_DEBUG_BTR, "after hung up\n"); /* @@ -599,7 +593,6 @@ int block_til_ready(void *port_, struct file * filp) } gs_dprintk (GS_DEBUG_BTR, "after subtype\n"); - /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. @@ -613,7 +606,6 @@ int block_til_ready(void *port_, struct file * filp) } gs_dprintk (GS_DEBUG_BTR, "after nonblock\n"); - if (port->flags & ASYNC_CALLOUT_ACTIVE) { if (port->normal_termios.c_cflag & CLOCAL) do_clocal = 1; @@ -622,8 +614,7 @@ int block_til_ready(void *port_, struct file * filp) do_clocal = 1; } - gs_dprintk (GS_DEBUG_BTR, "after clocal check.\n"); - + gs_dprintk (GS_DEBUG_BTR, "after clocal check.\n"); /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in @@ -632,10 +623,10 @@ int block_til_ready(void *port_, struct file * filp) * exit, either normal or abnormal. */ retval = 0; + add_wait_queue(&port->open_wait, &wait); - + gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); - cli(); if (!tty_hung_up_p(filp)) port->count--; @@ -667,7 +658,7 @@ int block_til_ready(void *port_, struct file * filp) } gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n", port->blocked_open); - current->state = TASK_RUNNING; + set_current_state (TASK_RUNNING); remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) port->count++; @@ -687,10 +678,8 @@ void gs_close(struct tty_struct * tty, struct file * filp) struct gs_port *port; func_enter (); - port = (struct gs_port *) tty->driver_data; - gs_dprintk (GS_DEBUG_CLOSE, "tty=%p, port=%p port->tty=%p\n", - tty, port, port->tty); + port = (struct gs_port *) tty->driver_data; if(! port) { func_exit(); @@ -703,9 +692,7 @@ void gs_close(struct tty_struct * tty, struct file * filp) port->tty = tty; } - save_flags(flags); cli(); - if (tty_hung_up_p(filp)) { restore_flags(flags); port->rd->hungup (port); diff --git a/drivers/char/lp.c b/drivers/char/lp.c index fcf0644ffed..90f0343966b 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -146,13 +146,6 @@ struct lp_struct lp_table[LP_NO]; static unsigned int lp_count = 0; -/* Test if printer is ready */ -#define LP_READY(status) ((status) & LP_PBUSY) -/* Test if the printer is not acking the strobe */ -#define LP_NO_ACKING(status) ((status) & LP_PACK) -/* Test if the printer has error conditions */ -#define LP_NO_ERROR(status) ((status) & LP_PERRORP) - #undef LP_DEBUG /* --- low-level port access ----------------------------------- */ @@ -265,6 +258,7 @@ static ssize_t lp_write(struct file * file, const char * buf, parport_set_timeout (lp_table[minor].dev, lp_table[minor].timeout); + if ((retv = lp_check_status (minor)) == 0) do { /* Write the data. */ written = parport_write (port, kbuf, copy_size); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 755c3b31807..40e6c7ba6cd 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -231,9 +231,10 @@ static ssize_t read_kmem(struct file *file, char *buf, { unsigned long p = *ppos; ssize_t read = 0; - ssize_t virtr; + ssize_t virtr = 0; + char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ - if (p < (unsigned long) high_memory) { + if (p < (unsigned long) high_memory) { read = count; if (count > (unsigned long) high_memory - p) read = (unsigned long) high_memory - p; @@ -258,11 +259,27 @@ static ssize_t read_kmem(struct file *file, char *buf, count -= read; } - virtr = vread(buf, (char *)p, count); - if (virtr < 0) - return virtr; - *ppos += p + virtr; - return virtr + read; + kbuf = (char *)__get_free_page(GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + while (count > 0) { + int len = count; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + len = vread(kbuf, (char *)p, len); + if (len && copy_to_user(buf, kbuf, len)) { + free_page((unsigned long)kbuf); + return -EFAULT; + } + count -= len; + buf += len; + virtr += len; + p += len; + } + free_page((unsigned long)kbuf); + *ppos = p; + return virtr + read; } /* diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c index 6b86db95be8..f4252a07122 100644 --- a/drivers/char/msp3400.c +++ b/drivers/char/msp3400.c @@ -871,7 +871,7 @@ static int msp3400c_thread(void *data) /* unmute */ msp3400c_setvolume(client, msp->left, msp->right); - if (msp->watch_stereo) + if (msp->watch_stereo) mod_timer(&msp->wake_stereo, jiffies+5*HZ); if (debug) @@ -1086,7 +1086,7 @@ static int msp3410d_thread(void *data) msp3400c_settreble(client, msp->treble); msp3400c_setvolume(client, msp->left, msp->right); - if (msp->watch_stereo) + if (msp->watch_stereo) mod_timer(&msp->wake_stereo, jiffies+HZ); msp->active = 0; @@ -1236,7 +1236,7 @@ static int msp3400c_mixer_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); - struct i2c_client *client = NULL; + struct i2c_client *client; struct msp3400c *msp; int i; @@ -1246,12 +1246,12 @@ msp3400c_mixer_open(struct inode *inode, struct file *file) if (msp->mixer_num == minor) { client = msps[i]; file->private_data = client; - goto match; + break; } } - return -ENODEV; + if (MSP3400_MAX == i) + return -ENODEV; -match: /* lock bttv in memory while the mixer is in use */ if (client->adapter->inc_use) client->adapter->inc_use(client->adapter); @@ -1265,8 +1265,8 @@ msp3400c_mixer_release(struct inode *inode, struct file *file) { struct i2c_client *client = file->private_data; - if (client->adapter->inc_use) - client->adapter->inc_use(client->adapter); + if (client->adapter->dec_use) + client->adapter->dec_use(client->adapter); MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/char/rio/func.h b/drivers/char/rio/func.h index f51eeb0bb5a..3cf72f3e463 100644 --- a/drivers/char/rio/func.h +++ b/drivers/char/rio/func.h @@ -168,4 +168,6 @@ extern int rio_minor (kdev_t device); extern int rio_ismodem (kdev_t device); extern void rio_udelay (int usecs); +extern void rio_start_card_running (struct Host * HostP); + #endif /* __func_h_def */ diff --git a/drivers/char/rio/host.h b/drivers/char/rio/host.h index 6a155c1a6c4..12c47eae211 100644 --- a/drivers/char/rio/host.h +++ b/drivers/char/rio/host.h @@ -56,16 +56,16 @@ struct Host uchar Mode; /* Control stuff */ uchar Slot; /* Slot */ volatile caddr_t Caddr; /* KV address of DPRAM */ - volatile struct DpRam *CardP; /* KV address of DPRAM, with overlay */ + volatile struct DpRam *CardP; /* KV address of DPRAM, with overlay */ paddr_t PaddrP; /* Phys. address of DPRAM */ char Name[MAX_NAME_LEN]; /* The name of the host */ uint UniqueNum; /* host unique number */ - spinlock_t HostLock; /* Lock structure for MPX */ - /*struct pci_devinfo PciDevInfo; *//* PCI Bus/Device/Function stuff */ + spinlock_t HostLock; /* Lock structure for MPX */ + /*struct pci_devinfo PciDevInfo; *//* PCI Bus/Device/Function stuff */ /*struct lockb HostLock; *//* Lock structure for MPX */ uint WorkToBeDone; /* set to true each interrupt */ uint InIntr; /* Being serviced? */ - uint IntSrvDone; /* host's interrupt has been serviced */ + uint IntSrvDone;/* host's interrupt has been serviced */ int (*Copy)( caddr_t, caddr_t, int ); /* copy func */ struct timer_list timer; /* diff --git a/drivers/char/rio/linux_compat.h b/drivers/char/rio/linux_compat.h index 5a4ae63c15f..3b6d2f3b1a9 100644 --- a/drivers/char/rio/linux_compat.h +++ b/drivers/char/rio/linux_compat.h @@ -47,7 +47,6 @@ struct ttystatics { #define SEM_SIGIGNORE 0x1234 - #ifdef DEBUG_SEM #define swait(a,b) printk ("waiting: " __FILE__ " line %d\n", __LINE__) #define ssignal(sem) printk ("signalling: " __FILE__ " line %d\n", __LINE__) diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index 0adc6efedee..625f7022bea 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -109,108 +109,6 @@ of boards in rio.h. You'll have to allocate more majors if you need more than 512 ports.... */ -/* ************************************************************** */ -/* * This section can be removed when 2.0 becomes outdated.... * */ -/* ************************************************************** */ - -#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */ -#define TWO_ZERO -#else -#if LINUX_VERSION_CODE < 0x020209 /* less than 2.2.x */ -#warning "Please use a recent 2.2.x kernel. " -#endif -#endif - - -#ifdef TWO_ZERO - -/* Here is the section that makes the 2.2 compatible driver source - work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2, - and provide for compatibility stuff here if possible. */ - -#include - -#define Get_user(a,b) a = get_user(b) -#define Put_user(a,b) 0,put_user(a,b) -#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) - -static inline int copy_from_user(void *to,const void *from, int c) -{ - memcpy_fromfs(to, from, c); - return 0; -} - -#define pci_present pcibios_present -#define pci_read_config_word pcibios_read_config_word -#define pci_read_config_dword pcibios_read_config_dword - -static inline unsigned char get_irq (unsigned char bus, unsigned char fn) -{ - unsigned char t; - pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &t); - return t; -} - -static inline void *ioremap(unsigned long base, long length) -{ - if (base < 0x100000) return (void *)base; - return vremap (base, length); -} - -#define my_iounmap(x, b) (((long)x<0x100000)?0:vfree ((void*)x)) - -#define capable(x) suser() - -#define queue_task queue_task_irq_off -#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer) -#define signal_pending(current) (current->signal & ~current->blocked) -#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) -#define time_after(t1,t2) (((long)t1-t2) > 0) - - -#define test_and_set_bit(nr, addr) set_bit(nr, addr) -#define test_and_clear_bit(nr, addr) clear_bit(nr, addr) - -/* Not yet implemented on 2.0 */ -#define ASYNC_SPD_SHI -1 -#define ASYNC_SPD_WARP -1 - - -/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it - to the "name" field that does exist. As long as the assignments are - done in the right order, there is nothing to worry about. */ -#define driver_name name - -/* Should be in a header somewhere. They are in tty.h on 2.2 */ -#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */ -#define TTY_HW_COOK_IN 15 /* in hardware - output and input */ - -/* The return type of a "close" routine. */ -#define INT void -#define NO_ERROR /* Nothing */ - -#else - -/* The 2.2.x compatibility section. */ -#include - -#define Get_user(a,b) get_user(a,b) -#define Put_user(a,b) put_user(a,b) -#define get_irq(pdev) pdev->irq - -#define INT int -#define NO_ERROR 0 - -#define my_iounmap(x,b) (iounmap((char *)(b))) - -#endif - -/* ************************************************************** */ -/* * End of compatibility section.. * */ -/* ************************************************************** */ - - - /* Why the hell am I defining these here? */ #define RIO_TYPE_NORMAL 1 #define RIO_TYPE_CALLOUT 2 @@ -373,55 +271,15 @@ static struct real_driver rio_real_driver = { NULL }; - -/* - This driver can spew a whole lot of debugging output at you. If you - need maximum performance, you should disable the DEBUG define. To - aid in debugging in the field, I'm leaving the compile-time debug - features enabled, and disable them "runtime". That allows me to - instruct people with problems to enable debugging without requiring - them to recompile... -*/ -#define DEBUG - -#ifdef DEBUG -#define rio_dprintk(f, str...) if (rio_debug & f) printk (str) -#else -#define rio_dprintk(f, str...) /* nothing */ -#endif - - -#define func_enter() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter " __FUNCTION__ "\n") -#define func_exit() rio_dprintk (RIO_DEBUG_FLOW, "rio: exit " __FUNCTION__ "\n") - -#define func_enter2() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter " __FUNCTION__ \ - "(port %d)\n", port->line) - - - - /* * Firmware loader driver specific routines * */ static struct file_operations rio_fw_fops = { - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - rio_fw_ioctl, - NULL, /* mmap */ - rio_fw_open, -#ifndef TWO_ZERO - NULL, /* flush */ -#endif - rio_fw_release, - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ + ioctl: rio_fw_ioctl, + open: rio_fw_open, + release: rio_fw_release, }; struct miscdevice rio_fw_device = { @@ -446,11 +304,11 @@ static inline int rio_paranoia_check(struct rio_port const * port, KERN_ERR "rio: Warning: null rio port for device %s in %s\n"; if (!port) { - printk(badinfo, kdevname(device), routine); + printk (badinfo, kdevname(device), routine); return 1; } if (port->magic != RIO_MAGIC) { - printk(badmagic, kdevname(device), routine); + printk (badmagic, kdevname(device), routine); return 1; } @@ -468,15 +326,15 @@ void my_hd (void *ad, int len) unsigned char *addr = ad; for (i=0;i 0x7f)?'.':ch)); + rio_dprintk (RIO_DEBUG_PARAM, "%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch)); } - printk ("\n"); + rio_dprintk (RIO_DEBUG_PARAM, "\n"); } } #else @@ -568,21 +426,26 @@ static int rio_set_real_termios (void *ptr) void rio_reset_interrupt (struct Host *HostP) { + func_enter(); + switch( HostP->Type ) { case RIO_AT: case RIO_MCA: case RIO_PCI: WBYTE(HostP->ResetInt , 0xff); } + + func_exit(); } static void rio_interrupt (int irq, void *ptr, struct pt_regs *regs) { struct Host *HostP; + func_enter (); - HostP = &p->RIOHosts[(long)ptr]; - /* func_enter (); */ + HostP = (struct Host*)ptr; /* &p->RIOHosts[(long)ptr]; */ + rio_dprintk (RIO_DEBUG_IFLOW, "rio: enter rio_interrupt (%d/%d)\n", irq, HostP->Ivec); @@ -627,7 +490,7 @@ static void rio_interrupt (int irq, void *ptr, struct pt_regs *regs) } } #endif - + rio_dprintk (RIO_DEBUG_IFLOW, "rio: We've have noticed the interrupt\n"); if (HostP->Ivec == irq) { /* Tell the card we've noticed the interrupt. */ rio_reset_interrupt (HostP); @@ -649,7 +512,7 @@ static void rio_interrupt (int irq, void *ptr, struct pt_regs *regs) clear_bit (RIO_BOARD_INTR_LOCK, &HostP->locks); rio_dprintk (RIO_DEBUG_IFLOW, "rio: exit rio_interrupt (%d/%d)\n", irq, HostP->Ivec); - /* func_exit (); */ + func_exit (); } @@ -657,7 +520,7 @@ static void rio_pollfunc (unsigned long data) { func_enter (); - rio_interrupt (0, (void *)data, NULL); + rio_interrupt (0, &p->RIOHosts[data], NULL); p->RIOHosts[data].timer.expires = jiffies + rio_poll; add_timer (&p->RIOHosts[data].timer); @@ -756,11 +619,11 @@ static void rio_shutdown_port (void * ptr) #if 0 port->gs.flags &= ~ GS_ACTIVE; if (!port->gs.tty) { - printk ("No tty.\n"); + rio_dprintk (RIO_DBUG_TTY, "No tty.\n"); return; } if (!port->gs.tty->termios) { - printk ("No termios.\n"); + rio_dprintk (RIO_DEBUG_TTY, "No termios.\n"); return; } if (port->gs.tty->termios->c_cflag & HUPCL) { @@ -1000,6 +863,8 @@ struct vpd_prom *get_VPD_PROM (struct Host *hp) if (rio_debug & RIO_DEBUG_PROBE) my_hd ((char *)&vpdp, 0x20); + + func_exit(); return &vpdp; } @@ -1099,7 +964,7 @@ static int rio_init_datastructures (void) /* However, the RIO driver allows users to configure their first RTA as the ports numbered 504-511. We therefore need to allocate the whole range. :-( -- REW */ - + #define RI_SZ sizeof(struct rio_info) #define HOST_SZ sizeof(struct Host) #define PORT_SZ sizeof(struct Port *) @@ -1138,11 +1003,20 @@ static int rio_init_datastructures (void) port->gs.close_delay = HZ/2; port->gs.closing_wait = 30 * HZ; port->gs.rd = &rio_real_driver; + + /* + * Initializing wait queue + */ + init_waitqueue_head(&port->gs.open_wait); + init_waitqueue_head(&port->gs.close_wait); + } #else /* We could postpone initializing them to when they are configured. */ #endif + + if (rio_debug & RIO_DEBUG_INIT) { my_hd (&rio_real_driver, sizeof (rio_real_driver)); } @@ -1166,7 +1040,6 @@ static int rio_init_datastructures (void) } -#ifdef MODULE static void rio_release_drivers(void) { func_enter(); @@ -1176,7 +1049,6 @@ static void rio_release_drivers(void) tty_unregister_driver (&rio_driver); func_exit(); } -#endif #ifdef TWO_ZERO #define PDEV unsigned char pci_bus, unsigned pci_fun @@ -1213,7 +1085,7 @@ void fix_rio_pci (PDEV) unsigned int t; #define CNTRL_REG_OFFSET 0x50 -#define CNTRL_REG_GOODVALUE 0x00260000 +#define CNTRL_REG_GOODVALUE 0x18260000 pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase); hwbase &= PCI_BASE_ADDRESS_MEM_MASK; @@ -1294,7 +1166,6 @@ int rio_init(void) pci_read_config_dword (pdev, 0x2c, &tint); tshort = (tint >> 16) & 0xffff; rio_dprintk (RIO_DEBUG_PROBE, "Got a specialix card: %x.\n", tint); - /* rio_dprintk (RIO_DEBUG_PROBE, "pdev = %d/%d (%x)\n", pdev, tint); */ if (tshort != 0x0100) { rio_dprintk (RIO_DEBUG_PROBE, "But it's not a RIO card (%d)...\n", tshort); @@ -1307,49 +1178,51 @@ int rio_init(void) hp = &p->RIOHosts[p->RIONumHosts]; hp->PaddrP = tint & PCI_BASE_ADDRESS_MEM_MASK; hp->Ivec = get_irq (pdev); - if (((1 << hp->Ivec) & rio_irqmask) == 0) hp->Ivec = 0; + if (((1 << hp->Ivec) & rio_irqmask) == 0) + hp->Ivec = 0; hp->CardP = (struct DpRam *) hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); hp->Type = RIO_PCI; hp->Copy = rio_pcicopy; - hp->Mode = RIO_PCI_DEFAULT_MODE; - + hp->Mode = RIO_PCI_BOOT_FROM_RAM; + rio_reset_interrupt (hp); + rio_start_card_running (hp); + rio_dprintk (RIO_DEBUG_PROBE, "Going to test it (%p/%p).\n", (void *)p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr); if (RIOBoardTest( p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr, RIO_PCI, 0 ) == RIO_SUCCESS) { - WBYTE(p->RIOHosts[p->RIONumHosts].ResetInt, 0xff); - p->RIOHosts[p->RIONumHosts].UniqueNum = - ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[0]) &0xFF)<< 0)| - ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[1]) &0xFF)<< 8)| - ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[2]) &0xFF)<<16)| - ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[3]) &0xFF)<<24); - rio_dprintk (RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", - p->RIOHosts[p->RIONumHosts].UniqueNum); - -#if 1 - fix_rio_pci (pdev); -#endif - p->RIOLastPCISearch = RIO_SUCCESS; - p->RIONumHosts++; - found++; + rio_dprintk (RIO_DEBUG_INIT, "Done RIOBoardTest\n"); + WBYTE(p->RIOHosts[p->RIONumHosts].ResetInt, 0xff); + p->RIOHosts[p->RIONumHosts].UniqueNum = + ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[0]) &0xFF)<< 0)| + ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[1]) &0xFF)<< 8)| + ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[2]) &0xFF)<<16)| + ((RBYTE(p->RIOHosts[p->RIONumHosts].Unique[3]) &0xFF)<<24); + rio_dprintk (RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", + p->RIOHosts[p->RIONumHosts].UniqueNum); + + fix_rio_pci (pdev); + p->RIOLastPCISearch = RIO_SUCCESS; + p->RIONumHosts++; + found++; } else { - my_iounmap (p->RIOHosts[p->RIONumHosts].PaddrP, - p->RIOHosts[p->RIONumHosts].Caddr); + my_iounmap (p->RIOHosts[p->RIONumHosts].PaddrP, + p->RIOHosts[p->RIONumHosts].Caddr); } - + #ifdef TWO_ZERO } /* We have two variants with the opening brace, so to prevent */ #else } /* Emacs from getting confused we have two closing braces too. */ #endif - /* Then look for the older PCI card.... : */ #ifndef TWO_ZERO + /* These older PCI cards have problems (only byte-mode access is supported), which makes them a bit awkward to support. They also have problems sharing interrupts. Be careful. @@ -1374,15 +1247,21 @@ int rio_init(void) hp = &p->RIOHosts[p->RIONumHosts]; hp->PaddrP = tint & PCI_BASE_ADDRESS_MEM_MASK; hp->Ivec = get_irq (pdev); - if (((1 << hp->Ivec) & rio_irqmask) == 0) hp->Ivec = 0; + if (((1 << hp->Ivec) & rio_irqmask) == 0) + hp->Ivec = 0; hp->Ivec |= 0x8000; /* Mark as non-sharable */ hp->CardP = (struct DpRam *) hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); hp->Type = RIO_PCI; hp->Copy = rio_pcicopy; - hp->Mode = RIO_PCI_DEFAULT_MODE; - - rio_dprintk (RIO_DEBUG_PROBE, "Going to test it (%p/%p).\n", + hp->Mode = RIO_PCI_BOOT_FROM_RAM; + + rio_dprintk (RIO_DEBUG_PROBE, "Ivec: %x\n", hp->Ivec); + rio_dprintk (RIO_DEBUG_PROBE, "Mode: %x\n", hp->Mode); + + rio_reset_interrupt (hp); + rio_start_card_running (hp); + rio_dprintk (RIO_DEBUG_PROBE, "Going to test it (%p/%p).\n", (void *)p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr); if (RIOBoardTest( p->RIOHosts[p->RIONumHosts].PaddrP, @@ -1425,11 +1304,15 @@ int rio_init(void) hp->CardP = (struct DpRam *) hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); hp->Type = RIO_AT; - hp->Copy = rio_pcicopy; + hp->Copy = rio_pcicopy; /* AT card PCI???? - PVDL + * -- YES! this is now a normal copy. Only the + * old PCI card uses the special PCI copy. + * Moreover, the ISA card will work with the + * special PCI copy anyway. -- REW */ hp->Mode = 0; vpdp = get_VPD_PROM (hp); - + rio_dprintk (RIO_DEBUG_PROBE, "Got VPD ROM\n"); okboard = 0; if ((strncmp (vpdp->identifier, RIO_ISA_IDENT, 16) == 0) || (strncmp (vpdp->identifier, RIO_ISA2_IDENT, 16) == 0) || @@ -1460,11 +1343,21 @@ int rio_init(void) if (hp->Ivec) { int mode = SA_SHIRQ; if (hp->Ivec & 0x8000) {mode = 0; hp->Ivec &= 0x7fff;} - if (request_irq (hp->Ivec, rio_interrupt, mode, "rio", (void *)i)) { - printk(KERN_ERR "rio: Cannot allocate irq %d.\n", hp->Ivec); - hp->Ivec = 0; + rio_dprintk (RIO_DEBUG_INIT, "Requesting interrupt hp: %p rio_interrupt: %d Mode: %x\n", hp,hp->Ivec, hp->Mode); + retval = request_irq (hp->Ivec, rio_interrupt, mode, "rio", hp); + rio_dprintk (RIO_DEBUG_INIT, "Return value from request_irq: %d\n", retval); + if (retval) { + printk(KERN_ERR "rio: Cannot allocate irq %d.\n", hp->Ivec); + hp->Ivec = 0; } rio_dprintk (RIO_DEBUG_INIT, "Got irq %d.\n", hp->Ivec); + if (hp->Ivec != 0){ + rio_dprintk (RIO_DEBUG_INIT, "Enabling interrupts on rio card.\n"); + hp->Mode |= RIO_PCI_INT_ENABLE; + } else + hp->Mode &= !RIO_PCI_INT_ENABLE; + rio_dprintk (RIO_DEBUG_INIT, "New Mode: %x\n", hp->Mode); + rio_start_card_running (hp); } /* Init the timer "always" to make sure that it can safely be deleted when we unload... */ @@ -1481,7 +1374,7 @@ int rio_init(void) } if (found) { - printk (KERN_INFO "rio: total of %d boards detected.\n", found); + rio_dprintk (RIO_DEBUG_INIT, "rio: total of %d boards detected.\n", found); if (misc_register(&rio_fw_device) < 0) { printk(KERN_ERR "RIO: Unable to register firmware loader driver.\n"); @@ -1506,7 +1399,7 @@ void cleanup_module(void) for (i=0,hp=p->RIOHosts;iRIONumHosts;i++, hp++) { RIOHostReset (hp->Type, hp->CardP, hp->Slot); if (hp->Ivec) { - free_irq (hp->Ivec, (void *)i); + free_irq (hp->Ivec, hp); rio_dprintk (RIO_DEBUG_INIT, "freed irq %d.\n", hp->Ivec); } /* It is safe/allowed to del_timer a non-active timer */ diff --git a/drivers/char/rio/rio_linux.h b/drivers/char/rio/rio_linux.h index 5141355ea7f..c8b72bdce94 100644 --- a/drivers/char/rio/rio_linux.h +++ b/drivers/char/rio/rio_linux.h @@ -23,13 +23,13 @@ * Version 1.0 -- July, 1999. * */ +#include #define RIO_NBOARDS 4 #define RIO_PORTSPERBOARD 128 #define RIO_NPORTS (RIO_NBOARDS * RIO_PORTSPERBOARD) #ifdef __KERNEL__ -#include #define RIO_MAGIC 0x12345678 @@ -160,3 +160,28 @@ static inline void *rio_memcpy_fromio (void *dest, void *source, int n) #define rio_memcpy_fromio memcpy_fromio #endif +#define DEBUG + + +/* + This driver can spew a whole lot of debugging output at you. If you + need maximum performance, you should disable the DEBUG define. To + aid in debugging in the field, I'm leaving the compile-time debug + features enabled, and disable them "runtime". That allows me to + instruct people with problems to enable debugging without requiring + them to recompile... +*/ + +#ifdef DEBUG +#define rio_dprintk(f, str...) if (rio_debug & f) printk (str) +#define func_enter() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter " __FUNCTION__ "\n") +#define func_exit() rio_dprintk (RIO_DEBUG_FLOW, "rio: exit " __FUNCTION__ "\n") +#define func_enter2() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter " __FUNCTION__ \ + "(port %d)\n", port->line) +#else +#define rio_dprintk(f, str...) /* nothing */ +#define func_enter() +#define func_exit() +#define func_enter2() +#endif + diff --git a/drivers/char/rio/rioboot.c b/drivers/char/rio/rioboot.c index 7a06b0dc996..8168f213339 100644 --- a/drivers/char/rio/rioboot.c +++ b/drivers/char/rio/rioboot.c @@ -81,7 +81,6 @@ static char *_rioboot_c_sccs_ = "@(#)rioboot.c 1.3"; #include "cmdblk.h" #include "route.h" - static uchar RIOAtVec2Ctrl[] = { @@ -113,6 +112,8 @@ struct DownLoad * rbp; { int offset; + func_enter (); + /* Linux doesn't allow you to disable interrupts during a "copyin". (Crash when a pagefault occurs). */ /* disable(oldspl); */ @@ -126,6 +127,7 @@ struct DownLoad * rbp; rio_dprint(RIO_DEBUG_BOOT, ("RTA Boot Code Too Large!\n")); p->RIOError.Error = HOST_FILE_TOO_LARGE; /* restore(oldspl); */ + func_exit (); return ENOMEM; } @@ -133,6 +135,7 @@ struct DownLoad * rbp; rio_dprint(RIO_DEBUG_BOOT, ("RTA Boot Code : BUSY BUSY BUSY!\n")); p->RIOError.Error = BOOT_IN_PROGRESS; /* restore(oldspl); */ + func_exit (); return EBUSY; } @@ -160,6 +163,7 @@ struct DownLoad * rbp; rio_dprint(RIO_DEBUG_BOOT, ("Bad data copy from user space\n")); p->RIOError.Error = COPYIN_FAILED; /* restore(oldspl); */ + func_exit (); return EFAULT; } @@ -171,9 +175,61 @@ struct DownLoad * rbp; p->RIOBootCount = rbp->Count; /* restore(oldspl); */ + func_exit(); return 0; } +void rio_start_card_running (struct Host * HostP) +{ + func_enter (); + + switch ( HostP->Type ) { + case RIO_AT: + rio_dprint(RIO_DEBUG_BOOT, ("Start ISA card running\n")); + WBYTE(HostP->Control, + BOOT_FROM_RAM | EXTERNAL_BUS_ON + | HostP->Mode + | RIOAtVec2Ctrl[HostP->Ivec & 0xF] ); + break; + +#ifdef FUTURE_RELEASE + case RIO_MCA: + /* + ** MCA handles IRQ vectors differently, so we don't write + ** them to this register. + */ + rio_dprint(RIO_DEBUG_BOOT, ("Start MCA card running\n")); + WBYTE(HostP->Control, McaTpBootFromRam | McaTpBusEnable | HostP->Mode); + break; + + case RIO_EISA: + /* + ** EISA is totally different and expects OUTBZs to turn it on. + */ + rio_dprint(RIO_DEBUG_BOOT, NULL,DBG_DAEMON,"Start EISA card running\n"); + OUTBZ( HostP->Slot, EISA_CONTROL_PORT, HostP->Mode | RIOEisaVec2Ctrl[HostP->Ivec] | EISA_TP_RUN | EISA_TP_BUS_ENABLE | EISA_TP_BOOT_FROM_RAM ); + break; +#endif + + case RIO_PCI: + /* + ** PCI is much the same as MCA. Everything is once again memory + ** mapped, so we are writing to memory registers instead of io + ** ports. + */ + rio_dprint(RIO_DEBUG_BOOT, ("Start PCI card running\n")); + WBYTE(HostP->Control, PCITpBootFromRam | PCITpBusEnable | HostP->Mode); + break; + default: + rio_dprint(RIO_DEBUG_BOOT, ("Unknown host type %d\n",HostP->Type)); + break; + } +/* + printk (KERN_INFO "Done with starting the card\n"); + func_exit (); +*/ + return; +} /* ** Load in the host boot code - load it directly onto all halted hosts @@ -206,6 +262,10 @@ register struct DownLoad *rbp; for ( host=0; hostRIONumHosts; host++ ) { rio_dprint(RIO_DEBUG_BOOT, ("Attempt to boot host %d\n",host)); HostP = &p->RIOHosts[host]; + + rio_dprint(RIO_DEBUG_BOOT, ("Host Type = 0x%x, Mode = 0x%x, IVec = 0x%x\n", + HostP->Type, HostP->Mode, HostP->Ivec ) ); + if ( (HostP->Flags & RUN_STATE) != RC_WAITING ) { rio_dprint(RIO_DEBUG_BOOT, ("%s %d already running\n","Host",host)); @@ -233,6 +293,7 @@ register struct DownLoad *rbp; if ( p->RIOConf.HostLoadBase < rbp->Count ) { rio_dprint(RIO_DEBUG_BOOT, ("Bin too large\n")); p->RIOError.Error = HOST_FILE_TOO_LARGE; + func_exit (); return EFBIG; } /* @@ -259,6 +320,7 @@ register struct DownLoad *rbp; if ( !DownCode ) { rio_dprint(RIO_DEBUG_BOOT, ("No system memory available\n")); p->RIOError.Error = NOT_ENOUGH_CORE_FOR_PCI_COPY; + func_exit (); return ENOMEM; } bzero(DownCode, rbp->Count); @@ -266,6 +328,7 @@ register struct DownLoad *rbp; if ( copyin((int)rbp->DataP,DownCode,rbp->Count)==COPYFAIL ) { rio_dprint(RIO_DEBUG_BOOT, ("Bad copyin of host data\n")); p->RIOError.Error = COPYIN_FAILED; + func_exit (); return EFAULT; } @@ -276,6 +339,7 @@ register struct DownLoad *rbp; else if ( copyin((int)rbp->DataP,StartP,rbp->Count)==COPYFAIL ) { rio_dprint(RIO_DEBUG_BOOT, ("Bad copyin of host data\n")); p->RIOError.Error = COPYIN_FAILED; + func_exit (); return EFAULT; } @@ -405,47 +469,8 @@ register struct DownLoad *rbp; rio_dprint(RIO_DEBUG_BOOT, ("Host Type = 0x%x, Mode = 0x%x, IVec = 0x%x\n", HostP->Type, HostP->Mode, HostP->Ivec ) ); - switch ( HostP->Type ) { - case RIO_AT: - rio_dprint(RIO_DEBUG_BOOT, ("Start ISA card running\n")); - WBYTE(HostP->Control, - BOOT_FROM_RAM | EXTERNAL_BUS_ON - | HostP->Mode - | RIOAtVec2Ctrl[HostP->Ivec & 0xF] ); - break; + rio_start_card_running(HostP); -#ifdef FUTURE_RELEASE - case RIO_MCA: - /* - ** MCA handles IRQ vectors differently, so we don't write - ** them to this register. - */ - rio_dprint(RIO_DEBUG_BOOT, ("Start MCA card running\n")); - WBYTE(HostP->Control, McaTpBootFromRam | McaTpBusEnable | HostP->Mode); - break; - - case RIO_EISA: - /* - ** EISA is totally different and expects OUTBZs to turn it on. - */ - rio_dprint(RIO_DEBUG_BOOT, NULL,DBG_DAEMON,"Start EISA card running\n"); - OUTBZ( HostP->Slot, EISA_CONTROL_PORT, HostP->Mode | RIOEisaVec2Ctrl[HostP->Ivec] | EISA_TP_RUN | EISA_TP_BUS_ENABLE | EISA_TP_BOOT_FROM_RAM ); - break; -#endif - - case RIO_PCI: - /* - ** PCI is much the same as MCA. Everything is once again memory - ** mapped, so we are writing to memory registers instead of io - ** ports. - */ - rio_dprint(RIO_DEBUG_BOOT, ("Start PCI card running\n")); - WBYTE(HostP->Control, PCITpBootFromRam | PCITpBusEnable | HostP->Mode); - break; - default: - rio_dprint(RIO_DEBUG_BOOT, ("Unknown host type %d\n",HostP->Type)); - break; - } rio_dprint(RIO_DEBUG_BOOT, ("Set control port\n")); /* @@ -453,9 +478,10 @@ register struct DownLoad *rbp; ** pointer: */ for ( wait_count=0; (wait_countRIOConf.StartupTime)&& - (RWORD(HostP->__ParmMapR)==OldParmMap); wait_count++ ) { + (RWORD(HostP->__ParmMapR)==OldParmMap); wait_count++ ) { rio_dprint(RIO_DEBUG_BOOT, ("Checkout %d, 0x%x\n",wait_count,RWORD(HostP->__ParmMapR))); delay(HostP, HUNDRED_MS); + } /* @@ -613,6 +639,7 @@ register struct DownLoad *rbp; p->RIOSystemUp++; rio_dprint(RIO_DEBUG_BOOT, ("Done everything %x\n", HostP->Ivec)); + func_exit (); return 0; } diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index 6f2e43da07a..835d815d35b 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -43,7 +43,6 @@ static char *_riocmd_c_sccs_ = "@(#)riocmd.c 1.2"; #include #include - #include #include diff --git a/drivers/char/rio/rioctrl.c b/drivers/char/rio/rioctrl.c index 27b46398aa7..fa68646d5c1 100644 --- a/drivers/char/rio/rioctrl.c +++ b/drivers/char/rio/rioctrl.c @@ -201,6 +201,8 @@ int su; int retval = 0; unsigned long flags; + func_enter (); + /* Confuse teh compiler to think that we've initialized these */ Host=0; PortP = NULL; @@ -1776,6 +1778,8 @@ RIO_DEBUG_CTRL, if (su) } rio_dprint(RIO_DEBUG_CTRL, ("INVALID DAEMON IOCTL 0x%x\n",cmd)); p->RIOError.Error = IOCTL_COMMAND_UNKNOWN; + + func_exit (); return EINVAL; } diff --git a/drivers/char/rio/rioinit.c b/drivers/char/rio/rioinit.c index 72e8632cd16..59cfdedd5cf 100644 --- a/drivers/char/rio/rioinit.c +++ b/drivers/char/rio/rioinit.c @@ -1414,7 +1414,7 @@ struct rio_info * p; (int)p->RIOHosts, sizeof(struct Host) ) ); for( host=0; hostRIOHosts[host].HostLock = SPIN_LOCK_UNLOCKED; /* Let the first guy takes it */ + spin_lock_init (&p->RIOHosts[host].HostLock); p->RIOHosts[host].timeout_id = 0; /* Let the first guy takes it */ } /* diff --git a/drivers/char/rio/riotty.c b/drivers/char/rio/riotty.c index 3e7236b191c..cda417f3f4d 100644 --- a/drivers/char/rio/riotty.c +++ b/drivers/char/rio/riotty.c @@ -89,7 +89,6 @@ static char *_riotty_c_sccs_ = "@(#)riotty.c 1.3"; #include "list.h" #include "sam.h" - #if 0 static void ttyseth_pv(struct Port *, struct ttystatics *, struct termios *sg, int); @@ -154,13 +153,21 @@ riotopen(struct tty_struct * tty, struct file * filp) unsigned long flags; int retval = 0; + func_enter (); + + /* Make sure driver_data is NULL in case the rio isn't booted jet. Else gs_close + is going to oops. + */ + tty->driver_data = NULL; + SysPort = rio_minor (tty->device); Modem = rio_ismodem (tty->device); if ( p->RIOFailed ) { rio_dprint(RIO_DEBUG_TTY, ("System initialisation failed\n")); pseterr(ENXIO); - return 0; + func_exit (); + return -ENXIO; } rio_dprint(RIO_DEBUG_TTY, ("port open SysPort %d (%s) (mapped:%d)\n", @@ -176,22 +183,24 @@ riotopen(struct tty_struct * tty, struct file * filp) if (SysPort >= RIO_PORTS) { /* out of range ? */ rio_dprint(RIO_DEBUG_TTY, ("Illegal port number %d\n",SysPort)); pseterr(ENXIO); - return 0; + func_exit(); + return -ENXIO; } /* ** Grab pointer to the port stucture */ PortP = p->RIOPortp[SysPort]; /* Get control struc */ - + rio_dprintk (RIO_DEBUG_TTY, "PortP: %p\n", PortP); if ( !PortP->Mapped ) { /* we aren't mapped yet! */ /* ** The system doesn't know which RTA this port ** corresponds to. */ rio_dprint(RIO_DEBUG_TTY, ("port not mapped into system\n")); + func_exit (); pseterr(ENXIO); - return 0; + return -ENXIO; } tty->driver_data = PortP; @@ -209,7 +218,8 @@ riotopen(struct tty_struct * tty, struct file * filp) if ( (PortP->HostP->Flags & RUN_STATE) != RC_RUNNING ) { rio_dprint(RIO_DEBUG_TTY, ("Host not running\n")); pseterr(ENXIO); - return 0; + func_exit (); + return -ENXIO; } /* @@ -224,12 +234,14 @@ riotopen(struct tty_struct * tty, struct file * filp) do { if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { rio_dprint(RIO_DEBUG_TTY, ("RTA EINTR in delay \n")); + func_exit (); return -EINTR; } if (repeat_this -- <= 0) { rio_dprint(RIO_DEBUG_TTY, ("Waiting for RTA to boot timeout\n")); RIOPreemptiveCmd(p, PortP, FCLOSE ); pseterr(EINTR); + func_exit (); return -EIO; } } while(!(PortP->HostP->Mapping[PortP->RupNum].Flags & RTA_BOOTED)); @@ -237,6 +249,7 @@ riotopen(struct tty_struct * tty, struct file * filp) } else { rio_dprint(RIO_DEBUG_TTY, ("RTA never booted\n")); pseterr(ENXIO); + func_exit (); return 0; } } @@ -249,6 +262,7 @@ riotopen(struct tty_struct * tty, struct file * filp) while (!(PortP->HostP->Mapping[PortP->RupNum].Flags & RTA_BOOTED)) { if (!PortP->WaitUntilBooted) { rio_dprint(RIO_DEBUG_TTY, ("RTA never booted\n")); + func_exit (); return -ENXIO; } @@ -258,10 +272,12 @@ riotopen(struct tty_struct * tty, struct file * filp) */ if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { rio_dprint(RIO_DEBUG_TTY, ("RTA_wait_for_boot: EINTR in delay \n")); + func_exit (); return -EINTR; } if (repeat_this -- <= 0) { rio_dprint(RIO_DEBUG_TTY, ("Waiting for RTA to boot timeout\n")); + func_exit (); return -EIO; } } @@ -276,8 +292,10 @@ riotopen(struct tty_struct * tty, struct file * filp) } #if 0 retval = gs_init_port(&PortP->gs); - if (retval) - return retval; + if (retval){ + func_exit (); + return retval; + } #endif /* @@ -306,6 +324,7 @@ riotopen(struct tty_struct * tty, struct file * filp) rio_dprint(RIO_DEBUG_TTY, ("Port unmapped while closing!\n")); rio_spin_unlock_irqrestore(&PortP->portSem, flags); retval = -ENXIO; + func_exit (); return retval; } @@ -375,6 +394,7 @@ riotopen(struct tty_struct * tty, struct file * filp) if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { rio_dprint(RIO_DEBUG_TTY, ("Waiting for open to finish broken by signal\n")); RIOPreemptiveCmd(p, PortP, FCLOSE ); + func_exit (); return -EINTR; } rio_spin_lock_irqsave(&PortP->portSem, flags); @@ -425,6 +445,7 @@ bombout: tp->tm.c_state &= ~WOPEN; PortP->State &= ~RIO_WOPEN; rio_spin_unlock_irqrestore(&PortP->portSem, flags); + func_exit (); return -EINTR; } } @@ -462,6 +483,7 @@ bombout: rio_spin_unlock_irqrestore(&PortP->portSem, flags); rio_dprint(RIO_DEBUG_TTY, ("Returning from open\n")); + func_exit (); return 0; } diff --git a/drivers/char/rio/unixrup.h b/drivers/char/rio/unixrup.h index 13821a9827f..eddf86278ab 100644 --- a/drivers/char/rio/unixrup.h +++ b/drivers/char/rio/unixrup.h @@ -49,7 +49,7 @@ struct UnixRup uint Id; /* Id number */ uint BaseSysPort; /* SysPort of first tty on this RTA */ uint ModTypes; /* Modules on this RTA */ - spinlock_t RupLock; /* Lock structure for MPX */ + spinlock_t RupLock; /* Lock structure for MPX */ /* struct lockb RupLock; */ /* Lock structure for MPX */ }; diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index bb9da901826..bfe700219b0 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -762,7 +762,7 @@ EXPORT_NO_SYMBOLS; static void rtc_dropped_irq(unsigned long data) { - printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq); + unsigned long freq; spin_lock_irq (&rtc_lock); @@ -773,8 +773,13 @@ static void rtc_dropped_irq(unsigned long data) rtc_irq_data += ((rtc_freq/HZ)<<8); rtc_irq_data &= ~0xff; rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */ + + freq = rtc_freq; + spin_unlock_irq(&rtc_lock); + printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", freq); + /* Now we have new data */ wake_up_interruptible(&rtc_wait); diff --git a/drivers/char/sh-sci.c b/drivers/char/sh-sci.c index 8ab735cdc1c..05bfcb607c7 100644 --- a/drivers/char/sh-sci.c +++ b/drivers/char/sh-sci.c @@ -189,10 +189,35 @@ static void sci_set_termios_cflag(struct sci_port *port) ctrl_out(smr_val, SCSMR); #if defined(CONFIG_SH_SCIF_SERIAL) +#if defined(__sh3__) + { /* For SH7709, SH7709A, SH7729 */ + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = ctrl_inw(SCPCR); + /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ + ctrl_outw(data&0x0fcf, SCPCR); + } +#endif if (C_CRTSCTS(port->gs.tty)) fcr_val |= 0x08; - else + else { +#if defined(__sh3__) + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = ctrl_inw(SCPCR); + /* Clear out SCP7MD1,0, SCP4MD1,0, + Set SCP6MD1,0 = {01} (output) */ + ctrl_outw((data&0x0fcf)|0x1000, SCPCR); + + data = ctrl_inb(SCPDR); + /* Set /RTS2 (bit6) = 0 */ + ctrl_outb(data&0xbf, SCPDR); +#elif defined(__SH4__) ctrl_outw(0x0080, SCSPTR); /* Set RTS = 1 */ +#endif + } ctrl_out(fcr_val, SCFCR); #endif @@ -721,7 +746,7 @@ int __init sci_init(void) int i; for (i=SCI_ERI_IRQ; igs.tty; while (1) { rx_op = sx_read_channel_byte (port, hi_rxopos); @@ -1134,7 +1134,7 @@ inline void sx_receive_chars (struct sx_port *port) /* tty_schedule_flip (tty); */ } - /* func_exit (); */ + func_exit (); } /* Inlined: it is called only once. Remove the inline if you add another @@ -2343,6 +2343,12 @@ static int sx_init_portstructs (int nboards, int nports) #ifdef NEW_WRITE_LOCKING port->gs.port_write_sem = MUTEX; #endif + /* + * Initializing wait queue + */ + init_waitqueue_head(&port->gs.open_wait); + init_waitqueue_head(&port->gs.close_wait); + port++; } } diff --git a/drivers/char/tda7432.c b/drivers/char/tda7432.c new file mode 100644 index 00000000000..4880913106d --- /dev/null +++ b/drivers/char/tda7432.c @@ -0,0 +1,505 @@ +/* + * For the STS-Thompson TDA7432 audio processor chip + * + * Handles audio functions: volume, balance, tone, loudness + * This driver will not complain if used with any + * other i2c device with the same address. + * + * Copyright (c) 2000 Eric Sandeen + * This code is placed under the terms of the GNU General Public License + * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Which was based on tda8425.c by Greg Alexander (c) 1998 + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * set to 2 if you'd like to be inundated with debug messages + * + * loudness - set between 0 and 15 for varying degrees of loudness effect + * + * TODO: + * Implement tone controls + * + * Revision: 0.3 - Fixed silly reversed volume controls. :) + * Revision: 0.2 - Cleaned up #defines + * fixed volume control + * Added I2C_DRIVERID_TDA7432 + * added loudness insmod control + * Revision: 0.1 - initial version + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + +/* This driver ID is brand new, so define it if it's not in i2c-id.h yet */ +#ifndef I2C_DRIVERID_TDA7432 + #define I2C_DRIVERID_TDA7432 27 +#endif + + +MODULE_AUTHOR("Eric Sandeen "); +MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip"); + +MODULE_PARM(debug,"i"); +MODULE_PARM(loudness,"i"); +static int loudness = 0; /* disable loudness by default */ +static int debug = 0; /* insmod parameter */ + + +/* Address to scan (I2C address of this chip) */ +static unsigned short normal_i2c[] = { + I2C_TDA7432 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +/* Structure of address and subaddresses for the tda7432 */ + +struct tda7432 { + int addr; + int input; + int volume; + int tone; + int lf, lr, rf, rr; + int loud; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define dprintk if (debug) printk +#define d2printk if (debug > 1) printk + +/* The TDA7432 is made by STS-Thompson + * http://www.st.com + * http://us.st.com/stonline/books/pdf/docs/4056.pdf + * + * TDA7432: I2C-bus controlled basic audio processor + * + * The TDA7432 controls basic audio functions like volume, balance, + * and tone control (including loudness). It also has four channel + * output (for front and rear). Since most vidcap cards probably + * don't have 4 channel output, this driver will set front & rear + * together (no independent control). + */ + + /* Subaddresses for TDA7432 */ + +#define TDA7432_IN 0x00 /* Input select */ +#define TDA7432_VL 0x01 /* Volume */ +#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */ +#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */ +#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */ +#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */ +#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */ +#define TDA7432_LD 0x07 /* Loudness */ + + + /* Masks for bits in TDA7432 subaddresses */ + +/* Many of these not used - just for documentation */ + +/* Subaddress 0x00 - Input selection and bass control */ + +/* Bits 0,1,2 control input: + * 0x00 - Stereo input + * 0x02 - Mono input + * 0x03 - Mute + * Mono probably isn't used - I'm guessing only the stereo + * input is connected on most cards, so we'll set it to stereo. + * + * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut + * Bit 4 controls bass range: 0/1 is extended/standard bass range + * + * Highest 3 bits not used + */ + +#define TDA7432_STEREO_IN 0 +#define TDA7432_MONO_IN 2 /* Probably won't be used */ +#define TDA7432_MUTE 3 /* Probably won't be used */ +#define TDA7432_BASS_SYM 1 << 3 +#define TDA7432_BASS_NORM 1 << 4 + +/* Subaddress 0x01 - Volume */ + +/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps + * Recommended maximum is +20 dB + * + * +32dB: 0x00 + * +20dB: 0x0c + * 0dB: 0x20 + * -79dB: 0x6f + * + * MSB (bit 7) controls loudness: 1/0 is loudness on/off + */ + +#define TDA7432_VOL_0DB 0x20 +#define TDA7432_LD_ON 1 << 7 + + +/* Subaddress 0x02 - Tone control */ + +/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB + * 0x0 is 14dB, 0x7 is 0dB + * + * Bit 3 controls treble attenuation/gain (sign) + * 1 = gain (+) + * 0 = attenuation (-) + * + * Bits 4,5,6 control absolute bass gain from 0dB to 14dB + * (This is only true for normal base range, set in 0x00) + * 0x0 << 4 is 14dB, 0x7 is 0dB + * + * Bit 7 controls bass attenuation/gain (sign) + * 1 << 7 = gain (+) + * 0 << 7 = attenuation (-) + * + * Example: + * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble + */ + +#define TDA7432_TREBLE_0DB 0xf +#define TDA7432_TREBLE 7 +#define TDA7432_TREBLE_GAIN 1 << 3 +#define TDA7432_BASS_0DB 0xf << 4 +#define TDA7432_BASS 7 << 4 +#define TDA7432_BASS_GAIN 1 << 7 + + +/* Subaddress 0x03 - Left Front attenuation */ +/* Subaddress 0x04 - Left Rear attenuation */ +/* Subaddress 0x05 - Right Front attenuation */ +/* Subaddress 0x06 - Right Rear attenuation */ + +/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB + * in 1.5dB steps. + * + * 0x00 is 0dB + * 0x1f is -37.5dB + * + * Bit 5 mutes that channel when set (1 = mute, 0 = unmute) + * We'll use the mute on the input, though (above) + * Bits 6,7 unused + */ + +#define TDA7432_ATTEN_0DB 0x00 + + +/* Subaddress 0x07 - Loudness Control */ + +/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps + * when bit 4 is NOT set + * + * 0x0 is 0dB + * 0xf is -15dB + * + * If bit 4 is set, then there is a flat attenuation according to + * the lower 4 bits, as above. + * + * Bits 5,6,7 unused + */ + + + +/* Begin code */ + +static int tda7432_write(struct i2c_client *client, int subaddr, int val) +{ + unsigned char buffer[2]; + d2printk("tda7432: In tda7432_write\n"); + dprintk("tda7432: Writing %d 0x%x\n", subaddr, val); + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda7432: I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +/* I don't think we ever actually _read_ the chip... */ +#if 0 +static int tda7432_read(struct i2c_client *client) +{ + unsigned char buffer; + d2printk("tda7432: In tda7432_read\n"); + if (1 != i2c_master_recv(client,&buffer,1)) { + printk(KERN_WARNING "tda7432: I/O error, trying (read)\n"); + return -1; + } + dprintk("tda7432: Read 0x%02x\n", buffer); + return buffer; +} +#endif + +static int tda7432_set(struct i2c_client *client) +{ + struct tda7432 *t = client->data; + unsigned char buf[16]; + d2printk("tda7432: In tda7432_set\n"); + + dprintk(KERN_INFO + "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + t->input,t->volume,t->tone,t->lf,t->lr,t->rf,t->rr,t->loud); + buf[0] = TDA7432_IN; + buf[1] = t->input; + buf[2] = t->volume; + buf[3] = t->tone; + buf[4] = t->lf; + buf[5] = t->lr; + buf[6] = t->rf; + buf[7] = t->rr; + buf[8] = t->loud; + if (9 != i2c_master_send(client,buf,9)) { + printk(KERN_WARNING "tda7432: I/O error, trying tda7432_set\n"); + return -1; + } + + return 0; +} + +static void do_tda7432_init(struct i2c_client *client) +{ + struct tda7432 *t = client->data; + d2printk("tda7432: In tda7432_init\n"); + + t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ + TDA7432_BASS_SYM | /* Symmetric bass cut */ + TDA7432_BASS_NORM; /* Normal bass range */ + t->volume = TDA7432_VOL_0DB; /* 0dB Volume */ + if (loudness) /* Turn loudness on? */ + t->volume |= TDA7432_LD_ON; + t->tone = TDA7432_TREBLE_0DB | /* 0dB Treble */ + TDA7432_BASS_0DB; /* 0dB Bass */ + t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->loud = loudness; /* insmod parameter */ + + tda7432_set(client); +} + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda7432_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda7432 *t; + struct i2c_client *client; + d2printk("tda7432: In tda7432_attach\n"); + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = t = kmalloc(sizeof *t,GFP_KERNEL); + if (!t) + return -ENOMEM; + memset(t,0,sizeof *t); + do_tda7432_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA7432"); + printk(KERN_INFO "tda7432: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda7432_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda7432_attach); + return 0; +} + +static int tda7432_detach(struct i2c_client *client) +{ + struct tda7432 *t = client->data; + + do_tda7432_init(client); + i2c_detach_client(client); + + kfree(t); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda7432_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda7432 *t = client->data; + d2printk("tda7432: In tda7432_command\n"); +#if 0 + __u16 *sarg = arg; +#endif + + switch (cmd) { + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + + /* Query card - scale from TDA7432 settings to V4L settings */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + dprintk("tda7432: VIDIOCGAUDIO\n"); + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + + /* Master volume control + * V4L volume is min 0, max 65535 + * TDA7432 Volume: + * Min (-79dB) is 0x6f + * Max (+20dB) is 0x07 + * (Mask out bit 7 of vol - it's for the loudness setting) + */ + + va->volume = ( 0x6f - (t->volume & 0x7F) ) * 630; + + /* Balance depends on L,R attenuation + * V4L balance is 0 to 65535, middle is 32768 + * TDA7432 attenuation: min (0dB) is 0, max (-37.5dB) is 0x1f + * to scale up to V4L numbers, mult by 1057 + * attenuation exists for lf, lr, rf, rr + * we use only lf and rf (front channels) + */ + + if ( (t->lf) < (t->rf) ) + /* right is attenuated, balance shifted left */ + va->balance = (32768 - 1057*(t->rf)); + else + /* left is attenuated, balance shifted right */ + va->balance = (32768 + 1057*(t->lf)); + + /* Bass/treble */ + va->bass = 32768; /* brain hurts... set to middle for now */ + va->treble = 32768; /* brain hurts... set to middle for now */ + + break; /* VIDIOCGAUDIO case */ + } + + /* Set card - scale from V4L settings to TDA7432 settings */ + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + dprintk("tda7432: VIDEOCSAUDIO\n"); + + t->volume = 0x6f - ( (va->volume)/630 ); + + if (loudness) /* Turn on the loudness bit */ + t->volume |= TDA7432_LD_ON; + + if (va->balance < 32768) { + /* shifted to left, attenuate right */ + t->rr = (32768 - va->balance)/1057; + t->rf = t->rr; + } + else { + /* shifted to right, attenuate left */ + t->lf = (va->balance - 32768)/1057; + t->lr = t->lf; + } + + /* t->tone = 0xff; */ /* Brain hurts - no tone control for now... */ + + tda7432_write(client,TDA7432_VL, t->volume); + /* tda7432_write(client,TDA7432_TN, t->tone); */ + tda7432_write(client,TDA7432_LF, t->lf); + tda7432_write(client,TDA7432_LR, t->lr); + tda7432_write(client,TDA7432_RF, t->rf); + tda7432_write(client,TDA7432_RR, t->rr); + + break; + + } /* end of VIDEOCSAUDIO case */ + + default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ + + /* nothing */ + d2printk("tda7432: Default\n"); + + } /* end of (cmd) switch */ + + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda7432 driver", + I2C_DRIVERID_TDA7432, + I2C_DF_NOTIFY, + tda7432_probe, + tda7432_detach, + tda7432_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda7432_init(void) +#endif +{ + + if ( (loudness < 0) || (loudness > 15) ) + { + printk(KERN_ERR "tda7432: loudness parameter must be between 0 and 15\n"); + return -EINVAL; + } + + + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/char/tda8425.c b/drivers/char/tda8425.c index 73df0b4ac89..a1dec22ebc5 100644 --- a/drivers/char/tda8425.c +++ b/drivers/char/tda8425.c @@ -31,7 +31,6 @@ #include "audiochip.h" /* Addresses to scan */ -#define I2C_TDA8425 0x82 static unsigned short normal_i2c[] = { I2C_TDA8425 >> 1, I2C_CLIENT_END}; diff --git a/drivers/char/tda985x.c b/drivers/char/tda985x.c index 62bcf15ba2c..73fb9bd5220 100644 --- a/drivers/char/tda985x.c +++ b/drivers/char/tda985x.c @@ -13,6 +13,7 @@ * * OPTIONS: * debug - set to 1 if you'd like to see debug messages + * - set to 2 if you'd like to be flooded with debug messages * chip - set to 9850 or 9855 to select your chip (default 9855) * * TODO: @@ -20,7 +21,8 @@ * and unmote to fix. - Is this still here? * Fine tune sound * Get rest of capabilities into video_audio struct... - * + * + * Revision 0.5 - cleaned up debugging messages, added debug level=2 * Revision: 0.4 - check for correct chip= insmod value * also cleaned up comments a bit * Revision: 0.3 - took out extraneous tda985x_write in tda985x_command @@ -55,9 +57,10 @@ static int chip = 9855; /* insmod parameter */ #define I2C_TDA985x_H 0xb6 static unsigned short normal_i2c[] = {I2C_CLIENT_END}; static unsigned short normal_i2c_range[] = { - I2C_TDA985x_L >> 1, - I2C_TDA985x_H >> 1, - I2C_CLIENT_END}; + I2C_TDA985x_L >> 1, + I2C_TDA985x_H >> 1, + I2C_CLIENT_END +}; static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; @@ -84,6 +87,7 @@ static struct i2c_driver driver; static struct i2c_client client_template; #define dprintk if (debug) printk +#define d2printk if (debug == 2) printk /* The TDA9850 and TDA9855 are both made by Philips Semiconductor * http://www.semiconductors.philips.com @@ -154,9 +158,9 @@ static struct i2c_client client_template; #define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ #define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ #define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ - /* Bits 0 to 3 select various combinations - * of line in and line out, only the - * interesting ones are defined */ + /* Bits 0 to 3 select various combinations + * of line in and line out, only the + * interesting ones are defined */ #define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ #define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ @@ -213,8 +217,8 @@ static struct i2c_client client_template; static int tda985x_write(struct i2c_client *client, int subaddr, int val) { unsigned char buffer[2]; - dprintk("In tda985x_write\n"); - dprintk("Writing %d 0x%x\n", subaddr, val); + d2printk("tda985x: In tda985x_write\n"); + dprintk("tda985x: Writing %d 0x%x\n", subaddr, val); buffer[0] = subaddr; buffer[1] = val; if (2 != i2c_master_send(client,buffer,2)) { @@ -228,12 +232,12 @@ static int tda985x_write(struct i2c_client *client, int subaddr, int val) static int tda985x_read(struct i2c_client *client) { unsigned char buffer; - dprintk("In tda985x_read\n"); + d2printk("tda985x: In tda985x_read\n"); if (1 != i2c_master_recv(client,&buffer,1)) { printk(KERN_WARNING "tda985x: I/O error, trying (read)\n"); return -1; } - dprintk("Read 0x%02x\n", buffer); + dprintk("tda985x: Read 0x%02x\n", buffer); return buffer; } @@ -241,12 +245,12 @@ static int tda985x_set(struct i2c_client *client) { struct tda985x *t = client->data; unsigned char buf[16]; - dprintk("In tda985x_set\n"); + d2printk("tda985x: In tda985x_set\n"); if (chip == 9855) { dprintk(KERN_INFO - "tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + "tda985x: tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", t->rvol,t->lvol,t->bass,t->treble,t->sub, t->c5,t->c6,t->c7,t->a1,t->a2,t->a3); buf[0] = TDA9855_VR; @@ -262,7 +266,7 @@ static int tda985x_set(struct i2c_client *client) buf[10] = t->a2; buf[11] = t->a3; if (12 != i2c_master_send(client,buf,12)) { - printk(KERN_WARNING "tda9855: I/O error, trying tda985x_set\n"); + printk(KERN_WARNING "tda985x: I/O error, trying tda985x_set\n"); return -1; } } @@ -270,7 +274,7 @@ static int tda985x_set(struct i2c_client *client) else if (chip == 9850) { dprintk(KERN_INFO - "tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + "tda986x: tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", t->c4,t->c5,t->c6,t->c7,t->a1,t->a2,t->a3); buf[0] = TDA9850_C4; buf[1] = t->c4; @@ -281,7 +285,7 @@ static int tda985x_set(struct i2c_client *client) buf[6] = t->a2; buf[7] = t->a3; if (8 != i2c_master_send(client,buf,8)) { - printk(KERN_WARNING "tda9850: I/O error, trying tda985x_set\n"); + printk(KERN_WARNING "tda985x: I/O error, trying tda985x_set\n"); return -1; } } @@ -292,11 +296,11 @@ static int tda985x_set(struct i2c_client *client) static void do_tda985x_init(struct i2c_client *client) { struct tda985x *t = client->data; - dprintk("In tda985x_init\n"); + d2printk("tda985x: In tda985x_init\n"); if (chip == 9855) { - printk("Using tda9855 options\n"); + printk("tda985x: Using tda9855 options\n"); t->rvol = 0x6f; /* 0dB */ t->lvol = 0x6f; /* 0dB */ t->bass = 0x0e; /* 0dB */ @@ -313,7 +317,7 @@ static void do_tda985x_init(struct i2c_client *client) else if (chip == 9850) { - printk("Using tda9850 options\n"); + printk("tda985x: Using tda9850 options\n"); t->c4 = 0x08; /* Set stereo noise thresh to nominal */ t->c5 = 0x08; /* Set SAP noise threshold to nominal */ t->c6 = TDA985x_STEREO; /* Select Stereo mode for decoder */ @@ -337,7 +341,7 @@ static int tda985x_attach(struct i2c_adapter *adap, int addr, { struct tda985x *t; struct i2c_client *client; - dprintk("In tda985x_attach\n"); + d2printk("tda985x: In tda985x_attach\n"); client = kmalloc(sizeof *client,GFP_KERNEL); if (!client) return -ENOMEM; @@ -382,7 +386,7 @@ static int tda985x_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct tda985x *t = client->data; - dprintk("In tda985x_command...\n"); + d2printk("tda985x: In tda985x_command\n"); #if 0 __u16 *sarg = arg; #endif @@ -394,7 +398,7 @@ static int tda985x_command(struct i2c_client *client, case VIDIOCGAUDIO: { struct video_audio *va = arg; - dprintk("VIDIOCGAUDIO\n"); + dprintk("tda985x: VIDIOCGAUDIO\n"); if (chip == 9855) { int left,right; @@ -430,7 +434,7 @@ static int tda985x_command(struct i2c_client *client, case VIDIOCSAUDIO: { struct video_audio *va = arg; - dprintk("VIDEOCSAUDIO...\n"); + dprintk("tda985x: VIDEOCSAUDIO\n"); if (chip == 9855) { int left,right; @@ -453,17 +457,17 @@ static int tda985x_command(struct i2c_client *client, switch (va->mode) { case VIDEO_SOUND_MONO: - dprintk("VIDEO_SOUND_MONO\n"); + dprintk("tda985x: VIDEO_SOUND_MONO\n"); t->c6= TDA985x_MONO | (t->c6 & 0x3f); tda985x_write(client,TDA985x_C6,t->c6); break; case VIDEO_SOUND_STEREO: - dprintk("VIDEO_SOUND_STEREO\n"); + dprintk("tda985x: VIDEO_SOUND_STEREO\n"); t->c6= TDA985x_STEREO | (t->c6 & 0x3f); tda985x_write(client,TDA985x_C6,t->c6); break; case VIDEO_SOUND_LANG1: - dprintk("VIDEO_SOUND_LANG1\n"); + dprintk("tda985x: VIDEO_SOUND_LANG1\n"); t->c6= TDA985x_SAP | (t->c6 & 0x3f); tda985x_write(client,TDA985x_C6,t->c6); break; @@ -476,7 +480,7 @@ static int tda985x_command(struct i2c_client *client, default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ /* nothing */ - dprintk("Default\n"); + d2printk("tda985x: Default\n"); } /* end of (cmd) switch */ diff --git a/drivers/char/tda9875.c b/drivers/char/tda9875.c new file mode 100644 index 00000000000..da3ceebfe62 --- /dev/null +++ b/drivers/char/tda9875.c @@ -0,0 +1,371 @@ +/* + * For the TDA9875 chip + * (The TDA9875 is used on the Diamond DTV2000 french version + * Other cards probably use these chips as well.) + * This driver will not complain if used with any + * other i2c device with the same address. + * + * Copyright (c) 2000 Guillamue Delvit based on Gerd Knorr source and + * Eric Sandeen + * This code is placed under the terms of the GNU General Public License + * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Which was based on tda8425.c by Greg Alexander (c) 1998 + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * + * Revision: 0.1 - original version + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + +/* This driver ID is brand new, so define it if it's not in i2c-id.h yet */ +#ifndef I2C_DRIVERID_TDA9875 + #define I2C_DRIVERID_TDA9875 28 +#endif + + +MODULE_PARM(debug,"i"); + +static int debug = 0; /* insmod parameter */ + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { + I2C_TDA9875 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +/* This is a superset of the TDA9875 */ +struct tda9875 { + int mode; + int rvol, lvol; + int bass, treble; +}; + + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define dprintk if (debug) printk + +/* The TDA9875 is made by Philips Semiconductor + * http://www.semiconductors.philips.com + * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator + * + */ + + /* subaddresses for TDA9875 */ +#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ +#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ +#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ +#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ + +#define TDA9875_CH1V 0x0c /*Chanel 1 volume (mute)*/ +#define TDA9875_CH2V 0x0d /*Chanel 2 volume (mute)*/ +#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ +#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ + +#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ +#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ +#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ +#define TDA9875_MVL 0x1a /* Main volume gauche */ +#define TDA9875_MVR 0x1b /* Main volume droite */ +#define TDA9875_MBA 0x1d /* Main Basse */ +#define TDA9875_MTR 0x1e /* Main treble */ +#define TDA9875_ACS 0x1f /* Auxilary channel select (FM) 0b0000000*/ +#define TDA9875_AVL 0x20 /* Auxilary volume gauche */ +#define TDA9875_AVR 0x21 /* Auxilary volume droite */ +#define TDA9875_ABA 0x22 /* Auxilary Basse */ +#define TDA9875_ATR 0x23 /* Auxilary treble */ + +#define TDA9875_MSR 0x02 /* Monitor select register */ +#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ +#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ +#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ +#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ +#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ +#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ +#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ +#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ +#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ + +/* values */ +#define TDA9875_MUTE_ON 0xff /* general mute */ +#define TDA9875_MUTE_OFF 0xcc /* general no mute */ + + + +/* Begin code */ + +static int tda9875_write(struct i2c_client *client, int subaddr, int val) +{ + unsigned char buffer[2]; + dprintk("In tda9875_write\n"); + dprintk("Writing %d 0x%x\n", subaddr, val); + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda9875: I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +#if 0 +static int tda9875_read(struct i2c_client *client) +{ + unsigned char buffer; + dprintk("In tda9875_read\n"); + if (1 != i2c_master_recv(client,&buffer,1)) { + printk(KERN_WARNING "tda9875: I/O error, trying (read)\n"); + return -1; + } + dprintk("Read 0x%02x\n", buffer); + return buffer; +} +#endif + +static void tda9875_set(struct i2c_client *client) +{ + struct tda9875 *tda = client->data; + + dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n",tda->lvol,tda->rvol,tda->bass,tda->treble); + tda9875_write(client, TDA9875_MVL, tda->lvol / 600 - 84); + tda9875_write(client, TDA9875_MVR, tda->rvol / 600 -84); + tda9875_write(client, TDA9875_MBA, tda->bass / 2340 -12); + tda9875_write(client, TDA9875_MTR, tda->treble / 2621 -12); +} + +static void do_tda9875_init(struct i2c_client *client) +{ + struct tda9875 *t = client->data; + dprintk("In tda9875_init\n"); + tda9875_write(client, TDA9875_CFG, 0xd0 ); /*reg de config 0 (reset)*/ + tda9875_write(client, TDA9875_MSR, 0x03 ); /* Monitor 0b00000XXX*/ + tda9875_write(client, TDA9875_C1MSB, 0x00 ); /*Car1(FM) MSB XMHz*/ + tda9875_write(client, TDA9875_C1MIB, 0x00 ); /*Car1(FM) MIB XMHz*/ + tda9875_write(client, TDA9875_C1LSB, 0x00 ); /*Car1(FM) LSB XMHz*/ + tda9875_write(client, TDA9875_C2MSB, 0x00 ); /*Car2(NICAM) MSB XMHz*/ + tda9875_write(client, TDA9875_C2MIB, 0x00 ); /*Car2(NICAM) MIB XMHz*/ + tda9875_write(client, TDA9875_C2LSB, 0x00 ); /*Car2(NICAM) LSB XMHz*/ + tda9875_write(client, TDA9875_DCR, 0x00 ); /*Demod config 0x00*/ + tda9875_write(client, TDA9875_DEEM, 0x44 ); /*DE-Emph 0b0100 0100*/ + tda9875_write(client, TDA9875_FMAT, 0x00 ); /*FM Matrix reg 0x00*/ + tda9875_write(client, TDA9875_SC1, 0x00 ); /* SCART 1 (SC1)*/ + tda9875_write(client, TDA9875_SC2, 0x01 ); /* SCART 2 (sc2)*/ + + tda9875_write(client, TDA9875_CH1V, 0x10 ); /* Chanel volume 1 mute*/ + tda9875_write(client, TDA9875_CH2V, 0x10 ); /* Chanel volume 2 mute */ + tda9875_write(client, TDA9875_DACOS, 0x02 ); /* sig DAC i/o(in:nicam)*/ + tda9875_write(client, TDA9875_ADCIS, 0x6f ); /* sig ADC input(in:mono)*/ + tda9875_write(client, TDA9875_LOSR, 0x00 ); /* line out (in:mono)*/ + tda9875_write(client, TDA9875_AER, 0x00 ); /*06 Effect (AVL+PSEUDO) */ + tda9875_write(client, TDA9875_MCS, 0x44 ); /* Main ch select (DAC) */ + tda9875_write(client, TDA9875_MVL, 0x03 ); /* Vol Main left 10dB */ + tda9875_write(client, TDA9875_MVR, 0x03 ); /* Vol Main right 10dB*/ + tda9875_write(client, TDA9875_MBA, 0x00 ); /* Main Bass Main 0dB*/ + tda9875_write(client, TDA9875_MTR, 0x00 ); /* Main Treble Main 0dB*/ + tda9875_write(client, TDA9875_ACS, 0x44 ); /* Aux chan select (dac)*/ + tda9875_write(client, TDA9875_AVL, 0x00 ); /* Vol Aux left 0dB*/ + tda9875_write(client, TDA9875_AVR, 0x00 ); /* Vol Aux right 0dB*/ + tda9875_write(client, TDA9875_ABA, 0x00 ); /* Aux Bass Main 0dB*/ + tda9875_write(client, TDA9875_ATR, 0x00 ); /* Aux Aigus Main 0dB*/ + + tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */ + + t->mode=AUDIO_MUTE; + t->lvol=t->rvol =51000; /* 0dB */ + t->bass=30420; /* 0dB */ + t->treble=34073; /* 0dB */ + tda9875_set(client); + +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda9875_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda9875 *t; + struct i2c_client *client; + dprintk("In tda9875_attach\n"); + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = t = kmalloc(sizeof *t,GFP_KERNEL); + if (!t) + return -ENOMEM; + memset(t,0,sizeof *t); + do_tda9875_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA9875"); + printk(KERN_INFO "tda9875: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda9875_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda9875_attach); + return 0; +} + +static int tda9875_detach(struct i2c_client *client) +{ + struct tda9875 *t = client->data; + + do_tda9875_init(client); + i2c_detach_client(client); + + kfree(t); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda9875_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda9875 *t = client->data; + + dprintk("In tda9875_command...\n"); + + switch (cmd) { + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + int left,right; + + dprintk("VIDIOCGAUDIO\n"); + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + + /* min is -84 max is 24 */ + left = (t->lvol+85)*600; + right = (t->rvol+85)*600; + va->volume=MAX(left,right); + va->balance=(32768*MIN(left,right))/ + (va->volume ? va->volume : 1); + va->balance=(leftbalance) : va->balance; + va->bass = (t->bass+13)*2340; /* min -12 max +15 */ + va->treble = (t->treble+13)*2621;/* min -12 max +12 */ + + va->mode |= VIDEO_SOUND_MONO; + + + break; /* VIDIOCGAUDIO case */ + } + + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + int left,right; + + dprintk("VIDEOCSAUDIO...\n"); + left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + right = (MIN(va->balance,32768) * + va->volume) / 32768; + t->lvol = left/600-84; + t->rvol = right/600-84; + t->bass = va->bass/2340-12; + t->treble = va->treble/2621-12; + tda9875_set(client); + + break; + + } /* end of VIDEOCSAUDIO case */ + + default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ + + /* nothing */ + dprintk("Default\n"); + + } /* end of (cmd) switch */ + + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda9875 driver", + I2C_DRIVERID_TDA9875, /* Get new one for TDA9875 */ + I2C_DF_NOTIFY, + tda9875_probe, + tda9875_detach, + tda9875_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda9875_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/drivers/char/tea6420.c b/drivers/char/tea6420.c new file mode 100644 index 00000000000..231ed9e4e0f --- /dev/null +++ b/drivers/char/tea6420.c @@ -0,0 +1,268 @@ +/* + * for the TEA6420 chip (only found on 3DFX (STB) TV/FM cards to the best + * of my knowledge) + * Copyright (C) 2000 Dave Stuart + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from tea6300 by . . . + * + * Copyright (c) 1998 Greg Alexander + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from msp3400.c, which is by Gerd Knorr + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + + +/* Addresses to scan */ +#define I2C_TEA6420 0x98 +static unsigned short normal_i2c[] = { + I2C_TEA6420 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ + +#define dprintk if (debug) printk + + +struct tea6420 { + int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ + int stereo; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define TEA6420_S_SA 0x00 /* stereo A input */ +#define TEA6420_S_SB 0x01 /* stereo B */ +#define TEA6420_S_SC 0x02 /* stereo C */ +#define TEA6420_S_SD 0x03 /* stereo D */ +#define TEA6420_S_SE 0x04 /* stereo E */ +#define TEA6420_S_GMU 0x05 /* general mute */ + + +/* ******************************** * + * functions for talking to TEA6420 * + * ******************************** */ + +static int tea6420_write(struct i2c_client *client, int val) +{ + unsigned char buffer[2]; + int result; + +/* buffer[0] = addr; */ + buffer[0] = val; + result = i2c_master_send(client,buffer,1); + if (1 != result) { + printk(KERN_WARNING "tea6420: I/O error, trying (write +0x%x) result = %d\n", val, result); + return -1; + } + return 0; +} + + +static void do_tea6420_init(struct i2c_client *client) +{ + struct tea6420 *tea = client->data; + + tea->mode=AUDIO_OFF; + tea->stereo=1; + tea6420_write(client, TEA6420_S_GMU); /* mute */ +} + +static void tea6420_audio(struct i2c_client *client, int mode) +{ + struct tea6420 *tea = client->data; + + /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ + dprintk(KERN_DEBUG "tea6420_audio:%d (T,R,E,I,O)\n",mode); + tea->mode=mode; + if (mode==AUDIO_OFF) { /* just mute it */ + tea6420_write(client, TEA6420_S_GMU); + return; + } + switch(mode) { + case AUDIO_TUNER: + tea6420_write(client, TEA6420_S_SA); + break; + case AUDIO_RADIO: + tea6420_write(client, TEA6420_S_SB); + break; + case AUDIO_EXTERN: + tea6420_write(client, TEA6420_S_SC); + break; + } +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tea6420_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tea6420 *tea; + struct i2c_client *client; + + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = tea = kmalloc(sizeof *tea,GFP_KERNEL); + if (!tea) + return -ENOMEM; + memset(tea,0,sizeof *tea); + do_tea6420_init(client); + + MOD_INC_USE_COUNT; + strcpy(client->name,"TEA6420"); + printk(KERN_INFO "tea6420: initialized\n"); + + i2c_attach_client(client); + return 0; +} + +static int tea6420_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tea6420_attach); + return 0; +} + +static int tea6420_detach(struct i2c_client *client) +{ + struct tea6420 *tea = client->data; + + do_tea6420_init(client); + i2c_detach_client(client); + + kfree(tea); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int +tea6420_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + __u16 *sarg = arg; + + switch (cmd) { + case AUDC_SET_RADIO: + tea6420_audio(client,AUDIO_RADIO); + break; + case AUDC_SET_INPUT: + tea6420_audio(client,*sarg); + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; +/* va->volume=MAX(tea->left,tea->right); + va->balance=(32768*MIN(tea->left,tea->right))/ + (va->volume ? va->volume : 1); + va->balance=(tea->leftright)? + (65535-va->balance) : va->balance; + va->bass = tea->bass; + va->treble = tea->treble; +*/ break; + } + case VIDIOCSAUDIO: + { + +/* tea->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + tea->right = (MIN(va->balance,32768) * + va->volume) / 32768; + tea->bass = va->bass; + tea->treble = va->treble; + tea6420_set(client); +*/ break; + } + +default: + /* nothing */ + } + return 0; +} + +static struct i2c_driver driver = { + "i2c tea6420 driver", + I2C_DRIVERID_TEA6420, + I2C_DF_NOTIFY, + tea6420_probe, + tea6420_detach, + tea6420_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tea6420_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index f922c7b7167..1516d985ec7 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -146,8 +146,8 @@ extern long vme_scc_console_init(void); extern int serial167_init(void); extern long serial167_console_init(void); #endif -#ifdef CONFIG_8xx -extern console_8xx_init(void); +#if (defined(CONFIG_8xx) || defined(CONFIG_8260)) +extern void console_8xx_init(void); extern int rs_8xx_init(void); #endif /* CONFIG_8xx */ #ifdef CONFIG_SGI_SERIAL @@ -2197,7 +2197,7 @@ void __init console_init(void) con_init(); #endif #ifdef CONFIG_SERIAL_CONSOLE -#ifdef CONFIG_8xx +#if (defined(CONFIG_8xx) || defined(CONFIG_8260)) console_8xx_init(); #elif defined(CONFIG_SERIAL) serial_console_init(); @@ -2346,7 +2346,7 @@ void __init tty_init(void) #ifdef CONFIG_RIO rio_init(); #endif -#ifdef CONFIG_8xx +#if (defined(CONFIG_8xx) || defined(CONFIG_8260)) rs_8xx_init(); #endif /* CONFIG_8xx */ pty_init(); diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index 364ae8e6952..618f02f85d2 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -41,7 +41,6 @@ static struct video_device *video_device[VIDEO_NUM_DEVICES]; #ifdef CONFIG_VIDEO_BT848 -extern int init_bttv_cards(struct video_init *); extern int i2c_tuner_init(struct video_init *); #endif #ifdef CONFIG_VIDEO_BWQCAM @@ -59,9 +58,8 @@ extern int init_zoran_cards(struct video_init *); static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_BT848 - {"i2c-tuner", i2c_tuner_init}, - {"bttv", init_bttv_cards}, -#endif + {"i2c-tuner", i2c_tuner_init}, +#endif #ifdef CONFIG_VIDEO_BWQCAM {"bw-qcam", init_bw_qcams}, #endif diff --git a/drivers/ide/Config.in b/drivers/ide/Config.in index 6d68bd5ea0f..09c6451cfd1 100644 --- a/drivers/ide/Config.in +++ b/drivers/ide/Config.in @@ -12,6 +12,15 @@ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE dep_mbool ' Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE $CONFIG_BLK_DEV_IDEDISK + + define_bool CONFIG_BLK_DEV_IDEDISK_VENDOR n + dep_mbool ' Fujitsu Vendor Specific' CONFIG_BLK_DEV_IDEDISK_FUJITSU $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' IBM Vendor Specific' CONFIG_BLK_DEV_IDEDISK_IBM $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Maxtor Vendor Specific' CONFIG_BLK_DEV_IDEDISK_MAXTOR $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Quantum Vendor Specific' CONFIG_BLK_DEV_IDEDISK_QUANTUM $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Seagate Vendor Specific' CONFIG_BLK_DEV_IDEDISK_SEAGATE $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Western Digital Vendor Specific' CONFIG_BLK_DEV_IDEDISK_WD $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE diff --git a/drivers/ide/aec62xx.c b/drivers/ide/aec62xx.c index 0158b427975..75b3153b7ad 100644 --- a/drivers/ide/aec62xx.c +++ b/drivers/ide/aec62xx.c @@ -176,6 +176,7 @@ struct chipset_bus_clock_list_entry { }; struct chipset_bus_clock_list_entry aec62xx_base [] = { +#ifdef CONFIG_BLK_DEV_IDEDMA { XFER_UDMA_4, 0x41, 0x04, 0x31, 0x05 }, { XFER_UDMA_3, 0x41, 0x03, 0x31, 0x04 }, { XFER_UDMA_2, 0x41, 0x02, 0x31, 0x03 }, @@ -185,7 +186,7 @@ struct chipset_bus_clock_list_entry aec62xx_base [] = { { XFER_MW_DMA_2, 0x41, 0x00, 0x31, 0x00 }, { XFER_MW_DMA_1, 0x42, 0x00, 0x31, 0x00 }, { XFER_MW_DMA_0, 0x7a, 0x00, 0x0a, 0x00 }, - +#endif /* CONFIG_BLK_DEV_IDEDMA */ { XFER_PIO_4, 0x41, 0x00, 0x31, 0x00 }, { XFER_PIO_3, 0x43, 0x00, 0x33, 0x00 }, { XFER_PIO_2, 0x78, 0x00, 0x08, 0x00 }, @@ -202,18 +203,22 @@ extern char *ide_xfer_verbose (byte xfer_rate); static byte pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table) { + int bus_speed = system_bus_clock(); + for ( ; chipset_table->xfer_speed ; chipset_table++) if (chipset_table->xfer_speed == speed) { - return ((byte) ((1) ? chipset_table->chipset_settings_33 : chipset_table->chipset_settings_34)); + return ((byte) ((bus_speed <= 33) ? chipset_table->chipset_settings_33 : chipset_table->chipset_settings_34)); } return 0x00; } static byte pci_bus_clock_list_ultra (byte speed, struct chipset_bus_clock_list_entry * chipset_table) { + int bus_speed = system_bus_clock(); + for ( ; chipset_table->xfer_speed ; chipset_table++) if (chipset_table->xfer_speed == speed) { - return ((byte) ((1) ? chipset_table->ultra_settings_33 : chipset_table->ultra_settings_34)); + return ((byte) ((bus_speed <= 33) ? chipset_table->ultra_settings_33 : chipset_table->ultra_settings_34)); } return 0x00; } @@ -222,8 +227,6 @@ static int aec6210_tune_chipset (ide_drive_t *drive, byte speed) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; - byte unit = (drive->select.b.unit & 0x01); - int drive_number = ((hwif->channel ? 2 : 0) + unit); int err = 0; unsigned short d_conf = 0x0000; byte ultra = 0x00; @@ -236,18 +239,18 @@ static int aec6210_tune_chipset (ide_drive_t *drive, byte speed) __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ - pci_read_config_word(dev, 0x40|(2*drive_number), &d_conf); + pci_read_config_word(dev, 0x40|(2*drive->dn), &d_conf); tmp0 = pci_bus_clock_list(speed, aec62xx_base); SPLIT_BYTE(tmp0,tmp1,tmp2); MAKE_WORD(d_conf,tmp1,tmp2); - pci_write_config_word(dev, 0x40|(2*drive_number), d_conf); + pci_write_config_word(dev, 0x40|(2*drive->dn), d_conf); tmp1 = 0x00; tmp2 = 0x00; pci_read_config_byte(dev, 0x54, &ultra); - tmp1 = ((0x00 << (2*drive_number)) | (ultra & ~(3 << (2*drive_number)))); + tmp1 = ((0x00 << (2*drive->dn)) | (ultra & ~(3 << (2*drive->dn)))); ultra_conf = pci_bus_clock_list_ultra(speed, aec62xx_base); - tmp2 = ((ultra_conf << (2*drive_number)) | (tmp1 & ~(3 << (2*drive_number)))); + tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn)))); pci_write_config_byte(dev, 0x54, tmp2); __restore_flags(flags); /* local CPU only */ @@ -261,7 +264,6 @@ static int aec6260_tune_chipset (ide_drive_t *drive, byte speed) ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; byte unit = (drive->select.b.unit & 0x01); - int drive_number = ((hwif->channel ? 2 : 0) + unit); byte ultra_pci = hwif->channel ? 0x45 : 0x44; int err = 0; byte drive_conf = 0x00; @@ -269,15 +271,14 @@ static int aec6260_tune_chipset (ide_drive_t *drive, byte speed) byte ultra = 0x00; byte tmp1 = 0x00; byte tmp2 = 0x00; - unsigned long flags; __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ - pci_read_config_byte(dev, 0x40|drive_number, &drive_conf); + pci_read_config_byte(dev, 0x40|drive->dn, &drive_conf); drive_conf = pci_bus_clock_list(speed, aec62xx_base); - pci_write_config_byte(dev, 0x40|drive_number, drive_conf); + pci_write_config_byte(dev, 0x40|drive->dn, drive_conf); pci_read_config_byte(dev, ultra_pci, &ultra); tmp1 = ((0x00 << (4*unit)) | (ultra & ~(7 << (4*unit)))); @@ -286,10 +287,24 @@ static int aec6260_tune_chipset (ide_drive_t *drive, byte speed) pci_write_config_byte(dev, ultra_pci, tmp2); __restore_flags(flags); /* local CPU only */ + if (!drive->init_speed) + drive->init_speed = speed; + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; return(err); } + +static int aec62xx_tune_chipset (ide_drive_t *drive, byte speed) +{ + if (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) { + return ((int) aec6210_tune_chipset(drive, speed)); + } else { + return ((int) aec6260_tune_chipset(drive, speed)); + } +} + #ifdef CONFIG_BLK_DEV_IDEDMA static int config_aec6210_chipset_for_dma (ide_drive_t *drive, byte ultra) { @@ -524,7 +539,7 @@ void __init ide_init_aec62xx (ide_hwif_t *hwif) { #ifdef CONFIG_AEC62XX_TUNING hwif->tuneproc = &aec62xx_tune_drive; - + hwif->speedproc = &aec62xx_tune_chipset; #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_base) hwif->dmaproc = &aec62xx_dmaproc; diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c index c5f570674a1..02af0a0f801 100644 --- a/drivers/ide/alim15x3.c +++ b/drivers/ide/alim15x3.c @@ -333,6 +333,7 @@ static int ali15x3_tune_chipset (ide_drive_t *drive, byte speed) err = ide_config_drive_speed(drive, speed); +#ifdef CONFIG_BLK_DEV_IDEDMA if (speed >= XFER_SW_DMA_0) { unsigned long dma_base = hwif->dma_base; @@ -353,6 +354,9 @@ static int ali15x3_tune_chipset (ide_drive_t *drive, byte speed) pci_write_config_byte(dev, 0x4b, tmpbyte); } } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + drive->current_speed = speed; return (err); } @@ -399,6 +403,9 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra33) (void) ali15x3_tune_chipset(drive, speed); + if (!drive->init_speed) + drive->init_speed = speed; + rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : ((id->dma_mword >> 8) & 7) ? ide_dma_on : @@ -629,7 +636,7 @@ unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif) * has 80-pin (from host view) */ pci_read_config_byte(dev, 0x4a, &tmpbyte); - ata66 = (!(tmpbyte & ata66mask)) ? 0 : 1; + ata66 = (!(tmpbyte & ata66mask)) ? 1 : 0; __restore_flags(flags); return(ata66); @@ -673,6 +680,7 @@ void __init ide_init_ali15x3 (ide_hwif_t *hwif) hwif->tuneproc = &ali15x3_tune_drive; hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; + hwif->speedproc = &ali15x3_tune_chipset; #ifndef CONFIG_BLK_DEV_IDEDMA hwif->autodma = 0; return; diff --git a/drivers/ide/amd7409.c b/drivers/ide/amd7409.c index 14a8a83f15c..d54a240d4c9 100644 --- a/drivers/ide/amd7409.c +++ b/drivers/ide/amd7409.c @@ -81,21 +81,22 @@ static int amd7409_tune_chipset (ide_drive_t *drive, byte speed) struct pci_dev *dev = hwif->pci_dev; int err = 0; byte unit = (drive->select.b.unit & 0x01); - int drive_number = ((HWIF(drive)->channel ? 2 : 0) + unit); +#ifdef CONFIG_BLK_DEV_IDEDMA unsigned long dma_base = hwif->dma_base; +#endif /* CONFIG_BLK_DEV_IDEDMA */ byte drive_pci = 0x00; byte drive_pci2 = 0x00; byte ultra_timing = 0x00; byte dma_pio_timing = 0x00; byte pio_timing = 0x00; - switch (drive_number) { + switch (drive->dn) { case 0: drive_pci = 0x53; drive_pci2 = 0x4b; break; case 1: drive_pci = 0x52; drive_pci2 = 0x4a; break; case 2: drive_pci = 0x51; drive_pci2 = 0x49; break; case 3: drive_pci = 0x50; drive_pci2 = 0x48; break; default: - return ((int) ide_dma_off_quietly); + return -1; } pci_read_config_byte(dev, drive_pci, &ultra_timing); @@ -109,7 +110,7 @@ static int amd7409_tune_chipset (ide_drive_t *drive, byte speed) ultra_timing &= ~0xC7; dma_pio_timing &= ~0xFF; - pio_timing &= ~(0x03 << drive_number); + pio_timing &= ~(0x03 << drive->dn); #ifdef DEBUG printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ", @@ -117,67 +118,74 @@ static int amd7409_tune_chipset (ide_drive_t *drive, byte speed) #endif switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA case XFER_UDMA_4: ultra_timing |= 0x45; dma_pio_timing |= 0x20; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_UDMA_3: ultra_timing |= 0x44; dma_pio_timing |= 0x20; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_UDMA_2: ultra_timing |= 0x40; dma_pio_timing |= 0x20; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_UDMA_1: ultra_timing |= 0x41; dma_pio_timing |= 0x20; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_UDMA_0: ultra_timing |= 0x42; dma_pio_timing |= 0x20; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_MW_DMA_2: dma_pio_timing |= 0x20; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_MW_DMA_1: dma_pio_timing |= 0x21; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_MW_DMA_0: dma_pio_timing |= 0x77; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ case XFER_PIO_4: dma_pio_timing |= 0x20; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_PIO_3: dma_pio_timing |= 0x22; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_PIO_2: dma_pio_timing |= 0x42; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_PIO_1: dma_pio_timing |= 0x65; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; case XFER_PIO_0: default: dma_pio_timing |= 0xA8; - pio_timing |= (0x03 << drive_number); + pio_timing |= (0x03 << drive->dn); break; } + if (!drive->init_speed) + drive->init_speed = speed; + +#ifdef CONFIG_BLK_DEV_IDEDMA pci_write_config_byte(dev, drive_pci, ultra_timing); +#endif /* CONFIG_BLK_DEV_IDEDMA */ pci_write_config_byte(dev, drive_pci2, dma_pio_timing); pci_write_config_byte(dev, 0x4c, pio_timing); @@ -186,12 +194,16 @@ static int amd7409_tune_chipset (ide_drive_t *drive, byte speed) ultra_timing, dma_pio_timing, pio_timing); #endif +#ifdef CONFIG_BLK_DEV_IDEDMA if (speed > XFER_PIO_4) { outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2); } else { outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2); } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; return (err); } @@ -231,6 +243,7 @@ static void config_chipset_for_pio (ide_drive_t *drive) break; } (void) amd7409_tune_chipset(drive, speed); + drive->current_speed = speed; } static void amd7409_tune_drive (ide_drive_t *drive, byte pio) @@ -403,13 +416,14 @@ unsigned int __init ata66_amd7409 (ide_hwif_t *hwif) void __init ide_init_amd7409 (ide_hwif_t *hwif) { hwif->tuneproc = &amd7409_tune_drive; + hwif->speedproc = &amd7409_tune_chipset; #ifndef CONFIG_BLK_DEV_IDEDMA hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; hwif->autodma = 0; return; -#endif /* CONFIG_BLK_DEV_IDEDMA */ +#else if (hwif->dma_base) { hwif->dmaproc = &amd7409_dmaproc; @@ -419,6 +433,7 @@ void __init ide_init_amd7409 (ide_hwif_t *hwif) hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; } +#endif /* CONFIG_BLK_DEV_IDEDMA */ } void ide_dmacapable_amd7409 (ide_hwif_t *hwif, unsigned long dmabase) diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index 8e100de3c20..7e9f0c7eecb 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -344,9 +344,9 @@ static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed) (void) ide_config_drive_speed(drive, speed); } -#ifdef CONFIG_BLK_DEV_IDEDMA -static int tune_chipset_for_dma (ide_drive_t *drive, byte speed) +static int cmd64x_tune_chipset (ide_drive_t *drive, byte speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; byte unit = (drive->select.b.unit & 0x01); @@ -357,6 +357,8 @@ static int tune_chipset_for_dma (ide_drive_t *drive, byte speed) u8 regU = 0; u8 regD = 0; + if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) return 1; + (void) pci_read_config_byte(dev, pciD, ®D); (void) pci_read_config_byte(dev, pciU, ®U); regD &= ~(unit ? 0x40 : 0x20); @@ -378,17 +380,37 @@ static int tune_chipset_for_dma (ide_drive_t *drive, byte speed) case XFER_SW_DMA_2: regD |= (unit ? 0x40 : 0x10); break; case XFER_SW_DMA_1: regD |= (unit ? 0x80 : 0x20); break; case XFER_SW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break; +#else + int err = 0; + + switch(speed) { +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: cmd64x_tuneproc(drive, 4); break; + case XFER_PIO_3: cmd64x_tuneproc(drive, 3); break; + case XFER_PIO_2: cmd64x_tuneproc(drive, 2); break; + case XFER_PIO_1: cmd64x_tuneproc(drive, 1); break; + case XFER_PIO_0: cmd64x_tuneproc(drive, 0); break; + default: return 1; } +#ifdef CONFIG_BLK_DEV_IDEDMA (void) pci_write_config_byte(dev, pciU, regU); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + err = ide_config_drive_speed(drive, speed); + + drive->current_speed = speed; + +#ifdef CONFIG_BLK_DEV_IDEDMA regD |= (unit ? 0x40 : 0x20); (void) pci_write_config_byte(dev, pciD, regD); +#endif /* CONFIG_BLK_DEV_IDEDMA */ return err; } +#ifdef CONFIG_BLK_DEV_IDEDMA static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66) { struct hd_driveid *id = drive->id; @@ -450,12 +472,15 @@ static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ul set_pio = 1; } + if (!drive->init_speed) + drive->init_speed = speed; + config_chipset_for_pio(drive, set_pio); if (set_pio) return ((int) ide_dma_off_quietly); - if (tune_chipset_for_dma(drive, speed)) + if (cmd64x_tune_chipset(drive, speed)) return ((int) ide_dma_off); rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on : @@ -680,8 +705,9 @@ void __init ide_init_cmd64x (ide_hwif_t *hwif) class_rev &= 0xff; hwif->tuneproc = &cmd64x_tuneproc; + hwif->speedproc = &cmd64x_tune_chipset; hwif->drives[0].autotune = 1; - hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; if (!hwif->dma_base) return; diff --git a/drivers/ide/hd.c b/drivers/ide/hd.c index b64a9738c06..1c6a0b286d6 100644 --- a/drivers/ide/hd.c +++ b/drivers/ide/hd.c @@ -881,3 +881,12 @@ static int revalidate_hddisk(kdev_t dev, int maxusage) return 0; } +static int parse_hd_setup (char *line) { + int ints[6]; + + (void) get_options(line, ARRAY_SIZE(ints), ints); + hd_setup(NULL, ints); + + return 0; +} +__setup("hd=", parse_hd_setup); diff --git a/drivers/ide/hpt34x.c b/drivers/ide/hpt34x.c index a68e7f7408a..911068f7846 100644 --- a/drivers/ide/hpt34x.c +++ b/drivers/ide/hpt34x.c @@ -96,14 +96,13 @@ extern char *ide_xfer_verbose (byte xfer_rate); static void hpt34x_clear_chipset (ide_drive_t *drive) { - int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); unsigned int reg1 = 0, tmp1 = 0; unsigned int reg2 = 0, tmp2 = 0; pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, ®1); pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2); - tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number)))); - tmp2 = (reg2 & ~(0x11 << drive_number)); + tmp1 = ((0x00 << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); + tmp2 = (reg2 & ~(0x11 << drive->dn)); pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1); pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2); } @@ -112,7 +111,6 @@ static int hpt34x_tune_chipset (ide_drive_t *drive, byte speed) { int err; byte hi_speed, lo_speed; - int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); unsigned int reg1 = 0, tmp1 = 0; unsigned int reg2 = 0, tmp2 = 0; @@ -127,20 +125,24 @@ static int hpt34x_tune_chipset (ide_drive_t *drive, byte speed) pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, ®1); pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2); - tmp1 = ((lo_speed << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number)))); - tmp2 = ((hi_speed << drive_number) | reg2); + tmp1 = ((lo_speed << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); + tmp2 = ((hi_speed << drive->dn) | reg2); err = ide_config_drive_speed(drive, speed); pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1); pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2); + if (!drive->init_speed) + drive->init_speed = speed; + #if HPT343_DEBUG_DRIVE_INFO printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \ " (0x%02x 0x%02x) 0x%04x\n", drive->name, ide_xfer_verbose(speed), - drive_number, reg1, tmp1, reg2, tmp2, + drive->dn, reg1, tmp1, reg2, tmp2, hi_speed, lo_speed, err); #endif /* HPT343_DEBUG_DRIVE_INFO */ + drive->current_speed = speed; return(err); } @@ -410,6 +412,7 @@ unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name) void __init ide_init_hpt34x (ide_hwif_t *hwif) { hwif->tuneproc = &hpt34x_tune_drive; + hwif->speedproc = &hpt34x_tune_chipset; #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_base) { diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c index fd5971ef964..28232b5768d 100644 --- a/drivers/ide/hpt366.c +++ b/drivers/ide/hpt366.c @@ -173,6 +173,14 @@ extern char *ide_xfer_verbose (byte xfer_rate); byte hpt363_shared_irq = 0; byte hpt363_shared_pin = 0; +static unsigned int pci_rev_check_hpt366 (struct pci_dev *dev) +{ + unsigned int class_rev; + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + return ((int) (class_rev == 0x03) ? 1 : 0); +} + static int check_in_drive_lists (ide_drive_t *drive, const char **list) { struct hd_driveid *id = drive->id; @@ -212,9 +220,6 @@ static unsigned int pci_bus_clock_list (byte speed, struct chipset_bus_clock_lis static int hpt366_tune_chipset (ide_drive_t *drive, byte speed) { int err; -#if HPT366_DEBUG_DRIVE_INFO - int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); -#endif /* HPT366_DEBUG_DRIVE_INFO */ byte regtime = (drive->select.b.unit & 0x01) ? 0x44 : 0x40; unsigned int reg1 = 0; unsigned int reg2 = 0; @@ -251,11 +256,15 @@ static int hpt366_tune_chipset (ide_drive_t *drive, byte speed) pci_write_config_dword(HWIF(drive)->pci_dev, regtime, reg2); err = ide_config_drive_speed(drive, speed); + if (!drive->init_speed) + drive->init_speed = speed; + #if HPT366_DEBUG_DRIVE_INFO printk("%s: speed=0x%02x(%s), drive%d, old=0x%08x, new=0x%08x, err=0x%04x\n", drive->name, speed, ide_xfer_verbose(speed), - drive_number, reg1, reg2, err); + drive->dn, reg1, reg2, err); #endif /* HPT366_DEBUG_DRIVE_INFO */ + drive->current_speed = speed; return(err); } @@ -469,10 +478,12 @@ int hpt366_dmaproc (ide_dma_action_t func, ide_drive_t *drive) case ide_dma_check: return config_drive_xfer_rate(drive); case ide_dma_lostirq: +#if 0 pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, ®50h); pci_write_config_byte(HWIF(drive)->pci_dev, 0x50, reg50h|0x03); pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, ®50h); /* ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); */ +#endif case ide_dma_timeout: default: break; @@ -508,6 +519,8 @@ unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name) if (!hpt366_proc) { hpt366_proc = 1; bmide_dev = dev; + if (pci_rev_check_hpt366(dev)) + bmide2_dev = dev; hpt366_display_info = &hpt366_get_info; } if ((hpt366_proc) && ((dev->devfn - bmide_dev->devfn) == 1)) { @@ -533,7 +546,11 @@ unsigned int __init ata66_hpt366 (ide_hwif_t *hwif) void __init ide_init_hpt366 (ide_hwif_t *hwif) { + if (pci_rev_check_hpt366(hwif->pci_dev)) return; + hwif->tuneproc = &hpt366_tune_drive; + hwif->speedproc = &hpt366_tune_chipset; + #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_base) { hwif->dmaproc = &hpt366_dmaproc; @@ -556,6 +573,11 @@ void ide_dmacapable_hpt366 (ide_hwif_t *hwif, unsigned long dmabase) byte dma_new = 0, dma_old = inb(dmabase+2); unsigned long flags; + if (pci_rev_check_hpt366(hwif->pci_dev)) { + ide_setup_dma(hwif, dmabase, 8); + return; + } + __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index 8ef0f9356e1..dcc50362faa 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -219,55 +220,73 @@ static iftype_t icside_identifyif (struct expansion_card *ec) * here, but we rely on the main IDE driver spotting that both * interfaces use the same IRQ, which should guarantee this. */ -#define TABLE_SIZE 2048 +#define NR_ENTRIES 256 +#define TABLE_SIZE (NR_ENTRIES * 8) -static int -icside_build_dmatable(ide_drive_t *drive, int reading) +static int ide_build_sglist(ide_hwif_t *hwif, struct request *rq) { - struct request *rq = HWGROUP(drive)->rq; - struct buffer_head *bh = rq->bh; - unsigned long addr, size; - unsigned char *virt_addr; - unsigned int count = 0; - dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable_cpu; + struct buffer_head *bh; + struct scatterlist *sg = hwif->sg_table; + int nents = 0; + if (rq->cmd == READ) + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + hwif->sg_dma_direction = PCI_DMA_TODEVICE; + bh = rq->bh; do { - if (bh == NULL) { - /* paging requests have (rq->bh == NULL) */ - virt_addr = rq->buffer; - addr = virt_to_bus (virt_addr); - size = rq->nr_sectors << 9; - } else { - /* group sequential buffers into one large buffer */ - virt_addr = bh->b_data; - addr = virt_to_bus (virt_addr); - size = bh->b_size; - while ((bh = bh->b_reqnext) != NULL) { - if ((addr + size) != virt_to_bus (bh->b_data)) - break; - size += bh->b_size; - } - } + unsigned char *virt_addr = bh->b_data; + unsigned int size = bh->b_size; - if (addr & 3) { - printk("%s: misaligned DMA buffer\n", drive->name); - return 0; + while ((bh = bh->b_reqnext) != NULL) { + if ((virt_addr + size) != (unsigned char *)bh->b_data) + break; + size += bh->b_size; } + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_addr; + sg[nents].length = size; + nents++; + } while (bh != NULL); - if (size) { - if (reading) - dma_cache_inv((unsigned int)virt_addr, size); - else - dma_cache_wback((unsigned int)virt_addr, size); - } + return pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); +} - sg[count].address = addr; - sg[count].length = size; - if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) { - printk("%s: DMA table too small\n", drive->name); +static int +icside_build_dmatable(ide_drive_t *drive, int reading) +{ + dmasg_t *ide_sg = (dmasg_t *)HWIF(drive)->dmatable_cpu; + unsigned int count = 0; + int i; + struct scatterlist *sg; + + HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq); + + sg = HWIF(drive)->sg_table; + while (i && sg_dma_len(sg)) { + u32 cur_addr; + u32 cur_len; + + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + if (count >= (TABLE_SIZE / sizeof(dmasg_t))) { + printk("%s: DMA table too small\n", + drive->name); + pci_unmap_sg(NULL, + HWIF(drive)->sg_table, + HWIF(drive)->sg_nents, + HWIF(drive)->sg_dma_direction); return 0; + } else { + ide_sg[count].address = cur_addr; + ide_sg[count].length = cur_len; } - } while (bh != NULL); + + count++; + sg++; + i--; + } if (!count) printk("%s: empty DMA table?\n", drive->name); @@ -275,6 +294,15 @@ icside_build_dmatable(ide_drive_t *drive, int reading) return count; } +/* Teardown mappings after DMA has completed. */ +static void icside_destroy_dmatable(ide_drive_t *drive) +{ + struct scatterlist *sg = HWIF(drive)->sg_table; + int nents = HWIF(drive)->sg_nents; + + pci_unmap_sg(NULL, sg, nents, HWIF(drive)->sg_dma_direction); +} + static int icside_config_if(ide_drive_t *drive, int xfer_mode) { @@ -303,6 +331,9 @@ icside_config_if(ide_drive_t *drive, int xfer_mode) break; } + if (!drive->init_speed) + drive->init_speed = (byte) xfer_mode; + if (drive->drive_data && ide_config_drive_speed(drive, (byte) xfer_mode) == 0) func = ide_dma_on; @@ -312,10 +343,18 @@ icside_config_if(ide_drive_t *drive, int xfer_mode) printk("%s: %s selected (peak %dMB/s)\n", drive->name, ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data); + drive->current_speed = (byte) xfer_mode; + return func; } static int +icside_set_speed(ide_drive_t *drive, byte speed) +{ + return ((int) icside_config_if(drive, (int) speed)); +} + +static int icside_dma_check(ide_drive_t *drive) { struct hd_driveid *id = drive->id; @@ -415,6 +454,7 @@ icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive) case ide_dma_end: drive->waiting_for_dma = 0; disable_dma(hwif->hw.dma); + icside_destroy_dmatable(drive); return get_dma_residue(hwif->hw.dma) != 0; case ide_dma_test_irq: @@ -425,8 +465,7 @@ icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive) } } -static unsigned long -icside_alloc_dmatable(void) +static void *icside_alloc_dmatable(void) { static unsigned long dmatable; static unsigned int leftover; @@ -448,28 +487,39 @@ icside_alloc_dmatable(void) leftover -= TABLE_SIZE; } - return table; + return (void *)table; } static int icside_setup_dma(ide_hwif_t *hwif, int autodma) { - unsigned long table = icside_alloc_dmatable(); - printk(" %s: SG-DMA", hwif->name); - if (!table) - printk(" -- ERROR, unable to allocate DMA table\n"); - else { - hwif->dmatable_cpu = (void *)table; - hwif->dmaproc = icside_dmaproc; - hwif->autodma = autodma; + hwif->sg_table = kmalloc(sizeof(struct scatterlist) * NR_ENTRIES, + GFP_KERNEL); + if (!hwif->sg_table) + goto failed; + + hwif->dmatable_cpu = icside_alloc_dmatable(); - printk(" capable%s\n", autodma ? - ", auto-enable" : ""); + if (!hwif->dmatable_cpu) { + kfree(hwif->sg_table); + hwif->sg_table = NULL; + goto failed; } - return hwif->dmatable_cpu != NULL; + hwif->dmaproc = &icside_dmaproc; + hwif->autodma = autodma; + hwif->speedproc = &icside_set_speed; + + printk(" capable%s\n", autodma ? + ", auto-enable" : ""); + + return 1; + +failed: + printk(" -- ERROR, unable to allocate DMA table\n"); + return 0; } #endif diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 66bf35a70c0..4e295201804 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -33,8 +33,6 @@ #undef REALLY_SLOW_IO /* most systems can safely undef this */ -#define _IDE_DISK_C /* Tell linux/hdsmart.h it's really us */ - #include #include #include @@ -688,13 +686,12 @@ static int set_multcount(ide_drive_t *drive, int arg) static int set_nowerr(ide_drive_t *drive, int arg) { - unsigned long flags; - - if (ide_spin_wait_hwgroup(drive, &flags)) + if (ide_spin_wait_hwgroup(drive)) return -EBUSY; + drive->nowerr = arg; drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; - spin_unlock_irqrestore(&io_request_lock, flags); + spin_unlock_irq(&io_request_lock); return 0; } @@ -713,7 +710,7 @@ static void idedisk_add_settings(ide_drive_t *drive) ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); - + ide_add_setting(drive, "lun", SETTING_RW, -1, -1, TYPE_INT, 0, 7, 1, 1, &drive->lun, NULL); } /* diff --git a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c index 371f57ad8db..fcf0aab6337 100644 --- a/drivers/ide/ide-features.c +++ b/drivers/ide/ide-features.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,46 @@ char *ide_dmafunc_verbose (ide_dma_action_t dmafunc) } /* + * + */ +byte ide_auto_reduce_xfer (ide_drive_t *drive) +{ + switch(drive->current_speed) { + case XFER_UDMA_7: return XFER_UDMA_6; + case XFER_UDMA_6: return XFER_UDMA_5; + case XFER_UDMA_5: return XFER_UDMA_4; + case XFER_UDMA_4: return XFER_UDMA_3; + case XFER_UDMA_3: return XFER_UDMA_2; + case XFER_UDMA_2: return XFER_UDMA_1; + case XFER_UDMA_1: return XFER_UDMA_0; + case XFER_UDMA_0: + if (drive->id->dma_mword & 0x0004) return XFER_MW_DMA_2; + else if (drive->id->dma_mword & 0x0002) return XFER_MW_DMA_1; + else if (drive->id->dma_mword & 0x0001) return XFER_MW_DMA_0; + else return XFER_PIO_4; + case XFER_MW_DMA_2: return XFER_MW_DMA_1; + case XFER_MW_DMA_1: return XFER_MW_DMA_0; + case XFER_MW_DMA_0: + if (drive->id->dma_1word & 0x0004) return XFER_SW_DMA_2; + else if (drive->id->dma_1word & 0x0002) return XFER_SW_DMA_1; + else if (drive->id->dma_1word & 0x0001) return XFER_SW_DMA_0; + else return XFER_PIO_4; + case XFER_SW_DMA_2: return XFER_SW_DMA_1; + case XFER_SW_DMA_1: return XFER_SW_DMA_0; + case XFER_SW_DMA_0: + { + return XFER_PIO_4; + } + case XFER_PIO_4: return XFER_PIO_3; + case XFER_PIO_3: return XFER_PIO_2; + case XFER_PIO_2: return XFER_PIO_1; + case XFER_PIO_1: return XFER_PIO_0; + case XFER_PIO_0: + default: return XFER_PIO_SLOW; + } +} + +/* * Update the */ int ide_driveid_update (ide_drive_t *drive) @@ -136,8 +177,10 @@ int ide_driveid_update (ide_drive_t *drive) ide_delay_50ms(); /* give drive a breather */ } while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT); ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */ - if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) + if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) { + printk("%s: CHECK for good STATUS\n", drive->name); return 0; + } __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only; some systems need this */ id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC); @@ -146,17 +189,14 @@ int ide_driveid_update (ide_drive_t *drive) ide__sti(); /* local CPU only */ __restore_flags(flags); /* local CPU only */ ide_fix_driveid(id); - if (id && id->cyls) { + if (id) { drive->id->dma_ultra = id->dma_ultra; drive->id->dma_mword = id->dma_mword; drive->id->dma_1word = id->dma_1word; /* anything more ? */ -#ifdef DEBUG - printk("%s: dma_ultra=%04X, dma_mword=%04X, dma_1word=%04X\n", - drive->name, id->dma_ultra, id->dma_mword, id->dma_1word); -#endif kfree(id); } + return 1; } @@ -167,7 +207,7 @@ int ide_driveid_update (ide_drive_t *drive) * in combination with the device (usually a disk) properly detect * and acknowledge each end of the ribbon. */ -int ide_ata66_check (ide_drive_t *drive, int cmd, int nsect, int feature) +int ide_ata66_check (ide_drive_t *drive, byte cmd, byte nsect, byte feature) { if ((cmd == WIN_SETFEATURES) && (nsect > XFER_UDMA_2) && @@ -189,15 +229,16 @@ int ide_ata66_check (ide_drive_t *drive, int cmd, int nsect, int feature) * 1 : Safe to update drive->id DMA registers. * 0 : OOPs not allowed. */ -int set_transfer (ide_drive_t *drive, int cmd, int nsect, int feature) +int set_transfer (ide_drive_t *drive, byte cmd, byte nsect, byte feature) { - struct hd_driveid *id = drive->id; - if ((cmd == WIN_SETFEATURES) && (nsect >= XFER_SW_DMA_0) && (feature == SETFEATURES_XFER) && - (id->dma_ultra || id->dma_mword || id->dma_1word)) + (drive->id->dma_ultra || + drive->id->dma_mword || + drive->id->dma_1word)) return 1; + return 0; } @@ -219,6 +260,8 @@ int ide_config_drive_speed (ide_drive_t *drive, byte speed) byte unit = (drive->select.b.unit & 0x01); byte stat; + outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2); + /* * Don't use ide_wait_cmd here - it will * attempt to set_geometry and recalibrate, @@ -307,6 +350,7 @@ int ide_config_drive_speed (ide_drive_t *drive, byte speed) return error; } +EXPORT_SYMBOL(ide_auto_reduce_xfer); EXPORT_SYMBOL(ide_driveid_update); EXPORT_SYMBOL(ide_ata66_check); EXPORT_SYMBOL(set_transfer); diff --git a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c index 8d999d91c61..c10ae4b51cb 100644 --- a/drivers/ide/ide-pci.c +++ b/drivers/ide/ide-pci.c @@ -30,6 +30,7 @@ #define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1}) #define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB}) #define DEVID_PIIX4E ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1}) +#define DEVID_PIIX4E2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1}) #define DEVID_PIIX4U ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1}) #define DEVID_PIIX4U2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1}) #define DEVID_VIA_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561}) @@ -303,6 +304,7 @@ static ide_pci_device_t ide_pci_chipsets[] __initdata = { {DEVID_PIIX3, "PIIX3", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4E, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4E2, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4U, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4U2, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_VIA_IDE, "VIA_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, @@ -476,6 +478,7 @@ static void __init ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t * unsigned short pcicmd = 0, tried_config = 0; byte tmp = 0; ide_hwif_t *hwif, *mate = NULL; + unsigned int class_rev; #ifdef CONFIG_IDEDMA_AUTO autodma = 1; @@ -504,6 +507,11 @@ check_if_enabled: } if (tried_config) printk("%s: device enabled (Linux)\n", d->name); + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + printk("%s: chipset revision %d\n", d->name, class_rev); + /* * Can we trust the reported IRQ? */ @@ -545,7 +553,7 @@ check_if_enabled: ide_pci_enablebit_t *e = &(d->enablebits[port]); if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val)) continue; /* port not enabled */ - if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && (port)) + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && (port) && (class_rev != 0x03)) return; if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || (dev->class & (port ? 4 : 1)) != 0) { ctl = dev->resource[(2*port)+1].start; @@ -656,9 +664,19 @@ static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_devic struct pci_dev *dev2 = NULL, *findev; ide_pci_device_t *d2; unsigned char pin1 = 0, pin2 = 0; + unsigned int class_rev; if (PCI_FUNC(dev->devfn) & 1) return; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + switch(class_rev) { + case 3: return; + default: break; + } + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1); pci_for_each_dev(findev) { if ((findev->vendor == dev->vendor) && diff --git a/drivers/ide/ide-pmac.c b/drivers/ide/ide-pmac.c index 7f5226db9db..45bbe77ac13 100644 --- a/drivers/ide/ide-pmac.c +++ b/drivers/ide/ide-pmac.c @@ -7,6 +7,8 @@ * * Copyright (C) 1998 Paul Mackerras. * + * Bits from Benjamin Herrenschmidt + * * 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 @@ -16,12 +18,6 @@ * * Copyright (c) 1995-1998 Mark Lord * - * BenH: I began adding more complete timing setup code, mostly because DMA - * won't work on new machines unless timings are setup correctly. This - * code was mainly stolen from Cmd646 driver and should be completed to - * include real timing calc. instead of hard coded values. The format of - * the timing register can be found in Darwin's source code, except for - * Keylargo ATA-4 controller. */ #include #include @@ -44,14 +40,18 @@ #endif #include "ide_modes.h" +extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc); + #undef IDE_PMAC_DEBUG -#define IDE_SYSCLK_NS 30 +#define IDE_SYSCLK_NS 30 +#define IDE_SYSCLK_ULTRA_PS 0x1d4c /* (15 * 1000 / 2)*/ struct pmac_ide_hwif { ide_ioreg_t regbase; int irq; int kind; + int aapl_bus_id; struct device_node* node; u32 timings[2]; #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC @@ -90,17 +90,27 @@ static pmac_ide_timing mdma_timings[] = static pmac_ide_timing udma_timings[] = { { 0, 114 }, /* Mode 0 */ - { 0, 73 }, /* 1 */ - { 0, 54 }, /* 2 */ - { 0, 39 }, /* 3 */ - { 0, 25 } /* 4 */ + { 0, 75 }, /* 1 */ + { 0, 55 }, /* 2 */ + { 100, 45 }, /* 3 */ + { 100, 25 } /* 4 */ }; -#define MAX_DCMDS 256 /* allow up to 256 DBDMA commands per xfer */ +/* allow up to 256 DBDMA commands per xfer */ +#define MAX_DCMDS 256 + +/* Wait 1.5s for disk to answer on IDE bus after + * enable operation. + * NOTE: There is at least one case I know of a disk that needs about 10sec + * before anwering on the bus. I beleive we could add a kernel command + * line arg to override this delay for such cases. + */ +#define IDE_WAKEUP_DELAY_MS 1500 static void pmac_ide_setup_dma(struct device_node *np, int ix); static int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive); static int pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr); +static int pmac_ide_tune_chipset(ide_drive_t *drive, byte speed); static void pmac_ide_tuneproc(ide_drive_t *drive, byte pio); static void pmac_ide_selectproc(ide_drive_t *drive); @@ -156,14 +166,6 @@ void pmac_ide_init_hwif_ports(hw_regs_t *hw, return; } - /* we check only for -EINVAL meaning that we have found a matching - bay but with the wrong device type */ - i = check_media_bay_by_base(data_port, MB_CD); - if (i == -EINVAL) { - hw->io_ports[IDE_DATA_OFFSET] = 0; - return; - } - for (i = 0; i < 8; ++i) hw->io_ports[i] = data_port + i * 0x10; hw->io_ports[8] = data_port + 0x160; @@ -178,6 +180,7 @@ void pmac_ide_init_hwif_ports(hw_regs_t *hw, #ifdef CONFIG_PMAC_IDEDMA_AUTO ide_hwifs[ix].autodma = 1; #endif +// ide_hwifs[ix].speedproc = &pmac_ide_tune_chipset; } } @@ -214,7 +217,9 @@ pmac_ide_selectproc(ide_drive_t *drive) /* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */ #define SYSCLK_TICKS(t) (((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS) +#define SYSCLK_TICKS_UDMA(t) (((t) + IDE_SYSCLK_ULTRA_PS - 1) / IDE_SYSCLK_ULTRA_PS) +/* Calculate PIO timings */ static void pmac_ide_tuneproc(ide_drive_t *drive, byte pio) { @@ -227,26 +232,31 @@ pmac_ide_tuneproc(ide_drive_t *drive, byte pio) if (i < 0) return; - /* The "ata-4" IDE controller of UMA machines is a bit different. - * We don't do anything for PIO modes until we know how to do the - * calculation. - */ - if (pmac_ide[i].kind == controller_kl_ata4) - return; - pio = ide_get_best_pio_mode(drive, pio, 4, &d); accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time); - if (accessTicks < 4) - accessTicks = 4; - recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4; - if (recTicks < 1) - recTicks = 1; if (drive->select.all & 0x10) timings = &pmac_ide[i].timings[1]; else timings = &pmac_ide[i].timings[0]; - *timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5); + if (pmac_ide[i].kind == controller_kl_ata4) { + /* The "ata-4" IDE controller of Core99 machines */ + accessTicks = SYSCLK_TICKS_UDMA(ide_pio_timings[pio].active_time * 1000); + recTicks = SYSCLK_TICKS_UDMA(d.cycle_time * 1000) - accessTicks; + + *timings = ((*timings) & 0x1FFFFFC00) | accessTicks | (recTicks << 5); + } else { + /* The old "ata-3" IDE controller */ + accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time); + if (accessTicks < 4) + accessTicks = 4; + recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4; + if (recTicks < 1) + recTicks = 1; + + *timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5); + } + #ifdef IDE_PMAC_DEBUG printk("ide_pmac: Set PIO timing for mode %d, reg: 0x%08x\n", pio, *timings); @@ -294,7 +304,7 @@ pmac_ide_probe(void) struct device_node *atas; struct device_node *p, **pp, *removables, **rp; unsigned long base; - int irq; + int irq, big_delay; ide_hwif_t *hwif; if (_machine != _MACH_Pmac) @@ -322,9 +332,11 @@ pmac_ide_probe(void) } *rp = NULL; *pp = removables; + big_delay = 0; for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) { struct device_node *tp; + int *bidp; /* * If this node is not under a mac-io or dbdma node, @@ -378,6 +390,9 @@ pmac_ide_probe(void) else pmac_ide[i].kind = controller_ohare; + bidp = (int *)get_property(np, "AAPL,bus-id", NULL); + pmac_ide[i].aapl_bus_id = bidp ? *bidp : 0; + if (np->parent && np->parent->name && strcasecmp(np->parent->name, "media-bay") == 0) { media_bay_set_ide_infos(np->parent,base,irq,i); @@ -388,42 +403,41 @@ pmac_ide_probe(void) */ feature_set(np, FEATURE_IDE0_enable); } else { - /* This is necessary to enable IDE when net-booting */ - int *bidp = (int *)get_property(np, "AAPL,bus-id", NULL); - int bid = bidp ? *bidp : 0; - printk("pmac_ide: enabling IDE bus ID %d\n", bid); - switch(bid) { + /* This is necessary to enable IDE when net-booting */ + printk("pmac_ide: enabling IDE bus ID %d\n", + pmac_ide[i].aapl_bus_id); + switch(pmac_ide[i].aapl_bus_id) { case 0: feature_set(np, FEATURE_IDE0_reset); - feature_set(np, FEATURE_IOBUS_enable); mdelay(10); feature_set(np, FEATURE_IDE0_enable); mdelay(10); feature_clear(np, FEATURE_IDE0_reset); break; case 1: - feature_set(np, FEATURE_Mediabay_IDE_reset); + feature_set(np, FEATURE_IDE1_reset); mdelay(10); - feature_set(np, FEATURE_Mediabay_IDE_enable); + feature_set(np, FEATURE_IDE1_enable); mdelay(10); - feature_clear(np, FEATURE_Mediabay_IDE_reset); + feature_clear(np, FEATURE_IDE1_reset); break; case 2: - /* This one exists only for KL, I don't know about any - enable bit */ + /* This one exists only for KL, I don't know + about any enable bit */ feature_set(np, FEATURE_IDE2_reset); mdelay(10); feature_clear(np, FEATURE_IDE2_reset); break; } - mdelay(1000); + big_delay = 1; } hwif = &ide_hwifs[i]; pmac_ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->irq); memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); hwif->chipset = ide_pmac; - hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; + hwif->noprobe = (!hwif->io_ports[IDE_DATA_OFFSET]) || + (check_media_bay_by_base(base, MB_CD) == -EINVAL); #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC if (np->n_addrs >= 2) { @@ -435,6 +449,8 @@ pmac_ide_probe(void) ++i; } pmac_ide_count = i; + if (big_delay) + mdelay(IDE_WAKEUP_DELAY_MS); #ifdef CONFIG_PMAC_PBOOK pmu_register_sleep_notifier(&idepmac_sleep_notifier); @@ -642,6 +658,7 @@ out: return result; } +/* Calculate MultiWord DMA timings */ static int pmac_ide_mdma_enable(ide_drive_t *drive, int idx) { @@ -652,18 +669,15 @@ pmac_ide_mdma_enable(ide_drive_t *drive, int idx) int accessTicks, recTicks; struct hd_driveid *id = drive->id; - /* For now, we don't know these values */ - if (pmac_ide[idx].kind == controller_kl_ata4 && feature != IDE_DMA2_ENABLE) - return 0; - if (pmac_ide[idx].kind != controller_kl_ata4 && feature == IDE_DMA0_ENABLE) - return 0; - /* Set feature on drive */ printk("%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf); if (pmac_ide_do_setfeature(drive, feature)) { printk("%s: Failed !\n", drive->name); return 0; } + + if (!drive->init_speed) + drive->init_speed = feature; /* which drive is it ? */ if (drive->select.all & 0x10) @@ -681,7 +695,10 @@ pmac_ide_mdma_enable(ide_drive_t *drive, int idx) /* For ata-4 controller, we don't know the calculation */ if (pmac_ide[idx].kind == controller_kl_ata4) { - *timings = 0x00019465; /* MDMA2 */ + accessTicks = SYSCLK_TICKS_UDMA(accessTime * 1000); + recTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000) - accessTicks; + *timings = ((*timings) & 0xffe003ff) | + (accessTicks | (recTicks << 5)) << 10; } else { int halfTick = 0; int origAccessTime = accessTime; @@ -696,7 +713,9 @@ pmac_ide_mdma_enable(ide_drive_t *drive, int idx) recTicks = 1; cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS; - if ((accessTicks > 1) && + /* KeyLargo ata-3 don't support the half-tick stuff */ + if ((pmac_ide[idx].kind != controller_kl_ata3) && + (accessTicks > 1) && ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) && ((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) { halfTick = 1; @@ -708,21 +727,21 @@ pmac_ide_mdma_enable(ide_drive_t *drive, int idx) #ifdef IDE_PMAC_DEBUG printk("ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n", feature & 0xf, *timings); -#endif +#endif + drive->current_speed = feature; return 1; } +/* Calculate Ultra DMA timings */ static int pmac_ide_udma_enable(ide_drive_t *drive, int idx) { byte bits = drive->id->dma_ultra & 0x1f; byte feature = udma_bits_to_command(bits); - u32 timings; + int cycleTime, accessTime; + int rdyToPauseTicks, cycleTicks; + u32 *timings; - /* We support only those values */ - if (feature != IDE_UDMA4_ENABLE && feature != IDE_UDMA2_ENABLE) - return 0; - /* Set feature on drive */ printk("%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf); if (pmac_ide_do_setfeature(drive, feature)) { @@ -730,23 +749,25 @@ pmac_ide_udma_enable(ide_drive_t *drive, int idx) return 0; } - /* Put this channel into UDMA mode. - * This value is set by MacOS on the iBook for U/DMA2 - */ - switch(feature) { - case IDE_UDMA4_ENABLE: - timings = 0x0cd00065; - break; - case IDE_UDMA2_ENABLE: - timings = 0x11100065; - break; - } - + if (!drive->init_speed) + drive->init_speed = feature; + + /* which drive is it ? */ if (drive->select.all & 0x10) - pmac_ide[idx].timings[1] = timings; + timings = &pmac_ide[idx].timings[1]; else - pmac_ide[idx].timings[0] = timings; - + timings = &pmac_ide[idx].timings[0]; + + cycleTime = udma_timings[feature & 0xf].cycleTime; + accessTime = udma_timings[feature & 0xf].accessTime; + + rdyToPauseTicks = SYSCLK_TICKS_UDMA(accessTime * 1000); + cycleTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000); + + *timings = ((*timings) & 0xe00fffff) | + ((cycleTicks << 1) | (rdyToPauseTicks << 5) | 1) << 20; + + drive->current_speed = feature; return 1; } @@ -792,6 +813,12 @@ pmac_ide_dma_onoff(ide_drive_t *drive, int enable) return 0; } +static int +pmac_ide_tune_chipset(ide_drive_t *drive, byte speed) +{ + return 0; +} + int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); @@ -813,6 +840,7 @@ int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive) pmac_ide_dma_onoff(drive, (func == ide_dma_on)); break; case ide_dma_check: + printk("IDE-DMA check !\n"); if (hwif->autodma) pmac_ide_dma_onoff(drive, 1); break; @@ -837,8 +865,21 @@ int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive) return (dstat & (RUN|DEAD|ACTIVE)) != RUN; case ide_dma_test_irq: return (in_le32(&dma->status) & (RUN|ACTIVE)) == RUN; + + /* Let's implement tose just in case someone wants them */ + case ide_dma_bad_drive: + case ide_dma_good_drive: + return check_drive_lists(drive, (func == ide_dma_good_drive)); + case ide_dma_verbose: + return report_drive_dmaing(drive); + case ide_dma_retune: + case ide_dma_lostirq: + case ide_dma_timeout: + printk("ide_pmac_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func), func); + return 1; default: - printk(KERN_ERR "pmac_ide_dmaproc: bad func %d\n", func); + printk("ide_pmac_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func); + return 1; } return 0; } @@ -869,8 +910,20 @@ static void idepmac_sleep_disk(int i, unsigned long base) } } feature_set(np, FEATURE_IDE0_reset); - feature_clear(np, FEATURE_IOBUS_enable); feature_clear(np, FEATURE_IDE0_enable); + switch(pmac_ide[i].aapl_bus_id) { + case 0: + feature_set(np, FEATURE_IDE0_reset); + feature_clear(np, FEATURE_IDE0_enable); + break; + case 1: + feature_set(np, FEATURE_IDE1_reset); + feature_clear(np, FEATURE_IDE1_enable); + break; + case 2: + feature_set(np, FEATURE_IDE2_reset); + break; + } pmac_ide[i].timings[0] = 0; pmac_ide[i].timings[1] = 0; } @@ -881,12 +934,30 @@ static void idepmac_wake_disk(int i, unsigned long base) int j; /* Revive IDE disk and controller */ - feature_set(np, FEATURE_IOBUS_enable); - mdelay(10); - feature_set(np, FEATURE_IDE0_enable); - mdelay(10); - feature_clear(np, FEATURE_IDE0_reset); - mdelay(100); + switch(pmac_ide[i].aapl_bus_id) { + case 0: + feature_set(np, FEATURE_IDE0_reset); + mdelay(10); + feature_set(np, FEATURE_IDE0_enable); + mdelay(10); + feature_clear(np, FEATURE_IDE0_reset); + break; + case 1: + feature_set(np, FEATURE_IDE1_reset); + mdelay(10); + feature_set(np, FEATURE_IDE1_enable); + mdelay(10); + feature_clear(np, FEATURE_IDE1_reset); + break; + case 2: + /* This one exists only for KL, I don't know + about any enable bit */ + feature_set(np, FEATURE_IDE2_reset); + mdelay(10); + feature_clear(np, FEATURE_IDE2_reset); + break; + } + mdelay(IDE_WAKEUP_DELAY_MS); /* Reset timings */ pmac_ide_selectproc(&ide_hwifs[i].drives[0]); @@ -940,7 +1011,7 @@ static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when) /* Disable irq during sleep */ disable_irq(pmac_ide[i].irq); ret = check_media_bay_by_base(base, MB_CD); - if (ret == -ENODEV) + if ((ret == -ENODEV) && ide_hwifs[i].drives[0].present) /* not media bay - put the disk to sleep */ idepmac_sleep_disk(i, base); } @@ -953,7 +1024,7 @@ static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when) hwif = &ide_hwifs[i]; /* We don't handle media bay devices this way */ ret = check_media_bay_by_base(base, MB_CD); - if (ret == -ENODEV) + if ((ret == -ENODEV) && ide_hwifs[i].drives[0].present) idepmac_wake_disk(i, base); else if (ret == 0) idepmac_wake_bay(i, base); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 7c5130e77f5..83b14b1fd7d 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -56,6 +56,11 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */ ide__sti(); /* local CPU only */ ide_fix_driveid(id); + + if (id->word156 == 0x4d42) { + printk("%s: drive->id->word156 == 0x%04x \n", drive->name, drive->id->word156); + } + if (!drive->forced_lun) drive->last_lun = id->last_lun & 0x7; #if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA) @@ -764,9 +769,10 @@ static void init_gendisk (ide_hwif_t *hwif) char name[64]; ide_add_generic_settings(hwif->drives + unit); + hwif->drives[unit].dn = ((hwif->channel ? 2 : 0) + unit); sprintf (name, "host%d/bus%d/target%d/lun%d", (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index, - hwif->channel, unit, 0); + hwif->channel, unit, hwif->drives[unit].lun); hwif->drives[unit].de = devfs_mk_dir (ide_devfs_handle, name, 0, NULL); } @@ -775,9 +781,9 @@ static void init_gendisk (ide_hwif_t *hwif) static int hwif_init (ide_hwif_t *hwif) { - ide_drive_t *drive; - void (*rfn)(request_queue_t *); - + request_queue_t *q; + unsigned int unit; + if (!hwif->present) return 0; if (!hwif->irq) { @@ -795,39 +801,7 @@ static int hwif_init (ide_hwif_t *hwif) #endif /* CONFIG_BLK_DEV_HD */ hwif->present = 0; /* we set it back to 1 if all is ok below */ - switch (hwif->major) { - case IDE0_MAJOR: rfn = &do_ide0_request; break; -#if MAX_HWIFS > 1 - case IDE1_MAJOR: rfn = &do_ide1_request; break; -#endif -#if MAX_HWIFS > 2 - case IDE2_MAJOR: rfn = &do_ide2_request; break; -#endif -#if MAX_HWIFS > 3 - case IDE3_MAJOR: rfn = &do_ide3_request; break; -#endif -#if MAX_HWIFS > 4 - case IDE4_MAJOR: rfn = &do_ide4_request; break; -#endif -#if MAX_HWIFS > 5 - case IDE5_MAJOR: rfn = &do_ide5_request; break; -#endif -#if MAX_HWIFS > 6 - case IDE6_MAJOR: rfn = &do_ide6_request; break; -#endif -#if MAX_HWIFS > 7 - case IDE7_MAJOR: rfn = &do_ide7_request; break; -#endif -#if MAX_HWIFS > 8 - case IDE8_MAJOR: rfn = &do_ide8_request; break; -#endif -#if MAX_HWIFS > 9 - case IDE9_MAJOR: rfn = &do_ide9_request; break; -#endif - default: - printk("%s: request_fn NOT DEFINED\n", hwif->name); - return (hwif->present = 0); - } + if (devfs_register_blkdev (hwif->major, hwif->name, ide_fops)) { printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major); return (hwif->present = 0); @@ -860,19 +834,11 @@ static int hwif_init (ide_hwif_t *hwif) read_ahead[hwif->major] = 8; /* (4kB) */ hwif->present = 1; /* success */ - /* - * FIXME(eric) - This needs to be tested. I *think* that this - * is correct. Also, I believe that there is no longer any - * reason to have multiple functions (do_ide[0-7]_request) - * functions - the queuedata field could be used to indicate - * the correct hardware group - either this, or we could add - * a new field to request_queue_t to hold this information. - */ - drive = &hwif->drives[0]; - blk_init_queue(&drive->queue, rfn); - - drive = &hwif->drives[1]; - blk_init_queue(&drive->queue, rfn); + for (unit = 0; unit < MAX_DRIVES; ++unit) { + q = &hwif->drives[unit].queue; + q->queuedata = hwif->hwgroup; + blk_init_queue(q, do_ide_request); + } #if (DEBUG_SPINLOCK > 0) { diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 039c0bfd9fb..cb34d354478 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -321,6 +321,7 @@ int drive_is_flashcard (ide_drive_t *drive) || !strncmp(id->model, "Hitachi CV", 10) /* Hitachi */ || !strncmp(id->model, "SunDisk SDCFB", 13) /* SunDisk */ || !strncmp(id->model, "HAGIWARA HPC", 12) /* Hagiwara */ + || !strncmp(id->model, "LEXAR ATA_FLASH", 15) /* Lexar */ || !strncmp(id->model, "ATA_FLASH", 9)) /* Simple Tech */ { return 1; /* yes, it is a flash memory card */ @@ -767,6 +768,18 @@ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err) args[1] = err; args[2] = IN_BYTE(IDE_NSECTOR_REG); } + } else if (rq->cmd == IDE_DRIVE_TASK) { + byte *args = (byte *) rq->buffer; + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + if (args) { + args[0] = stat; + args[1] = err; + args[2] = IN_BYTE(IDE_NSECTOR_REG); + args[3] = IN_BYTE(IDE_SECTOR_REG); + args[4] = IN_BYTE(IDE_LCYL_REG); + args[5] = IN_BYTE(IDE_HCYL_REG); + args[6] = IN_BYTE(IDE_SELECT_REG); + } } spin_lock_irqsave(&io_request_lock, flags); blkdev_dequeue_request(rq); @@ -876,7 +889,7 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat) if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) return ide_stopped; /* retry only "normal" I/O: */ - if (rq->cmd == IDE_DRIVE_CMD) { + if (rq->cmd == IDE_DRIVE_CMD || rq->cmd == IDE_DRIVE_TASK) { rq->errors = 1; ide_end_drive_cmd(drive, stat, err); return ide_stopped; @@ -1036,7 +1049,20 @@ int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, by static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) { byte *args = rq->buffer; - if (args) { + if (args && rq->cmd == IDE_DRIVE_TASK) { + +#ifdef DEBUG + printk("%s: DRIVE_TASK_CMD data=x%02x cmd=0x%02x fr=0x%02x ns=0x%02x sc=0x%02x lcyl=0x%02x hcyl=0x%02x sel=0x%02x\n", + drive->name, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); +#endif + OUT_BYTE(args[1], IDE_FEATURE_REG); + OUT_BYTE(args[3], IDE_SECTOR_REG); + OUT_BYTE(args[4], IDE_LCYL_REG); + OUT_BYTE(args[5], IDE_HCYL_REG); + OUT_BYTE(args[6], IDE_SELECT_REG); + ide_cmd(drive, args[0], args[2], &drive_cmd_intr); + return ide_started; + } else if (args) { #ifdef DEBUG printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n", drive->name, args[0], args[1], args[2], args[3]); @@ -1116,7 +1142,7 @@ static ide_startstop_t start_request (ide_drive_t *drive) return startstop; } if (!drive->special.all) { - if (rq->cmd == IDE_DRIVE_CMD) { + if (rq->cmd == IDE_DRIVE_CMD || rq->cmd == IDE_DRIVE_TASK) { return execute_drive_cmd(drive, rq); } if (drive->driver != NULL) { @@ -1225,7 +1251,7 @@ repeat: * the driver. This makes the driver much more friendlier to shared IRQs * than previous designs, while remaining 100% (?) SMP safe and capable. */ -static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) +static void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq) { ide_drive_t *drive; ide_hwif_t *hwif; @@ -1313,73 +1339,13 @@ request_queue_t *ide_get_queue (kdev_t dev) return &hwif->drives[DEVICE_NR(dev) & 1].queue; } -void do_ide0_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[0].hwgroup, 0); -} - -#if MAX_HWIFS > 1 -void do_ide1_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[1].hwgroup, 0); -} -#endif /* MAX_HWIFS > 1 */ - -#if MAX_HWIFS > 2 -void do_ide2_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[2].hwgroup, 0); -} -#endif /* MAX_HWIFS > 2 */ - -#if MAX_HWIFS > 3 -void do_ide3_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[3].hwgroup, 0); -} -#endif /* MAX_HWIFS > 3 */ - -#if MAX_HWIFS > 4 -void do_ide4_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[4].hwgroup, 0); -} -#endif /* MAX_HWIFS > 4 */ - -#if MAX_HWIFS > 5 -void do_ide5_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[5].hwgroup, 0); -} -#endif /* MAX_HWIFS > 5 */ - -#if MAX_HWIFS > 6 -void do_ide6_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[6].hwgroup, 0); -} -#endif /* MAX_HWIFS > 6 */ - -#if MAX_HWIFS > 7 -void do_ide7_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[7].hwgroup, 0); -} -#endif /* MAX_HWIFS > 7 */ - -#if MAX_HWIFS > 8 -void do_ide8_request (request_queue_t *q) -{ - ide_do_request (ide_hwifs[8].hwgroup, 0); -} -#endif /* MAX_HWIFS > 8 */ - -#if MAX_HWIFS > 9 -void do_ide9_request (request_queue_t *q) +/* + * Passes the stuff to ide_do_request + */ +void do_ide_request(request_queue_t *q) { - ide_do_request (ide_hwifs[9].hwgroup, 0); + ide_do_request(q->queuedata, 0); } -#endif /* MAX_HWIFS > 9 */ /* * ide_timer_expiry() is our timeout function for all drive operations. @@ -1656,16 +1622,8 @@ ide_drive_t *get_info_ptr (kdev_t i_rdev) */ void ide_init_drive_cmd (struct request *rq) { - rq->buffer = NULL; + memset(rq, 0, sizeof(*rq)); rq->cmd = IDE_DRIVE_CMD; - rq->sector = 0; - rq->nr_sectors = 0; - rq->nr_segments = 0; - rq->current_nr_sectors = 0; - rq->sem = NULL; - rq->bh = NULL; - rq->bhtail = NULL; - rq->q = NULL; } /* @@ -2049,8 +2007,10 @@ void ide_unregister (unsigned int index) hwgroup->hwif = HWIF(hwgroup->drive); #ifdef CONFIG_BLK_DEV_IDEDMA - if (hwif->dma_base) + if (hwif->dma_base) { (void) ide_release_dma(hwif); + hwif->dma_base = 0; + } #endif /* CONFIG_BLK_DEV_IDEDMA */ /* @@ -2083,6 +2043,7 @@ void ide_unregister (unsigned int index) init_hwif_data (index); /* restore hwif data to pristine status */ hwif->hwgroup = old_hwif.hwgroup; hwif->tuneproc = old_hwif.tuneproc; + hwif->speedproc = old_hwif.speedproc; hwif->selectproc = old_hwif.selectproc; hwif->resetproc = old_hwif.resetproc; hwif->dmaproc = old_hwif.dmaproc; @@ -2103,7 +2064,7 @@ void ide_unregister (unsigned int index) hwif->pci_devid = old_hwif.pci_devid; #endif /* CONFIG_BLK_DEV_IDEPCI */ hwif->straight8 = old_hwif.straight8; - + hwif->hwif_data = old_hwif.hwif_data; abort: restore_flags(flags); /* all CPUs */ } @@ -2304,24 +2265,24 @@ int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting) return val; } -int ide_spin_wait_hwgroup (ide_drive_t *drive, unsigned long *flags) +int ide_spin_wait_hwgroup (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); unsigned long timeout = jiffies + (3 * HZ); - spin_lock_irqsave(&io_request_lock, *flags); + spin_lock_irq(&io_request_lock); while (hwgroup->busy) { - unsigned long lflags; - spin_unlock_irqrestore(&io_request_lock, *flags); - __save_flags(lflags); /* local CPU only */ + unsigned long flags; + spin_unlock_irq(&io_request_lock); + __save_flags(flags); /* local CPU only */ __sti(); /* local CPU only; needed for jiffies */ if (0 < (signed long)(jiffies - timeout)) { - __restore_flags(lflags); /* local CPU only */ + __restore_flags(flags); printk("%s: channel busy\n", drive->name); return -EBUSY; } - __restore_flags(lflags); /* local CPU only */ - spin_lock_irqsave(&io_request_lock, *flags); + __restore_flags(flags); /* local CPU only */ + spin_lock_irq(&io_request_lock); } return 0; } @@ -2333,7 +2294,6 @@ int ide_spin_wait_hwgroup (ide_drive_t *drive, unsigned long *flags) */ int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val) { - unsigned long flags; int i; u32 *p; @@ -2345,7 +2305,7 @@ int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val) return -EINVAL; if (setting->set) return setting->set(drive, val); - if (ide_spin_wait_hwgroup(drive, &flags)) + if (ide_spin_wait_hwgroup(drive)) return -EBUSY; switch (setting->data_type) { case TYPE_BYTE: @@ -2363,7 +2323,7 @@ int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val) *p = val; break; } - spin_unlock_irqrestore(&io_request_lock, flags); + spin_unlock_irq(&io_request_lock); return 0; } @@ -2416,6 +2376,9 @@ void ide_add_generic_settings (ide_drive_t *drive) ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL); ide_add_setting(drive, "using_dma", SETTING_RW, HDIO_GET_DMA, HDIO_SET_DMA, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma); ide_add_setting(drive, "ide_scsi", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->scsi, NULL); + ide_add_setting(drive, "init_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 69, 1, 1, &drive->init_speed, NULL); + ide_add_setting(drive, "current_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 69, 1, 1, &drive->current_speed, NULL); + ide_add_setting(drive, "number", SETTING_RW, -1, -1, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL); } int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf) @@ -2435,6 +2398,16 @@ int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int secto return ide_do_drive_cmd(drive, &rq, ide_wait); } +int ide_wait_cmd_task (ide_drive_t *drive, byte *buf) +{ + struct request rq; + + ide_init_drive_cmd(&rq); + rq.cmd = IDE_DRIVE_TASK; + rq.buffer = buf; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + /* * Delay for *at least* 50ms. As we don't know how much time is left * until the next tick occurs, we wait an extra tick to be safe. @@ -2551,6 +2524,7 @@ static int ide_ioctl (struct inode *inode, struct file *file, case HDIO_DRIVE_CMD: { byte args[4], *argbuf = args; + byte xfer_rate = 0; int argsize = 4; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (NULL == (void *) arg) @@ -2564,18 +2538,22 @@ static int ide_ioctl (struct inode *inode, struct file *file, return -ENOMEM; memcpy(argbuf, args, 4); } - if (ide_ata66_check(drive, args[0], args[1], args[2])) - goto abort; + + if (set_transfer(drive, args[0], args[1], args[2])) { + xfer_rate = args[1]; + if (ide_ata66_check(drive, args[0], args[1], args[2])) + goto abort; + } err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf); - if (!err && set_transfer(drive, args[0], args[1], args[2])) { -#if 0 + if (!err && xfer_rate) { /* active-retuning-calls future */ - if (HWIF(drive)->tune2proc) - HWIF(drive)->tune2proc(drive, args[1]); -#endif + if ((HWIF(drive)->speedproc) != NULL) + HWIF(drive)->speedproc(drive, xfer_rate); ide_driveid_update(drive); + } else { + printk("%s: \n", drive->name); } abort: if (copy_to_user((void *)arg, argbuf, argsize)) @@ -2584,6 +2562,18 @@ static int ide_ioctl (struct inode *inode, struct file *file, kfree(argbuf); return err; } + case HDIO_DRIVE_TASK: + { + byte args[7], *argbuf = args; + int argsize = 7; + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (copy_from_user(args, (void *)arg, 7)) + return -EFAULT; + err = ide_wait_cmd_task(drive, argbuf); + if (copy_to_user((void *)arg, argbuf, argsize)) + err = -EFAULT; + return err; + } case HDIO_SCAN_HWIF: { @@ -3512,37 +3502,9 @@ EXPORT_SYMBOL(ide_timer_expiry); EXPORT_SYMBOL(ide_intr); EXPORT_SYMBOL(ide_fops); EXPORT_SYMBOL(ide_get_queue); -EXPORT_SYMBOL(do_ide0_request); EXPORT_SYMBOL(ide_add_generic_settings); EXPORT_SYMBOL(ide_devfs_handle); -#if MAX_HWIFS > 1 -EXPORT_SYMBOL(do_ide1_request); -#endif /* MAX_HWIFS > 1 */ -#if MAX_HWIFS > 2 -EXPORT_SYMBOL(do_ide2_request); -#endif /* MAX_HWIFS > 2 */ -#if MAX_HWIFS > 3 -EXPORT_SYMBOL(do_ide3_request); -#endif /* MAX_HWIFS > 3 */ -#if MAX_HWIFS > 4 -EXPORT_SYMBOL(do_ide4_request); -#endif /* MAX_HWIFS > 4 */ -#if MAX_HWIFS > 5 -EXPORT_SYMBOL(do_ide5_request); -#endif /* MAX_HWIFS > 5 */ -#if MAX_HWIFS > 6 -EXPORT_SYMBOL(do_ide6_request); -#endif /* MAX_HWIFS > 6 */ -#if MAX_HWIFS > 7 -EXPORT_SYMBOL(do_ide7_request); -#endif /* MAX_HWIFS > 7 */ -#if MAX_HWIFS > 8 -EXPORT_SYMBOL(do_ide8_request); -#endif /* MAX_HWIFS > 8 */ -#if MAX_HWIFS > 9 -EXPORT_SYMBOL(do_ide9_request); -#endif /* MAX_HWIFS > 9 */ - +EXPORT_SYMBOL(do_ide_request); /* * Driver module */ @@ -3567,6 +3529,7 @@ EXPORT_SYMBOL(ide_end_request); EXPORT_SYMBOL(ide_revalidate_disk); EXPORT_SYMBOL(ide_cmd); EXPORT_SYMBOL(ide_wait_cmd); +EXPORT_SYMBOL(ide_wait_cmd_task); EXPORT_SYMBOL(ide_delay_50ms); EXPORT_SYMBOL(ide_stall_queue); #ifdef CONFIG_PROC_FS @@ -3652,8 +3615,13 @@ void cleanup_module (void) { int index; - for (index = 0; index < MAX_HWIFS; ++index) + for (index = 0; index < MAX_HWIFS; ++index) { ide_unregister(index); +#ifdef CONFIG_BLK_DEV_IDEDMA + if (ide_hwifs[index].dma_base) + (void) ide_release_dma(&ide_hwifs[index]); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + } #ifdef CONFIG_PROC_FS proc_ide_destroy(); diff --git a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c index b93c6ad1b67..f16d3392143 100644 --- a/drivers/ide/pdc202xx.c +++ b/drivers/ide/pdc202xx.c @@ -262,59 +262,99 @@ static void decode_registers (byte registers, byte value) #endif /* PDC202XX_DECODE_REGISTER_INFO */ -/* 0 1 2 3 4 5 6 7 8 - * 960, 480, 390, 300, 240, 180, 120, 90, 60 - * 180, 150, 120, 90, 60 - * DMA_Speed - * 180, 120, 90, 90, 90, 60, 30 - * 11, 5, 4, 3, 2, 1, 0 - */ -static int config_chipset_for_pio (ide_drive_t *drive, byte pio) +static int pdc202xx_tune_chipset (ide_drive_t *drive, byte speed) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; - byte drive_pci, speed; - byte AP, BP, TA, TB; - int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); - int err; + unsigned int drive_conf; + int err; + byte drive_pci, AP, BP, CP, DP; + byte TA = 0, TB = 0, TC = 0; - switch (drive_number) { + switch (drive->dn) { case 0: drive_pci = 0x60; break; case 1: drive_pci = 0x64; break; case 2: drive_pci = 0x68; break; case 3: drive_pci = 0x6c; break; - default: return 1; + default: return -1; } + if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) return -1; + + pci_read_config_dword(dev, drive_pci, &drive_conf); pci_read_config_byte(dev, (drive_pci), &AP); pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + pci_read_config_byte(dev, (drive_pci)|0x03, &DP); +#ifdef CONFIG_BLK_DEV_IDEDMA + if (speed >= XFER_SW_DMA_0) { + if ((BP & 0xF0) && (CP & 0x0F)) { + /* clear DMA modes of upper 842 bits of B Register */ + /* clear PIO forced mode upper 1 bit of B Register */ + pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0xF0); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + + /* clear DMA modes of lower 8421 bits of C Register */ + pci_write_config_byte(dev, (drive_pci)|0x02, CP & ~0x0F); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + } + } else { +#else + { +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if ((AP & 0x0F) || (BP & 0x07)) { + /* clear PIO modes of lower 8421 bits of A Register */ + pci_write_config_byte(dev, (drive_pci), AP & ~0x0F); + pci_read_config_byte(dev, (drive_pci), &AP); - if ((AP & 0x0F) || (BP & 0x07)) { - /* clear PIO modes of lower 8421 bits of A Register */ - pci_write_config_byte(dev, (drive_pci), AP & ~0x0F); - pci_read_config_byte(dev, (drive_pci), &AP); + /* clear PIO modes of lower 421 bits of B Register */ + pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); - /* clear PIO modes of lower 421 bits of B Register */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07); - pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + } + } - pci_read_config_byte(dev, (drive_pci), &AP); - pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_4: TB = 0x20; TC = 0x01; break; /* speed 8 == UDMA mode 4 */ + case XFER_UDMA_3: TB = 0x40; TC = 0x02; break; /* speed 7 == UDMA mode 3 */ + case XFER_UDMA_2: TB = 0x20; TC = 0x01; break; /* speed 6 == UDMA mode 2 */ + case XFER_UDMA_1: TB = 0x40; TC = 0x02; break; /* speed 5 == UDMA mode 1 */ + case XFER_UDMA_0: TB = 0x60; TC = 0x03; break; /* speed 4 == UDMA mode 0 */ + case XFER_MW_DMA_2: TB = 0x60; TC = 0x03; break; /* speed 4 == MDMA mode 2 */ + case XFER_MW_DMA_1: TB = 0x60; TC = 0x04; break; /* speed 3 == MDMA mode 1 */ + case XFER_MW_DMA_0: TB = 0x60; TC = 0x05; break; /* speed 2 == MDMA mode 0 */ + case XFER_SW_DMA_2: TB = 0x60; TC = 0x05; break; /* speed 0 == SDMA mode 2 */ + case XFER_SW_DMA_1: TB = 0x80; TC = 0x06; break; /* speed 1 == SDMA mode 1 */ + case XFER_SW_DMA_0: TB = 0xC0; TC = 0x0B; break; /* speed 0 == SDMA mode 0 */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: TA = 0x01; TB = 0x04; break; + case XFER_PIO_3: TA = 0x02; TB = 0x06; break; + case XFER_PIO_2: TA = 0x03; TB = 0x08; break; + case XFER_PIO_1: TA = 0x05; TB = 0x0C; break; + case XFER_PIO_0: + default: TA = 0x09; TB = 0x13; break; } - pio = (pio == 5) ? 4 : pio; - switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) { - case 4:speed = XFER_PIO_4; TA=0x01; TB=0x04; break; - case 3:speed = XFER_PIO_3; TA=0x02; TB=0x06; break; - case 2:speed = XFER_PIO_2; TA=0x03; TB=0x08; break; - case 1:speed = XFER_PIO_1; TA=0x05; TB=0x0C; break; - case 0: - default:speed = XFER_PIO_0; TA=0x09; TB=0x13; break; +#ifdef CONFIG_BLK_DEV_IDEDMA + if (speed >= XFER_SW_DMA_0) { + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); + pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC); + } else { +#else + { +#endif /* CONFIG_BLK_DEV_IDEDMA */ + pci_write_config_byte(dev, (drive_pci), AP|TA); + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); } - pci_write_config_byte(dev, (drive_pci), AP|TA); - pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); #if PDC202XX_DECODE_REGISTER_INFO pci_read_config_byte(dev, (drive_pci), &AP); @@ -333,13 +373,30 @@ static int config_chipset_for_pio (ide_drive_t *drive, byte pio) #if PDC202XX_DEBUG_DRIVE_INFO printk("%s: %s drive%d 0x%08x ", drive->name, ide_xfer_verbose(speed), - drive_number, drive_conf); + drive->dn, drive_conf); pci_read_config_dword(dev, drive_pci, &drive_conf); printk("0x%08x\n", drive_conf); #endif /* PDC202XX_DEBUG_DRIVE_INFO */ return err; } +/* 0 1 2 3 4 5 6 7 8 + * 960, 480, 390, 300, 240, 180, 120, 90, 60 + * 180, 150, 120, 90, 60 + * DMA_Speed + * 180, 120, 90, 90, 90, 60, 30 + * 11, 5, 4, 3, 2, 1, 0 + */ +static int config_chipset_for_pio (ide_drive_t *drive, byte pio) +{ + byte speed = 0x00; + + pio = (pio == 5) ? 4 : pio; + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, pio, NULL); + + return ((int) pdc202xx_tune_chipset(drive, speed)); +} + static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio) { (void) config_chipset_for_pio(drive, pio); @@ -352,15 +409,15 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; unsigned long high_16 = dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK; + unsigned long dma_base = hwif->dma_base; + byte unit = (drive->select.b.unit & 0x01); - int err; unsigned int drive_conf; byte drive_pci; byte test1, test2, speed = -1; - byte AP, BP, CP, DP, TB, TC; + byte AP; unsigned short EP; byte CLKSPD = IN_BYTE(high_16 + 0x11); - int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0; byte udma_33 = ultra ? (inb(high_16 + 0x001f) & 1) : 0; @@ -397,9 +454,9 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) * check to make sure drive on same channel * is u66 capable */ - if (hwif->drives[!(drive_number%2)].id) { - if ((hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0010) || - (hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0008)) { + if (hwif->drives[!(drive->dn%2)].id) { + if ((hwif->drives[!(drive->dn%2)].id->dma_ultra & 0x0010) || + (hwif->drives[!(drive->dn%2)].id->dma_ultra & 0x0008)) { OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); } else { OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); @@ -410,7 +467,7 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) } } - switch(drive_number) { + switch(drive->dn) { case 0: drive_pci = 0x60; pci_read_config_dword(dev, drive_pci, &drive_conf); if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) @@ -451,101 +508,34 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) chipset_is_set: - if (drive->media != ide_disk) - return ide_dma_off_quietly; + if (drive->media != ide_disk) return ide_dma_off_quietly; pci_read_config_byte(dev, (drive_pci), &AP); - pci_read_config_byte(dev, (drive_pci)|0x01, &BP); - pci_read_config_byte(dev, (drive_pci)|0x02, &CP); - pci_read_config_byte(dev, (drive_pci)|0x03, &DP); - - if (id->capability & 4) { /* IORDY_EN */ + if (id->capability & 4) /* IORDY_EN */ pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN); - pci_read_config_byte(dev, (drive_pci), &AP); - } - - if (drive->media == ide_disk) { /* PREFETCH_EN */ - pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN); - pci_read_config_byte(dev, (drive_pci), &AP); - } - - if ((BP & 0xF0) && (CP & 0x0F)) { - /* clear DMA modes of upper 842 bits of B Register */ - /* clear PIO forced mode upper 1 bit of B Register */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0xF0); - pci_read_config_byte(dev, (drive_pci)|0x01, &BP); - - /* clear DMA modes of lower 8421 bits of C Register */ - pci_write_config_byte(dev, (drive_pci)|0x02, CP & ~0x0F); - pci_read_config_byte(dev, (drive_pci)|0x02, &CP); - } - pci_read_config_byte(dev, (drive_pci), &AP); - pci_read_config_byte(dev, (drive_pci)|0x01, &BP); - pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + if (drive->media == ide_disk) /* PREFETCH_EN */ + pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN); - if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) { - /* speed 8 == UDMA mode 4 == speed 6 plus cable */ - speed = XFER_UDMA_4; TB = 0x20; TC = 0x01; - } else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) { - /* speed 7 == UDMA mode 3 == speed 5 plus cable */ - speed = XFER_UDMA_3; TB = 0x40; TC = 0x02; - } else if ((id->dma_ultra & 0x0004) && (udma_33)) { - /* speed 6 == UDMA mode 2 */ - speed = XFER_UDMA_2; TB = 0x20; TC = 0x01; - } else if ((id->dma_ultra & 0x0002) && (udma_33)) { - /* speed 5 == UDMA mode 1 */ - speed = XFER_UDMA_1; TB = 0x40; TC = 0x02; - } else if ((id->dma_ultra & 0x0001) && (udma_33)) { - /* speed 4 == UDMA mode 0 */ - speed = XFER_UDMA_0; TB = 0x60; TC = 0x03; - } else if (id->dma_mword & 0x0004) { - /* speed 4 == DMA mode 2 multi-word */ - speed = XFER_MW_DMA_2; TB = 0x60; TC = 0x03; - } else if (id->dma_mword & 0x0002) { - /* speed 3 == DMA mode 1 multi-word */ - speed = XFER_MW_DMA_1; TB = 0x60; TC = 0x04; - } else if (id->dma_mword & 0x0001) { - /* speed 2 == DMA mode 0 multi-word */ - speed = XFER_MW_DMA_0; TB = 0x60; TC = 0x05; - } else if (id->dma_1word & 0x0004) { - /* speed 2 == DMA mode 2 single-word */ - speed = XFER_SW_DMA_2; TB = 0x60; TC = 0x05; - } else if (id->dma_1word & 0x0002) { - /* speed 1 == DMA mode 1 single-word */ - speed = XFER_SW_DMA_1; TB = 0x80; TC = 0x06; - } else if (id->dma_1word & 0x0001) { - /* speed 0 == DMA mode 0 single-word */ - speed = XFER_SW_DMA_0; TB = 0xC0; TC = 0x0B; - } else { + if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) speed = XFER_UDMA_4; + else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) speed = XFER_UDMA_3; + else if ((id->dma_ultra & 0x0004) && (udma_33)) speed = XFER_UDMA_2; + else if ((id->dma_ultra & 0x0002) && (udma_33)) speed = XFER_UDMA_1; + else if ((id->dma_ultra & 0x0001) && (udma_33)) speed = XFER_UDMA_0; + else if (id->dma_mword & 0x0004) speed = XFER_MW_DMA_2; + else if (id->dma_mword & 0x0002) speed = XFER_MW_DMA_1; + else if (id->dma_mword & 0x0001) speed = XFER_MW_DMA_0; + else if (id->dma_1word & 0x0004) speed = XFER_SW_DMA_2; + else if (id->dma_1word & 0x0002) speed = XFER_SW_DMA_1; + else if (id->dma_1word & 0x0001) speed = XFER_SW_DMA_0; + else { /* restore original pci-config space */ pci_write_config_dword(dev, drive_pci, drive_conf); return ide_dma_off_quietly; } - pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC); - -#if PDC202XX_DECODE_REGISTER_INFO - pci_read_config_byte(dev, (drive_pci), &AP); - pci_read_config_byte(dev, (drive_pci)|0x01, &BP); - pci_read_config_byte(dev, (drive_pci)|0x02, &CP); - - decode_registers(REG_A, AP); - decode_registers(REG_B, BP); - decode_registers(REG_C, CP); - decode_registers(REG_D, DP); -#endif /* PDC202XX_DECODE_REGISTER_INFO */ - - err = ide_config_drive_speed(drive, speed); - -#if PDC202XX_DEBUG_DRIVE_INFO - printk("%s: %s drive%d 0x%08x ", - drive->name, ide_xfer_verbose(speed), - drive_number, drive_conf); - pci_read_config_dword(dev, drive_pci, &drive_conf); - printk("0x%08x\n", drive_conf); -#endif /* PDC202XX_DEBUG_DRIVE_INFO */ + outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2); + (void) pdc202xx_tune_chipset(drive, speed); return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : @@ -716,6 +706,7 @@ unsigned int __init ata66_pdc202xx (ide_hwif_t *hwif) void __init ide_init_pdc202xx (ide_hwif_t *hwif) { hwif->tuneproc = &pdc202xx_tune_drive; + hwif->speedproc = &pdc202xx_tune_chipset; #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_base) { diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c index fafd0533fee..90af168c351 100644 --- a/drivers/ide/piix.c +++ b/drivers/ide/piix.c @@ -112,6 +112,7 @@ static int piix_get_info (char *buffer, char **addr, off_t offset, int count) p += sprintf(p, "\n Intel PIIX4 Ultra 66 Chipset.\n"); break; case PCI_DEVICE_ID_INTEL_82801AB_1: + case PCI_DEVICE_ID_INTEL_82443MX_1: case PCI_DEVICE_ID_INTEL_82371AB: p += sprintf(p, "\n Intel PIIX4 Ultra 33 Chipset.\n"); break; @@ -258,28 +259,18 @@ static void piix_tune_drive (ide_drive_t *drive, byte pio) } #if defined(CONFIG_BLK_DEV_IDEDMA) && defined(CONFIG_PIIX_TUNING) -static int piix_config_drive_for_dma (ide_drive_t *drive) +static int piix_tune_chipset (ide_drive_t *drive, byte speed) { - struct hd_driveid *id = drive->id; ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; - - int sitre; - short reg4042, reg44, reg48, reg4a, reg54; - byte speed; - byte maslave = hwif->channel ? 0x42 : 0x40; - byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0; - int ultra66 = ((dev->device == PCI_DEVICE_ID_INTEL_82801AA_1) || - (dev->device == PCI_DEVICE_ID_INTEL_82372FB_1)) ? 1 : 0; - int ultra = ((ultra66) || - (dev->device == PCI_DEVICE_ID_INTEL_82371AB) || - (dev->device == PCI_DEVICE_ID_INTEL_82801AB_1)) ? 1 : 0; - int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); - int a_speed = 2 << (drive_number * 4); - int u_flag = 1 << drive_number; - int v_flag = 0x10 << drive_number; + int a_speed = 2 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int v_flag = 0x10 << drive->dn; int u_speed = 0; + int err = 0; + int sitre; + short reg4042, reg44, reg48, reg4a, reg54; pci_read_config_word(dev, maslave, ®4042); sitre = (reg4042 & 0x4000) ? 1 : 0; @@ -288,29 +279,16 @@ static int piix_config_drive_for_dma (ide_drive_t *drive) pci_read_config_word(dev, 0x4a, ®4a); pci_read_config_word(dev, 0x54, ®54); - if ((id->dma_ultra & 0x0010) && (ultra)) { - u_speed = 2 << (drive_number * 4); - speed = ((udma_66) && (ultra66)) ? XFER_UDMA_4 : XFER_UDMA_2; - } else if ((id->dma_ultra & 0x0008) && (ultra)) { - u_speed = 1 << (drive_number * 4); - speed = ((udma_66) && (ultra66)) ? XFER_UDMA_3 : XFER_UDMA_1; - } else if ((id->dma_ultra & 0x0004) && (ultra)) { - u_speed = 2 << (drive_number * 4); - speed = XFER_UDMA_2; - } else if ((id->dma_ultra & 0x0002) && (ultra)) { - u_speed = 1 << (drive_number * 4); - speed = XFER_UDMA_1; - } else if ((id->dma_ultra & 0x0001) && (ultra)) { - u_speed = 0 << (drive_number * 4); - speed = XFER_UDMA_0; - } else if (id->dma_mword & 0x0004) { - speed = XFER_MW_DMA_2; - } else if (id->dma_mword & 0x0002) { - speed = XFER_MW_DMA_1; - } else if (id->dma_1word & 0x0004) { - speed = XFER_SW_DMA_2; - } else { - speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + switch(speed) { + case XFER_UDMA_4: + case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_3: + case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_SW_DMA_2: break; + default: return -1; } if (speed >= XFER_UDMA_0) { @@ -328,7 +306,6 @@ static int piix_config_drive_for_dma (ide_drive_t *drive) pci_write_config_word(dev, 0x54, reg54 & ~v_flag); } } - if (speed < XFER_UDMA_0) { if (reg48 & u_flag) pci_write_config_word(dev, 0x48, reg48 & ~u_flag); @@ -340,11 +317,52 @@ static int piix_config_drive_for_dma (ide_drive_t *drive) piix_tune_drive(drive, piix_dma_2_pio(speed)); - (void) ide_config_drive_speed(drive, speed); - #if PIIX_DEBUG_DRIVE_INFO - printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number); + printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive->dn); #endif /* PIIX_DEBUG_DRIVE_INFO */ + if (!drive->init_speed) + drive->init_speed = speed; + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; + return err; +} + +static int piix_config_drive_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte speed; + + byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0; + int ultra66 = ((dev->device == PCI_DEVICE_ID_INTEL_82801AA_1) || + (dev->device == PCI_DEVICE_ID_INTEL_82372FB_1)) ? 1 : 0; + int ultra = ((ultra66) || + (dev->device == PCI_DEVICE_ID_INTEL_82371AB) || + (dev->device == PCI_DEVICE_ID_INTEL_82443MX_1) || + (dev->device == PCI_DEVICE_ID_INTEL_82801AB_1)) ? 1 : 0; + + if ((id->dma_ultra & 0x0010) && (ultra)) { + speed = ((udma_66) && (ultra66)) ? XFER_UDMA_4 : XFER_UDMA_2; + } else if ((id->dma_ultra & 0x0008) && (ultra)) { + speed = ((udma_66) && (ultra66)) ? XFER_UDMA_3 : XFER_UDMA_1; + } else if ((id->dma_ultra & 0x0004) && (ultra)) { + speed = XFER_UDMA_2; + } else if ((id->dma_ultra & 0x0002) && (ultra)) { + speed = XFER_UDMA_1; + } else if ((id->dma_ultra & 0x0001) && (ultra)) { + speed = XFER_UDMA_0; + } else if (id->dma_mword & 0x0004) { + speed = XFER_MW_DMA_2; + } else if (id->dma_mword & 0x0002) { + speed = XFER_MW_DMA_1; + } else if (id->dma_1word & 0x0004) { + speed = XFER_SW_DMA_2; + } else { + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + } + + (void) piix_tune_chipset(drive, speed); return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : @@ -413,6 +431,7 @@ void __init ide_init_piix (ide_hwif_t *hwif) #ifdef CONFIG_PIIX_TUNING hwif->autodma = 1; hwif->dmaproc = &piix_dmaproc; + hwif->speedproc = &piix_tune_chipset; #endif /* CONFIG_PIIX_TUNING */ #endif /* !CONFIG_BLK_DEV_IDEDMA */ } diff --git a/drivers/ide/qd6580.c b/drivers/ide/qd6580.c index ad56c295cee..4c00de5042f 100644 --- a/drivers/ide/qd6580.c +++ b/drivers/ide/qd6580.c @@ -1,12 +1,21 @@ /* - * linux/drivers/ide/qd6580.c Version 0.02 Feb 09, 1996 + * linux/drivers/ide/qd6580.c Version 0.03 May 13, 2000 * - * Copyright (C) 1996 Linus Torvalds & author (see below) + * Copyright (C) 1996-2000 Linus Torvalds & author (see below) */ /* - * QDI QD6580 EIDE controller fast support by Colten Edwards. - * No net access, but (maybe) can be reached at pje120@cs.usask.ca + * Version 0.03 Cleaned auto-tune, added probe + * + * QDI QD6580 EIDE controller fast support + * + * To activate controller support use kernel parameter "ide0=qd6580" + * To enable tuning use kernel parameter "ide0=autotune" + */ + +/* + * Rewritten from the work of Colten Edwards by + * Samuel Thibault */ #undef REALLY_SLOW_IO /* most systems can safely undef this */ @@ -27,42 +36,237 @@ #include "ide_modes.h" /* - * Register 0xb3 looks like: - * 0x4f is fast mode3 ? - * 0x3f is medium mode2 ? - * 0x2f is slower mode1 ? - * 0x1f is slower yet mode0 ? - * 0x0f ??? ??? + * I/O ports are 0xb0 0xb1 0xb2 and 0xb3 + * or 0x30 0x31 0x32 and 0x33 + * -- this is a dual IDE interface with I/O chips + * + * More research on qd6580 being done by willmore@cig.mot.com (David) + */ + +/* + * 0xb0: Timer1 * - * Don't know whether this sets BOTH drives, or just the first drive. - * Don't know if there is a separate setting for the second drive. + * + * 0xb1: Status * - * Feel free to patch this if you have one of these beasts - * and can work out the answers! + * && 0xf0 is either 0b1010000 or 0b01010000, or else it isn't a qd6580 + * bit 3 & 2: unknown (useless ?) I have 0 & 1, respectively + * bit 1: 1 if qd6580 baseport is 0xb0 + * 0 if qd6580 baseport is 0x30 + * bit 0: 1 if ide baseport is 0x1f0 + * 0 if ide baseport is 0x170 + * (? Strange: the Dos driver uses it, and then forces baseport to 0x1f0 ?) + * + * + * 0xb2: Timer2 * - * I/O ports are 0xb0 0xb2 and 0xb3 + * + * 0xb3: Control * - * More research on qd6580 being done by willmore@cig.mot.com (David) - * -- this is apparently a *dual* IDE interface + * bits 0-3 are always set 1 + * bit 6 : if 1, must be set 1 + * bit 1 : if 1, bit 7 must be set 1 + * bit 0 : if 1, drives are independant, we can have two different timers for + * the two drives. + * if 0, we have to take the slowest drive into account, + * but we may tune the second hwif ? */ -static void tune_qd6580 (ide_drive_t *drive, byte pio) +typedef struct ide_hd_timings_s { + int active_time; /* Active pulse (ns) minimum */ + int recovery_time; /* Recovery pulse (ns) minimum */ +} ide_hd_timings_t; + +static int basePort; /* base port address (0x30 or 0xb0) */ +static byte status; /* status register of qd6580 */ +static byte control; /* control register of qd6580 */ + +/* truncates a in [b,c] */ +#define IDE_IN(a,b,c) ( ((a)<(b)) ? (b) : ( (a)>(c) ? (c) : (a)) ) + +static int bus_clock; /* Vesa local bus clock (ns) */ +static int tuned=0; /* to remember whether we've already been tuned */ + +/* + * tune_drive + * + * Finds timings for the specified drive, returns it in struc t + */ + +static void tune_drive ( ide_drive_t *drive, byte pio, ide_hd_timings_t *t) +{ + ide_pio_data_t d; + + t->active_time = 0xaf; + t->recovery_time = 0x19f; /* worst cases values from the dos driver */ + + if (drive->present == 0) { /* not present : free to give any timing */ + t->active_time = 0x0; + t->recovery_time = 0x0; + return; + } + + pio = ide_get_best_pio_mode(drive, pio, 4, &d); + + if (pio) { + + switch (pio) { + case 0: break; + case 3: t->active_time = 0x56; + t->recovery_time = d.cycle_time-0x66; + break; + case 4: t->active_time = 0x46; + t->recovery_time = d.cycle_time-0x3d; + break; + default: if (d.cycle_time >= 0xb4) { + t->active_time = 0x6e; + t->recovery_time = d.cycle_time - 0x78; + } else { + t->active_time = ide_pio_timings[pio].active_time; + t->recovery_time = d.cycle_time + -t->active_time + -ide_pio_timings[pio].setup_time; + } + } + } + printk("%s: PIO mode%d, tim1=%dns tim2=%dns\n", drive->name, pio, t->active_time, t->recovery_time); +} + +/* + * tune_ide + * + * Tunes the whole ide, ie tunes each drives, and takes the worst timings + * to tune qd6580 + */ + +static void tune_ide ( ide_hwif_t *hwif, byte pio ) { unsigned long flags; + ide_hd_timings_t t[2]={{0,0},{0,0}}; + + byte active_cycle; + byte recovery_cycle; + byte parameter; + int bus_speed = ide_system_bus_speed (); + + bus_clock = 1000 / bus_speed; + + save_flags(flags); /* all CPUs */ + cli(); /* all CPUs */ + outb( (bus_clock<30) ? 0x0 : 0x0a, basePort + 0x02); + outb( 0x40 | ((control & 0x02) ? 0x9f:0x1f), basePort+0x03); + restore_flags(flags); - pio = ide_get_best_pio_mode(drive, pio, 3, NULL); + tune_drive (&hwif->drives[0], pio, &t[0]); + tune_drive (&hwif->drives[1], pio, &t[1]); - save_flags(flags); /* all CPUs */ - cli(); /* all CPUs */ - outb_p(0x8d,0xb0); - outb_p(0x0 ,0xb2); - outb_p(((pio+1)<<4)|0x0f,0xb3); + t[0].active_time = IDE_MAX(t[0].active_time, t[1].active_time); + t[0].recovery_time = IDE_MAX(t[0].recovery_time,t[1].recovery_time); + + active_cycle = 17-IDE_IN(t[0].active_time / bus_clock + 1, 2, 17); + recovery_cycle = 15-IDE_IN(t[0].recovery_time / bus_clock + 1, 2, 15); + + parameter=active_cycle | (recovery_cycle<<4); + + printk("%s: tim1=%dns tim2=%dns => %#x\n", hwif->name, t[0].active_time, t[0].recovery_time, parameter); + + save_flags(flags); /* all CPUs */ + cli(); /* all CPUs */ + outb_p(parameter,0xb0); inb(0x3f6); restore_flags(flags); /* all CPUs */ + +} + +/* + * tune_qd6580 + * + * tunes the hwif if not tuned + */ + +static void tune_qd6580 (ide_drive_t *drive, byte pio) +{ + if (! tuned) { + tune_ide(HWIF(drive), pio); + tuned = 1; + } +} + +/* + * testreg + * + * tests if the given port is a register + */ + +static int __init testreg(int port) +{ + byte savereg; + byte readreg; + unsigned long flags; + + save_flags(flags); /* all CPUs */ + cli(); /* all CPUs */ + savereg = inb(port); + outb_p(0x15,port); /* safe value */ + readreg = inb_p(port); + outb(savereg,port); + restore_flags(flags); /* all CPUs */ + + if (savereg == 0x15) { + printk("Outch ! the probe for qd6580 isn't reliable !\n"); + printk("Please contact samuel.thibault@fnac.net to tell about your hardware\n"); + printk("Assuming qd6580 is present"); + } + + return (readreg == 0x15); } +/* + * trybase: + * + * tries to find a qd6580 at the given base and save it if found + */ + +static int __init trybase (int base) +{ + unsigned long flags; + + save_flags(flags); /* all CPUs */ + cli(); /* all CPUs */ + status = inb(base+0x01); + control = inb(base+0x03); + restore_flags(flags); /* all CPUs */ + + if (((status & 0xf0) != 0x50) && ((status & 0xf0) != 0xa0)) return(0); + if (! ( ((status & 0x02) == 0x0) == (base == 0x30) ) ) return (0); + + /* Seems to be OK, let's use it */ + + basePort = base; + return(testreg(base)); +} + +/* + * probe: + * + * probes qd6580 at 0xb0 (the default) or 0x30 + */ + +static int __init probe (void) +{ + return (trybase(0xb0) ? 1 : trybase(0x30)); +} + + void __init init_qd6580 (void) { + if (! probe()) { + printk("qd6580: not found\n"); + return; + } + + printk("qd6580: base=%#x, status=%#x, control=%#x\n", basePort, status, control); + ide_hwifs[0].chipset = ide_qd6580; ide_hwifs[1].chipset = ide_qd6580; ide_hwifs[0].tuneproc = &tune_qd6580; diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c index 581992c63d2..be56a7f2489 100644 --- a/drivers/ide/sis5513.c +++ b/drivers/ide/sis5513.c @@ -227,9 +227,8 @@ static void config_drive_art_rwp (ide_drive_t *drive) ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; - int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); byte reg4bh = 0; - byte rw_prefetch = (0x11 << drive_number); + byte rw_prefetch = (0x11 << drive->dn); pci_read_config_byte(dev, 0x4b, ®4bh); if (drive->media != ide_disk) @@ -248,12 +247,8 @@ static void config_art_rwp_pio (ide_drive_t *drive, byte pio) unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90}; unsigned short xfer_pio = drive->id->eide_pio_modes; - int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); -#if 0 config_drive_art_rwp(drive); -#endif - pio = ide_get_best_pio_mode(drive, 255, pio, NULL); if (xfer_pio> 4) @@ -281,7 +276,7 @@ static void config_art_rwp_pio (ide_drive_t *drive, byte pio) * Cycle time 20T (600ns) 13T (390ns) 8T (240ns) 6T (180ns) 4T (120ns) */ - switch(drive_number) { + switch(drive->dn) { case 0: drive_pci = 0x40; break; case 1: drive_pci = 0x42; break; case 2: drive_pci = 0x44; break; @@ -329,7 +324,7 @@ static int config_chipset_for_pio (ide_drive_t *drive, byte pio) return err; } -#undef SIS5513_TUNEPROC +#define SIS5513_TUNEPROC #ifdef SIS5513_TUNEPROC static void sis5513_tune_drive (ide_drive_t *drive, byte pio) @@ -354,7 +349,6 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) unsigned long dma_base = hwif->dma_base; byte unit = (drive->select.b.unit & 0x01); byte speed = 0x00, unmask = 0xE0, four_two = 0x00; - int drive_number = ((hwif->channel ? 2 : 0) + unit); byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0; if (host_dev) { @@ -370,7 +364,7 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) } } - switch(drive_number) { + switch(drive->dn) { case 0: drive_pci = 0x40;break; case 1: drive_pci = 0x42;break; case 2: drive_pci = 0x44;break; @@ -438,7 +432,7 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) err = ide_config_drive_speed(drive, speed); #if SIS5513_DEBUG_DRIVE_INFO - printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number); + printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive->dn); #endif /* SIS5513_DEBUG_DRIVE_INFO */ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on : diff --git a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c index f9d96a86560..39258362ca8 100644 --- a/drivers/ide/via82cxxx.c +++ b/drivers/ide/via82cxxx.c @@ -115,6 +115,7 @@ static struct chipset_bus_clock_list_entry * via82cxxx_table = NULL; struct chipset_bus_clock_list_entry via82cxxx_type_one [] = { /* speed */ /* 25 */ /* 33 */ /* 37.5 */ /* 41.5 */ +#ifdef CONFIG_BLK_DEV_IDEDMA { XFER_UDMA_4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { XFER_UDMA_3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { XFER_UDMA_2, 0x60, 0x20, 0x60, 0x20, 0x60, 0x21, 0x00, 0x00 }, @@ -124,7 +125,7 @@ struct chipset_bus_clock_list_entry via82cxxx_type_one [] = { { XFER_MW_DMA_2, 0x03, 0x20, 0x03, 0x20, 0x03, 0x21, 0x00, 0x00 }, { XFER_MW_DMA_1, 0x03, 0x31, 0x03, 0x31, 0x03, 0x32, 0x00, 0x00 }, { XFER_MW_DMA_0, 0x03, 0x31, 0x03, 0x31, 0x03, 0x32, 0x00, 0x00 }, - +#endif /* CONFIG_BLK_DEV_IDEDMA */ { XFER_PIO_4, 0x03, 0x20, 0x03, 0x20, 0x03, 0x21, 0x00, 0x00 }, { XFER_PIO_3, 0x03, 0x31, 0x03, 0x31, 0x03, 0x32, 0x00, 0x00 }, { XFER_PIO_2, 0x03, 0x65, 0x03, 0x65, 0x03, 0x76, 0x00, 0x00 }, @@ -135,6 +136,7 @@ struct chipset_bus_clock_list_entry via82cxxx_type_one [] = { struct chipset_bus_clock_list_entry via82cxxx_type_two [] = { /* speed */ /* 25 */ /* 33 */ /* 37.5 */ /* 41.5 */ +#ifdef CONFIG_BLK_DEV_IDEDMA { XFER_UDMA_4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { XFER_UDMA_3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { XFER_UDMA_2, 0xE0, 0x20, 0xE0, 0x20, 0xE1, 0x31, 0xE1, 0x32 }, @@ -144,7 +146,7 @@ struct chipset_bus_clock_list_entry via82cxxx_type_two [] = { { XFER_MW_DMA_2, 0x03, 0x20, 0x03, 0x20, 0x03, 0x31, 0x03, 0x32 }, { XFER_MW_DMA_1, 0x03, 0x31, 0x03, 0x31, 0x03, 0x42, 0x03, 0x53 }, { XFER_MW_DMA_0, 0x03, 0x31, 0x03, 0x31, 0x03, 0x42, 0x03, 0x53 }, - +#endif /* CONFIG_BLK_DEV_IDEDMA */ { XFER_PIO_4, 0x03, 0x20, 0x03, 0x20, 0x03, 0x31, 0x03, 0x32 }, { XFER_PIO_3, 0x03, 0x31, 0x03, 0x31, 0x03, 0x42, 0x03, 0x53 }, { XFER_PIO_2, 0x03, 0x65, 0x03, 0x65, 0x03, 0x87, 0x03, 0xA8 }, @@ -155,6 +157,7 @@ struct chipset_bus_clock_list_entry via82cxxx_type_two [] = { struct chipset_bus_clock_list_entry via82cxxx_type_three [] = { /* speed */ /* 25 */ /* 33 */ /* 37.5 */ /* 41.5 */ +#ifdef CONFIG_BLK_DEV_IDEDMA { XFER_UDMA_4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { XFER_UDMA_3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { XFER_UDMA_2, 0xE0, 0x20, 0xE0, 0x20, 0xE1, 0x31, 0xE1, 0x32 }, @@ -164,7 +167,7 @@ struct chipset_bus_clock_list_entry via82cxxx_type_three [] = { { XFER_MW_DMA_2, 0x03, 0x20, 0x03, 0x20, 0x03, 0x31, 0x03, 0x32 }, { XFER_MW_DMA_1, 0x03, 0x31, 0x03, 0x31, 0x03, 0x42, 0x03, 0x53 }, { XFER_MW_DMA_0, 0x03, 0x31, 0x03, 0x31, 0x03, 0x42, 0x03, 0x53 }, - +#endif /* CONFIG_BLK_DEV_IDEDMA */ { XFER_PIO_4, 0x03, 0x20, 0x03, 0x20, 0x03, 0x31, 0x03, 0x32 }, { XFER_PIO_3, 0x03, 0x31, 0x03, 0x31, 0x03, 0x42, 0x03, 0x53 }, { XFER_PIO_2, 0x03, 0x65, 0x03, 0x65, 0x03, 0x87, 0x03, 0xA8 }, @@ -175,6 +178,7 @@ struct chipset_bus_clock_list_entry via82cxxx_type_three [] = { struct chipset_bus_clock_list_entry via82cxxx_type_four [] = { /* speed */ /* 25 */ /* 33 */ /* 37.5 */ /* 41.5 */ +#ifdef CONFIG_BLK_DEV_IDEDMA { XFER_UDMA_4, 0x00, 0x00, 0xE0, 0x20, 0xE1, 0x31, 0x00, 0x00 }, { XFER_UDMA_3, 0x00, 0x00, 0xE1, 0x20, 0xE2, 0x31, 0x00, 0x00 }, { XFER_UDMA_2, 0x00, 0x00, 0xE2, 0x20, 0xE4, 0x31, 0x00, 0x00 }, @@ -184,7 +188,7 @@ struct chipset_bus_clock_list_entry via82cxxx_type_four [] = { { XFER_MW_DMA_2, 0x00, 0x00, 0x03, 0x20, 0x03, 0x31, 0x00, 0x00 }, { XFER_MW_DMA_1, 0x00, 0x00, 0x03, 0x31, 0x03, 0x42, 0x00, 0x00 }, { XFER_MW_DMA_0, 0x00, 0x00, 0x03, 0x31, 0x03, 0x42, 0x00, 0x00 }, - +#endif /* CONFIG_BLK_DEV_IDEDMA */ { XFER_PIO_4, 0x00, 0x00, 0x03, 0x20, 0x03, 0x31, 0x00, 0x00 }, { XFER_PIO_3, 0x00, 0x00, 0x03, 0x31, 0x03, 0x42, 0x00, 0x00 }, { XFER_PIO_2, 0x00, 0x00, 0x03, 0x65, 0x03, 0x87, 0x00, 0x00 }, @@ -651,8 +655,7 @@ static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; - byte unit = (drive->select.b.unit & 0x01); - int drive_number = ((hwif->channel ? 2 : 0) + unit); + struct chipset_bus_clock_list_entry * temp_table = NULL; byte ata2_pci = 0x00; byte ata3_pci = 0x00; @@ -662,7 +665,10 @@ static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed) int bus_speed = system_bus_clock(); - switch(drive_number) { + if (via82cxxx_table == NULL) + return -1; + + switch(drive->dn) { case 0: ata2_pci = 0x4b; ata3_pci = 0x53; break; case 1: ata2_pci = 0x4a; ata3_pci = 0x52; break; case 2: ata2_pci = 0x49; ata3_pci = 0x51; break; @@ -671,16 +677,26 @@ static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed) return -1; } + if ((via82cxxx_table == via82cxxx_type_four) && (speed <= XFER_UDMA_2)) { + temp_table = via82cxxx_type_three; + } else { + temp_table = via82cxxx_table; + } + pci_read_config_byte(dev, ata2_pci, &timing); - timing = pci_bus_clock_list(speed, bus_speed, via82cxxx_table); + timing = pci_bus_clock_list(speed, bus_speed, temp_table); pci_write_config_byte(dev, ata2_pci, timing); pci_read_config_byte(dev, ata3_pci, &ultra); - ultra = pci_bus_clock_list_ultra(speed, bus_speed, via82cxxx_table); + ultra = pci_bus_clock_list_ultra(speed, bus_speed, temp_table); pci_write_config_byte(dev, ata3_pci, ultra); + if (!drive->init_speed) + drive->init_speed = speed; + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; return(err); } @@ -845,6 +861,7 @@ unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name) struct pci_dev *isa; int i, j, ata33, ata66; + int bus_speed = system_bus_clock(); byte revision = 0; for (i = 0; i < arraysize (ApolloHostChipInfo) && !host_dev; i++) { @@ -873,18 +890,26 @@ unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name) ata33 = 1; ata66 = 0; + via82cxxx_table = ApolloISAChipInfo[j].chipset_table; + if (ApolloISAChipInfo[j].flags & VIA_FLAG_CHECK_REV) { pci_read_config_byte(isa_dev, 0x0d, &revision); ata33 = (revision >= 0x20) ? 1 : 0; } else if (ApolloISAChipInfo[j].flags & VIA_FLAG_ATA_66) { + byte ata66_0 = 0, ata66_1 = 0; ata33 = 0; ata66 = 1; + pci_read_config_byte(dev, 0x50, &ata66_1); + pci_read_config_byte(dev, 0x52, &ata66_0); + if ((ata66_0 & 0x04) || (ata66_1 & 0x04)) { + via82cxxx_table = (bus_speed == 33 || bus_speed == 37) ? + via82cxxx_type_four : + via82cxxx_type_three; + } } if (ata33 | ata66) printk(" Chipset Core ATA-%s", ata66 ? "66" : "33"); - - via82cxxx_table = ApolloISAChipInfo[j].chipset_table; } printk("\n"); } @@ -915,6 +940,7 @@ void __init ide_init_via82cxxx (ide_hwif_t *hwif) #ifdef CONFIG_VIA82CXXX_TUNING hwif->tuneproc = &via82cxxx_tune_drive; + hwif->speedproc = &via82cxxx_tune_chipset; hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; hwif->autodma = 0; diff --git a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c index 140023d49eb..6da2d24c1a2 100644 --- a/drivers/isdn/avmb1/kcapi.c +++ b/drivers/isdn/avmb1/kcapi.c @@ -320,7 +320,7 @@ endloop: *eof = 1; if (off >= len+begin) return 0; - *start = page + (begin-off); + *start = page + (off-begin); return ((count < begin+len-off) ? count : begin+len-off); } diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index c9a40a3c196..ac096db95a5 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -12,20 +12,20 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -L_TARGET := macintosh.a -L_OBJS := +O_TARGET := macintosh.o +O_OBJS := M_OBJS := ifeq ($(CONFIG_PMAC_PBOOK),y) - L_OBJS += mediabay.o + O_OBJS += mediabay.o else ifeq ($(CONFIG_MAC_FLOPPY),y) - L_OBJS += mediabay.o + O_OBJS += mediabay.o endif endif ifeq ($(CONFIG_MAC_SERIAL),y) - L_OBJS += macserial.o + O_OBJS += macserial.o else ifeq ($(CONFIG_MAC_SERIAL),m) M_OBJS += macserial.o @@ -33,7 +33,7 @@ else endif ifeq ($(CONFIG_NVRAM),y) - L_OBJS += nvram.o + O_OBJS += nvram.o else ifeq ($(CONFIG_NVRAM),m) M_OBJS += nvram.o @@ -41,39 +41,39 @@ else endif ifdef CONFIG_ADB - LX_OBJS := adb.o + OX_OBJS := adb.o endif ifdef CONFIG_ADB_KEYBOARD - L_OBJS += mac_keyb.o + O_OBJS += mac_keyb.o endif ifdef CONFIG_ADB_MACII - L_OBJS += via-macii.o + O_OBJS += via-macii.o endif ifdef CONFIG_ADB_MACIISI - L_OBJS += via-maciisi.o + O_OBJS += via-maciisi.o endif ifdef CONFIG_ADB_CUDA - L_OBJS += via-cuda.o + O_OBJS += via-cuda.o endif ifdef CONFIG_ADB_IOP - L_OBJS += adb-iop.o + O_OBJS += adb-iop.o endif ifdef CONFIG_ADB_PMU - L_OBJS += via-pmu.o + O_OBJS += via-pmu.o endif ifdef CONFIG_ADB_PMU68K - L_OBJS += via-pmu68k.o + O_OBJS += via-pmu68k.o endif ifdef CONFIG_ADB_MACIO - L_OBJS += macio-adb.o + O_OBJS += macio-adb.o endif include $(TOPDIR)/Rules.make diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index fd8c1645aac..2fc74f80613 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -72,6 +72,7 @@ static struct adb_driver *adb_driver_list[] = { struct adb_driver *adb_controller; struct notifier_block *adb_client_list = NULL; static int adb_got_sleep = 0; +static int adb_inited = 0; #ifdef CONFIG_PMAC_PBOOK static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when); @@ -213,6 +214,11 @@ int __init adb_init(void) return 0; #endif + /* xmon may do early-init */ + if (adb_inited) + return 0; + adb_inited = 1; + adb_controller = NULL; i = 0; diff --git a/drivers/macintosh/mac_keyb.c b/drivers/macintosh/mac_keyb.c index 04999bb4cef..261e41d6175 100644 --- a/drivers/macintosh/mac_keyb.c +++ b/drivers/macintosh/mac_keyb.c @@ -16,7 +16,7 @@ * * - Standard 1 button mouse * - All standard Apple Extended protocol (handler ID 4) - * - mouseman and trackman mice & trackballs + * - mouseman and trackman mice & trackballs * - PowerBook Trackpad (default setup: enable tapping) * - MicroSpeed mouse & trackball (needs testing) * - CH Products Trackball Pro (needs testing) @@ -344,7 +344,7 @@ input_keycode(int keycode, int repeat) #ifdef CONFIG_ADBMOUSE /* * XXX: Add mouse button 2+3 fake codes here if mouse open. - * Keep track of 'button' states here as we only send + * Keep track of 'button' states here as we only send * single up/down events! * Really messy; might need to check if keyboard is in * VC_RAW mode. @@ -591,11 +591,12 @@ extern int backlight_level; static void buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) { +#ifdef CONFIG_ADB_PMU /* * XXX: Where is the contrast control for the passive? * -- Cort */ - + /* Ignore data from register other than 0 */ #if 0 if ((adb_hardware != ADB_VIAPMU) || (data[0] & 0x3) || (nb < 2)) @@ -603,7 +604,7 @@ buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) if ((data[0] & 0x3) || (nb < 2)) #endif return; - + switch (data[1]&0xf ) { /* mute */ @@ -627,28 +628,25 @@ buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) /* brightness decrease */ case 0xa: /* down event */ -#ifdef CONFIG_PPC if ( data[1] == (data[1]&0xf) ) { if (backlight_level > 2) pmu_set_brightness(backlight_level-2); else pmu_set_brightness(0); } -#endif break; /* brightness increase */ case 0x9: /* down event */ -#ifdef CONFIG_PPC if ( data[1] == (data[1]&0xf) ) { if (backlight_level < 0x1e) pmu_set_brightness(backlight_level+2); - else + else pmu_set_brightness(0x1f); } -#endif break; } +#endif /* CONFIG_ADB_PMU */ } /* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */ @@ -736,7 +734,7 @@ void __init mackbd_init_hw(void) led_request.complete = 1; mackeyb_probe(); - + notifier_chain_register(&adb_client_list, &mackeyb_adb_notifier); } @@ -744,11 +742,11 @@ static int adb_message_handler(struct notifier_block *this, unsigned long code, void *x) { unsigned long flags; - + switch (code) { case ADB_MSG_PRE_RESET: case ADB_MSG_POWERDOWN: - /* Stop the repeat timer. Autopoll is already off at this point */ + /* Stop the repeat timer. Autopoll is already off at this point */ save_flags(flags); cli(); del_timer(&repeat_timer); @@ -758,7 +756,7 @@ adb_message_handler(struct notifier_block *this, unsigned long code, void *x) while(!led_request.complete) adb_poll(); break; - + case ADB_MSG_POST_RESET: mackeyb_probe(); break; @@ -841,7 +839,7 @@ mackeyb_probe(void) || (adb_mouse_kinds[id] == ADBMOUSE_MICROSPEED)) { init_microspeed(id); } else if (adb_mouse_kinds[id] == ADBMOUSE_MS_A3) { - init_ms_a3(id); + init_ms_a3(id); } else if (adb_mouse_kinds[id] == ADBMOUSE_EXTENDED) { /* * Register 1 is usually used for device @@ -854,7 +852,7 @@ mackeyb_probe(void) if ((req.reply_len) && (req.reply[1] == 0x9a) && ((req.reply[2] == 0x21) - || (req.reply[2] == 0x20))) + || (req.reply[2] == 0x20))) init_trackball(id); else if ((req.reply_len >= 4) && (req.reply[1] == 0x74) && (req.reply[2] == 0x70) && @@ -877,10 +875,10 @@ mackeyb_probe(void) } } -static void +static void init_trackpad(int id) { - struct adb_request req; + struct adb_request req; unsigned char r1_buffer[8]; printk(" (trackpad)"); @@ -907,15 +905,15 @@ init_trackpad(int id) adb_request(&req, NULL, ADBREQ_SYNC, 9, ADB_WRITEREG(id,2), - 0x99, - 0x94, - 0x19, - 0xff, - 0xb2, - 0x8a, - 0x1b, - 0x50); - + 0x99, + 0x94, + 0x19, + 0xff, + 0xb2, + 0x8a, + 0x1b, + 0x50); + adb_request(&req, NULL, ADBREQ_SYNC, 9, ADB_WRITEREG(id,1), r1_buffer[0], @@ -929,13 +927,13 @@ init_trackpad(int id) } } -static void +static void init_trackball(int id) { struct adb_request req; - + printk(" (trackman/mouseman)"); - + adb_mouse_kinds[id] = ADBMOUSE_TRACKBALL; adb_request(&req, NULL, ADBREQ_SYNC, 3, @@ -971,7 +969,7 @@ init_turbomouse(int id) printk(" (TurboMouse 5)"); adb_mouse_kinds[id] = ADBMOUSE_TURBOMOUSE5; - + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(3)); @@ -1012,7 +1010,7 @@ init_microspeed(int id) /* This will initialize mice using the Microspeed, MacPoint and other compatible firmware. Bit 12 enables extended protocol. - + Register 1 Listen (4 Bytes) 0 - 3 Button is mouse (set also for double clicking!!!) 4 - 7 Button is locking (affects change speed also) @@ -1027,7 +1025,7 @@ init_microspeed(int id) 0 - 7 Product code 8 - 23 undefined, reserved 24 - 31 Version number - + Speed 0 is max. 1 to 255 set speed in increments of 1/256 of max. */ adb_request(&req, NULL, ADBREQ_SYNC, 5, @@ -1051,7 +1049,7 @@ init_ms_a3(int id) ADB_WRITEREG(id, 0x2), 0x00, 0x07); - + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); } diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index 94ec60e1c4b..8e0528cba42 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -131,17 +131,17 @@ static int serial_refcount; #define _INLINE_ inline #ifdef SERIAL_DEBUG_OPEN -#define OPNDBG(fmt, arg...) printk(KERN_INFO fmt , ## arg) +#define OPNDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg) #else #define OPNDBG(fmt, arg...) do { } while (0) #endif #ifdef SERIAL_DEBUG_POWER -#define PWRDBG(fmt, arg...) printk(KERN_INFO fmt , ## arg) +#define PWRDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg) #else #define PWRDBG(fmt, arg...) do { } while (0) #endif #ifdef SERIAL_DEBUG_BAUDS -#define BAUDBG(fmt, arg...) printk(KERN_INFO fmt , ## arg) +#define BAUDBG(fmt, arg...) printk(fmt , ## arg) #else #define BAUDBG(fmt, arg...) do { } while (0) #endif @@ -414,7 +414,7 @@ static _INLINE_ void receive_chars(struct mac_serial *info, continue; if (tty->flip.count >= TTY_FLIPBUF_SIZE) tty_flip_buffer_push(tty); - + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { static int flip_buf_ovf; if (++flip_buf_ovf <= 1) @@ -505,7 +505,7 @@ static _INLINE_ void status_handle(struct mac_serial *info) if (info->tx_stopped) { #ifdef SERIAL_DEBUG_FLOW printk("CTS up\n"); -#endif +#endif info->tx_stopped = 0; if (!info->tx_active) transmit_chars(info); @@ -513,7 +513,7 @@ static _INLINE_ void status_handle(struct mac_serial *info) } else { #ifdef SERIAL_DEBUG_FLOW printk("CTS down\n"); -#endif +#endif info->tx_stopped = 1; } } @@ -536,7 +536,7 @@ static _INLINE_ void receive_special_dma(struct mac_serial *info) == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1)) where -= in_le16(&info->rx->res_count); where--; - + stat = read_zsreg(info->zs_channel, R1); flag = stat_to_flag(stat); @@ -582,7 +582,7 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg); -#endif +#endif if ((zs_intreg & CHAN_IRQMASK) == 0) break; @@ -659,7 +659,7 @@ static void rs_stop(struct tty_struct *tty) if (serial_paranoia_check(info, tty->device, "rs_stop")) return; - + #if 0 save_flags(flags); cli(); if (info->curregs[5] & TxENAB) { @@ -675,7 +675,7 @@ static void rs_start(struct tty_struct *tty) { struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; - + #ifdef SERIAL_DEBUG_STOP printk("rs_start %ld....\n", tty->ldisc.chars_in_buffer(tty)); @@ -683,7 +683,7 @@ static void rs_start(struct tty_struct *tty) if (serial_paranoia_check(info, tty->device, "rs_start")) return; - + save_flags(flags); cli(); #if 0 if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) { @@ -717,7 +717,7 @@ static void do_softint(void *private_) { struct mac_serial *info = (struct mac_serial *) private_; struct tty_struct *tty; - + tty = info->tty; if (!tty) return; @@ -750,7 +750,7 @@ static int startup(struct mac_serial * info, int can_sleep) OPNDBG("starting up ttyS%d (irq %d)...\n", info->line, info->irq); delay = set_scc_power(info, 1); - + setup_scc(info); OPNDBG("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq); @@ -758,7 +758,6 @@ static int startup(struct mac_serial * info, int can_sleep) info->flags |= ZILOG_INITIALIZED; enable_irq(info->irq); if (info->dma_initted) { -// enable_irq(info->tx_dma_irq); enable_irq(info->rx_dma_irq); } @@ -881,8 +880,6 @@ static void dma_init(struct mac_serial * info) volatile struct dbdma_cmd *cd; unsigned char *p; -//printk(KERN_DEBUG "SCC: dma_init\n"); - info->rx_nbuf = 8; /* various mem set up */ @@ -958,10 +955,10 @@ static void dma_init(struct mac_serial * info) static int setup_scc(struct mac_serial * info) { unsigned long flags; - + OPNDBG("setting up ttys%d SCC...\n", info->line); - save_flags(flags); cli(); /* Disable interrupts */ + save_flags(flags); cli(); /* Disable interrupts */ /* * Reset the chip. @@ -992,7 +989,8 @@ static int setup_scc(struct mac_serial * info) /* * Turn on RTS and DTR. */ - zs_rtsdtr(info, 1); + if (!info->is_irda) + zs_rtsdtr(info, 1); /* * Finally, enable sequencing and interrupts @@ -1058,7 +1056,7 @@ static void shutdown(struct mac_serial * info) { OPNDBG("Shutting down serial port %d (irq %d)....\n", info->line, info->irq); - + if (!(info->flags & ZILOG_INITIALIZED)) { OPNDBG("(already shutdown)\n"); return; @@ -1124,7 +1122,7 @@ static int set_scc_power(struct mac_serial * info, int state) /* The timings looks strange but that's the ones MacOS seems to use for the internal modem. I think we can use a lot faster ones, at least whe not using the modem, this should be tested. - */ + */ if (state) { PWRDBG("ttyS%02d: powering up hardware\n", info->line); if (feature_test(info->dev_node, FEATURE_Serial_enable) == 0) { @@ -1150,7 +1148,7 @@ static int set_scc_power(struct mac_serial * info, int state) delay = 2500; /* wait for 2.5s before using */ } #ifdef CONFIG_PMAC_PBOOK - if (info->is_pwbk_ir) + if (info->is_irda) pmu_enable_irled(1); #endif /* CONFIG_PMAC_PBOOK */ } else { @@ -1161,14 +1159,14 @@ static int set_scc_power(struct mac_serial * info, int state) mdelay(10); } #ifdef CONFIG_PMAC_PBOOK - if (info->is_pwbk_ir) + if (info->is_irda) pmu_enable_irled(0); #endif /* CONFIG_PMAC_PBOOK */ - - if (info->zs_chan_a == info->zs_channel) { + + if (info->zs_chan_a == info->zs_channel && !info->is_irda) { PWRDBG("ttyS%02d: shutting down SCC channel A\n", info->line); feature_clear(info->dev_node, FEATURE_Serial_IO_A); - } else { + } else if (!info->is_irda) { PWRDBG("ttyS%02d: shutting down SCC channel B\n", info->line); feature_clear(info->dev_node, FEATURE_Serial_IO_B); } @@ -1188,6 +1186,113 @@ static int set_scc_power(struct mac_serial * info, int state) return delay; } +static void irda_rts_pulses(struct mac_serial *info, int w) +{ + unsigned long flags; + + udelay(w); + save_flags(flags); cli(); + write_zsreg(info->zs_channel, 5, Tx8 | TxENAB); + udelay(2); + write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); + udelay(8); + write_zsreg(info->zs_channel, 5, Tx8 | TxENAB); + udelay(4); + write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); + restore_flags(flags); +} + +/* + * Set the irda codec on the imac to the specified baud rate. + */ +static void irda_setup(struct mac_serial *info) +{ + int code, speed, t; + unsigned long flags; + + speed = info->tty->termios->c_cflag & CBAUD; + if (speed < B2400 || speed > B115200) + return; + code = 0x4d + B115200 - speed; + + /* disable serial interrupts and receive DMA */ + write_zsreg(info->zs_channel, 1, info->curregs[1] & ~0x9f); + + /* wait for transmitter to drain */ + t = 10000; + while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0 + || (read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { + if (--t <= 0) { + printk(KERN_ERR "transmitter didn't drain\n"); + return; + } + udelay(10); + } + udelay(100); + + /* set to 8 bits, no parity, 19200 baud, RTS on, DTR off */ + write_zsreg(info->zs_channel, 4, X16CLK | SB1); + write_zsreg(info->zs_channel, 11, TCBR | RCBR); + t = BPS_TO_BRG(19200, ZS_CLOCK/16); + write_zsreg(info->zs_channel, 12, t); + write_zsreg(info->zs_channel, 13, t >> 8); + write_zsreg(info->zs_channel, 14, BRENABL); + write_zsreg(info->zs_channel, 3, Rx8 | RxENABLE); + write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); + + /* set TxD low for ~104us and pulse RTS */ + udelay(1000); + save_flags(flags); cli(); + write_zsdata(info->zs_channel, 0xfe); + irda_rts_pulses(info, 150); + restore_flags(flags); + irda_rts_pulses(info, 180); + irda_rts_pulses(info, 50); + udelay(100); + + /* assert DTR, wait 30ms, talk to the chip */ + write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS | DTR); + udelay(30000); + while (read_zsreg(info->zs_channel, 0) & Rx_CH_AV) + read_zsdata(info->zs_channel); + + write_zsdata(info->zs_channel, 1); + t = 1000; + while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) { + if (--t <= 0) { + printk(KERN_ERR "irda_setup timed out on 1st byte\n"); + goto out; + } + udelay(10); + } + t = read_zsdata(info->zs_channel); + if (t != 4) + printk(KERN_ERR "irda_setup 1st byte = %x\n", t); + + write_zsdata(info->zs_channel, code); + t = 1000; + while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) { + if (--t <= 0) { + printk(KERN_ERR "irda_setup timed out on 2nd byte\n"); + goto out; + } + udelay(10); + } + t = read_zsdata(info->zs_channel); + if (t != code) + printk(KERN_ERR "irda_setup 2nd byte = %x (%x)\n", t, code); + + /* Drop DTR again and do some more RTS pulses */ + out: + udelay(100); + write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); + irda_rts_pulses(info, 80); + + /* We should be right to go now. We assume that load_zsregs + will get called soon to load up the correct baud rate etc. */ + info->curregs[5] = (info->curregs[5] | RTS) & ~DTR; + info->pendregs[5] = info->curregs[5]; +} /* * This routine is called to set the UART divisor registers to match @@ -1195,7 +1300,6 @@ static int set_scc_power(struct mac_serial * info, int state) */ static void change_speed(struct mac_serial *info, struct termios *old_termios) { - unsigned short port; unsigned cflag; int bits; int brg, baud; @@ -1203,8 +1307,6 @@ static void change_speed(struct mac_serial *info, struct termios *old_termios) if (!info->tty || !info->tty->termios) return; - if (!(port = info->port)) - return; cflag = info->tty->termios->c_cflag; baud = tty_get_baud_rate(info->tty); @@ -1227,7 +1329,7 @@ static void change_speed(struct mac_serial *info, struct termios *old_termios) info->zs_baud = baud; info->clk_divisor = 16; - BAUDBG("set speed to %d bds, ", baud); + BAUDBG(KERN_DEBUG "set speed to %d bds, ", baud); switch (baud) { case ZS_CLOCK/16: /* 230400 */ @@ -1325,6 +1427,10 @@ static void change_speed(struct mac_serial *info, struct termios *old_termios) BAUDBG("timeout=%d/%ds, base:%d\n", (int)info->timeout, (int)HZ, (int)info->baud_base); + /* set the irda codec to the right rate */ + if (info->is_irda) + irda_setup(info); + /* Load up the new values */ load_zsregs(info->zs_channel, info->curregs); @@ -1391,7 +1497,7 @@ static int rs_write(struct tty_struct * tty, int from_user, } else { while (1) { save_flags(flags); - cli(); + cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); @@ -1419,7 +1525,7 @@ static int rs_write_room(struct tty_struct *tty) { struct mac_serial *info = (struct mac_serial *)tty->driver_data; int ret; - + if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; @@ -1431,7 +1537,7 @@ static int rs_write_room(struct tty_struct *tty) static int rs_chars_in_buffer(struct tty_struct *tty) { struct mac_serial *info = (struct mac_serial *)tty->driver_data; - + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) return 0; return info->xmit_cnt; @@ -1441,7 +1547,7 @@ static void rs_flush_buffer(struct tty_struct *tty) { struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; - + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; save_flags(flags); cli(); @@ -1466,13 +1572,12 @@ static void rs_throttle(struct tty_struct * tty) struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->device, "rs_throttle")) return; - + if (I_IXOFF(tty)) { save_flags(flags); cli(); info->x_char = STOP_CHAR(tty); @@ -1502,13 +1607,12 @@ static void rs_unthrottle(struct tty_struct * tty) struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) return; - + if (I_IXOFF(tty)) { save_flags(flags); cli(); if (info->x_char) @@ -1685,8 +1789,6 @@ static void rs_break(struct tty_struct *tty, int break_state) if (serial_paranoia_check(info, tty->device, "rs_break")) return; - if (!info->port) - return; save_flags(flags); cli(); if (break_state == -1) @@ -1714,7 +1816,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } - + switch (cmd) { case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); @@ -1736,7 +1838,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, info, sizeof(struct mac_serial))) return -EFAULT; return 0; - + default: return -ENOIOCTLCMD; } @@ -1775,15 +1877,15 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (!info || serial_paranoia_check(info, tty->device, "rs_close")) return; - + save_flags(flags); cli(); - + if (tty_hung_up_p(filp)) { MOD_DEC_USE_COUNT; restore_flags(flags); return; } - + OPNDBG("rs_close ttys%d, count = %d\n", info->line, info->count); if ((tty->count == 1) && (info->count != 1)) { /* @@ -1984,7 +2086,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, info->flags |= ZILOG_CALLOUT_ACTIVE; return 0; } - + /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. @@ -2004,7 +2106,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; } - + /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in @@ -2024,7 +2126,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, while (1) { cli(); if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && - (tty->termios->c_cflag & CBAUD)) + (tty->termios->c_cflag & CBAUD) && + !info->is_irda) zs_rtsdtr(info, 1); sti(); set_current_state(TASK_INTERRUPTIBLE); @@ -2034,7 +2137,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, if (info->flags & ZILOG_HUP_NOTIFY) retval = -EAGAIN; else - retval = -ERESTARTSYS; + retval = -ERESTARTSYS; #else retval = -EAGAIN; #endif @@ -2063,7 +2166,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, return retval; info->flags |= ZILOG_NORMAL_ACTIVE; return 0; -} +} /* * This routine is called whenever a serial port is opened. It @@ -2208,11 +2311,11 @@ chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan, /* XXX tested only with wallstreet PowerBook, should do no harm anyway */ conn = get_property(ch, "AAPL,connector", &len); - zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0); + zss->is_irda = conn && (strcmp(conn, "infrared") == 0); /* 1999 Powerbook G3 has slot-names property instead */ slots = (struct slot_names_prop *)get_property(ch, "slot-names", &len); if (slots && slots->count > 0 && strcmp(slots->name, "IrDA") == 0) - zss->is_pwbk_ir = 1; + zss->is_irda = 1; if (zss->has_dma) { zss->dma_priv = NULL; @@ -2345,7 +2448,11 @@ int macserial_init(void) memset(&serial_driver, 0, sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; +#ifdef CONFIG_DEVFS_FS + serial_driver.name = "tts/%d"; +#else serial_driver.name = "ttyS"; +#endif /* CONFIG_DEVFS_FS */ serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64; serial_driver.num = zs_channels_found; @@ -2383,7 +2490,11 @@ int macserial_init(void) * major number and the subtype code. */ callout_driver = serial_driver; +#ifdef CONFIG_DEVFS_FS + callout_driver.name = "cua/%d"; +#else callout_driver.name = "cua"; +#endif /* CONFIG_DEVFS_FS */ callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; @@ -2402,7 +2513,7 @@ int macserial_init(void) zs_soft[channel].clk_divisor = 16; /* -- we are not sure the SCC is powered ON at this point zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); -*/ +*/ zs_soft[channel].zs_baud = 38400; /* If console serial line, then enable interrupts. */ @@ -2453,16 +2564,16 @@ int macserial_init(void) printk(", port = %s", connector); if (info->is_cobalt_modem) printk(" (cobalt modem)"); - if (info->is_pwbk_ir) - printk(" (powerbook IR)"); + if (info->is_irda) + printk(" (IrDA)"); printk("\n"); #ifndef CONFIG_XMON #ifdef CONFIG_KGDB if (!info->kgdb_channel) #endif /* CONFIG_KGDB */ - /* By default, disable the port */ - set_scc_power(info, 0); + /* By default, disable the port */ + set_scc_power(info, 0); #endif /* CONFIG_XMON */ } tmp_buf = 0; @@ -2561,7 +2672,7 @@ static void serial_console_write(struct console *co, const char *s, while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0) eieio(); - + write_zsdata(info->zs_channel, 13); } } @@ -2869,7 +2980,7 @@ void __init zs_kgdb_hook(int tty_num) probe_sccs(); set_scc_power(&zs_soft[tty_num], 1); - + zs_kgdbchan = zs_soft[tty_num].zs_channel; zs_soft[tty_num].change_needed = 0; zs_soft[tty_num].clk_divisor = 16; @@ -2891,12 +3002,12 @@ int serial_notify_sleep(struct pmu_sleep_notifier *self, int when) { int i; - + switch (when) { case PBOOK_SLEEP_REQUEST: case PBOOK_SLEEP_REJECT: break; - + case PBOOK_SLEEP_NOW: for (i=0; icontents) >> 4) & 7) +inline int mb_content(volatile struct media_bay_info *bay) +{ + if (bay->pismo) { + unsigned char new_gpio = in_8(bay->extint_gpio + 0xe) & 2; + if (new_gpio) { + bay->gpio_cache = new_gpio; + return MB_NO; + } else if (bay->gpio_cache != new_gpio) { + /* make sure content bits are set */ + feature_set(bay->dev_node, FEATURE_Mediabay_content); + udelay(5); + bay->gpio_cache = new_gpio; + } + return (in_le32((unsigned*)bay->addr) >> 4) & 0xf; + } else { + int cont = (in_8(&bay->addr->contents) >> 4) & 7; + return (cont == 7) ? MB_NO : cont; + } +} #ifdef CONFIG_BLK_DEV_IDE /* check the busy bit in the media-bay ide interface @@ -184,12 +207,22 @@ media_bay_init(void) media_bays[n].addr = (volatile struct media_bay_hw *) ioremap(np->addrs[0].address, sizeof(struct media_bay_hw)); + media_bays[n].pismo = device_is_compatible(np, "keylargo-media-bay"); + if (media_bays[n].pismo) { + if (!np->parent || strcmp(np->parent->name, "mac-io")) { + printk(KERN_ERR "Pismo media-bay has no mac-io parent !\n"); + continue; + } + media_bays[n].extint_gpio = ioremap(np->parent->addrs[0].address + + 0x58, 0x10); + } + #ifdef MB_USE_INTERRUPTS if (np->n_intrs == 0) { printk(KERN_ERR "media bay %d has no irq\n",n); continue; } - + if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) { printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n", np->intrs[0].line, n); @@ -203,10 +236,11 @@ media_bay_init(void) /* Force an immediate detect */ set_mb_power(n,0); mdelay(MB_POWER_DELAY); - out_8(&media_bays[n].addr->contents, 0x70); + if(!media_bays[n].pismo) + out_8(&media_bays[n].addr->contents, 0x70); mdelay(MB_STABLE_DELAY); media_bays[n].content_id = MB_NO; - media_bays[n].last_value = MB_CONTENTS(n); + media_bays[n].last_value = mb_content(&media_bays[n]); media_bays[n].value_count = MS_TO_HZ(MB_STABLE_DELAY); media_bays[n].state = mb_empty; do { @@ -214,11 +248,11 @@ media_bay_init(void) media_bay_step(n); } while((media_bays[n].state != mb_empty) && (media_bays[n].state != mb_up)); - + n++; np=np->next; } - + if (media_bay_count) { printk(KERN_INFO "Registered %d media-bay(s)\n", media_bay_count); @@ -227,7 +261,8 @@ media_bay_init(void) pmu_register_sleep_notifier(&mb_sleep_notifier); #endif /* CONFIG_PMAC_PBOOK */ - kernel_thread(media_bay_task, NULL, 0); + kernel_thread(media_bay_task, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); } } @@ -252,7 +287,11 @@ set_mb_power(int which, int onoff) MBDBG("mediabay%d: powering up\n", which); } else { feature_clear(mb->dev_node, FEATURE_Mediabay_floppy_enable); - feature_clear(mb->dev_node, FEATURE_IDE1_enable); + if (mb->pismo) + feature_clear(mb->dev_node, FEATURE_IDE0_enable); + else + feature_clear(mb->dev_node, FEATURE_IDE1_enable); + feature_clear(mb->dev_node, FEATURE_Mediabay_IDE_switch); feature_clear(mb->dev_node, FEATURE_Mediabay_PCI_enable); feature_clear(mb->dev_node, FEATURE_SWIM3_enable); feature_clear(mb->dev_node, FEATURE_Mediabay_power); @@ -271,9 +310,17 @@ set_media_bay(int which, int id) switch (id) { case MB_CD: - feature_set(bay->dev_node, FEATURE_IDE1_enable); - udelay(10); - feature_set(bay->dev_node, FEATURE_IDE1_reset); + if (bay->pismo) { + feature_set(bay->dev_node, FEATURE_Mediabay_IDE_switch); + udelay(10); + feature_set(bay->dev_node, FEATURE_IDE0_enable); + udelay(10); + feature_set(bay->dev_node, FEATURE_IDE0_reset); + } else { + feature_set(bay->dev_node, FEATURE_IDE1_enable); + udelay(10); + feature_set(bay->dev_node, FEATURE_IDE1_reset); + } printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which); break; case MB_FD: @@ -345,8 +392,8 @@ media_bay_set_ide_infos(struct device_node* which_bay, unsigned long base, if ((MB_CD != media_bays[i].content_id) || media_bays[i].state != mb_up) return 0; - - printk(KERN_DEBUG "Registered ide %d for media bay %d\n", index, i); + + printk(KERN_DEBUG "Registered ide %d for media bay %d\n", index, i); do { if (MB_IDE_READY(i)) { media_bays[i].cd_index = index; @@ -354,7 +401,7 @@ media_bay_set_ide_infos(struct device_node* which_bay, unsigned long base, } mdelay(1); } while(--timeout); - printk(KERN_DEBUG "Timeout waiting IDE in bay %d\n", i); + printk(KERN_DEBUG "Timeount waiting IDE in bay %d\n", i); return -ENODEV; } #endif @@ -397,7 +444,10 @@ media_bay_step(int i) } #ifdef CONFIG_BLK_DEV_IDE MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id); - feature_clear(bay->dev_node, FEATURE_IDE1_reset); + if (bay->pismo) + feature_clear(bay->dev_node, FEATURE_IDE0_reset); + else + feature_clear(bay->dev_node, FEATURE_IDE1_reset); bay->timer = MS_TO_HZ(MB_IDE_WAIT); bay->state = mb_ide_resetting; #else @@ -476,23 +526,21 @@ media_bay_step(int i) int __pmac media_bay_task(void *x) { - int i = 0; - + int i; + strcpy(current->comm, "media-bay"); #ifdef MB_IGNORE_SIGNALS sigfillset(¤t->blocked); #endif for (;;) { - media_bay_step(i); + for (i = 0; i < media_bay_count; ++i) + media_bay_step(i); - if (++i >= media_bay_count) { - i = 0; current->state = TASK_INTERRUPTIBLE; schedule_timeout(1); if (signal_pending(current)) return 0; - } } } @@ -500,7 +548,7 @@ void __pmac poll_media_bay(int which) { volatile struct media_bay_info* bay = &media_bays[which]; - int id = MB_CONTENTS(which); + int id = mb_content(bay); if (id == bay->last_value) { if (id != bay->content_id @@ -548,27 +596,26 @@ mb_notify_sleep(struct pmu_sleep_notifier *self, int when) bay = &media_bays[i]; set_mb_power(i, 0); mdelay(10); - out_8(&bay->addr->contents, 0x70); } break; case PBOOK_WAKE: for (i=0; iaddr->contents, 0x70); + if (!bay->pismo) + out_8(&bay->addr->contents, 0x70); mdelay(MB_STABLE_DELAY); - if (MB_CONTENTS(i) != bay->content_id) + if (mb_content(bay) != bay->content_id) continue; set_mb_power(i, 1); - mdelay(MB_POWER_DELAY); - media_bays[i].last_value = bay->content_id; - media_bays[i].value_count = MS_TO_HZ(MB_STABLE_DELAY); - media_bays[i].timer = 0; - media_bays[i].cd_retry = 0; + bay->last_value = bay->content_id; + bay->value_count = MS_TO_HZ(MB_STABLE_DELAY); + bay->timer = MS_TO_HZ(MB_POWER_DELAY); + bay->cd_retry = 0; do { mdelay(1000/HZ); media_bay_step(i); @@ -580,3 +627,4 @@ mb_notify_sleep(struct pmu_sleep_notifier *self, int when) return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ + diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c index eb5dcaa3b04..a44e624d80c 100644 --- a/drivers/macintosh/nvram.c +++ b/drivers/macintosh/nvram.c @@ -96,21 +96,18 @@ static struct miscdevice nvram_dev = { &nvram_fops }; -int nvram_init(void) +int __init nvram_init(void) { printk(KERN_INFO "Macintosh non-volatile memory driver v%s\n", NVRAM_VERSION); misc_register(&nvram_dev); return 0; } -#ifdef MODULE -int init_module (void) -{ - return( nvram_init() ); -} -void cleanup_module (void) +void __exit nvram_cleanup(void) { misc_deregister( &nvram_dev ); } -#endif + +module_init(nvram_init); +module_exit(nvram_cleanup); diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 4acdc55c5fc..2b6402b0c04 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -41,6 +41,7 @@ #include #include #include +#include /* Misc minor number allocated for /dev/pmu */ #define PMU_MINOR 154 @@ -201,7 +202,7 @@ static char *pbook_type[] = { "PowerBook 2400/3400/3500(G3)", "PowerBook G3 Series", "1999 PowerBook G3", - "Core99 (iBook/iMac/G4)" + "Core99" }; int __openfirmware @@ -326,7 +327,7 @@ void via_pmu_start(void) out_8(&via[IER], IER_SET | SR_INT | CB1_INT); pmu_fully_inited = 1; - + /* Enable backlight */ pmu_enable_backlight(1); } @@ -508,7 +509,7 @@ pmu_adb_reset_bus(void) if (save_autopoll != 0) pmu_adb_autopoll(save_autopoll); - + return 0; } @@ -845,9 +846,9 @@ pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) } else { #ifdef CONFIG_XMON if (len == 4 && data[1] == 0x2c) { - extern int xmon_wants_key, xmon_pmu_keycode; + extern int xmon_wants_key, xmon_adb_keycode; if (xmon_wants_key) { - xmon_pmu_keycode = data[2]; + xmon_adb_keycode = data[2]; return; } } @@ -886,7 +887,7 @@ pmu_enable_backlight(int on) if ((vias == NULL) || !pmu_has_backlight) return; - + /* first call: get current backlight value */ if (on && backlight_level < 0) { switch (pmu_kind) { @@ -983,12 +984,12 @@ pmu_restart(void) struct adb_request req; cli(); - + pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | PMU_INT_TICK ); while(!req.complete) pmu_poll(); - + pmu_request(&req, NULL, 1, PMU_RESET); while(!req.complete || (pmu_state != idle)) pmu_poll(); @@ -1002,7 +1003,7 @@ pmu_shutdown(void) struct adb_request req; cli(); - + pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | PMU_INT_TICK ); while(!req.complete) @@ -1065,6 +1066,8 @@ broadcast_sleep(int when, int fallback) current = list_entry(list, struct pmu_sleep_notifier, list); ret = current->notifier_call(current, when); if (ret != PBOOK_SLEEP_OK) { + printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n", + when, current, current->notifier_call); for (; list != &sleep_notifiers; list = list->next) { current = list_entry(list, struct pmu_sleep_notifier, list); current->notifier_call(current, fallback); @@ -1179,7 +1182,7 @@ void pmu_blink(int n) while (!req.complete) pmu_poll(); udelay(50000); } - udelay(50000); + udelay(150000); } #endif @@ -1240,20 +1243,14 @@ int __openfirmware powerbook_sleep_G3(void) /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); -#if 0 - /* Save the state of PCI config space for some slots */ - pbook_pci_save(); -#endif + /* For 750, save backside cache setting and disable it */ save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */ if (save_l2cr) _set_L2CR(0); - if (macio_base != 0) { + if (macio_base != 0) save_fcr = in_le32(FEATURE_CTRL(macio_base)); - /* Check if this is still valid on older powerbooks */ - out_le32(FEATURE_CTRL(macio_base), save_fcr & ~(0x00000140UL)); - } if (current->thread.regs && (current->thread.regs->msr & MSR_FP) != 0) giveup_fpu(current); @@ -1267,12 +1264,15 @@ int __openfirmware powerbook_sleep_G3(void) /* Ask the PMU to put us to sleep */ pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); while (!sleep_req.complete) - mb(); + pmu_poll(); cli(); while (pmu_state != idle) pmu_poll(); + /* clear IOBUS enable */ + out_le32(FEATURE_CTRL(macio_base), save_fcr & ~HRW_IOBUS_ENABLE); + /* Call low-level ASM sleep handler */ low_sleep_handler(); @@ -1281,17 +1281,15 @@ int __openfirmware powerbook_sleep_G3(void) pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); + /* reenable IOBUS */ + out_le32(FEATURE_CTRL(macio_base), save_fcr | HRW_IOBUS_ENABLE); + /* Make sure the PMU is idle */ while (pmu_state != idle) pmu_poll(); sti(); -#if 0 - /* According to someone from Apple, this should not be needed, - at least not for all devices. Let's keep it for now until we - have something that works. */ - pbook_pci_restore(); -#endif + set_context(current->mm->context); /* Restore L2 cache */ @@ -1302,6 +1300,7 @@ int __openfirmware powerbook_sleep_G3(void) sleep_restore_intrs(); /* Notify drivers */ + mdelay(10); broadcast_wake(); return 0; diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index c5fcec9bc40..d90732d8a2b 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -423,7 +423,7 @@ static struct pci_device_id vortex_pci_tbl[] __devinitdata = { { 0x10B7, 0x6560, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE656 }, { 0x10B7, 0x6562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656 }, { 0x10B7, 0x4500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C450 }, - {0,}, /* 0 terminated list. */ + {0,} /* 0 terminated list. */ }; MODULE_DEVICE_TABLE(pci, vortex_pci_tbl); diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 9a5b25038f5..c1b9701432c 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -24,6 +24,15 @@ Daniel Kobras - identified specific locations of posted MMIO write bugginess + Gerard Sharp - bug fix + + Submitting bug reports: + + "rtl8139-diag -mmmaaavvveefN" output + enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log + + See 8139too.txt for more details. + ----------------------------------------------------------------------------- Theory of Operation @@ -88,7 +97,7 @@ an MMIO register read. #include -#define RTL8139_VERSION "0.9.4.1" +#define RTL8139_VERSION "0.9.5" #define RTL8139_MODULE_NAME "8139too" #define RTL8139_DRIVER_NAME RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION #define PFX RTL8139_MODULE_NAME ": " @@ -201,7 +210,7 @@ static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = { /* {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/ {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 }, {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 }, - {0,}, + {0,} }; MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); @@ -304,6 +313,17 @@ enum rx_mode_bits { AcceptAllPhys = 0x01, }; +/* Bits in TxConfig. */ +enum tx_config_bits { + TxIFG1 = (1 << 25), /* Interframe Gap Time */ + TxIFG0 = (1 << 24), /* Enabling these bits violates IEEE 802.3 */ + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ + TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ + TxClearAbt = (1 << 0), /* Clear abort (WO) */ + + TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ +}; + /* Bits in Config1 */ enum Config1Bits { Cfg1_PM_Enable = 0x01, @@ -369,28 +389,53 @@ struct ring_info { dma_addr_t mapping; }; + typedef enum { CH_8139 = 0, CH_8139A, + CH_8139A_G, CH_8139B, + CH_8130, + CH_8139C, } chip_t; + /* directly indexed by chip_t, above */ const static struct { const char *name; + u32 version; /* from RTL8139C docs */ u32 RxConfigMask; /* should clear the bits supported by this chip */ } rtl_chip_info[] = { { "RTL-8139", + 0x60000000, 0xf0fe0040, /* XXX copied from RTL8139A, verify */ }, { "RTL-8139A", + 0x70000000, 0xf0fe0040, }, - { "RTL-8139B(L)", + { "RTL-8139A rev. G", + 0x70800000, + 0xf0fe0040, + }, + + { "RTL-8139B", + 0x78000000, 0xf0fc0040 }, + + { "RTL-8130", + 0x7C000000, + 0xf0fe0040, /* XXX copied from RTL8139A, verify */ + }, + + { "RTL-8139C", + 0x74000000, + 0xf0fc0040, /* XXX copied from RTL8139B, verify */ + }, + }; @@ -574,12 +619,8 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* enable device (incl. PCI PM wakeup), and bus-mastering */ rc = pci_enable_device (pdev); - if (rc) { - printk (KERN_ERR PFX "cannot enable PCI device (bus %d, " - "devfn %d), aborting\n", - pdev->bus->number, pdev->devfn); + if (rc) goto err_out_free_mmio; - } pci_set_master (pdev); @@ -631,15 +672,20 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, } /* identify chip attached to board */ - tmp = RTL_R32 (TxConfig); - if (((tmp >> 28) & 7) == 7) { - if (pio_len == RTL8139B_IO_SIZE) - tp->chipset = CH_8139B; - else - tp->chipset = CH_8139A; - } else { - tp->chipset = CH_8139; - } + tmp = RTL_R32 (TxConfig) & TxVersionMask; + for (i = arraysize (rtl_chip_info) - 1; i >= 0; i--) + if (tmp == rtl_chip_info[i].version) { + tp->chipset = i; + goto match; + } + + /* if unknown chip, assume array element #0, original RTL-8139 in this case */ + printk (KERN_DEBUG PFX "PCI device %s: unknown chip version, assuming RTL-8139\n", + pdev->slot_name); + printk (KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%x\n", pdev->slot_name, RTL_R32 (TxConfig)); + tp->chipset = 0; + +match: DPRINTK ("chipset id (%d/%d/%d) == %d, '%s'\n", CH_8139, CH_8139A, @@ -738,7 +784,7 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, tp->phys[0] = 32; - printk (KERN_INFO "%s: '%s' board found at 0x%lx, IRQ %d\n", + printk (KERN_INFO "%s: %s board found at 0x%lx, IRQ %d\n", dev->name, board_info[ent->driver_data].name, dev->base_addr, dev->irq); @@ -1122,6 +1168,7 @@ static void rtl8139_hw_start (struct net_device *dev) /* Check this value: the documentation for IFG contradicts ifself. */ RTL_W32 (TxConfig, (TX_DMA_BURST << 8)); +#if 0 /* if link status not ok... */ if ((RTL_R16 (BasicModeStatus) & (1<<2)) == 0) { printk (KERN_INFO "%s: no link, starting NWay\n", dev->name); @@ -1144,10 +1191,13 @@ static void rtl8139_hw_start (struct net_device *dev) /* XXX writing Config1 here is flat out wrong */ /* RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20); */ } +#endif - tmp = RTL_R8 (Config1) & Config1Clear; - tmp |= (tp->chipset == CH_8139B) ? 3 : 1; /* Enable PM/VPD */ - RTL_W8_F (Config1, tmp); + if (tp->chipset > CH_8139) { + tmp = RTL_R8 (Config1) & Config1Clear; + tmp |= (tp->chipset == CH_8139B) ? 3 : 1; /* Enable PM/VPD */ + RTL_W8_F (Config1, tmp); + } if (tp->chipset == CH_8139B) { tmp = RTL_R8 (Config4) & ~(1<<2); @@ -1596,13 +1646,19 @@ static void rtl8139_rx_interrupt (struct net_device *dev, /* A.C.: Reset the multicast list. */ rtl8139_set_rx_mode (dev); + /* XXX potentially temporary hack to + * restart hung receiver */ while (--tmp_work > 0) { - tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear; + tmp8 = RTL_R8 (ChipCmd); if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) break; - RTL_W8_F (ChipCmd, tmp8 | CmdRxEnb | CmdTxEnb); + RTL_W8_F (ChipCmd, (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb); } + /* G.S.: Re-enable receiver */ + /* XXX temporary hack to work around receiver hang */ + rtl8139_set_rx_mode (dev); + if (tmp_work <= 0) printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); } else { @@ -1973,7 +2029,6 @@ static void rtl8139_set_rx_mode (struct net_device *dev) u32 mc_filter[2]; /* Multicast hash filter */ int i, rx_mode; u32 tmp; - unsigned long flags=0; DPRINTK ("ENTER\n"); @@ -2006,7 +2061,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev) /* if called from irq handler, lock already acquired */ if (!in_irq ()) - spin_lock_irqsave (&tp->lock, flags); + spin_lock_irq (&tp->lock); /* We can safely update without stopping the chip. */ tmp = rtl8139_rx_config | rx_mode | @@ -2016,7 +2071,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev) RTL_W32_F (MAR0 + 4, mc_filter[1]); if (!in_irq ()) - spin_unlock_irqrestore (&tp->lock, flags); + spin_unlock_irq (&tp->lock); DPRINTK ("EXIT\n"); } diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 7aaeb9398d2..43c9d00affc 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -51,6 +51,9 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_SGI_IP27" = "y" ]; then bool ' SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH fi + if [ "$CONFIG_SUPERH" = "y" -a "$CONFIG_SH_SOLUTION_ENGINE" = "y" ]; then + tristate ' National DP83902AV support' CONFIG_STNIC + fi bool ' 3COM cards' CONFIG_NET_VENDOR_3COM if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then tristate ' 3c501 "EtherLink" support' CONFIG_EL1 @@ -218,7 +221,7 @@ if [ ! "$CONFIG_PPP" = "n" ]; then dep_tristate ' PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP dep_tristate ' PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP dep_tristate ' PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP - dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP + dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP $CONFIG_PPP if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP fi diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3490fd92f3c..35e8aff805f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -146,6 +146,8 @@ obj-$(CONFIG_DM9102) += dmfe.o obj-$(CONFIG_YELLOWFIN) += yellowfin.o obj-$(CONFIG_ACENIC) += acenic.o +obj-$(CONFIG_STNIC) += stnic.o 8390.o + ifeq ($(CONFIG_SK98LIN),y) SUB_DIRS += sk98lin obj-y += sk98lin/sk98lin.o diff --git a/drivers/net/Space.c b/drivers/net/Space.c index e93a9f70de2..57fa9a44e0f 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -94,10 +94,6 @@ extern int bionet_probe(struct net_device *); extern int pamsnet_probe(struct net_device *); extern int cs89x0_probe(struct net_device *dev); extern int ethertap_probe(struct net_device *dev); -extern int ether1_probe (struct net_device *dev); -extern int ether3_probe (struct net_device *dev); -extern int etherh_probe (struct net_device *dev); -extern int am79c961_probe(struct net_device *dev); extern int hplance_probe(struct net_device *dev); extern int bagetlance_probe(struct net_device *); extern int mvme147lance_probe(struct net_device *dev); @@ -393,22 +389,6 @@ struct devprobe mips_probes[] __initdata = { {NULL, 0}, }; -struct devprobe arm_probes[] __initdata = { -#ifdef CONFIG_ARM_ETHERH - {etherh_probe , 0}, -#endif -#ifdef CONFIG_ARM_ETHER3 - {ether3_probe , 0}, -#endif -#ifdef CONFIG_ARM_ETHER1 - {ether1_probe , 0}, -#endif -#ifdef CONFIG_ARM_AM79C961A - {am79c961_probe, 0}, -#endif - {NULL, 0}, -}; - /* * Unified ethernet device probe, segmented per architecture and * per bus interface. This drives the legacy devices only for now. @@ -429,8 +409,6 @@ static int __init ethif_probe(struct net_device *dev) * The arch specific probes are 1st so that any on-board ethernet * will be probed before other ISA/EISA/MCA/PCI bus cards. */ - if (probe_list(dev, arm_probes) == 0) - return 0; if (probe_list(dev, m68k_probes) == 0) return 0; if (probe_list(dev, mips_probes) == 0) diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 7d53e1260f9..29d5d763879 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -2966,6 +2966,6 @@ static int __init read_eeprom_byte(struct net_device *dev, /* * Local variables: - * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" + * compile-command: "gcc -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ diff --git a/drivers/net/am79c961a.c b/drivers/net/am79c961a.c index c7e7f2fe573..83012de4491 100644 --- a/drivers/net/am79c961a.c +++ b/drivers/net/am79c961a.c @@ -3,7 +3,10 @@ * * Derived from various things including skeleton.c * - * R.M.King 1995. + * Russell King 1995-2000. + * + * This is a special driver for the am79c961A Lance chip used in the + * Intel (formally Digital Equipment Corp) EBSA110 platform. */ #include #include @@ -21,9 +24,11 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -33,26 +38,13 @@ #include "am79c961a.h" -static int am79c961_probe1 (struct net_device *dev); -static int am79c961_open (struct net_device *dev); -static int am79c961_sendpacket (struct sk_buff *skb, struct net_device *dev); static void am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs); static void am79c961_rx (struct net_device *dev, struct dev_priv *priv); static void am79c961_tx (struct net_device *dev, struct dev_priv *priv); -static int am79c961_close (struct net_device *dev); -static struct enet_statistics *am79c961_getstats (struct net_device *dev); -static void am79c961_setmulticastlist (struct net_device *dev); -static void am79c961_timeout(struct net_device *dev); static unsigned int net_debug = NET_DEBUG; -static void -am79c961_setmulticastlist (struct net_device *dev); - -static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.01\n"; - -#define FUNC_PROLOGUE \ - struct dev_priv *priv = (struct dev_priv *)dev->priv +static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.02\n"; /* --------------------------------------------------------------------------- */ @@ -270,109 +262,6 @@ am79c961_init_for_open(struct net_device *dev) write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); } -static int -am79c961_init(struct net_device *dev) -{ - unsigned long flags; - - am79c961_ramtest(dev, 0x66); - am79c961_ramtest(dev, 0x99); - - save_flags_cli (flags); - - write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */ - write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); - - restore_flags (flags); - - return 0; -} - -/* - * This is the real probe routine. - */ -static int -am79c961_probe1(struct net_device *dev) -{ - static unsigned version_printed = 0; - struct dev_priv *priv; - int i; - - if (!dev->priv) { - dev->priv = kmalloc (sizeof (struct dev_priv), GFP_KERNEL); - if (!dev->priv) - return -ENOMEM; - } - - priv = (struct dev_priv *) dev->priv; - memset (priv, 0, sizeof(struct dev_priv)); - - /* - * The PNP initialisation should have been done by the ether bootp loader. - */ - inb((dev->base_addr + NET_RESET) >> 1); /* reset the device */ - - udelay (5); - - if (inb (dev->base_addr >> 1) != 0x08 || - inb ((dev->base_addr >> 1) + 1) != 00 || - inb ((dev->base_addr >> 1) + 2) != 0x2b) { - kfree (dev->priv); - dev->priv = NULL; - return -ENODEV; - } - - /* - * Ok, we've found a valid hw ID - */ - - if (net_debug && version_printed++ == 0) - printk (KERN_INFO "%s", version); - - printk(KERN_INFO "%s: am79c961 found [%04lx, %d] ", dev->name, dev->base_addr, dev->irq); - request_region (dev->base_addr, 0x18, "am79c961"); - - /* Retrive and print the ethernet address. */ - for (i = 0; i < 6; i++) { - dev->dev_addr[i] = inb ((dev->base_addr >> 1) + i) & 0xff; - printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); - } - - if (am79c961_init(dev)) { - kfree (dev->priv); - dev->priv = NULL; - return -ENODEV; - } - - dev->open = am79c961_open; - dev->stop = am79c961_close; - dev->hard_start_xmit = am79c961_sendpacket; - dev->get_stats = am79c961_getstats; - dev->set_multicast_list = am79c961_setmulticastlist; - dev->tx_timeout = am79c961_timeout; - - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); - - return 0; -} - -int -am79c961_probe(struct net_device *dev) -{ - static int initialised = 0; - - if (initialised) - return -ENODEV; - initialised = 1; - - dev->base_addr = 0x220; - dev->irq = 3; - - return am79c961_probe1(dev); -} - /* * Open/initialize the board. This is called (in the current kernel) * sometime after booting when the 'ifconfig' program is run. @@ -408,9 +297,17 @@ am79c961_open(struct net_device *dev) static int am79c961_close(struct net_device *dev) { + unsigned long flags; + netif_stop_queue(dev); - am79c961_init(dev); + save_flags_cli (flags); + + write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */ + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); + + restore_flags (flags); free_irq (dev->irq, dev); @@ -709,3 +606,103 @@ am79c961_tx(struct net_device *dev, struct dev_priv *priv) netif_wake_queue(dev); } + +static int +am79c961_hw_init(struct net_device *dev) +{ + unsigned long flags; + + am79c961_ramtest(dev, 0x66); + am79c961_ramtest(dev, 0x99); + + save_flags_cli (flags); + + write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */ + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); + + restore_flags (flags); + + return 0; +} + +static void __init am79c961_banner(void) +{ + static unsigned version_printed = 0; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static int __init am79c961_init(void) +{ + struct net_device *dev; + struct dev_priv *priv; + int i, ret; + + dev = init_etherdev(NULL, sizeof(struct dev_priv)); + ret = -ENOMEM; + if (!dev) + goto out; + + priv = (struct dev_priv *) dev->priv; + + /* + * Fixed address and IRQ lines here. + * The PNP initialisation should have been + * done by the ether bootp loader. + */ + dev->base_addr = 0x220; + dev->irq = IRQ_EBSA110_ETHERNET; + + /* + * Reset the device. + */ + inb((dev->base_addr + NET_RESET) >> 1); + udelay(5); + + /* + * Check the manufacturer part of the + * ether address. + */ + ret = -ENODEV; + if (inb(dev->base_addr >> 1) != 0x08 || + inb((dev->base_addr >> 1) + 1) != 00 || + inb((dev->base_addr >> 1) + 2) != 0x2b) + goto nodev; + + if (!request_region(dev->base_addr, 0x18, dev->name)) + goto nodev; + + am79c961_banner(); + printk(KERN_INFO "%s: am79c961 found at %08lx, IRQ%d, ether address ", + dev->name, dev->base_addr, dev->irq); + + /* Retrive and print the ethernet address. */ + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = inb((dev->base_addr >> 1) + i) & 0xff; + printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); + } + + if (am79c961_hw_init(dev)) + goto release; + + dev->open = am79c961_open; + dev->stop = am79c961_close; + dev->hard_start_xmit = am79c961_sendpacket; + dev->get_stats = am79c961_getstats; + dev->set_multicast_list = am79c961_setmulticastlist; + dev->tx_timeout = am79c961_timeout; + + return 0; + +release: + release_region(dev->base_addr, 0x18); +nodev: + unregister_netdev(dev); + kfree(dev); +out: + return ret; +} + +module_init(am79c961_init); diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index 02331da2497..d36f74aa867 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -345,9 +345,8 @@ static int __init arcrimi_setup(char *s) s = get_options(s, 8, ints); if (!ints[0]) return 1; - dev = alloc_bootmem(sizeof(struct net_device) + 10); - memset(dev, 0, sizeof(struct net_device) + 10); - dev->name = (char *) (dev + 1); + dev = alloc_bootmem(sizeof(struct net_device)); + memset(dev, 0, sizeof(struct net_device)); dev->init = arcrimi_probe; switch (ints[0]) { diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c index 7f8a022bf62..9363422188c 100644 --- a/drivers/net/arcnet/com20020-isa.c +++ b/drivers/net/arcnet/com20020-isa.c @@ -194,10 +194,9 @@ static int __init com20020isa_setup(char *s) s = get_options(s, 8, ints); if (!ints[0]) return 1; - dev = alloc_bootmem(sizeof(struct net_device) + sizeof(struct arcnet_local) + 10); - memset(dev, 0, sizeof(struct net_device) + sizeof(struct arcnet_local) + 10); + dev = alloc_bootmem(sizeof(struct net_device) + sizeof(struct arcnet_local)); + memset(dev, 0, sizeof(struct net_device) + sizeof(struct arcnet_local)); lp = dev->priv = (struct arcnet_local *) (dev + 1); - dev->name = (char *) (lp + 1); dev->init = com20020isa_probe; switch (ints[0]) { diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index f468f38292e..0c92cc4d247 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -139,7 +139,9 @@ static struct pci_device_id com20020pci_id_table[] __devinitdata = { {0,} }; -static struct pci_driver com20020pci_driver __devinitdata = { +MODULE_DEVICE_TABLE(pci, com20020pci_id_table); + +static struct pci_driver com20020pci_driver = { name: "com20020", id_table: com20020pci_id_table, probe: com20020pci_probe, diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index aca9635aef8..24ba13ef231 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -428,9 +428,8 @@ static int __init com90io_setup(char *s) s = get_options(s, 4, ints); if (!ints[0]) return 0; - dev = alloc_bootmem(sizeof(struct net_device) + 10); - memset(dev, 0, sizeof(struct net_device) + 10); - dev->name = (char *) (dev + 1); + dev = alloc_bootmem(sizeof(struct net_device)); + memset(dev, 0, sizeof(struct net_device)); dev->init = com90io_probe; switch (ints[0]) { diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index 7293a16f89b..97f8d0a41c5 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -677,9 +677,8 @@ static int __init com90xx_setup(char *s) printk("com90xx: Disabled.\n"); return 1; } - dev = alloc_bootmem(sizeof(struct net_device) + 10); - memset(dev, 0, sizeof(struct net_device) + 10); - dev->name = (char *) (dev + 1); + dev = alloc_bootmem(sizeof(struct net_device)); + memset(dev, 0, sizeof(struct net_device)); dev->init = com90xx_probe; switch (ints[0]) { diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index afb6cbfeb04..048f7edd165 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -2206,7 +2206,7 @@ static struct pci_device_id eepro100_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559ER, PCI_ANY_ID, PCI_ANY_ID, }, - { 0,}, + { 0,} }; MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl); diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 80a756ae853..6e6af292f8d 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -189,7 +189,7 @@ static struct pci_device_id epic_pci_tbl[] __devinitdata = { { 0x10B8, 0x0005, 0x1092, 0x0AB4, 0, 0, SMSC_83C170_0 }, { 0x10B8, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMSC_83C170 }, { 0x10B8, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMSC_83C175 }, - { 0,}, + { 0,} }; MODULE_DEVICE_TABLE (pci, epic_pci_tbl); diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 39625e4814f..443eaffd244 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -3123,11 +3123,11 @@ int hp100_port[5] = { 0, -1, -1, -1, -1 }; MODULE_PARM(hp100_port, "1-5i"); #endif -#ifndef LINUX_2_1 -static char devname[5][IFNAMSIZ] = { "", "", "", "", "" }; -static char *hp100_name[5] = { devname[0], devname[1], - devname[2], devname[3], - devname[4] }; +/* Allocate 5 string of length IFNAMSIZ, one string for each device */ +char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" }; +#ifdef LINUX_2_1 +/* Allow insmod to write those 5 strings individually */ +MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ)); #endif /* List of devices */ @@ -3159,9 +3159,11 @@ int init_module( void ) /* Create device and set basics args */ hp100_devlist[i] = kmalloc(sizeof(struct net_device), GFP_KERNEL); memset(hp100_devlist[i], 0x00, sizeof(struct net_device)); -#ifndef LINUX_2_1 +#if LINUX_VERSION_CODE >= 0x020362 /* 2.3.99-pre7 */ + memcpy(hp100_devlist[i]->name, hp100_name[i], IFNAMSIZ); /* Copy name */ +#else hp100_devlist[i]->name = hp100_name[i]; -#endif +#endif /* LINUX_VERSION_CODE >= 0x020362 */ hp100_devlist[i]->base_addr = hp100_port[i]; hp100_devlist[i]->init = &hp100_probe; diff --git a/drivers/net/ncr885e.c b/drivers/net/ncr885e.c index f91796d313c..9a5c65cb486 100644 --- a/drivers/net/ncr885e.c +++ b/drivers/net/ncr885e.c @@ -424,7 +424,7 @@ ncr885e_tx( struct net_device *dev ) /* look for any channel status (?) */ if ( xfer ) { - dev_kfree_skb( sp->tx_skbufs[i] ); + dev_kfree_skb_irq( sp->tx_skbufs[i] ); if ( txbits & TX_STATUS_TXOK ) { sp->stats.tx_packets++; diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c index c36c76dff99..3f40f5aaed9 100644 --- a/drivers/net/ne2k-pci.c +++ b/drivers/net/ne2k-pci.c @@ -115,7 +115,7 @@ static struct pci_device_id ne2k_pci_tbl[] __devinitdata = { { 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F }, { 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 }, { 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 }, - { 0, }, + { 0, } }; MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl); diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 7b1f7644808..dc580b0fe5c 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -334,9 +334,11 @@ static struct pci_device_id pcnet32_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PCNETHOME, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0x1014, 0x2000, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0 }, + { 0, } }; +MODULE_DEVICE_TABLE (pci, pcnet32_pci_tbl); + static u16 pcnet32_wio_read_csr (unsigned long addr, int index) { outw (index, addr+PCNET32_WIO_RAP); diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index d84f059563b..5d852dfc685 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -269,7 +269,7 @@ ppp_synctty_ioctl(struct tty_struct *tty, struct file *file, err = -EFAULT; switch (cmd) { - case PPPIOCGUNIT: + case PPPIOCGCHAN: err = -ENXIO; if (ap == 0) break; @@ -279,6 +279,16 @@ ppp_synctty_ioctl(struct tty_struct *tty, struct file *file, err = 0; break; + case PPPIOCGUNIT: + err = -ENXIO; + if (ap == 0) + break; + err = -EFAULT; + if (put_user(ppp_unit_number(&ap->chan), (int *) arg)) + break; + err = 0; + break; + case TCGETS: case TCGETA: err = n_tty_ioctl(tty, file, cmd, arg); diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 9e772466c16..841c0e12ced 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -46,10 +47,12 @@ static int __attribute__((unused)) pppoe_debug = 7; -#define PPPOE_HASH_SIZE 16 +#define PPPOE_HASH_BITS 4 +#define PPPOE_HASH_SIZE (1<> ( j * PPPOE_HASH_BITS ); + } + } - i = (int) (hash.c[0] & (PPPOE_HASH_SIZE - 1)); + for (i = 0; i < (sizeof(unsigned long)*8) / PPPOE_HASH_BITS ; ++i) + hash ^= sid >> (i*PPPOE_HASH_BITS); - return i; + return hash & ( PPPOE_HASH_SIZE - 1 ); } static struct pppox_opt *item_hash_table[PPPOE_HASH_SIZE] = { 0, }; @@ -105,7 +103,7 @@ static struct pppox_opt *item_hash_table[PPPOE_HASH_SIZE] = { 0, }; /********************************************************************** * * Set/get/delete/rehash items (internal versions) - * + * **********************************************************************/ static struct pppox_opt *__get_item(unsigned long sid, unsigned char *addr) { @@ -162,7 +160,7 @@ static struct pppox_opt *__delete_item(unsigned long sid, char *addr) return ret; } -static struct pppox_opt *__find_on_dev(struct net_device *dev, +static struct pppox_opt *__find_on_dev(struct net_device *dev, struct pppox_opt *start) { struct pppox_opt *po; @@ -195,9 +193,9 @@ static struct pppox_opt *__find_on_dev(struct net_device *dev, /********************************************************************** * * Set/get/delete/rehash items - * + * **********************************************************************/ -static inline struct pppox_opt *get_item(unsigned long sid, +static inline struct pppox_opt *get_item(unsigned long sid, unsigned char *addr) { struct pppox_opt *po; @@ -239,7 +237,7 @@ static inline struct pppox_opt *delete_item(unsigned long sid, char *addr) return ret; } -static struct pppox_opt *find_on_dev(struct net_device *dev, +static struct pppox_opt *find_on_dev(struct net_device *dev, struct pppox_opt *start) { struct pppox_opt *po; @@ -255,7 +253,7 @@ static struct pppox_opt *find_on_dev(struct net_device *dev, * Certain device events require that sockets be unconnected * **************************************************************************/ -static int pppoe_device_event(struct notifier_block *this, +static int pppoe_device_event(struct notifier_block *this, unsigned long event, void *ptr) { int error = NOTIFY_DONE; @@ -277,7 +275,7 @@ static int pppoe_device_event(struct notifier_block *this, if (po->sk->state & PPPOX_CONNECTED) pppox_unbind_sock(po->sk); - + if (po->sk->state & PPPOX_CONNECTED) { lock_sock(po->sk); po->sk->shutdown = RCV_SHUTDOWN&SEND_SHUTDOWN; @@ -313,7 +311,7 @@ static struct notifier_block pppoe_notifier = { * Receive a PPPoE Session frame. * ***********************************************************************/ -static int pppoe_rcv(struct sk_buff *skb, +static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) @@ -336,6 +334,19 @@ static int pppoe_rcv(struct sk_buff *skb, skb_pull(skb, sizeof(struct pppoe_hdr)); ppp_input(&po->chan, skb); + } else if( sk->state & PPPOX_RELAY ){ + struct pppox_opt *relay_po; + + relay_po = get_item_by_addr( &po->pppoe_relay ); + + if( relay_po == NULL || + !( relay_po->sk->state & PPPOX_CONNECTED ) ) + goto abort; + + skb_pull(skb, sizeof(struct pppoe_hdr)); + if( !__pppoe_xmit( relay_po->sk , skb) ) + goto abort; + } else { sock_queue_rcv_skb(sk, skb); } @@ -353,7 +364,7 @@ abort: * -- This is solely for detection of PADT frames * ***********************************************************************/ -static int pppoe_disc_rcv(struct sk_buff *skb, +static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) @@ -539,7 +550,7 @@ int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, dev = dev_get_by_name(sp->sa_addr.pppoe.dev); - error = -ENODEV; + error = -ENODEV; if (!dev) goto end; @@ -768,13 +779,11 @@ end: /************************************************************************ * - * xmit function called by generic PPP driver - * sends PPP frame over PPPoE socket + * xmit function for internal use. * ***********************************************************************/ -int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb) +int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) { - struct sock *sk = (struct sock *) chan->private; struct net_device *dev = sk->protinfo.pppox->pppoe_dev; struct pppoe_hdr hdr; struct pppoe_hdr *ph; @@ -833,6 +842,20 @@ int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb) return 0; } + +/************************************************************************ + * + * xmit function called by generic PPP driver + * sends PPP frame over PPPoE socket + * + ***********************************************************************/ +int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk = (struct sock *) chan->private; + return __pppoe_xmit(sk, skb); +} + + struct ppp_channel_ops pppoe_chan_ops = { pppoe_xmit , NULL }; int pppoe_rcvmsg(struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm) diff --git a/drivers/net/setup.c b/drivers/net/setup.c index 7e01e3f83b6..b4e5da20012 100644 --- a/drivers/net/setup.c +++ b/drivers/net/setup.c @@ -12,7 +12,6 @@ extern int mkiss_init_ctrl_dev(void); extern int slip_init_ctrl_dev(void); extern int strip_init_ctrl_dev(void); extern int x25_asy_init_ctrl_dev(void); -extern int slhc_install(void); extern int dmascc_init(void); extern int yam_init(void); @@ -22,7 +21,7 @@ extern int awc4500_isa_probe(void); extern int awc4500_pnp_probe(void); extern int awc4500_365_probe(void); extern int arcnet_init(void); -extern int cpm_enet_init(void); +extern int scc_enet_init(void); extern int dlci_setup(void); extern int lapbeth_init(void); extern int sdla_setup(void); @@ -34,7 +33,7 @@ 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 tring of 9 zeros. */ +/* 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" #define __PAD5 __PAD6 "\0" #define __PAD4 __PAD5 "\0" @@ -73,27 +72,17 @@ struct net_probe pci_probes[] __initdata = { #if defined(CONFIG_ARCNET) {arcnet_init, 0}, #endif -#if defined(CONFIG_8xx) - {cpm_enet_init, 0}, +#if defined(CONFIG_SCC_ENET) + {scc_enet_init, 0}, #endif #if defined(CONFIG_COMX) {comx_init, 0}, -#endif /* - * SLHC if present needs attaching so other people see it - * even if not opened. - */ +#endif + #if defined(CONFIG_LANMEDIA) {lmc_setup, 0}, #endif -#ifdef CONFIG_INET -#if (defined(CONFIG_SLIP) && defined(CONFIG_SLIP_COMPRESSED)) \ - || defined(CONFIG_PPP) \ - || (defined(CONFIG_ISDN) && defined(CONFIG_ISDN_PPP)) - {slhc_install, 0}, -#endif -#endif - /* * * Wireless non-HAM diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index b48b726f646..c92e73180dc 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -79,7 +79,8 @@ static struct pci_device_id sis900_pci_tbl [] __initdata = { {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_900}, {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018} + PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018}, + {0,} }; MODULE_DEVICE_TABLE (pci, sis900_pci_tbl); diff --git a/drivers/net/slhc.c b/drivers/net/slhc.c index ccc8a93d662..10df972259c 100644 --- a/drivers/net/slhc.c +++ b/drivers/net/slhc.c @@ -77,7 +77,6 @@ #include #include #include -#include #include #include #include @@ -751,12 +750,6 @@ void cleanup_module(void) return; } -#else /* MODULE */ -int __init slhc_install(void) -{ - return 0; -} - #endif /* MODULE */ #else /* CONFIG_INET */ diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 3d59d63c163..b5f70ff1616 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -211,7 +211,7 @@ enum chipset { static struct pci_device_id starfire_pci_tbl[] __devinitdata = { { 0x9004, 0x6915, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_6915 }, - { 0, }, + { 0, } }; MODULE_DEVICE_TABLE(pci, starfire_pci_tbl); diff --git a/drivers/net/stnic.c b/drivers/net/stnic.c new file mode 100644 index 00000000000..991b2125896 --- /dev/null +++ b/drivers/net/stnic.c @@ -0,0 +1,302 @@ +/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1999 kaz Kojima + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "8390.h" + +#define byte unsigned char +#define half unsigned short +#define word unsigned int +#define vbyte volatile unsigned char +#define vhalf volatile unsigned short +#define vword volatile unsigned int + +#define STNIC_RUN 0x01 /* 1 == Run, 0 == reset. */ + +#define START_PG 0 /* First page of TX buffer */ +#define STOP_PG 128 /* Last page +1 of RX ring */ + +/* Alias */ +#define STNIC_CR E8390_CMD +#define PG0_RSAR0 EN0_RSARLO +#define PG0_RSAR1 EN0_RSARHI +#define PG0_RBCR0 EN0_RCNTLO +#define PG0_RBCR1 EN0_RCNTHI + +#define CR_RRD E8390_RREAD +#define CR_RWR E8390_RWRITE +#define CR_PG0 E8390_PAGE0 +#define CR_STA E8390_START +#define CR_RDMA E8390_NODMA + +/* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS. */ +static byte stnic_eadr[6] = +{0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07}; + +static struct net_device *stnic_dev; + +static int stnic_open (struct net_device *dev); +static int stnic_close (struct net_device *dev); +static void stnic_reset (struct net_device *dev); +static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void stnic_block_input (struct net_device *dev, int count, + struct sk_buff *skb , int ring_offset); +static void stnic_block_output (struct net_device *dev, int count, + const unsigned char *buf, int start_page); + +static void stnic_init (struct net_device *dev); + +/* SH7750 specific read/write io. */ +static inline void +STNIC_DELAY (void) +{ + vword trash; + trash = *(vword *) 0xa0000000; + trash = *(vword *) 0xa0000000; +} + +static inline byte +STNIC_READ (int reg) +{ + byte val; + + val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff; + STNIC_DELAY (); + return val; +} + +static inline void +STNIC_WRITE (int reg, byte val) +{ + *(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8); + STNIC_DELAY (); +} + +int __init stnic_probe(void) +{ + struct net_device tmp, *dev = NULL; + int i; + + tmp.base_addr = 0x1000; + dev = &tmp; + + if (load_8390_module ("stnic.c")) + return -ENOSYS; + + /* New style probing API */ + dev = init_etherdev (0, 0); + stnic_dev = dev; + + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init (dev)) + { + printk ("Unable to get memory for dev->priv.\n"); + return -ENOMEM; + } + + for (i = 0; i < ETHER_ADDR_LEN; i++) + dev->dev_addr[i] = stnic_eadr[i]; + + /* Set the base address to point to the NIC, not the "real" base! */ + dev->base_addr = 0x1000; + dev->irq = IRQ_STNIC; + dev->open = &stnic_open; + dev->stop = &stnic_close; + + /* 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, "DP83902A", dev)) + { + printk (" unable to get IRQ %d.\n", dev->irq); + kfree(dev->priv); + dev->priv = NULL; + return EAGAIN; + } + + ei_status.name = "eth0"; + ei_status.word16 = 1; + ei_status.tx_start_page = START_PG; + ei_status.rx_start_page = START_PG + TX_PAGES; + ei_status.stop_page = STOP_PG; + + ei_status.reset_8390 = &stnic_reset; + ei_status.get_8390_hdr = &stnic_get_hdr; + ei_status.block_input = &stnic_block_input; + ei_status.block_output = &stnic_block_output; + stnic_init (dev); + + printk (KERN_INFO "NS ST-NIC 83902A\n"); + + return 0; +} + +static int +stnic_open (struct net_device *dev) +{ +#if 0 + printk ("stnic open\n"); +#endif + ei_open (dev); + MOD_INC_USE_COUNT; + return 0; +} + +static int +stnic_close (struct net_device *dev) +{ + ei_close (dev); + MOD_DEC_USE_COUNT; + return 0; +} + +static void +stnic_reset (struct net_device *dev) +{ + *(vhalf *) PA_83902_RST = 0; + udelay (5); + if (ei_debug > 1) + printk("8390 reset done (%ld).", jiffies); + *(vhalf *) PA_83902_RST = ~0; + udelay (5); +} + +static void +stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page) +{ + half buf[2]; + + STNIC_WRITE (PG0_RSAR0, 0); + STNIC_WRITE (PG0_RSAR1, ring_page); + STNIC_WRITE (PG0_RBCR0, 4); + STNIC_WRITE (PG0_RBCR1, 0); + STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); + + buf[0] = *(vhalf *) PA_83902_IF; + STNIC_DELAY (); + buf[1] = *(vhalf *) PA_83902_IF; + STNIC_DELAY (); + hdr->next = buf[0] >> 8; + hdr->status = buf[0] & 0xff; +#ifdef __LITTLE_ENDIAN__ + hdr->count = buf[1]; +#else + hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8); +#endif + + if (ei_debug > 1) + printk ("ring %x status %02x next %02x count %04x.\n", + ring_page, hdr->status, hdr->next, hdr->count); + + STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); +} + +/* Block input and output, similar to the Crynwr packet driver. If you are + porting to a new ethercard look at the packet driver source for hints. + The HP LAN doesn't use shared memory -- we put the packet + out through the "remote DMA" dataport. */ + +static void +stnic_block_input (struct net_device *dev, int length, struct sk_buff *skb, + int offset) +{ + char *buf = skb->data; + half val; + + STNIC_WRITE (PG0_RSAR0, offset & 0xff); + STNIC_WRITE (PG0_RSAR1, offset >> 8); + STNIC_WRITE (PG0_RBCR0, length & 0xff); + STNIC_WRITE (PG0_RBCR1, length >> 8); + STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); + + if (length & 1) + length++; + + while (length > 0) + { + val = *(vhalf *) PA_83902_IF; +#ifdef __LITTLE_ENDIAN__ + *buf++ = val & 0xff; + *buf++ = val >> 8; +#else + *buf++ = val >> 8; + *buf++ = val & 0xff; +#endif + STNIC_DELAY (); + length -= sizeof (half); + } + + STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); +} + +static void +stnic_block_output (struct net_device *dev, int length, + const unsigned char *buf, int output_page) +{ + + STNIC_WRITE (PG0_RBCR0, 1); + STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); + + STNIC_WRITE (PG0_RSAR0, 0); + STNIC_WRITE (PG0_RSAR1, output_page); + STNIC_WRITE (PG0_RBCR0, length & 0xff); + STNIC_WRITE (PG0_RBCR1, length >> 8); + STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA); + + if (length & 1) + length++; + + while (length > 0) + { +#ifdef __LITTLE_ENDIAN__ + *(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0]; +#else + *(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1]; +#endif + STNIC_DELAY (); + buf += sizeof (half); + length -= sizeof (half); + } + + STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); +} + +/* This function resets the STNIC if something screws up. */ +static void +stnic_init (struct net_device *dev) +{ + stnic_reset (dev); + NS8390_init (dev, 0); + return; +} + +/* Hardware interrupt handler. */ +extern void ei_interrupt (int irq, void *dev_id, struct pt_regs *regs); + +void +do_stnic_intr (int irq, void *dev_id, struct pt_regs *regs) +{ + ei_interrupt (0, stnic_dev, regs); +} + +module_init(stnic_probe); diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index bc4dec29cb3..12cee4da539 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -168,7 +168,7 @@ static struct pci_device_id tulip_pci_tbl[] __devinitdata = { { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, { 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, - {0}, + {0, } }; MODULE_DEVICE_TABLE(pci, tulip_pci_tbl); diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 77816670f30..67db42e974e 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -291,7 +291,7 @@ static struct pci_device_id via_rhine_pci_tbl[] __devinitdata = { {0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT86C100A}, {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT3043}, - {0,}, /* terminate list */ + {0,} /* terminate list */ }; MODULE_DEVICE_TABLE(pci, via_rhine_pci_tbl); diff --git a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c index 7381dc8a9c4..23899bde98a 100644 --- a/drivers/net/wan/comx-hw-comx.c +++ b/drivers/net/wan/comx-hw-comx.c @@ -1031,11 +1031,6 @@ static int comxhw_write_proc(struct file *file, const char *buffer, char *page; - if (file->f_dentry->d_inode->i_ino != entry->low_ino) { - printk(KERN_ERR "comx_write_proc: file <-> data internal error\n"); - return -EIO; - } - if(ch->init_status & HW_OPEN) { return -EAGAIN; } @@ -1234,7 +1229,6 @@ static int COMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &comxhw_read_proc; new_file->write_proc = &comxhw_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 6; new_file->nlink = 1; @@ -1245,7 +1239,6 @@ static int COMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &comxhw_read_proc; new_file->write_proc = &comxhw_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 5; new_file->nlink = 1; @@ -1256,7 +1249,6 @@ static int COMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &comxhw_read_proc; new_file->write_proc = &comxhw_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 2; // Ezt tudjuk new_file->nlink = 1; @@ -1268,7 +1260,6 @@ static int COMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &comxhw_read_proc; new_file->write_proc = &comxhw_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 9; new_file->nlink = 1; } @@ -1280,7 +1271,6 @@ static int COMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &comxhw_read_proc; new_file->write_proc = &comxhw_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 8; new_file->nlink = 1; @@ -1291,7 +1281,6 @@ static int COMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &comxhw_read_proc; new_file->write_proc = NULL; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, @@ -1301,7 +1290,6 @@ static int COMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &comxhw_read_proc; new_file->write_proc = &comxhw_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; if (ch->hardware == &comx_hw) { diff --git a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c index 010f0237314..94561e85992 100644 --- a/drivers/net/wan/comx-hw-locomx.c +++ b/drivers/net/wan/comx-hw-locomx.c @@ -330,11 +330,6 @@ static int locomx_write_proc(struct file *file, const char *buffer, int val; char *page; - if (file->f_dentry->d_inode->i_ino != entry->low_ino) { - printk(KERN_ERR "hw_write_proc: file <-> data internal error\n"); - return -EIO; - } - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { return -ENOMEM; } @@ -395,7 +390,6 @@ static int LOCOMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &locomx_read_proc; new_file->write_proc = &locomx_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, @@ -405,7 +399,6 @@ static int LOCOMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &locomx_read_proc; new_file->write_proc = &locomx_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; /* No clock yet */ @@ -417,7 +410,6 @@ static int LOCOMX_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &locomx_read_proc; new_file->write_proc = &locomx_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; */ diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c index 552443f886c..3ee2b690b7a 100644 --- a/drivers/net/wan/comx-hw-mixcom.c +++ b/drivers/net/wan/comx-hw-mixcom.c @@ -742,11 +742,6 @@ static int mixcom_write_proc(struct file *file, const char *buffer, char *page; int value; - if (file->f_dentry->d_inode->i_ino != entry->low_ino) { - printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n"); - return -EIO; - } - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { return -ENOMEM; } @@ -829,7 +824,6 @@ static int MIXCOM_init(struct net_device *dev) { new_file->data = (void *)new_file; new_file->read_proc = &mixcom_read_proc; new_file->write_proc = &mixcom_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, @@ -839,7 +833,6 @@ static int MIXCOM_init(struct net_device *dev) { new_file->data = (void *)new_file; new_file->read_proc = &mixcom_read_proc; new_file->write_proc = &mixcom_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; #if 0 @@ -850,7 +843,6 @@ static int MIXCOM_init(struct net_device *dev) { new_file->data = (void *)new_file; new_file->read_proc = &mixcom_read_proc; new_file->write_proc = &mixcom_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; #endif @@ -861,7 +853,6 @@ static int MIXCOM_init(struct net_device *dev) { new_file->data = (void *)new_file; new_file->read_proc = &mixcom_read_proc; new_file->write_proc = &mixcom_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, @@ -871,7 +862,6 @@ static int MIXCOM_init(struct net_device *dev) { new_file->data = (void *)new_file; new_file->read_proc = &mixcom_read_proc; new_file->write_proc = &mixcom_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->nlink = 1; setup_twin(dev); diff --git a/drivers/net/wan/comx-proto-fr.c b/drivers/net/wan/comx-proto-fr.c index ad62e310c92..f32c84e7684 100644 --- a/drivers/net/wan/comx-proto-fr.c +++ b/drivers/net/wan/comx-proto-fr.c @@ -641,11 +641,6 @@ static int fr_write_proc(struct file *file, const char *buffer, fr = ch->LINE_privdata; } - if (file->f_dentry->d_inode->i_ino != entry->low_ino) { - printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n"); - return -EIO; - } - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { return -ENOMEM; } @@ -803,7 +798,6 @@ static int fr_master_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 5; new_file->nlink = 1; @@ -814,7 +808,6 @@ static int fr_master_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 4; new_file->nlink = 1; @@ -860,7 +853,6 @@ static int fr_slave_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 5; new_file->nlink = 1; @@ -871,7 +863,6 @@ static int fr_slave_init(struct net_device *dev) new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = 10; new_file->nlink = 1; MOD_INC_USE_COUNT; diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c index c0fc0c6bd67..abf8977ff3b 100644 --- a/drivers/net/wan/comx-proto-lapb.c +++ b/drivers/net/wan/comx-proto-lapb.c @@ -221,11 +221,6 @@ static int comxlapb_write_proc(struct file *file, const char *buffer, unsigned long parm; char *page; - if (file->f_dentry->d_inode->i_ino != entry->low_ino) { - printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n"); - return -EIO; - } - if (lapb_getparms(dev->priv, &parms)) { return -ENODEV; } @@ -499,7 +494,6 @@ static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode, new_file->data = (void *)new_file; new_file->read_proc = &comxlapb_read_proc; new_file->write_proc = &comxlapb_write_proc; - new_file->proc_iops = &comx_normal_inode_ops; new_file->size = size; new_file->nlink = 1; } diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c index 4190e2ed481..d8e76deefb2 100644 --- a/drivers/net/wan/comx.c +++ b/drivers/net/wan/comx.c @@ -81,21 +81,18 @@ extern int comx_proto_fr_init(void); static struct comx_hardware *comx_channels = NULL; static struct comx_protocol *comx_lines = NULL; -struct inode_operations comx_normal_inode_ops; -static struct inode_operations comx_root_inode_ops; // for mkdir -static struct inode_operations comx_debug_inode_ops; // mas a file_ops -static struct file_operations comx_normal_file_ops; // with open/relase -static struct file_operations comx_debug_file_ops; // with lseek+read +static struct inode_operations comx_root_inode_ops = { + lookup: comx_lookup, + mkdir: comx_mkdir, + rmdir: comx_rmdir, +}; -static void comx_delete_dentry(struct dentry *dentry); +static int comx_delete_dentry(struct dentry *dentry); static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, int size, struct proc_dir_entry *dir); static struct dentry_operations comx_dentry_operations = { - NULL, /* revalidate */ - NULL, /* d_hash */ - NULL, /* d_compare */ - &comx_delete_dentry /* d_delete */ + d_delete: comx_delete_dentry, }; @@ -601,11 +598,6 @@ static int comx_write_proc(struct file *file, const char *buffer, u_long count, char str[30]; int ret=0; - if (file->f_dentry->d_inode->i_ino != entry->low_ino) { - printk(KERN_ERR "comx_write_proc: file <-> data internal error\n"); - return -EIO; - } - if (count > PAGE_SIZE) { printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE); return -ENOSPC; @@ -761,84 +753,12 @@ static int comx_write_proc(struct file *file, const char *buffer, u_long count, return count; } -static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig) -{ - switch(orig) { - case 0: - file->f_pos = max(0, min(offset, - file->f_dentry->d_inode->i_size)); - return(file->f_pos); - case 1: - file->f_pos = max(0, min(offset + file->f_pos, - file->f_dentry->d_inode->i_size)); - return(file->f_pos); - case 2: - file->f_pos = max(0, - min(offset + file->f_dentry->d_inode->i_size, - file->f_dentry->d_inode->i_size)); - return(file->f_pos); - } - return(file->f_pos); -} - -static int comx_file_open(struct inode *inode, struct file *file) -{ - - if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) { - return -EACCES; - } - - MOD_INC_USE_COUNT; - return 0; -} - -static int comx_file_release(struct inode *inode, struct file *file) -{ - MOD_DEC_USE_COUNT; - return 0; -} - -static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count, - loff_t *ppos) -{ - struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip; - struct net_device *dev = de->parent->data; - struct comx_channel *ch = dev->priv; - loff_t copied = 0; - unsigned long flags; - - save_flags(flags); cli(); // We may run into trouble when debug_area is filled - // from irq inside read. no problem if the buffer is - // large enough - - while (count > 0 && ch->debug_start != ch->debug_end) { - int len; - - len = min( (ch->debug_end - ch->debug_start + ch->debug_size) - %ch->debug_size, min (ch->debug_size - - ch->debug_start, count)); - - if (len) copy_to_user(buffer + copied, - ch->debug_area + ch->debug_start, len); - ch->debug_start = (ch->debug_start + len) % ch->debug_size; - - de->size -= len; - count -= len; - copied += len; - } - - restore_flags(flags); - return copied; -} - static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct proc_dir_entry *new_dir, *debug_file; struct net_device *dev; struct comx_channel *ch; - if (dir->i_ino != comx_root_dir->low_ino) return -ENOTDIR; - if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, comx_root_dir)) == NULL) { return -EIO; @@ -867,7 +787,6 @@ static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode) S_IFREG | 0644, new_dir)) == NULL) { return -ENOMEM; } - debug_file->proc_iops = &comx_debug_inode_ops; debug_file->data = (void *)debug_file; debug_file->read_proc = NULL; // see below debug_file->write_proc = &comx_write_proc; @@ -912,9 +831,6 @@ static int comx_rmdir(struct inode *dir, struct dentry *dentry) struct comx_channel *ch = dev->priv; int ret; - /* Egyelore miert ne ? */ - if (dir->i_ino != comx_root_dir->low_ino) return -ENOTDIR; - if (dev->flags & IFF_UP) { printk(KERN_ERR "%s: down interface before removing it\n", dev->name); return -EBUSY; @@ -962,10 +878,6 @@ static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry) struct proc_dir_entry *de; struct inode *inode = NULL; - if (!dir || !S_ISDIR(dir->i_mode)) { - return ERR_PTR(-ENOTDIR); - } - if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) { for (de = de->subdir ; de ; de = de->next) { if ((de && de->low_ino) && @@ -998,9 +910,9 @@ int comx_strcasecmp(const char *cs, const char *ct) return __res; } -static void comx_delete_dentry(struct dentry *dentry) +static int comx_delete_dentry(struct dentry *dentry) { - d_drop(dentry); + return 1; } static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, @@ -1009,7 +921,6 @@ static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, struct proc_dir_entry *new_file; if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) { - new_file->proc_iops = &comx_normal_inode_ops; new_file->data = (void *)new_file; new_file->read_proc = &comx_read_proc; new_file->write_proc = &comx_write_proc; @@ -1115,23 +1026,6 @@ int __init comx_init(void) { struct proc_dir_entry *new_file; - comx_root_inode_ops.lookup = &comx_lookup; - comx_root_inode_ops.mkdir = &comx_mkdir; - comx_root_inode_ops.rmdir = &comx_rmdir; - - comx_normal_inode_ops.lookup = &comx_lookup; - - memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops, - sizeof(struct inode_operations)); - - comx_normal_file_ops.open = &comx_file_open; - comx_normal_file_ops.release = &comx_file_release; - - memcpy(&comx_debug_file_ops, &comx_normal_file_ops, - sizeof(struct file_operations)); - comx_debug_file_ops.llseek = &comx_debug_lseek; - comx_debug_file_ops.read = &comx_debug_read; - comx_root_dir = create_proc_entry("comx", S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, &proc_root); if (!comx_root_dir) @@ -1143,7 +1037,6 @@ int __init comx_init(void) return -ENOMEM; } - new_file->proc_iops = &comx_normal_inode_ops; new_file->data = new_file; new_file->read_proc = &comx_root_read_proc; new_file->write_proc = NULL; @@ -1154,7 +1047,6 @@ int __init comx_init(void) return -ENOMEM; } - new_file->proc_iops = &comx_normal_inode_ops; new_file->data = new_file; new_file->read_proc = &comx_root_read_proc; new_file->write_proc = NULL; @@ -1211,5 +1103,4 @@ EXPORT_SYMBOL(comx_lineup_func); EXPORT_SYMBOL(comx_status); EXPORT_SYMBOL(comx_rx); EXPORT_SYMBOL(comx_strcasecmp); -EXPORT_SYMBOL(comx_normal_inode_ops); EXPORT_SYMBOL(comx_root_dir); diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index ebf339d2ee9..6346873e887 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -112,7 +112,7 @@ int LMC_PKT_BUF_SZ = 1542; #ifdef MODULE static struct pci_device_id lmc_pci_tbl[] __devinitdata = { { 0x1011, 0x009, 0x1379, PCI_ANY_ID, 0, 0, 0}, - { 0 }, + { 0, } }; MODULE_DEVICE_TABLE(pci, lmc_pci_tbl); diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c index b6be90321d6..51fc0b9a67b 100644 --- a/drivers/net/wavelan.c +++ b/drivers/net/wavelan.c @@ -4211,6 +4211,7 @@ int init_module(void) break; } memset(dev, 0x00, sizeof(struct net_device)); + memcpy(dev->name, name[i], IFNAMSIZ); /* Copy name */ dev->base_addr = io[i]; dev->irq = irq[i]; dev->init = &wavelan_config; diff --git a/drivers/net/wavelan.p.h b/drivers/net/wavelan.p.h index 593b5655c72..3e20776fb21 100644 --- a/drivers/net/wavelan.p.h +++ b/drivers/net/wavelan.p.h @@ -695,8 +695,10 @@ static unsigned short iobase[] = /* Parameters set by insmod */ static int io[4] = { 0, 0, 0, 0 }; static int irq[4] = { 0, 0, 0, 0 }; +static char name[4][IFNAMSIZ] = { "", "", "", "" }; MODULE_PARM(io, "1-4i"); MODULE_PARM(irq, "1-4i"); +MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ)); #endif /* MODULE */ #endif /* WAVELAN_P_H */ diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 2be7d5823e1..4e2d6435e87 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -237,7 +237,7 @@ static struct chip_info chip_info[] = { static struct pci_device_id yellowfin_pci_tbl[] __devinitdata = { { 0x1000, 0x0702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, YELLOWFIN_GNIC }, { 0x1000, 0x0701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SYM83C885 }, - { 0, }, + { 0, } }; MODULE_DEVICE_TABLE (pci, yellowfin_pci_tbl); diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog index 1e09974c9d4..97b7c53c99d 100644 --- a/drivers/parport/ChangeLog +++ b/drivers/parport/ChangeLog @@ -1,3 +1,43 @@ +2000-05-16 Tim Waugh + + * share.c (parport_claim): Fix SMP race. + +2000-05-15 Gunther Mayer + + * parport_pc.c (parport_pc_compat_write_block_pio): Check for + timeouts. + (parport_pc_ecp_write_block_pio): Likewise. + (parport_pc_ecp_read_block_pio): Likewise. + +2000-05-02 Gunther Mayer + + * parport_pc.c: PCI SYBA patch and verbose PCI detection. + +2000-05-02 Gunther Mayer + + * parport_pc.c (decode_smsc): Fix SMSC 665/666 identification. + +2000-04-28 Tim Waugh + + * ieee1284.c: Short function descriptions can't be multiline. + + * daisy.c: Short function descriptions can't be multiline. + +2000-04-19 Tim Waugh + + * parport_pc.c (parport_pc_fifo_write_block_dma): Make maxlen + calculation a bit clearer. + + * ieee1284.c (parport_negotiate): Turn on data line drivers. + + * ieee1284_ops.c (parport_ieee1284_read_byte): Turn off data line + drivers. + (parport_ieee1284_write_compat): Turn on data line drivers. + + * daisy.c (assign_addrs): Turn on data line drivers. + (cpp_mux): Likewise. + (cpp_daisy): Likewise. + 2000-04-04 Tim Waugh * parport_pc.c: Add support for another PCI card. diff --git a/drivers/parport/daisy.c b/drivers/parport/daisy.c index ead3443b4f9..b99d5a344ed 100644 --- a/drivers/parport/daisy.c +++ b/drivers/parport/daisy.c @@ -252,8 +252,7 @@ void parport_close (struct pardevice *dev) } /** - * parport_device_num - convert device coordinates into a - * canonical device number + * parport_device_num - convert device coordinates * @parport: parallel port number * @mux: multiplexor port number (-1 for no multiplexor) * @daisy: daisy chain address (-1 for no daisy chain address) @@ -279,8 +278,7 @@ int parport_device_num (int parport, int mux, int daisy) } /** - * parport_device_coords - convert a canonical device number into - * device coordinates + * parport_device_coords - convert canonical device number * @devnum: device number * @parport: pointer to storage for parallel port number * @mux: pointer to storage for multiplexor port number @@ -325,6 +323,7 @@ static int cpp_daisy (struct parport *port, int cmd) { unsigned char s; + parport_data_forward (port); parport_write_data (port, 0xaa); udelay (2); parport_write_data (port, 0x55); udelay (2); parport_write_data (port, 0x00); udelay (2); @@ -373,6 +372,7 @@ static int cpp_mux (struct parport *port, int cmd) unsigned char s; int rc; + parport_data_forward (port); parport_write_data (port, 0xaa); udelay (2); parport_write_data (port, 0x55); udelay (2); parport_write_data (port, 0xf0); udelay (2); @@ -430,6 +430,7 @@ static int assign_addrs (struct parport *port) int thisdev = numdevs; char *deviceid; + parport_data_forward (port); parport_write_data (port, 0xaa); udelay (2); parport_write_data (port, 0x55); udelay (2); parport_write_data (port, 0x00); udelay (2); @@ -502,8 +503,7 @@ static int assign_addrs (struct parport *port) 'from' itself is skipped. */ /** - * parport_find_device - find a device with a specified - * manufacturer and model string + * parport_find_device - find a specific device * @mfg: required manufacturer string * @mdl: required model string * @from: previous device number found in search, or %NULL for diff --git a/drivers/parport/ieee1284.c b/drivers/parport/ieee1284.c index f25c50abdea..11f98bea035 100644 --- a/drivers/parport/ieee1284.c +++ b/drivers/parport/ieee1284.c @@ -380,6 +380,7 @@ int parport_negotiate (struct parport *port, int mode) udelay(1); /* Event 0: Set data */ + parport_data_forward (port); parport_write_data (port, m); udelay (400); /* Shouldn't need to wait this long. */ @@ -734,7 +735,6 @@ ssize_t parport_read (struct parport *port, void *buffer, size_t len) /** * parport_set_timeout - set the inactivity timeout for a device - * on a port * @dev: device on a port * @inactivity: inactivity timeout (in jiffies) * diff --git a/drivers/parport/ieee1284_ops.c b/drivers/parport/ieee1284_ops.c index a3b76ffbc18..cf5c5fb03c0 100644 --- a/drivers/parport/ieee1284_ops.c +++ b/drivers/parport/ieee1284_ops.c @@ -57,6 +57,7 @@ size_t parport_ieee1284_write_compat (struct parport *port, port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA; parport_write_control (port, ctl); + parport_data_forward (port); while (count < len) { long expire = jiffies + dev->timeout; long wait = (HZ + 99) / 100; @@ -268,6 +269,9 @@ size_t parport_ieee1284_read_byte (struct parport *port, break; } + /* Event 14: Place data bus in high impedance state. */ + parport_data_reverse (port); + /* Event 7: Set nAutoFd low. */ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 09255a3c106..a759b2d7462 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -704,6 +704,7 @@ size_t parport_pc_compat_write_block_pio (struct parport *port, int flags) { size_t written; + int r; /* Special case: a timeout of zero means we cannot call schedule(). */ if (!port->physport->cad->timeout) @@ -745,9 +746,14 @@ size_t parport_pc_compat_write_block_pio (struct parport *port, frob_econtrol (port, 0xe0, ECR_PS2 << 5); } - parport_wait_peripheral (port, - PARPORT_STATUS_BUSY, - PARPORT_STATUS_BUSY); + r = parport_wait_peripheral (port, + PARPORT_STATUS_BUSY, + PARPORT_STATUS_BUSY); + if (r) + printk (KERN_DEBUG + "%s: BUSY timeout (%d) in compat_write_block_pio\n", + port->name, r); + port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE; return written; @@ -760,6 +766,7 @@ size_t parport_pc_ecp_write_block_pio (struct parport *port, int flags) { size_t written; + int r; /* Special case: a timeout of zero means we cannot call schedule(). */ if (!port->physport->cad->timeout) @@ -772,9 +779,12 @@ size_t parport_pc_ecp_write_block_pio (struct parport *port, parport_frob_control (port, PARPORT_CONTROL_INIT, 0); /* Event 40: PError goes high. */ - parport_wait_peripheral (port, - PARPORT_STATUS_PAPEROUT, - PARPORT_STATUS_PAPEROUT); + r = parport_wait_peripheral (port, + PARPORT_STATUS_PAPEROUT, + PARPORT_STATUS_PAPEROUT); + if (r) + printk (KERN_DEBUG "%s: PError timeout (%d) " + "in ecp_write_block_pio\n", port->name, r); } /* Set up ECP parallel port mode.*/ @@ -818,18 +828,30 @@ size_t parport_pc_ecp_write_block_pio (struct parport *port, parport_pc_data_reverse (port); /* Must be in PS2 mode */ udelay (5); parport_frob_control (port, PARPORT_CONTROL_INIT, 0); - parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0); + r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0); + if (r) + printk (KERN_DEBUG "%s: PE,1 timeout (%d) " + "in ecp_write_block_pio\n", port->name, r); + parport_frob_control (port, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT); - parport_wait_peripheral (port, - PARPORT_STATUS_PAPEROUT, - PARPORT_STATUS_PAPEROUT); + r = parport_wait_peripheral (port, + PARPORT_STATUS_PAPEROUT, + PARPORT_STATUS_PAPEROUT); + if (r) + printk (KERN_DEBUG "%s: PE,2 timeout (%d) " + "in ecp_write_block_pio\n", port->name, r); } - parport_wait_peripheral (port, - PARPORT_STATUS_BUSY, - PARPORT_STATUS_BUSY); + r = parport_wait_peripheral (port, + PARPORT_STATUS_BUSY, + PARPORT_STATUS_BUSY); + if(r) + printk (KERN_DEBUG + "%s: BUSY timeout (%d) in ecp_write_block_pio\n", + port->name, r); + port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE; return written; @@ -840,6 +862,7 @@ size_t parport_pc_ecp_read_block_pio (struct parport *port, { size_t left = length; size_t fifofull; + int r; const int fifo = FIFO(port); const struct parport_pc_private *priv = port->physport->private_data; const int fifo_depth = priv->fifo_depth; @@ -882,7 +905,10 @@ size_t parport_pc_ecp_read_block_pio (struct parport *port, 0); /* Event 40: PError goes low */ - parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0); + r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0); + if (r) + printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) " + "in ecp_read_block_pio\n", port->name, r); } /* Set up ECP FIFO mode.*/ @@ -961,9 +987,14 @@ size_t parport_pc_ecp_read_block_pio (struct parport *port, /* Go to forward idle mode to shut the peripheral up. */ parport_frob_control (port, PARPORT_CONTROL_INIT, 0); - parport_wait_peripheral (port, - PARPORT_STATUS_PAPEROUT, - PARPORT_STATUS_PAPEROUT); + r = parport_wait_peripheral (port, + PARPORT_STATUS_PAPEROUT, + PARPORT_STATUS_PAPEROUT); + if (r) + printk (KERN_DEBUG + "%s: PE timeout FWDIDLE (%d) in ecp_read_block_pio\n", + port->name, r); + port->ieee1284.phase = IEEE1284_PH_FWD_IDLE; /* Finish up. */ @@ -1218,7 +1249,7 @@ static void __devinit decode_smsc(int efer, int key, int devid, int devrev) if (devid == devrev) /* simple heuristics, we happened to read some - non-winbond register */ + non-smsc register */ return; func=NULL; @@ -1228,8 +1259,8 @@ static void __devinit decode_smsc(int efer, int key, int devid, int devrev) if (id==0x0302) {type="37c669"; func=show_parconfig_smsc37c669;} else if (id==0x6582) type="37c665IR"; - else if ((id==0x6502) && (key==0x44)) type="37c665GT"; - else if ((id==0x6502) && (key==0x55)) type="37c666GT"; + else if (devid==0x65) type="37c665GT"; + else if (devid==0x66) type="37c666GT"; if(type==NULL) printk("SMSC unknown chip type\n"); @@ -2263,6 +2294,7 @@ enum parport_pc_pci_cards { plx_9050, afavlab_tk9902, timedia_1889, + syba_2p_epp, }; @@ -2270,9 +2302,11 @@ enum parport_pc_pci_cards { * (but offset by last_sio) */ static struct parport_pc_pci { int numports; - struct { + struct { /* BAR (base address registers) numbers in the config + space header */ int lo; - int hi; /* -ve if not there */ + int hi; /* -1 if not there, >6 for offset-method (max + BAR is 6) */ } addr[4]; } cards[] __devinitdata = { /* siig_1s1p_10x_550 */ { 1, { { 3, 4 }, } }, @@ -2301,6 +2335,9 @@ static struct parport_pc_pci { /* plx_9050 */ { 2, { { 4, -1 }, { 5, -1 }, } }, /* afavlab_tk9902 */ { 1, { { 0, 1 }, } }, /* timedia_1889 */ { 1, { { 2, -1 }, } }, + /* SYBA uses fixed offsets in + a 1K io window */ + /* syba_2p_epp */ { 2, { { 0, 0x078 }, { 0, 0x178 }, } }, }; static struct pci_device_id parport_pc_pci_tbl[] __devinitdata = { @@ -2360,7 +2397,8 @@ static struct pci_device_id parport_pc_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, afavlab_tk9902 }, { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, PCI_ANY_ID, PCI_ANY_ID, 0, 0, timedia_1889 }, - { 0, }, /* terminate list */ + { 0x1592, 0x0782, PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_2p_epp }, + { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl); @@ -2384,9 +2422,16 @@ static int __devinit parport_pc_pci_probe (struct pci_dev *dev, unsigned long io_lo, io_hi; io_lo = pci_resource_start (dev, lo); io_hi = 0; - if (hi >= 0) + if ((hi >= 0) && (hi <= 6)) io_hi = pci_resource_start (dev, hi); + else if (hi > 6) + io_lo += hi; /* Reinterpret the meaning of + "hi" as an offset (see SYBA + def.) */ /* TODO: test if sharing interrupts works */ + printk (KERN_DEBUG "PCI parallel port detected: %04x:%04x, " + "I/O at %#lx(%#lx)\n", parport_pc_pci_tbl[i].vendor, + parport_pc_pci_tbl[i].device, io_lo, io_hi); if (parport_pc_probe_port (io_lo, io_hi, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, dev)) count++; diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 63a185029fd..c85addc5601 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -602,6 +602,7 @@ parport_register_device(struct parport *port, const char *name, void parport_unregister_device(struct pardevice *dev) { struct parport *port; + unsigned long flags; #ifdef PARPORT_PARANOID if (dev == NULL) { @@ -614,11 +615,14 @@ void parport_unregister_device(struct pardevice *dev) port = dev->port->physport; + read_lock_irqsave (&port->cad_lock, flags); if (port->cad == dev) { + read_unlock_irqrestore (&port->cad_lock, flags); printk(KERN_DEBUG "%s: %s forgot to release port\n", port->name, dev->name); parport_release (dev); } + read_unlock_irqrestore (&port->cad_lock, flags); spin_lock(&port->pardevice_lock); if (dev->next) @@ -663,14 +667,17 @@ int parport_claim(struct pardevice *dev) struct parport *port = dev->port->physport; unsigned long flags; + read_lock_irqsave (&port->cad_lock, flags); if (port->cad == dev) { + read_unlock_irqrestore (&port->cad_lock, flags); printk(KERN_INFO "%s: %s already owner\n", dev->port->name,dev->name); return 0; } + read_unlock_irqrestore (&port->cad_lock, flags); -try_again: /* Preempt any current device */ + write_lock_irqsave (&port->cad_lock, flags); if ((oldcad = port->cad) != NULL) { if (oldcad->preempt) { if (oldcad->preempt(oldcad->private)) @@ -680,7 +687,9 @@ try_again: goto blocked; if (port->cad != oldcad) { - printk(KERN_WARNING + /* I think we'll actually deadlock rather than + get here, but just in case.. */ + printk(KERN_WARNING "%s: %s released port when preempted!\n", port->name, oldcad->name); if (port->cad) @@ -707,9 +716,7 @@ try_again: } /* Now we do the change of devices */ - write_lock_irqsave(&port->cad_lock, flags); port->cad = dev; - write_unlock_irqrestore(&port->cad_lock, flags); #ifdef CONFIG_PARPORT_1284 /* If it's a mux port, select it. */ @@ -729,6 +736,7 @@ try_again: /* Restore control registers */ port->ops->restore_state(port, dev->state); + write_unlock_irqrestore(&port->cad_lock, flags); dev->time = jiffies; return 0; @@ -736,13 +744,10 @@ blocked: /* If this is the first time we tried to claim the port, register an interest. This is only allowed for devices sleeping in parport_claim_or_block(), or those with a wakeup function. */ + + /* The cad_lock is still held for writing here */ if (dev->waiting & 2 || dev->wakeup) { - spin_lock_irqsave (&port->waitlist_lock, flags); - if (port->cad == NULL) { - /* The port got released in the meantime. */ - spin_unlock_irqrestore (&port->waitlist_lock, flags); - goto try_again; - } + spin_lock (&port->waitlist_lock); if (test_and_set_bit(0, &dev->waiting) == 0) { /* First add ourselves to the end of the wait list. */ dev->waitnext = NULL; @@ -753,8 +758,9 @@ blocked: } else port->waithead = port->waittail = dev; } - spin_unlock_irqrestore (&port->waitlist_lock, flags); + spin_unlock (&port->waitlist_lock); } + write_unlock_irqrestore (&port->cad_lock, flags); return -EAGAIN; } @@ -826,7 +832,9 @@ void parport_release(struct pardevice *dev) unsigned long flags; /* Make sure that dev is the current device */ + write_lock_irqsave(&port->cad_lock, flags); if (port->cad != dev) { + write_unlock_irqrestore (&port->cad_lock, flags); printk(KERN_WARNING "%s: %s tried to release parport " "when not owner\n", port->name, dev->name); return; @@ -846,7 +854,6 @@ void parport_release(struct pardevice *dev) } #endif - write_lock_irqsave(&port->cad_lock, flags); port->cad = NULL; write_unlock_irqrestore(&port->cad_lock, flags); @@ -863,7 +870,7 @@ void parport_release(struct pardevice *dev) return; } else if (pd->wakeup) { pd->wakeup(pd->private); - if (dev->port->cad) + if (dev->port->cad) /* racy but no matter */ return; } else { printk(KERN_ERR "%s: don't know how to wake %s\n", port->name, pd->name); diff --git a/drivers/pci/pci.ids b/drivers/pci/pci.ids index 15fff1fa86e..4eeeae9aab1 100644 --- a/drivers/pci/pci.ids +++ b/drivers/pci/pci.ids @@ -2268,10 +2268,13 @@ 1164 Advanced Peripherals Technologies 1165 Imagraph Corporation 0001 Motion TPEG Recorder/Player with audio -1166 Relience Computer +1166 ServerWorks 0007 CNB20-LE CPU to PCI Bridge 0008 CNB20HE - 0009 CNB20HE + 0009 CNB20LE + 0010 CIOB30 + 0011 CMIC_HE + 0201 CSB5 1167 Mutoh Industries Inc 1168 Thine Electronics Inc 1169 Centre for Development of Advanced Computing diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c index 857e8f043e9..9ce07690be1 100644 --- a/drivers/pcmcia/yenta.c +++ b/drivers/pcmcia/yenta.c @@ -564,7 +564,7 @@ static void yenta_clear_maps(pci_socket_t *socket) */ static void yenta_power_sense(pci_socket_t *socket) { - u32 status = config_readl(socket, CB_SOCKET_STATE); + u32 status = cb_readl(socket, CB_SOCKET_STATE); /* * Nothing inserted, nothing to sense.. diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index e757c435fb6..5a735401c98 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -19,64 +19,16 @@ extern int dasd_proc_read_statistics ( char *, char **, off_t, int); extern int dasd_proc_read_debug ( char *, char **, off_t, int); #endif /* DASD_PROFILE */ -struct proc_dir_entry dasd_proc_root_entry = { - 0, - 4,"dasd", - S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, - 1,0,0, - 0, - NULL, -}; - -struct proc_dir_entry dasd_proc_devices_entry = { - 0, - 7,"devices", - S_IFREG | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, - 1,0,0, - 0, - NULL, - &dasd_proc_read_devices, -}; - -#ifdef DASD_PROFILE -struct proc_dir_entry dasd_proc_stats_entry = { - 0, - 10,"statistics", - S_IFREG | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, - 1,0,0, - 0, - NULL, - &dasd_proc_read_statistics, -}; - -struct proc_dir_entry dasd_proc_debug_entry = { - 0, - 5,"debug", - S_IFREG | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, - 1,0,0, - 0, - NULL, - &dasd_proc_read_debug, -}; -#endif /* DASD_PROFILE */ - -struct proc_dir_entry dasd_proc_device_template = { - 0, - 6,"dd????", - S_IFBLK | S_IRUGO | S_IWUSR | S_IWGRP, - 1,0,0, - 0, - NULL, -}; +static struct proc_dir_entry *dasd_proc_root_entry; void dasd_proc_init ( void ) { - proc_register( & proc_root, & dasd_proc_root_entry); - proc_register( & dasd_proc_root_entry, & dasd_proc_devices_entry); + dasd_proc_root_entry = proc_mkdir("dasd", NULL); + create_proc_info_entry("devices",0,&dasd_proc_root_entry,dasd_proc_read_devices); #ifdef DASD_PROFILE - proc_register( & dasd_proc_root_entry, & dasd_proc_stats_entry); - proc_register( & dasd_proc_root_entry, & dasd_proc_debug_entry); + create_proc_info_entry("statistics",0,&dasd_proc_root_entry,dasd_proc_read_statistics); + create_proc_info_entry("debug",0,&dasd_proc_root_entry,dasd_proc_read_debug); #endif /* DASD_PROFILE */ } diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 3e491d886e8..aa61a55f8b2 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -4354,7 +4354,8 @@ Target Requested Completed Requested Completed Requested Completed\n\ HostAdapter, Length, BusLogic_MessageBufferSize); if ((Length -= Offset) <= 0) return 0; if (Length >= BytesAvailable) Length = BytesAvailable; - *StartPointer = &HostAdapter->MessageBuffer[Offset]; + memcpy(ProcBuffer, HostAdapter->MessageBuffer + Offset, Length); + *StartPointer = ProcBuffer; return Length; } diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index b11872c4217..240041cf043 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -13,9 +13,13 @@ * General Public License for more details. * * - * $Id: aha152x.c,v 2.0 1999/12/25 15:07:32 fischer Exp fischer $ + * $Id: aha152x.c,v 2.1 2000/05/17 16:23:17 fischer Exp fischer $ * * $Log: aha152x.c,v $ + * Revision 2.1 2000/05/17 16:23:17 fischer + * - signature update + * - fix for data out w/o scatter gather + * * Revision 2.0 1999/12/25 15:07:32 fischer * - interrupt routine completly reworked * - basic support for new eh code @@ -202,7 +206,7 @@ #include -#ifdef PCMCIA +#if defined(PCMCIA) #undef MODULE #endif @@ -275,7 +279,6 @@ #define DPRINTK(when,msgs...) #define DO_LOCK(flags) spin_lock_irqsave(&QLOCK,flags) #define DO_UNLOCK(flags) spin_unlock_irqrestore(&QLOCK,flags) -#define DEBUG_DEFAULT 0 #endif #define LEAD "(scsi%d:%d:%d) " @@ -290,6 +293,7 @@ (cmd) ? ((cmd)->lun & 0x07) : -1 #define DELAY_DEFAULT 100 +#define DEBUG_DEFAULT 0 /* possible irq range */ #if defined(PCMCIA) @@ -1714,7 +1718,9 @@ static void reset_ports(struct Scsi_Host *shpnt) */ int aha152x_host_reset(Scsi_Cmnd * SCpnt) { +#if defined(AHA152X_DEBUG) struct Scsi_Host *shpnt = SCpnt->host; +#endif DPRINTK(debug_eh, DEBUG_LEAD "aha152x_host_reset(%p)\n", CMDINFO(SCpnt), SCpnt); @@ -2731,14 +2737,19 @@ static void datao_end(struct Scsi_Host *shpnt) CURRENT_SC->resid += data_count; - data_count -= CURRENT_SC->SCp.ptr - CURRENT_SC->SCp.buffer->address; - while(data_count>0) { - CURRENT_SC->SCp.buffer--; - CURRENT_SC->SCp.buffers_residual++; - data_count -= CURRENT_SC->SCp.buffer->length; + if(CURRENT_SC->use_sg) { + data_count -= CURRENT_SC->SCp.ptr - CURRENT_SC->SCp.buffer->address; + while(data_count>0) { + CURRENT_SC->SCp.buffer--; + CURRENT_SC->SCp.buffers_residual++; + data_count -= CURRENT_SC->SCp.buffer->length; + } + CURRENT_SC->SCp.ptr = CURRENT_SC->SCp.buffer->address - data_count; + CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length + data_count; + } else { + CURRENT_SC->SCp.ptr -= data_count; + CURRENT_SC->SCp.this_residual += data_count; } - CURRENT_SC->SCp.ptr = CURRENT_SC->SCp.buffer->address - data_count; - CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length + data_count; } DPRINTK(debug_datao, DEBUG_LEAD "datao_end: request_bufflen=%d; resid=%d; stcnt=%d\n", diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 1a4360b7e03..f051d391ad7 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -1481,7 +1481,7 @@ int mega_findCard (Scsi_Host_Template * pHostTmpl, if ((flag & BOARD_QUARTZ) && (skip_id == -1)) { u16 magic; pci_read_config_word(pdev, PCI_CONF_AMISIG, &magic); - if (magic != AMI_SIGNATURE) + if ((magic != AMI_SIGNATURE) && (magic != AMI_SIGNATURE_471)) continue; /* not an AMI board */ } printk (KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x: in %s\n", diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index f650b5ae637..dc6207ba2fc 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -109,6 +109,7 @@ #define PCI_CONF_IRQ_OFFSET 0x3c #define PCI_CONF_AMISIG 0xa0 #define AMI_SIGNATURE 0x3344 +#define AMI_SIGNATURE_471 0xCCCC #if LINUX_VERSION_CODE < 0x20100 #define MEGARAID \ diff --git a/drivers/sound/ac97_codec.c b/drivers/sound/ac97_codec.c index 13e588c9853..b26c3a8d5d3 100644 --- a/drivers/sound/ac97_codec.c +++ b/drivers/sound/ac97_codec.c @@ -71,6 +71,7 @@ static struct { {0x83847605, "SigmaTel STAC9704" , NULL}, {0x83847608, "SigmaTel STAC9708" , NULL}, {0x83847609, "SigmaTel STAC9721/23" , sigmatel_init}, + {0x54524106, "TriTech TR28026" , NULL}, {0x54524108, "TriTech TR28028" , NULL}, {0x574D4C00, "Wolfson WM9704" , NULL}, {0x00000000, NULL, NULL} @@ -330,6 +331,10 @@ static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) /* else, write the first set in the mask as the output */ + /* clear out current set value first (AC97 supports only 1 input!) */ + val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT)&0x07]); + if (mask != val) mask &= ~val; + val = ffs(mask); val = ac97_oss_rm[val-1]; val |= val << 8; /* set both channels */ @@ -418,6 +423,7 @@ static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ if (!codec->recmask_io) return -EINVAL; + if(!val) return 0; if (!(val &= codec->record_sources)) return -EINVAL; codec->recmask_io(codec, 0, val); diff --git a/drivers/sound/cmpci.c b/drivers/sound/cmpci.c index 7067bbc1f84..df05c9b64bd 100644 --- a/drivers/sound/cmpci.c +++ b/drivers/sound/cmpci.c @@ -586,7 +586,7 @@ static void start_adc(struct cm_state *s) /* --------------------------------------------------------------------- */ -#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) #define DMABUF_MINORDER 1 static void dealloc_dmabuf(struct dmabuf *db) diff --git a/drivers/sound/dmasound/dmasound_awacs.c b/drivers/sound/dmasound/dmasound_awacs.c index 7af01f0c5c8..3893f9b58de 100644 --- a/drivers/sound/dmasound/dmasound_awacs.c +++ b/drivers/sound/dmasound/dmasound_awacs.c @@ -18,12 +18,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include "awacs_defs.h" #include "dmasound.h" @@ -1577,259 +1580,281 @@ awacs_enable_amp(int spkr_vol) * /dev/mixer abstraction */ -static int PMacMixerIoctl(u_int cmd, u_long arg) +static int awacs_mixer_ioctl(u_int cmd, u_long arg) { int data; - /* Different IOCTLS for burgundy*/ - if (awacs_revision < AWACS_BURGUNDY) { - switch (cmd) { - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD | SOUND_MASK_RECLEV - | SOUND_MASK_ALTPCM - | SOUND_MASK_MONITOR; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - data = 0; - if (awacs_reg[0] & MASK_MUX_AUDIN) - data |= SOUND_MASK_LINE; - if (awacs_reg[0] & MASK_MUX_MIC) - data |= SOUND_MASK_MIC; - if (awacs_reg[0] & MASK_MUX_CD) - data |= SOUND_MASK_CD; - if (awacs_reg[1] & MASK_LOOPTHRU) - data |= SOUND_MASK_MONITOR; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_CD - | SOUND_MASK_MONITOR); - awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC - | MASK_MUX_AUDIN); - awacs_reg[1] &= ~MASK_LOOPTHRU; - if (data & SOUND_MASK_LINE) - awacs_reg[0] |= MASK_MUX_AUDIN; - if (data & SOUND_MASK_MIC) - awacs_reg[0] |= MASK_MUX_MIC; - if (data & SOUND_MASK_CD) - awacs_reg[0] |= MASK_MUX_CD; - if (data & SOUND_MASK_MONITOR) - awacs_reg[1] |= MASK_LOOPTHRU; - awacs_write(awacs_reg[0] | MASK_ADDR0); - awacs_write(awacs_reg[1] | MASK_ADDR1); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_RECLEV; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_READ_VOLUME: - data = (awacs_reg[1] & MASK_AMUTE)? 0: - awacs_get_volume(awacs_reg[2], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, dmasound_set_volume(data)); - case SOUND_MIXER_READ_SPEAKER: - if (awacs_revision == 3 - && sys_ctrler == SYS_CTRLER_CUDA) - data = awacs_spkr_vol; - else - data = (awacs_reg[1] & MASK_CMUTE)? 0: - awacs_get_volume(awacs_reg[4], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_SPEAKER: - IOCTL_IN(arg, data); - if (awacs_revision == 3 - && sys_ctrler == SYS_CTRLER_CUDA) - awacs_enable_amp(data); - else - data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_LINE: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_AUDIN; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_AUDIN; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_LINE: - data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - data &= 0xff; - awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); - if (data >= 25) { - awacs_reg[0] |= MASK_MUX_MIC; - if (data >= 75) - awacs_reg[0] |= MASK_GAINLINE; - } - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_MIC: - data = (awacs_reg[0] & MASK_MUX_MIC)? - (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_CD: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_CD; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_CD: - data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = awacs_volume_setter(data, 0, 0, 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = awacs_get_volume(awacs_reg[0], 4); - return IOCTL_OUT(arg, data); + + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD | SOUND_MASK_RECLEV + | SOUND_MASK_ALTPCM + | SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + if (awacs_reg[1] & MASK_LOOPTHRU) + data |= SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD + | SOUND_MASK_MONITOR); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + awacs_reg[1] &= ~MASK_LOOPTHRU; + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + if (data & SOUND_MASK_MONITOR) + awacs_reg[1] |= MASK_LOOPTHRU; + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_VOLUME: + data = (awacs_reg[1] & MASK_AMUTE)? 0: + awacs_get_volume(awacs_reg[2], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, PMacSetVolume(data)); + case SOUND_MIXER_READ_SPEAKER: + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + data = awacs_spkr_vol; + else + data = (awacs_reg[1] & MASK_CMUTE)? 0: + awacs_get_volume(awacs_reg[4], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + awacs_enable_amp(data); + else + data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_AUDIN; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_AUDIN; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + data &= 0xff; + awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); + if (data >= 25) { + awacs_reg[0] |= MASK_MUX_MIC; + if (data >= 75) + awacs_reg[0] |= MASK_GAINLINE; } - } else { - /* We are, we are, we are... Burgundy or better */ - switch(cmd) { - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_CD | - SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - data = 0; - if (awacs_reg[0] & MASK_MUX_AUDIN) - data |= SOUND_MASK_LINE; - if (awacs_reg[0] & MASK_MUX_MIC) - data |= SOUND_MASK_MIC; - if (awacs_reg[0] & MASK_MUX_CD) - data |= SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_CD); - awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC - | MASK_MUX_AUDIN); - if (data & SOUND_MASK_LINE) - awacs_reg[0] |= MASK_MUX_AUDIN; - if (data & SOUND_MASK_MIC) - awacs_reg[0] |= MASK_MUX_MIC; - if (data & SOUND_MASK_CD) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(awacs_reg[0] | MASK_ADDR0); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_RECLEV | SOUND_MASK_CD - | SOUND_MASK_LINE; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); - /* Fall through */ - case SOUND_MIXER_READ_VOLUME: - return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); - case SOUND_MIXER_WRITE_SPEAKER: - IOCTL_IN(arg, data); - - if (!(data & 0xff)) { - /* Mute the left speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); - } else { - /* Unmute the left speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); - } - if (!(data & 0xff00)) { - /* Mute the right speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); - } else { - /* Unmute the right speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); - } + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = (awacs_reg[0] & MASK_MUX_MIC)? + (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_CD; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case MIXER_WRITE(SOUND_MASK_MONITOR): + IOCTL_IN(arg, data); + awacs_reg[1] &= ~MASK_LOOPTHRU; + if ((data & 0xff) >= 50) + awacs_reg[1] |= MASK_LOOPTHRU; + awacs_write(MASK_ADDR1 | awacs_reg[1]); + /* fall through */ + case MIXER_READ(SOUND_MASK_MONITOR): + data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0; + return IOCTL_OUT(arg, data); + } + return -EINVAL; +} - data = (((data&0xff)*16)/100 > 0xf ? 0xf : - (((data&0xff)*16)/100)) + - ((((data>>8)*16)/100 > 0xf ? 0xf : - ((((data>>8)*16)/100)))<<4); - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); - /* Fall through */ - case SOUND_MIXER_READ_SPEAKER: - data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); - data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); - return IOCTL_OUT(arg, ~data); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_LINE: - IOCTL_IN(arg, data); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); - - /* fall through */ - case SOUND_MIXER_READ_LINE: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - /* Mic is mono device */ - data = (data << 8) + (data << 24); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); - /* fall through */ - case SOUND_MIXER_READ_MIC: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); - data <<= 24; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_CD: - IOCTL_IN(arg, data); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); - /* fall through */ - case SOUND_MIXER_READ_CD: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = awacs_volume_setter(data, 0, 0, 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = awacs_get_volume(awacs_reg[0], 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_OUTMASK: - break; - case SOUND_MIXER_OUTSRC: - break; +static int burgundy_mixer_ioctl(u_int cmd, u_long arg) +{ + int data; + + /* We are, we are, we are... Burgundy or better */ + switch(cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(awacs_reg[0] | MASK_ADDR0); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV | SOUND_MASK_CD + | SOUND_MASK_LINE; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); + /* Fall through */ + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + + if (!(data & 0xff)) { + /* Mute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); + } else { + /* Unmute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); + } + if (!(data & 0xff00)) { + /* Mute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); + } else { + /* Unmute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); } + + data = (((data&0xff)*16)/100 > 0xf ? 0xf : + (((data&0xff)*16)/100)) + + ((((data>>8)*16)/100 > 0xf ? 0xf : + ((((data>>8)*16)/100)))<<4); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); + /* Fall through */ + case SOUND_MIXER_READ_SPEAKER: + data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); + data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); + return IOCTL_OUT(arg, ~data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); + + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + /* Mic is mono device */ + data = (data << 8) + (data << 24); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); + data <<= 24; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_OUTMASK: + break; + case SOUND_MIXER_OUTSRC: + break; } return -EINVAL; } +static int PMacMixerIoctl(u_int cmd, u_long arg) +{ + /* Different IOCTLS for burgundy*/ + if (awacs_revision >= AWACS_BURGUNDY) + return burgundy_mixer_ioctl(cmd, arg); + return awacs_mixer_ioctl(cmd, arg); +} + static void PMacWriteSqSetup(void) { diff --git a/drivers/sound/emu10k1/main.c b/drivers/sound/emu10k1/main.c index 5f06dc3680f..bce892fe33c 100644 --- a/drivers/sound/emu10k1/main.c +++ b/drivers/sound/emu10k1/main.c @@ -80,7 +80,7 @@ static char *card_names[] __devinitdata = { "EMU10K1", }; -static struct pci_device_id emu10k1_pci_tbl[] = { +static struct pci_device_id emu10k1_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_EMU10K1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, EMU10K1}, {0,} diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c index f4573477137..7f05925cd5e 100644 --- a/drivers/sound/es1370.c +++ b/drivers/sound/es1370.c @@ -2599,7 +2599,7 @@ static void __devinit es1370_remove(struct pci_dev *dev) static struct pci_device_id id_table[] __devinitdata = { { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, 0, 0, 0, 0, 0 } + { 0, } }; MODULE_DEVICE_TABLE(pci, id_table); diff --git a/drivers/sound/es1371.c b/drivers/sound/es1371.c index 8ae10a074f8..1623c98ba68 100644 --- a/drivers/sound/es1371.c +++ b/drivers/sound/es1371.c @@ -2804,7 +2804,7 @@ static struct pci_device_id id_table[] __devinitdata = { { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, { PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, 0, 0, 0, 0, 0 } + { 0, } }; MODULE_DEVICE_TABLE(pci, id_table); diff --git a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c index aa1fc77d19e..1c62eafed74 100644 --- a/drivers/sound/esssolo1.c +++ b/drivers/sound/esssolo1.c @@ -2335,7 +2335,7 @@ static void __devinit solo1_remove(struct pci_dev *dev) static struct pci_device_id id_table[] __devinitdata = { { PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, 0, 0, 0, 0, 0 } + { 0, } }; MODULE_DEVICE_TABLE(pci, id_table); diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c index 427167b3d75..a6e5448567a 100644 --- a/drivers/sound/sb_card.c +++ b/drivers/sound/sb_card.c @@ -249,6 +249,11 @@ static struct { ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0,0,0,0, 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, {"Sound Blaster 16", ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), @@ -364,6 +369,11 @@ static struct { ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0,0,0,0, 0,1,1,-1}, + {"ESS 1688", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), + 0,0,0,0, + 0,1,2,-1}, {"ESS 1868", ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), @@ -442,11 +452,6 @@ static struct { ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 1,0,0,0}, - {"Creative SB16 PnP", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, {0} }; diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c index 6588f3b3f6e..5aa31f50657 100644 --- a/drivers/sound/sonicvibes.c +++ b/drivers/sound/sonicvibes.c @@ -2634,7 +2634,7 @@ static void __devinit sv_remove(struct pci_dev *dev) static struct pci_device_id id_table[] __devinitdata = { { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, 0, 0, 0, 0, 0 } + { 0, } }; MODULE_DEVICE_TABLE(pci, id_table); diff --git a/drivers/sound/trident.c b/drivers/sound/trident.c index a1f462f3b34..6a3b8b4d6c1 100644 --- a/drivers/sound/trident.c +++ b/drivers/sound/trident.c @@ -29,6 +29,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * History + * v0.14.3 May 20 2000 Aaron Holtzman + * Fix kfree'd memory access in release + * Fix race in open while looking for a free virtual channel slot + * remove open_wait wq (which appears to be unused) * v0.14.2 Mar 29 2000 Ching Ling Lee * Add clear to silence advance in trident_update_ptr * fix invalid data of the end of the sound @@ -166,10 +170,6 @@ struct trident_state { unsigned int magic; struct trident_card *card; /* Card info */ - /* single open lock mechanism, only used for recording */ - struct semaphore open_sem; - wait_queue_head_t open_wait; - /* file mode */ mode_t open_mode; @@ -261,6 +261,9 @@ struct trident_card { /* We keep trident cards in a linked list */ struct trident_card *next; + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + /* The trident has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; @@ -1904,6 +1907,7 @@ static int trident_open(struct inode *inode, struct file *file) /* find an avaiable virtual channel (instance of /dev/dsp) */ while (card != NULL) { + down(&card->open_sem); for (i = 0; i < NR_HW_CH; i++) { if (card->states[i] == NULL) { state = card->states[i] = (struct trident_state *) @@ -1915,6 +1919,7 @@ static int trident_open(struct inode *inode, struct file *file) goto found_virt; } } + up(&card->open_sem); card = card->next; } /* no more virtual channel avaiable */ @@ -1939,10 +1944,8 @@ static int trident_open(struct inode *inode, struct file *file) state->card = card; state->magic = TRIDENT_STATE_MAGIC; init_waitqueue_head(&dmabuf->wait); - init_MUTEX(&state->open_sem); file->private_data = state; - down(&state->open_sem); /* set default sample format. According to OSS Programmer's Guide /dev/dsp should be default to unsigned 8-bits, mono, with sample rate 8kHz and @@ -1985,7 +1988,7 @@ static int trident_open(struct inode *inode, struct file *file) } state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&state->open_sem); + up(&card->open_sem); #ifdef DEBUG printk(KERN_ERR "trident: open virtual channel %d, hard channel %d\n", @@ -1999,6 +2002,7 @@ static int trident_open(struct inode *inode, struct file *file) static int trident_release(struct inode *inode, struct file *file) { struct trident_state *state = (struct trident_state *)file->private_data; + struct trident_card *card = state->card; struct dmabuf *dmabuf = &state->dmabuf; VALIDATE_STATE(state); @@ -2009,25 +2013,25 @@ static int trident_release(struct inode *inode, struct file *file) } /* stop DMA state machine and free DMA buffers/channels */ - down(&state->open_sem); + down(&card->open_sem); if (file->f_mode & FMODE_WRITE) { stop_dac(state); dealloc_dmabuf(state); state->card->free_pcm_channel(state->card, dmabuf->channel->num); } + if (file->f_mode & FMODE_READ) { stop_adc(state); dealloc_dmabuf(state); state->card->free_pcm_channel(state->card, dmabuf->channel->num); } - kfree(state->card->states[state->virt]); - state->card->states[state->virt] = NULL; - state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + card->states[state->virt] = NULL; + kfree(state); /* we're covered by the open_sem */ - up(&state->open_sem); + up(&card->open_sem); MOD_DEC_USE_COUNT; return 0; @@ -2408,6 +2412,7 @@ static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device card->banks[BANK_A].bitmap = 0UL; card->banks[BANK_B].addresses = &bank_b_addrs; card->banks[BANK_B].bitmap = 0UL; + init_MUTEX(&card->open_sem); spin_lock_init(&card->lock); devs = card; diff --git a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c index 47bac97d85f..e4403d9664a 100644 --- a/drivers/sound/via82cxxx_audio.c +++ b/drivers/sound/via82cxxx_audio.c @@ -305,7 +305,7 @@ static void via_chan_pcm_fmt (struct via_info *card, static struct pci_device_id via_pci_tbl[] __initdata = { { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, }, + { 0, } }; MODULE_DEVICE_TABLE(pci,via_pci_tbl); diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 3b376c36585..74253ed994f 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -43,7 +43,7 @@ comment 'USB Devices' dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB - dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB + dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI if [ "$CONFIG_USB_STORAGE" != "n" ]; then bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG fi diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 354c1d6cf92..40903a9c299 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -3,7 +3,7 @@ /* * audio.c -- USB Audio Class driver * - * Copyright (C) 1999 + * Copyright (C) 1999, 2000 * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * @@ -59,6 +59,21 @@ * 1999-12-20: Fix bad bug in conversion to per interface probing. * disconnect was called multiple times for the audio device, * leading to a premature freeing of the audio structures + * 2000-05-13: I don't remember who changed the find_format routine, + * but the change was completely broken for the Dallas + * chip. Anyway taking sampling rate into account in find_format + * is bad and should not be done unless there are devices with + * completely broken audio descriptors. Unless someone shows + * me such a descriptor, I will not allow find_format to + * take the sampling rate into account. + * Also, the former find_format made: + * - mpg123 play mono instead of stereo + * - sox completely fail for wav's with sample rates < 44.1kHz + * for the Dallas chip. + * Also fix a rather long standing problem with applications that + * use "small" writes producing no sound at all. + * 2000-05-15: My fears came true, the Philips camera indeed has pretty stupid + * audio descriptors. * */ @@ -441,9 +456,9 @@ static int dmabuf_init(struct dmabuf *db) db->bufsize = nr << PAGE_SHIFT; db->ready = 1; printk(KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " - "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d\n", + "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x\n", bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, - db->numfrag, db->dmasize, db->bufsize); + db->numfrag, db->dmasize, db->bufsize, db->format); return 0; } @@ -973,6 +988,8 @@ static int usbin_start(struct usb_audiodev *as) } spin_lock_irqsave(&as->lock, flags); } + if (u->dma.count <= 0 && !u->dma.mapped) + return 0; u->flags |= FLG_RUNNING; if (!(u->flags & FLG_URB0RUNNING)) { urb = &u->durb[0].urb; @@ -1332,6 +1349,8 @@ static int usbout_start(struct usb_audiodev *as) } spin_lock_irqsave(&as->lock, flags); } + if (u->dma.count <= 0 && !u->dma.mapped) + return 0; u->flags |= FLG_RUNNING; if (!(u->flags & FLG_URB0RUNNING)) { urb = &u->durb[0].urb; @@ -1395,30 +1414,39 @@ static int usbout_start(struct usb_audiodev *as) /* --------------------------------------------------------------------- */ -static unsigned int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int rate) +static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate) { - unsigned int i; + unsigned int g = 0; + + if (srate < afp->sratelo) + g += afp->sratelo - srate; + if (srate > afp->sratehi) + g += srate - afp->sratehi; + if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt)) + g += 0x100000; + if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt)) + g += 0x400000; + if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt)) + g += 0x100000; + if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt)) + g += 0x400000; + return g; +} + +static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate) +{ + unsigned int i, g, gb = ~0; + int j = -1; /* default to failure */ - /* first find an exact match, taking both format and sample rate into account, - but ignore stereo bit */ + /* find "best" format (according to format_goodness) */ for (i = 0; i < nr; i++) { - if (afp[i].format == (fmt & ~AFMT_STEREO) && rate >= afp[i].sratelo && rate <= afp[i].sratehi) - return i; + g = format_goodness(&afp[i], fmt, srate); + if (g >= gb) + continue; + j = i; + gb = g; } - - /* second find a match with the same stereo/mono and 8bit/16bit property */ - for (i = 0; i < nr; i++) - if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt) && - !AFMT_IS16BIT(afp[i].format) == !AFMT_IS16BIT(fmt) && - rate >= afp[i].sratelo && rate <= afp[i].sratehi) - return i; - /* third find a match with the same number of channels */ - for (i = 0; i < nr; i++) - if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt) && - rate >= afp[i].sratelo && rate <= afp[i].sratehi) - return i; - /* return failure */ - return -1; + return j; } static int set_format_in(struct usb_audiodev *as) @@ -1430,9 +1458,9 @@ static int set_format_in(struct usb_audiodev *as) struct usbin *u = &as->usbin; struct dmabuf *d = &u->dma; struct audioformat *fmt; - unsigned int fmtnr, ep; + unsigned int ep; unsigned char data[3]; - int ret; + int fmtnr, ret; if (u->interface < 0 || u->interface >= config->bNumInterfaces) return 0; @@ -1465,7 +1493,9 @@ static int set_format_in(struct usb_audiodev *as) d->srate = fmt->sratelo; if (d->srate > fmt->sratehi) d->srate = fmt->sratehi; -printk(KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#if 1 + printk(KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#endif if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) < 0) { printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", dev->devnum, u->interface, fmt->altsetting); @@ -1517,9 +1547,9 @@ static int set_format_out(struct usb_audiodev *as) struct usbout *u = &as->usbout; struct dmabuf *d = &u->dma; struct audioformat *fmt; - unsigned int fmtnr, ep; + unsigned int ep; unsigned char data[3]; - int ret; + int fmtnr, ret; if (u->interface < 0 || u->interface >= config->bNumInterfaces) return 0; @@ -1559,7 +1589,9 @@ static int set_format_out(struct usb_audiodev *as) d->srate = fmt->sratelo; if (d->srate > fmt->sratehi) d->srate = fmt->sratehi; -printk(KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#if 1 + printk(KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#endif if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", dev->devnum, u->interface, fmt->altsetting); @@ -1927,6 +1959,7 @@ static int drain_out(struct usb_audiodev *as, int nonblock) if (as->usbout.dma.mapped || !as->usbout.dma.ready) return 0; + usbout_start(as); add_wait_queue(&as->usbout.dma.wait, &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); @@ -2033,6 +2066,7 @@ static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t cou ssize_t ret = 0; unsigned long flags; unsigned int ptr; + unsigned int start_thr; int cnt, err; if (ppos != &file->f_pos) @@ -2043,10 +2077,11 @@ static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t cou return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES)); add_wait_queue(&as->usbout.dma.wait, &wait); while (count > 0) { #if 0 - printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%x\n", + printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%lx\n", count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize, as->usbout.flags, current->state); #endif @@ -2097,7 +2132,7 @@ static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t cou count -= cnt; buffer += cnt; ret += cnt; - if (usbout_start(as)) { + if (as->usbout.dma.count >= start_thr && usbout_start(as)) { if (!ret) ret = -ENODEV; break; @@ -2525,17 +2560,6 @@ static /*const*/ struct file_operations usb_audio_fops = { /* --------------------------------------------------------------------- */ -/* - * TO DO in order to get to the point of building an OSS interface - * structure, let alone playing music.. - * - * Use kmalloc/kfree for the descriptors we build - * Write the descriptor->OSS convertor code - * Figure how we deal with mixers - * Check alternate configurations. For now assume we will find one - * zero bandwidth (idle) config and one or more live one pers interface. - */ - static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum); static void usb_audio_disconnect(struct usb_device *dev, void *ptr); @@ -2543,25 +2567,11 @@ static struct usb_driver usb_audio_driver = { "audio", usb_audio_probe, usb_audio_disconnect, - /*{ NULL, NULL }, */ LIST_HEAD_INIT(usb_audio_driver.driver_list), + LIST_HEAD_INIT(usb_audio_driver.driver_list), NULL, 0 }; - -#if 0 -static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) -{ -#if 0 - struct usb_audio_device *aud = (struct usb_audio_device *)dev_id; - - printk(KERN_DEBUG "irq on %p\n", aud); -#endif - - return 1; -} -#endif - static void *find_descriptor(void *descstart, unsigned int desclen, void *after, u8 dtype, int iface, int altsetting) { @@ -3559,11 +3569,6 @@ static void usb_audio_disconnect(struct usb_device *dev, void *ptr) unregister_sound_mixer(ms->dev_mixer); ms->dev_mixer = -1; } -#if 0 - if(aud->irq_handle) - usb_release_irq(dev, aud->irq_handle, aud->irqpipe); - aud->irq_handle = NULL; -#endif release(s); wake_up(&open_wait); } diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 534325986c1..0b7cf6b0936 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -338,7 +338,7 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dbg("portstatus %x, change %x, %s", portstatus, portchange, - portstatus&(1<handle; struct input_dev **devptr = &input_dev; + struct input_handle *dnext; /* * Kill any pending repeat timers. @@ -256,9 +257,10 @@ void input_unregister_device(struct input_dev *dev) */ while (handle) { + dnext = handle->dnext; input_unlink_handle(handle); handle->handler->disconnect(handle); - handle = handle->dnext; + handle = dnext; } /* @@ -309,15 +311,17 @@ void input_unregister_handler(struct input_handler *handler) { struct input_handler **handlerptr = &input_handler; struct input_handle *handle = handler->handle; + struct input_handle *hnext; /* * Tell the handler to disconnect from all devices it keeps open. */ while (handle) { + hnext = handle->hnext; input_unlink_handle(handle); handler->disconnect(handle); - handle = handle->hnext; + handle = hnext; } /* diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index b047d07763a..e97410c32ec 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -6,7 +6,7 @@ * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000 * Snapshot code by Kevin Moore * OV7620 fixes by Charl P. Botha - * Changes by Claudio Matsuoka, claudio@conectiva.com, 3/26/2000 + * Changes by Claudio Matsuoka * * Based on the Linux CPiA driver written by Peter Pregler, * Scott J. Bertin and Johannes Erdfelt. @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.13"; +static const char version[] = "1.14"; #define __NO_VERSION__ @@ -64,6 +64,9 @@ static const char version[] = "1.13"; #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 +#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_RGB24 ? 384 : 256) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_RGB24 ? 24 : 8) + /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -90,9 +93,13 @@ static int sensor = 0; static int i2c_detect_tries = 5; /* For legal values, see the OV7610/7620 specs under register Common F, - upper nybble (set to 0-F) */ + * upper nybble (set to 0-F) */ static int aperture = -1; +/* Force image to be read in RGB instead of BGR. This option allow + * programs that expect RGB data (e.g. gqcam) to work with this driver. */ +static int force_rgb = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(fix_rgb_offset, "i"); @@ -100,8 +107,9 @@ MODULE_PARM(snapshot, "i"); MODULE_PARM(sensor, "i"); MODULE_PARM(i2c_detect_tries, "i"); MODULE_PARM(aperture, "i"); +MODULE_PARM(force_rgb, "i"); -MODULE_AUTHOR("Mark McClelland (and others)"); +MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); MODULE_DESCRIPTION("OV511 USB Camera Driver"); char kernel_version[] = UTS_RELEASE; @@ -126,6 +134,26 @@ static struct cam_list clist[] = { { -1, NULL } }; +static struct palette_list plist[] = { + { VIDEO_PALETTE_GREY, "GREY" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_RGB24, "RGB24" }, + { VIDEO_PALETTE_RGB32, "RGB32" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_YUV422P,"YUV422P" }, + { VIDEO_PALETTE_YUV411P,"YUV411P" }, + { VIDEO_PALETTE_YUV420P,"YUV420P" }, + { VIDEO_PALETTE_YUV410P,"YUV410P" }, + { -1, NULL } +}; + /********************************************************************** * * Memory management @@ -236,7 +264,8 @@ static void rvfree(void *mem, unsigned long size) **********************************************************************/ #ifdef CONFIG_PROC_FS -static struct proc_dir_entry *ov511_proc_root = NULL; +static struct proc_dir_entry *ov511_proc_entry = NULL; +static struct proc_dir_entry *video_proc_entry = NULL; #define YES_NO(x) ((x) ? "yes" : "no") @@ -244,7 +273,7 @@ static int ov511_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = page; - int i, len; + int i, j, len; struct usb_ov511 *ov511 = data; /* IMPORTANT: This output MUST be kept under PAGE_SIZE @@ -259,6 +288,10 @@ static int ov511_read_proc(char *page, char **start, off_t off, out += sprintf (out, "subcapture : %s\n", YES_NO (ov511->sub_flag)); out += sprintf (out, "sub_size : %d %d %d %d\n", ov511->subx, ov511->suby, ov511->subw, ov511->subh); + out += sprintf (out, "data_format : %s\n", force_rgb ? "RGB" : "BGR"); + out += sprintf (out, "brightness : %d\n", ov511->brightness >> 8); + out += sprintf (out, "colour : %d\n", ov511->colour >> 8); + out += sprintf (out, "contrast : %d\n", ov511->contrast >> 8); out += sprintf (out, "num_frames : %d\n", OV511_NUMFRAMES); for (i = 0; i < OV511_NUMFRAMES; i++) { out += sprintf (out, "frame : %d\n", i); @@ -266,27 +299,40 @@ static int ov511_read_proc(char *page, char **start, off_t off, ov511->frame[i].depth); out += sprintf (out, " size : %d %d\n", ov511->frame[i].width, ov511->frame[i].height); +#if 0 out += sprintf (out, " hdr_size : %d %d\n", ov511->frame[i].hdrwidth, ov511->frame[i].hdrheight); - out += sprintf (out, " format : %d\n", - ov511->frame[i].format); +#endif + out += sprintf (out, " format : "); + for (j = 0; plist[j].num >= 0; j++) { + if (plist[j].num == ov511->frame[i].format) { + out += sprintf (out, "%s\n", plist[j].name); + break; + } + } + if (plist[j].num < 0) + out += sprintf (out, "unknown\n"); out += sprintf (out, " segsize : %d\n", ov511->frame[i].segsize); + out += sprintf (out, " data_buffer : 0x%p\n", + ov511->frame[i].data); #if 0 - out += sprintf (out, " curline : %d\n", - ov511->frame[i].curline); - out += sprintf (out, " segment : %d\n", - ov511->frame[i].segment); - out += sprintf (out, " scanlength : %ld\n", - ov511->frame[i].scanlength); out += sprintf (out, " bytesread : %ld\n", ov511->frame[i].bytes_read); #endif } out += sprintf (out, "snap_enabled : %s\n", YES_NO (ov511->snap_enabled)); - out += sprintf (out, "bridge : %d\n", ov511->bridge); - out += sprintf (out, "sensor : %d\n", ov511->sensor); + out += sprintf (out, "bridge : %s\n", + ov511->bridge == BRG_OV511 ? "OV511" : + ov511->bridge == BRG_OV511PLUS ? "OV511+" : + "unknown"); + out += sprintf (out, "sensor : %s\n", + ov511->sensor == SEN_OV7610 ? "OV7610" : + ov511->sensor == SEN_OV7620 ? "OV7620" : + ov511->sensor == SEN_OV7620AE ? "OV7620AE" : + "unknown"); out += sprintf (out, "packet_size : %d\n", ov511->packet_size); + out += sprintf (out, "framebuffer : 0x%p\n", ov511->fbuf); len = out - page; len -= off; @@ -303,130 +349,7 @@ static int ov511_read_proc(char *page, char **start, off_t off, static int ov511_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { - int retval = -EINVAL; - -#if 0 - /* struct cam_data *cam = data; */ - struct usb_ov511 new_params; - int size = count; - int find_colon; - unsigned long val; - u32 command_flags = 0; - u8 new_mains; - - if (down_interruptible(&cam->param_lock)) - return -ERESTARTSYS; - - /* - * Skip over leading whitespace - */ - while (count && isspace(*buffer)) { - --count; - ++buffer; - } - - -#define MATCH(x) \ - ({ \ - int _len = strlen(x), _ret, _colon_found; \ - _ret = (_len <= count && strncmp(buffer, x, _len) == 0); \ - if (_ret) { \ - buffer += _len; \ - count -= _len; \ - if (find_colon) { \ - _colon_found = 0; \ - while (count && (*buffer == ' ' || *buffer == '\t' || \ - (!_colon_found && *buffer == ':'))) { \ - if (*buffer == ':') \ - _colon_found = 1; \ - --count; \ - ++buffer; \ - } \ - if (!count || !_colon_found) \ - retval = -EINVAL; \ - find_colon = 0; \ - } \ - } \ - _ret; \ - }) - -#define VALUE \ - ({ \ - char *_p; \ - unsigned long int _ret; \ - _ret = simple_strtoul(buffer, &_p, 0); \ - if (_p == buffer) \ - retval = -EINVAL; \ - else { \ - count -= _p - buffer; \ - buffer = _p; \ - } \ - _ret; \ - }) - - - retval = 0; - while (count && !retval) { - find_colon = 1; - - if (MATCH("")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - /* ... = val */ ; - else - retval = -EINVAL; - } - } else { - DBG("No match found\n"); - retval = -EINVAL; - } - - if (!retval) { - while (count && isspace(*buffer) && *buffer != '\n') { - --count; - ++buffer; - } - if (count) { - if (*buffer != '\n' && *buffer != ';') - retval = -EINVAL; - else { - --count; - ++buffer; - } - } - } - } - -#undef MATCH -#undef FIRMWARE_VERSION -#undef VALUE -#undef FIND_VALUE -#undef FIND_END - if (!retval) { - if (command_flags & COMMAND_SETCOLOURPARAMS) { - /* Adjust cam->vp to reflect these changes */ - cam->vp.brightness = - new_params.colourParams.brightness*65535/100; - cam->vp.contrast = - new_params.colourParams.contrast*65535/100; - cam->vp.colour = - new_params.colourParams.saturation*65535/100; - } - - memcpy(&cam->params, &new_params, sizeof(struct cam_params)); - cam->mainsFreq = new_mains; - cam->cmd_queue |= command_flags; - retval = size; - } else - PDEBUG(3, "error: %d\n", retval); - - up(&cam->param_lock); -#endif - - return retval; + return -EINVAL; } static void create_proc_ov511_cam (struct usb_ov511 *ov511) @@ -434,14 +357,15 @@ static void create_proc_ov511_cam (struct usb_ov511 *ov511) char name[7]; struct proc_dir_entry *ent; - PDEBUG (4, "***************"); - if (!ov511_proc_root || !ov511) + PDEBUG (4, "creating /proc/video/ov511/videoX entry"); + if (!ov511_proc_entry || !ov511) return; sprintf(name, "video%d", ov511->vdev.minor); - PDEBUG (4, "==== name: %s", name); + PDEBUG (4, "creating %s", name); - ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_root); + ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry); + if (!ent) return; @@ -460,26 +384,44 @@ static void destroy_proc_ov511_cam (struct usb_ov511 *ov511) return; sprintf(name, "video%d", ov511->vdev.minor); - PDEBUG (4, "==== name: %s", name); -#if 0 - remove_proc_entry(name, ov511_proc_root); + PDEBUG (4, "destroying %s", name); + remove_proc_entry(name, ov511_proc_entry); ov511->proc_entry = NULL; -#endif } static void proc_ov511_create(void) { - ov511_proc_root = create_proc_entry("ov511", S_IFDIR, 0); + struct proc_dir_entry *p = NULL; + + /* No current standard here. Alan prefers /proc/video/ as it keeps + * /proc "less cluttered than /proc/randomcardifoundintheshed/" + * -claudio + */ + PDEBUG (3, "creating /proc/video"); + video_proc_entry = proc_mkdir("video", p); + if (!video_proc_entry) { + if (!p) { + err("Unable to initialise /proc/video\n"); + return; + } else { /* FIXME - this doesn't work */ + PDEBUG (3, "/proc/video already exists"); + video_proc_entry = p; + } + } - if (ov511_proc_root) - ov511_proc_root->owner = THIS_MODULE; + ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry); + + if (ov511_proc_entry) + ov511_proc_entry->owner = THIS_MODULE; else - printk("Unable to initialise /proc/ov511\n"); /***********/ + err("Unable to initialise /proc/video/ov511\n"); } static void proc_ov511_destroy(void) { - remove_proc_entry("ov511", 0); + PDEBUG (3, "removing /proc/video/ov511"); + remove_proc_entry("ov511", video_proc_entry); + remove_proc_entry("video", NULL); } #endif /* CONFIG_PROC_FS */ @@ -510,7 +452,6 @@ static int ov511_reg_write(struct usb_device *dev, return rc; } - /* returns: negative is error, pos or zero is data */ static int ov511_reg_read(struct usb_device *dev, unsigned char reg) { @@ -533,7 +474,6 @@ static int ov511_reg_read(struct usb_device *dev, unsigned char reg) } } - static int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char value) @@ -580,7 +520,6 @@ error: return rc; } - /* returns: negative is error, pos or zero is data */ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg) { @@ -653,7 +592,6 @@ error: return rc; } - static int ov511_write_regvals(struct usb_device *dev, struct ov511_regvals * pRegvals) { @@ -682,9 +620,8 @@ error: return rc; } - -#if 0 -static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn) +#ifdef OV511_DEBUG +static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn) { int i; int rc; @@ -694,15 +631,13 @@ static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn) } } - -static void ov511_dump_i2c_regs( struct usb_device *dev) +static void ov511_dump_i2c_regs(struct usb_device *dev) { PDEBUG(3, "I2C REGS"); ov511_dump_i2c_range(dev, 0x00, 0x38); } - -static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn) +static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) { int i; int rc; @@ -712,8 +647,8 @@ static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn) } } - -static void ov511_dump_regs( struct usb_device *dev) +#if 0 +static void ov511_dump_regs(struct usb_device *dev) { PDEBUG(1, "CAMERA INTERFACE REGS"); ov511_dump_reg_range(dev, 0x10, 0x1f); @@ -736,7 +671,7 @@ static void ov511_dump_regs( struct usb_device *dev) } #endif - +#endif static int ov511_reset(struct usb_device *dev, unsigned char reset_type) { @@ -752,24 +687,21 @@ static int ov511_reset(struct usb_device *dev, unsigned char reset_type) return rc; } - /* Temporarily stops OV511 from functioning. Must do this before changing * registers while the camera is streaming */ static inline int ov511_stop(struct usb_device *dev) { - PDEBUG(4, "ov511_stop()"); + PDEBUG(4, "stopping"); return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d)); } - /* Restarts OV511 after ov511_stop() is called */ static inline int ov511_restart(struct usb_device *dev) { - PDEBUG(4, "ov511_restart()"); + PDEBUG(4, "restarting"); return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00)); } - static int ov511_set_packet_size(struct usb_ov511 *ov511, int size) { int alt, mult; @@ -844,9 +776,16 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) if (ov511_stop(dev) < 0) return -EIO; + ov511->contrast = p->contrast; + ov511->brightness = p->brightness; + ov511->colour = p->colour; + ov511->hue = p->hue; + ov511->whiteness = p->whiteness; + if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0) return -EIO; #if 0 + /* disable auto adjust mode */ if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0) return -EIO; #endif @@ -880,7 +819,6 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } - static inline int ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) { @@ -903,12 +841,10 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) p->hue = 0x8000; p->whiteness = 105 << 8; -#if 0 - p->depth = 3; /* Don't know if this is right */ -#else - p->depth = 24; -#endif - p->palette = VIDEO_PALETTE_RGB24; + + /* Can we get these from frame[0]? -claudio? */ + p->depth = ov511->frame[0].depth; + p->palette = ov511->frame[0].format; if (ov511_restart(dev) < 0) return -EIO; @@ -916,6 +852,22 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } +/* FIXME: add 176x144, 160x140 */ +static struct mode_list mlist[] = { + { 640, 480, VIDEO_PALETTE_GREY, 0x4f, 0x3d, 0x00, 0x00, + 0x4f, 0x3d, 0x00, 0x00, 0x04, 0x03, 0x24, 0x04, 0x9e }, + { 640, 480, VIDEO_PALETTE_RGB24,0x4f, 0x3d, 0x00, 0x00, + 0x4f, 0x3d, 0x00, 0x00, 0x06, 0x03, 0x24, 0x04, 0x9e }, + { 320, 240, VIDEO_PALETTE_GREY, 0x27, 0x1f, 0x00, 0x00, + 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e }, + { 320, 240, VIDEO_PALETTE_RGB24,0x27, 0x1f, 0x00, 0x00, + 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e }, + { 352, 288, VIDEO_PALETTE_GREY, 0x2b, 0x25, 0x00, 0x00, + 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 352, 288, VIDEO_PALETTE_RGB24,0x2b, 0x25, 0x00, 0x00, + 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 0, 0 } +}; static int ov511_mode_init_regs(struct usb_ov511 *ov511, @@ -925,6 +877,7 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, struct usb_device *dev = ov511->dev; int hwsbase = 0; int hwebase = 0; + int i; PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); @@ -975,6 +928,9 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, break; } +#if 0 + /* FIXME: subwindow support is currently broken! + */ if (width == 640 && height == 480) { if (sub_flag) { /* horizontal window start */ @@ -1018,41 +974,51 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, } } - ov511_reg_write(dev, 0x14, 0x00); - ov511_reg_write(dev, 0x15, 0x00); + ov511_reg_write(dev, 0x14, 0x00); /* Pixel divisor */ + ov511_reg_write(dev, 0x15, 0x00); /* Line divisor */ /* FIXME?? Shouldn't below be true only for YUV420? */ - ov511_reg_write(dev, 0x18, 0x03); + ov511_reg_write(dev, 0x18, 0x03); /* YUV420/422, YFIR */ - ov511_i2c_write(dev, 0x12, 0x24); - ov511_i2c_write(dev, 0x14, 0x04); + ov511_i2c_write(dev, 0x12, 0x24); /* Common A */ + ov511_i2c_write(dev, 0x14, 0x04); /* Common C */ /* 7620 doesn't have register 0x35, so play it safe */ if (ov511->sensor != SEN_OV7620) ov511_i2c_write(dev, 0x35, 0x9e); - } else if (width == 320 && height == 240) { - ov511_reg_write(dev, 0x12, 0x27); - ov511_reg_write(dev, 0x13, 0x1f); - ov511_reg_write(dev, 0x14, 0x00); - ov511_reg_write(dev, 0x15, 0x00); - ov511_reg_write(dev, 0x18, 0x03); +#endif + + for (i = 0; mlist[i].width; i++) { + if (width != mlist[i].width || + height != mlist[i].height || + mode != mlist[i].mode) + continue; + + ov511_reg_write(dev, 0x12, mlist[i].pxcnt); + ov511_reg_write(dev, 0x13, mlist[i].lncnt); + ov511_reg_write(dev, 0x14, mlist[i].pxdv); + ov511_reg_write(dev, 0x15, mlist[i].lndv); + ov511_reg_write(dev, 0x18, mlist[i].m420); /* Snapshot additions */ - ov511_reg_write(dev, 0x1a, 0x27); - ov511_reg_write(dev, 0x1b, 0x1f); - ov511_reg_write(dev, 0x1c, 0x00); - ov511_reg_write(dev, 0x1d, 0x00); + ov511_reg_write(dev, 0x1a, mlist[i].s_pxcnt); + ov511_reg_write(dev, 0x1b, mlist[i].s_lncnt); + ov511_reg_write(dev, 0x1c, mlist[i].s_pxdv); + ov511_reg_write(dev, 0x1d, mlist[i].s_lndv); - if (mode == VIDEO_PALETTE_GREY) { - ov511_i2c_write(dev, 0x11, 1); /* check */ - } else { - ov511_i2c_write(dev, 0x11, 1); /* check */ - } + ov511_i2c_write(dev, 0x11, mlist[i].clock); /* check */ - ov511_i2c_write(dev, 0x12, 0x04); - ov511_i2c_write(dev, 0x14, 0x24); - ov511_i2c_write(dev, 0x35, 0x1e); - } else { + ov511_i2c_write(dev, 0x12, mlist[i].common_A); + ov511_i2c_write(dev, 0x14, mlist[i].common_C); + + /* 7620 doesn't have register 0x35, so play it safe */ + if (ov511->sensor != SEN_OV7620) + ov511_i2c_write(dev, 0x35, mlist[i].common_L); + + break; + } + + if (mlist[i].width == 0) { err("Unknown mode (%d, %d): %d", width, height, mode); rc = -EINVAL; } @@ -1060,10 +1026,14 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, if (ov511_restart(ov511->dev) < 0) return -EIO; +#ifdef OV511_DEBUG + if (debug >= 5) + ov511_dump_i2c_regs(dev); +#endif + return rc; } - /********************************************************************** * * Color correction functions @@ -1093,32 +1063,38 @@ static inline void ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, int rowPixels, unsigned char * rgb) { - const double brightness = 1.0; // 0->black; 1->full scale - const double saturation = 1.0; // 0->greyscale; 1->full color + const double brightness = 1.0; /* 0->black; 1->full scale */ + const double saturation = 1.0; /* 0->greyscale; 1->full color */ const double fixScale = brightness * 256 * 256; const int rvScale = (int)(1.402 * saturation * fixScale); const int guScale = (int)(-0.344136 * saturation * fixScale); const int gvScale = (int)(-0.714136 * saturation * fixScale); const int buScale = (int)(1.772 * saturation * fixScale); const int yScale = (int)(fixScale); + int r, g, b; + + g = guScale * u + gvScale * v; + if (force_rgb) { + r = buScale * u; + b = rvScale * v; + } else { + r = rvScale * v; + b = buScale * u; + } - int r = rvScale * v; - int g = guScale * u + gvScale * v; - int b = buScale * u; yTL *= yScale; yTR *= yScale; yBL *= yScale; yBR *= yScale; - //Write out top two pixels + /* Write out top two pixels */ rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL); rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR); - //Skip down to next line to write out bottom two pixels + /* Skip down to next line to write out bottom two pixels */ rgb += 3 * rowPixels; rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL); rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR); } - /* * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. * The first 64 bytes of each segment are U, the next 64 are V. The U and @@ -1155,13 +1131,13 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, #undef OV511_DUMPPIX static void -ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, +ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iOutUV, int iHalf, int iWidth) { #ifndef OV511_DUMPPIX int k, l, m; - unsigned char * pIn; - unsigned char * pOut, * pOut1; + unsigned char *pIn; + unsigned char *pOut, *pOut1; /* Just copy the Y's if in the first stripe */ if (!iHalf) { @@ -1232,7 +1208,6 @@ ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, pOut += 8 * 3; } } - #else /* Just dump pix data straight out for debug */ int i, j; @@ -1249,7 +1224,6 @@ ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, #endif } - /* * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. * The segments represent 4 squares of 8x8 pixels as follows: @@ -1257,11 +1231,11 @@ ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 * 8 9 ... 15 72 73 ... 79 200 201 ... 207 * ... ... ... - * 56 57 ... 63 120 121 127 248 249 ... 255 + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 * */ static void -ov511_parse_data_grey(unsigned char * pIn0, unsigned char * pOut0, +ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iWidth) { int k, l, m; @@ -1276,13 +1250,12 @@ ov511_parse_data_grey(unsigned char * pIn0, unsigned char * pOut0, for (m = 0; m < 8; m++) { *pOut1++ = *pIn++; } - pOut1 += iWidth - 8; + pOut1 += iWidth - WDIV; } pOut += 8; } } - /* * fixFrameRGBoffset-- * My camera seems to return the red channel about 1 pixel @@ -1317,7 +1290,6 @@ static void fixFrameRGBoffset(struct ov511_frame *frame) } } - /********************************************************************** * * OV511 data transfer, IRQ handler @@ -1488,7 +1460,6 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) return totlen; } - static void ov511_isoc_irq(struct urb *urb) { int len; @@ -1520,7 +1491,6 @@ static void ov511_isoc_irq(struct urb *urb) return; } - static int ov511_init_isoc(struct usb_ov511 *ov511) { urb_t *urb; @@ -1595,7 +1565,6 @@ static int ov511_init_isoc(struct usb_ov511 *ov511) return 0; } - static void ov511_stop_isoc(struct usb_ov511 *ov511) { if (!ov511->streaming || !ov511->dev) @@ -1622,7 +1591,6 @@ static void ov511_stop_isoc(struct usb_ov511 *ov511) } } - static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) { struct ov511_frame *frame; @@ -1658,25 +1626,17 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) /* Make sure it's not too big */ if (width > DEFAULT_WIDTH) width = DEFAULT_WIDTH; -#if 0 - width = (width / 8) * 8; /* Multiple of 8 */ -#endif - width &= ~7L; + + width &= ~7L; /* Multiple of 8 */ if (height > DEFAULT_HEIGHT) height = DEFAULT_HEIGHT; -#if 0 - height = (height / 4) * 4; /* Multiple of 4 */ -#endif - width &= ~3L; -// /* We want a fresh frame every 30 we get */ -// ov511->compress = (ov511->compress + 1) % 30; + width &= ~3L; /* Multiple of 4 */ return 0; } - /**************************************************************************** * * V4L API @@ -1687,43 +1647,45 @@ static int ov511_open(struct video_device *dev, int flags) { int err = -EBUSY; struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int i; - PDEBUG(4, "ov511_open"); + PDEBUG(4, "opening"); down(&ov511->lock); - if (ov511->user) - goto out_unlock; - ov511->frame[0].grabstate = FRAME_UNUSED; - ov511->frame[1].grabstate = FRAME_UNUSED; + if (ov511->user) { + up(&ov511->lock); + return -EBUSY; + } err = -ENOMEM; /* Allocate memory for the frame buffers */ - ov511->fbuf = rvmalloc(2 * MAX_DATA_SIZE); + ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE); if (!ov511->fbuf) - goto open_err_ret; + return err; - ov511->frame[0].data = ov511->fbuf; - ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE; ov511->sub_flag = 0; - PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data); - PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data); + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].grabstate = FRAME_UNUSED; + ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE; + PDEBUG(4, "frame [%d] @ %p", i, ov511->frame[0].data); - ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ov511->sbuf[0].data) - goto open_err_on0; - ov511->sbuf[1].data = kmalloc(FRAMES_PER_DESC * MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ov511->sbuf[1].data) - goto open_err_on1; - - PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data); - PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data); + ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC * + MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ov511->sbuf[i].data) { +open_free_ret: + while (--i) kfree(ov511->sbuf[i].data); + rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); + return err; + } + PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data); + } err = ov511_init_isoc(ov511); if (err) - goto open_err_on2; + goto open_free_ret; ov511->user++; up(&ov511->lock); @@ -1731,24 +1693,12 @@ static int ov511_open(struct video_device *dev, int flags) MOD_INC_USE_COUNT; return 0; - -open_err_on2: - kfree (ov511->sbuf[1].data); -open_err_on1: - kfree (ov511->sbuf[0].data); -open_err_on0: - rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); -open_err_ret: - return err; -out_unlock: - up(&ov511->lock); - return err; } - static void ov511_close(struct video_device *dev) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int i; PDEBUG(4, "ov511_close"); @@ -1759,10 +1709,9 @@ static void ov511_close(struct video_device *dev) ov511_stop_isoc(ov511); - rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); - - kfree(ov511->sbuf[1].data); - kfree(ov511->sbuf[0].data); + rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); + for (i = 0; i < OV511_NUMFRAMES; i++) + kfree(ov511->sbuf[i].data); up(&ov511->lock); @@ -1772,7 +1721,6 @@ static void ov511_close(struct video_device *dev) } } - static int ov511_init_done(struct video_device *dev) { #ifdef CONFIG_PROC_FS @@ -1782,13 +1730,11 @@ static int ov511_init_done(struct video_device *dev) return 0; } - static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) { return -EINVAL; } - static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev; @@ -2097,7 +2043,6 @@ redo: return 0; } - static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; @@ -2138,10 +2083,6 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count, frame = &ov511->frame[frmx]; - /* FIXME */ - frame->segsize = frame->format == VIDEO_PALETTE_RGB24 ? 384 : 256; - frame->depth = frame->format == VIDEO_PALETTE_RGB24 ? 24 : 8; - restart: if (!ov511->dev) return -EIO; @@ -2165,11 +2106,8 @@ restart: /* Repeat until we get a snapshot frame */ - if (!ov511->snap_enabled) { - PDEBUG (4, "snap disabled"); - } else { + if (ov511->snap_enabled) PDEBUG (4, "Waiting snapshot frame"); - } if (ov511->snap_enabled && !frame->snapshot) { frame->bytes_read = 0; if (ov511_new_frame(ov511, frmx)) @@ -2178,13 +2116,11 @@ restart: } /* Clear the snapshot */ - if (ov511->snap_enabled) - PDEBUG (4, "Clear snapshot"); if (ov511->snap_enabled && frame->snapshot) { frame->snapshot = 0; - ov511_reg_write(ov511->dev, 0x52, 0x01); - ov511_reg_write(ov511->dev, 0x52, 0x03); - ov511_reg_write(ov511->dev, 0x52, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); } PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, @@ -2212,7 +2148,7 @@ restart: /* Mark it as available to be used again. */ ov511->frame[frmx].grabstate = FRAME_UNUSED; - if (ov511_new_frame(ov511, frmx ? 0 : 1)) + if (ov511_new_frame(ov511, !frmx)) err("ov511_new_frame returned error"); } @@ -2221,7 +2157,6 @@ restart: return count; } - static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long size) { @@ -2229,7 +2164,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long start = (unsigned long)adr; unsigned long page, pos; - if (!ov511->dev) + if (ov511->dev == NULL) return -EIO; PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); @@ -2238,8 +2173,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, return -EINVAL; pos = (unsigned long)ov511->fbuf; - while (size > 0) - { + while (size > 0) { page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; @@ -2254,7 +2188,6 @@ static int ov511_mmap(struct video_device *dev, const char *adr, return 0; } - static struct video_device ov511_template = { name: "OV511 USB Camera", type: VID_TYPE_CAPTURE, @@ -2268,7 +2201,6 @@ static struct video_device ov511_template = { initialize: ov511_init_done, }; - /**************************************************************************** * * OV511/OV7610 configuration @@ -2281,68 +2213,70 @@ static int ov76xx_configure(struct usb_ov511 *ov511) int i, success; int rc; - static struct ov511_regvals aRegvalsNorm7610[] = - {{OV511_I2C_BUS, 0x10, 0xff}, - {OV511_I2C_BUS, 0x16, 0x06}, - {OV511_I2C_BUS, 0x28, 0x24}, - {OV511_I2C_BUS, 0x2b, 0xac}, - {OV511_I2C_BUS, 0x05, 0x00}, - {OV511_I2C_BUS, 0x06, 0x00}, - {OV511_I2C_BUS, 0x12, 0x00}, - {OV511_I2C_BUS, 0x38, 0x81}, - {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */ - {OV511_I2C_BUS, 0x05, 0x00}, - {OV511_I2C_BUS, 0x0f, 0x05}, - {OV511_I2C_BUS, 0x15, 0x01}, - {OV511_I2C_BUS, 0x20, 0x1c}, - {OV511_I2C_BUS, 0x23, 0x2a}, - {OV511_I2C_BUS, 0x24, 0x10}, - {OV511_I2C_BUS, 0x25, 0x8a}, - {OV511_I2C_BUS, 0x27, 0xc2}, - {OV511_I2C_BUS, 0x29, 0x03}, /* 91 */ - {OV511_I2C_BUS, 0x2a, 0x04}, - {OV511_I2C_BUS, 0x2c, 0xfe}, - {OV511_I2C_BUS, 0x30, 0x71}, - {OV511_I2C_BUS, 0x31, 0x60}, - {OV511_I2C_BUS, 0x32, 0x26}, - {OV511_I2C_BUS, 0x33, 0x20}, - {OV511_I2C_BUS, 0x34, 0x48}, - {OV511_I2C_BUS, 0x12, 0x24}, - {OV511_I2C_BUS, 0x11, 0x01}, - {OV511_I2C_BUS, 0x0c, 0x24}, - {OV511_I2C_BUS, 0x0d, 0x24}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsNorm7610[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x06, 0x00 }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x38, 0x81 }, + { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x0f, 0x05 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x20, 0x1c }, + { OV511_I2C_BUS, 0x23, 0x2a }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x27, 0xc2 }, + { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */ + { OV511_I2C_BUS, 0x2a, 0x04 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, }; - static struct ov511_regvals aRegvalsNorm7620[] = - {{OV511_I2C_BUS, 0x10, 0xff}, - {OV511_I2C_BUS, 0x16, 0x06}, - {OV511_I2C_BUS, 0x28, 0x24}, - {OV511_I2C_BUS, 0x2b, 0xac}, - {OV511_I2C_BUS, 0x12, 0x00}, - {OV511_I2C_BUS, 0x28, 0x24}, - {OV511_I2C_BUS, 0x05, 0x00}, - {OV511_I2C_BUS, 0x0f, 0x05}, - {OV511_I2C_BUS, 0x15, 0x01}, - {OV511_I2C_BUS, 0x23, 0x00}, - {OV511_I2C_BUS, 0x24, 0x10}, - {OV511_I2C_BUS, 0x25, 0x8a}, - {OV511_I2C_BUS, 0x27, 0xe2}, - {OV511_I2C_BUS, 0x29, 0x03}, - {OV511_I2C_BUS, 0x2a, 0x00}, - {OV511_I2C_BUS, 0x2c, 0xfe}, - {OV511_I2C_BUS, 0x30, 0x71}, - {OV511_I2C_BUS, 0x31, 0x60}, - {OV511_I2C_BUS, 0x32, 0x26}, - {OV511_I2C_BUS, 0x33, 0x20}, - {OV511_I2C_BUS, 0x34, 0x48}, - {OV511_I2C_BUS, 0x12, 0x24}, - {OV511_I2C_BUS, 0x11, 0x01}, - {OV511_I2C_BUS, 0x0c, 0x24}, - {OV511_I2C_BUS, 0x0d, 0x24}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsNorm7620[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x0f, 0x05 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x23, 0x00 }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x27, 0xe2 }, + { OV511_I2C_BUS, 0x29, 0x03 }, + { OV511_I2C_BUS, 0x2a, 0x00 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, }; + PDEBUG (4, "starting configuration"); + if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, OV7610_I2C_WRITE_ID) < 0) return -1; @@ -2385,13 +2319,13 @@ static int ov76xx_configure(struct usb_ov511 *ov511) err("Error detecting sensor type"); return -1; } else if((rc & 3) == 3) { - printk("ov511: Sensor is an OV7610\n"); + info("Sensor is an OV7610"); ov511->sensor = SEN_OV7610; } else if((rc & 3) == 1) { - printk("ov511: Sensor is an OV7620AE\n"); + info("Sensor is an OV7620AE"); ov511->sensor = SEN_OV7620AE; } else if((rc & 3) == 0) { - printk("ov511: Sensor is an OV7620\n"); + info("Sensor is an OV7620"); ov511->sensor = SEN_OV7620; } else { err("Unknown image sensor version: %d", rc & 3); @@ -2399,13 +2333,15 @@ static int ov76xx_configure(struct usb_ov511 *ov511) } } else { /* sensor != 0; user overrode detection */ ov511->sensor = sensor; - printk("ov511: Sensor set to type %d\n", ov511->sensor); + info("Sensor set to type %d", ov511->sensor); } if (ov511->sensor == SEN_OV7620) { + PDEBUG(4, "Writing 7620 registers"); if (ov511_write_regvals(dev, aRegvalsNorm7620)) return -1; } else { + PDEBUG(4, "Writing 7610 registers"); if (ov511_write_regvals(dev, aRegvalsNorm7610)) return -1; } @@ -2438,34 +2374,35 @@ static int ov76xx_configure(struct usb_ov511 *ov511) static int ov511_configure(struct usb_ov511 *ov511) { struct usb_device *dev = ov511->dev; + int i; - static struct ov511_regvals aRegvalsInit[] = - {{OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f}, - {OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01}, - {OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f}, - {OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01}, - {OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f}, - {OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01}, - {OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsInit[] = { + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d }, + { OV511_DONE_BUS, 0x0, 0x00}, }; - static struct ov511_regvals aRegvalsNorm511[] = - {{OV511_REG_BUS, 0x20, 0x01}, - {OV511_REG_BUS, 0x52, 0x02}, - {OV511_REG_BUS, 0x52, 0x00}, - {OV511_REG_BUS, 0x31, 0x1f}, /* 0f */ - {OV511_REG_BUS, 0x70, 0x3f}, - {OV511_REG_BUS, 0x71, 0x3f}, - {OV511_REG_BUS, 0x72, 0x01}, - {OV511_REG_BUS, 0x73, 0x01}, - {OV511_REG_BUS, 0x74, 0x01}, - {OV511_REG_BUS, 0x75, 0x01}, - {OV511_REG_BUS, 0x76, 0x01}, - {OV511_REG_BUS, 0x77, 0x01}, - {OV511_REG_BUS, 0x78, 0x06}, - {OV511_REG_BUS, 0x79, 0x03}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsNorm511[] = { + { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, + { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, /* 0f */ + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x3f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x3f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 }, + { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00 }, }; memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); @@ -2488,16 +2425,15 @@ static int ov511_configure(struct usb_ov511 *ov511) /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ - ov511->frame[0].width = DEFAULT_WIDTH; - ov511->frame[0].height = DEFAULT_HEIGHT; - ov511->frame[0].depth = 24; /**************/ - ov511->frame[0].bytes_read = 0; - ov511->frame[0].segment = 0; - ov511->frame[1].width = DEFAULT_WIDTH; - ov511->frame[1].height = DEFAULT_HEIGHT; - ov511->frame[1].depth = 24; - ov511->frame[1].bytes_read = 0; - ov511->frame[1].segment = 0; + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].width = DEFAULT_WIDTH; + ov511->frame[i].height = DEFAULT_HEIGHT; + ov511->frame[i].depth = 24; + ov511->frame[i].bytes_read = 0; + ov511->frame[i].segment = 0; + ov511->frame[i].format = VIDEO_PALETTE_RGB24; + ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format); + } /* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */ @@ -2588,7 +2524,7 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) PDEBUG (4, "CustomID = %d", ov511->customid); for (i = 0; clist[i].id >= 0; i++) { if (ov511->customid == clist[i].id) { - printk ("Camera: %s\n", clist[i].description); + info("camera: %s", clist[i].description); ov511->desc = i; break; } @@ -2606,6 +2542,11 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) err("support for your camera."); } + /* Workaround for some applications that want data in RGB + * instead of BGR */ + if (force_rgb) + info("data format set to RGB"); + if (!ov511_configure(ov511)) { ov511->user = 0; init_MUTEX(&ov511->lock); /* to 1 == available */ @@ -2670,7 +2611,6 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr) } #ifdef CONFIG_PROC_FS - PDEBUG(3, "destroying /proc/ov511/video%d", ov511->vdev.minor); destroy_proc_ov511_cam(ov511); #endif @@ -2712,13 +2652,12 @@ static int __init usb_ov511_init(void) static void __exit usb_ov511_exit(void) { + usb_deregister(&ov511_driver); + info("ov511 driver deregistered"); + #ifdef CONFIG_PROC_FS - PDEBUG(3, "destroying /proc/ov511"); proc_ov511_destroy(); #endif - - usb_deregister(&ov511_driver); - info("ov511 driver deregistered"); } module_init(usb_ov511_init); diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h index 5b8ce967db7..d3a9a78bad6 100644 --- a/drivers/usb/ov511.h +++ b/drivers/usb/ov511.h @@ -1,3 +1,4 @@ + #ifndef __LINUX_OV511_H #define __LINUX_OV511_H @@ -9,7 +10,7 @@ #ifdef OV511_DEBUG # define PDEBUG(level, fmt, args...) \ -if (debug >= level) printk("ov511: [" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LINE__ , ## args) +if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) #else # define PDEBUG(level, fmt, args...) do {} while(0) #endif @@ -119,48 +120,55 @@ if (debug >= level) printk("ov511: [" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LI #define OV511PLUS_ALT_SIZE_961 7 /* OV7610 registers */ -#define OV7610_REG_GAIN 0x00 -#define OV7610_REG_BLUE 0x01 -#define OV7610_REG_RED 0x02 -#define OV7610_REG_SAT 0x03 -#define OV7610_REG_CNT 0x05 -#define OV7610_REG_BRT 0x06 -#define OV7610_REG_BLUE_BIAS 0x0C -#define OV7610_REG_RED_BIAS 0x0D -#define OV7610_REG_GAMMA_COEFF 0x0E -#define OV7610_REG_WB_RANGE 0x0F -#define OV7610_REG_EXP 0x10 -#define OV7610_REG_CLOCK 0x11 -#define OV7610_REG_COM_A 0x12 -#define OV7610_REG_COM_B 0x13 -#define OV7610_REG_COM_C 0x14 -#define OV7610_REG_COM_D 0x15 -#define OV7610_REG_FIELD_DIVIDE 0x16 -#define OV7610_REG_HWIN_START 0x17 -#define OV7610_REG_HWIN_END 0x18 -#define OV7610_REG_VWIN_START 0x19 -#define OV7610_REG_VWIN_END 0x1A -#define OV7610_REG_PIXEL_SHIFT 0x1B -#define OV7610_REG_ID_HIGH 0x1C -#define OV7610_REG_ID_LOW 0x1D -#define OV7610_REG_COM_E 0x20 -#define OV7610_REG_YOFFSET 0x21 -#define OV7610_REG_UOFFSET 0x22 -#define OV7610_REG_ECW 0x24 -#define OV7610_REG_ECB 0x25 -#define OV7610_REG_COM_F 0x26 -#define OV7610_REG_COM_G 0x27 -#define OV7610_REG_COM_H 0x28 -#define OV7610_REG_COM_I 0x29 -#define OV7610_REG_FRAMERATE_H 0x2A -#define OV7610_REG_FRAMERATE_L 0x2B -#define OV7610_REG_ALC 0x2C -#define OV7610_REG_COM_J 0x2D -#define OV7610_REG_VOFFSET 0x2E -#define OV7610_REG_YGAMMA 0x33 -#define OV7610_REG_BIAS_ADJUST 0x34 -#define OV7610_REG_COM_L 0x35 -#define OV7610_REG_COM_K 0x38 +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_BLUE 0x01 /* blue channel balance */ +#define OV7610_REG_RED 0x02 /* red channel balance */ +#define OV7610_REG_SAT 0x03 /* saturation */ + /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ + /* 08-0b reserved */ +#define OV7610_REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */ +#define OV7610_REG_RED_BIAS 0x0D /* read channel bias (5:0) */ +#define OV7610_REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define OV7610_REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define OV7610_REG_EXP 0x10 /* manual exposure setting */ +#define OV7610_REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define OV7610_REG_COM_A 0x12 /* misc common regs */ +#define OV7610_REG_COM_B 0x13 /* misc common regs */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_COM_D 0x15 /* misc common regs */ +#define OV7610_REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define OV7610_REG_HWIN_START 0x17 /* horizontal window start */ +#define OV7610_REG_HWIN_END 0x18 /* horizontal window end */ +#define OV7610_REG_VWIN_START 0x19 /* vertical window start */ +#define OV7610_REG_VWIN_END 0x1A /* vertical window end */ +#define OV7610_REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define OV7610_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1D /* manufacturer ID LSB */ + /* 0e-0f reserved */ +#define OV7610_REG_COM_E 0x20 /* misc common regs */ +#define OV7610_REG_YOFFSET 0x21 /* Y channel offset */ +#define OV7610_REG_UOFFSET 0x22 /* U channel offset */ + /* 23 reserved */ +#define OV7610_REG_ECW 0x24 /* Exposure white level for AEC */ +#define OV7610_REG_ECB 0x25 /* Exposure black level for AEC */ +#define OV7610_REG_COM_F 0x26 /* misc settings */ +#define OV7610_REG_COM_G 0x27 /* misc settings */ +#define OV7610_REG_COM_H 0x28 /* misc settings */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ +#define OV7610_REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define OV7610_REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define OV7610_REG_ALC 0x2C /* Auto Level Control settings */ +#define OV7610_REG_COM_J 0x2D /* misc settings */ +#define OV7610_REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define OV7610_REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */ + /* 30-32 reserved */ +#define OV7610_REG_YGAMMA 0x33 /* misc gamma settings (7:6) */ +#define OV7610_REG_BIAS_ADJUST 0x34 /* misc bias settings */ +#define OV7610_REG_COM_L 0x35 /* misc settings */ + /* 36-37 reserved */ +#define OV7610_REG_COM_K 0x38 /* misc registers */ #define STREAM_BUF_SIZE (PAGE_SIZE * 4) @@ -171,8 +179,7 @@ if (debug >= level) printk("ov511: [" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LI #define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */ #define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ -// FIXME - should this be 0x81 (endpoint address) or 0x01 (endpoint number)? -#define OV511_ENDPOINT_ADDRESS 0x81 /* Address of isoc endpoint */ +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ // CAMERA SPECIFIC // FIXME - these can vary between specific models @@ -223,9 +230,9 @@ enum { struct ov511_regvals { enum { - OV511_DONE_BUS, - OV511_REG_BUS, - OV511_I2C_BUS, + OV511_DONE_BUS, + OV511_REG_BUS, + OV511_I2C_BUS, } bus; unsigned char reg; unsigned char val; @@ -269,15 +276,16 @@ struct usb_ov511 { /* Device structure */ struct usb_device *dev; -#if 0 - unsigned char customid; /* Type of camera */ -#else int customid; int desc; -#endif - unsigned char iface; + int brightness; + int colour; + int contrast; + int hue; + int whiteness; + struct semaphore lock; int user; /* user count for exclusive use */ @@ -318,11 +326,34 @@ struct usb_ov511 { struct proc_dir_entry *proc_entry; /* /proc/ov511/videoX */ }; - struct cam_list { int id; char *description; }; +struct palette_list { + int num; + char *name; +}; + +struct mode_list { + int width; + int height; + int mode; + u8 pxcnt; + u8 lncnt; + u8 pxdv; + u8 lndv; + u8 s_pxcnt; + u8 s_lncnt; + u8 s_pxdv; + u8 s_lndv; + u8 clock; + u8 m420; + u8 common_A; + u8 common_C; + u8 common_L; +}; + #endif diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 7f4459d2f0b..b40a947edc8 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -16,7 +16,7 @@ #include -static const char *version = __FILE__ ": v0.3.9 2000/04/11 Written by Petko Manolov (petkan@spct.net)\n"; +static const char *version = __FILE__ ": v0.3.12 2000/05/22 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; #define PEGASUS_MTU 1500 @@ -24,12 +24,15 @@ static const char *version = __FILE__ ": v0.3.9 2000/04/11 Written by Petko Mano #define SROM_WRITE 0x01 #define SROM_READ 0x02 #define PEGASUS_TX_TIMEOUT (HZ*5) +#define PEGASUS_RESET 1 #define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) + struct pegasus { struct usb_device *usb; struct net_device *net; struct net_device_stats stats; + int flags; spinlock_t pegasus_lock; struct urb rx_urb, tx_urb, intr_urb; unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); @@ -44,9 +47,11 @@ struct usb_eth_dev { void *private; }; + static int loopback = 0; static int multicast_filter_limit = 32; + MODULE_AUTHOR("Petko Manolov "); MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); MODULE_PARM(loopback, "i"); @@ -98,6 +103,7 @@ static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regd return 1; } + static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) { int i; @@ -115,6 +121,7 @@ static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regd return 1; } + static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction) { int i; @@ -134,6 +141,7 @@ static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retda return 1; } + static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) { int i; @@ -143,6 +151,7 @@ static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) return 0; } + static int pegasus_reset_mac(struct usb_device *dev) { __u8 data = 0x8; @@ -165,6 +174,7 @@ static int pegasus_reset_mac(struct usb_device *dev) return 1; } + static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) { __u16 partmedia, temp; @@ -195,13 +205,14 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) data[0] = 0xc9; data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); - data[2] = (loopback & 1) ? 0x08 : 0x00; + data[2] = (loopback & 1) ? 0x09 : 0x01; pegasus_set_registers(usb, 0, 3, data); return 0; } + static void pegasus_read_bulk(struct urb *urb) { struct pegasus *pegasus = urb->context; @@ -253,15 +264,16 @@ goon: warn("(prb)failed rx_urb %d", res); } + static void pegasus_irq(urb_t *urb) { - if(urb->status) { - __u8 *d = urb->transfer_buffer; - printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x", - d[0], d[1], d[2], d[3], d[4], d[5]); - } + __u8 *d = urb->transfer_buffer; + + if ( d[0] ) + dbg("txst0=0x%2x", d[0]); } + static void pegasus_write_bulk(struct urb *urb) { struct pegasus *pegasus = urb->context; @@ -280,12 +292,15 @@ static void pegasus_tx_timeout(struct net_device *net) struct pegasus *pegasus = net->priv; warn("%s: Tx timed out. Reseting...", net->name); + usb_unlink_urb(&pegasus->tx_urb); pegasus->stats.tx_errors++; net->trans_start = jiffies; + pegasus->flags |= PEGASUS_RESET; netif_wake_queue(net); } + static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) { struct pegasus *pegasus = net->priv; @@ -317,11 +332,13 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) return 0; } + static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) { return &((struct pegasus *)dev->priv)->stats; } + static int pegasus_open(struct net_device *net) { struct pegasus *pegasus = (struct pegasus *)net->priv; @@ -334,8 +351,10 @@ static int pegasus_open(struct net_device *net) if ((res = usb_submit_urb(&pegasus->rx_urb))) warn("(open)failed rx_urb %d", res); - -/* usb_submit_urb(&pegasus->intr_urb);*/ + + if ((res = usb_submit_urb(&pegasus->intr_urb))) + warn("(open)failed intr_urb %d", res); + netif_start_queue(net); MOD_INC_USE_COUNT; @@ -343,6 +362,7 @@ static int pegasus_open(struct net_device *net) return 0; } + static int pegasus_close(struct net_device *net) { struct pegasus *pegasus = net->priv; @@ -351,13 +371,14 @@ static int pegasus_close(struct net_device *net) usb_unlink_urb(&pegasus->rx_urb); usb_unlink_urb(&pegasus->tx_urb); -/* usb_unlink_urb(&pegasus->intr_urb); */ + usb_unlink_urb(&pegasus->intr_urb); MOD_DEC_USE_COUNT; return 0; } + static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) { __u16 *data = (__u16 *)&rq->ifr_data; @@ -379,6 +400,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) } } + static void pegasus_set_rx_mode(struct net_device *net) { struct pegasus *pegasus = net->priv; @@ -400,6 +422,7 @@ static void pegasus_set_rx_mode(struct net_device *net) netif_wake_queue(net); } + static int check_device_ids( __u16 vendor, __u16 product ) { int i=0; @@ -413,6 +436,7 @@ static int check_device_ids( __u16 vendor, __u16 product ) return -1; } + static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) { struct net_device *net; @@ -463,7 +487,7 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk, pegasus); FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), - pegasus->intr_buff, 8, pegasus_irq, pegasus, 250); + pegasus->intr_buff, 8, pegasus_irq, pegasus, 500); printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name); @@ -471,6 +495,7 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) return pegasus; } + static void pegasus_disconnect(struct usb_device *dev, void *ptr) { struct pegasus *pegasus = ptr; @@ -487,11 +512,12 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) usb_unlink_urb(&pegasus->rx_urb); usb_unlink_urb(&pegasus->tx_urb); -/* usb_unlink_urb(&pegasus->intr_urb);*/ + usb_unlink_urb(&pegasus->intr_urb); kfree(pegasus); } + static struct usb_driver pegasus_driver = { name: "pegasus", probe: pegasus_probe, diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index d64258e8ed0..c013a815402 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -1,5 +1,5 @@ /* - * printer.c Version 0.3 + * printer.c Version 0.4 * * Copyright (c) 1999 Michael Gee * Copyright (c) 1999 Pavel Machek @@ -13,6 +13,7 @@ * v0.1 - thorough cleaning, URBification, almost a rewrite * v0.2 - some more cleanups * v0.3 - cleaner again, waitqueue fixes + * v0.4 - fixes in unidirectional mode */ /* @@ -102,7 +103,7 @@ static void usblp_bulk(struct urb *urb) return; if (urb->status) - warn("nonzero read bulk status received: %d", urb->status); + warn("nonzero read/write bulk status received: %d", urb->status); wake_up_interruptible(&usblp->wait); } @@ -172,9 +173,12 @@ static int usblp_open(struct inode *inode, struct file *file) usblp->writeurb.transfer_buffer_length = 0; usblp->writeurb.status = 0; - usblp->readcount = 0; - usb_submit_urb(&usblp->readurb); + if (usblp->bidir) { + usblp->readcount = 0; + usb_submit_urb(&usblp->readurb); + } + return 0; } @@ -185,7 +189,8 @@ static int usblp_release(struct inode *inode, struct file *file) usblp->used = 0; if (usblp->dev) { - usb_unlink_urb(&usblp->readurb); + if (usblp->bidir) + usb_unlink_urb(&usblp->readurb); usb_unlink_urb(&usblp->writeurb); MOD_DEC_USE_COUNT; return 0; @@ -203,8 +208,8 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait { struct usblp *usblp = file->private_data; poll_wait(file, &usblp->wait, wait); - return (usblp->readurb.status == -EINPROGRESS ? 0 : POLLIN | POLLRDNORM) - | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); + return ((usblp->bidir || usblp->readurb.status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) + | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); } static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) @@ -315,7 +320,6 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) int minor, i, alts = -1, bidir = 0; char *buf; - for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) { interface = &dev->actconfig->interface[ifnum].altsetting[i]; @@ -342,22 +346,21 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) err("can't set desired altsetting %d on interface %d", alts, ifnum); epwrite = interface->endpoint + 0; - epread = NULL; - - if (bidir) { - epread = interface->endpoint + 1; - if ((epread->bEndpointAddress & 0x80) != 0x80) { - epwrite = interface->endpoint + 1; - epread = interface->endpoint + 0; + epread = bidir ? interface->endpoint + 1 : NULL; - if ((epread->bEndpointAddress & 0x80) != 0x80) - return NULL; - } + if ((epwrite->bEndpointAddress & 0x80) == 0x80) { + if (interface->bNumEndpoints == 1) + return NULL; + epwrite = interface->endpoint + 1; + epread = bidir ? interface->endpoint + 0 : NULL; } if ((epwrite->bEndpointAddress & 0x80) == 0x80) return NULL; + if (bidir && (epread->bEndpointAddress & 0x80) != 0x80) + return NULL; + for (minor = 0; minor < USBLP_MINORS && usblp_table[minor]; minor++); if (usblp_table[minor]) { err("no more free usblp devices"); @@ -386,10 +389,9 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), buf, 0, usblp_bulk, usblp); - if (bidir) { + if (bidir) FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp); - } info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts); @@ -408,8 +410,9 @@ static void usblp_disconnect(struct usb_device *dev, void *ptr) usblp->dev = NULL; - usb_unlink_urb(&usblp->readurb); usb_unlink_urb(&usblp->writeurb); + if (usblp->bidir) + usb_unlink_urb(&usblp->readurb); kfree(usblp->writeurb.transfer_buffer); diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index fa69fa52aa0..7d24c34f915 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -4,8 +4,26 @@ O_TARGET := usb-serial.o M_OBJS := usb-serial.o -O_OBJS := usbserial.o visor.o whiteheat.o ftdi_sio.o keyspan_pda.o omninet.o digi_acceleport.o +O_OBJS := usbserial.o MOD_LIST_NAME := USB_SERIAL_MODULES +ifeq ($(CONFIG_USB_SERIAL_VISOR),y) + O_OBJS += visor.o +endif +ifeq ($(CONFIG_USB_SERIAL_WHITEHEAT),y) + O_OBJS += whiteheat.o +endif +ifeq ($(CONFIG_USB_SERIAL_FTDI_SIO),y) + O_OBJS += ftdi_sio.o +endif +ifeq ($(CONFIG_USB_SERIAL_KEYSPAN_PDA),y) + O_OBJS += keyspan_pda.o +endif +ifeq ($(CONFIG_USB_SERIAL_OMNINET),y) + O_OBJS += omninet.o +endif +ifeq ($(CONFIG_USB_SERIAL_DIGI_ACCELEPORT),y) + O_OBJS += digi_acceleport.o +endif + include $(TOPDIR)/Rules.make - diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 6700f9e0103..fe2d2013b32 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -14,16 +14,39 @@ * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com) * +* (5/16/2000) pberger and borchers +* -- added timeouts to sleeps +* -- handle transition to/from B0 in digi_set_termios +* +* (5/13/2000) pberger and borchers +* -- all commands now sent on out of band port, using digi_write_oob +* -- get modem control signals whenever they change, support TIOCMGET/ +* SET/BIS/BIC ioctls +* -- digi_set_termios now supports parity, word size, stop bits, and +* receive enable +* -- cleaned up open and close, use digi_set_termios and digi_write_oob +* to set port parameters +* -- added digi_startup_device to start read chains on all ports +* -- write buffer is only used when count==1, to be sure put_char can +* write a char (unless the buffer is full) +* +* (5/10/2000) pberger and borchers +* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls +* -- Fixed problem where the first incoming character is lost on +* port opens after the first close on that port. Now we keep +* the read_urb chain open until shutdown. +* -- Added more port conditioning calls in digi_open and digi_close. +* -- Convert port->active to a use count so that we can deal with multiple +* opens and closes properly. +* -- Fixed some problems with the locking code. +* * (5/3/2000) pberger and borchers -* First alpha version of the driver--many known limitations and bugs. +* -- First alpha version of the driver--many known limitations and bugs. * -* $Id: digi_acceleport.c,v 1.28 2000/05/04 01:47:08 root Exp root $ +* $Id: digi_acceleport.c,v 1.43 2000/05/17 03:21:38 root Exp root $ */ #include - -#ifdef CONFIG_USB_SERIAL_DIGI_ACCELEPORT - #include #include #include @@ -37,45 +60,57 @@ #include #include #include +#include +#include "usb-serial.h" #ifdef CONFIG_USB_SERIAL_DEBUG #define DEBUG #else #undef DEBUG #endif -#include -#include "usb-serial.h" /* Defines */ /* port buffer length -- must be <= transfer buffer length - 2 */ /* so we can be sure to send the full buffer in one urb */ -#define DIGI_PORT_BUF_LEN 16 +#define DIGI_PORT_BUF_LEN 16 + +/* retry timeout while waiting for urb->status to go to 0 */ +#define DIGI_RETRY_TIMEOUT (HZ/10) /* AccelePort USB Defines */ /* ids */ -#define DIGI_VENDOR_ID 0x05c5 -#define DIGI_ID 0x0004 - -/* commands */ -#define DIGI_CMD_SET_BAUD_RATE 0 -#define DIGI_CMD_SET_WORD_SIZE 1 -#define DIGI_CMD_SET_PARITY 2 -#define DIGI_CMD_SET_STOP_BITS 3 -#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 -#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 -#define DIGI_CMD_SET_DTR_SIGNAL 6 -#define DIGI_CMD_SET_RTS_SIGNAL 7 -#define DIGI_CMD_RECEIVE_ENABLE 10 -#define DIGI_CMD_BREAK_CONTROL 11 -#define DIGI_CMD_LOCAL_LOOPBACK 12 -#define DIGI_CMD_TRANSMIT_IDLE 13 -#define DIGI_CMD_WRITE_UART_REGISTER 15 -#define DIGI_CMD_AND_UART_REGISTER 16 -#define DIGI_CMD_OR_UART_REGISTER 17 -#define DIGI_CMD_SEND_DATA 18 +#define DIGI_VENDOR_ID 0x05c5 +#define DIGI_ID 0x0004 + +/* commands + * "INB": can be used on the in-band endpoint + * "OOB": can be used on the out-of-band endpoint + */ +#define DIGI_CMD_SET_BAUD_RATE 0 /* INB, OOB */ +#define DIGI_CMD_SET_WORD_SIZE 1 /* INB, OOB */ +#define DIGI_CMD_SET_PARITY 2 /* INB, OOB */ +#define DIGI_CMD_SET_STOP_BITS 3 /* INB, OOB */ +#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 /* INB, OOB */ +#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 /* INB, OOB */ +#define DIGI_CMD_SET_DTR_SIGNAL 6 /* INB, OOB */ +#define DIGI_CMD_SET_RTS_SIGNAL 7 /* INB, OOB */ +#define DIGI_CMD_READ_INPUT_SIGNALS 8 /* OOB */ +#define DIGI_CMD_IFLUSH_FIFO 9 /* OOB */ +#define DIGI_CMD_RECEIVE_ENABLE 10 /* INB, OOB */ +#define DIGI_CMD_BREAK_CONTROL 11 /* INB, OOB */ +#define DIGI_CMD_LOCAL_LOOPBACK 12 /* INB, OOB */ +#define DIGI_CMD_TRANSMIT_IDLE 13 /* INB, OOB */ +#define DIGI_CMD_READ_UART_REGISTER 14 /* OOB */ +#define DIGI_CMD_WRITE_UART_REGISTER 15 /* INB, OOB */ +#define DIGI_CMD_AND_UART_REGISTER 16 /* INB, OOB */ +#define DIGI_CMD_OR_UART_REGISTER 17 /* INB, OOB */ +#define DIGI_CMD_SEND_DATA 18 /* INB */ +#define DIGI_CMD_RECEIVE_DATA 19 /* INB */ +#define DIGI_CMD_RECEIVE_DISABLE 20 /* INB */ +#define DIGI_CMD_GET_PORT_TYPE 21 /* OOB */ /* baud rates */ #define DIGI_BAUD_50 0 @@ -102,10 +137,71 @@ #define DIGI_BAUD_230400 21 #define DIGI_BAUD_460800 22 -/* flow control arguments */ -#define DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL 1 -#define DIGI_ENABLE_RTS_CTS_FLOW_CONTROL 2 -#define DIGI_ENABLE_DTR_DSR_FLOW_CONTROL 4 +/* arguments */ +#define DIGI_WORD_SIZE_5 0 +#define DIGI_WORD_SIZE_6 1 +#define DIGI_WORD_SIZE_7 2 +#define DIGI_WORD_SIZE_8 3 + +#define DIGI_PARITY_NONE 0 +#define DIGI_PARITY_ODD 1 +#define DIGI_PARITY_EVEN 2 +#define DIGI_PARITY_MARK 3 +#define DIGI_PARITY_SPACE 4 + +#define DIGI_STOP_BITS_1 0 +#define DIGI_STOP_BITS_2 1 + +#define DIGI_INPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_INPUT_FLOW_CONTROL_RTS 2 +#define DIGI_INPUT_FLOW_CONTROL_DTR 4 + +#define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_OUTPUT_FLOW_CONTROL_CTS 2 +#define DIGI_OUTPUT_FLOW_CONTROL_DSR 4 + +#define DIGI_DTR_INACTIVE 0 +#define DIGI_DTR_ACTIVE 1 +#define DIGI_DTR_INPUT_FLOW_CONTROL 2 + +#define DIGI_RTS_INACTIVE 0 +#define DIGI_RTS_ACTIVE 1 +#define DIGI_RTS_INPUT_FLOW_CONTROL 2 +#define DIGI_RTS_TOGGLE 3 + +#define DIGI_FLUSH_TX 1 +#define DIGI_FLUSH_RX 2 +#define DIGI_RESUME_TX 4 /* clears xoff condition */ + +#define DIGI_DISABLE 0 +#define DIGI_ENABLE 1 + +#define DIGI_DEASSERT 0 +#define DIGI_ASSERT 1 + +/* in band status codes */ +#define DIGI_OVERRUN_ERROR 4 +#define DIGI_PARITY_ERROR 8 +#define DIGI_FRAMING_ERROR 16 +#define DIGI_BREAK_ERROR 32 + +/* out of band status */ +#define DIGI_NO_ERROR 0 +#define DIGI_BAD_FIRST_PARAMETER 1 +#define DIGI_BAD_SECOND_PARAMETER 2 +#define DIGI_INVALID_LINE 3 +#define DIGI_INVALID_OPCODE 4 + +/* input signals */ +#define DIGI_READ_INPUT_SIGNALS_SLOT 1 +#define DIGI_READ_INPUT_SIGNALS_ERR 2 +#define DIGI_READ_INPUT_SIGNALS_BUSY 4 +#define DIGI_READ_INPUT_SIGNALS_PE 8 +#define DIGI_READ_INPUT_SIGNALS_CTS 16 +#define DIGI_READ_INPUT_SIGNALS_DSR 32 +#define DIGI_READ_INPUT_SIGNALS_RI 64 +#define DIGI_READ_INPUT_SIGNALS_DCD 128 + /* macros */ #define MAX(a,b) (((a)>(b))?(a):(b)) @@ -117,32 +213,21 @@ typedef struct digi_private { spinlock_t dp_port_lock; int dp_buf_len; - char dp_buf[32]; + unsigned char dp_buf[DIGI_PORT_BUF_LEN]; + unsigned int dp_modem_signals; } digi_private_t; -struct s_digiusb { - u8 opcode; - u8 length; - u8 val; - u8 pad; -}; - /* Local Function Declarations */ -static void digi_send_cmd( char *mes, struct usb_serial_port *port, int opcode, - int length, int val ); -static void digi_send_oob( char *mes, int opcode, int linenum, int data1, int data2 ); +static int digi_write_oob( unsigned char *buf, int count ); +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ); static void digi_rx_throttle (struct usb_serial_port *port); static void digi_rx_unthrottle (struct usb_serial_port *port); -static int digi_setbaud( struct usb_serial_port *port, int baud ); static void digi_set_termios( struct usb_serial_port *port, struct termios *old_termios ); static void digi_break_ctl( struct usb_serial_port *port, int break_state ); -static int digi_get_modem_info( struct usb_serial *serial, - unsigned char *value ); -static int digi_set_modem_info( struct usb_serial *serial, - unsigned char value ); static int digi_ioctl( struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg ); static int digi_write( struct usb_serial_port *port, int from_user, @@ -152,138 +237,186 @@ static int digi_write_room( struct usb_serial_port *port ); static int digi_chars_in_buffer( struct usb_serial_port *port ); static int digi_open( struct usb_serial_port *port, struct file *filp ); static void digi_close( struct usb_serial_port *port, struct file *filp ); -static int digi_startup (struct usb_serial *serial); +static int digi_startup_device( struct usb_serial *serial ); +static int digi_startup( struct usb_serial *serial ); static void digi_shutdown( struct usb_serial *serial ); static void digi_read_bulk_callback( struct urb *urb ); +static void digi_read_oob( struct urb *urb ); /* Statics */ /* device info needed for the Digi serial converter */ -static __u16 digi_vendor_id = DIGI_VENDOR_ID; -static __u16 digi_product_id = DIGI_ID; +static __u16 digi_vendor_id = DIGI_VENDOR_ID; +static __u16 digi_product_id = DIGI_ID; /* out of band port */ -static int oob_port_num; /* index of out-of-band port */ -static struct usb_serial_port *oob_port; /* out-of-band control port */ -static int oob_read_started = 0; +static int oob_port_num; /* index of out-of-band port */ +static struct usb_serial_port *oob_port; /* out-of-band port */ +static int device_startup = 0; -/* config lock -- used to protect digi statics and globals, like oob vars */ -spinlock_t config_lock; +/* startup lock -- used to by digi_startup_device */ +spinlock_t startup_lock; /* Globals */ struct usb_serial_device_type digi_acceleport_device = { - name: "Digi USB", - idVendor: &digi_vendor_id, - idProduct: &digi_product_id, - needs_interrupt_in: DONT_CARE, - needs_bulk_in: MUST_HAVE, - needs_bulk_out: MUST_HAVE, - num_interrupt_in: 0, - num_bulk_in: 5, - num_bulk_out: 5, - num_ports: 4, - open: digi_open, - close: digi_close, - write: digi_write, - write_room: digi_write_room, - write_bulk_callback: digi_write_bulk_callback, - read_bulk_callback: digi_read_bulk_callback, - chars_in_buffer: digi_chars_in_buffer, - throttle: digi_rx_throttle, - unthrottle: digi_rx_unthrottle, - ioctl: digi_ioctl, - set_termios: digi_set_termios, - break_ctl: digi_break_ctl, - startup: digi_startup, - shutdown: digi_shutdown, + name: "Digi USB", + idVendor: &digi_vendor_id, + idProduct: &digi_product_id, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 0, + num_bulk_in: 5, + num_bulk_out: 5, + num_ports: 4, + open: digi_open, + close: digi_close, + write: digi_write, + write_room: digi_write_room, + write_bulk_callback: digi_write_bulk_callback, + read_bulk_callback: digi_read_bulk_callback, + chars_in_buffer: digi_chars_in_buffer, + throttle: digi_rx_throttle, + unthrottle: digi_rx_unthrottle, + ioctl: digi_ioctl, + set_termios: digi_set_termios, + break_ctl: digi_break_ctl, + startup: digi_startup, + shutdown: digi_shutdown, }; /* Functions */ -/* Send message on the out-of-Band endpoint */ -static void digi_send_oob( char *mes, int opcode, int linenum, int data1, int data2 ) +/* +* Digi Write OOB +* +* Write commands on the out of band port. Commands are 4 +* bytes each, multiple commands can be sent at once, and +* no command will be split across USB packets. Returns 0 +* if successful, -EINTR if interrupted while sleeping, or +* a negative error returned by usb_submit_urb. +*/ + +static int digi_write_oob( unsigned char *buf, int count ) { - int ret; - struct s_digiusb digiusb; - digi_private_t *priv = (digi_private_t *)(oob_port->private); + int ret = 0; + int len; + digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); -dbg( "digi_send_oob: TOP: from '%s', opcode: %d, linenum:%d, data1: %d, data2: %d", mes, opcode, linenum, data1, data2 ); - digiusb.opcode = (u8)opcode; - digiusb.length = (u8)linenum; - digiusb.val = (u8)data1; - digiusb.pad = (u8)data2; +dbg( "digi_write_oob: TOP: port=%d, count=%d", oob_port->number, count ); - spin_lock( &priv->dp_port_lock ); + spin_lock( &oob_priv->dp_port_lock ); - while (oob_port->write_urb->status == -EINPROGRESS) { -dbg( "digi_send_oob: opcode:%d already writing...", opcode ); - spin_unlock( &priv->dp_port_lock ); - interruptible_sleep_on(&oob_port->write_wait); - if (signal_pending(current)) { - return; + while( count > 0 ) { + + while( oob_port->write_urb->status == -EINPROGRESS ) { + spin_unlock( &oob_priv->dp_port_lock ); + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock( &oob_priv->dp_port_lock ); + } + + /* len must be a multiple of 4, so commands are not split */ + len = MIN( count, oob_port->bulk_out_size ); + if( len > 4 ) + len &= ~3; + + memcpy( oob_port->write_urb->transfer_buffer, buf, len ); + oob_port->write_urb->transfer_buffer_length = len; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + count -= len; + buf += len; + } else { + dbg( "digi_write_oob: usb_submit_urb failed, ret=%d", + ret ); + break; } - spin_lock( &priv->dp_port_lock ); - } - memcpy( oob_port->write_urb->transfer_buffer, &digiusb, sizeof(digiusb) ); - oob_port->write_urb->transfer_buffer_length = sizeof(digiusb); - if( (ret=usb_submit_urb(oob_port->write_urb)) != 0 ) { - dbg( - "digi_send_oob: usb_submit_urb(write bulk) failed, opcode=%d, ret=%d", - opcode, ret ); } - spin_unlock( &priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); -dbg( "digi_send_oob: opcode %d done", opcode ); + return( ret ); } -static void digi_send_cmd( char *mes, struct usb_serial_port *port, int opcode, - int length, int val ) +/* +* Digi Set Modem Signals +* +* Sets or clears DTR and RTS on the port, according to the +* modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags +* for the modem_signals argument. Returns 0 if successful, +* -EINTR if interrupted while sleeping, or a non-zero error +* returned by usb_submit_urb. +*/ + +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ) { int ret; - struct s_digiusb digiusb; - digi_private_t *priv = (digi_private_t *)(port->private); + unsigned char *data = oob_port->write_urb->transfer_buffer; + digi_private_t *port_priv = (digi_private_t *)(port->private); + digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); -dbg( "digi_send_cmd: TOP: from '%s', opcode: %d, val: %d", mes, opcode, val ); +dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x", +port->number, modem_signals ); - digiusb.opcode = (u8)opcode; - digiusb.length = (u8)length; - digiusb.val = (u8)val; - digiusb.pad = 0; + spin_lock( &oob_priv->dp_port_lock ); + spin_lock( &port_priv->dp_port_lock ); - spin_lock( &priv->dp_port_lock ); - - while( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_send_cmd: opcode=%d already writing...", opcode ); - spin_unlock( &priv->dp_port_lock ); - interruptible_sleep_on( &port->write_wait ); + while( oob_port->write_urb->status == -EINPROGRESS ) { + spin_unlock( &port_priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); if( signal_pending(current) ) { - return; + return( -EINTR ); } - spin_lock( &priv->dp_port_lock ); + spin_lock( &oob_priv->dp_port_lock ); + spin_lock( &port_priv->dp_port_lock ); } - memcpy( port->write_urb->transfer_buffer, &digiusb, sizeof(digiusb) ); - port->write_urb->transfer_buffer_length = sizeof(digiusb); - if( (ret=usb_submit_urb(port->write_urb)) != 0 ) - dbg( - "digi_send_cmd: usb_submit_urb(write bulk) failed, opcode=%d, ret=%d", - opcode, ret ); + /* command is 4 bytes: command, line, argument, pad */ + data[0] = DIGI_CMD_SET_DTR_SIGNAL; + data[1] = port->number; + data[2] = (modem_signals&TIOCM_DTR) ? + DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE; + data[3] = 0; + + data[4] = DIGI_CMD_SET_RTS_SIGNAL; + data[5] = port->number; + data[6] = (modem_signals&TIOCM_RTS) ? + DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; + data[7] = 0; + + oob_port->write_urb->transfer_buffer_length = 8; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + port_priv->dp_modem_signals = + (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) + | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); + } else { + dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d", + ret ); + } -dbg( "digi_send_cmd: opcode %d done", opcode ); + spin_unlock( &port_priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); - spin_unlock( &priv->dp_port_lock ); + return( ret ); } @@ -312,52 +445,7 @@ dbg( "digi_rx_unthrottle: TOP: port=%d", port->number ); /* just restart the receive interrupt URB */ //if (usb_submit_urb(port->interrupt_in_urb)) - // dbg( "digi_rx_unthrottle: usb_submit_urb(read urb) failed" ); - -} - - -static int digi_setbaud( struct usb_serial_port *port, int baud ) -{ - - int bindex; - - -dbg( "digi_setbaud: TOP: port=%d", port->number ); - - switch( baud ) { - case 50: bindex = DIGI_BAUD_50; break; - case 75: bindex = DIGI_BAUD_75; break; - case 110: bindex = DIGI_BAUD_110; break; - case 150: bindex = DIGI_BAUD_150; break; - case 200: bindex = DIGI_BAUD_200; break; - case 300: bindex = DIGI_BAUD_300; break; - case 600: bindex = DIGI_BAUD_600; break; - case 1200: bindex = DIGI_BAUD_1200; break; - case 1800: bindex = DIGI_BAUD_1800; break; - case 2400: bindex = DIGI_BAUD_2400; break; - case 4800: bindex = DIGI_BAUD_4800; break; - case 7200: bindex = DIGI_BAUD_7200; break; - case 9600: bindex = DIGI_BAUD_9600; break; - case 14400: bindex = DIGI_BAUD_14400; break; - case 19200: bindex = DIGI_BAUD_19200; break; - case 28800: bindex = DIGI_BAUD_28800; break; - case 38400: bindex = DIGI_BAUD_38400; break; - case 57600: bindex = DIGI_BAUD_57600; break; - case 76800: bindex = DIGI_BAUD_76800; break; - case 115200: bindex = DIGI_BAUD_115200; break; - case 153600: bindex = DIGI_BAUD_153600; break; - case 230400: bindex = DIGI_BAUD_230400; break; - case 460800: bindex = DIGI_BAUD_460800; break; - default: - dbg( "digi_setbaud: can't handle requested baud rate %d", baud ); - return( -EINVAL ); - break; - } - - digi_send_cmd( "digi_setbaud:", port, DIGI_CMD_SET_BAUD_RATE, 2, bindex ); - - return( 0 ); /* FIX -- send_cmd should return a value??, return it */ + // dbg( "digi_rx_unthrottle: usb_submit_urb failed" ); } @@ -367,167 +455,243 @@ static void digi_set_termios( struct usb_serial_port *port, { unsigned int iflag = port->tty->termios->c_iflag; - unsigned int old_iflag = old_termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; + unsigned int old_iflag = old_termios->c_iflag; unsigned int old_cflag = old_termios->c_cflag; - int arg; + unsigned char buf[32]; + int arg,ret; + int i = 0; dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", port->number, iflag, old_iflag, cflag, old_cflag ); /* set baud rate */ - /* if( (cflag&CBAUD) != (old_cflag&CBAUD) ) */ { + if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { + + arg = -1; + + /* reassert DTR and (maybe) RTS on transition from B0 */ + if( (old_cflag&CBAUD) == B0 ) { + /* don't set RTS if using hardware flow control */ + /* and throttling input -- not implemented yet */ + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); + } + switch( (cflag&CBAUD) ) { - case B50: digi_setbaud(port, 50); break; - case B75: digi_setbaud(port, 75); break; - case B110: digi_setbaud(port, 110); break; - case B150: digi_setbaud(port, 150); break; - case B200: digi_setbaud(port, 200); break; - case B300: digi_setbaud(port, 300); break; - case B600: digi_setbaud(port, 600); break; - case B1200: digi_setbaud(port, 1200); break; - case B1800: digi_setbaud(port, 1800); break; - case B2400: digi_setbaud(port, 2400); break; - case B4800: digi_setbaud(port, 4800); break; - case B9600: digi_setbaud(port, 9600); break; - case B19200: digi_setbaud(port, 19200); break; - case B38400: digi_setbaud(port, 38400); break; - case B57600: digi_setbaud(port, 57600); break; - case B115200: digi_setbaud(port, 115200); break; - default: - dbg( "digi_set_termios: can't handle baud rate 0x%x", - (cflag&CBAUD) ); - break; + /* drop DTR and RTS on transition to B0 */ + case B0: digi_set_modem_signals( port, 0 ); break; + case B50: arg = DIGI_BAUD_50; break; + case B75: arg = DIGI_BAUD_75; break; + case B110: arg = DIGI_BAUD_110; break; + case B150: arg = DIGI_BAUD_150; break; + case B200: arg = DIGI_BAUD_200; break; + case B300: arg = DIGI_BAUD_300; break; + case B600: arg = DIGI_BAUD_600; break; + case B1200: arg = DIGI_BAUD_1200; break; + case B1800: arg = DIGI_BAUD_1800; break; + case B2400: arg = DIGI_BAUD_2400; break; + case B4800: arg = DIGI_BAUD_4800; break; + case B9600: arg = DIGI_BAUD_9600; break; + case B19200: arg = DIGI_BAUD_19200; break; + case B38400: arg = DIGI_BAUD_38400; break; + case B57600: arg = DIGI_BAUD_57600; break; + case B115200: arg = DIGI_BAUD_115200; break; + case B230400: arg = DIGI_BAUD_230400; break; + case B460800: arg = DIGI_BAUD_460800; break; + default: + dbg( "digi_set_termios: can't handle baud rate 0x%x", + (cflag&CBAUD) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_BAUD_RATE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set parity */ + if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) { + + if( (cflag&PARENB) ) { + if( (cflag&PARODD) ) + arg = DIGI_PARITY_ODD; + else + arg = DIGI_PARITY_EVEN; + } else { + arg = DIGI_PARITY_NONE; } + + buf[i++] = DIGI_CMD_SET_PARITY; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + + } + + /* set word size */ + if( (cflag&CSIZE) != (old_cflag&CSIZE) ) { + + arg = -1; + + switch( (cflag&CSIZE) ) { + case CS5: arg = DIGI_WORD_SIZE_5; break; + case CS6: arg = DIGI_WORD_SIZE_6; break; + case CS7: arg = DIGI_WORD_SIZE_7; break; + case CS8: arg = DIGI_WORD_SIZE_8; break; + default: + dbg( "digi_set_termios: can't handle word size %d", + (cflag&CSIZE) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_WORD_SIZE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set stop bits */ + if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) { + + if( (cflag&CSTOPB) ) + arg = DIGI_STOP_BITS_2; + else + arg = DIGI_STOP_BITS_1; + + buf[i++] = DIGI_CMD_SET_STOP_BITS; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } /* set input flow control */ - /* if( (iflag&IXOFF) != (old_iflag&IXOFF) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) */ { + if( (iflag&IXOFF) != (old_iflag&IXOFF) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { arg = 0; if( (iflag&IXOFF) ) - arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + if( (cflag&CRTSCTS) ) - arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + arg |= DIGI_INPUT_FLOW_CONTROL_RTS; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS; - digi_send_cmd( "digi_termios: set input flow control:", port, - DIGI_CMD_SET_INPUT_FLOW_CONTROL, 2, arg ); + buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; } /* set output flow control */ - /* if( (iflag&IXON) != (old_iflag&IXON) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) */ { + /*if( (iflag&IXON) != (old_iflag&IXON) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) )*/ { arg = 0; if( (iflag&IXON) ) - arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + if( (cflag&CRTSCTS) ) - arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; - digi_send_cmd( "digi_set_termios: set output flow control:", port, - DIGI_CMD_SET_OUTPUT_FLOW_CONTROL, 2, arg ); + buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; } -} + /* set receive enable/disable */ + if( (cflag&CREAD) != (old_cflag&CREAD) ) { + if( (cflag&CREAD) ) + arg = DIGI_ENABLE; + else + arg = DIGI_DISABLE; -static void digi_break_ctl( struct usb_serial_port *port, int break_state ) -{ -dbg( "digi_break_ctl: TOP: port=%d", port->number ); -} + buf[i++] = DIGI_CMD_RECEIVE_ENABLE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } -/* modem control pins: DTR and RTS are outputs and can be controlled; - DCD, RI, DSR, CTS are inputs and can be read */ + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_set_termios: write oob failed, ret=%d", ret ); -static int digi_get_modem_info( struct usb_serial *serial, - unsigned char *value ) -{ -dbg( "digi_get_modem_info: TOP" ); - return( 0 ); } -static int digi_set_modem_info( struct usb_serial *serial, - unsigned char value ) +static void digi_break_ctl( struct usb_serial_port *port, int break_state ) { -dbg( "digi_set_modem_info: TOP" ); - return( 0 ); +dbg( "digi_break_ctl: TOP: port=%d", port->number ); } static int digi_ioctl( struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg ) { - struct usb_serial *serial = port->serial; - int rc; - unsigned int value; - unsigned char status, mask; + + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned int val; + dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", port->number, cmd ); -return( -ENOIOCTLCMD ); switch (cmd) { - case TIOCMGET: /* get modem pins state */ - rc = digi_get_modem_info(serial, &status); - if (rc < 0) - return rc; - value = - ((status & (1<<7)) ? TIOCM_DTR : 0) | - ((status & (1<<6)) ? TIOCM_CAR : 0) | - ((status & (1<<5)) ? TIOCM_RNG : 0) | - ((status & (1<<4)) ? TIOCM_DSR : 0) | - ((status & (1<<3)) ? TIOCM_CTS : 0) | - ((status & (1<<2)) ? TIOCM_RTS : 0); - if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) - return -EFAULT; - return 0; - case TIOCMSET: /* set a state as returned by MGET */ - if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) - return -EFAULT; - status = - ((value & TIOCM_DTR) ? (1<<7) : 0) | - ((value & TIOCM_CAR) ? (1<<6) : 0) | - ((value & TIOCM_RNG) ? (1<<5) : 0) | - ((value & TIOCM_DSR) ? (1<<4) : 0) | - ((value & TIOCM_CTS) ? (1<<3) : 0) | - ((value & TIOCM_RTS) ? (1<<2) : 0); - rc = digi_set_modem_info(serial, status); - if (rc < 0) - return rc; - return 0; - case TIOCMBIS: /* set bits in bitmask */ - case TIOCMBIC: /* clear bits from bitmask */ - if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) - return -EFAULT; - rc = digi_get_modem_info(serial, &status); - if (rc < 0) - return rc; - mask = - ((value & TIOCM_RTS) ? (1<<2) : 0) | - ((value & TIOCM_DTR) ? (1<<7) : 0); - if (cmd == TIOCMBIS) - status |= mask; - else - status &= ~mask; - rc = digi_set_modem_info(serial, status); - if (rc < 0) - return rc; - return 0; + + case TIOCMGET: + spin_lock( &priv->dp_port_lock ); + val = priv->dp_modem_signals; + spin_unlock( &priv->dp_port_lock ); + if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) ) + return( -EFAULT ); + return( 0 ); + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) ) + return( -EFAULT ); + spin_lock( &priv->dp_port_lock ); + if( cmd == TIOCMBIS ) + val = priv->dp_modem_signals | val; + else if( cmd == TIOCMBIC ) + val = priv->dp_modem_signals & ~val; + spin_unlock( &priv->dp_port_lock ); + return( digi_set_modem_signals( port, val ) ); + case TIOCMIWAIT: /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ /* TODO */ + return( 0 ); + case TIOCGICOUNT: /* return count of modemline transitions */ - return 0; /* TODO */ + /* TODO */ + return 0; + } - - return -ENOIOCTLCMD; + + return( -ENOIOCTLCMD ); + } @@ -535,7 +699,7 @@ static int digi_write( struct usb_serial_port *port, int from_user, const unsigned char *buf, int count ) { - int i,ret,data_len,new_len; + int ret,data_len,new_len; digi_private_t *priv = (digi_private_t *)(port->private); @@ -550,16 +714,18 @@ port->number, count, from_user, in_interrupt() ); /* wait for urb status clear to submit another urb */ if( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_write: -EINPROGRESS set" ); - - /* buffer the data if possible */ - new_len = MIN( count, DIGI_PORT_BUF_LEN-priv->dp_buf_len ); - memcpy( priv->dp_buf+priv->dp_buf_len, buf, new_len ); - priv->dp_buf_len += new_len; + /* buffer data if count is 1 (probably put_char) if possible */ + if( count == 1 ) { + new_len = MIN( count, + DIGI_PORT_BUF_LEN-priv->dp_buf_len ); + memcpy( priv->dp_buf+priv->dp_buf_len, buf, new_len ); + priv->dp_buf_len += new_len; + } else { + new_len = 0; + } - /* unlock and return number of bytes buffered */ spin_unlock( &priv->dp_port_lock ); -dbg( "digi_write: buffering, return %d", new_len ); + return( new_len ); } @@ -569,19 +735,16 @@ dbg( "digi_write: buffering, return %d", new_len ); new_len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); data_len = new_len + priv->dp_buf_len; -dbg( "digi_write: counts: new data %d, buf data %d, total data %d (max %d)", new_len, priv->dp_buf_len, data_len, port->bulk_out_size-2 ); - - /* nothing to send */ if( data_len == 0 ) { spin_unlock( &priv->dp_port_lock ); return( 0 ); } - /* set command and length bytes */ - *((u8 *)(port->write_urb->transfer_buffer)) = (u8)DIGI_CMD_SEND_DATA; - *((u8 *)(port->write_urb->transfer_buffer)+1) = (u8)data_len; + *((unsigned char *)(port->write_urb->transfer_buffer)) + = (unsigned char)DIGI_CMD_SEND_DATA; + *((unsigned char *)(port->write_urb->transfer_buffer)+1) + = (unsigned char)data_len; - /* set total transfer buffer length */ port->write_urb->transfer_buffer_length = data_len+2; /* copy in buffered data first */ @@ -590,33 +753,34 @@ dbg( "digi_write: counts: new data %d, buf data %d, total data %d (max %d)", new /* copy in new data */ if( from_user ) { - copy_from_user( port->write_urb->transfer_buffer+2+priv->dp_buf_len, + copy_from_user( + port->write_urb->transfer_buffer+2+priv->dp_buf_len, buf, new_len ); - } - else { + } else { memcpy( port->write_urb->transfer_buffer+2+priv->dp_buf_len, buf, new_len ); } -#ifdef DEBUG - printk( KERN_DEBUG __FILE__ ": digi_write: length=%d, data=", - port->write_urb->transfer_buffer_length ); +#ifdef DEBUG_DATA +{ + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", + port->number, port->write_urb->transfer_buffer_length ); for( i=0; iwrite_urb->transfer_buffer_length; ++i ) { printk( "%.2x ", - ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); } printk( "\n" ); +} #endif - /* submit urb */ if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { - /* submit successful, return length of new data written */ ret = new_len; - /* clear buffer */ priv->dp_buf_len = 0; - } - else { - dbg( "digi_write: usb_submit_urb(write bulk) failed, ret=%d", ret ); + } else { + dbg( "digi_write: usb_submit_urb failed, ret=%d", + ret ); /* no bytes written - should we return the error code or 0? */ ret = 0; } @@ -658,28 +822,34 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", port->number ); spin_lock( &priv->dp_port_lock ); if( port->write_urb->status != -EINPROGRESS && priv->dp_buf_len > 0 ) { - /* set command and length bytes */ - *((u8 *)(port->write_urb->transfer_buffer)) - = (u8)DIGI_CMD_SEND_DATA; - *((u8 *)(port->write_urb->transfer_buffer)+1) - = (u8)priv->dp_buf_len; + *((unsigned char *)(port->write_urb->transfer_buffer)) + = (unsigned char)DIGI_CMD_SEND_DATA; + *((unsigned char *)(port->write_urb->transfer_buffer)+1) + = (unsigned char)priv->dp_buf_len; - /* set total transfer buffer length */ port->write_urb->transfer_buffer_length = priv->dp_buf_len+2; - /* copy in buffered data */ memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, priv->dp_buf_len ); - /* submit urb */ -dbg( "digi_write_bulk_callback: submit urb to write buffer, data len=%d", -priv->dp_buf_len ); +#ifdef DEBUG_DATA +{ + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=", + port->number, port->write_urb->transfer_buffer_length ); + for( i=0; iwrite_urb->transfer_buffer_length; ++i ) { + printk( "%.2x ", + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + } + printk( "\n" ); +} +#endif + if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { - /* successful, clear buffer */ priv->dp_buf_len = 0; - } - else { - dbg( "digi_write_bulk_callback: usb_submit_urb(write bulk) failed, ret=%d", ret ); + } else { + dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", ret ); } } @@ -690,7 +860,8 @@ priv->dp_buf_len ); /* wake up line discipline */ tty = port->tty; - if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) + if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup ) (tty->ldisc.write_wakeup)(tty); /* wake up other tty processes */ @@ -717,7 +888,7 @@ dbg( "digi_write_room: TOP: port=%d", port->number ); spin_unlock( &priv->dp_port_lock ); -dbg( "digi_write_room: return room=%d", room ); +dbg( "digi_write_room: port=%d, room=%d", port->number, room ); return( room ); } @@ -732,11 +903,10 @@ static int digi_chars_in_buffer( struct usb_serial_port *port ) dbg( "digi_chars_in_buffer: TOP: port=%d", port->number ); if( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_chars_in_buffer: return=%d", port->bulk_out_size ); - return( port->bulk_out_size ); - } - else { -dbg( "digi_chars_in_buffer: return=%d", priv->dp_buf_len ); +dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, port->bulk_out_size - 2 ); + return( port->bulk_out_size - 2 ); + } else { +dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, priv->dp_buf_len ); return( priv->dp_buf_len ); } @@ -746,61 +916,53 @@ dbg( "digi_chars_in_buffer: return=%d", priv->dp_buf_len ); static int digi_open( struct usb_serial_port *port, struct file *filp ) { + int i = 0; int ret; + unsigned char buf[32]; digi_private_t *priv = (digi_private_t *)(port->private); + struct termios not_termios; + + +dbg( "digi_open: TOP: port %d, active:%d", port->number, port->active ); + /* be sure the device is started up */ + if( digi_startup_device( port->serial ) != 0 ) + return( -ENXIO ); -dbg( "digi_open: TOP: port %d", port->number ); + MOD_INC_USE_COUNT; /* if port is already open, just return */ - /* be sure exactly one open succeeds */ + /* be sure exactly one open proceeds */ spin_lock( &priv->dp_port_lock ); - if( port->active ) { + if( port->active++ ) { + spin_unlock( &priv->dp_port_lock ); return( 0 ); } - port->active = 1; spin_unlock( &priv->dp_port_lock ); - /* start reading from the out-of-band port for the device */ - /* be sure this happens exactly once */ - spin_lock( &config_lock ); - if( !oob_read_started ) { - if( (ret=usb_submit_urb(oob_port->read_urb)) != 0 ) { - dbg( "digi_open: usb_submit_urb(read bulk) for oob failed, ret=%d", - ret ); - spin_unlock( &config_lock ); - return( -ENXIO ); - } - else { -dbg( "digi_open: usb_submit_urb(read bulk) for oob succeeded" ); - oob_read_started = 1; - } - } - spin_unlock( &config_lock ); - - /* initialize port */ -dbg( "digi_open: init..." ); - /* set 9600, 8N1, DTR, RTS, RX enable, no input or output flow control */ - digi_setbaud( port, 9600 ); - digi_send_cmd( "digi_open: wordsize", port, DIGI_CMD_SET_WORD_SIZE, 2, 3 ); - digi_send_cmd( "digi_open: parity", port, DIGI_CMD_SET_PARITY, 2, 0 ); - digi_send_cmd( "digi_open: stopbits", port, DIGI_CMD_SET_STOP_BITS, 2, 0 ); - digi_send_cmd( "digi_open: DTR on", port, DIGI_CMD_SET_DTR_SIGNAL, 2, 1 ); - digi_send_cmd( "digi_open: RTS on", port, DIGI_CMD_SET_RTS_SIGNAL, 2, 1 ); - digi_send_cmd( "digi_open: RX enable on", port, DIGI_CMD_RECEIVE_ENABLE, 2, - 1 ); - digi_send_cmd( "digi_open: input flow control off", port, - DIGI_CMD_SET_INPUT_FLOW_CONTROL, 2, 0 ); - digi_send_cmd( "digi_open: output flow control off", port, - DIGI_CMD_SET_OUTPUT_FLOW_CONTROL, 2, 0 ); - - /* start reading from the device */ - if( (ret=usb_submit_urb(port->read_urb)) != 0 ) { - dbg( "digi_open: usb_submit_urb(read bulk) failed, ret=%d", ret ); - return( -ENXIO ); - } + /* read modem signals automatically whenever they change */ + buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[i++] = port->number; + buf[i++] = DIGI_ENABLE; + buf[i++] = 0; + + /* flush fifos */ + buf[i++] = DIGI_CMD_IFLUSH_FIFO; + buf[i++] = port->number; + buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[i++] = 0; + + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_open: write oob failed, ret=%d", ret ); + + /* set termios settings */ + not_termios.c_cflag = ~port->tty->termios->c_cflag; + not_termios.c_iflag = ~port->tty->termios->c_iflag; + digi_set_termios( port, ¬_termios ); + + /* set DTR and RTS */ + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); -dbg( "digi_open: done" ); return( 0 ); } @@ -809,39 +971,119 @@ dbg( "digi_open: done" ); static void digi_close( struct usb_serial_port *port, struct file *filp ) { -dbg( "digi_close: TOP: port %d", port->number ); - - /* Need to change the control lines here */ - /* TODO */ -dbg( "digi_close: wanna clear DTR and RTS..." ); + int i = 0; + int ret; + unsigned char buf[32]; + digi_private_t *priv = (digi_private_t *)(port->private); -//digi_send_cmd( "digi_close DTR off", port, 6, 2, 0); // clear DTR -//digi_send_cmd( "digi_close RTS off", port, 7, 2, 0); // clear RTS -//digi_send_cmd( "digi_close RX disable", port, 10, 2, 0); // Rx Disable -digi_send_oob( "digi_close RTS off", DIGI_CMD_SET_RTS_SIGNAL, - port->number, 0, 0 ); // clear RTS -digi_send_oob( "digi_close DTR off", DIGI_CMD_SET_DTR_SIGNAL, - port->number, 0, 0 ); // clear DTR +dbg( "digi_close: TOP: port %d, active:%d", port->number, port->active ); + + /* do cleanup only after final close on this port */ + spin_lock( &priv->dp_port_lock ); + if( --port->active ) { + spin_unlock( &priv->dp_port_lock ); + MOD_DEC_USE_COUNT; + return; + } + spin_unlock( &priv->dp_port_lock ); + + /* drop DTR and RTS */ + digi_set_modem_signals( port, 0 ); + + /* disable input flow control */ + buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* disable output flow control */ + buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* disable reading modem signals automatically */ + buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* flush fifos */ + buf[i++] = DIGI_CMD_IFLUSH_FIFO; + buf[i++] = port->number; + buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[i++] = 0; + + /* disable receive */ + buf[i++] = DIGI_CMD_RECEIVE_ENABLE; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_close: write oob failed, ret=%d", ret ); + + /* wait for final commands on oob port to complete */ while( oob_port->write_urb->status == -EINPROGRESS ) { -dbg ("digi_close: waiting for final writes to complete on oob port %d...", oob_port->number ); - interruptible_sleep_on( &oob_port->write_wait ); + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); if( signal_pending(current) ) { break; } } - /* shutdown our bulk reads and writes */ + /* shutdown any outstanding bulk writes */ usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - port->active = 0; + MOD_DEC_USE_COUNT; + +} + + +/* +* Digi Startup Device +* +* Starts reads on all ports. Must be called AFTER startup, with +* urbs initialized. Returns 0 if successful, non-zero error otherwise. +*/ + +static int digi_startup_device( struct usb_serial *serial ) +{ + + int i,ret = 0; + + + spin_lock( &startup_lock ); + + /* be sure this happens exactly once */ + if( device_startup ) { + spin_unlock( &startup_lock ); + return( 0 ); + } + + /* start reading from each bulk in endpoint for the device */ + for( i=0; iport[i].read_urb)) != 0 ) { + dbg( "digi_startup_device: usb_submit_urb failed, port=%d, ret=%d", + i, ret ); + break; + } + + } + + device_startup = 1; + + spin_unlock( &startup_lock ); + + return( ret ); } -static int digi_startup (struct usb_serial *serial) +static int digi_startup( struct usb_serial *serial ) { int i; @@ -850,22 +1092,24 @@ static int digi_startup (struct usb_serial *serial) dbg( "digi_startup: TOP" ); - /* initialize config lock */ - spin_lock_init( &config_lock ); + spin_lock_init( &startup_lock ); /* allocate the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ for( i=0; iport[i].active = 0; + /* allocate private structure */ priv = serial->port[i].private = - (digi_private_t *)kmalloc( sizeof(struct digi_private), + (digi_private_t *)kmalloc( sizeof(digi_private_t), GFP_KERNEL ); if( priv == (digi_private_t *)0 ) - return( 1 ); /* error */ + return( 1 ); /* error */ /* initialize private structure */ priv->dp_buf_len = 0; + priv->dp_modem_signals = 0; spin_lock_init( &priv->dp_port_lock ); /* initialize write wait queue for this port */ @@ -876,7 +1120,7 @@ dbg( "digi_startup: TOP" ); /* initialize out of band port info */ oob_port_num = digi_acceleport_device.num_ports; oob_port = &serial->port[oob_port_num]; - oob_read_started = 0; + device_startup = 0; return( 0 ); @@ -891,10 +1135,13 @@ static void digi_shutdown( struct usb_serial *serial ) dbg( "digi_shutdown: TOP" ); - /* stop writing and reading from the out-of-band port */ - usb_unlink_urb( oob_port->write_urb ); - usb_unlink_urb( oob_port->read_urb ); - oob_read_started = 0; + /* stop reads and writes on all ports */ + for( i=0; iport[i].read_urb); + usb_unlink_urb (serial->port[i].write_urb); + } + + device_startup = 0; /* free the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ @@ -910,7 +1157,10 @@ static void digi_read_bulk_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; struct tty_struct *tty = port->tty; - unsigned char *data = urb->transfer_buffer; + int opcode = ((unsigned char *)urb->transfer_buffer)[0]; + int len = ((unsigned char *)urb->transfer_buffer)[1]; + int status = ((unsigned char *)urb->transfer_buffer)[2]; + unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3; int ret,i; @@ -918,58 +1168,108 @@ dbg( "digi_read_bulk_callback: TOP: port=%d", port->number ); /* handle oob callback */ if( port->number == oob_port_num ) { -dbg( "digi_read_bulk_callback: oob_port callback, opcode=%d, line=%d, status=%d, ret=%d", data[0], data[1], data[2], data[3] ); - if( urb->status ) { - dbg( "digi_read_bulk_callback: nonzero read bulk status on oob: %d", - urb->status ); - } - if( (ret=usb_submit_urb(urb)) != 0 ) { - dbg( "digi_read_bulk_callback: failed resubmitting oob urb, ret=%d", - ret ); - } + digi_read_oob( urb ); return; } /* sanity checks */ if( port_paranoia_check( port, "digi_read_bulk_callback" ) || serial_paranoia_check( serial, "digi_read_bulk_callback" ) ) { - return; + goto resubmit; } - /* check status */ if( urb->status ) { dbg( "digi_read_bulk_callback: nonzero read bulk status: %d", urb->status ); - return; + goto resubmit; } -#ifdef DEBUG - if (urb->actual_length) { - printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: length=%d, data=", - urb->actual_length ); - for( i=0; iactual_length; ++i ) { - printk( "%.2x ", data[i] ); - } - printk( "\n" ); +#ifdef DEBUG_DATA +if( urb->actual_length ) { + printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=", + port->number, urb->actual_length ); + for( i=0; iactual_length; ++i ) { + printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] ); } + printk( "\n" ); +} #endif - /* Digi read packets are: */ - /* 0 1 2 3 4 ... 3+length-1 == 2+length*/ - /* opcode, length, status, data[0], data[1]...data[length-2] */ - if( urb->actual_length > 3 ) { - for( i=3; i<2+data[1]; ++i ) { + if( urb->actual_length != len + 2 ) + err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", port->number, opcode, len, urb->actual_length, status ); + + /* receive data */ + if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) { + len = MIN( len, urb->actual_length-3 ); + for( i=0; icontext; + struct usb_serial *serial = port->serial; + digi_private_t *priv; + int oob_opcode = ((unsigned char *)urb->transfer_buffer)[0]; + int oob_line = ((unsigned char *)urb->transfer_buffer)[1]; + int oob_status = ((unsigned char *)urb->transfer_buffer)[2]; + int oob_ret = ((unsigned char *)urb->transfer_buffer)[3]; + int ret; + + +dbg( "digi_read_oob: opcode=%d, line=%d, status=%d, ret=%d", oob_opcode, oob_line, oob_status, oob_ret ); + + if( urb->status ) { + dbg( "digi_read_oob: nonzero read bulk status on oob: %d", + urb->status ); + goto resubmit; + } + + if( oob_opcode == DIGI_CMD_READ_INPUT_SIGNALS && oob_status == 0 ) { + + priv = serial->port[oob_line].private; + + spin_lock( &priv->dp_port_lock ); + + /* convert from digi flags to termiox flags */ + if( oob_ret & DIGI_READ_INPUT_SIGNALS_CTS ) + priv->dp_modem_signals |= TIOCM_CTS; + else + priv->dp_modem_signals &= ~TIOCM_CTS; + if( oob_ret & DIGI_READ_INPUT_SIGNALS_DSR ) + priv->dp_modem_signals |= TIOCM_DSR; + else + priv->dp_modem_signals &= ~TIOCM_DSR; + if( oob_ret & DIGI_READ_INPUT_SIGNALS_RI ) + priv->dp_modem_signals |= TIOCM_RI; + else + priv->dp_modem_signals &= ~TIOCM_RI; + if( oob_ret & DIGI_READ_INPUT_SIGNALS_DCD ) + priv->dp_modem_signals |= TIOCM_CD; + else + priv->dp_modem_signals &= ~TIOCM_CD; + + spin_unlock( &priv->dp_port_lock ); + + } + +resubmit: + if( (ret=usb_submit_urb(urb)) != 0 ) { + dbg( "digi_read_oob: failed resubmitting oob urb, ret=%d", + ret ); + } + +} diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index cdad3cabe3b..8aec735697b 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -32,9 +32,6 @@ #include - -#ifdef CONFIG_USB_SERIAL_FTDI_SIO - #include #include #include @@ -723,6 +720,3 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns return 0; } /* ftdi_sio_ioctl */ -#endif /* CONFIG_USB_SERIAL_FTDI_SIO */ - - diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index f91e1b592a4..c18137ffa39 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -18,9 +18,6 @@ #include - -#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA - #include #include #include @@ -700,4 +697,3 @@ struct usb_serial_device_type keyspan_pda_device = { shutdown: keyspan_pda_shutdown, }; -#endif /* CONFIG_USB_SERIAL_KEYSPAN_PDA */ diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 9eb6767f08f..2e5e50849f1 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -13,9 +13,6 @@ */ #include - -#ifdef CONFIG_USB_SERIAL_OMNINET - #include #include #include @@ -336,6 +333,3 @@ static void omninet_write_bulk_callback (struct urb *urb) return; } -#endif /* CONFIG_USB_SERIAL_OMNINET */ - - diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 9fe65ae8dcf..b2e18bd8eb9 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -14,6 +14,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (05/22/2000) gkh + * Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be + * removed from the individual device source files. + * * (05/03/2000) gkh * Added the Digi Acceleport driver from Al Borchers and Peter Berger. * diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 979a56f0119..5cfb4c42ab5 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -20,9 +20,6 @@ */ #include - -#ifdef CONFIG_USB_SERIAL_VISOR - #include #include #include @@ -207,7 +204,3 @@ static int visor_startup (struct usb_serial *serial) return (0); } - -#endif /* CONFIG_USB_SERIAL_VISOR*/ - - diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 58f761fbfe1..c168efa02df 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -21,9 +21,6 @@ */ #include - -#ifdef CONFIG_USB_SERIAL_WHITEHEAT - #include #include #include @@ -407,6 +404,3 @@ static void whiteheat_shutdown (struct usb_serial *serial) return; } -#endif /* CONFIG_USB_SERIAL_WHITEHEAT */ - - diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 2eaab47ef38..a3d8d3d4240 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -821,6 +821,7 @@ status_phase: /* Control status phase */ status = uhci_status_bits(td->status); +#ifdef I_HAVE_BUGGY_APC_BACKUPS /* APC BackUPS Pro kludge */ /* It tries to send all of the descriptor instead of the amount */ /* we requested */ @@ -828,6 +829,7 @@ status_phase: status & TD_CTRL_ACTIVE && status & TD_CTRL_NAK) return 0; +#endif if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -2325,31 +2327,30 @@ static int found_uhci(struct pci_dev *dev) { int i; + /* disable legacy emulation */ + pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); + + if (pci_enable_device(dev) < 0) + return -1; + + if (!dev->irq) { + err("found UHCI device with no IRQ assigned. check BIOS settings!"); + return -1; + } + /* Search for the IO base address.. */ for (i = 0; i < 6; i++) { - unsigned int io_addr = dev->resource[i].start; - unsigned int io_size = - dev->resource[i].end - dev->resource[i].start + 1; + unsigned int io_addr = pci_resource_start(dev, i); + unsigned int io_size = pci_resource_len(dev, i); /* IO address? */ - if (!(dev->resource[i].flags & IORESOURCE_IO)) + if (!(pci_resource_flags(dev, i) & IORESOURCE_IO)) continue; /* Is it already in use? */ if (check_region(io_addr, io_size)) break; - if (!dev->irq) { - err("found UHCI device with no IRQ assigned. check BIOS settings!"); - continue; - } - - /* disable legacy emulation */ - pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); - - if (pci_enable_device(dev) < 0) - continue; - return setup_uhci(dev, dev->irq, io_addr, io_size); } diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index 50388dccf9d..a37e805f0f0 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -92,7 +92,6 @@ static void urb_rm_priv (urb_t * urb) kfree (urb->hcpriv); urb->hcpriv = NULL; - wake_up (&op_wakeup); } /*-------------------------------------------------------------------------*/ @@ -493,16 +492,16 @@ static int sohci_submit_urb (urb_t * urb) urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1): (le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff; } + urb->status = USB_ST_URB_PENDING; + urb->actual_length = 0; if (ed->state != ED_OPER) /* link the ed into a chain if is not already */ ep_link (ohci, ed); + urb->status = USB_ST_URB_PENDING; td_submit_urb (urb); /* fill the TDs and link it to the ed */ spin_unlock_irqrestore (&usb_ed_lock, flags); - - urb->status = USB_ST_URB_PENDING; - // queue_urb(s, &urb->urb_list); return 0; } @@ -529,6 +528,8 @@ static int sohci_unlink_urb (urb_t * urb) urb_print (urb, "UNLINK", 1); #endif + usb_dec_dev_use (urb->dev); + if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) return rh_unlink_urb (urb); /* a request to the virtual root hub */ @@ -546,19 +547,23 @@ static int sohci_unlink_urb (urb_t * urb) ep_rm_ed (urb->dev, urb_priv->ed); urb_priv->ed->state |= ED_URB_DEL; spin_unlock_irqrestore (&usb_ed_lock, flags); - - add_wait_queue (&op_wakeup, &wait); - current->state = TASK_UNINTERRUPTIBLE; - if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */ - err("unlink URB timeout!"); - remove_wait_queue (&op_wakeup, &wait); - } else + if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { + add_wait_queue (&op_wakeup, &wait); + current->state = TASK_UNINTERRUPTIBLE; + if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */ + err("unlink URB timeout!"); + remove_wait_queue (&op_wakeup, &wait); + urb->status = -ENOENT; + } else + urb->status = -EINPROGRESS; + } else { urb_rm_priv (urb); - - urb->status = -ENOENT; // mark urb as killed - if (urb->complete) - urb->complete ((struct urb *) urb); - usb_dec_dev_use (urb->dev); + if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { + urb->complete (urb); + urb->status = 0; + } else + urb->status = -ENOENT; + } } return 0; } @@ -967,7 +972,7 @@ static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed) /* prepare a TD */ -static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int type, int index) +static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index) { volatile td_t * td, * td_pt; urb_priv_t * urb_priv = urb->hcpriv; @@ -984,7 +989,6 @@ static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int t td->index = index; td->urb = urb; td->hwINFO = cpu_to_le32 (info); - td->type = type; if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) { td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data)) & 0xFFFFF000); @@ -1031,12 +1035,12 @@ static void td_submit_urb (urb_t * urb) info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; while(data_len > 4096) { - td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); data += 4096; data_len -= 4096; cnt++; } info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; - td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); cnt++; writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ break; @@ -1044,20 +1048,20 @@ static void td_submit_urb (urb_t * urb) case PIPE_INTERRUPT: info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle; - td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++); + td_fill (info, data, data_len, urb, cnt++); break; case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill (info, ctrl, 8, urb, ST_ADDR, cnt++); + td_fill (info, ctrl, 8, urb, cnt++); if (data_len > 0) { info = usb_pipeout (urb->pipe)? TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; - td_fill (info, data, data_len, urb, ADD_LEN, cnt++); + td_fill (info, data, data_len, urb, cnt++); } info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; - td_fill (info, NULL, 0, urb, 0, cnt++); + td_fill (info, NULL, 0, urb, cnt++); writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ break; @@ -1065,7 +1069,7 @@ static void td_submit_urb (urb_t * urb) for (cnt = 0; cnt < urb->number_of_packets; cnt++) { td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), (__u8 *) data + urb->iso_frame_desc[cnt].offset, - urb->iso_frame_desc[cnt].length, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + urb->iso_frame_desc[cnt].length, urb, cnt); } break; } @@ -1076,7 +1080,55 @@ static void td_submit_urb (urb_t * urb) /*-------------------------------------------------------------------------* * Done List handling functions *-------------------------------------------------------------------------*/ - + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + __u16 tdPSW; + urb_t * urb = td->urb; + urb_priv_t * urb_priv = urb->hcpriv; + int dlen = 0; + int cc = 0; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (tdINFO & TD_ISO) { + tdPSW = le16_to_cpu (td->hwPSW[0]); + cc = (tdPSW >> 12) & 0xF; + if (cc < 0xE) { + if (usb_pipeout(urb->pipe)) { + dlen = urb->iso_frame_desc[td->index].length; + } else { + dlen = tdPSW & 0x3ff; + } + urb->actual_length += dlen; + urb->iso_frame_desc[td->index].actual_length = dlen; + if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + urb->iso_frame_desc[td->index].status = cc_to_error[cc]; + } + } else { /* BULK, INT, CONTROL DATA */ + if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1; + else + urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + /* replies to the request have to be on a FIFO basis so * we reverse the reversed done-list */ @@ -1130,6 +1182,7 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) unsigned long flags; ed_t * ed; __u32 edINFO; + __u32 tdINFO; td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP; __u32 * td_p; int ctrl = 0, bulk = 0; @@ -1141,16 +1194,25 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); edINFO = le32_to_cpup (&ed->hwINFO); td_p = &ed->hwHeadP; - + for (td = tdHeadP; td != tdTailP; td = td_next) { urb_t * urb = td->urb; urb_priv_t * urb_priv = td->urb->hcpriv; td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0); if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) { + tdINFO = le32_to_cpup (&td->hwINFO); + if (TD_CC_GET (tdINFO) < 0xE) dl_transfer_length (td); *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); if(++ (urb_priv->td_cnt) == urb_priv->length) urb_rm_priv (urb); + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + usb_dec_dev_use (urb->dev); + urb->status = -ECONNRESET; + urb->complete (urb); + } else { + wake_up (&op_wakeup); + } } else { td_p = &td->hwNextTD; } @@ -1167,7 +1229,7 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) } else { ed->state &= ~ED_URB_DEL; - ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); + ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); } if ((ed->type & 3) == CTRL) ctrl |= 1; @@ -1185,6 +1247,8 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) spin_unlock_irqrestore (&usb_ed_lock, flags); } + + /*-------------------------------------------------------------------------*/ /* td done list */ @@ -1193,12 +1257,11 @@ static void dl_done_list (ohci_t * ohci, td_t * td_list) { td_t * td_list_next = NULL; ed_t * ed; - int dlen = 0; int cc = 0; urb_t * urb; urb_priv_t * urb_priv; - __u32 tdINFO, tdBE, tdCBP, edHeadP, edTailP; - __u16 tdPSW; + __u32 tdINFO, edHeadP, edTailP; + unsigned long flags; while (td_list) { @@ -1207,40 +1270,11 @@ static void dl_done_list (ohci_t * ohci, td_t * td_list) urb = td_list->urb; urb_priv = urb->hcpriv; tdINFO = le32_to_cpup (&td_list->hwINFO); - tdBE = le32_to_cpup (&td_list->hwBE); - tdCBP = le32_to_cpup (&td_list->hwCBP); ed = td_list->ed; - if (td_list->type & ST_ADDR) - urb->actual_length = 0; - - if (td_list->type & ADD_LEN) { /* accumulate length of multi td transfers */ - if (tdINFO & TD_ISO) { - tdPSW = le16_to_cpu (td_list->hwPSW[0]); - cc = (tdPSW >> 12) & 0xF; - if (cc < 0xE) { - if (usb_pipeout(urb->pipe)) { - dlen = urb->iso_frame_desc[td_list->index].length; - } else { - dlen = tdPSW & 0x3ff; - } - urb->actual_length += dlen; - urb->iso_frame_desc[td_list->index].actual_length = dlen; - if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - urb->iso_frame_desc[td_list->index].status = cc_to_error[cc]; - } - } else { - if (tdBE != 0) { - if (td_list->hwCBP == 0) - urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1; - else - urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer; - } - } - } + dl_transfer_length(td_list); + /* error code of transfer */ cc = TD_CC_GET (tdINFO); if( cc == TD_CC_STALL) usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); @@ -1936,11 +1970,11 @@ static int hc_start_ohci (struct pci_dev * dev) { unsigned long mem_base; - mem_base = dev->resource[0].start; if (pci_enable_device(dev) < 0) return -ENODEV; pci_set_master (dev); + mem_base = dev->resource[0].start; mem_base = (unsigned long) ioremap_nocache (mem_base, 4096); if (!mem_base) { diff --git a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h index e854e0c1d3d..3ac5dafb24c 100644 --- a/drivers/usb/usb-ohci.h +++ b/drivers/usb/usb-ohci.h @@ -432,7 +432,7 @@ static int ep_unlink(ohci_t * ohci, ed_t * ed); static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load); static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed); /* td */ -static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int type, int index); +static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int index); static void td_submit_urb(urb_t * urb); /* root hub */ static int rh_submit_urb(urb_t * urb); diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c index 42157dbabd2..b9cead4febd 100644 --- a/drivers/usb/usb-storage.c +++ b/drivers/usb/usb-storage.c @@ -1,7 +1,10 @@ /* Driver for USB Mass Storage compliant devices * - * (c) 1999 Michael Gee (michael@linuxspecific.com) - * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * * This driver is based on the 'USB Mass Storage Class' document. This * describes in detail the protocol used to communicate with such @@ -16,6 +19,9 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. */ #include @@ -111,10 +117,15 @@ struct us_data { struct semaphore ip_waitq; /* for CBI interrupts */ int ip_wanted; /* is an IRQ expected? */ + /* interrupt communications data */ struct semaphore irq_urb_sem; /* to protect irq_urb */ struct urb *irq_urb; /* for USB int requests */ unsigned char irqbuf[2]; /* buffer for USB IRQ */ + /* control and bulk communications data */ + struct semaphore current_urb_sem; /* to protect irq_urb */ + struct urb *current_urb; /* non-int USB requests */ + /* mutual exclusion structures */ struct semaphore notify; /* thread begin/end */ struct semaphore sleeper; /* to sleep the thread on */ @@ -147,6 +158,155 @@ static struct usb_driver storage_driver = { * Data transfer routines ***********************************************************************/ +/* This is the completion handler which will wake us up when an URB + * completes. + */ +static void usb_stor_blocking_completion(urb_t *urb) +{ + api_wrapper_data *awd = (api_wrapper_data *)urb->context; + + if (waitqueue_active(awd->wakeup)) + wake_up(awd->wakeup); +} + +/* This is our function to emulate usb_control_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_control_msg(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + devrequest *dr; + + /* allocate the device request structure */ + dr = kmalloc(sizeof(devrequest), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + /* fill in the structure */ + dr->requesttype = requesttype; + dr->request = request; + dr->value = cpu_to_le16(value); + dr->index = cpu_to_le16(index); + dr->length = cpu_to_le16(size); + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, + (unsigned char*) dr, data, size, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + kfree(dr); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule_timeout(10*HZ); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* did we time out? */ + if (us->current_urb->status == -EINPROGRESS) { + US_DEBUGP("usb_stor_control_msg() timeout\n"); + usb_unlink_urb(us->current_urb); + status = -ETIMEDOUT; + } else + status = us->current_urb->status; + + /* return the actual length of the data transferred if no error*/ + if (status >= 0) + status = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + kfree(dr); + return status; +} + +/* This is our function to emulate usb_bulk_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, + unsigned int len, unsigned int *act_len) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule_timeout(10*HZ); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* did we time out? */ + if (us->current_urb->status == -EINPROGRESS) { + US_DEBUGP("usb_stor_bulk_msg() timeout\n"); + usb_unlink_urb(us->current_urb); + status = -ETIMEDOUT; + } else + status = us->current_urb->status; + + /* return the actual length of the data transferred */ + *act_len = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + return status; +} + /* * Transfer one SCSI scatter-gather buffer via bulk transfer * @@ -158,26 +318,33 @@ static struct usb_driver storage_driver = { * timeout limit. Thus we don't have to worry about it for individual * packets. */ -static int us_transfer_partial(struct us_data *us, int pipe, - char *buf, int length) +static int us_transfer_partial(struct us_data *us, char *buf, int length) { int result; int partial; + int pipe; + + /* calculate the appropriate pipe information */ + if (US_DIRECTION(us->srb->cmnd[0])) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); /* transfer the data */ - US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length); - result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, 5*HZ); - US_DEBUGP("bulk_msg returned %d xferred %d/%d\n", + US_DEBUGP("us_transfer_partial(): xfer %d bytes\n", length); + result = usb_stor_bulk_msg(us, buf, pipe, length, &partial); + US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n", result, partial, length); - + /* if we stall, we need to clear it before we go on */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); usb_clear_halt(us->pusb_dev, pipe); } - + /* did we send all the data? */ if (partial == length) { + US_DEBUGP("us_transfer_partial(): transfer complete\n"); return US_BULK_TRANSFER_GOOD; } @@ -186,7 +353,17 @@ static int us_transfer_partial(struct us_data *us, int pipe, /* NAK - that means we've retried a few times allready */ if (result == -ETIMEDOUT) { US_DEBUGP("us_transfer_partial(): device NAKed\n"); + return US_BULK_TRANSFER_FAILED; } + + /* -ENOENT -- we canceled this transfer */ + if (result == -ENOENT) { + US_DEBUGP("us_transfer_partial(): transfer aborted\n"); + return US_BULK_TRANSFER_ABORTED; + } + + /* the catch-all case */ + US_DEBUGP("us_transfer_partial(): unknown error\n"); return US_BULK_TRANSFER_FAILED; } @@ -203,21 +380,12 @@ static int us_transfer_partial(struct us_data *us, int pipe, * function simply determines if we're going to use scatter-gather or not, * and acts appropriately. For now, it also re-interprets the error codes. */ -static void us_transfer(Scsi_Cmnd *srb, int dir_in) +static void us_transfer(Scsi_Cmnd *srb, struct us_data* us, int dir_in) { - struct us_data *us; int i; int result = -1; - unsigned int pipe; struct scatterlist *sg; - /* calculate the appropriate pipe information */ - us = (struct us_data*) srb->host_scribble; - if (dir_in) - pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); - else - pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); - /* are we scatter-gathering? */ if (srb->use_sg) { @@ -226,7 +394,7 @@ static void us_transfer(Scsi_Cmnd *srb, int dir_in) */ sg = (struct scatterlist *) srb->request_buffer; for (i = 0; i < srb->use_sg; i++) { - result = us_transfer_partial(us, pipe, sg[i].address, + result = us_transfer_partial(us, sg[i].address, sg[i].length); if (result) break; @@ -234,7 +402,7 @@ static void us_transfer(Scsi_Cmnd *srb, int dir_in) } else /* no scatter-gather, just make the request */ - result = us_transfer_partial(us, pipe, srb->request_buffer, + result = us_transfer_partial(us, srb->request_buffer, srb->request_bufflen); /* return the result in the data structure itself */ @@ -433,6 +601,8 @@ static void invoke_transport(Scsi_Cmnd *srb, struct us_data *us) /* * Control/Bulk/Interrupt transport */ + +/* The interrupt handler for CBI devices */ static void CBI_irq(struct urb *urb) { struct us_data *us = (struct us_data *)urb->context; @@ -465,13 +635,13 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) /* COMMAND STAGE */ /* let's send the command via the control pipe */ - result = usb_control_msg(us->pusb_dev, - usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - us->ifnum, srb->cmnd, srb->cmd_len, HZ*5); + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ - US_DEBUGP("Call to usb_control_msg() returned %d\n", result); + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); if (result < 0) { /* STALL must be cleared when they are detected */ if (result == -EPIPE) { @@ -493,7 +663,7 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) /* DATA STAGE */ /* transfer the data payload for this command, if one exists*/ if (us_transfer_length(srb, us)) { - us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); US_DEBUGP("CBI data stage result is 0x%x\n", srb->result); } @@ -561,15 +731,14 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) /* COMMAND STAGE */ /* let's send the command via the control pipe */ - result = usb_control_msg(us->pusb_dev, - usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - us->ifnum, srb->cmnd, srb->cmd_len, HZ*5); + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); if (result < 0) { - US_DEBUGP("Call to usb_control_msg() returned %d\n", result); - /* a stall is a fatal condition from the device */ if (result == -EPIPE) { US_DEBUGP("-- Stall on control pipe. Clearing\n"); @@ -587,7 +756,7 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) /* DATA STAGE */ /* transfer the data payload for this command, if one exists*/ if (us_transfer_length(srb, us)) { - us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); US_DEBUGP("CB data stage result is 0x%x\n", srb->result); } @@ -602,6 +771,39 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) /* * Bulk only transport */ + +/* Determine what the maximum LUN supported is */ +static int Bulk_max_lun(struct us_data *us) +{ + unsigned char data; + int result; + int pipe; + + /* issue the command */ + pipe = usb_rcvctrlpipe(us->pusb_dev, 0); + result = usb_control_msg(us->pusb_dev, pipe, + US_BULK_GET_MAX_LUN, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + 0, us->ifnum, &data, sizeof(data), HZ); + + US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", + result, data); + + /* if we have a successful request, return the result */ + if (!result) + return data; + + /* if we get a STALL, clear the stall */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } + + /* return the default -- no LUNs */ + return 0; +} + static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) { struct bulk_cb_wrap bcb; @@ -629,8 +831,8 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n", le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun, bcb.DataTransferLength, bcb.Flags, bcb.Length); - result = usb_bulk_msg(us->pusb_dev, pipe, &bcb, - US_BULK_CB_WRAP_LEN, &partial, HZ*5); + result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, + &partial); US_DEBUGP("Bulk command transfer result=%d\n", result); /* if we stall, we need to clear it before we go on */ @@ -643,7 +845,7 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) if (result == 0) { /* send/receive data payload, if there is any */ if (bcb.DataTransferLength) { - us_transfer(srb, bcb.Flags); + us_transfer(srb, us, bcb.Flags); US_DEBUGP("Bulk data transfer result 0x%x\n", srb->result); } @@ -658,8 +860,8 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) /* get CSW for device status */ US_DEBUGP("Attempting to get CSW...\n"); - result = usb_bulk_msg(us->pusb_dev, pipe, &bcs, - US_BULK_CS_WRAP_LEN, &partial, HZ*2); + result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, + &partial); /* did the attempt to read the CSW fail? */ if (result == -EPIPE) { @@ -668,8 +870,8 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) /* get the status again */ US_DEBUGP("Attempting to get CSW (2nd try)...\n"); - result = usb_bulk_msg(us->pusb_dev, pipe, &bcs, - US_BULK_CS_WRAP_LEN, &partial, HZ*2); + result = usb_stor_bulk_msg(us, &bcs, pipe, + US_BULK_CS_WRAP_LEN, &partial); /* if it fails again, we need a reset and return an error*/ if (result == -EPIPE) { @@ -1025,9 +1227,9 @@ static int Bulk_reset(struct us_data *us) result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_BULK_RESET, + US_BULK_RESET_REQUEST, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5); + 0, us->ifnum, NULL, 0, HZ*5); if (result < 0) US_DEBUGP("Bulk hard reset failed %d\n", result); @@ -1049,7 +1251,7 @@ static int Bulk_reset(struct us_data *us) static const char* us_info(struct Scsi_Host *host) { - return "SCSI emulation for USB Mass Storage devices\n"; + return "SCSI emulation for USB Mass Storage devices"; } /* detect a virtual adapter (always works) */ @@ -1109,6 +1311,7 @@ static int us_release(struct Scsi_Host *psh) /* free the data structure we were using */ US_DEBUGP("-- freeing private host data structure\n"); + kfree(us->current_urb); kfree(us); (struct us_data*)psh->hostdata[0] = NULL; @@ -1289,22 +1492,11 @@ static Scsi_Host_Template my_host_template = { }; static unsigned char sense_notready[] = { - 0x70, /* current error */ - 0x00, - 0x02, /* not ready */ - 0x00, - 0x00, - 0x0a, /* additional length */ - 0x00, - 0x00, - 0x00, - 0x00, - 0x04, /* not ready */ - 0x03, /* manual intervention */ - 0x00, - 0x00, - 0x00, - 0x00 + [0] = 0x70, /* current error */ + [2] = 0x02, /* not ready */ + [5] = 0x0a, /* additional length */ + [10] = 0x04, /* not ready */ + [11] = 0x03 /* manual intervention */ }; static int usb_stor_control_thread(void * __us) @@ -1429,25 +1621,31 @@ static int usb_stor_control_thread(void * __us) return 0; } +/* This is the list of devices we recognize, along with their flag data */ static struct us_unusual_dev us_unusual_dev_list[] = { + { 0x03f0, 0x0107, 0x0200, + "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, + { 0x04e6, 0x0001, 0x0200, + "Matshita LS-120", US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x04e6, 0x0002, 0x0100, + "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x04e6, 0x0006, 0x0100, + "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, { 0x057b, 0x0000, 0x0114, - "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN }, + "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, { 0x059b, 0x0030, 0x0100, - "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN }, + "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN}, + { 0x0693, 0x0002, 0x0100, + "Hagiwara FlashGate SmartMedia", US_SC_SCSI, US_PR_BULK, + US_FL_ALT_LENGTH}, { 0x0781, 0x0001, 0x0200, - "Sandisk ImageMate (w/eject button)", US_SC_SCSI, US_PR_CB, - US_FL_SINGLE_LUN | US_FL_START_STOP }, + "Sandisk ImageMate (SDDR-01)", US_SC_SCSI, US_PR_CB, + US_FL_SINGLE_LUN | US_FL_START_STOP}, { 0x0781, 0x0002, 0x0009, - "** SECRET DEVICE **", US_SC_SCSI, US_PR_BULK, - US_FL_SINGLE_LUN | US_FL_IGNORE_SER }, + "Sandisk Imagemate (SDDR-31)", US_SC_SCSI, US_PR_BULK, + US_FL_SINGLE_LUN | US_FL_IGNORE_SER}, { 0x07af, 0x0005, 0x0100, "Microtech USB-SCSI-HD50", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x04e6, 0x0002, 0x0100, - "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x04e6, 0x0006, 0x0100, - "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x03f0, 0x0107, 0x0200, - "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, { 0x0000, 0x0000, 0x0, "", 0, 0, 0} }; @@ -1713,12 +1911,20 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } memset(ss, 0, sizeof(struct us_data)); + /* allocate the URB we're going to use */ + ss->current_urb = usb_alloc_urb(0); + if (!ss->current_urb) { + kfree(ss); + return NULL; + } + /* Initialize the mutexes only when the struct is new */ init_MUTEX_LOCKED(&(ss->sleeper)); init_MUTEX_LOCKED(&(ss->notify)); init_MUTEX_LOCKED(&(ss->ip_waitq)); init_MUTEX(&(ss->queue_exclusion)); init_MUTEX(&(ss->irq_urb_sem)); + init_MUTEX(&(ss->current_urb_sem)); init_MUTEX(&(ss->dev_semaphore)); /* copy over the subclass and protocol data */ @@ -1774,11 +1980,14 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) ss->transport_name = "Bulk"; ss->transport = Bulk_transport; ss->transport_reset = Bulk_reset; + /* FIXME: for testing purposes only */ + Bulk_max_lun(ss); break; default: ss->transport_name = "Unknown"; up(&us_list_semaphore); + kfree(ss->current_urb); kfree(ss); return NULL; break; @@ -1802,6 +2011,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n"); US_DEBUGP("if you see this message.\n"); up(&us_list_semaphore); + kfree(ss->current_urb); kfree(ss); return NULL; break; @@ -1824,6 +2034,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) default: ss->protocol_name = "Unknown"; up(&us_list_semaphore); + kfree(ss->current_urb); kfree(ss); return NULL; break; @@ -1859,6 +2070,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) if (ss->pid < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n"); + kfree(ss->current_urb); kfree(ss); return NULL; } diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h index 0d5152e367e..6e54b059be0 100644 --- a/drivers/usb/usb-storage.h +++ b/drivers/usb/usb-storage.h @@ -77,16 +77,17 @@ struct bulk_cs_wrap { #define US_BULK_STAT_FAIL 1 #define US_BULK_STAT_PHASE 2 -#define US_BULK_RESET 0xff -#define US_BULK_RESET_SOFT 1 -#define US_BULK_RESET_HARD 0 +/* bulk-only class specific requests */ +#define US_BULK_RESET_REQUEST 0xff +#define US_BULK_GET_MAX_LUN 0xfe /* * us_bulk_transfer() return codes */ -#define US_BULK_TRANSFER_GOOD 0 -#define US_BULK_TRANSFER_SHORT 1 -#define US_BULK_TRANSFER_FAILED 2 +#define US_BULK_TRANSFER_GOOD 0 /* good transfer */ +#define US_BULK_TRANSFER_SHORT 1 /* transfered less than expected */ +#define US_BULK_TRANSFER_FAILED 2 /* transfer died in the middle */ +#define US_BULK_TRANSFER_ABORTED 3 /* transfer canceled */ /* * Transport return codes @@ -95,6 +96,7 @@ struct bulk_cs_wrap { #define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */ #define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */ #define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */ +#define USB_STOR_TRANSPORT_ABORTED 3 /* Transport aborted */ /* * CBI accept device specific command diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index 8a33186128f..f2c51f2058c 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -12,7 +12,7 @@ * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Randy Dunlap * - * $Id: usb-uhci.c,v 1.228 2000/04/02 19:55:51 acher Exp $ + * $Id: usb-uhci.c,v 1.231 2000/05/13 15:34:17 acher Exp $ */ #include @@ -48,7 +48,7 @@ /* This enables an extra UHCI slab for memory debugging */ #define DEBUG_SLAB -#define VERSTR "$Revision: 1.228 $ time " __TIME__ " " __DATE__ +#define VERSTR "$Revision: 1.231 $ time " __TIME__ " " __DATE__ #include #include "usb-uhci.h" @@ -109,10 +109,10 @@ void clean_descs(uhci_t *s, int force) while (q != &s->free_desc) { qh = list_entry (q, uhci_desc_t, horizontal); + q=qh->horizontal.prev; + if ((qh->last_used!=now) || force) delete_qh(s,qh); - - q=qh->horizontal.prev; } } /*-------------------------------------------------------------------*/ @@ -1142,6 +1142,12 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force) if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) urb->status = -ENOENT; // now the urb is really dead + switch (usb_pipetype (pipe)) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + uhci_clean_iso_step2(s, urb_priv); + break; + } usb_dec_dev_use (dev); #ifdef DEBUG_SLAB @@ -1149,12 +1155,7 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force) #else kfree (urb_priv); #endif - switch (usb_pipetype (pipe)) { - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - uhci_clean_iso_step2(s, urb_priv); - break; - } + list_del (&urb->urb_list); } } @@ -1168,7 +1169,9 @@ _static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb) async_dbg("unlink_urb_async called %p",urb); - if (urb->status == -EINPROGRESS) { + if ((urb->status == -EINPROGRESS) || + ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags)) + { ((urb_priv_t*)urb->hcpriv)->started = ~0; dequeue_urb (s, urb); @@ -1560,7 +1563,7 @@ _static int uhci_submit_urb (urb_t *urb) urb->hcpriv = urb_priv; INIT_LIST_HEAD (&urb_priv->desc_list); - urb_priv->short_control_packet = 0; + urb_priv->flags = 0; dbg("submit_urb: scheduling %p", urb); urb_priv->next_queued_urb = NULL; urb_priv->prev_queued_urb = NULL; @@ -2151,7 +2154,7 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode) status stage is completed */ - if (urb_priv->short_control_packet && + if (urb_priv->flags && ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) goto transfer_finished; @@ -2199,7 +2202,7 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode) dbg("short packet during control transfer, retrigger status stage @ %p",last_desc); //uhci_show_td (desc); //uhci_show_td (last_desc); - urb_priv->short_control_packet=1; + urb_priv->flags = 1; // mark as short control packet return 0; } } @@ -2280,35 +2283,43 @@ _static int process_interrupt (uhci_t *s, urb_t *urb) if (urb->complete) { //dbg("process_interrupt: calling completion, status %i",status); urb->status = status; - + ((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion + spin_unlock(&s->urb_list_lock); urb->complete ((struct urb *) urb); spin_lock(&s->urb_list_lock); - - urb->status = -EINPROGRESS; + + ((urb_priv_t*)urb->hcpriv)->flags=0; } + + if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) && + (urb->status != -ENOENT)) { + + urb->status = -EINPROGRESS; - // Recycle INT-TD if interval!=0, else mark TD as one-shot - if (urb->interval) { - - desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE); - if (status==0) { - ((urb_priv_t*)urb->hcpriv)->started=jiffies; - desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); - usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); - } else { - desc->hw.td.info |= (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + // Recycle INT-TD if interval!=0, else mark TD as one-shot + if (urb->interval) { + + desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE); + if (status==0) { + ((urb_priv_t*)urb->hcpriv)->started=jiffies; + desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } else { + desc->hw.td.info |= (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + } + desc->hw.td.status= (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); + mb(); + } + else { + uhci_unlink_urb_async(s, urb); + desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD } - desc->hw.td.status= (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | - (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); - mb(); - } - else { - desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD } } @@ -2334,7 +2345,7 @@ _static int process_iso (uhci_t *s, urb_t *urb, int mode) dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s), urb->number_of_packets,mode,desc->hw.td.status); - for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) { + for (i = 0; p != &urb_priv->desc_list; i++) { desc = list_entry (p, uhci_desc_t, desc_list); //uhci_show_td(desc); @@ -2378,8 +2389,9 @@ _static int process_iso (uhci_t *s, urb_t *urb, int mode) dbg("process_iso: %i: len:%d %08x status:%x", i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status); - delete_desc (desc); list_del (p); + p = p->next; + delete_desc (desc); } dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length); @@ -2824,7 +2836,6 @@ int __init uhci_init (void) if (type != 0) continue; - if (pci_enable_device (dev) < 0) continue; diff --git a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h index 3c5717d1eb3..67eb4d210cb 100644 --- a/drivers/usb/usb-uhci.h +++ b/drivers/usb/usb-uhci.h @@ -2,7 +2,7 @@ #define __LINUX_UHCI_H /* - $Id: usb-uhci.h,v 1.54 2000/04/02 19:55:53 acher Exp $ + $Id: usb-uhci.h,v 1.55 2000/05/13 12:50:30 acher Exp $ */ #define MODNAME "usb-uhci" #define UHCI_LATENCY_TIMER 0 @@ -160,7 +160,7 @@ typedef struct { uhci_desc_t *bottom_qh; uhci_desc_t *next_qh; // next helper QH char use_loop; - char short_control_packet; + char flags; } urb_priv_t, *purb_priv_t; struct virt_root_hub { diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c index cb77f0717f2..3fa4a2d35b7 100644 --- a/drivers/usb/wacom.c +++ b/drivers/usb/wacom.c @@ -104,8 +104,8 @@ struct wacom_features { int distance_max; void (*irq)(struct urb *urb); unsigned long evbit; - unsigned long relbit; unsigned long absbit; + unsigned long relbit; unsigned long btnbit; unsigned long digibit; }; diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 2e87c79d7d6..8a8bea5a0d4 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -1636,3 +1636,5 @@ static void __exit cyberpro_exit(void) module_init(cyber2000fb_init); #endif module_exit(cyberpro_exit); + +MODULE_DEVICE_TABLE(pci, cyberpro_pci_table); diff --git a/drivers/video/cyber2000fb.h b/drivers/video/cyber2000fb.h index eefc038da62..c6d60555358 100644 --- a/drivers/video/cyber2000fb.h +++ b/drivers/video/cyber2000fb.h @@ -3,6 +3,7 @@ * * Integraphics Cyber2000 frame buffer device */ +#include #define cyber2000_outb(dat,reg) writeb(dat, CyberRegs + reg) #define cyber2000_outw(dat,reg) writew(dat, CyberRegs + reg) diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 4d214b02502..4b12369645c 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -69,7 +69,7 @@ if(!(expr)) { \ printk( "Assertion failed! %s,%s,%s,line=%d\n",\ #expr,__FILE__,__FUNCTION__,__LINE__); \ - *(int*)0 = 0; \ + BUG(); \ } #else #define assert(expr) @@ -127,7 +127,7 @@ static struct pci_device_id rivafb_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_VTNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_VTNT2 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UVTNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_VTNT2 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_ITNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_ITNT2 }, - { 0, }, /* terminate list */ + { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, rivafb_pci_tbl); diff --git a/fs/Makefile b/fs/Makefile index 9219a138f6e..0693daf69fd 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -21,7 +21,7 @@ ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ nfsd nls devpts devfs adfs partitions qnx4 udf bfs cramfs \ openpromfs autofs4 ramfs -SUB_DIRS := partitions +SUB_DIRS := ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -29,6 +29,14 @@ else O_OBJS += noquot.o endif +ifdef CONFIG_PROC_FS +SUB_DIRS += proc +endif + +SUB_DIRS += partitions + +# Do not add any filesystems before this line + ifeq ($(CONFIG_EXT2_FS),y) SUB_DIRS += ext2 else @@ -93,10 +101,6 @@ else endif endif -ifdef CONFIG_PROC_FS -SUB_DIRS += proc -endif - ifeq ($(CONFIG_BFS_FS),y) SUB_DIRS += bfs else diff --git a/fs/affs/namei.c b/fs/affs/namei.c index c1a849a706d..e81e321e384 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -249,7 +249,6 @@ affs_unlink(struct inode *dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_version = ++event; mark_inode_dirty(inode); - d_delete(dentry); mark_inode_dirty(dir); retval = 0; @@ -380,7 +379,6 @@ affs_rmdir(struct inode *dir, struct dentry *dentry) dir->i_version = ++event; mark_inode_dirty(dir); mark_inode_dirty(inode); - d_delete(dentry); rmdir_done: affs_brelse(bh); diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index b71c3a819bd..9c114f1c2bf 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -12,29 +12,10 @@ #include "autofs_i.h" -static int autofs_dir_readdir(struct file *filp, - void *dirent, filldir_t filldir) -{ - struct inode *inode=filp->f_dentry->d_inode; - - switch((unsigned long) filp->f_pos) - { - case 0: - if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) - return 0; - filp->f_pos++; - /* fall through */ - case 1: - if (filldir(dirent, "..", 2, 1, AUTOFS_ROOT_INO) < 0) - return 0; - filp->f_pos++; - /* fall through */ - } - return 1; -} - /* - * No entries except for "." and "..", both of which are handled by the VFS layer + * No entries except for "." and "..", both of which are handled by the VFS + * layer. So all children are negative and dcache-based versions of operations + * are OK. */ static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) { @@ -44,7 +25,7 @@ static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) struct file_operations autofs_dir_operations = { read: generic_read_dir, - readdir: autofs_dir_readdir, + readdir: dcache_readdir, }; struct inode_operations autofs_dir_inode_operations = { diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 168d7861b7b..448143fd079 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -133,8 +133,8 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) autofs_say(ent->name,ent->len); autofs_init_usage(dh,ent); - if ( ent->dentry ) - ent->dentry->d_count++; + if (ent->dentry) + dget(ent->dentry); dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; ent->next = *dhnp; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 7f73378028f..00951bf8eb0 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -16,7 +16,6 @@ #include #include "autofs_i.h" -static int autofs4_dir_readdir(struct file *,void *,filldir_t); static struct dentry *autofs4_dir_lookup(struct inode *,struct dentry *); static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *); static int autofs4_dir_unlink(struct inode *,struct dentry *); @@ -27,13 +26,13 @@ static struct dentry *autofs4_root_lookup(struct inode *,struct dentry *); struct file_operations autofs4_root_operations = { read: generic_read_dir, - readdir: autofs4_dir_readdir, + readdir: dcache_readdir, ioctl: autofs4_root_ioctl, }; struct file_operations autofs4_dir_operations = { read: generic_read_dir, - readdir: autofs4_dir_readdir, + readdir: dcache_readdir, }; struct inode_operations autofs4_root_inode_operations = { @@ -52,67 +51,6 @@ struct inode_operations autofs4_dir_inode_operations = { rmdir: autofs4_dir_rmdir, }; -static inline struct dentry *nth_child(struct dentry *dir, int nr) -{ - struct list_head *tmp = dir->d_subdirs.next; - - while(tmp != &dir->d_subdirs) { - if (nr-- == 0) - return list_entry(tmp, struct dentry, d_child); - tmp = tmp->next; - } - return NULL; -} - -static int autofs4_dir_readdir(struct file *filp, void *dirent, - filldir_t filldir) -{ - struct autofs_sb_info *sbi; - struct autofs_info *ino; - struct dentry *dentry = filp->f_dentry; - struct dentry *dent_ptr; - struct inode *dir = dentry->d_inode; - struct list_head *cursor; - off_t nr; - - sbi = autofs4_sbi(dir->i_sb); - ino = autofs4_dentry_ino(dentry); - nr = filp->f_pos; - - switch(nr) - { - case 0: - if (filldir(dirent, ".", 1, nr, dir->i_ino) < 0) - return 0; - filp->f_pos = ++nr; - /* fall through */ - case 1: - if (filldir(dirent, "..", 2, nr, dentry->d_parent->d_inode->i_ino) < 0) - return 0; - filp->f_pos = ++nr; - /* fall through */ - default: - dent_ptr = nth_child(dentry, nr-2); - if (dent_ptr == NULL) - break; - - cursor = &dent_ptr->d_child; - - while(cursor != &dentry->d_subdirs) { - dent_ptr = list_entry(cursor, struct dentry, d_child); - if (dent_ptr->d_inode && - filldir(dirent, dent_ptr->d_name.name, dent_ptr->d_name.len, nr, - dent_ptr->d_inode->i_ino) < 0) - return 0; - filp->f_pos = ++nr; - cursor = cursor->next; - } - break; - } - - return 0; -} - /* Update usage from here to top of tree, so that scan of top-level directories will give a useful result */ static void autofs4_update_usage(struct dentry *dentry) diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 38881a1b666..bd8be88c3f2 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -32,11 +32,6 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) unsigned int offset; int block; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - printf("Bad inode or not a directory %s:%08lx\n", bdevname(dev), dir->i_ino); - return -EBADF; - } - if (f->f_pos & (BFS_DIRENT_SIZE-1)) { printf("Bad f_pos=%08lx for %s:%08lx\n", (unsigned long)f->f_pos, bdevname(dev), dir->i_ino); @@ -188,7 +183,6 @@ static int bfs_unlink(struct inode * dir, struct dentry * dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); error = 0; out_brelse: diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 49d818e21e7..ef4af4dfefd 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -86,6 +86,8 @@ static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file) struct user dump; #if defined(__alpha__) # define START_DATA(u) (u.start_data) +#elif defined(__arm__) +# define START_DATA(u) ((u.u_tsize << PAGE_SHIFT) + u.start_code) #elif defined(__sparc__) # define START_DATA(u) (u.u_tsize) #elif defined(__i386__) || defined(__mc68000__) @@ -217,7 +219,7 @@ static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm) envp = (char **) sp; sp -= argc+1; argv = (char **) sp; -#if defined(__i386__) || defined(__mc68000__) +#if defined(__i386__) || defined(__mc68000__) || defined(__arm__) put_user((unsigned long) envp,--sp); put_user((unsigned long) argv,--sp); #endif diff --git a/fs/buffer.c b/fs/buffer.c index a7a6f79a8a4..47d690fa446 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1324,7 +1324,7 @@ int block_flushpage(struct page *page, unsigned long offset) * instead. */ if (!offset) { - if (!try_to_free_buffers(page)) { + if (!try_to_free_buffers(page, 0)) { atomic_inc(&buffermem_pages); return 0; } @@ -2121,15 +2121,17 @@ out: * This all is required so that we can free up memory * later. */ -static void sync_page_buffers(struct buffer_head *bh) +static void sync_page_buffers(struct buffer_head *bh, int wait) { - struct buffer_head * tmp; + struct buffer_head * tmp = bh; - tmp = bh; do { struct buffer_head *p = tmp; tmp = tmp->b_this_page; - if (buffer_dirty(p) && !buffer_locked(p)) + if (buffer_locked(p)) { + if (wait) + __wait_on_buffer(p); + } else if (buffer_dirty(p)) ll_rw_block(WRITE, 1, &p); } while (tmp != bh); } @@ -2151,7 +2153,7 @@ static void sync_page_buffers(struct buffer_head *bh) * obtain a reference to a buffer head within a page. So we must * lock out all of these paths to cleanly toss the page. */ -int try_to_free_buffers(struct page * page) +int try_to_free_buffers(struct page * page, int wait) { struct buffer_head * tmp, * bh = page->buffers; int index = BUFSIZE_INDEX(bh->b_size); @@ -2201,7 +2203,7 @@ busy_buffer_page: spin_unlock(&free_list[index].lock); write_unlock(&hash_table_lock); spin_unlock(&lru_list_lock); - sync_page_buffers(bh); + sync_page_buffers(bh, wait); return 0; } diff --git a/fs/coda/cache.c b/fs/coda/cache.c index fa8a66e0ca8..68be7a69d3a 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -259,8 +259,6 @@ int coda_cache_check(struct inode *inode, int mask) void coda_purge_dentries(struct inode *inode) { - struct list_head *tmp, *head = &inode->i_dentry; - if (!inode) return ; @@ -268,23 +266,7 @@ void coda_purge_dentries(struct inode *inode) iget(inode->i_sb, inode->i_ino); /* catch the dentries later if some are still busy */ coda_flag_inode(inode, C_PURGE); - -restart: - tmp = head; - while ((tmp = tmp->next) != head) { - struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); - if (!dentry->d_count) { - CDEBUG(D_DOWNCALL, - "coda_free_dentries: freeing %s/%s, i_count=%d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - inode->i_count); - dget(dentry); - d_drop(dentry); - dput(dentry); - goto restart; - } - - } + d_prune_aliases(inode); iput(inode); } @@ -311,28 +293,18 @@ static void coda_flag_children(struct dentry *parent, int flag) void coda_flag_inode_children(struct inode *inode, int flag) { - struct list_head *alias; struct dentry *alias_de; ENTRY; - if ( !inode ) + if ( !inode || !S_ISDIR(inode->i_mode)) return; - if (list_empty(&inode->i_dentry)) - return; - - /* I believe that shrink_dcache_parent will not - remove dentries from the alias list. If it - does we are toast. - */ - alias = inode->i_dentry.next; - while ( alias != &inode->i_dentry ) { - alias_de = list_entry(alias, struct dentry, d_alias); - coda_flag_children(alias_de, flag); - alias = alias->next; - shrink_dcache_parent(alias_de); - } - + alias_de = d_find_alias(inode); + if (!alias_de) + return; + coda_flag_children(alias_de, flag); + shrink_dcache_parent(alias_de); + dput(alias_de); } /* this will not zap the inode away */ diff --git a/fs/coda/dir.c b/fs/coda/dir.c index bb51b0c057a..83f7bbcc5e6 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -45,7 +45,7 @@ static int coda_readdir(struct file *file, void *dirent, filldir_t filldir); /* dentry ops */ static int coda_dentry_revalidate(struct dentry *de, int); -static void coda_dentry_delete(struct dentry *); +static int coda_dentry_delete(struct dentry *); /* support routines */ static int coda_venus_readdir(struct file *filp, void *dirent, @@ -434,7 +434,6 @@ int coda_unlink(struct inode *dir, struct dentry *de) /* cache management: mtime has changed, ask Venus */ dircnp->c_flags |= C_VATTR; de->d_inode->i_nlink--; - d_delete(de); return 0; } @@ -801,20 +800,21 @@ static int coda_dentry_revalidate(struct dentry *de, int flags) * This is the callback from dput() when d_count is going to 0. * We use this to unhash dentries with bad inodes. */ -static void coda_dentry_delete(struct dentry * dentry) +static int coda_dentry_delete(struct dentry * dentry) { int flags; if (!dentry->d_inode) - return ; + return 0; flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE; if (is_bad_inode(dentry->d_inode) || flags) { CDEBUG(D_DOWNCALL, "bad inode, unhashing %s/%s, %ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_ino); - d_drop(dentry); + return 1; } + return 0; } diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 1a7fb195ce2..b88c602c602 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -296,6 +296,8 @@ static int coda_psdev_release(struct inode * inode, struct file * file) vcp->vc_inuse, vcp->vc_pid, current->pid); if ( vcp->vc_pid != current->pid ) { + /* FIXME: this is broken. If venus does fork(), accounting goes wrong */ + printk( "Closed by someone else than caller?\n" ); return 0; } diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 279b0bfef76..3f51c2aa191 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -84,15 +84,17 @@ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inod #define NEXT_BUFFER(_ix) ((_ix) ^ 1) /* - * BLKS_PER_BUF_SHIFT must be at least 1 to allow for "compressed" - * data that takes up more space than the original. 1 is guaranteed - * to suffice, though. Larger values provide more read-ahead and - * proportionally less wastage at the end of the buffer. + * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed" + * data that takes up more space than the original and with unlucky + * alignment. */ -#define BLKS_PER_BUF_SHIFT (2) -#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) -static unsigned char read_buffers[READ_BUFFERS][BLKS_PER_BUF][PAGE_CACHE_SIZE]; +#define BLKS_PER_BUF_SHIFT (2) +#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) +#define BUFFER_SIZE (BLKS_PER_BUF*PAGE_CACHE_SIZE) + +static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; static unsigned buffer_blocknr[READ_BUFFERS]; +static struct super_block * buffer_dev[READ_BUFFERS]; static int next_buffer = 0; /* @@ -102,19 +104,27 @@ static int next_buffer = 0; static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) { struct buffer_head * bh_array[BLKS_PER_BUF]; - unsigned i, blocknr, last_blocknr, buffer; + unsigned i, blocknr, buffer; + char *data; if (!len) return NULL; blocknr = offset >> PAGE_CACHE_SHIFT; - last_blocknr = (offset + len - 1) >> PAGE_CACHE_SHIFT; - if (last_blocknr - blocknr >= BLKS_PER_BUF) - goto eek; resume: offset &= PAGE_CACHE_SIZE - 1; + + /* Check if an existing buffer already has the data.. */ for (i = 0; i < READ_BUFFERS; i++) { - if ((blocknr >= buffer_blocknr[i]) && - (last_blocknr - buffer_blocknr[i] < BLKS_PER_BUF)) - return &read_buffers[i][blocknr - buffer_blocknr[i]][offset]; + unsigned int blk_offset; + + if (buffer_dev[i] != sb) + continue; + if (blocknr < buffer_blocknr[i]) + continue; + blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT; + blk_offset += offset; + if (blk_offset + len > BUFFER_SIZE) + continue; + return read_buffers[i] + blk_offset; } /* Ok, read in BLKS_PER_BUF pages completely first. */ @@ -125,24 +135,19 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i buffer = next_buffer; next_buffer = NEXT_BUFFER(buffer); buffer_blocknr[buffer] = blocknr; + buffer_dev[buffer] = sb; + + data = read_buffers[buffer]; for (i = 0; i < BLKS_PER_BUF; i++) { struct buffer_head * bh = bh_array[i]; if (bh) { - memcpy(read_buffers[buffer][i], bh->b_data, PAGE_CACHE_SIZE); + memcpy(data, bh->b_data, PAGE_CACHE_SIZE); bforget(bh); } else - memset(read_buffers[buffer][i], 0, PAGE_CACHE_SIZE); + memset(data, 0, PAGE_CACHE_SIZE); + data += PAGE_CACHE_SIZE; } - return read_buffers[buffer][0] + offset; - - eek: - printk(KERN_ERR - "cramfs (device %s): requested chunk (%u:+%u) bigger than buffer\n", - bdevname(sb->s_dev), offset, len); - /* TODO: return EIO to process or kill the current process - instead of resuming. */ - *((int *)0) = 0; /* XXX: doesn't work on all archs */ - goto resume; + return read_buffers[buffer] + offset; } diff --git a/fs/dcache.c b/fs/dcache.c index 1b3ff98b253..ad897ff3864 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -131,7 +131,8 @@ repeat: * Each fs will have to watch for this. */ if (dentry->d_op && dentry->d_op->d_delete) { - dentry->d_op->d_delete(dentry); + if (dentry->d_op->d_delete(dentry)) + d_drop(dentry); count = dentry->d_count - 1; if (count != 0) @@ -220,6 +221,53 @@ int d_invalidate(struct dentry * dentry) return 0; } +/** + * d_find_alias - grab a hashed alias of inode + * @inode: inode in question + * + * If inode has a hashed alias - acquire the reference to alias and + * return it. Otherwise return NULL. Notice that if inode is a directory + * there can be only one alias and it can be unhashed only if it has + * no children. + */ + +struct dentry * d_find_alias(struct inode *inode) +{ + struct list_head *head, *next, *tmp; + struct dentry *alias; + + head = &inode->i_dentry; + next = inode->i_dentry.next; + while (next != head) { + tmp = next; + next = tmp->next; + alias = list_entry(tmp, struct dentry, d_alias); + if (!d_unhashed(alias)) + return dget(alias); + } + return NULL; +} + +/* + * Try to kill dentries associated with this inode. + * WARNING: you must own a reference to inode. + */ +void d_prune_aliases(struct inode *inode) +{ + struct list_head *tmp, *head = &inode->i_dentry; +restart: + tmp = head; + while ((tmp = tmp->next) != head) { + struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); + if (!dentry->d_count) { + dget(dentry); + d_drop(dentry); + dput(dentry); + goto restart; + } + } +} + /* * Throw away a dentry - free the inode, dput the parent. * This requires that the LRU list has already been @@ -384,37 +432,6 @@ resume: return 0; /* No mount points found in tree */ } -int d_active_refs(struct dentry *root) -{ - struct dentry *this_parent = root; - struct list_head *next; - int count = root->d_count; - -repeat: - next = this_parent->d_subdirs.next; -resume: - while (next != &this_parent->d_subdirs) { - struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); - next = tmp->next; - /* Decrement count for unused children */ - count += (dentry->d_count - 1); - if (!list_empty(&dentry->d_subdirs)) { - this_parent = dentry; - goto repeat; - } - } - /* - * All done at this level ... ascend and resume the search. - */ - if (this_parent != root) { - next = this_parent->d_child.next; - this_parent = this_parent->d_parent; - goto resume; - } - return count; -} - /* * Search the dentry child list for the specified parent, * and move any unused dentries to the end of the unused @@ -788,7 +805,7 @@ void d_rehash(struct dentry * entry) * Note that we have to be a lot more careful about getting the hash * switched - we have to switch the hash value properly even if it * then no longer matches the actual (corrupted) string of the target. - * The has value has to match the hash queue that the dentry is on.. + * The hash value has to match the hash queue that the dentry is on.. */ static inline void switch_names(struct dentry * dentry, struct dentry * target) { @@ -948,7 +965,12 @@ global_root: asmlinkage long sys_getcwd(char *buf, unsigned long size) { int error; - struct dentry *pwd = current->fs->pwd; + struct vfsmount *pwdmnt; + struct dentry *pwd; + + lock_kernel(); + pwdmnt = mntget(current->fs->pwdmnt); + pwd = dget(current->fs->pwd); error = -ENOENT; /* Has the current directory has been unlinked? */ @@ -959,9 +981,7 @@ asmlinkage long sys_getcwd(char *buf, unsigned long size) unsigned long len; char * cwd; - lock_kernel(); cwd = d_path(pwd, current->fs->pwdmnt, page, PAGE_SIZE); - unlock_kernel(); error = -ERANGE; len = PAGE_SIZE + page - cwd; @@ -973,6 +993,9 @@ asmlinkage long sys_getcwd(char *buf, unsigned long size) free_page((unsigned long) page); } } + dput(pwd); + mntput(pwdmnt); + unlock_kernel(); return error; } @@ -1010,6 +1033,34 @@ int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry) return result; } +void d_genocide(struct dentry *root) +{ + struct dentry *this_parent = root; + struct list_head *next; + +repeat: + next = this_parent->d_subdirs.next; +resume: + while (next != &this_parent->d_subdirs) { + struct list_head *tmp = next; + struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + next = tmp->next; + if (d_unhashed(dentry)||!dentry->d_inode) + continue; + if (!list_empty(&dentry->d_subdirs)) { + this_parent = dentry; + goto repeat; + } + dentry->d_count--; + } + if (this_parent != root) { + next = this_parent->d_child.next; + this_parent->d_count--; + this_parent = this_parent->d_parent; + goto resume; + } +} + /** * find_inode_number - check for dentry with name * @dir: directory to check diff --git a/fs/devfs/base.c b/fs/devfs/base.c index 567156868b7..040e0b79af3 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -555,7 +555,7 @@ typedef struct wait_queue *wait_queue_head_t; #define OOPS(format, args...) {printk (format, ## args); \ printk ("Forcing Oops\n"); \ - *(int *) 0 = 0;} + BUG();} struct directory_type { @@ -729,17 +729,21 @@ static struct file_operations devfsd_fops = /* Support functions follow */ + +/** + * search_for_entry_in_dir - Search for a devfs entry inside another devfs entry. + * @parent: The parent devfs entry. + * @name: The name of the entry. + * @namelen: The number of characters in @name. + * @traverse_symlink: If %TRUE then the entry is traversed if it is a symlink. + * + * Returns a pointer to the entry on success, else %NULL. + */ + static struct devfs_entry *search_for_entry_in_dir (struct devfs_entry *parent, const char *name, unsigned int namelen, int traverse_symlink) -/* [SUMMARY] Search for a devfs entry inside another devfs entry. - The parent devfs entry. - The name of the entry. - The number of characters in <>. - If TRUE then the entry is traversed if it is a symlink. - [RETURNS] A pointer to the entry on success, else NULL. -*/ { struct devfs_entry *curr; @@ -783,10 +787,14 @@ static struct devfs_entry *create_entry (struct devfs_entry *parent, return new; } /* End Function create_entry */ + +/** + * get_root_entry - Get the root devfs entry. + * + * Returns the root devfs entry on success, else %NULL. + */ + static struct devfs_entry *get_root_entry (void) -/* [SUMMARY] Get the root devfs entry. - [RETURNS] The root devfs entry on success, else NULL. -*/ { struct devfs_entry *new; @@ -809,23 +817,27 @@ static struct devfs_entry *get_root_entry (void) return root_entry; } /* End Function get_root_entry */ + +/** + * search_for_entry - Search for an entry in the devfs tree. + * @dir: The parent directory to search from. If this is %NULL the root is used + * @name: The name of the entry. + * @namelen: The number of characters in @name. + * @mkdir: If %TRUE intermediate directories are created as needed. + * @mkfile: If %TRUE the file entry is created if it doesn't exist. + * @is_new: If the returned entry was newly made, %TRUE is written here. If + * this is %NULL nothing is written here. + * @traverse_symlink: If %TRUE then symbolic links are traversed. + * + * If the entry is created, then it will be in the unregistered state. + * Returns a pointer to the entry on success, else %NULL. + */ + static struct devfs_entry *search_for_entry (struct devfs_entry *dir, const char *name, unsigned int namelen, int mkdir, int mkfile, int *is_new, int traverse_symlink) -/* [SUMMARY] Search for an entry in the devfs tree. - The parent directory to search from. If this is NULL the root is used - The name of the entry. - The number of characters in <>. - If TRUE intermediate directories are created as needed. - If TRUE the file entry is created if it doesn't exist. - If the returned entry was newly made, TRUE is written here. If - this is NULL nothing is written here. - If TRUE then symbolic links are traversed. - [NOTE] If the entry is created, then it will be in the unregistered state. - [RETURNS] A pointer to the entry on success, else NULL. -*/ { int len; const char *subname, *stop, *ptr; @@ -887,16 +899,20 @@ static struct devfs_entry *search_for_entry (struct devfs_entry *dir, return NULL; } /* End Function search_for_entry */ + +/** + * find_by_dev - Find a devfs entry in a directory. + * @major: The major number to search for. + * @minor: The minor number to search for. + * @type: The type of special file to search for. This may be either + * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK. + * + * Returns the devfs_entry pointer on success, else %NULL. + */ + static struct devfs_entry *find_by_dev (struct devfs_entry *dir, unsigned int major, unsigned int minor, char type) -/* [SUMMARY] Find a devfs entry in a directory. - The major number to search for. - The minor number to search for. - The type of special file to search for. This may be either - DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. - [RETURNS] The devfs_entry pointer on success, else NULL. -*/ { struct devfs_entry *entry, *de; @@ -926,26 +942,31 @@ static struct devfs_entry *find_by_dev (struct devfs_entry *dir, return NULL; } /* End Function find_by_dev */ + +/** + * find_entry - Find a devfs entry. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * name is relative to the root of the devfs. + * @name: The name of the entry. This is ignored if @handle is not %NULL. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @major: The major number. This is used if @handle and @name are %NULL. + * @minor: The minor number. This is used if @handle and @name are %NULL. + * NOTE: If @major and @minor are both 0, searching by major and minor + * numbers is disabled. + * @type: The type of special file to search for. This may be either + * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK. + * @traverse_symlink: If %TRUE then symbolic links are traversed. + * + * FIXME: What the hell is @handle? - ch + * Returns the devfs_entry pointer on success, else %NULL. + */ + static struct devfs_entry *find_entry (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int major, unsigned int minor, char type, int traverse_symlink) -/* [SUMMARY] Find a devfs entry. - The handle to the parent devfs directory entry. If this is NULL the - name is relative to the root of the devfs. - The name of the entry. This is ignored if <> is not NULL. - The number of characters in <>, not including a NULL - terminator. If this is 0, then <> must be NULL-terminated and the - length is computed internally. - The major number. This is used if <> and <> are NULL. - The minor number. This is used if <> and <> are NULL. - [NOTE] If <> and <> are both 0, searching by major and minor - numbers is disabled. - The type of special file to search for. This may be either - DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. - If TRUE then symbolic links are traversed. - [RETURNS] The devfs_entry pointer on success, else NULL. -*/ { struct devfs_entry *entry; @@ -991,11 +1012,13 @@ static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode) return fs_info->table[inode->i_ino - FIRST_INODE]; } /* End Function get_devfs_inode_from_vfs_inode */ + +/** + * free_dentries - Free the dentries for a device entry and invalidate inodes. + * @de: The entry. + */ + static void free_dentries (struct devfs_entry *de) -/* [SUMMARY] Free the dentries for a device entry and invalidate inodes. - The entry. - [RETURNS] Nothing. -*/ { struct devfs_inode *di; struct dentry *dentry; @@ -1015,11 +1038,15 @@ static void free_dentries (struct devfs_entry *de) } } /* End Function free_dentries */ + +/** + * is_devfsd_or_child - Test if the current process is devfsd or one of its children. + * fs_info: The filesystem information. + * + * Returns %TRUE if devfsd or child, else %FALSE. + */ + static int is_devfsd_or_child (struct fs_info *fs_info) -/* [SUMMARY] Test if the current process is devfsd or one of its children. - The filesystem information. - [RETURNS] TRUE if devfsd or child, else FALSE. -*/ { struct task_struct *p; @@ -1030,20 +1057,28 @@ static int is_devfsd_or_child (struct fs_info *fs_info) return (FALSE); } /* End Function is_devfsd_or_child */ + +/** + * devfsd_queue_empty - Test if devfsd has work pending in its event queue. + * @fs_info: The filesystem information. + * + * Returns %TRUE if the queue is empty, else %FALSE. + */ + static inline int devfsd_queue_empty (struct fs_info *fs_info) -/* [SUMMARY] Test if devfsd has work pending in its event queue. - The filesystem information. - [RETURNS] TRUE if the queue is empty, else FALSE. -*/ { return (fs_info->devfsd_buf_out == fs_info->devfsd_buf_in) ? TRUE : FALSE; } /* End Function devfsd_queue_empty */ + +/** + * wait_for_devfsd_finished - Wait for devfsd to finish processing its event queue. + * @fs_info: The filesystem information. + * + * Returns %TRUE if no more waiting will be required, else %FALSE. + */ + static int wait_for_devfsd_finished (struct fs_info *fs_info) -/* [SUMMARY] Wait for devfsd to finish processing its event queue. - The filesystem information. - [RETURNS] TRUE if no more waiting will be required, else FALSE. -*/ { DECLARE_WAITQUEUE (wait, current); @@ -1059,17 +1094,21 @@ static int wait_for_devfsd_finished (struct fs_info *fs_info) return (TRUE); } /* End Function wait_for_devfsd_finished */ + +/** + * devfsd_notify_one - Notify a single devfsd daemon of a change. + * @data: Data to be passed. + * @type: The type of change. + * @mode: The mode of the entry. + * @uid: The user ID. + * @gid: The group ID. + * @fs_info: The filesystem info. + * + * Returns %TRUE if an event was queued and devfsd woken up, else %FALSE. + */ + static int devfsd_notify_one (void *data, unsigned int type, umode_t mode, uid_t uid, gid_t gid, struct fs_info *fs_info) -/* [SUMMARY] Notify a single devfsd daemon of a change. - Data to be passed. - The type of change. - The mode of the entry. - The user ID. - The group ID. - The filesystem info. - [RETURNS] TRUE if an event was queued and devfsd woken up, else FALSE. -*/ { unsigned int next_pos; unsigned long flags; @@ -1103,14 +1142,16 @@ static int devfsd_notify_one (void *data, unsigned int type, umode_t mode, return (TRUE); } /* End Function devfsd_notify_one */ + +/** + * devfsd_notify - Notify all devfsd daemons of a change. + * @de: The devfs entry that has changed. + * @type: The type of change event. + * @wait: If TRUE, the functions waits for all daemons to finish processing + * the event. + */ + static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) -/* [SUMMARY] Notify all devfsd daemons of a change. - The devfs entry that has changed. - The type of change event. - If TRUE, the functions waits for all daemons to finish processing - the event. - [RETURNS] Nothing. -*/ { struct fs_info *fs_info; @@ -1122,35 +1163,38 @@ static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) } } /* End Function devfsd_notify */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register - Register a device entry. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new name is relative to the root of the devfs. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). + * @major: The major number. Not needed for regular files. + * @minor: The minor number. Not needed for regular files. + * @mode: The default file mode. + * @uid: The default UID of the file. + * @guid: The default GID of the file. + * @ops: The &file_operations or &block_device_operations structure. + * This must not be externally deallocated. + * @info: An arbitrary pointer which will be written to the @private_data + * field of the &file structure passed to the device driver. You can set + * this to whatever you like, and change it once the file is opened (the next + * file opened will not see this change). + * + * Returns a handle which may later be used in a call to devfs_unregister(). + * On failure %NULL is returned. + */ + devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int flags, unsigned int major, unsigned int minor, umode_t mode, uid_t uid, gid_t gid, void *ops, void *info) -/* [SUMMARY] Register a device entry. - The handle to the parent devfs directory entry. If this is NULL the - new name is relative to the root of the devfs. - The name of the entry. - The number of characters in <>, not including a NULL - terminator. If this is 0, then <> must be NULL-terminated and the - length is computed internally. - A set of bitwise-ORed flags (DEVFS_FL_*). - The major number. Not needed for regular files. - The minor number. Not needed for regular files. - The default file mode. - The default UID of the file. - The default GID of the file. - The <> or <> structure. - This must not be externally deallocated. - An arbitrary pointer which will be written to the <> - field of the <> structure passed to the device driver. You can set - this to whatever you like, and change it once the file is opened (the next - file opened will not see this change). - [RETURNS] A handle which may later be used in a call to - []. On failure NULL is returned. -*/ { int is_new; struct devfs_entry *de; @@ -1276,11 +1320,13 @@ devfs_handle_t devfs_register (devfs_handle_t dir, return de; } /* End Function devfs_register */ + +/** + * unregister - Unregister a device entry. + * @de: The entry to unregister. + */ + static void unregister (struct devfs_entry *de) -/* [SUMMARY] Unregister a device entry. - The entry to unregister. - [RETURNS] Nothing. -*/ { struct devfs_entry *child; @@ -1332,13 +1378,14 @@ static void unregister (struct devfs_entry *de) } } /* End Function unregister */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_unregister - Unregister a device entry. + * de: A handle previously created by devfs_register() or returned from + * devfs_find_handle(). If this is %NULL the routine does nothing. + */ + void devfs_unregister (devfs_handle_t de) -/* [SUMMARY] Unregister a device entry. - A handle previously created by [] or returned from - []. If this is NULL the routine does nothing. - [RETURNS] Nothing. -*/ { if (de == NULL) return; #ifdef CONFIG_DEVFS_DEBUG @@ -1349,28 +1396,31 @@ void devfs_unregister (devfs_handle_t de) unregister (de); } /* End Function devfs_unregister */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_mk_symlink Create a symbolic link in the devfs namespace. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new name is relative to the root of the devfs. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). + * @link: The destination name. + * @linklength: The number of characters in @link, not including a %NULL + * terminator. If this is 0, then @link must be %NULL-terminated and the + * length is computed internally. + * @handle: The handle to the symlink entry is written here. This may be %NULL. + * @info: An arbitrary pointer which will be associated with the entry. + * + * Returns 0 on success, else a negative error code is returned. + */ + int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int flags, const char *link, unsigned int linklength, devfs_handle_t *handle, void *info) -/* [SUMMARY] Create a symbolic link in the devfs namespace. - The handle to the parent devfs directory entry. If this is NULL the - new name is relative to the root of the devfs. - The name of the entry. - The number of characters in <>, not including a NULL - terminator. If this is 0, then <> must be NULL-terminated and the - length is computed internally. - A set of bitwise-ORed flags (DEVFS_FL_*). - The destination name. - The number of characters in <>, not including a NULL - terminator. If this is 0, then <> must be NULL-terminated and the - length is computed internally. - The handle to the symlink entry is written here. This may be NULL. - An arbitrary pointer which will be associated with the entry. - [RETURNS] 0 on success, else a negative error code is returned. -*/ { int is_new; char *newname; @@ -1439,23 +1489,26 @@ int devfs_mk_symlink (devfs_handle_t dir, return 0; } /* End Function devfs_mk_symlink */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_mk_dir - Create a directory in the devfs namespace. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new name is relative to the root of the devfs. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @info: An arbitrary pointer which will be associated with the entry. + * + * Use of this function is optional. The devfs_register() function + * will automatically create intermediate directories as needed. This function + * is provided for efficiency reasons, as it provides a handle to a directory. + * Returns a handle which may later be used in a call to devfs_unregister(). + * On failure %NULL is returned. + */ + devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, unsigned int namelen, void *info) -/* [SUMMARY] Create a directory in the devfs namespace. - The handle to the parent devfs directory entry. If this is NULL the - new name is relative to the root of the devfs. - The name of the entry. - The number of characters in <>, not including a NULL - terminator. If this is 0, then <> must be NULL-terminated and the - length is computed internally. - An arbitrary pointer which will be associated with the entry. - [NOTE] Use of this function is optional. The [] function - will automatically create intermediate directories as needed. This function - is provided for efficiency reasons, as it provides a handle to a directory. - [RETURNS] A handle which may later be used in a call to - []. On failure NULL is returned. -*/ { int is_new; struct devfs_entry *de; @@ -1499,29 +1552,31 @@ devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, return de; } /* End Function devfs_mk_dir */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_find_handle - Find the handle of a devfs entry. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * name is relative to the root of the devfs. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @major: The major number. This is used if @name is %NULL. + * @minor: The minor number. This is used if @name is %NULL. + * @type: The type of special file to search for. This may be either + * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK. + * @traverse_symlinks: If %TRUE then symlink entries in the devfs namespace are + * traversed. Symlinks pointing out of the devfs namespace will cause a + * failure. Symlink traversal consumes stack space. + * + * Returns a handle which may later be used in a call to devfs_unregister(), + * devfs_get_flags(), or devfs_set_flags(). On failure %NULL is returned. + */ + devfs_handle_t devfs_find_handle (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int major, unsigned int minor, char type, int traverse_symlinks) -/* [SUMMARY] Find the handle of a devfs entry. - The handle to the parent devfs directory entry. If this is NULL the - name is relative to the root of the devfs. - The name of the entry. - The number of characters in <>, not including a NULL - terminator. If this is 0, then <> must be NULL-terminated and the - length is computed internally. - The major number. This is used if <> is NULL. - The minor number. This is used if <> is NULL. - The type of special file to search for. This may be either - DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. - If TRUE then symlink entries in the devfs namespace are - traversed. Symlinks pointing out of the devfs namespace will cause a - failure. Symlink traversal consumes stack space. - [RETURNS] A handle which may later be used in a call to - [], [], or []. - On failure NULL is returned. -*/ { devfs_handle_t de; @@ -1533,13 +1588,16 @@ devfs_handle_t devfs_find_handle (devfs_handle_t dir, return de; } /* End Function devfs_find_handle */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_flags - Get the flags for a devfs entry. + * @de: The handle to the device entry. + * @flags: The flags are written here. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_get_flags (devfs_handle_t de, unsigned int *flags) -/* [SUMMARY] Get the flags for a devfs entry. - The handle to the device entry. - The flags are written here. - [RETURNS] 0 on success, else a negative error code. -*/ { unsigned int fl = 0; @@ -1557,13 +1615,16 @@ int devfs_get_flags (devfs_handle_t de, unsigned int *flags) return 0; } /* End Function devfs_get_flags */ -/*PUBLIC_FUNCTION*/ + +/* + * devfs_set_flags - Set the flags for a devfs entry. + * @de: The handle to the device entry. + * @flags: The flags to set. Unset flags are cleared. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_set_flags (devfs_handle_t de, unsigned int flags) -/* [SUMMARY] Set the flags for a devfs entry. - The handle to the device entry. - The flags to set. Unset flags are cleared. - [RETURNS] 0 on success, else a negative error code. -*/ { if (de == NULL) return -EINVAL; if (!de->registered) return -ENODEV; @@ -1592,15 +1653,18 @@ int devfs_set_flags (devfs_handle_t de, unsigned int flags) return 0; } /* End Function devfs_set_flags */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_maj_min - Get the major and minor numbers for a devfs entry. + * @de: The handle to the device entry. + * @major: The major number is written here. This may be %NULL. + * @minor: The minor number is written here. This may be %NULL. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_get_maj_min (devfs_handle_t de, unsigned int *major, unsigned int *minor) -/* [SUMMARY] Get the major and minor numbers for a devfs entry. - The handle to the device entry. - The major number is written here. This may be NULL. - The minor number is written here. This may be NULL. - [RETURNS] 0 on success, else a negative error code. -*/ { if (de == NULL) return -EINVAL; if (!de->registered) return -ENODEV; @@ -1611,12 +1675,15 @@ int devfs_get_maj_min (devfs_handle_t de, unsigned int *major, return 0; } /* End Function devfs_get_maj_min */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_handle_from_inode - Get the devfs handle for a VFS inode. + * @inode: The VFS inode. + * + * Returns the devfs handle on success, else %NULL. + */ + devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) -/* [SUMMARY] Get the devfs handle for a VFS inode. - The VFS inode. - [RETURNS] The devfs handle on success, else NULL. -*/ { struct devfs_inode *di; @@ -1627,16 +1694,19 @@ devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) return di->de; } /* End Function devfs_get_handle_from_inode */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_generate_path - Generate a pathname for an entry, relative to the devfs root. + * @de: The devfs entry. + * @path: The buffer to write the pathname to. The pathname and '\0' + * terminator will be written at the end of the buffer. + * @buflen: The length of the buffer. + * + * Returns the offset in the buffer where the pathname starts on success, + * else a negative error code. + */ + int devfs_generate_path (devfs_handle_t de, char *path, int buflen) -/* [SUMMARY] Generate a pathname for an entry, relative to the devfs root. - The devfs entry. - The buffer to write the pathname to. The pathname and '\0' - terminator will be written at the end of the buffer. - The length of the buffer. - [RETURNS] The offset in the buffer where the pathname starts on success, - else a negative error code. -*/ { int pos; @@ -1656,12 +1726,15 @@ int devfs_generate_path (devfs_handle_t de, char *path, int buflen) return pos; } /* End Function devfs_generate_path */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_ops - Get the device operations for a devfs entry. + * @de: The handle to the device entry. + * + * Returns a pointer to the device operations on success, else NULL. + */ + void *devfs_get_ops (devfs_handle_t de) -/* [SUMMARY] Get the device operations for a devfs entry. - The handle to the device entry. - [RETURNS] A pointer to the device operations on success, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; @@ -1670,13 +1743,16 @@ void *devfs_get_ops (devfs_handle_t de) return NULL; } /* End Function devfs_get_ops */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_set_file_size - Set the file size for a devfs regular file. + * de: The handle to the device entry. + * size: The new file size. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_set_file_size (devfs_handle_t de, unsigned long size) -/* [SUMMARY] Set the file size for a devfs regular file. - The handle to the device entry. - The new file size. - [RETURNS] 0 on success, else a negative error code. -*/ { struct devfs_inode *di; @@ -1694,24 +1770,28 @@ int devfs_set_file_size (devfs_handle_t de, unsigned long size) return 0; } /* End Function devfs_set_file_size */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_info - Get the info pointer written to private_data of @de upon open. + * @de: The handle to the device entry. + * + * Returns the info pointer. + */ void *devfs_get_info (devfs_handle_t de) -/* [SUMMARY] Get the info pointer written to <> upon open. - The handle to the device entry. - [RETURNS] The info pointer. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; return de->info; } /* End Function devfs_get_info */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_set_info - Set the info pointer written to private_data upon open. + * @de: The handle to the device entry. + * + * Returns 0 on success, else a negative error code. + */ int devfs_set_info (devfs_handle_t de, void *info) -/* [SUMMARY] Set the info pointer written to <> upon open. - The handle to the device entry. - [RETURNS] 0 on success, else a negative error code. -*/ { if (de == NULL) return -EINVAL; if (!de->registered) return -EINVAL; @@ -1719,24 +1799,29 @@ int devfs_set_info (devfs_handle_t de, void *info) return 0; } /* End Function devfs_set_info */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_parent - Get the parent device entry. + * @de: The handle to the device entry. + * + * Returns the parent device entry if it exists, else %NULL. + */ devfs_handle_t devfs_get_parent (devfs_handle_t de) -/* [SUMMARY] Get the parent device entry. - The handle to the device entry. - [RETURNS] The parent device entry if it exists, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; return de->parent; } /* End Function devfs_get_parent */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_first_child - Get the first leaf node in a directory. + * @de: The handle to the device entry. + * + * Returns the leaf node device entry if it exists, else %NULL. + */ + devfs_handle_t devfs_get_first_child (devfs_handle_t de) -/* [SUMMARY] Get the first leaf node in a directory. - The handle to the device entry. - [RETURNS] The leaf node device entry if it exists, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; @@ -1744,27 +1829,31 @@ devfs_handle_t devfs_get_first_child (devfs_handle_t de) return de->u.dir.first; } /* End Function devfs_get_first_child */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_next_sibling - Get the next sibling leaf node. for a device entry. + * @de: The handle to the device entry. + * + * Returns the leaf node device entry if it exists, else %NULL. + */ + devfs_handle_t devfs_get_next_sibling (devfs_handle_t de) -/* [SUMMARY] Get the next sibling leaf node. for a device entry. - The handle to the device entry. - [RETURNS] The leaf node device entry if it exists, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; return de->next; } /* End Function devfs_get_next_sibling */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_auto_unregister - Configure a devfs entry to be automatically unregistered. + * @master: The master devfs entry. Only one slave may be registered. + * @slave: The devfs entry which will be automatically unregistered when the + * master entry is unregistered. It is illegal to call devfs_unregister() + * on this entry. + */ + void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave) -/* [SUMMARY] Configure a devfs entry to be automatically unregistered. - The master devfs entry. Only one slave may be registered. - The devfs entry which will be automatically unregistered when the - master entry is unregistered. It is illegal to call [] on - this entry. - [RETURNS] Nothing. -*/ { if (master == NULL) return; if (master->slave != NULL) @@ -1779,25 +1868,30 @@ void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave) master->slave = slave; } /* End Function devfs_auto_unregister */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_unregister_slave - Get the slave entry which will be automatically unregistered. + * @master: The master devfs entry. + * + * Returns the slave which will be unregistered when @master is unregistered. + */ + devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master) -/* [SUMMARY] Get the slave entry which will be automatically unregistered. - The master devfs entry. - [RETURNS] The slave which will be unregistered when <> is - unregistered. -*/ { if (master == NULL) return NULL; return master->slave; } /* End Function devfs_get_unregister_slave */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_name - Get the name for a device entry in its parent directory. + * @de: The handle to the device entry. + * @namelen: The length of the name is written here. This may be %NULL. + * + * Returns the name on success, else %NULL. + */ + const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen) -/* [SUMMARY] Get the name for a device entry in its parent directory. - The handle to the device entry. - The length of the name is written here. This may be NULL. - [RETURNS] The name on success, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; @@ -1805,61 +1899,73 @@ const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen) return de->name; } /* End Function devfs_get_name */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_chrdev - Optionally register a conventional character driver. + * @major: The major number for the driver. + * @name: The name of the driver (as seen in /proc/devices). + * @fops: The &file_operations structure pointer. + * + * This function will register a character driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_register_chrdev (unsigned int major, const char *name, struct file_operations *fops) -/* [SUMMARY] Optionally register a conventional character driver. - [PURPOSE] This function will register a character driver provided the - "devfs=only" option was not provided at boot time. - The major number for the driver. - The name of the driver (as seen in /proc/devices). - The file_operations structure pointer. - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return register_chrdev (major, name, fops); } /* End Function devfs_register_chrdev */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_blkdev - Optionally register a conventional block driver. + * @major: The major number for the driver. + * @name: The name of the driver (as seen in /proc/devices). + * @bdops: The &block_device_operations structure pointer. + * + * This function will register a block driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_register_blkdev (unsigned int major, const char *name, struct block_device_operations *bdops) -/* [SUMMARY] Optionally register a conventional block driver. - [PURPOSE] This function will register a block driver provided the - "devfs=only" option was not provided at boot time. - The major number for the driver. - The name of the driver (as seen in /proc/devices). - The block_device_operations structure pointer. - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return register_blkdev (major, name, bdops); } /* End Function devfs_register_blkdev */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_unregister_chrdev - Optionally unregister a conventional character driver. + * major: The major number for the driver. + * name: The name of the driver (as seen in /proc/devices). + * + * This function will unregister a character driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_unregister_chrdev (unsigned int major, const char *name) -/* [SUMMARY] Optionally unregister a conventional character driver. - [PURPOSE] This function will unregister a character driver provided the - "devfs=only" option was not provided at boot time. - The major number for the driver. - The name of the driver (as seen in /proc/devices). - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return unregister_chrdev (major, name); } /* End Function devfs_unregister_chrdev */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_unregister_blkdev - Optionally unregister a conventional block driver. + * @major: The major number for the driver. + * @name: The name of the driver (as seen in /proc/devices). + * + * This function will unregister a block driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_unregister_blkdev (unsigned int major, const char *name) -/* [SUMMARY] Optionally unregister a conventional block driver. - [PURPOSE] This function will unregister a block driver provided the - "devfs=only" option was not provided at boot time. - The major number for the driver. - The name of the driver (as seen in /proc/devices). - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return unregister_blkdev (major, name); @@ -1867,13 +1973,13 @@ int devfs_unregister_blkdev (unsigned int major, const char *name) #ifndef MODULE -/*UNPUBLISHED_FUNCTION*/ +/** + * devfs_setup - Process kernel boot options. + * @str: The boot options after the "devfs=". + * @unused: Unused. + */ + SETUP_STATIC int __init devfs_setup (char *str) -/* [SUMMARY] Process kernel boot options. - The boot options after the "devfs=". - Unused. - [RETURNS] Nothing. -*/ { while ( (*str != '\0') && !isspace (*str) ) { @@ -2027,13 +2133,17 @@ static void update_devfs_inode_from_entry (struct devfs_inode *di) } } /* End Function update_devfs_inode_from_entry */ + +/** + * create_devfs_inode - Create a devfs inode entry. + * @de: The devfs entry to associate the new inode with. + * @fs_info: The FS info. + * + * Returns a pointer to the devfs inode on success, else %NULL. + */ + static struct devfs_inode *create_devfs_inode (struct devfs_entry *de, struct fs_info *fs_info) -/* [SUMMARY] Create a devfs inode entry. - The devfs entry to associate the new inode with. - The FS info. - [RETURNS] A pointer to the devfs inode on success, else NULL. -*/ { struct devfs_inode *di, **table; @@ -2077,18 +2187,22 @@ static struct devfs_inode *create_devfs_inode (struct devfs_entry *de, return di; } /* End Function create_devfs_inode */ + +/** + * try_modload - Notify devfsd of an inode lookup. + * @parent: The parent devfs entry. + * @fs_info: The filesystem info. + * @name: The device name. + * @namelen: The number of characters in @name. + * @buf: A working area that will be used. This must not go out of scope until + * devfsd is idle again. + * + * Returns 0 on success, else a negative error code. + */ + static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, const char *name, unsigned namelen, char buf[STRING_LENGTH]) -/* [SUMMARY] Notify devfsd of an inode lookup. - The parent devfs entry. - The filesystem info. - The device name. - The number of characters in <>. - A working area that will be used. This must not go out of scope until - devfsd is idle again. - [RETURNS] 0 on success, else a negative error code. -*/ { int pos; @@ -2136,11 +2250,15 @@ static void delete_fs (struct fs_info *fs_info) kfree (fs_info); } /* End Function delete_fs */ + +/** + * check_disc_changed - Check if a removable disc was changed. + * @de: The device. + * + * Returns 1 if the media was changed, else 0. + */ + static int check_disc_changed (struct devfs_entry *de) -/* [SUMMARY] Check if a removable disc was changed. - The device. - [RETURNS] 1 if the media was changed, else 0. -*/ { int tmp; kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor); @@ -2166,11 +2284,13 @@ static int check_disc_changed (struct devfs_entry *de) return 1; } /* End Function check_disc_changed */ + +/** + * scan_dir_for_removable - Scan a directory for removable media devices and check media. + * @dir: The directory. + */ + static void scan_dir_for_removable (struct devfs_entry *dir) -/* [SUMMARY] Scan a directory for removable media devices and check media. - The directory. - [RETURNS] Nothing. -*/ { struct devfs_entry *de; @@ -2184,14 +2304,17 @@ static void scan_dir_for_removable (struct devfs_entry *dir) } } /* End Function scan_dir_for_removable */ +/** + * get_removable_partition - Get removable media partition. + * @dir: The parent directory. + * @name: The name of the entry. + * @namelen: The number of characters in <>. + * + * Returns 1 if the media was changed, else 0. + */ + static int get_removable_partition (struct devfs_entry *dir, const char *name, unsigned int namelen) -/* [SUMMARY] Get removable media partition. - The parent directory. - The name of the entry. - The number of characters in <>. - [RETURNS] 1 if the media was changed, else 0. -*/ { struct devfs_entry *de; @@ -2365,15 +2488,19 @@ static struct super_operations devfs_sops = statfs: devfs_statfs, }; + +/** + * get_vfs_inode - Get a VFS inode. + * @sb: The super block. + * @di: The devfs inode. + * @dentry The dentry to register with the devfs inode. + * + * Returns the inode on success, else %NULL. + */ + static struct inode *get_vfs_inode (struct super_block *sb, struct devfs_inode *di, struct dentry *dentry) -/* [SUMMARY] Get a VFS inode. - The super block. - The devfs inode. - The dentry to register with the devfs inode. - [RETURNS] The inode on success, else NULL. -*/ { struct inode *inode; @@ -2551,9 +2678,13 @@ static struct file_operations devfs_fops = /* Dentry operations for device entries follow */ + +/** + * devfs_d_release - Callback for when a dentry is freed. + * @dentry: The dentry. + */ + static void devfs_d_release (struct dentry *dentry) -/* [SUMMARY] Callback for when a dentry is freed. -*/ { #ifdef CONFIG_DEVFS_DEBUG struct inode *inode = dentry->d_inode; @@ -2564,9 +2695,13 @@ static void devfs_d_release (struct dentry *dentry) #endif } /* End Function devfs_d_release */ +/** + * devfs_d_iput - Callback for when a dentry loses its inode. + * @dentry: The dentry. + * @inode: The inode. + */ + static void devfs_d_iput (struct dentry *dentry, struct inode *inode) -/* [SUMMARY] Callback for when a dentry loses its inode. -*/ { struct devfs_inode *di; @@ -2587,7 +2722,7 @@ static void devfs_d_iput (struct dentry *dentry, struct inode *inode) iput (inode); } /* End Function devfs_d_iput */ -static void devfs_d_delete (struct dentry *dentry); +static int devfs_d_delete (struct dentry *dentry); static struct dentry_operations devfs_dops = { @@ -2606,9 +2741,12 @@ static struct dentry_operations devfs_wait_dops = d_revalidate: devfs_d_revalidate_wait, }; -static void devfs_d_delete (struct dentry *dentry) -/* [SUMMARY] Callback for when all files for a dentry are closed. -*/ +/** + * devfs_d_delete - Callback for when all files for a dentry are closed. + * @detry: The dentry. + */ + +static int devfs_d_delete (struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct devfs_inode *di; @@ -2623,8 +2761,7 @@ static void devfs_d_delete (struct dentry *dentry) printk ("%s: d_delete(): dropping negative dentry: %p\n", DEVFS_NAME, dentry); #endif - d_drop (dentry); - return; + return 1; } fs_info = inode->i_sb->u.generic_sbp; di = get_devfs_inode_from_vfs_inode (inode); @@ -2633,16 +2770,16 @@ static void devfs_d_delete (struct dentry *dentry) printk ("%s: d_delete(): dentry: %p inode: %p devfs_inode: %p\n", DEVFS_NAME, dentry, inode, di); #endif - if (di == NULL) return; - if (di->de == NULL) return; + if (di == NULL) return 0; + if (di->de == NULL) return 0; if ( !S_ISCHR (di->mode) && !S_ISBLK (di->mode) && !S_ISREG (di->mode) ) - return; - if (!di->de->u.fcb.open) return; + return 0; + if (!di->de->u.fcb.open) return 0; di->de->u.fcb.open = FALSE; if (di->de->u.fcb.aopen_notify) devfsd_notify_one (di->de, DEVFSD_NOTIFY_CLOSE, inode->i_mode, current->euid, current->egid, fs_info); - if (!di->de->u.fcb.auto_owner) return; + if (!di->de->u.fcb.auto_owner) return 0; /* Change the ownership/protection back */ di->mode = (di->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; di->uid = di->de->u.fcb.default_uid; @@ -2650,6 +2787,7 @@ static void devfs_d_delete (struct dentry *dentry) inode->i_mode = di->mode; inode->i_uid = di->uid; inode->i_gid = di->gid; + return 0; } /* End Function devfs_d_delete */ static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) @@ -3431,11 +3569,9 @@ int __init init_devfs_fs (void) void __init mount_devfs_fs (void) { int err; - extern long do_sys_mount (char *dev_name, char *dir_name, - char *type, int flags, void *data); if ( (boot_options & OPTION_NOMOUNT) ) return; - err = do_sys_mount ("none", "/dev", "devfs", 0, ""); + err = do_mount ("none", "/dev", "devfs", 0, ""); if (err == 0) printk ("Mounted devfs on /dev\n"); else printk ("Warning: unable to mount devfs, err: %d\n", err); } /* End Function mount_devfs_fs */ diff --git a/fs/devfs/util.c b/fs/devfs/util.c index 7e22ae7cb93..fe47464487e 100644 --- a/fs/devfs/util.c +++ b/fs/devfs/util.c @@ -38,13 +38,14 @@ /* Private functions follow */ +/** + * _devfs_convert_name - Convert from an old style location-based name to new style. + * @new: The new name will be written here. + * @old: The old name. + * @disc: If true, disc partitioning information should be processed. + */ + static void __init _devfs_convert_name (char *new, const char *old, int disc) -/* [SUMMARY] Convert from an old style location-based name to new style. - The new name will be written here. - The old name. - If true, disc partitioning information should be processed. - [RETURNS] Nothing. -*/ { int host, bus, target, lun; char *ptr; @@ -73,12 +74,12 @@ static void __init _devfs_convert_name (char *new, const char *old, int disc) /* Public functions follow */ -/*PUBLIC_FUNCTION*/ +/** + * devfs_make_root - Create the root FS device entry if required. + * @name: The name of the root FS device, as passed by "root=". + */ + void __init devfs_make_root (const char *name) -/* [SUMMARY] Create the root FS device entry if required. - The name of the root FS device, as passed by "root=". - [RETURNS] Nothing. -*/ { char dest[64]; @@ -97,12 +98,13 @@ void __init devfs_make_root (const char *name) devfs_mk_symlink (NULL, name, 0, DEVFS_FL_DEFAULT, dest, 0, NULL,NULL); } /* End Function devfs_make_root */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_tape - Register a tape device in the "/dev/tapes" hierarchy. + * @de: Any tape device entry in the device directory. + */ + void devfs_register_tape (devfs_handle_t de) -/* [SUMMARY] Register a tape device in the "/dev/tapes" hierarchy. - Any tape device entry in the device directory. - [RETURNS] Nothing. -*/ { int pos; devfs_handle_t parent, slave; @@ -122,30 +124,31 @@ void devfs_register_tape (devfs_handle_t de) } /* End Function devfs_register_tape */ EXPORT_SYMBOL(devfs_register_tape); -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_series - Register a sequence of device entries. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new names are relative to the root of the devfs. + * @format: The printf-style format string. A single "\%u" is allowed. + * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). + * @major: The major number. Not needed for regular files. + * @minor_start: The starting minor number. Not needed for regular files. + * @mode: The default file mode. + * @uid: The default UID of the file. + * @guid: The default GID of the file. + * @ops: The &file_operations or &block_device_operations structure. + * This must not be externally deallocated. + * @info: An arbitrary pointer which will be written to the private_data + * field of the &file structure passed to the device driver. You can set + * this to whatever you like, and change it once the file is opened (the next + * file opened will not see this change). + */ + void devfs_register_series (devfs_handle_t dir, const char *format, unsigned int num_entries, unsigned int flags, unsigned int major, unsigned int minor_start, umode_t mode, uid_t uid, gid_t gid, void *ops, void *info) -/* [SUMMARY] Register a sequence of device entries. - The handle to the parent devfs directory entry. If this is NULL the - new names are relative to the root of the devfs. - The printf-style format string. A single "%u" is allowed. - A set of bitwise-ORed flags (DEVFS_FL_*). - The major number. Not needed for regular files. - The starting minor number. Not needed for regular files. - The default file mode. - The default UID of the file. - The default GID of the file. - The <> or <> structure. - This must not be externally deallocated. - An arbitrary pointer which will be written to the <> - field of the <> structure passed to the device driver. You can set - this to whatever you like, and change it once the file is opened (the next - file opened will not see this change). - [RETURNS] Nothing. -*/ { unsigned int count; char devname[128]; diff --git a/fs/exec.c b/fs/exec.c index 992bbd6aa8a..6811398438e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -45,8 +45,8 @@ #include #endif -static struct linux_binfmt *formats = (struct linux_binfmt *) NULL; -static spinlock_t binfmt_lock = SPIN_LOCK_UNLOCKED; +static struct linux_binfmt *formats; +static rwlock_t binfmt_lock = RW_LOCK_UNLOCKED; int register_binfmt(struct linux_binfmt * fmt) { @@ -56,17 +56,17 @@ int register_binfmt(struct linux_binfmt * fmt) return -EINVAL; if (fmt->next) return -EBUSY; - spin_lock(&binfmt_lock); + write_lock(&binfmt_lock); while (*tmp) { if (fmt == *tmp) { - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return -EBUSY; } tmp = &(*tmp)->next; } fmt->next = formats; formats = fmt; - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return 0; } @@ -74,16 +74,16 @@ int unregister_binfmt(struct linux_binfmt * fmt) { struct linux_binfmt ** tmp = &formats; - spin_lock(&binfmt_lock); + write_lock(&binfmt_lock); while (*tmp) { if (fmt == *tmp) { *tmp = fmt->next; - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return 0; } tmp = &(*tmp)->next; } - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return -EINVAL; } @@ -103,35 +103,34 @@ asmlinkage long sys_uselib(const char * library) { int fd, retval; struct file * file; - struct linux_binfmt * fmt; - lock_kernel(); fd = sys_open(library, 0, 0); - retval = fd; if (fd < 0) - goto out; + return fd; file = fget(fd); retval = -ENOEXEC; - if (file && file->f_op && file->f_op->read) { - spin_lock(&binfmt_lock); - for (fmt = formats ; fmt ; fmt = fmt->next) { - if (!fmt->load_shlib) - continue; - if (!try_inc_mod_count(fmt->module)) - continue; - spin_unlock(&binfmt_lock); - retval = fmt->load_shlib(file); - spin_lock(&binfmt_lock); - put_binfmt(fmt); - if (retval != -ENOEXEC) - break; + if (file) { + if(file->f_op && file->f_op->read) { + struct linux_binfmt * fmt; + + read_lock(&binfmt_lock); + for (fmt = formats ; fmt ; fmt = fmt->next) { + if (!fmt->load_shlib) + continue; + if (!try_inc_mod_count(fmt->module)) + continue; + read_unlock(&binfmt_lock); + retval = fmt->load_shlib(file); + read_lock(&binfmt_lock); + put_binfmt(fmt); + if (retval != -ENOEXEC) + break; + } + read_unlock(&binfmt_lock); } - spin_unlock(&binfmt_lock); + fput(file); } - fput(file); sys_close(fd); -out: - unlock_kernel(); return retval; } @@ -288,6 +287,7 @@ int setup_arg_pages(struct linux_binprm *bprm) if (!mpnt) return -ENOMEM; + down(¤t->mm->mmap_sem); { mpnt->vm_mm = current->mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; @@ -311,6 +311,7 @@ int setup_arg_pages(struct linux_binprm *bprm) } stack_base += PAGE_SIZE; } + up(¤t->mm->mmap_sem); return 0; } @@ -745,14 +746,14 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) } #endif for (try=0; try<2; try++) { - spin_lock(&binfmt_lock); + read_lock(&binfmt_lock); for (fmt = formats ; fmt ; fmt = fmt->next) { int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary; if (!fn) continue; if (!try_inc_mod_count(fmt->module)) continue; - spin_unlock(&binfmt_lock); + read_unlock(&binfmt_lock); retval = fn(bprm, regs); if (retval >= 0) { put_binfmt(fmt); @@ -762,16 +763,16 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) current->did_exec = 1; return retval; } - spin_lock(&binfmt_lock); + read_lock(&binfmt_lock); put_binfmt(fmt); if (retval != -ENOEXEC) break; if (!bprm->file) { - spin_unlock(&binfmt_lock); + read_unlock(&binfmt_lock); return retval; } } - spin_unlock(&binfmt_lock); + read_unlock(&binfmt_lock); if (retval != -ENOEXEC) { break; #ifdef CONFIG_KMOD diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 90ce121cecb..a3f8ae4cede 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -209,7 +209,7 @@ static inline int load_block_bitmap (struct super_block * sb, */ if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && sb->u.ext2_sb.s_block_bitmap_number[0] == block_group && - sb->u.ext2_sb.s_block_bitmap[block_group]) { + sb->u.ext2_sb.s_block_bitmap[0]) { return 0; } /* diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index dff1b6841f9..3e471f42bfb 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -577,7 +577,6 @@ static int ext2_rmdir (struct inode * dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); - d_delete(dentry); end_rmdir: brelse (bh); @@ -619,7 +618,6 @@ static int ext2_unlink(struct inode * dir, struct dentry *dentry) mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; - d_delete(dentry); /* This also frees the inode */ end_unlink: brelse (bh); diff --git a/fs/hfs/balloc.c b/fs/hfs/balloc.c index d7e17e72f84..4edd6b7482d 100644 --- a/fs/hfs/balloc.c +++ b/fs/hfs/balloc.c @@ -86,6 +86,8 @@ static struct hfs_bnode_ref hfs_bnode_init(struct hfs_btree * tree, retval.bn->magic = HFS_BNODE_MAGIC; retval.bn->tree = tree; retval.bn->node = node; + hfs_init_waitqueue(&retval.bn->wqueue); + hfs_init_waitqueue(&retval.bn->rqueue); hfs_bnode_lock(&retval, HFS_LOCK_WRITE); retval.bn->buf = get_new_node(tree, node); diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 85e3a909b76..e2b975fae96 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -60,7 +60,7 @@ typedef struct { hfs_byte_t RExtRec[12]; /* first extent record for the resource fork */ hfs_lword_t Resrv; /* reserved by Apple */ -} FIL_REC; +} __attribute__((packed)) FIL_REC; /* the catalog record for a directory */ typedef struct { @@ -74,14 +74,14 @@ typedef struct { hfs_dinfo_t UsrInfo; /* data used by the Finder */ hfs_dxinfo_t FndrInfo; /* more data used by Finder */ hfs_byte_t Resrv[16]; /* reserved by Apple */ -} DIR_REC; +} __attribute__((packed)) DIR_REC; /* the catalog record for a thread */ typedef struct { hfs_byte_t Reserv[8]; /* reserved by Apple */ hfs_lword_t ParID; /* CNID of parent directory */ struct hfs_name CName; /* The name of this entry */ -} THD_REC; +} __attribute__((packed)) THD_REC; /* A catalog tree record */ struct hfs_cat_rec { @@ -92,7 +92,7 @@ struct hfs_cat_rec { DIR_REC dir; THD_REC thd; } u; -}; +} __attribute__((packed)); /*================ File-local variables ================*/ diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index 07f4ab93df0..d6de087e9f6 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -274,7 +274,6 @@ int hfs_unlink(struct inode * dir, struct dentry *dentry) inode->i_nlink--; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); - d_delete(dentry); update_dirs_minus(entry, 0); } @@ -328,7 +327,6 @@ int hfs_rmdir(struct inode * parent, struct dentry *dentry) inode->i_nlink = 0; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); - d_delete(dentry); update_dirs_minus(entry, 1); hfs_rmdir_put: diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index 80e47fc6284..ba62615b035 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -158,7 +158,7 @@ struct hdr_hdr { hfs_byte_t filler[16]; hfs_word_t entries; hfs_byte_t descrs[12*HFS_HDR_MAX]; -}; +} __attribute__((packed)); /*================ File-local functions ================*/ diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index 3819a685f48..9552bbb3415 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -130,7 +130,7 @@ struct hfs_name { hfs_byte_t Len; hfs_byte_t Name[31]; -}; +} __attribute__((packed)); typedef struct { hfs_word_t v; @@ -150,21 +150,21 @@ typedef struct { hfs_word_t fdFlags; hfs_point_t fdLocation; hfs_word_t fdFldr; -} hfs_finfo_t; +} __attribute__((packed)) hfs_finfo_t; typedef struct { hfs_word_t fdIconID; hfs_byte_t fdUnused[8]; hfs_word_t fdComment; hfs_lword_t fdPutAway; -} hfs_fxinfo_t; +} __attribute__((packed)) hfs_fxinfo_t; typedef struct { hfs_rect_t frRect; hfs_word_t frFlags; hfs_point_t frLocation; hfs_word_t frView; -} hfs_dinfo_t; +} __attribute__((packed)) hfs_dinfo_t; typedef struct { hfs_point_t frScroll; @@ -172,7 +172,7 @@ typedef struct { hfs_word_t frUnused; hfs_word_t frComment; hfs_lword_t frPutAway; -} hfs_dxinfo_t; +} __attribute__((packed)) hfs_dxinfo_t; union hfs_finder_info { struct { @@ -189,7 +189,7 @@ union hfs_finder_info { struct hfs_bkey { hfs_byte_t KeyLen; /* number of bytes in the key */ hfs_byte_t value[1]; /* (KeyLen) bytes of key */ -}; +} __attribute__((packed)); /* Cast to a pointer to a generic bkey */ #define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X))) @@ -200,7 +200,7 @@ struct hfs_cat_key { hfs_byte_t Resrv1; /* padding */ hfs_lword_t ParID; /* CNID of the parent dir */ struct hfs_name CName; /* The filename of the entry */ -}; +} __attribute__((packed)); /* The key used in the extents b-tree: */ struct hfs_ext_key { @@ -208,7 +208,7 @@ struct hfs_ext_key { hfs_byte_t FkType; /* HFS_FK_{DATA,RSRC} */ hfs_lword_t FNum; /* The File ID of the file */ hfs_word_t FABN; /* allocation blocks number*/ -}; +} __attribute__((packed)); /*======== Data structures kept in memory ========*/ diff --git a/fs/hfs/hfs_btree.h b/fs/hfs/hfs_btree.h index 97423b35017..39d6df4d52d 100644 --- a/fs/hfs/hfs_btree.h +++ b/fs/hfs/hfs_btree.h @@ -90,7 +90,7 @@ struct BTHdrRec { hfs_byte_t bthResv2; /* reserved */ hfs_lword_t bthAtrb; /* (F) attributes */ hfs_lword_t bthResv3[16]; /* Reserved */ -}; +} __attribute__((packed)); /* * struct NodeDescriptor @@ -112,7 +112,7 @@ struct NodeDescriptor { hfs_byte_t ndNHeight; /* (F) The level of this node (leaves=1) */ hfs_word_t ndNRecs; /* (V) The number of records in this node */ hfs_word_t ndResv2; /* Reserved */ -}; +} __attribute__((packed)); /* * typedef hfs_cmpfn diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c index 386a6ae7936..b44fdafc839 100644 --- a/fs/hfs/mdb.c +++ b/fs/hfs/mdb.c @@ -71,7 +71,7 @@ struct raw_mdb { hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */ hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */ hfs_byte_t drCTExtRec[12]; /* catalog B-tree's first 3 extents */ -}; +} __attribute__((packed)); /*================ Global functions ================*/ diff --git a/fs/hfs/part_tbl.c b/fs/hfs/part_tbl.c index 12922c6d781..2392a0791be 100644 --- a/fs/hfs/part_tbl.c +++ b/fs/hfs/part_tbl.c @@ -77,7 +77,7 @@ struct old_pmap { hfs_lword_t pdSize; hfs_lword_t pdFSID; } pdEntry[42]; -}; +} __attribute__((packed)); /*================ File-local functions ================*/ diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index dee75d7d002..b09ad98eacc 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -326,7 +326,6 @@ int hpfs_unlink(struct inode *dir, struct dentry *dentry) if (r != 2) { inode->i_nlink--; hpfs_unlock_2inodes(dir, inode); - d_delete(dentry); } else { /* no space for deleting, try to truncate file */ struct iattr newattrs; int err; @@ -388,7 +387,6 @@ int hpfs_rmdir(struct inode *dir, struct dentry *dentry) dir->i_nlink--; inode->i_nlink = 0; hpfs_unlock_2inodes(dir, inode); - d_delete(dentry); } else hpfs_unlock_2inodes(dir, inode); return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; } diff --git a/fs/inode.c b/fs/inode.c index 1bacb24a7b2..64373d6ad05 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -377,7 +377,7 @@ static int invalidate_list(struct list_head *head, struct super_block * sb, stru * * Discard all of the inodes for a given superblock. If the discard * fails because there are busy inodes then a non zero value is returned. - * If the discard is successful all the inodes are dicarded. + * If the discard is successful all the inodes have been discarded. */ int invalidate_inodes(struct super_block * sb) @@ -470,7 +470,7 @@ int shrink_icache_memory(int priority, int gfp_mask) /* * Called with the inode lock held. * NOTE: we are not increasing the inode-refcount, you must call __iget() - * by hand after calling find_inode now! This simplify iunique and won't + * by hand after calling find_inode now! This simplifies iunique and won't * add any additional branch in the common code. */ static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 376eed9cc4a..cc3025ee326 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -233,11 +233,21 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) struct rpc_clnt *clnt; struct nlm_args *argp = &req->a_args; struct nlm_res *resp = &req->a_res; + struct file *filp = argp->lock.fl.fl_file; + struct rpc_message msg; int status; dprintk("lockd: call procedure %s on %s\n", nlm_procname(proc), host->h_name); + msg.rpc_proc = proc; + msg.rpc_argp = argp; + msg.rpc_resp = resp; + if (filp) + msg.rpc_cred = nfs_file_cred(filp); + else + msg.rpc_cred = NULL; + do { if (host->h_reclaiming && !argp->reclaim) { interruptible_sleep_on(&host->h_gracewait); @@ -249,7 +259,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) return -ENOLCK; /* Perform the RPC call. If an error occurs, try again */ - if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) { + if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) { dprintk("lockd: rpc_call returned error %d\n", -status); switch (status) { case -EPROTONOSUPPORT: @@ -330,11 +340,31 @@ int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) { struct nlm_host *host = req->a_host; - int status; + struct rpc_clnt *clnt; + struct nlm_args *argp = &req->a_args; + struct nlm_res *resp = &req->a_res; + struct file *file = argp->lock.fl.fl_file; + struct rpc_message msg; + int status; + dprintk("lockd: call procedure %s on %s (async)\n", + nlm_procname(proc), host->h_name); + + /* If we have no RPC client yet, create one. */ + if ((clnt = nlm_bind_host(host)) == NULL) + return -ENOLCK; + + /* bootstrap and kick off the async RPC call */ + msg.rpc_proc = proc; + msg.rpc_argp = argp; + msg.rpc_resp =resp; + if (file) + msg.rpc_cred = nfs_file_cred(file); + else + msg.rpc_cred = NULL; /* Increment host refcount */ nlm_get_host(host); - status = nlmsvc_async_call(req, proc, callback); + status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); if (status < 0) nlm_release_host(host); return status; diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 97a9d27efdd..279fcc3c19c 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -533,8 +533,10 @@ callback: nlmsvc_insert_block(block, jiffies + 30 * HZ); /* Call the client */ - nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG, - nlmsvc_grant_callback); + nlm_get_host(block->b_call.a_host); + if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG, + nlmsvc_grant_callback) < 0) + nlm_release_host(block->b_call.a_host); up(&file->f_sema); } diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 7dc20ea7cd7..653c3415ad7 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -409,7 +409,6 @@ static int minix_rmdir(struct inode * dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; mark_inode_dirty(dir); - d_delete(dentry); retval = 0; end_rmdir: brelse(bh); @@ -444,7 +443,6 @@ static int minix_unlink(struct inode * dir, struct dentry *dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); /* This also frees the inode */ retval = 0; end_unlink: brelse(bh); diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 612bd659756..bcb40df9052 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -337,7 +337,6 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry) dir->i_nlink--; mark_inode_dirty(inode); mark_inode_dirty(dir); - d_delete(dentry); res = 0; rmdir_done: @@ -432,7 +431,6 @@ int msdos_unlink( struct inode *dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); mark_inode_dirty(dir); - d_delete(dentry); /* This also frees the inode */ res = 0; unlink_done: return res; diff --git a/fs/namei.c b/fs/namei.c index 67f8c0a182f..50100038102 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -902,20 +902,18 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) if (nd->last.name[nd->last.len]) goto exit; - dir = dget(nd->dentry); + dir = nd->dentry; down(&dir->d_inode->i_sem); dentry = lookup_hash(&nd->last, nd->dentry); error = PTR_ERR(dentry); if (IS_ERR(dentry)) { up(&dir->d_inode->i_sem); - dput(dir); goto exit; } if (dentry->d_inode) { up(&dir->d_inode->i_sem); - dput(dir); error = -EEXIST; if (flag & O_EXCL) goto exit_dput; @@ -940,12 +938,12 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) goto exit; } else { error = vfs_create(dir->d_inode, dentry, mode); + up(&dir->d_inode->i_sem); /* Don't check for write permission, don't truncate */ acc_mode = 0; flag &= ~O_TRUNC; dput(nd->dentry); nd->dentry = dentry; - unlock_dir(dir); if (error) goto exit; } @@ -1219,6 +1217,8 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) if (!error) dentry->d_inode->i_flags |= S_DEAD; double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); + if (!error) + d_delete(dentry); dput(dentry); return error; @@ -1276,6 +1276,8 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) if (dir->i_op && dir->i_op->unlink) { DQUOT_INIT(dir); error = dir->i_op->unlink(dir, dentry); + if (!error) + d_delete(dentry); } } up(&dir->i_zombie); diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index f5a5856f3e3..55daea198d7 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -71,7 +71,7 @@ struct inode_operations ncp_dir_inode_operations = static int ncp_lookup_validate(struct dentry *, int); static int ncp_hash_dentry(struct dentry *, struct qstr *); static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *); -static void ncp_delete_dentry(struct dentry *); +static int ncp_delete_dentry(struct dentry *); struct dentry_operations ncp_dentry_operations = { @@ -119,32 +119,22 @@ ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) /* * This is the callback from dput() when d_count is going to 0. - * We use this to unhash dentries with bad inodes and close files. + * We use this to unhash dentries with bad inodes. + * Closing files can be safely postponed until iput() - it's done there anyway. */ -static void +static int ncp_delete_dentry(struct dentry * dentry) { struct inode *inode = dentry->d_inode; - if (inode) - { + if (inode) { if (is_bad_inode(inode)) - { - d_drop(dentry); - } - /* - * Lock the superblock, then recheck the dentry count. - * (Somebody might have used it again ...) - */ - if (dentry->d_count == 1 && NCP_FINFO(inode)->opened) { - PPRINTK("ncp_delete_dentry: closing file %s/%s\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - ncp_make_closed(inode); - } + return 1; } else { /* N.B. Unhash negative dentries? */ } + return 0; } static inline int @@ -1000,7 +990,6 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) case 0x00: DPRINTK("ncp: removed %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - d_delete(dentry); break; case 0x85: case 0x8A: diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2d2ee4a0297..f35ef3bdb5d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -35,8 +35,6 @@ #define NFS_PARANOIA 1 /* #define NFS_DEBUG_VERBOSE 1 */ -static int nfs_safe_remove(struct dentry *); - static int nfs_readdir(struct file *, void *, filldir_t); static struct dentry *nfs_lookup(struct inode *, struct dentry *); static int nfs_create(struct inode *, struct dentry *, int); @@ -71,6 +69,70 @@ struct inode_operations nfs_dir_inode_operations = { }; typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); +typedef struct { + struct file *file; + struct page *page; + unsigned long page_index; + unsigned page_offset; + u64 target; + struct nfs_entry *entry; + decode_dirent_t decode; + int plus; + int error; +} nfs_readdir_descriptor_t; + +/* Now we cache directories properly, by stuffing the dirent + * data directly in the page cache. + * + * Inode invalidation due to refresh etc. takes care of + * _everything_, no sloppy entry flushing logic, no extraneous + * copying, network direct to page cache, the way it was meant + * to be. + * + * NOTE: Dirent information verification is done always by the + * page-in of the RPC reply, nowhere else, this simplies + * things substantially. + */ +static +int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) +{ + struct file *file = desc->file; + struct inode *inode = file->f_dentry->d_inode; + void *buffer = (void *)kmap(page); + int plus = NFS_USE_READDIRPLUS(inode); + int error; + + dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); + + again: + error = NFS_PROTO(inode)->readdir(file, desc->entry->cookie, buffer, + NFS_SERVER(inode)->dtsize, plus); + /* We requested READDIRPLUS, but the server doesn't grok it */ + if (desc->plus && error == -ENOTSUPP) { + NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; + plus = 0; + goto again; + } + if (error < 0) + goto error; + SetPageUptodate(page); + kunmap(page); + /* Ensure consistent page alignment of the data. + * Note: assumes we have exclusive access to this mapping either + * throught inode->i_sem or some other mechanism. + */ + if (page->index == 0) + invalidate_inode_pages(inode); + UnlockPage(page); + return 0; + error: + SetPageError(page); + kunmap(page); + UnlockPage(page); + invalidate_inode_pages(inode); + desc->error = error; + return -EIO; +} /* * Given a pointer to a buffer that has already been filled by a call @@ -81,309 +143,217 @@ typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); * read. */ static inline -long find_dirent(struct page *page, loff_t offset, - struct nfs_entry *entry, - decode_dirent_t decode, int plus, int use_cookie) +int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) { - u8 *p = (u8 *)kmap(page), - *start = p; - unsigned long base = page_offset(page), - pg_offset = 0; - int loop_count = 0; + struct nfs_entry *entry = desc->entry; + char *start = (char *)kmap(page), + *p = start; + int loop_count = 0, + status = 0; - if (!p) - return -EIO; for(;;) { - p = (u8*)decode((__u32*)p, entry, plus); - if (IS_ERR(p)) + p = (char *)desc->decode((u32*)p, entry, desc->plus); + if (IS_ERR(p)) { + status = PTR_ERR(p); break; - pg_offset = p - start; - entry->prev = entry->offset; - entry->offset = base + pg_offset; - if ((use_cookie ? entry->cookie : entry->offset) > offset) + } + desc->page_offset = p - start; + dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); + if (entry->prev_cookie == desc->target) break; if (loop_count++ > 200) { loop_count = 0; schedule(); } } - kunmap(page); - return (IS_ERR(p)) ? PTR_ERR(p) : (long)pg_offset; + dfprintk(VFS, "NFS: find_dirent() returns %d\n", status); + return status; } /* * Find the given page, and call find_dirent() in order to try to * return the next entry. - * - * Returns -EIO if the page is not available, or up to date. */ static inline -long find_dirent_page(struct inode *inode, loff_t offset, - struct nfs_entry *entry) +int find_dirent_page(nfs_readdir_descriptor_t *desc) { - decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent; + struct inode *inode = desc->file->f_dentry->d_inode; struct page *page; - unsigned long index = entry->offset >> PAGE_CACHE_SHIFT; - long status = -EIO; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode); - - dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", entry->offset & PAGE_CACHE_MASK); - - if (entry->page) - page_cache_release(entry->page); - - page = find_get_page(&inode->i_data, index); + unsigned long index = desc->page_index; + int status; - if (page && Page_Uptodate(page)) - status = find_dirent(page, offset, entry, decode, plus, use_cookie); + dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); - /* NB: on successful return we will be holding the page */ - if (status < 0) { - entry->page = NULL; - if (page) - page_cache_release(page); - } else - entry->page = page; + if (desc->page) { + page_cache_release(desc->page); + desc->page = NULL; + } - dfprintk(VFS, "NFS: find_dirent_page() returns %ld\n", status); + page = read_cache_page(&inode->i_data, index, + (filler_t *)nfs_readdir_filler, desc); + if (IS_ERR(page)) { + status = PTR_ERR(page); + goto out; + } + if (!Page_Uptodate(page)) + goto read_error; + + /* NOTE: Someone else may have changed the READDIRPLUS flag */ + desc->plus = NFS_USE_READDIRPLUS(inode); + status = find_dirent(desc, page); + if (status >= 0) + desc->page = page; + else + page_cache_release(page); + out: + dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status); return status; + read_error: + page_cache_release(page); + return -EIO; } - /* * Recurse through the page cache pages, and return a * filled nfs_entry structure of the next directory entry if possible. * - * The target for the search is position 'offset'. - * The latter may either be an offset into the page cache, or (better) - * a cookie depending on whether we're interested in strictly following - * the RFC wrt. not assuming monotonicity of cookies or not. - * - * For most systems, the latter is more reliable since it naturally - * copes with holes in the directory. + * The target for the search is 'desc->target'. */ static inline -long search_cached_dirent_pages(struct inode *inode, loff_t offset, - struct nfs_entry *entry) +int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) { - long res = 0; + int res = 0; int loop_count = 0; - dfprintk(VFS, "NFS: search_cached_dirent_pages() searching for cookie %Ld\n", (long long)offset); + dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); for (;;) { - res = find_dirent_page(inode, offset, entry); - if (res == -EAGAIN) { - /* Align to beginning of next page */ - entry->offset &= PAGE_CACHE_MASK; - entry->offset += PAGE_CACHE_SIZE; - } + res = find_dirent_page(desc); if (res != -EAGAIN) break; + /* Align to beginning of next page */ + desc->page_offset = 0; + desc->page_index ++; if (loop_count++ > 200) { loop_count = 0; schedule(); } } - if (res < 0 && entry->page) { - page_cache_release(entry->page); - entry->page = NULL; - } - dfprintk(VFS, "NFS: search_cached_dirent_pages() returned %ld\n", res); - return res; -} - - -/* Now we cache directories properly, by stuffing the dirent - * data directly in the page cache. - * - * Inode invalidation due to refresh etc. takes care of - * _everything_, no sloppy entry flushing logic, no extraneous - * copying, network direct to page cache, the way it was meant - * to be. - * - * NOTE: Dirent information verification is done always by the - * page-in of the RPC reply, nowhere else, this simplies - * things substantially. - */ -static inline -long try_to_get_dirent_page(struct file *file, struct inode *inode, - struct nfs_entry *entry) -{ - struct dentry *dir = file->f_dentry; - struct page *page; - __u32 *p; - unsigned long index = entry->offset >> PAGE_CACHE_SHIFT; - long res = 0; - unsigned int dtsize = NFS_SERVER(inode)->dtsize; - int plus = NFS_USE_READDIRPLUS(inode); - - dfprintk(VFS, "NFS: try_to_get_dirent_page() reading directory page @ index %ld\n", index); - - page = grab_cache_page(&inode->i_data, index); - - if (!page) { - res = -ENOMEM; - goto out; - } - - if (Page_Uptodate(page)) { - dfprintk(VFS, "NFS: try_to_get_dirent_page(): page already up to date.\n"); - goto unlock_out; - } - - p = (__u32 *)kmap(page); - - if (dtsize > PAGE_CACHE_SIZE) - dtsize = PAGE_CACHE_SIZE; - res = NFS_PROTO(inode)->readdir(dir, entry->cookie, p, dtsize, plus); - - kunmap(page); - - if (res < 0) - goto error; - if (PageError(page)) - ClearPageError(page); - SetPageUptodate(page); - - unlock_out: - UnlockPage(page); - page_cache_release(page); - out: - dfprintk(VFS, "NFS: try_to_get_dirent_page() returns %ld\n", res); + dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res); return res; - error: - SetPageError(page); - goto unlock_out; } -/* Recover from a revalidation flush. The case here is that - * the inode for the directory got invalidated somehow, and - * all of our cached information is lost. In order to get - * a correct cookie for the current readdir request from the - * user, we must (re-)fetch all the older readdir page cache - * entries. - * - * Returns < 0 if some error occurs. +/* + * Once we've found the start of the dirent within a page: fill 'er up... */ -static inline -long refetch_to_readdir(struct file *file, struct inode *inode, - loff_t off, struct nfs_entry *entry) +static +int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) { - struct nfs_entry my_dirent, - *dirent = &my_dirent; - long res; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode), - loop_count = 0; - - dfprintk(VFS, "NFS: refetch_to_readdir() searching for cookie %Ld\n", (long long)off); - *dirent = *entry; - entry->page = NULL; - - for (res = 0;res >= 0;) { - if (loop_count++ > 200) { - loop_count = 0; - schedule(); - } + struct file *file = desc->file; + struct nfs_entry *entry = desc->entry; + char *start = (char *)kmap(desc->page), + *p = start + desc->page_offset; + unsigned long fileid; + int loop_count = 0, + res = 0; - /* Search for last cookie in page cache */ - res = search_cached_dirent_pages(inode, off, dirent); + dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); - if (res >= 0) { - /* Cookie was found */ - if ((use_cookie?dirent->cookie:dirent->offset) > off) { - *entry = *dirent; - dirent->page = NULL; - break; + for(;;) { + /* Note: entry->prev_cookie contains the cookie for + * retrieving the current dirent on the server */ + fileid = nfs_fileid_to_ino_t(entry->ino); + res = filldir(dirent, entry->name, entry->len, + entry->prev_cookie, fileid); + if (res < 0) + break; + file->f_pos = desc->target = entry->cookie; + p = (char *)desc->decode((u32 *)p, entry, desc->plus); + if (IS_ERR(p)) { + if (PTR_ERR(p) == -EAGAIN) { + desc->page_offset = 0; + desc->page_index ++; } - continue; - } - - if (dirent->page) - page_cache_release(dirent->page); - dirent->page = NULL; - - if (res != -EIO) { - *entry = *dirent; break; } - - /* Read in a new page */ - res = try_to_get_dirent_page(file, inode, dirent); - if (res == -EBADCOOKIE) { - memset(dirent, 0, sizeof(*dirent)); - nfs_zap_caches(inode); - res = 0; - } - /* We requested READDIRPLUS, but the server doesn't grok it */ - if (plus && res == -ENOTSUPP) { - NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; - memset(dirent, 0, sizeof(*dirent)); - nfs_zap_caches(inode); - plus = 0; - res = 0; + desc->page_offset = p - start; + if (loop_count++ > 200) { + loop_count = 0; + schedule(); } } - if (dirent->page) - page_cache_release(dirent->page); + kunmap(desc->page); + page_cache_release(desc->page); + desc->page = NULL; - dfprintk(VFS, "NFS: refetch_to_readdir() returns %ld\n", res); + dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); return res; } /* - * Once we've found the start of the dirent within a page: fill 'er up... + * If we cannot find a cookie in our cache, we suspect that this is + * because it points to a deleted file, so we ask the server to return + * whatever it thinks is the next entry. We then feed this to filldir. + * If all goes well, we should then be able to find our way round the + * cache on the next call to readdir_search_pagecache(); + * + * NOTE: we cannot add the anonymous page to the pagecache because + * the data it contains might not be page aligned. Besides, + * we should already have a complete representation of the + * directory in the page cache by the time we get here. */ -static -int nfs_do_filldir(struct file *file, struct inode *inode, - struct nfs_entry *entry, void *dirent, filldir_t filldir) +static inline +int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) { - decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent; - struct page *page = entry->page; - __u8 *p, - *start; - unsigned long base = page_offset(page), - offset = entry->offset, - pg_offset, - fileid; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode), - loop_count = 0, - res = 0; - - dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ offset %ld\n", entry->offset); - pg_offset = offset & ~PAGE_CACHE_MASK; - start = (u8*)kmap(page); - p = start + pg_offset; + struct file *file = desc->file; + struct inode *inode = file->f_dentry->d_inode; + struct page *page = NULL; + u32 *p; + int status = -EIO; + + dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); + if (desc->page) { + page_cache_release(desc->page); + desc->page = NULL; + } - for(;;) { - /* Note: entry->prev contains the offset of the start of the - * current dirent */ - fileid = nfs_fileid_to_ino_t(entry->ino); - if (use_cookie) - res = filldir(dirent, entry->name, entry->len, entry->prev_cookie, fileid); + page = page_cache_alloc(); + if (!page) { + status = -ENOMEM; + goto out; + } + p = (u32 *)kmap(page); + status = NFS_PROTO(inode)->readdir(file, desc->target, p, + NFS_SERVER(inode)->dtsize, 0); + if (status >= 0) { + p = desc->decode(p, desc->entry, 0); + if (IS_ERR(p)) + status = PTR_ERR(p); else - res = filldir(dirent, entry->name, entry->len, entry->prev, fileid); - if (res < 0) - break; - file->f_pos = (use_cookie) ? entry->cookie : entry->offset; - p = (u8*)decode((__u32*)p, entry, plus); - if (!p || IS_ERR(p)) - break; - pg_offset = p - start; - entry->prev = entry->offset; - entry->offset = base + pg_offset; - if (loop_count++ > 200) { - loop_count = 0; - schedule(); - } + desc->entry->prev_cookie = desc->target; } kunmap(page); - - dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ offset %ld; returning = %d\n", entry->offset, res); - return res; + if (status < 0) + goto out_release; + + desc->page_index = 0; + desc->page_offset = 0; + desc->page = page; + status = nfs_do_filldir(desc, dirent, filldir); + + /* Reset read descriptor so it searches the page cache from + * the start upon the next call to readdir_search_pagecache() */ + desc->page_index = 0; + desc->page_offset = 0; + memset(desc->entry, 0, sizeof(*desc->entry)); + out: + dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status); + return status; + out_release: + page_cache_release(page); + goto out; } /* The file offset position is now represented as a true offset into the @@ -393,10 +363,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; - struct page *page; - struct nfs_entry my_entry, - *entry = &my_entry; - loff_t offset; + nfs_readdir_descriptor_t my_desc, + *desc = &my_desc; + struct nfs_entry my_entry; long res; res = nfs_revalidate(dentry); @@ -409,36 +378,41 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) * read from the last dirent to revalidate f_pos * itself. */ - memset(entry, 0, sizeof(*entry)); + memset(desc, 0, sizeof(*desc)); + memset(&my_entry, 0, sizeof(my_entry)); - offset = filp->f_pos; + desc->file = filp; + desc->target = filp->f_pos; + desc->entry = &my_entry; + desc->decode = NFS_PROTO(inode)->decode_dirent; - while(!entry->eof) { - res = search_cached_dirent_pages(inode, offset, entry); - - if (res < 0) { - if (entry->eof) - break; - res = refetch_to_readdir(filp, inode, offset, entry); - if (res < 0) + while(!desc->entry->eof) { + res = readdir_search_pagecache(desc); + if (res == -EBADCOOKIE) { + /* This means either end of directory */ + if (desc->entry->cookie == desc->target) { + res = 0; break; + } + /* Or that the server has 'lost' a cookie */ + res = uncached_readdir(desc, dirent, filldir); + if (res >= 0) + continue; } + if (res < 0) + break; - page = entry->page; - if (!page) - printk(KERN_ERR "NFS: Missing page...\n"); - res = nfs_do_filldir(filp, inode, entry, dirent, filldir); - page_cache_release(page); - entry->page = NULL; + res = nfs_do_filldir(desc, dirent, filldir); if (res < 0) { res = 0; break; } - offset = filp->f_pos; } - if (entry->page) - page_cache_release(entry->page); - if (res < 0 && res != -EBADCOOKIE) + if (desc->page) + page_cache_release(desc->page); + if (desc->error < 0) + return desc->error; + if (res < 0) return res; return 0; } @@ -583,26 +557,18 @@ out_bad: /* * This is called from dput() when d_count is going to 0. - * We use it to clean up silly-renamed files. */ -static void nfs_dentry_delete(struct dentry *dentry) +static int nfs_dentry_delete(struct dentry *dentry) { dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_flags); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - int error; - - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; - /* Unhash it first */ - d_drop(dentry); - error = nfs_safe_remove(dentry); - if (error) - printk("NFS: can't silly-delete %s/%s, error=%d\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, error); + /* Unhash it, so that ->d_iput() would be called */ + return 1; } + return 0; } @@ -627,36 +593,30 @@ static void nfs_dentry_release(struct dentry *dentry) nfs_fh_free(dentry->d_fsdata); } -struct dentry_operations nfs_dentry_operations = { - d_revalidate: nfs_lookup_revalidate, - d_delete: nfs_dentry_delete, - d_release: nfs_dentry_release, -}; - -#if 0 /* dead code */ -#ifdef NFS_PARANOIA /* - * Display all dentries holding the specified inode. + * Called when the dentry loses inode. + * We use it to clean up silly-renamed files. */ -static void show_dentry(struct list_head * dlist) +static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) { - struct list_head *tmp = dlist; - - while ((tmp = tmp->next) != dlist) { - struct dentry * dentry = list_entry(tmp, struct dentry, d_alias); - const char * unhashed = ""; - - if (d_unhashed(dentry)) - unhashed = "(unhashed)"; - - printk("show_dentry: %s/%s, d_count=%d%s\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, dentry->d_count, - unhashed); + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { + struct dentry *dir = dentry->d_parent; + struct inode *dir_i = dir->d_inode; + int error; + + nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); + error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); } + iput(inode); } -#endif /* NFS_PARANOIA */ -#endif /* 0 */ + +struct dentry_operations nfs_dentry_operations = { + d_revalidate: nfs_lookup_revalidate, + d_delete: nfs_dentry_delete, + d_release: nfs_dentry_release, + d_iput: nfs_dentry_iput, +}; static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry) { @@ -715,7 +675,6 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, nfs_renew_times(dentry); error = 0; } - NFS_CACHEINV(dentry->d_parent->d_inode); return error; } @@ -809,7 +768,6 @@ static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode) d_drop(dentry); #endif nfs_zap_caches(dir_i); - dir_i->i_nlink++; error = NFS_PROTO(dir_i)->mkdir(dir, &dentry->d_name, &attr, &fhandle, &fattr); if (!error && fhandle.size != 0) @@ -830,13 +788,6 @@ static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry) nfs_zap_caches(dir_i); error = NFS_PROTO(dir_i)->rmdir(dir, &dentry->d_name); - /* Update i_nlink and invalidate dentry. */ - if (!error) { - d_drop(dentry); - if (dir_i->i_nlink) - dir_i->i_nlink--; - } - return error; } @@ -919,7 +870,7 @@ out: * Remove a file after making sure there are no pending writes, * and after checking that the file has only one user. * - * We update inode->i_nlink and free the inode prior to the operation + * We invalidate the attribute cache and free the inode prior to the operation * to avoid possible races if the server reuses the inode. */ static int nfs_safe_remove(struct dentry *dentry) @@ -927,28 +878,12 @@ static int nfs_safe_remove(struct dentry *dentry) struct dentry *dir = dentry->d_parent; struct inode *dir_i = dir->d_inode; struct inode *inode = dentry->d_inode; - int error, rehash = 0; + int error = -EBUSY, rehash = 0; dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino); - /* N.B. not needed now that d_delete is done in advance? */ - error = -EBUSY; - if (!inode) { -#ifdef NFS_PARANOIA -printk("nfs_safe_remove: %s/%s already negative??\n", -dentry->d_parent->d_name.name, dentry->d_name.name); -#endif - } - - if (dentry->d_count > 1) { -#ifdef NFS_PARANOIA -printk("nfs_safe_remove: %s/%s busy, d_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); -#endif - goto out; - } /* * Unhash the dentry while we remove the file ... */ @@ -956,24 +891,26 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); d_drop(dentry); rehash = 1; } + if (dentry->d_count > 1) { +#ifdef NFS_PARANOIA + printk("nfs_safe_remove: %s/%s busy, d_count=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_count); +#endif + goto out; + } nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); if (error < 0) goto out; /* - * Update i_nlink and free the inode + * Free the inode */ - if (inode) { - if (inode->i_nlink) - inode->i_nlink --; - d_delete(dentry); - } - /* - * Rehash the negative dentry if the operation succeeded. - */ - if (rehash) - d_add(dentry, NULL); + d_delete(dentry); out: + if (rehash) + d_rehash(dentry); return error; } @@ -1067,14 +1004,8 @@ nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) */ d_drop(dentry); nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->link(old_dentry, dir, &dentry->d_name); - if (!error) { - /* - * Update the link count immediately, as some apps - * (e.g. pine) test this after making a link. - */ - inode->i_nlink++; - } return error; } @@ -1107,8 +1038,17 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; - struct dentry *dentry = NULL; - int error, rehash = 0; + struct dentry *dentry = NULL, *rehash = NULL; + int error = -EBUSY; + + /* + * To prevent any new references to the target during the rename, + * we unhash the dentry and free the inode in advance. + */ + if (!d_unhashed(new_dentry)) { + d_drop(new_dentry); + rehash = new_dentry; + } dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, @@ -1125,7 +1065,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ if (!new_inode) goto go_ahead; - error = -EBUSY; if (S_ISDIR(new_inode->i_mode)) goto out; else if (new_dentry->d_count > 1) { @@ -1139,10 +1078,10 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* silly-rename the existing target ... */ err = nfs_sillyrename(new_dir, new_dentry); if (!err) { - new_dentry = dentry; + new_dentry = rehash = dentry; new_inode = NULL; - /* hash the replacement target */ - d_add(new_dentry, NULL); + /* instantiate the replacement target */ + d_instantiate(new_dentry, NULL); } /* dentry still busy? */ @@ -1166,14 +1105,6 @@ go_ahead: shrink_dcache_parent(old_dentry); } - /* - * To prevent any new references to the target during the rename, - * we unhash the dentry and free the inode in advance. - */ - if (!d_unhashed(new_dentry)) { - d_drop(new_dentry); - rehash = 1; - } if (new_inode) d_delete(new_dentry); @@ -1183,15 +1114,12 @@ go_ahead: &old_dentry->d_name, new_dentry->d_parent, &new_dentry->d_name); - NFS_CACHEINV(old_dir); - NFS_CACHEINV(new_dir); - /* Update the dcache if needed */ +out: if (rehash) - d_add(new_dentry, NULL); + d_rehash(rehash); if (!error && !S_ISDIR(old_inode->i_mode)) d_move(old_dentry, new_dentry); -out: /* new dentry created? */ if (dentry) dput(dentry); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 2d71aa7b5c8..44e3030c381 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -555,22 +555,18 @@ nfs_free_dentries(struct inode *inode) struct list_head *tmp, *head = &inode->i_dentry; int unhashed; -restart: + if (S_ISDIR(inode->i_mode)) { + struct dentry *dentry = d_find_alias(inode); + if (dentry) { + shrink_dcache_parent(dentry); + dput(dentry); + } + } + d_prune_aliases(inode); tmp = head; unhashed = 0; while ((tmp = tmp->next) != head) { struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); - dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_count, !d_unhashed(dentry)); - if (!list_empty(&dentry->d_subdirs)) - shrink_dcache_parent(dentry); - if (!dentry->d_count) { - dget(dentry); - d_drop(dentry); - dput(dentry); - goto restart; - } if (d_unhashed(dentry)) unhashed++; } @@ -895,11 +891,20 @@ nfs_revalidate(struct dentry *dentry) */ int nfs_open(struct inode *inode, struct file *filp) { + struct rpc_auth *auth = NFS_CLIENT(inode)->cl_auth; + struct rpc_cred *cred = rpcauth_lookupcred(auth, 0); + + filp->private_data = cred; return 0; } int nfs_release(struct inode *inode, struct file *filp) { + struct rpc_auth *auth = NFS_CLIENT(inode)->cl_auth; + struct rpc_cred *cred = nfs_file_cred(filp); + + if (cred) + rpcauth_releasecred(auth, cred); return 0; } diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 1dd1553ba13..a8b61c2e7ef 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -485,13 +485,6 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) break; } } - p++; /* EOF flag */ - - if (p > end) { - printk(KERN_NOTICE - "NFS: short packet in readdir reply!\n"); - return -errno_NFSERR_IO; - } return nr; } diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 67de662a6b1..921841ba3df 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -138,14 +138,16 @@ nfs3_proc_readlink(struct dentry *dentry, void *buffer, unsigned int buflen) } static int -nfs3_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, +nfs3_proc_read(struct file *file, struct nfs_fattr *fattr, int flags, loff_t offset, unsigned int count, void *buffer, int *eofp) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_readargs arg = { NFS_FH(dentry), offset, count, 1, {{buffer, count}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} }; struct nfs_readres res = { fattr, count, 0 }; - struct rpc_message msg = { NFS3PROC_READ, &arg, &res, NULL }; + struct rpc_message msg = { NFS3PROC_READ, &arg, &res, cred }; int status; dprintk("NFS call read %d @ %Ld\n", count, (long long)offset); @@ -157,16 +159,18 @@ nfs3_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, } static int -nfs3_proc_write(struct dentry *dentry, struct nfs_fattr *fattr, int flags, +nfs3_proc_write(struct file *file, struct nfs_fattr *fattr, int flags, loff_t offset, unsigned int count, void *buffer, struct nfs_writeverf *verf) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_writeargs arg = { NFS_FH(dentry), offset, count, NFS_FILE_SYNC, 1, {{buffer, count}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} }; struct nfs_writeres res = { fattr, verf, 0 }; - struct rpc_message msg = { NFS3PROC_WRITE, &arg, &res, NULL }; + struct rpc_message msg = { NFS3PROC_WRITE, &arg, &res, cred }; int status, rpcflags = 0; dprintk("NFS call write %d @ %Ld\n", count, (long long)offset); @@ -369,13 +373,15 @@ nfs3_proc_rmdir(struct dentry *dir, struct qstr *name) * readdirplus. */ static int -nfs3_proc_readdir(struct dentry *dir, u64 cookie, void *entry, +nfs3_proc_readdir(struct file *file, u64 cookie, void *entry, unsigned int size, int plus) { + struct dentry *dir = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_fattr dir_attr; struct nfs3_readdirargs arg = { NFS_FH(dir), cookie, {0, 0}, 0, 0, 0 }; struct nfs3_readdirres res = { &dir_attr, 0, 0, 0, 0 }; - struct rpc_message msg = { NFS3PROC_READDIR, &arg, &res, NULL }; + struct rpc_message msg = { NFS3PROC_READDIR, &arg, &res, cred }; u32 *verf = NFS_COOKIEVERF(dir->d_inode); int status; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index d6d5322174f..0abf65af2e8 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -122,14 +122,16 @@ nfs_proc_readlink(struct dentry *dentry, void *buffer, unsigned int bufsiz) } static int -nfs_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, +nfs_proc_read(struct file *file, struct nfs_fattr *fattr, int flags, loff_t offset, unsigned int count, void *buffer, int *eofp) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_readargs arg = { NFS_FH(dentry), offset, count, 1, {{ buffer, count }, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} }; struct nfs_readres res = { fattr, count, 0}; - struct rpc_message msg = { NFSPROC_READ, &arg, &res, NULL }; + struct rpc_message msg = { NFSPROC_READ, &arg, &res, cred }; int status; dprintk("NFS call read %d @ %Ld\n", count, (long long)offset); @@ -142,16 +144,18 @@ nfs_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, } static int -nfs_proc_write(struct dentry *dentry, struct nfs_fattr *fattr, int how, +nfs_proc_write(struct file *file, struct nfs_fattr *fattr, int how, loff_t offset, unsigned int count, void *buffer, struct nfs_writeverf *verf) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_writeargs arg = {NFS_FH(dentry), offset, count, NFS_FILE_SYNC, 1, {{buffer, count}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}}; struct nfs_writeres res = {fattr, verf, count}; - struct rpc_message msg = { NFSPROC_WRITE, &arg, &res, NULL }; + struct rpc_message msg = { NFSPROC_WRITE, &arg, &res, cred }; int status, flags = 0; dprintk("NFS call write %d @ %Ld\n", count, (long long)offset); @@ -311,12 +315,14 @@ nfs_proc_rmdir(struct dentry *dir, struct qstr *name) * from nfs_readdir by calling the decode_entry function directly. */ static int -nfs_proc_readdir(struct dentry *dir, __u64 cookie, void *entry, +nfs_proc_readdir(struct file *file, __u64 cookie, void *entry, unsigned int size, int plus) { + struct dentry *dir = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_readdirargs arg; struct nfs_readdirres res; - struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, NULL }; + struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, cred }; struct nfs_server *server = NFS_DSERVER(dir); int status; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 1c70ae58df7..b15f50e6150 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -83,8 +83,9 @@ static void nfs_readdata_release(struct rpc_task *task) * Read a page synchronously. */ static int -nfs_readpage_sync(struct dentry *dentry, struct page *page) +nfs_readpage_sync(struct file *file, struct page *page) { + struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct nfs_fattr fattr; loff_t offset = page_offset(page); @@ -112,7 +113,7 @@ nfs_readpage_sync(struct dentry *dentry, struct page *page) (long long)offset, rsize, buffer); lock_kernel(); - result = NFS_PROTO(inode)->read(dentry, &fattr, flags, offset, + result = NFS_PROTO(inode)->read(file, &fattr, flags, offset, rsize, buffer, &eof); unlock_kernel(); nfs_refresh_inode(inode, &fattr); @@ -195,9 +196,9 @@ nfs_mark_request_read(struct nfs_page *req) } static int -nfs_readpage_async(struct dentry *dentry, struct page *page) +nfs_readpage_async(struct file *file, struct page *page) { - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; struct nfs_page *req, *new = NULL; int result; @@ -227,7 +228,7 @@ nfs_readpage_async(struct dentry *dentry, struct page *page) } result = -ENOMEM; - new = nfs_create_request(dentry, page, 0, PAGE_CACHE_SIZE); + new = nfs_create_request(file, page, 0, PAGE_CACHE_SIZE); if (!new) break; } @@ -462,20 +463,16 @@ nfs_readpage_result(struct rpc_task *task) /* * Read a page over NFS. * We read the page synchronously in the following cases: - * - The file is a swap file. Swap-ins are always sync operations, - * so there's no need bothering to make async reads 100% fail-safe. * - The NFS rsize is smaller than PAGE_CACHE_SIZE. We could kludge our way * around this by creating several consecutive read requests, but * that's hardly worth it. * - The error flag is set for this page. This happens only when a * previous async read operation failed. - * - The server is congested. */ int nfs_readpage(struct file *file, struct page *page) { - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; int error; dprintk("NFS: nfs_readpage (%p %ld@%lu)\n", @@ -493,11 +490,11 @@ nfs_readpage(struct file *file, struct page *page) error = -1; if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) - error = nfs_readpage_async(dentry, page); + error = nfs_readpage_async(file, page); if (error >= 0) goto out; - error = nfs_readpage_sync(dentry, page); + error = nfs_readpage_sync(file, page); if (error < 0 && IS_SWAPFILE(inode)) printk("Aiee.. nfs swap-in of page failed!\n"); out: diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 52af85acb45..464776ac3c6 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -89,8 +89,7 @@ struct nfs_write_data { /* * Local function declarations */ -static struct nfs_page * nfs_update_request(struct file*, struct dentry *, - struct page *page, +static struct nfs_page * nfs_update_request(struct file*, struct page *page, unsigned int, unsigned int); static void nfs_strategy(struct inode *inode); static void nfs_writeback_done(struct rpc_task *); @@ -168,9 +167,10 @@ nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr) * Offset is the data offset within the page. */ static int -nfs_writepage_sync(struct dentry *dentry, struct page *page, +nfs_writepage_sync(struct file *file, struct page *page, unsigned int offset, unsigned int count) { + struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; loff_t base; unsigned int wsize = NFS_SERVER(inode)->wsize; @@ -193,7 +193,7 @@ nfs_writepage_sync(struct dentry *dentry, struct page *page, if (count < wsize && !IS_SWAPFILE(inode)) wsize = count; - result = NFS_PROTO(inode)->write(dentry, &fattr, flags, + result = NFS_PROTO(inode)->write(file, &fattr, flags, base, wsize, buffer, &verf); nfs_write_attributes(inode, &fattr); @@ -229,18 +229,18 @@ io_error: } static int -nfs_writepage_async(struct file *file, struct dentry *dentry, struct page *page, +nfs_writepage_async(struct file *file, struct page *page, unsigned int offset, unsigned int count) { struct nfs_page *req; int status; - req = nfs_update_request(file, dentry, page, offset, count); + req = nfs_update_request(file, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status < 0) goto out; nfs_release_request(req); - nfs_strategy(dentry->d_inode); + nfs_strategy(file->f_dentry->d_inode); out: return status; } @@ -251,8 +251,7 @@ nfs_writepage_async(struct file *file, struct dentry *dentry, struct page *page, int nfs_writepage(struct file *file, struct page *page) { - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT; unsigned offset = PAGE_CACHE_SIZE; int err; @@ -267,11 +266,11 @@ nfs_writepage(struct file *file, struct page *page) return -EIO; do_it: if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) { - err = nfs_writepage_async(file, dentry, page, 0, offset); + err = nfs_writepage_async(file, page, 0, offset); if (err >= 0) goto out_ok; } - err = nfs_writepage_sync(dentry, page, 0, offset); + err = nfs_writepage_sync(file, page, 0, offset); if ( err == offset) goto out_ok; return err; @@ -476,10 +475,12 @@ nfs_mark_request_commit(struct nfs_page *req) * Page must be locked by the caller. This makes sure we never create * two different requests for the same page, and avoids possible deadlock * when we reach the hard limit on the number of dirty pages. + * It should be safe to sleep here. */ -struct nfs_page *nfs_create_request(struct dentry *dentry, struct page *page, +struct nfs_page *nfs_create_request(struct file *file, struct page *page, unsigned int offset, unsigned int count) { + struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct nfs_reqlist *cache = NFS_REQUESTLIST(inode); struct nfs_page *req = NULL; @@ -531,8 +532,10 @@ struct nfs_page *nfs_create_request(struct dentry *dentry, struct page *page, page_cache_get(page); req->wb_offset = offset; req->wb_bytes = count; - req->wb_dentry = dget(dentry); - req->wb_cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + req->wb_file = file; + get_file(file); + req->wb_dentry = dentry; + req->wb_cred = nfs_file_cred(file); req->wb_count = 1; /* register request's existence */ @@ -573,12 +576,7 @@ nfs_release_request(struct nfs_page *req) if (NFS_WBACK_BUSY(req)) printk(KERN_ERR "NFS: Request released while still locked!\n"); - rpcauth_releasecred(NFS_CLIENT(inode)->cl_auth, req->wb_cred); - lock_kernel(); - if (req->wb_file) - fput(req->wb_file); - dput(req->wb_dentry); - unlock_kernel(); + fput(req->wb_file); page_cache_release(page); nfs_page_free(req); /* wake up anyone waiting to allocate a request */ @@ -789,10 +787,6 @@ int nfs_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned if (prev) { if (req->wb_file != prev->wb_file) break; - if (req->wb_dentry != prev->wb_dentry) - break; - if (req->wb_cred != prev->wb_cred) - break; if (page_index(req->wb_page) != page_index(prev->wb_page)+1) break; @@ -818,10 +812,10 @@ int nfs_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned * Note: Should always be called with the Page Lock held! */ static struct nfs_page * -nfs_update_request(struct file* file, struct dentry *dentry, struct page *page, +nfs_update_request(struct file* file, struct page *page, unsigned int offset, unsigned int bytes) { - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; struct nfs_page *req, *new = NULL; unsigned long rqend, end; @@ -856,21 +850,14 @@ nfs_update_request(struct file* file, struct dentry *dentry, struct page *page, } spin_unlock(&nfs_wreq_lock); - - /* Create the request. It's safe to sleep in this call because - * we only get here if the page is locked. - * + /* * If we're over the soft limit, flush out old requests */ - if (file && nfs_nr_requests >= MAX_REQUEST_SOFT) + if (inode->u.nfs_i.npages >= MAX_REQUEST_SOFT) nfs_wb_file(inode, file); - new = nfs_create_request(dentry, page, offset, bytes); + new = nfs_create_request(file, page, offset, bytes); if (!new) return ERR_PTR(-ENOMEM); - if (file) { - new->wb_file = file; - get_file(file); - } /* If the region is locked, adjust the timeout */ if (region_locked(inode, new)) new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY; @@ -1006,7 +993,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign * page synchronously. */ if (NFS_SERVER(inode)->wsize < PAGE_SIZE) - return nfs_writepage_sync(dentry, page, offset, count); + return nfs_writepage_sync(file, page, offset, count); /* * Try to find an NFS request corresponding to this page @@ -1015,7 +1002,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign * it out now. */ do { - req = nfs_update_request(file, dentry, page, offset, count); + req = nfs_update_request(file, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status != -EBUSY) break; diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index 79ef12a7bc3..c47830ff30a 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -49,16 +49,21 @@ nfsd_cache_init(void) struct svc_cacherep *rp; struct nfscache_head *rh; size_t i; + unsigned long order; if (cache_initialized) return; i = CACHESIZE * sizeof (struct svc_cacherep); - nfscache = kmalloc (i, GFP_KERNEL); + for (order = 0; (PAGE_SIZE << order) < i; order++) + ; + nfscache = (struct svc_cacherep *) + __get_free_pages(GFP_KERNEL, order); if (!nfscache) { printk (KERN_ERR "nfsd: cannot allocate %d bytes for reply cache\n", i); return; } + memset(nfscache, 0, i); i = HASHSIZE * sizeof (struct nfscache_head); hash_list = kmalloc (i, GFP_KERNEL); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index a5fcdcf7d73..f681c9bfc1d 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -812,17 +812,11 @@ fh_put(struct svc_fh *fhp) { struct dentry * dentry = fhp->fh_dentry; if (fhp->fh_dverified) { + fhp->fh_dentry = NULL; fh_unlock(fhp); fhp->fh_dverified = 0; - if (!dentry->d_count) - goto out_bad; dput(dentry); nfsd_nr_put++; } return; - -out_bad: - printk(KERN_ERR "fh_put: %s/%s has d_count 0!\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - return; } diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index 92eed75596f..3b875a4452e 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -1,4 +1,4 @@ -/* $Id: inode.c,v 1.10 2000/03/24 01:32:51 davem Exp $ +/* $Id: inode.c,v 1.11 2000/05/22 07:29:42 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) @@ -870,7 +870,6 @@ static int openpromfs_unlink (struct inode *dir, struct dentry *dentry) buffer [10 + len] = 0; prom_feval (buffer); } - d_delete(dentry); return 0; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 7f9ed2ba78b..3b252ec639d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -135,6 +135,9 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) case IDE0_MAJOR: maj = "hd"; break; + case MD_MAJOR: + unit -= 'a'-'0'; + break; } if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) { unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16; diff --git a/fs/pipe.c b/fs/pipe.c index 525cd7285c2..b97851fab7b 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -465,7 +465,7 @@ fail_page: return NULL; } -static struct vfsmount *pipe_mnt = NULL; +static struct vfsmount *pipe_mnt; static struct inode * get_pipe_inode(void) { @@ -609,6 +609,7 @@ static struct super_block * pipefs_read_super(struct super_block *sb, void *data root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; + sb->s_magic = PIPEFS_MAGIC; sb->s_op = &pipefs_ops; sb->s_root = d_alloc(NULL, &(const struct qstr) { "pipe:", 5, 0 }); if (!sb->s_root) { diff --git a/fs/proc/base.c b/fs/proc/base.c index 2e83c6a4ec5..d513987d828 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -22,6 +22,7 @@ #include #include #include +#include /* * For hysterical raisins we keep the same inumbers as in the old procfs. @@ -651,6 +652,11 @@ static int pid_fd_revalidate(struct dentry * dentry, int flags) return 0; } +/* + * Exceptional case: normally we are not allowed to unhash a busy + * directory. In this case, however, we can do it - no aliasing problems + * due to the way we treat inodes. + */ static int pid_base_revalidate(struct dentry * dentry, int flags) { if (dentry->d_inode->u.proc_i.task->p_pptr) @@ -659,9 +665,9 @@ static int pid_base_revalidate(struct dentry * dentry, int flags) return 0; } -static void pid_delete_dentry(struct dentry * dentry) +static int pid_delete_dentry(struct dentry * dentry) { - d_drop(dentry); + return 1; } static struct dentry_operations pid_fd_dentry_operations = @@ -861,6 +867,28 @@ static struct inode_operations proc_base_inode_operations = { lookup: proc_base_lookup, }; +/* + * /proc/self: + */ +static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + char tmp[30]; + sprintf(tmp, "%d", current->pid); + return vfs_readlink(dentry,buffer,buflen,tmp); +} + +static int proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char tmp[30]; + sprintf(tmp, "%d", current->pid); + return vfs_follow_link(nd,tmp); +} + +static struct inode_operations proc_self_inode_operations = { + readlink: proc_self_readlink, + follow_link: proc_self_follow_link, +}; + struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) { unsigned int pid, c; @@ -872,6 +900,23 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) pid = 0; name = dentry->d_name.name; len = dentry->d_name.len; + if (len == 4 && !memcmp(name, "self", 4)) { + inode = get_empty_inode(); + if (!inode) + return ERR_PTR(-ENOMEM); + inode->i_sb = dir->i_sb; + inode->i_dev = dir->i_sb->s_dev; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_ino = fake_ino(0, PROC_PID_INO); + inode->u.proc_i.file = NULL; + inode->u.proc_i.task = NULL; + inode->i_mode = S_IFLNK|S_IRWXUGO; + inode->i_uid = inode->i_gid = 0; + inode->i_size = 64; + inode->i_op = &proc_self_inode_operations; + d_add(dentry, inode); + return NULL; + } while (len-- > 0) { c = *name - '0'; name++; @@ -916,7 +961,8 @@ void proc_pid_delete_inode(struct inode *inode) { if (inode->u.proc_i.file) fput(inode->u.proc_i.file); - free_task_struct(inode->u.proc_i.task); + if (inode->u.proc_i.task) + free_task_struct(inode->u.proc_i.task); } #define PROC_NUMBUF 10 @@ -932,7 +978,7 @@ static int get_pid_list(int index, unsigned int *pids) struct task_struct *p; int nr_pids = 0; - index -= FIRST_PROCESS_ENTRY; + index--; read_lock(&tasklist_lock); for_each_task(p) { int pid = p->pid; @@ -953,9 +999,17 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { unsigned int pid_array[PROC_MAXPIDS]; char buf[PROC_NUMBUF]; - unsigned int nr = filp->f_pos; + unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; unsigned int nr_pids, i; + if (!nr) { + ino_t ino = fake_ino(0,PROC_PID_INO); + if (filldir(dirent, "self", 4, filp->f_pos, ino) < 0) + return 0; + filp->f_pos++; + nr++; + } + nr_pids = get_pid_list(nr, pid_array); for (i = 0; i < nr_pids; i++) { @@ -963,11 +1017,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) ino_t ino = fake_ino(pid,PROC_PID_INO); unsigned long j = PROC_NUMBUF; - do { - j--; - buf[j] = '0' + (pid % 10); - pid /= 10; - } while (pid); + do buf[--j] = '0' + (pid % 10); while (pid/=10); if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino) < 0) break; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 31e43fab99f..1585657a2d7 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -140,9 +140,13 @@ proc_file_lseek(struct file * file, loff_t offset, int orig) { switch (orig) { case 0: + if (offset < 0) + return -EINVAL; file->f_pos = offset; return(file->f_pos); case 1: + if (offset + file->f_pos < 0) + return -EINVAL; file->f_pos += offset; return(file->f_pos); case 2: @@ -218,10 +222,9 @@ static struct inode_operations proc_link_inode_operations = { * smarter: we could keep a "volatile" flag in the * inode to indicate which ones to keep. */ -static void -proc_delete_dentry(struct dentry * dentry) +static int proc_delete_dentry(struct dentry * dentry) { - d_drop(dentry); + return 1; } static struct dentry_operations proc_dentry_operations = @@ -340,7 +343,7 @@ static struct inode_operations proc_dir_inode_operations = { lookup: proc_lookup, }; -int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) +static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) { int i; diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 13ec76b0201..01db469dac9 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -315,13 +315,12 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t size_t elf_buflen; int num_vma; - /* XXX we need to somehow lock vmlist between here - * and after elf_kcore_store_hdr() returns. - * For now assume that num_vma does not change (TA) - */ + read_lock(&vmlist_lock); proc_root_kcore->size = size = get_kcore_size(&num_vma, &elf_buflen); - if (buflen == 0 || *fpos >= size) + if (buflen == 0 || *fpos >= size) { + read_unlock(&vmlist_lock); return 0; + } /* trim buflen to not go beyond EOF */ if (buflen > size - *fpos) @@ -335,10 +334,13 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t if (buflen < tsz) tsz = buflen; elf_buf = kmalloc(elf_buflen, GFP_ATOMIC); - if (!elf_buf) + if (!elf_buf) { + read_unlock(&vmlist_lock); return -ENOMEM; + } memset(elf_buf, 0, elf_buflen); elf_kcore_store_hdr(elf_buf, num_vma, elf_buflen); + read_unlock(&vmlist_lock); if (copy_to_user(buffer, elf_buf + *fpos, tsz)) { kfree(elf_buf); return -EFAULT; @@ -352,7 +354,8 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t /* leave now if filled buffer already */ if (buflen == 0) return acc; - } + } else + read_unlock(&vmlist_lock); /* where page 0 not mapped, write zeros into buffer */ #if defined (__i386__) || defined (__mc68000__) diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 88d41c3c21f..c64166f78ac 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -112,7 +112,6 @@ static void add_node(struct device_node *np, struct proc_dir_entry *de) al = proc_symlink(at, de, ent->name); if (al == 0) break; - proc_register(de, al); *lastp = al; lastp = &al->next; } diff --git a/fs/proc/root.c b/fs/proc/root.c index 8088d064d90..075a5843df6 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -22,38 +22,9 @@ struct proc_dir_entry *proc_net, *proc_bus, *proc_root_fs, *proc_root_driver; struct proc_dir_entry *proc_sys_root; #endif -/* - * /proc/self: - */ -static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen) -{ - char tmp[30]; - sprintf(tmp, "%d", current->pid); - return vfs_readlink(dentry,buffer,buflen,tmp); -} - -static int proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char tmp[30]; - sprintf(tmp, "%d", current->pid); - return vfs_follow_link(nd,tmp); -} - -static struct inode_operations proc_self_inode_operations = { - readlink: proc_self_readlink, - follow_link: proc_self_follow_link -}; - -static struct proc_dir_entry proc_root_self = { - 0, 4, "self", - S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 1, 0, 0, - 64, &proc_self_inode_operations, -}; - void __init proc_root_init(void) { proc_misc_init(); - proc_register(&proc_root, &proc_root_self); proc_net = proc_mkdir("net", 0); #ifdef CONFIG_SYSVIPC proc_mkdir("sysvipc", 0); diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 5be9b240f0e..3ef38467cd0 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -184,7 +184,6 @@ int qnx4_rmdir(struct inode *dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; mark_inode_dirty(dir); - d_delete(dentry); retval = 0; end_rmdir: @@ -228,7 +227,6 @@ int qnx4_unlink(struct inode *dir, struct dentry *dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); retval = 0; end_unlink: diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 75e94efd99a..4416e8be688 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -227,7 +227,6 @@ static int ramfs_unlink(struct inode * dir, struct dentry *dentry) inode->i_nlink--; dput(dentry); /* Undo the count from "create" - this does all the work */ - d_delete(dentry); retval = 0; } return retval; @@ -269,57 +268,6 @@ static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * return error; } -/* - * This really should be the same as the proc filldir, - * once proc does the "one dentry tree" thing.. - */ -static int ramfs_readdir(struct file * filp, void * dirent, filldir_t filldir) -{ - int i; - struct dentry *dentry = filp->f_dentry; - - i = filp->f_pos; - switch (i) { - case 0: - if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino) < 0) - break; - i++; - filp->f_pos++; - /* fallthrough */ - case 1: - if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino) < 0) - break; - i++; - filp->f_pos++; - /* fallthrough */ - default: { - struct list_head *list = dentry->d_subdirs.next; - - int j = i-2; - for (;;) { - if (list == &dentry->d_subdirs) - return 0; - if (!j) - break; - j--; - list = list->next; - } - - do { - struct dentry *de = list_entry(list, struct dentry, d_child); - - if (ramfs_positive(de)) { - if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino) < 0) - break; - } - filp->f_pos++; - list = list->next; - } while (list != &dentry->d_subdirs); - } - } - return 0; -} - static struct address_space_operations ramfs_aops = { readpage: ramfs_readpage, writepage: ramfs_writepage, @@ -335,7 +283,7 @@ static struct file_operations ramfs_file_operations = { static struct file_operations ramfs_dir_operations = { read: generic_read_dir, - readdir: ramfs_readdir, + readdir: dcache_readdir, }; static struct inode_operations ramfs_dir_inode_operations = { @@ -350,9 +298,15 @@ static struct inode_operations ramfs_dir_inode_operations = { rename: ramfs_rename, }; +static void ramfs_put_super(struct super_block *sb) +{ + d_genocide(sb->s_root); + shrink_dcache_parent(sb->s_root); +} static struct super_operations ramfs_ops = { - statfs: ramfs_statfs, + put_super: ramfs_put_super, + statfs: ramfs_statfs, }; static struct super_block *ramfs_read_super(struct super_block * sb, void * data, int silent) diff --git a/fs/readdir.c b/fs/readdir.c index e6256636edb..059ab391d3e 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -32,6 +32,53 @@ out: return res; } +int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + int i; + struct dentry *dentry = filp->f_dentry; + + i = filp->f_pos; + switch (i) { + case 0: + if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino) < 0) + break; + i++; + filp->f_pos++; + /* fallthrough */ + case 1: + if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino) < 0) + break; + i++; + filp->f_pos++; + /* fallthrough */ + default: { + struct list_head *list = dentry->d_subdirs.next; + + int j = i-2; + for (;;) { + if (list == &dentry->d_subdirs) + return 0; + if (!j) + break; + j--; + list = list->next; + } + + do { + struct dentry *de = list_entry(list, struct dentry, d_child); + + if (!d_unhashed(de) && de->d_inode) { + if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino) < 0) + break; + } + filp->f_pos++; + list = list->next; + } while (list != &dentry->d_subdirs); + } + } + return 0; +} + /* * Traditional linux readdir() handling.. * diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index a29e55c7a78..b5715b220d0 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -168,7 +168,7 @@ file->f_dentry->d_name.name); static int smb_lookup_validate(struct dentry *, int); static int smb_hash_dentry(struct dentry *, struct qstr *); static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *); -static void smb_delete_dentry(struct dentry *); +static int smb_delete_dentry(struct dentry *); static struct dentry_operations smbfs_dentry_operations = { @@ -259,9 +259,9 @@ out: /* * This is the callback from dput() when d_count is going to 0. - * We use this to unhash dentries with bad inodes and close files. + * We use this to unhash dentries with bad inodes. */ -static void +static int smb_delete_dentry(struct dentry * dentry) { if (dentry->d_inode) @@ -272,13 +272,13 @@ smb_delete_dentry(struct dentry * dentry) printk("smb_delete_dentry: bad inode, unhashing %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); #endif - d_drop(dentry); + return 1; } - smb_close_dentry(dentry); } else { /* N.B. Unhash negative dentries? */ } + return 0; } /* @@ -466,10 +466,7 @@ smb_unlink(struct inode *dir, struct dentry *dentry) smb_invalid_dir_cache(dir); error = smb_proc_unlink(dentry); if (!error) - { smb_renew_times(dentry); - d_delete(dentry); - } return error; } diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 61f50bdff51..b47e236b0ee 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -26,12 +26,6 @@ /* #define SMBFS_DEBUG_VERBOSE 1 */ /* #define pr_debug printk */ -static inline int -min(int a, int b) -{ - return a < b ? a : b; -} - static int smb_fsync(struct file *file, struct dentry * dentry) { @@ -340,28 +334,15 @@ out: static int smb_file_open(struct inode *inode, struct file * file) { -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_file_open: opening %s/%s, d_count=%d\n", -file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name, -file->f_dentry->d_count); -#endif + inode->u.smbfs_i.openers++; return 0; } static int smb_file_release(struct inode *inode, struct file * file) { - struct dentry * dentry = file->f_dentry; - -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_file_release: closing %s/%s, d_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); -#endif - - if (dentry->d_count == 1) - { + if (!--inode->u.smbfs_i.openers) smb_close(inode); - } return 0; } diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 06cd5dda9a7..669deb0d79b 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -818,11 +818,6 @@ smb_open(struct dentry *dentry, int wish) goto out; } - /* - * Note: If the caller holds an active dentry and the file is - * currently open, we can be sure that the file isn't about - * to be closed. (See smb_close_dentry() below.) - */ if (!smb_is_open(inode)) { struct smb_sb_info *server = SMB_SERVER(inode); @@ -944,46 +939,6 @@ smb_close(struct inode *ino) } /* - * This routine is called from dput() when d_count is going to 0. - * We use this to close the file so that cached dentries don't - * keep too many files open. - * - * There are some tricky race conditions here: the dentry may go - * back into use while we're closing the file, and we don't want - * the new user to be confused as to the open status. - */ -void -smb_close_dentry(struct dentry * dentry) -{ - struct inode *ino = dentry->d_inode; - - if (ino) - { - if (smb_is_open(ino)) - { - struct smb_sb_info *server = SMB_SERVER(ino); - smb_lock_server(server); - /* - * Check whether the dentry is back in use. - */ - if (dentry->d_count <= 1) - { -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_close_dentry: closing %s/%s, count=%d\n", - DENTRY_PATH(dentry), dentry->d_count); -#endif - smb_proc_close_inode(server, ino); - } - smb_unlock_server(server); - } -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_close_dentry: closed %s/%s, count=%d\n", - DENTRY_PATH(dentry), dentry->d_count); -#endif - } -} - -/* * This is used to close a file following a failed instantiate. * Since we don't have an inode, we can't use any of the above. */ diff --git a/fs/super.c b/fs/super.c index b32b1fc6cd5..f1d87333105 100644 --- a/fs/super.c +++ b/fs/super.c @@ -76,7 +76,7 @@ LIST_HEAD(super_blocks); * Once the reference is obtained we can drop the spinlock. */ -static struct file_system_type *file_systems = NULL; +static struct file_system_type *file_systems; static rwlock_t file_systems_lock = RW_LOCK_UNLOCKED; /* WARNING: This can be used only if we _already_ own a reference */ @@ -315,6 +315,7 @@ static struct vfsmount *add_vfsmnt(struct super_block *sb, strcpy(name, dir_name); mnt->mnt_dirname = name; } + mnt->mnt_owner = current->uid; if (parent) list_add(&mnt->mnt_child, &parent->mnt_mounts); @@ -1020,10 +1021,6 @@ asmlinkage long sys_umount(char * name, int flags) struct nameidata nd; char *kname; int retval; - struct super_block *sb; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; lock_kernel(); kname = getname(name); @@ -1036,10 +1033,14 @@ asmlinkage long sys_umount(char * name, int flags) putname(kname); if (retval) goto out; - sb = nd.dentry->d_inode->i_sb; retval = -EINVAL; if (nd.dentry!=nd.mnt->mnt_root) goto dput_and_out; + + retval = -EPERM; + if (!capable(CAP_SYS_ADMIN) && current->uid!=nd.mnt->mnt_owner) + goto dput_and_out; + dput(nd.dentry); /* puts nd.mnt */ down(&mount_sem); @@ -1062,6 +1063,21 @@ asmlinkage long sys_oldumount(char * name) return sys_umount(name,0); } +static int mount_is_safe(struct nameidata *nd) +{ + if (capable(CAP_SYS_ADMIN)) + return 0; + if (S_ISLNK(nd->dentry->d_inode->i_mode)) + return -EPERM; + if (nd->dentry->d_inode->i_mode & S_ISVTX) { + if (current->uid != nd->dentry->d_inode->i_uid) + return -EPERM; + } + if (permission(nd->dentry->d_inode, MAY_WRITE)) + return -EPERM; + return 0; +} + /* * do loopback mount. */ @@ -1071,18 +1087,22 @@ static int do_loopback(char *old_name, char *new_name) int err = 0; if (!old_name || !*old_name) return -EINVAL; - if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &old_nd)) + if (path_init(old_name, LOOKUP_POSITIVE, &old_nd)) err = path_walk(old_name, &old_nd); if (err) goto out; - if (path_init(new_name, LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &new_nd)) + if (path_init(new_name, LOOKUP_POSITIVE, &new_nd)) err = path_walk(new_name, &new_nd); if (err) goto out1; - err = -EPERM; - if (!capable(CAP_SYS_ADMIN) && - current->uid != new_nd.dentry->d_inode->i_uid) + err = mount_is_safe(&new_nd); + if (err) + goto out2; + err = -EINVAL; + if (S_ISDIR(new_nd.dentry->d_inode->i_mode) != + S_ISDIR(old_nd.dentry->d_inode->i_mode)) goto out2; + down(&mount_sem); err = -ENOENT; if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry)) @@ -1143,31 +1163,29 @@ static int do_remount(const char *dir,int flags,char *data) return retval; } -static int copy_mount_options (const void * data, unsigned long *where) +static int copy_mount_options (const void *data, unsigned long *where) { int i; unsigned long page; - struct vm_area_struct * vma; *where = 0; if (!data) return 0; - vma = find_vma(current->mm, (unsigned long) data); - if (!vma || (unsigned long) data < vma->vm_start) - return -EFAULT; - if (!(vma->vm_flags & VM_READ)) - return -EFAULT; - i = vma->vm_end - (unsigned long) data; - if (PAGE_SIZE <= (unsigned long) i) - i = PAGE_SIZE-1; - if (!(page = __get_free_page(GFP_KERNEL))) { + if (!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; - } - if (copy_from_user((void *) page,data,i)) { + + /* We only care that *some* data at the address the user + * gave us is valid. Just in case, we'll zero + * the remainder of the page. + */ + i = copy_from_user((void *)page, data, PAGE_SIZE); + if (i == PAGE_SIZE) { free_page(page); return -EFAULT; } + if (i) + memset((char *)page + PAGE_SIZE - i, 0, i); *where = page; return 0; } @@ -1186,7 +1204,7 @@ static int copy_mount_options (const void * data, unsigned long *where) * aren't used, as the syscall assumes we are talking to an older * version that didn't understand them. */ -long do_sys_mount(char * dev_name, char * dir_name, char *type_page, +long do_mount(char * dev_name, char * dir_name, char *type_page, unsigned long new_flags, void *data_page) { struct file_system_type * fstype; @@ -1279,26 +1297,24 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, unsigned long new_flags, void * data) { int retval; - unsigned long data_page = 0; - unsigned long type_page = 0; - unsigned long dev_page = 0; + unsigned long data_page; + unsigned long type_page; + unsigned long dev_page; char *dir_page; - lock_kernel(); retval = copy_mount_options (type, &type_page); if (retval < 0) - goto out; + return retval; /* copy_mount_options allows a NULL user pointer, * and just returns zero in that case. But if we * allow the type to be NULL we will crash. * Previously we did not check this case. */ - if (type_page == 0) { - retval = -EINVAL; - goto out; - } + if (type_page == 0) + return -EINVAL; + lock_kernel(); dir_page = getname(dir_name); retval = PTR_ERR(dir_page); if (IS_ERR(dir_page)) @@ -1309,7 +1325,7 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto out2; retval = copy_mount_options (data, &data_page); if (retval >= 0) { - retval = do_sys_mount((char*)dev_page,dir_page,(char*)type_page, + retval = do_mount((char*)dev_page,dir_page,(char*)type_page, new_flags, (void*)data_page); free_page(data_page); } @@ -1318,7 +1334,6 @@ out2: putname(dir_page); out1: free_page(type_page); -out: unlock_kernel(); return retval; } @@ -1493,10 +1508,6 @@ static void chroot_fs_refs(struct dentry *old_root, { struct task_struct *p; - /* We can't afford dput() blocking under the tasklist_lock */ - mntget(old_rootmnt); - dget(old_root); - read_lock(&tasklist_lock); for_each_task(p) { if (!p->fs) continue; @@ -1506,9 +1517,6 @@ static void chroot_fs_refs(struct dentry *old_root, set_fs_pwd(p->fs, new_rootmnt, new_root); } read_unlock(&tasklist_lock); - - dput(old_root); - mntput(old_rootmnt); } /* @@ -1525,8 +1533,8 @@ static void chroot_fs_refs(struct dentry *old_root, asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) { - struct dentry *root = current->fs->root; - struct vfsmount *root_mnt = current->fs->rootmnt; + struct dentry *root; + struct vfsmount *root_mnt; struct vfsmount *tmp; struct nameidata new_nd, old_nd; char *name; @@ -1559,6 +1567,8 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) if (error) goto out1; + root_mnt = mntget(current->fs->rootmnt); + root = dget(current->fs->root); down(&mount_sem); error = -ENOENT; if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry)) @@ -1597,6 +1607,8 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) error = 0; out2: up(&mount_sem); + dput(root); + mntput(root_mnt); path_release(&old_nd); out1: path_release(&new_nd); diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 0abffaac6e1..ef1e0438130 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -398,7 +398,6 @@ static int sysv_rmdir(struct inode * dir, struct dentry * dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); mark_inode_dirty(dir); - d_delete(dentry); retval = 0; end_rmdir: brelse(bh); @@ -429,7 +428,6 @@ static int sysv_unlink(struct inode * dir, struct dentry * dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); retval = 0; end_unlink: brelse(bh); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index dcd980030cb..d56ff9a0ccf 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -346,7 +346,7 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, sb = dir->i_sb; - if (dentry->d_name.len) + if (dentry) { if ( !(udf_char_to_ustr(&unifilename, dentry->d_name.name, dentry->d_name.len)) ) { @@ -447,20 +447,17 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, } } - if (!lfi) + if (!lfi || !dentry) continue; - if ((flen = udf_get_filename(nameptr, fname, lfi))) - { - if (udf_match(flen, fname, &(dentry->d_name))) - { - if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); - udf_release_data(bh); - *err = -EEXIST; - return NULL; - } + if ((flen = udf_get_filename(nameptr, fname, lfi)) && + udf_match(flen, fname, &(dentry->d_name))) { + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + udf_release_data(bh); + *err = -EEXIST; + return NULL; } } } @@ -691,7 +688,6 @@ static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) struct udf_fileident_bh fibh; int err; struct FileIdentDesc cfi, *fi; - struct dentry parent; err = -EMLINK; if (dir->i_nlink >= (256<i_nlink))-1) @@ -704,10 +700,8 @@ static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; - parent.d_name.len = 0; - parent.d_name.name = NULL; inode->i_size = 0; - if (!(fi = udf_add_entry(inode, &parent, &fibh, &cfi, &err))) + if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err))) { inode->i_nlink--; mark_inode_dirty(inode); @@ -852,7 +846,6 @@ static int udf_rmdir(struct inode * dir, struct dentry * dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; UDF_I_UCTIME(inode) = UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; mark_inode_dirty(dir); - d_delete(dentry); end_rmdir: if (fibh.sbh != fibh.ebh) @@ -902,7 +895,6 @@ static int udf_unlink(struct inode * dir, struct dentry * dentry) mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; - d_delete(dentry); /* This also frees the inode */ end_unlink: if (fibh.sbh != fibh.ebh) diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index c60fcbcdbca..2bd998cf30d 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -673,7 +673,6 @@ static int ufs_rmdir (struct inode * dir, struct dentry *dentry) dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); - d_delete(dentry); end_rmdir: brelse (bh); @@ -730,7 +729,6 @@ static int ufs_unlink(struct inode * dir, struct dentry *dentry) mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; - d_delete(dentry); /* This also frees the inode */ end_unlink: brelse (bh); diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile index f1b7b3ed415..c14c1b61547 100644 --- a/fs/umsdos/Makefile +++ b/fs/umsdos/Makefile @@ -8,7 +8,7 @@ # Note 2: the CFLAGS definitions are now in the main makefile. O_TARGET := umsdos.o -O_OBJS := dir.o inode.o ioctl.o mangle.o namei.o rdir.o emd.o check.o +O_OBJS := dir.o inode.o ioctl.o mangle.o namei.o rdir.o emd.o M_OBJS := $(O_TARGET) diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c deleted file mode 100644 index 58755dd2c15..00000000000 --- a/fs/umsdos/check.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * linux/fs/umsdos/check.c - * - * Sanity-checking code - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef CHECK_PAGE_TABLES -static int check_one_table (struct pde *page_dir) -{ - if (pgd_none (*page_dir)) - return 0; - if (pgd_bad (*page_dir)) - return 1; - return 0; -} - -/* - * This function checks all page tables of "current" - */ -void check_page_tables (void) -{ - struct pgd *pg_dir; - static int err = 0; - - int stack_level = (long) (&pg_dir) - current->kernel_stack_page; - - if (stack_level < 1500) - printk ("** %d ** ", stack_level); - pg_dir = PAGE_DIR_OFFSET (current, 0); - if (err == 0) { - int i; - - for (i = 0; i < PTRS_PER_PAGE; i++, page_dir++) { - int notok = check_one_table (page_dir); - - if (notok) { - err++; - printk ("|%d:%08lx| ", i, page_dir->pgd); - } - } - if (err) - printk ("\nError MM %d\n", err); - } -} -#endif - - -#if UMS_DEBUG -/* - * check for wait queue in 2.3.x - */ -inline void uq_log (char *txt, struct inode *inode) -{ - printk (KERN_ERR "%s: (%lu) magic=%lu creator=%lu lock=%u\n", txt, inode->i_ino, inode->u.umsdos_i.dir_info.p.__magic, inode->u.umsdos_i.dir_info.p.__creator, inode->u.umsdos_i.dir_info.p.lock.lock); -} - -/* - * check a superblock - */ - -void check_sb (struct super_block *sb, const char c) -{ - if (sb) { - printk (" (has %c_sb=%d, %d)", - c, MAJOR (sb->s_dev), MINOR (sb->s_dev)); - } else { - printk (" (%c_sb is NULL)", c); - } -} - -/* - * check an inode - */ - -void check_inode (struct inode *inode) -{ - if (inode) { - printk (KERN_DEBUG "* inode is %lu (i_count=%d)", - inode->i_ino, inode->i_count); - check_sb (inode->i_sb, 'i'); - - if (inode->i_dentry.next) { /* FIXME: does this work ? */ - printk (" (has i_dentry)"); - } else { - printk (" (NO i_dentry)"); - } - - printk (" (i_patched=%d)", inode->u.umsdos_i.i_patched); - - } else { - printk (KERN_DEBUG "* inode is NULL\n"); - } -} - -/* - * checks all inode->i_dentry - * - */ -void checkd_inode (struct inode *inode) -{ - struct dentry *ret; - struct list_head *cur; - int count = 0; - if (!inode) { - printk (KERN_ERR "checkd_inode: inode is NULL!\n"); - return; - } - - printk (KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino); - cur = inode->i_dentry.next; - while (count++ < 10) { - PRINTK (("1...")); - if (!cur) { - printk (KERN_ERR "checkd_inode: *** NULL reached. exit.\n"); - return; - } - PRINTK (("2...")); - ret = list_entry (cur, struct dentry, d_alias); - PRINTK (("3...")); - if (cur == cur->next) { - printk (KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n"); - return; - } - PRINTK (("4...")); - if (!ret) { - printk (KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n"); - return; - } - PRINTK (("5... (ret=%p)...", ret)); - PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name))); - PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len)); - PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name)); - printk (KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name); - PRINTK (("6...")); - cur = cur->next; - PRINTK (("7...")); -#if 1 - printk (KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"); - return; -#endif - } - printk (KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"); - return; -} - -/* - * internal part of check_dentry. does the real job. - * - */ - -void check_dent_int (struct dentry *dentry, int parent) -{ - if (parent) { - printk (KERN_DEBUG "* parent(%d) dentry: %.*s\n", - parent, (int) dentry->d_name.len, dentry->d_name.name); - } else { - printk (KERN_DEBUG "* checking dentry: %.*s\n", - (int) dentry->d_name.len, dentry->d_name.name); - } - check_inode (dentry->d_inode); - printk (KERN_DEBUG "* d_count=%d", dentry->d_count); - check_sb (dentry->d_sb, 'd'); - if (dentry->d_op == NULL) { - printk (" (d_op is NULL)\n"); - } else { - printk (" (d_op is UNKNOWN: %p)\n", dentry->d_op); - } -} - -/* - * checks dentry with full traceback to root and prints info. Limited to 10 recursive depths to avoid infinite loops. - * - */ - -void check_dentry_path (struct dentry *dentry, const char *desc) -{ - int count=0; - printk (KERN_DEBUG "*** check_dentry_path: %.60s\n", desc); - - if (!dentry) { - printk (KERN_DEBUG "*** checking dentry... it is NULL !\n"); - return; - } - if (IS_ERR(dentry)) { - printk (KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n", - PTR_ERR(dentry)); - return; - } - - while (dentry && count < 10) { - check_dent_int (dentry, count++); - if (IS_ROOT(dentry)) { - printk (KERN_DEBUG "*** end checking dentry (root reached ok)\n"); - break; - } - dentry = dentry->d_parent; - } - - if (count >= 10) { /* if infinite loop detected */ - printk (KERN_ERR - "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"); - } - - if (!dentry) { - printk (KERN_ERR - "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n"); - } -} -#else -inline void uq_log (char *txt, struct inode *inode) {}; -void check_sb (struct super_block *sb, const char c) {}; -void check_inode (struct inode *inode) {}; -void checkd_inode (struct inode *inode) {}; -void check_dentry_path (struct dentry *dentry, const char *desc) {}; -#endif /* UMS_DEBUG */ - diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 29eebb3f2d8..9d37f24bc4f 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -36,12 +36,14 @@ static int umsdos_dentry_validate(struct dentry *dentry, int flags) } /* for now, drop everything to force lookups ... */ -static void umsdos_dentry_dput(struct dentry *dentry) +/* ITYM s/everything/& positive/... */ +static int umsdos_dentry_dput(struct dentry *dentry) { struct inode *inode = dentry->d_inode; if (inode) { - d_drop(dentry); + return 1; } + return 0; } struct dentry_operations umsdos_dentry_operations = diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 2e172e80beb..af69877d9c5 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -367,14 +367,6 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data, sb->s_root = new_root; printk(KERN_INFO "UMSDOS: changed to alternate root\n"); } - - /* if d_count is not 1, mount will fail with -EBUSY! */ - if (sb->s_root->d_count > 1) { - shrink_dcache_sb(sb); - if (sb->s_root->d_count > 1) { - printk(KERN_ERR "UMSDOS: root count %d > 1 !", sb->s_root->d_count); - } - } return sb; out_fail: diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index 2390433183b..a8adf6ed877 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -331,6 +331,8 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); ret = -EISDIR; if (!S_ISDIR(temp->d_inode->i_mode)) ret = msdos_unlink (dir, temp); + if (!ret) + d_delete(temp); } dput (temp); goto out; @@ -355,6 +357,8 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); ret = -ENOTDIR; if (S_ISDIR(temp->d_inode->i_mode)) ret = msdos_rmdir (dir, temp); + if (!ret) + d_delete(temp); } dput (temp); goto out; diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 75715116f2f..d3fe5eb6188 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -524,6 +524,7 @@ out_error: out_unlink: printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n"); UMSDOS_unlink (dir, dentry); + d_drop(dentry); goto out; } @@ -898,9 +899,11 @@ if (err) printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n", demd->d_parent->d_name.name, demd->d_name.name, err); #endif - dput(demd); - if (!err) + if (!err) { + d_delete(demd); ret = 0; + } + dput(demd); } } else if (empty == 2) ret = 0; @@ -921,6 +924,7 @@ demd->d_parent->d_name.name, demd->d_name.name, err); if (ret && ret != -ENOENT) goto out_dput; + d_delete(temp); /* OK so far ... remove the name from the EMD */ ret = umsdos_delentry (dentry->d_parent, &info, 1); #ifdef UMSDOS_PARANOIA @@ -1009,6 +1013,8 @@ Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname)); } ret = msdos_unlink(dir, temp); + if (!ret) + d_delete(temp); #ifdef UMSDOS_PARANOIA if (ret) printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n", @@ -1018,8 +1024,6 @@ temp->d_parent->d_name.name, temp->d_name.name, ret); /* dput() temp if we didn't do it above */ out_dput: dput(temp); - if (!ret) - d_delete (dentry); out_unlock: umsdos_unlockcreate (dir); @@ -1065,7 +1069,8 @@ link->d_parent->d_name.name, link->d_name.name, ret)); printk(KERN_WARNING "umsdos_unlink: link removal failed, ret=%d\n", ret); - } + } else + d_delete(link); } else { struct iattr newattrs; inode->i_nlink--; @@ -1100,11 +1105,13 @@ int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry, * If the target already exists, delete it first. */ if (new_dentry->d_inode) { - new_dentry->d_count++; + dget(new_dentry); if (S_ISDIR(old_dentry->d_inode->i_mode)) ret = UMSDOS_rmdir (new_dir, new_dentry); else ret = UMSDOS_unlink (new_dir, new_dentry); + if (!ret) + d_drop(new_dentry); dput(new_dentry); if (ret) return ret; diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 8c71dd7270e..a477ade2cfc 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -174,6 +174,8 @@ static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry) ret = 0; if (demd->d_inode) ret = msdos_unlink (dentry->d_inode, demd); + if (!ret) + d_delete(demd); dput(demd); } } diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index ceb67870df3..0439d63fc06 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -961,24 +961,6 @@ static int vfat_find(struct inode *dir,struct qstr* qname, return res ? res : -ENOENT; } -/* Find a hashed dentry for inode; NULL if there are none */ -static struct dentry *find_alias(struct inode *inode) -{ - struct list_head *head, *next, *tmp; - struct dentry *alias; - - head = &inode->i_dentry; - next = inode->i_dentry.next; - while (next != head) { - tmp = next; - next = tmp->next; - alias = list_entry(tmp, struct dentry, d_alias); - if (!d_unhashed(alias)) - return dget(alias); - } - return NULL; -} - struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry) { int res; @@ -1005,7 +987,7 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry) fat_brelse(dir->i_sb, bh); if (res) return ERR_PTR(res); - alias = find_alias(inode); + alias = d_find_alias(inode); if (alias) { if (d_invalidate(alias)==0) dput(alias); @@ -1116,7 +1098,6 @@ int vfat_unlink(struct inode *dir, struct dentry* dentry) mark_inode_dirty(dentry->d_inode); /* releases bh */ vfat_remove_entry(dir,&sinfo,bh,de); - d_delete(dentry); return res; } diff --git a/include/asm-alpha/ide.h b/include/asm-alpha/ide.h index ecb69609978..f142fc6dc73 100644 --- a/include/asm-alpha/ide.h +++ b/include/asm-alpha/ide.h @@ -74,7 +74,8 @@ static __inline__ void ide_init_default_hwifs(void) int index; for (index = 0; index < MAX_HWIFS; index++) { - ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, 0); + memset(&hw, 0, sizeof(hw_regs_t)); + ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); hw.irq = ide_default_irq(ide_default_io_base(index)); ide_register_hw(&hw, NULL); } diff --git a/include/asm-arm/arch-ebsa285/irq.h b/include/asm-arm/arch-ebsa285/irq.h index 9dcfa9480e8..134729f34af 100644 --- a/include/asm-arm/arch-ebsa285/irq.h +++ b/include/asm-arm/arch-ebsa285/irq.h @@ -44,19 +44,8 @@ static int isa_irq = -1; static inline int fixup_irq(unsigned int irq) { #ifdef CONFIG_HOST_FOOTBRIDGE - if (irq == isa_irq) { + if (irq == isa_irq) irq = *(unsigned char *)PCIIACK_BASE; - - /* - * The NetWinder appears to randomly give wrong interrupt - * numbers from time to time. When it does, map them to - * the unused IRQ 13 - */ - if (irq >= NR_IRQS) { - printk(KERN_ERR "Strange interrupt %d?\n", irq); - irq = _ISA_IRQ(13); - } - } #endif return irq; diff --git a/include/asm-arm/arch-l7200/dma.h b/include/asm-arm/arch-l7200/dma.h new file mode 100644 index 00000000000..be68279f722 --- /dev/null +++ b/include/asm-arm/arch-l7200/dma.h @@ -0,0 +1,23 @@ +#ifndef __ASM_ARCH_DMA_H +#define __ASM_ARCH_DMA_H + +/* + * This is the maximum DMA address that can be DMAd to. + * There should not be more than (0xd0000000 - 0xc0000000) + * bytes of RAM. + */ +#define MAX_DMA_ADDRESS 0xd0000000 +#define MAX_DMA_CHANNELS 8 + +#define DMA_0 0 +#define DMA_1 1 +#define DMA_2 2 +#define DMA_3 3 +#define DMA_S0 4 +#define DMA_S1 5 +#define DMA_VIRTUAL_FLOPPY 6 +#define DMA_VIRTUAL_SOUND 7 + +#define DMA_FLOPPY DMA_VIRTUAL_FLOPPY + +#endif diff --git a/include/asm-arm/arch-l7200/hardware.h b/include/asm-arm/arch-l7200/hardware.h new file mode 100644 index 00000000000..d800cbc8d09 --- /dev/null +++ b/include/asm-arm/arch-l7200/hardware.h @@ -0,0 +1,49 @@ +/* + * linux/include/asm-arm/arch-l7200/hardware.h + * + * Copyright (C) 2000 Rob Scott (rscott@mtrob.fdns.net) + * Steve Hill (sjhill@cotw.com) + * + * This file contains the hardware definitions for the + * LinkUp Systems L7200 SOC development board. + * + * Changelog: + * 02-01-2000 RS Created L7200 version, derived from rpc code + * 03-21-2000 SJH Cleaned up file + * 04-21-2000 RS Changed mapping of I/O in virtual space + * 04-25-2000 SJH Removed unused symbols and such + * 05-05-2000 SJH Complete rewrite + */ +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +/* Hardware addresses of major areas. + * *_START is the physical address + * *_SIZE is the size of the region + * *_BASE is the virtual address + */ +#define RAM_START 0xf0000000 +#define RAM_SIZE 0x02000000 +#define RAM_BASE 0xc0000000 + +#define IO_START 0x80000000 /* I/O */ +#define IO_SIZE 0x01000000 +#define IO_BASE 0xd0000000 + +#define IO_START_2 0x90000000 /* I/O */ +#define IO_SIZE_2 0x01000000 +#define IO_BASE_2 0xd1000000 + +#define ISA_START 0x20000000 /* ISA */ +#define ISA_SIZE 0x20000000 +#define ISA_BASE 0xe0000000 + +#define FLUSH_BASE_PHYS 0x40000000 /* ROM */ +#define FLUSH_BASE 0xdf000000 + +#define PARAMS_BASE (PAGE_OFFSET + 0x0100) +#define Z_PARAMS_BASE (RAM_START + PARAMS_OFFSET) + +#define PCIO_BASE IO_BASE + +#endif diff --git a/include/asm-arm/arch-l7200/ide.h b/include/asm-arm/arch-l7200/ide.h new file mode 100644 index 00000000000..0cfcf3aacb8 --- /dev/null +++ b/include/asm-arm/arch-l7200/ide.h @@ -0,0 +1,27 @@ +/* + * linux/include/asm-arm/arch-l7200/ide.h + * + * Copyright (c) 2000 Steve Hill (sjhill@cotw.com) + * + * Changelog: + * 29-03-2000 SJH Created file placeholder + */ +#include + +/* + * Set up a hw structure for a specified data port, control port and IRQ. + * This should follow whatever the default interface uses. + */ +static __inline__ void +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +{ +} + +/* + * This registers the standard ports for this architecture with the IDE + * driver. + */ +static __inline__ void +ide_init_default_hwifs(void) +{ +} diff --git a/include/asm-arm/arch-l7200/io.h b/include/asm-arm/arch-l7200/io.h new file mode 100644 index 00000000000..787b621089f --- /dev/null +++ b/include/asm-arm/arch-l7200/io.h @@ -0,0 +1,210 @@ +/* + * linux/include/asm-arm/arch-l7200/io.h + * + * Copyright (C) 2000 Steven Hill (sjhill@cotw.com) + * + * Modifications: + * 21-03-2000 SJH Created from linux/include/asm-arm/arch-nexuspci/io.h + */ +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#include + +#define IO_SPACE_LIMIT 0xffffffff + +/* + * We use two different types of addressing - PC style addresses, and ARM + * addresses. PC style accesses the PC hardware with the normal PC IO + * addresses, eg 0x3f8 for serial#1. ARM addresses are 0x80000000+ + * and are translated to the start of IO. Note that all addresses are + * shifted left! + */ +#define __PORT_PCIO(x) (!((x) & 0x80000000)) + +/* + * Dynamic IO functions. + */ + +extern __inline__ void __outb (unsigned int value, unsigned int port) +{ + unsigned long temp; + __asm__ __volatile__( + "tst %2, #0x80000000\n\t" + "mov %0, %4\n\t" + "addeq %0, %0, %3\n\t" + "strb %1, [%0, %2, lsl #2] @ outb" + : "=&r" (temp) + : "r" (value), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE) + : "cc"); +} + +extern __inline__ void __outw (unsigned int value, unsigned int port) +{ + unsigned long temp; + __asm__ __volatile__( + "tst %2, #0x80000000\n\t" + "mov %0, %4\n\t" + "addeq %0, %0, %3\n\t" + "str %1, [%0, %2, lsl #2] @ outw" + : "=&r" (temp) + : "r" (value|value<<16), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE) + : "cc"); +} + +extern __inline__ void __outl (unsigned int value, unsigned int port) +{ + unsigned long temp; + __asm__ __volatile__( + "tst %2, #0x80000000\n\t" + "mov %0, %4\n\t" + "addeq %0, %0, %3\n\t" + "str %1, [%0, %2, lsl #2] @ outl" + : "=&r" (temp) + : "r" (value), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE) + : "cc"); +} + +#define DECLARE_DYN_IN(sz,fnsuffix,instr) \ +extern __inline__ unsigned sz __in##fnsuffix (unsigned int port) \ +{ \ + unsigned long temp, value; \ + __asm__ __volatile__( \ + "tst %2, #0x80000000\n\t" \ + "mov %0, %4\n\t" \ + "addeq %0, %0, %3\n\t" \ + "ldr" ##instr## " %1, [%0, %2, lsl #2] @ in"###fnsuffix \ + : "=&r" (temp), "=r" (value) \ + : "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE) \ + : "cc"); \ + return (unsigned sz)value; \ +} + +extern __inline__ unsigned int __ioaddr (unsigned int port) \ +{ \ + if (__PORT_PCIO(port)) \ + return (unsigned int)(PCIO_BASE + (port << 2)); \ + else \ + return (unsigned int)(IO_BASE + (port << 2)); \ +} + +#define DECLARE_IO(sz,fnsuffix,instr) \ + DECLARE_DYN_IN(sz,fnsuffix,instr) + +DECLARE_IO(char,b,"b") +DECLARE_IO(short,w,"") +DECLARE_IO(int,l,"") + +#undef DECLARE_IO +#undef DECLARE_DYN_IN + +/* + * Constant address IO functions + * + * These have to be macros for the 'J' constraint to work - + * +/-4096 immediate operand. + */ +#define __outbc(value,port) \ +({ \ + if (__PORT_PCIO((port))) \ + __asm__ __volatile__( \ + "strb %0, [%1, %2] @ outbc" \ + : : "r" (value), "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + else \ + __asm__ __volatile__( \ + "strb %0, [%1, %2] @ outbc" \ + : : "r" (value), "r" (IO_BASE), "r" ((port) << 2)); \ +}) + +#define __inbc(port) \ +({ \ + unsigned char result; \ + if (__PORT_PCIO((port))) \ + __asm__ __volatile__( \ + "ldrb %0, [%1, %2] @ inbc" \ + : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + else \ + __asm__ __volatile__( \ + "ldrb %0, [%1, %2] @ inbc" \ + : "=r" (result) : "r" (IO_BASE), "r" ((port) << 2)); \ + result; \ +}) + +#define __outwc(value,port) \ +({ \ + unsigned long v = value; \ + if (__PORT_PCIO((port))) \ + __asm__ __volatile__( \ + "str %0, [%1, %2] @ outwc" \ + : : "r" (v|v<<16), "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + else \ + __asm__ __volatile__( \ + "str %0, [%1, %2] @ outwc" \ + : : "r" (v|v<<16), "r" (IO_BASE), "r" ((port) << 2)); \ +}) + +#define __inwc(port) \ +({ \ + unsigned short result; \ + if (__PORT_PCIO((port))) \ + __asm__ __volatile__( \ + "ldr %0, [%1, %2] @ inwc" \ + : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + else \ + __asm__ __volatile__( \ + "ldr %0, [%1, %2] @ inwc" \ + : "=r" (result) : "r" (IO_BASE), "r" ((port) << 2)); \ + result & 0xffff; \ +}) + +#define __outlc(value,port) \ +({ \ + unsigned long v = value; \ + if (__PORT_PCIO((port))) \ + __asm__ __volatile__( \ + "str %0, [%1, %2] @ outlc" \ + : : "r" (v), "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + else \ + __asm__ __volatile__( \ + "str %0, [%1, %2] @ outlc" \ + : : "r" (v), "r" (IO_BASE), "r" ((port) << 2)); \ +}) + +#define __inlc(port) \ +({ \ + unsigned long result; \ + if (__PORT_PCIO((port))) \ + __asm__ __volatile__( \ + "ldr %0, [%1, %2] @ inlc" \ + : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + else \ + __asm__ __volatile__( \ + "ldr %0, [%1, %2] @ inlc" \ + : "=r" (result) : "r" (IO_BASE), "r" ((port) << 2)); \ + result; \ +}) + +#define __ioaddrc(port) \ + (__PORT_PCIO((port)) ? PCIO_BASE + ((port) << 2) : IO_BASE + ((port) << 2)) + +#define inb(p) (__builtin_constant_p((p)) ? __inbc(p) : __inb(p)) +#define inw(p) (__builtin_constant_p((p)) ? __inwc(p) : __inw(p)) +#define inl(p) (__builtin_constant_p((p)) ? __inlc(p) : __inl(p)) +#define outb(v,p) (__builtin_constant_p((p)) ? __outbc(v,p) : __outb(v,p)) +#define outw(v,p) (__builtin_constant_p((p)) ? __outwc(v,p) : __outw(v,p)) +#define outl(v,p) (__builtin_constant_p((p)) ? __outlc(v,p) : __outl(v,p)) +#define __ioaddr(p) (__builtin_constant_p((p)) ? __ioaddr(p) : __ioaddrc(p)) + +/* + * Translated address IO functions + * + * IO address has already been translated to a virtual address + */ +#define outb_t(v,p) (*(volatile unsigned char *)(p) = (v)) +#define inb_t(p) (*(volatile unsigned char *)(p)) +#define outw_t(v,p) (*(volatile unsigned int *)(p) = (v)) +#define inw_t(p) (*(volatile unsigned int *)(p)) +#define outl_t(v,p) (*(volatile unsigned long *)(p) = (v)) +#define inl_t(p) (*(volatile unsigned long *)(p)) + +#endif diff --git a/include/asm-arm/arch-l7200/irq.h b/include/asm-arm/arch-l7200/irq.h new file mode 100644 index 00000000000..58b61664fdd --- /dev/null +++ b/include/asm-arm/arch-l7200/irq.h @@ -0,0 +1,66 @@ +/* + * include/asm-arm/arch-l7200/irq.h + * + * Copyright (C) 2000 Rob Scott (rscott@mtrob.fdns.ne + * Steve Hill (sjhill@cotw.com) + * + * Changelog: + * 01-02-2000 RS Created l7200 version, derived from ebsa110 code + * 04-15-2000 RS Made dependent on hardware.h + * 05-05-2000 SJH Complete rewrite + */ + +/* + * IRQ base register + */ +#define IRQ_BASE (IO_BASE_2 + 0x1000) + +/* + * Normal IRQ registers + */ +#define IRQ_STATUS (*(volatile unsigned long *) (IRQ_BASE + 0x000)) +#define IRQ_RAWSTATUS (*(volatile unsigned long *) (IRQ_BASE + 0x004)) +#define IRQ_ENABLE (*(volatile unsigned long *) (IRQ_BASE + 0x008)) +#define IRQ_ENABLECLEAR (*(volatile unsigned long *) (IRQ_BASE + 0x00c)) +#define IRQ_SOFT (*(volatile unsigned long *) (IRQ_BASE + 0x010)) +#define IRQ_SOURCESEL (*(volatile unsigned long *) (IRQ_BASE + 0x018)) + +/* + * Fast IRQ registers + */ +#define FIQ_STATUS (*(volatile unsigned long *) (IRQ_BASE + 0x100)) +#define FIQ_RAWSTATUS (*(volatile unsigned long *) (IRQ_BASE + 0x104)) +#define FIQ_ENABLE (*(volatile unsigned long *) (IRQ_BASE + 0x108)) +#define FIQ_ENABLECLEAR (*(volatile unsigned long *) (IRQ_BASE + 0x10c)) +#define FIQ_SOFT (*(volatile unsigned long *) (IRQ_BASE + 0x110)) +#define FIQ_SOURCESEL (*(volatile unsigned long *) (IRQ_BASE + 0x118)) + +#define fixup_irq(x) (x) + +static void l7200_mask_irq(unsigned int irq) +{ + IRQ_ENABLECLEAR = 1 << irq; +} + +static void l7200_unmask_irq(unsigned int irq) +{ + IRQ_ENABLE = 1 << irq; +} + +static __inline__ void irq_init_irq(void) +{ + int irq; + + IRQ_ENABLECLEAR = 0xffffffff; /* clear all interrupt enables */ + FIQ_ENABLECLEAR = 0xffffffff; /* clear all fast interrupt enables */ + + for (irq = 0; irq < NR_IRQS; irq++) { + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + irq_desc[irq].mask_ack = l7200_mask_irq; + irq_desc[irq].mask = l7200_mask_irq; + irq_desc[irq].unmask = l7200_unmask_irq; + } + + init_FIQ(); +} diff --git a/include/asm-arm/arch-l7200/irqs.h b/include/asm-arm/arch-l7200/irqs.h new file mode 100644 index 00000000000..175efa1bd32 --- /dev/null +++ b/include/asm-arm/arch-l7200/irqs.h @@ -0,0 +1,45 @@ +/* + * include/asm-arm/arch-l7200/irqs.h + * + * Copyright (C) 2000 Rob Scott (rscott@mtrob.fdns.net) + * Steve Hill (sjhill@cotw.com) + * + * Changelog: + * 01-02-2000 RS Create l7200 version + * 03-28-2000 SJH Removed unused interrupt + */ + +#define NR_IRQS 32 + +#define IRQ_STWDOG 0 /* Watchdog timer */ +#define IRQ_PROG 1 /* Programmable interrupt */ +#define IRQ_DEBUG_RX 2 /* Comm Rx debug */ +#define IRQ_DEBUG_TX 3 /* Comm Tx debug */ +#define IRQ_GCTC1 4 /* Timer 1 */ +#define IRQ_GCTC2 5 /* Timer 2 */ +#define IRQ_DMA 6 /* DMA controller */ +#define IRQ_CLCD 7 /* Color LCD controller */ +#define IRQ_SM_RX 8 /* Smart card */ +#define IRQ_SM_TX 9 /* Smart cart */ +#define IRQ_SM_RST 10 /* Smart card */ +#define IRQ_SIB 11 /* Serial Interface Bus */ +#define IRQ_MMC 12 /* MultiMediaCard */ +#define IRQ_SSP1 13 /* Synchronous Serial Port 1 */ +#define IRQ_SSP2 14 /* Synchronous Serial Port 1 */ +#define IRQ_SPI 15 /* SPI slave */ +#define IRQ_UART_1 16 /* UART 1 */ +#define IRQ_UART_2 17 /* UART 2 */ +#define IRQ_IRDA 18 /* IRDA */ +#define IRQ_RTC_TICK 19 /* Real Time Clock tick */ +#define IRQ_RTC_ALARM 20 /* Real Time Clock alarm */ +#define IRQ_GPIO 21 /* General Purpose IO */ +#define IRQ_GPIO_DMA 22 /* General Purpose IO, DMA */ +#define IRQ_M2M 23 /* Memory to memory DMA */ +#define IRQ_RESERVED 24 /* RESERVED, don't use */ +#define IRQ_INTF 25 /* External active low interrupt */ +#define IRQ_INT0 26 /* External active low interrupt */ +#define IRQ_INT1 27 /* External active low interrupt */ +#define IRQ_INT2 28 /* External active low interrupt */ +#define IRQ_INT3 29 /* External active low interrupt */ +#define IRQ_BAT_LO 30 /* Low batery or external power */ +#define IRQ_MEDIA_CHG 31 /* Media change interrupt */ diff --git a/include/asm-arm/arch-l7200/memory.h b/include/asm-arm/arch-l7200/memory.h new file mode 100644 index 00000000000..f2aaabd5c56 --- /dev/null +++ b/include/asm-arm/arch-l7200/memory.h @@ -0,0 +1,44 @@ +/* + * linux/include/asm-arm/arch-l7200/memory.h + * + * Copyright (c) 2000 Steven Hill (sjhill@cotw.com) + * Copyright (c) 2000 Rob Scott (rscott@mtrob.fdns.net) + * + * Changelog: + * 03-13-2000 SJH Created + * 04-13-2000 RS Changed bus macros for new addr + * 05-03-2000 SJH Removed bus macros and fixed virt_to_phys macro + */ +#ifndef __ASM_ARCH_MMU_H +#define __ASM_ARCH_MMU_H + +/* + * Task size: 3GB + */ +#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE_26 (0x04000000UL) + +/* + * Page offset: 3GB + */ +#define PAGE_OFFSET (0xc0000000UL) + +/* + * Physical DRAM offset on the L7200 SDB. + */ +#define PHYS_OFFSET (0xf0000000UL) + +/* + * The DRAM is contiguous. + */ +#define __virt_to_phys__is_a_macro +#define __virt_to_phys(vpage) ((vpage) - PAGE_OFFSET + PHYS_OFFSET) +#define __phys_to_virt__is_a_macro +#define __phys_to_virt(ppage) ((ppage) + PAGE_OFFSET - PHYS_OFFSET) + +#define __virt_to_bus__is_a_macro +#define __virt_to_bus(x) __virt_to_phys(x) +#define __bus_to_virt__is_a_macro +#define __bus_to_virt(x) __phys_to_virt(x) + +#endif diff --git a/include/asm-arm/arch-l7200/param.h b/include/asm-arm/arch-l7200/param.h new file mode 100644 index 00000000000..5cd0bcc7826 --- /dev/null +++ b/include/asm-arm/arch-l7200/param.h @@ -0,0 +1,23 @@ +/* + * linux/include/asm-arm/arch-l7200/param.h + * + * Copyright (C) 2000 Rob Scott (rscott@mtrob.fdns.net) + * Steve Hill (sjhill@cotw.com) + * + * This file contains the hardware definitions for the + * LinkUp Systems L7200 SOC development board. + * + * Changelog: + * 04-21-2000 RS Created L7200 version + * 04-25-2000 SJH Cleaned up file + * 05-03-2000 SJH Change comments and rate + */ +#ifndef __ASM_ARCH_PARAM_H +#define __ASM_ARCH_PARAM_H + +/* + * See 'time.h' for how the RTC HZ rate is set + */ +#define HZ 128 + +#endif diff --git a/include/asm-arm/arch-l7200/processor.h b/include/asm-arm/arch-l7200/processor.h new file mode 100644 index 00000000000..ee4b4b2ca05 --- /dev/null +++ b/include/asm-arm/arch-l7200/processor.h @@ -0,0 +1,27 @@ +/* + * linux/include/asm-arm/arch-l7200/processor.h + * + * Copyright (c) 2000 Steven Hill (sjhill@cotw.com) + * + * Changelog: + * 03-21-2000 SJH Created + * 05-03-2000 SJH Comment cleaning + */ + +#ifndef __ASM_ARCH_PROCESSOR_H +#define __ASM_ARCH_PROCESSOR_H + +/* + * Bus types + */ +#define EISA_bus 0 +#define EISA_bus__is_a_macro /* for versions in ksyms.c */ +#define MCA_bus 0 +#define MCA_bus__is_a_macro /* for versions in ksyms.c */ + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) + +#endif diff --git a/include/asm-arm/arch-l7200/serial_l7200.h b/include/asm-arm/arch-l7200/serial_l7200.h new file mode 100644 index 00000000000..238c595d97e --- /dev/null +++ b/include/asm-arm/arch-l7200/serial_l7200.h @@ -0,0 +1,101 @@ +/* + * linux/include/asm-arm/arch-l7200/serial_l7200.h + * + * Copyright (c) 2000 Steven Hill (sjhill@cotw.com) + * + * Changelog: + * 05-09-2000 SJH Created + */ +#ifndef __ASM_ARCH_SERIAL_L7200_H +#define __ASM_ARCH_SERIAL_L7200_H + +#include + +/* + * This assumes you have a 3.6864 MHz clock for your UART. + */ +#define BASE_BAUD 3686400 + +/* + * UART base register addresses + */ +#define UART1_BASE (IO_BASE + 0x00044000) +#define UART2_BASE (IO_BASE + 0x00045000) + +/* + * UART register offsets + */ +#define UARTDR 0x00 /* Tx/Rx data */ +#define RXSTAT 0x04 /* Rx status */ +#define H_UBRLCR 0x08 /* mode register high */ +#define M_UBRLCR 0x0C /* mode reg mid (MSB of buad)*/ +#define L_UBRLCR 0x10 /* mode reg low (LSB of baud)*/ +#define UARTCON 0x14 /* control register */ +#define UARTFLG 0x18 /* flag register */ +#define UARTINTSTAT 0x1C /* FIFO IRQ status register */ +#define UARTINTMASK 0x20 /* FIFO IRQ mask register */ + +/* + * UART baud rate register values + */ +#define BR_110 0x827 +#define BR_1200 0x06e +#define BR_2400 0x05f +#define BR_4800 0x02f +#define BR_9600 0x017 +#define BR_14400 0x00f +#define BR_19200 0x00b +#define BR_38400 0x005 +#define BR_57600 0x003 +#define BR_76800 0x002 +#define BR_115200 0x001 + +/* + * Receiver status register (RXSTAT) mask values + */ +#define RXSTAT_NO_ERR 0x00 /* No error */ +#define RXSTAT_FRM_ERR 0x01 /* Framing error */ +#define RXSTAT_PAR_ERR 0x02 /* Parity error */ +#define RXSTAT_OVR_ERR 0x04 /* Overrun error */ + +/* + * High byte of UART bit rate and line control register (H_UBRLCR) values + */ +#define UBRLCR_BRK 0x01 /* generate break on tx */ +#define UBRLCR_PEN 0x02 /* enable parity */ +#define UBRLCR_PDIS 0x00 /* disable parity */ +#define UBRLCR_EVEN 0x04 /* 1= even parity,0 = odd parity */ +#define UBRLCR_STP2 0x08 /* transmit 2 stop bits */ +#define UBRLCR_FIFO 0x10 /* enable FIFO */ +#define UBRLCR_LEN5 0x60 /* word length5 */ +#define UBRLCR_LEN6 0x40 /* word length6 */ +#define UBRLCR_LEN7 0x20 /* word length7 */ +#define UBRLCR_LEN8 0x00 /* word length8 */ + +/* + * UART control register (UARTCON) values + */ +#define UARTCON_UARTEN 0x01 /* Enable UART */ +#define UARTCON_DMAONERR 0x08 /* Mask RxDmaRq when errors occur */ + +/* + * UART flag register (UARTFLG) mask values + */ +#define UARTFLG_UTXFF 0x20 /* Transmit FIFO full */ +#define UARTFLG_URXFE 0x10 /* Receiver FIFO empty */ +#define UARTFLG_UBUSY 0x08 /* Transmitter busy */ +#define UARTFLG_DCD 0x04 /* Data carrier detect */ +#define UARTFLG_DSR 0x02 /* Data set ready */ +#define UARTFLG_CTS 0x01 /* Clear to send */ + +/* + * UART interrupt status/clear registers (UARTINTSTAT/CLR) values + */ +#define UART_TXINT 0x01 /* TX interrupt */ +#define UART_RXINT 0x02 /* RX interrupt */ +#define UART_RXERRINT 0x04 /* RX error interrupt */ +#define UART_MSINT 0x08 /* Modem Status interrupt */ +#define UART_UDINT 0x10 /* UART Disabled interrupt */ +#define UART_ALLIRQS 0x1f /* All interrupts */ + +#endif diff --git a/include/asm-arm/arch-l7200/system.h b/include/asm-arm/arch-l7200/system.h new file mode 100644 index 00000000000..c3bbe37733a --- /dev/null +++ b/include/asm-arm/arch-l7200/system.h @@ -0,0 +1,30 @@ +/* + * linux/include/asm-arm/arch-l7200/system.h + * + * Copyright (c) 2000 Steven Hill (sjhill@cotw.com) + * + * Changelog + * 03-21-2000 SJH Created + * 04-26-2000 SJH Fixed functions + * 05-03-2000 SJH Removed usage of obsolete 'iomd.h' + */ +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +extern __inline__ void arch_idle(void) +{ + while (!current->need_resched && !hlt_counter) + { }; +/* outb(0, IOMD_SUSMODE);*/ +} + +#define arch_power_off() do { } while (0) + +extern inline void arch_reset(char mode) +{ + if (mode == 's') { + cpu_reset(0); + } +} + +#endif diff --git a/include/asm-arm/arch-l7200/time.h b/include/asm-arm/arch-l7200/time.h new file mode 100644 index 00000000000..077735e218d --- /dev/null +++ b/include/asm-arm/arch-l7200/time.h @@ -0,0 +1,68 @@ +/* + * linux/include/asm-arm/arch-l7200/time.h + * + * Copyright (C) 2000 Rob Scott (rscott@mtrob.fdns.net) + * Steve Hill (sjhill@cotw.com) + * + * Changelog: + * 01-02-2000 RS Created l7200 version, derived from rpc code + * 05-03-2000 SJH Complete rewrite + */ +#ifndef _ASM_ARCH_TIME_H +#define _ASM_ARCH_TIME_H + +#include + +/* + * RTC base register address + */ +#define RTC_BASE (IO_BASE_2 + 0x2000) + +/* + * RTC registers + */ +#define RTC_RTCDR (*(volatile unsigned char *) (RTC_BASE + 0x000)) +#define RTC_RTCMR (*(volatile unsigned char *) (RTC_BASE + 0x004)) +#define RTC_RTCS (*(volatile unsigned char *) (RTC_BASE + 0x008)) +#define RTC_RTCC (*(volatile unsigned char *) (RTC_BASE + 0x008)) +#define RTC_RTCDV (*(volatile unsigned char *) (RTC_BASE + 0x00c)) +#define RTC_RTCCR (*(volatile unsigned char *) (RTC_BASE + 0x010)) + +/* + * RTCCR register values + */ +#define RTC_RATE_32 0x00 /* 32 Hz tick */ +#define RTC_RATE_64 0x10 /* 64 Hz tick */ +#define RTC_RATE_128 0x20 /* 128 Hz tick */ +#define RTC_RATE_256 0x30 /* 256 Hz tick */ +#define RTC_EN_ALARM 0x01 /* Enable alarm */ +#define RTC_EN_TIC 0x04 /* Enable counter */ +#define RTC_EN_STWDOG 0x08 /* Enable watchdog */ + +/* + * Handler for timer interrupt + */ +static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + do_timer(regs); + do_profile(regs); + RTC_RTCC = 0; /* Clear interrupt */ +} + +/* + * Set up timer interrupt, and return the current time in seconds. + */ +extern __inline__ void setup_timer(void) +{ + xtime.tv_sec = RTC_RTCDR; + + RTC_RTCC = 0; /* Clear interrupt */ + + timer_irq.handler = timer_interrupt; + + setup_arm_irq(IRQ_RTC_TICK, &timer_irq); + + RTC_RTCCR = RTC_RATE_128 | RTC_EN_TIC; /* Set rate and enable timer */ +} + +#endif diff --git a/include/asm-arm/arch-l7200/timex.h b/include/asm-arm/arch-l7200/timex.h new file mode 100644 index 00000000000..3c3202620f0 --- /dev/null +++ b/include/asm-arm/arch-l7200/timex.h @@ -0,0 +1,20 @@ +/* + * linux/include/asm-arm/arch-l7200/timex.h + * + * Copyright (C) 2000 Rob Scott (rscott@mtrob.fdns.net) + * Steve Hill (sjhill@cotw.com) + * + * 04-21-2000 RS Created file + * 05-03-2000 SJH Tick rate was wrong + * + */ + +/* + * On the ARM720T, clock ticks are set to 128 Hz. + * + * NOTE: The actual RTC value is set in 'time.h' which + * must be changed when choosing a different tick + * rate. The value of HZ in 'param.h' must also + * be changed to match below. + */ +#define CLOCK_TICK_RATE 128 diff --git a/include/asm-arm/arch-l7200/uncompress.h b/include/asm-arm/arch-l7200/uncompress.h new file mode 100644 index 00000000000..d2e56455b86 --- /dev/null +++ b/include/asm-arm/arch-l7200/uncompress.h @@ -0,0 +1,19 @@ +/* + * linux/include/asm-arm/arch-l7200/uncompress.h + * + * Copyright (C) 2000 Steve Hill (sjhill@cotw.com) + */ + +static __inline__ void putc(char c) +{ +} + +static void puts(const char *s) +{ +} + +static __inline__ void arch_decomp_setup(void) +{ +} + +#define arch_decomp_wdog() diff --git a/include/asm-arm/arch-l7200/vmalloc.h b/include/asm-arm/arch-l7200/vmalloc.h new file mode 100644 index 00000000000..04fa07e7cbf --- /dev/null +++ b/include/asm-arm/arch-l7200/vmalloc.h @@ -0,0 +1,16 @@ +/* + * linux/include/asm-arm/arch-l7200/vmalloc.h + */ + +/* + * Just any arbitrary offset to the start of the vmalloc VM area: the + * current 8MB value just means that there will be a 8MB "hole" after the + * physical memory until the kernel virtual memory starts. That means that + * any out-of-bounds memory accesses will hopefully be caught. + * The vmalloc() routines leaves a hole of 4kB between each vmalloced + * area for the same reason. ;) + */ +#define VMALLOC_OFFSET (8*1024*1024) +#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) +#define VMALLOC_VMADDR(x) ((unsigned long)(x)) +#define VMALLOC_END (PAGE_OFFSET + 0x10000000) diff --git a/include/asm-arm/hardirq.h b/include/asm-arm/hardirq.h index 77a36a2a480..c12ed91e31f 100644 --- a/include/asm-arm/hardirq.h +++ b/include/asm-arm/hardirq.h @@ -5,23 +5,27 @@ #include extern unsigned int local_irq_count[NR_CPUS]; +extern unsigned int local_bh_count[NR_CPUS]; + +#define local_irq_count(cpu) (local_irq_count[(cpu)]) +#define local_bh_count(cpu) (local_bh_count[(cpu)]) /* * Are we in an interrupt context? Either doing bottom half * or hardware interrupt processing? */ #define in_interrupt() ({ const int __cpu = smp_processor_id(); \ - (local_irq_count[__cpu] + local_bh_count[__cpu] != 0); }) + (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }) -#define in_irq() (local_irq_count[smp_processor_id()] != 0) +#define in_irq() (local_irq_count(smp_processor_id()) != 0) #ifndef CONFIG_SMP -#define hardirq_trylock(cpu) (local_irq_count[cpu] == 0) +#define hardirq_trylock(cpu) (local_irq_count(cpu) == 0) #define hardirq_endlock(cpu) do { } while (0) -#define hardirq_enter(cpu) (local_irq_count[cpu]++) -#define hardirq_exit(cpu) (local_irq_count[cpu]--) +#define irq_enter(cpu,irq) (local_irq_count(cpu)++) +#define irq_exit(cpu,irq) (local_irq_count(cpu)--) #define synchronize_irq() do { } while (0) diff --git a/include/asm-arm/proc-armo/semaphore.h b/include/asm-arm/proc-armo/semaphore.h deleted file mode 100644 index 6926fad126a..00000000000 --- a/include/asm-arm/proc-armo/semaphore.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * linux/include/asm-arm/proc-armo/locks.h - * - * Copyright (C) 2000 Russell King - * - * Interrupt safe locking assembler. - */ -#ifndef __ASM_PROC_LOCKS_H -#define __ASM_PROC_LOCKS_H - -#define __down_op(ptr,fail) \ - ({ \ - __asm__ __volatile__ ( \ - "@ atomic down operation\n" \ -" mov r0, pc\n" \ -" orr lr, r0, #0x08000000\n" \ -" teqp lr, #0\n" \ -" ldr lr, [%0]\n" \ -" and r0, r0, #0x0c000003\n" \ -" subs lr, lr, #1\n" \ -" str lr, [%0]\n" \ -" orrmi r0, r0, #0x80000000 @ set N\n" \ -" teqp r0, #0\n" \ -" movmi r0, %0\n" \ - blmi " SYMBOL_NAME_STR(fail) \ - : \ - : "r" (ptr) \ - : "r0", "lr", "cc"); \ - }) - -#define __down_op_ret(ptr,fail) \ - ({ \ - unsigned int result; \ - __asm__ __volatile__ ( \ -" @ down_op_ret\n" \ -" mov r0, pc\n" \ -" orr lr, r0, #0x08000000\n" \ -" teqp lr, #0\n" \ -" ldr lr, [%1]\m" \ -" and r0, r0, #0x0c000003\n" \ -" subs lr, lr, #1\n" \ -" str lr, [%1]\n" \ -" orrmi r0, r0, #0x80000000 @ set N\n" \ -" teqp r0, #0\n" \ -" movmi r0, %1\n" \ -" movpl r0, #0\n" \ -" blmi " SYMBOL_NAME_STR(fail) "\n" \ -" mov %0, r0" \ - : "=&r" (result) \ - : "r" (ptr) \ - : "r0", "lr", "cc"); \ - result; \ - }) - -#define __up_op(ptr,wake) \ - ({ \ - __asm__ __volatile__ ( \ - "@ up_op\n" \ - mov r0, pc\n" \ - orr lr, r0, #0x08000000\n" \ - teqp lr, #0\n" \ - ldr lr, [%0]\n" \ - and r0, r0, #0x0c000003\n" \ - adds lr, lr, #1\n" \ - str lr, [%0]\n" \ - orrle r0, r0, #0x80000000 @ set N\n" \ - teqp r0, #0\n" \ - movmi r0, %0\n" \ - blmi " SYMBOL_NAME_STR(wake) \ - : \ - : "r" (ptr) \ - : "r0", "lr", "cc"); \ - }) - -#endif diff --git a/include/asm-arm/proc-armv/locks.h b/include/asm-arm/proc-armv/locks.h index c1cfded3e4d..0a0391faee7 100644 --- a/include/asm-arm/proc-armv/locks.h +++ b/include/asm-arm/proc-armv/locks.h @@ -12,18 +12,18 @@ ({ \ __asm__ __volatile__( \ "@ down_op\n" \ -" mrs r0, cpsr\n" \ -" orr lr, r0, #128\n" \ +" mrs ip, cpsr\n" \ +" orr lr, ip, #128\n" \ " msr cpsr_c, lr\n" \ " ldr lr, [%0]\n" \ " subs lr, lr, %1\n" \ " str lr, [%0]\n" \ -" msr cpsr_c, r0\n" \ -" movmi r0, %0\n" \ +" msr cpsr_c, ip\n" \ +" movmi ip, %0\n" \ " blmi " SYMBOL_NAME_STR(fail) \ : \ : "r" (ptr), "I" (1) \ - : "r0", "lr", "cc"); \ + : "ip", "lr", "cc"); \ }) #define __down_op_ret(ptr,fail) \ @@ -31,20 +31,20 @@ unsigned int ret; \ __asm__ __volatile__( \ "@ down_op_ret\n" \ -" mrs r0, cpsr\n" \ -" orr lr, r0, #128\n" \ +" mrs ip, cpsr\n" \ +" orr lr, ip, #128\n" \ " msr cpsr_c, lr\n" \ " ldr lr, [%1]\n" \ " subs lr, lr, %2\n" \ " str lr, [%1]\n" \ -" msr cpsr_c, r0\n" \ -" movmi r0, %1\n" \ -" movpl r0, #0\n" \ +" msr cpsr_c, ip\n" \ +" movmi ip, %1\n" \ +" movpl ip, #0\n" \ " blmi " SYMBOL_NAME_STR(fail) "\n" \ -" mov %0, r0" \ +" mov %0, ip" \ : "=&r" (ret) \ : "r" (ptr), "I" (1) \ - : "r0", "lr", "cc"); \ + : "ip", "lr", "cc"); \ ret; \ }) @@ -52,18 +52,18 @@ ({ \ __asm__ __volatile__( \ "@ up_op\n" \ -" mrs r0, cpsr\n" \ -" orr lr, r0, #128\n" \ +" mrs ip, cpsr\n" \ +" orr lr, ip, #128\n" \ " msr cpsr_c, lr\n" \ " ldr lr, [%0]\n" \ " adds lr, lr, %1\n" \ " str lr, [%0]\n" \ -" msr cpsr_c, r0\n" \ -" movle r0, %0\n" \ +" msr cpsr_c, ip\n" \ +" movle ip, %0\n" \ " blle " SYMBOL_NAME_STR(wake) \ : \ : "r" (ptr), "I" (1) \ - : "r0", "lr", "cc"); \ + : "ip", "lr", "cc"); \ }) /* @@ -78,36 +78,36 @@ ({ \ __asm__ __volatile__( \ "@ down_op_write\n" \ -" mrs r0, cpsr\n" \ -" orr lr, r0, #128\n" \ +" mrs ip, cpsr\n" \ +" orr lr, ip, #128\n" \ " msr cpsr_c, lr\n" \ " ldr lr, [%0]\n" \ " subs lr, lr, %1\n" \ " str lr, [%0]\n" \ -" msr cpsr_c, r0\n" \ -" movne r0, %0\n" \ +" msr cpsr_c, ip\n" \ +" movne ip, %0\n" \ " blne " SYMBOL_NAME_STR(fail) \ : \ : "r" (ptr), "I" (RW_LOCK_BIAS) \ - : "r0", "lr", "cc"); \ + : "ip", "lr", "cc"); \ }) #define __up_op_write(ptr,wake) \ ({ \ __asm__ __volatile__( \ "@ up_op_read\n" \ -" mrs r0, cpsr\n" \ -" orr lr, r0, #128\n" \ +" mrs ip, cpsr\n" \ +" orr lr, ip, #128\n" \ " msr cpsr_c, lr\n" \ " ldr lr, [%0]\n" \ " adds lr, lr, %1\n" \ " str lr, [%0]\n" \ -" msr cpsr_c, r0\n" \ -" movcs r0, %0\n" \ +" msr cpsr_c, ip\n" \ +" movcs ip, %0\n" \ " blcs " SYMBOL_NAME_STR(wake) \ : \ : "r" (ptr), "I" (RW_LOCK_BIAS) \ - : "r0", "lr", "cc"); \ + : "ip", "lr", "cc"); \ }) #define __down_op_read(ptr,fail) \ @@ -117,18 +117,18 @@ ({ \ __asm__ __volatile__( \ "@ up_op_read\n" \ -" mrs r0, cpsr\n" \ -" orr lr, r0, #128\n" \ +" mrs ip, cpsr\n" \ +" orr lr, ip, #128\n" \ " msr cpsr_c, lr\n" \ " ldr lr, [%0]\n" \ " adds lr, lr, %1\n" \ " str lr, [%0]\n" \ -" msr cpsr_c, r0\n" \ -" moveq r0, %0\n" \ +" msr cpsr_c, ip\n" \ +" moveq ip, %0\n" \ " bleq " SYMBOL_NAME_STR(wake) \ : \ : "r" (ptr), "I" (1) \ - : "r0", "lr", "cc"); \ + : "ip", "lr", "cc"); \ }) #endif diff --git a/include/asm-arm/softirq.h b/include/asm-arm/softirq.h index f9875481313..01e0d73d106 100644 --- a/include/asm-arm/softirq.h +++ b/include/asm-arm/softirq.h @@ -4,14 +4,12 @@ #include #include -extern unsigned int local_bh_count[NR_CPUS]; - -#define cpu_bh_disable(cpu) do { local_bh_count[(cpu)]++; barrier(); } while (0) -#define cpu_bh_enable(cpu) do { barrier(); local_bh_count[(cpu)]--; } while (0) +#define cpu_bh_disable(cpu) do { local_bh_count(cpu)++; barrier(); } while (0) +#define cpu_bh_enable(cpu) do { barrier(); local_bh_count(cpu)--; } while (0) #define local_bh_disable() cpu_bh_disable(smp_processor_id()) #define local_bh_enable() cpu_bh_enable(smp_processor_id()) -#define in_softirq() (local_bh_count[smp_processor_id()] != 0) +#define in_softirq() (local_bh_count(smp_processor_id()) != 0) #endif /* __ASM_SOFTIRQ_H */ diff --git a/include/asm-arm/string.h b/include/asm-arm/string.h index dfe4cd9aca1..2a8ab162412 100644 --- a/include/asm-arm/string.h +++ b/include/asm-arm/string.h @@ -13,12 +13,17 @@ extern char * strrchr(const char * s, int c); extern char * strchr(const char * s, int c); #define __HAVE_ARCH_MEMCPY +extern void * memcpy(void *, const void *, __kernel_size_t); + #define __HAVE_ARCH_MEMMOVE +extern void * memmove(void *, const void *, __kernel_size_t); + #define __HAVE_ARCH_MEMCHR -extern void * memchr(const void *cs, int c, size_t count); +extern void * memchr(const void *, int, __kernel_size_t); #define __HAVE_ARCH_MEMZERO #define __HAVE_ARCH_MEMSET +extern void * memset(void *, int, __kernel_size_t); extern void __memzero(void *ptr, __kernel_size_t n); @@ -36,4 +41,3 @@ extern void __memzero(void *ptr, __kernel_size_t n); #define memzero(p,n) ({ if ((n) != 0) __memzero((p),(n)); (p); }) #endif - diff --git a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h index ca6da3e1320..9755af1ab0e 100644 --- a/include/asm-arm/unistd.h +++ b/include/asm-arm/unistd.h @@ -65,7 +65,7 @@ #define __NR_mpx (__NR_SYSCALL_BASE+ 56) #define __NR_setpgid (__NR_SYSCALL_BASE+ 57) #define __NR_ulimit (__NR_SYSCALL_BASE+ 58) -#define __NR_oldolduname (__NR_SYSCALL_BASE+ 59) + #define __NR_umask (__NR_SYSCALL_BASE+ 60) #define __NR_chroot (__NR_SYSCALL_BASE+ 61) #define __NR_ustat (__NR_SYSCALL_BASE+ 62) @@ -115,7 +115,7 @@ #define __NR_stat (__NR_SYSCALL_BASE+106) #define __NR_lstat (__NR_SYSCALL_BASE+107) #define __NR_fstat (__NR_SYSCALL_BASE+108) -#define __NR_olduname (__NR_SYSCALL_BASE+109) + #define __NR_iopl (__NR_SYSCALL_BASE+110) #define __NR_vhangup (__NR_SYSCALL_BASE+111) #define __NR_idle (__NR_SYSCALL_BASE+112) diff --git a/include/asm-i386/apicdef.h b/include/asm-i386/apicdef.h index 2f0e2d3c30e..d8a21095235 100644 --- a/include/asm-i386/apicdef.h +++ b/include/asm-i386/apicdef.h @@ -7,7 +7,8 @@ * Alan Cox , 1995. * Ingo Molnar , 1999, 2000 */ -#define APIC_PHYS_BASE 0xfee00000 /* IA s/w dev Vol 3, Section 7.4 */ + +#define APIC_DEFAULT_PHYS_BASE 0xfee00000 #define APIC_ID 0x20 #define GET_APIC_ID(x) (((x)>>24)&0x0F) diff --git a/include/asm-i386/hw_irq.h b/include/asm-i386/hw_irq.h index 413a98c5590..0c883af9d6e 100644 --- a/include/asm-i386/hw_irq.h +++ b/include/asm-i386/hw_irq.h @@ -28,36 +28,34 @@ */ /* - * Special IRQ vectors used by the SMP architecture, 0x30-0x4f + * Special IRQ vectors used by the SMP architecture, 0xf0-0xff * * some of the following vectors are 'rare', they are merged * into a single vector (CALL_FUNCTION_VECTOR) to save vector space. * TLB, reschedule and local APIC vectors are performance-critical. + * + * Vectors 0xf0-0xfa are free (reserved for future Linux use). */ -#define INVALIDATE_TLB_VECTOR 0x30 -#define LOCAL_TIMER_VECTOR 0x31 -#define RESCHEDULE_VECTOR 0x40 - -/* 'rare' vectors: */ -#define CALL_FUNCTION_VECTOR 0x41 +#define SPURIOUS_APIC_VECTOR 0xff +#define ERROR_APIC_VECTOR 0xfe +#define INVALIDATE_TLB_VECTOR 0xfd +#define RESCHEDULE_VECTOR 0xfc +#define CALL_FUNCTION_VECTOR 0xfb /* - * These IRQs should never really happen on perfect hardware running - * a perfect kernel, but we nevertheless print a message to catch the - * rest ;) Subtle, the APIC architecture mandates the spurious vector - * to have bits 0-3 set to 1. Note that these vectors do not occur - * normally, so we violate the 'only 2 vectors per priority level' - * rule here. + * Local APIC timer IRQ vector is on a different priority level, + * to work around the 'lost local interrupt if more than 2 IRQ + * sources per level' errata. */ -#define SPURIOUS_APIC_VECTOR 0x3f -#define ERROR_APIC_VECTOR 0x43 +#define LOCAL_TIMER_VECTOR 0xef /* - * First APIC vector available to drivers: (vectors 0x51-0xfe) - * we start at 0x51 to spread out vectors between priority levels - * evenly. (note that 0x80 is the syscall vector) + * First APIC vector available to drivers: (vectors 0x30-0xee) + * we start at 0x31 to spread out vectors evenly between priority + * levels. (0x80 is the syscall vector) */ -#define IRQ0_TRAP_VECTOR 0x51 +#define FIRST_DEVICE_VECTOR 0x31 +#define FIRST_SYSTEM_VECTOR 0xef extern int irq_vector[NR_IRQS]; #define IO_APIC_VECTOR(irq) irq_vector[irq] diff --git a/include/asm-i386/ide.h b/include/asm-i386/ide.h index 886eebb91d8..25ec3b59429 100644 --- a/include/asm-i386/ide.h +++ b/include/asm-i386/ide.h @@ -78,6 +78,7 @@ static __inline__ void ide_init_default_hwifs(void) int index; for(index = 0; index < MAX_HWIFS; index++) { + memset(&hw, 0, sizeof(hw_regs_t)); ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); hw.irq = ide_default_irq(ide_default_io_base(index)); ide_register_hw(&hw, NULL); diff --git a/include/asm-i386/uaccess.h b/include/asm-i386/uaccess.h index 2e3081a5c36..3f3bc50f406 100644 --- a/include/asm-i386/uaccess.h +++ b/include/asm-i386/uaccess.h @@ -135,16 +135,8 @@ extern void __put_user_bad(void); :"0" (ptr),"d" (x) \ :"cx") -#define put_user(x,ptr) \ -({ int __ret_pu; \ - switch(sizeof (*(ptr))) { \ - case 1: __put_user_x(1,__ret_pu,(__typeof__(*(ptr)))(x),ptr); break; \ - case 2: __put_user_x(2,__ret_pu,(__typeof__(*(ptr)))(x),ptr); break; \ - case 4: __put_user_x(4,__ret_pu,(__typeof__(*(ptr)))(x),ptr); break; \ - default: __put_user_x(X,__ret_pu,x,ptr); break; \ - } \ - __ret_pu; \ -}) +#define put_user(x,ptr) \ + __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) #define __get_user(x,ptr) \ __get_user_nocheck((x),(ptr),sizeof(*(ptr))) @@ -158,6 +150,16 @@ extern void __put_user_bad(void); __pu_err; \ }) + +#define __put_user_check(x,ptr,size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) *__pu_addr = (ptr); \ + if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ + __put_user_size((x),__pu_addr,(size),__pu_err); \ + __pu_err; \ +}) + #define __put_user_size(x,ptr,size,retval) \ do { \ retval = 0; \ diff --git a/include/asm-ia64/ide.h b/include/asm-ia64/ide.h index 4ea79986180..1b473c25299 100644 --- a/include/asm-ia64/ide.h +++ b/include/asm-ia64/ide.h @@ -84,6 +84,7 @@ ide_init_default_hwifs (void) int index; for(index = 0; index < MAX_HWIFS; index++) { + memset(&hw, 0, sizeof(hw_regs_t)); ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); hw.irq = ide_default_irq(ide_default_io_base(index)); ide_register_hw(&hw, NULL); diff --git a/include/asm-ppc/bitops.h b/include/asm-ppc/bitops.h index d5506a3480d..f62ad151f29 100644 --- a/include/asm-ppc/bitops.h +++ b/include/asm-ppc/bitops.h @@ -7,7 +7,6 @@ #define _PPC_BITOPS_H #include -#include #include extern void set_bit(int nr, volatile void *addr); @@ -278,3 +277,4 @@ found_middle: #define minix_find_first_zero_bit(addr,size) ext2_find_first_zero_bit(addr,size) #endif /* _PPC_BITOPS_H */ + diff --git a/include/asm-ppc/cpm_8260.h b/include/asm-ppc/cpm_8260.h index c479f87d202..5f500d5c333 100644 --- a/include/asm-ppc/cpm_8260.h +++ b/include/asm-ppc/cpm_8260.h @@ -124,6 +124,14 @@ typedef struct cpm_buf_desc { #define BD_SC_OV ((ushort)0x0002) /* Overrun */ #define BD_SC_CD ((ushort)0x0001) /* ?? */ +/* Function code bits, usually generic to devices. +*/ +#define CPMFCR_GBL ((u_char)0x20) /* Set memory snooping */ +#define CPMFCR_EB ((u_char)0x10) /* Set big endian byte order */ +#define CPMFCR_TC2 ((u_char)0x04) /* Transfer code 2 value */ +#define CPMFCR_DTB ((u_char)0x02) /* Use local bus for data when set */ +#define CPMFCR_BDB ((u_char)0x01) /* Use local bus for BD when set */ + /* Parameter RAM offsets from the base. */ #define PROFF_SCC1 ((uint)0x8000) @@ -184,13 +192,6 @@ typedef struct smc_uart { uint smc_stmp; /* SDMA Temp */ } smc_uart_t; -/* Function code bits. -*/ -#define SMC_GBL ((u_char)0x20) /* Set memory snooping */ -#define SMC_EB ((u_char)0x10) /* Set big endian byte order */ -#define SMC_TC2 ((u_char)0x04) /* Transfer code 2 value */ -#define SMC_DTB ((u_char)0x02) /* Use local bus when set */ - /* SMC uart mode register (Internal memory map). */ #define SMCMR_REN ((ushort)0x0001) @@ -337,10 +338,6 @@ typedef struct scc_param { uint scc_tcrc; /* Internal */ } sccp_t; -/* Function code bits. -*/ -#define SCC_EB ((u_char)0x10) /* Set big endian byte order */ - /* CPM Ethernet through SCC1. */ typedef struct scc_enet { diff --git a/include/asm-ppc/init.h b/include/asm-ppc/init.h index 106e57c1af5..a25f6015e9e 100644 --- a/include/asm-ppc/init.h +++ b/include/asm-ppc/init.h @@ -16,6 +16,12 @@ __argprep __prep; \ __argprep +#define __chrp __attribute__ ((__section__ (".text.chrp"))) +#define __chrpdata __attribute__ ((__section__ (".data.chrp"))) +#define __chrpfunc(__argchrp) \ + __argchrp __chrp; \ + __argchrp + #define __apus __attribute__ ((__section__ (".text.apus"))) #define __apusdata __attribute__ ((__section__ (".data.apus"))) #define __apusfunc(__argapus) \ diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h index e069f8b152b..a4f6b0a4bfb 100644 --- a/include/asm-ppc/page.h +++ b/include/asm-ppc/page.h @@ -1,4 +1,7 @@ #include +#ifndef __ASSEMBLY__ +#include /* for xmon definition */ +#endif /* ndef __ASSEMBLY__ */ #ifndef _PPC_PAGE_H #define _PPC_PAGE_H diff --git a/include/asm-ppc/siginfo.h b/include/asm-ppc/siginfo.h index 58e4b22e972..2561510f615 100644 --- a/include/asm-ppc/siginfo.h +++ b/include/asm-ppc/siginfo.h @@ -122,7 +122,7 @@ typedef struct siginfo { * SIGSEGV si_codes */ #define SEGV_MAPERR 1 /* address not mapped to object */ -#define SRGV_ACCERR 2 /* invalid permissions for mapped object */ +#define SEGV_ACCERR 2 /* invalid permissions for mapped object */ #define NSIGSEGV 2 /* diff --git a/include/asm-ppc/string.h b/include/asm-ppc/string.h index e0158a215ad..d912a6b5fed 100644 --- a/include/asm-ppc/string.h +++ b/include/asm-ppc/string.h @@ -15,5 +15,15 @@ extern int strcasecmp(const char *, const char *); extern int strncasecmp(const char *, const char *, int); +extern char * strcpy(char *,const char *); +extern char * strncpy(char *,const char *, __kernel_size_t); +extern __kernel_size_t strlen(const char *); +extern int strcmp(const char *,const char *); +extern char * strcat(char *, const char *); +extern void * memset(void *,int,__kernel_size_t); +extern void * memcpy(void *,const void *,__kernel_size_t); +extern void * memmove(void *,const void *,__kernel_size_t); +extern int memcmp(const void *,const void *,__kernel_size_t); +extern void * memchr(const void *,int,__kernel_size_t); #endif diff --git a/include/asm-sh/cache.h b/include/asm-sh/cache.h index 17108905d75..df6d5122597 100644 --- a/include/asm-sh/cache.h +++ b/include/asm-sh/cache.h @@ -14,8 +14,4 @@ #define L1_CACHE_BYTES 32 #endif -extern void cache_flush_area(unsigned long start, unsigned long end); -extern void cache_purge_area(unsigned long start, unsigned long end); -extern void cache_wback_area(unsigned long start, unsigned long end); - #endif /* __ASM_SH_CACHE_H */ diff --git a/include/asm-sh/checksum.h b/include/asm-sh/checksum.h index 846e64509a8..6e1a728bff0 100644 --- a/include/asm-sh/checksum.h +++ b/include/asm-sh/checksum.h @@ -72,16 +72,16 @@ unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum); static __inline__ unsigned int csum_fold(unsigned int sum) { unsigned int __dummy; - __asm__("clrt\n\t" - "mov %0, %1\n\t" - "shll16 %0\n\t" - "addc %0, %1\n\t" - "movt %0\n\t" - "shlr16 %1\n\t" - "add %1, %0" + __asm__("swap.w %0, %1\n\t" + "extu.w %0, %0\n\t" + "extu.w %1, %1\n\t" + "add %1, %0\n\t" + "swap.w %0, %1\n\t" + "add %1, %0\n\t" + "not %0, %0\n\t" : "=r" (sum), "=&r" (__dummy) : "0" (sum)); - return ~sum; + return sum; } /* @@ -93,31 +93,26 @@ static __inline__ unsigned int csum_fold(unsigned int sum) */ static __inline__ unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) { - unsigned int sum, __dummy; + unsigned int sum, __dummy0, __dummy1; __asm__ __volatile__( "mov.l @%1+, %0\n\t" - "add #-4, %2\n\t" - "clrt\n\t" "mov.l @%1+, %3\n\t" - "addc %3, %0\n\t" - "mov.l @%1+, %3\n\t" - "addc %3, %0\n\t" - "mov.l @%1+, %3\n\t" - "addc %3, %0\n" + "add #-2, %2\n\t" + "clrt\n\t" "1:\t" - "mov.l @%1+, %3\n\t" "addc %3, %0\n\t" - "movt %3\n\t" + "movt %4\n\t" + "mov.l @%1+, %3\n\t" "dt %2\n\t" "bf/s 1b\n\t" - " cmp/eq #1, %3\n\t" - "mov #0, %3\n\t" + " cmp/eq #1, %4\n\t" "addc %3, %0\n\t" + "addc %2, %0" /* Here %2 is 0, add carry-bit */ /* Since the input registers which are loaded with iph and ihl are modified, we must also specify them as outputs, or gcc will assume they contain their original values. */ - : "=r" (sum), "=r" (iph), "=r" (ihl), "=&z" (__dummy) + : "=r" (sum), "=r" (iph), "=r" (ihl), "=&r" (__dummy0), "=&z" (__dummy1) : "1" (iph), "2" (ihl)); return csum_fold(sum); diff --git a/include/asm-sh/hitachi_se.h b/include/asm-sh/hitachi_se.h new file mode 100644 index 00000000000..282bbce8faf --- /dev/null +++ b/include/asm-sh/hitachi_se.h @@ -0,0 +1,53 @@ +#ifndef __ASM_SH_HITACHI_SE_H +#define __ASM_SH_HITACHI_SE_H + +/* + * linux/include/asm-sh/hitachi_se.h + * + * Copyright (C) 2000 Kazumoto Kojima + * + * Hitachi SolutionEngine support + */ + +/* Box specific addresses. */ + +#define PA_ROM 0x00000000 /* EPROM */ +#define PA_ROM_SIZE 0x00400000 /* EPROM size 4M byte */ +#define PA_FROM 0x01000000 /* EPROM */ +#define PA_FROM_SIZE 0x00400000 /* EPROM size 4M byte */ +#define PA_EXT1 0x04000000 +#define PA_EXT1_SIZE 0x04000000 +#define PA_EXT2 0x08000000 +#define PA_EXT2_SIZE 0x04000000 +#define PA_SDRAM 0x0c000000 +#define PA_SDRAM_SIZE 0x04000000 + +#define PA_EXT4 0x12000000 +#define PA_EXT4_SIZE 0x02000000 +#define PA_EXT5 0x14000000 +#define PA_EXT5_SIZE 0x04000000 +#define PA_PCIC 0x18000000 /* MR-SHPC-01 PCMCIA */ + +#define PA_83902 0xb0000000 /* DP83902A */ +#define PA_83902_IF 0xb0040000 /* DP83902A remote io port */ +#define PA_83902_RST 0xb0080000 /* DP83902A reset port */ + +#define PA_SUPERIO 0xb0400000 /* SMC37C935A super io chip */ +#define PA_DIPSW0 0xb0800000 /* Dip switch 5,6 */ +#define PA_DIPSW1 0xb0800002 /* Dip switch 7,8 */ +#define PA_LED 0xb0c00000 /* LED */ +#define PA_BCR 0xb1400000 /* FPGA */ + +#define PA_MRSHPC 0xb83fffe0 /* MR-SHPC-01 PCMCIA controler */ + +#define BCR_ILCRA (PA_BCR + 0) +#define BCR_ILCRB (PA_BCR + 2) +#define BCR_ILCRC (PA_BCR + 4) +#define BCR_ILCRD (PA_BCR + 6) +#define BCR_ILCRE (PA_BCR + 8) +#define BCR_ILCRF (PA_BCR + 10) +#define BCR_ILCRG (PA_BCR + 12) + +#define IRQ_STNIC 10 + +#endif /* __ASM_SH_HITACHI_SE_H */ diff --git a/include/asm-sh/ide.h b/include/asm-sh/ide.h index 95a3855968b..ec82befa24a 100644 --- a/include/asm-sh/ide.h +++ b/include/asm-sh/ide.h @@ -17,7 +17,7 @@ #include #ifndef MAX_HWIFS -#define MAX_HWIFS 1 /* XXX: For my board -- gniibe */ +#define MAX_HWIFS 1 #endif #define ide__sti() __sti() @@ -25,8 +25,8 @@ static __inline__ int ide_default_irq(ide_ioreg_t base) { switch (base) { - case 0xba0001f0: return 14; - case 0xba000170: return 14; + case 0x01f0: return 14; + case 0x0170: return 15; default: return 0; } @@ -36,9 +36,9 @@ static __inline__ ide_ioreg_t ide_default_io_base(int index) { switch (index) { case 0: - return 0xba0001f0; + return 0x01f0; case 1: - return 0xba000170; + return 0x0170; default: return 0; } @@ -69,6 +69,7 @@ static __inline__ void ide_init_default_hwifs(void) int index; for(index = 0; index < MAX_HWIFS; index++) { + memset(&hw, 0, sizeof(hw_regs_t)); ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); hw.irq = ide_default_irq(ide_default_io_base(index)); ide_register_hw(&hw, NULL); diff --git a/include/asm-sh/io.h b/include/asm-sh/io.h index 5a7b0468bb5..c6de2a42a59 100644 --- a/include/asm-sh/io.h +++ b/include/asm-sh/io.h @@ -14,9 +14,6 @@ #include -#define inb_p inb -#define outb_p outb - #define inw_p inw #define outw_p outw @@ -53,103 +50,62 @@ extern __inline__ void writel(unsigned int b, unsigned long addr) *(volatile unsigned long*)addr = b; } -extern __inline__ unsigned long inb_local(unsigned long addr) -{ - return readb(addr); -} - -extern __inline__ void outb_local(unsigned char b, unsigned long addr) -{ - return writeb(b,addr); -} - -extern __inline__ unsigned long inb(unsigned long addr) -{ - return readb(addr); -} - -extern __inline__ unsigned long inw(unsigned long addr) -{ - return readw(addr); -} - -extern __inline__ unsigned long inl(unsigned long addr) -{ - return readl(addr); -} - -extern __inline__ void insb(unsigned long addr, void *buffer, int count) -{ - unsigned char *buf=buffer; - while(count--) *buf++=inb(addr); -} - -extern __inline__ void insw(unsigned long addr, void *buffer, int count) -{ - unsigned short *buf=buffer; - while(count--) *buf++=inw(addr); -} - -extern __inline__ void insl(unsigned long addr, void *buffer, int count) -{ - unsigned long *buf=buffer; - while(count--) *buf++=inl(addr); -} - -extern __inline__ void outb(unsigned char b, unsigned long addr) -{ - return writeb(b,addr); -} - -extern __inline__ void outw(unsigned short b, unsigned long addr) -{ - return writew(b,addr); -} - -extern __inline__ void outl(unsigned int b, unsigned long addr) -{ - return writel(b,addr); -} +extern unsigned long inb(unsigned int port); +extern unsigned long inb_p(unsigned int port); +extern unsigned long inw(unsigned int port); +extern unsigned long inl(unsigned int port); +extern void insb(unsigned int port, void *addr, unsigned long count); +extern void insw(unsigned int port, void *addr, unsigned long count); +extern void insl(unsigned int port, void *addr, unsigned long count); -extern __inline__ void outsb(unsigned long addr, const void *buffer, int count) -{ - const unsigned char *buf=buffer; - while(count--) outb(*buf++, addr); -} +extern void outb(unsigned long value, unsigned int port); +extern void outb_p(unsigned long value, unsigned int port); +extern void outw(unsigned long value, unsigned int port); +extern void outl(unsigned long value, unsigned int port); +extern void outsb(unsigned int port, const void *addr, unsigned long count); +extern void outsw(unsigned int port, const void *addr, unsigned long count); +extern void outsl(unsigned int port, const void *addr, unsigned long count); -extern __inline__ void outsw(unsigned long addr, const void *buffer, int count) -{ - const unsigned short *buf=buffer; - while(count--) outw(*buf++, addr); -} - -extern __inline__ void outsl(unsigned long addr, const void *buffer, int count) -{ - const unsigned long *buf=buffer; - while(count--) outl(*buf++, addr); -} +/* + * If the platform has PC-like I/O, this function gives us the address + * from the offset. + */ +extern unsigned long sh_isa_slot(unsigned long offset); + +#define isa_readb(a) readb(sh_isa_slot(a)) +#define isa_readw(a) readw(sh_isa_slot(a)) +#define isa_readl(a) readl(sh_isa_slot(a)) +#define isa_writeb(b,a) writeb(b,sh_isa_slot(a)) +#define isa_writew(w,a) writew(w,sh_isa_slot(a)) +#define isa_writel(l,a) writel(l,sh_isa_slot(a)) +#define isa_memset_io(a,b,c) \ + memset((void *)(sh_isa_slot((unsigned long)a)),(b),(c)) +#define isa_memcpy_fromio(a,b,c) \ + memcpy((a),(void *)(sh_isa_slot((unsigned long)(b))),(c)) +#define isa_memcpy_toio(a,b,c) \ + memcpy((void *)(sh_isa_slot((unsigned long)(a))),(b),(c)) #define ctrl_in(addr) *(addr) #define ctrl_out(data,addr) *(addr) = (data) extern __inline__ unsigned long ctrl_inb(unsigned long addr) { - return *(volatile unsigned char*)addr; + return *(volatile unsigned char*)addr; } extern __inline__ unsigned long ctrl_inw(unsigned long addr) { - return *(volatile unsigned short*)addr; + return *(volatile unsigned short*)addr; } extern __inline__ unsigned long ctrl_inl(unsigned long addr) { - return *(volatile unsigned long*)addr; + return *(volatile unsigned long*)addr; } extern __inline__ void ctrl_outb(unsigned char b, unsigned long addr) { - *(volatile unsigned char*)addr = b; + *(volatile unsigned char*)addr = b; } extern __inline__ void ctrl_outw(unsigned short b, unsigned long addr) diff --git a/include/asm-sh/irq.h b/include/asm-sh/irq.h index ab492fa5b15..d013f35fc63 100644 --- a/include/asm-sh/irq.h +++ b/include/asm-sh/irq.h @@ -5,15 +5,31 @@ * * linux/include/asm-sh/irq.h * - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima * */ #include -#define TIMER_IRQ 16 /* Hard-wired */ -#define TIMER_IPR_OFFSET 12 -#define TIMER_PRIORITY 2 +#if defined(__sh3__) +#define INTC_IPRA 0xfffffee2UL +#define INTC_IPRB 0xfffffee4UL +#elif defined(__SH4__) +#define INTC_IPRA 0xffd00004UL +#define INTC_IPRB 0xffd00008UL +#define INTC_IPRC 0xffd0000cUL +#endif + +#define TIMER_IRQ 16 +#define TIMER_IPR_ADDR INTC_IPRA +#define TIMER_IPR_POS 3 +#define TIMER_PRIORITY 2 + +#define RTC_IRQ 22 +#define RTC_IPR_ADDR INTC_IPRA +#define RTC_IPR_POS 0 +#define RTC_PRIORITY TIMER_PRIORITY #if defined(__SH4__) /* @@ -37,8 +53,52 @@ extern void enable_irq(unsigned int); /* * Function for "on chip support modules". */ -extern void set_ipr_data(unsigned int irq, int offset, int priority); -extern void make_onChip_irq(unsigned int irq); +extern void set_ipr_data(unsigned int irq, unsigned int addr, + int pos, int priority); +extern void make_ipr_irq(unsigned int irq); extern void make_imask_irq(unsigned int irq); +#if defined(CONFIG_CPU_SUBTYPE_SH7709) +#define INTC_IRR0 0xa4000004UL +#define INTC_IRR1 0xa4000006UL +#define INTC_IRR2 0xa4000008UL + +#define INTC_ICR0 0xfffffee0UL +#define INTC_ICR1 0xa4000010UL +#define INTC_ICR2 0xa4000012UL +#define INTC_INTER 0xa4000014UL + +#define INTC_IPRC 0xa4000016UL +#define INTC_IPRD 0xa4000018UL +#define INTC_IPRE 0xa400001aUL + +#define IRQ0_IRQ 32 +#define IRQ1_IRQ 33 +#define IRQ2_IRQ 34 +#define IRQ3_IRQ 35 +#define IRQ4_IRQ 36 +#define IRQ5_IRQ 37 + +#define IRQ0_IRP_ADDR INTC_IPRC +#define IRQ1_IRP_ADDR INTC_IPRC +#define IRQ2_IRP_ADDR INTC_IPRC +#define IRQ3_IRP_ADDR INTC_IPRC +#define IRQ4_IRP_ADDR INTC_IPRD +#define IRQ5_IRP_ADDR INTC_IPRD + +#define IRQ0_IRP_POS 0 +#define IRQ1_IRP_POS 1 +#define IRQ2_IRP_POS 2 +#define IRQ3_IRP_POS 3 +#define IRQ4_IRP_POS 0 +#define IRQ5_IRP_POS 1 + +#define IRQ0_PRIORITY 1 +#define IRQ1_PRIORITY 1 +#define IRQ2_PRIORITY 1 +#define IRQ3_PRIORITY 1 +#define IRQ4_PRIORITY 1 +#define IRQ5_PRIORITY 1 +#endif + #endif /* __ASM_SH_IRQ_H */ diff --git a/include/asm-sh/pgtable.h b/include/asm-sh/pgtable.h index ca6fffbca78..f39d88aaa37 100644 --- a/include/asm-sh/pgtable.h +++ b/include/asm-sh/pgtable.h @@ -22,7 +22,10 @@ extern void paging_init(void); * - flush_cache_mm(mm) flushes the specified mm context's cache lines * - flush_cache_page(mm, vmaddr) flushes a single page * - flush_cache_range(mm, start, end) flushes a range of pages + * * - flush_page_to_ram(page) write back kernel page to ram + * - flush_icache_range(start, end) flushes(invalidates) a range for icache + * - flush_icache_page(vma, pg) flushes(invalidates) a page for icache * * Caches are indexed (effectively) by physical address on SH-3, so * we don't need them. @@ -43,8 +46,7 @@ extern void flush_cache_mm(struct mm_struct *mm); extern void flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end); extern void flush_cache_page(struct vm_area_struct *vma, unsigned long addr); -extern void __flush_page_to_ram(unsigned long page_va); -#define flush_page_to_ram(page) __flush_page_to_ram(page_address(page)) +extern void flush_page_to_ram(struct page *page); extern void flush_icache_range(unsigned long start, unsigned long end); extern void flush_icache_page(struct vm_area_struct *vma, struct page *pg); #endif diff --git a/include/asm-sh/smc37c93x.h b/include/asm-sh/smc37c93x.h new file mode 100644 index 00000000000..585da2a8fc4 --- /dev/null +++ b/include/asm-sh/smc37c93x.h @@ -0,0 +1,190 @@ +#ifndef __ASM_SH_SMC37C93X_H +#define __ASM_SH_SMC37C93X_H + +/* + * linux/include/asm-sh/smc37c93x.h + * + * Copyright (C) 2000 Kazumoto Kojima + * + * SMSC 37C93x Super IO Chip support + */ + +/* Default base I/O address */ +#define FDC_PRIMARY_BASE 0x3f0 +#define IDE1_PRIMARY_BASE 0x1f0 +#define IDE1_SECONDARY_BASE 0x170 +#define PARPORT_PRIMARY_BASE 0x378 +#define COM1_PRIMARY_BASE 0x2f8 +#define COM2_PRIMARY_BASE 0x3f8 +#define RTC_PRIMARY_BASE 0x070 +#define KBC_PRIMARY_BASE 0x060 +#define AUXIO_PRIMARY_BASE 0x000 /* XXX */ + +/* Logical device number */ +#define LDN_FDC 0 +#define LDN_IDE1 1 +#define LDN_IDE2 2 +#define LDN_PARPORT 3 +#define LDN_COM1 4 +#define LDN_COM2 5 +#define LDN_RTC 6 +#define LDN_KBC 7 +#define LDN_AUXIO 8 + +/* Configuration port and key */ +#define CONFIG_PORT 0x3f0 +#define INDEX_PORT CONFIG_PORT +#define DATA_PORT 0x3f1 +#define CONFIG_ENTER 0x55 +#define CONFIG_EXIT 0xaa + +/* Configuration index */ +#define CURRENT_LDN_INDEX 0x07 +#define POWER_CONTROL_INDEX 0x22 +#define ACTIVATE_INDEX 0x30 +#define IO_BASE_HI_INDEX 0x60 +#define IO_BASE_LO_INDEX 0x61 +#define IRQ_SELECT_INDEX 0x70 +#define DMA_SELECT_INDEX 0x74 + +#define GPIO46_INDEX 0xc6 +#define GPIO47_INDEX 0xc7 + +/* UART stuff. Only for debugging. */ +/* UART Register */ + +#define UART_RBR 0x0 /* Receiver Buffer Register (Read Only) */ +#define UART_THR 0x0 /* Transmitter Holding Register (Write Only) */ +#define UART_IER 0x2 /* Interrupt Enable Register */ +#define UART_IIR 0x4 /* Interrupt Ident Register (Read Only) */ +#define UART_FCR 0x4 /* FIFO Control Register (Write Only) */ +#define UART_LCR 0x6 /* Line Control Register */ +#define UART_MCR 0x8 /* MODEM Control Register */ +#define UART_LSR 0xa /* Line Status Register */ +#define UART_MSR 0xc /* MODEM Status Register */ +#define UART_SCR 0xe /* Scratch Register */ +#define UART_DLL 0x0 /* Divisor Latch (LS) */ +#define UART_DLM 0x2 /* Divisor Latch (MS) */ + +#ifndef __ASSEMBLY__ +typedef struct uart_reg { + volatile __u16 rbr; + volatile __u16 ier; + volatile __u16 iir; + volatile __u16 lcr; + volatile __u16 mcr; + volatile __u16 lsr; + volatile __u16 msr; + volatile __u16 scr; +} uart_reg; +#endif /* ! __ASSEMBLY__ */ + +/* Alias for Write Only Register */ + +#define thr rbr +#define tcr iir + +/* Alias for Divisor Latch Register */ + +#define dll rbr +#define dlm ier +#define fcr iir + +/* Interrupt Enable Register */ + +#define IER_ERDAI 0x0100 /* Enable Received Data Available Interrupt */ +#define IER_ETHREI 0x0200 /* Enable Transmitter Holding Register Empty Interrupt */ +#define IER_ELSI 0x0400 /* Enable Receiver Line Status Interrupt */ +#define IER_EMSI 0x0800 /* Enable MODEM Status Interrupt */ + +/* Interrupt Ident Register */ + +#define IIR_IP 0x0100 /* "0" if Interrupt Pending */ +#define IIR_IIB0 0x0200 /* Interrupt ID Bit 0 */ +#define IIR_IIB1 0x0400 /* Interrupt ID Bit 1 */ +#define IIR_IIB2 0x0800 /* Interrupt ID Bit 2 */ +#define IIR_FIFO 0xc000 /* FIFOs enabled */ + +/* FIFO Control Register */ + +#define FCR_FEN 0x0100 /* FIFO enable */ +#define FCR_RFRES 0x0200 /* Receiver FIFO reset */ +#define FCR_TFRES 0x0400 /* Transmitter FIFO reset */ +#define FCR_DMA 0x0800 /* DMA mode select */ +#define FCR_RTL 0x4000 /* Receiver triger (LSB) */ +#define FCR_RTM 0x8000 /* Receiver triger (MSB) */ + +/* Line Control Register */ + +#define LCR_WLS0 0x0100 /* Word Length Select Bit 0 */ +#define LCR_WLS1 0x0200 /* Word Length Select Bit 1 */ +#define LCR_STB 0x0400 /* Number of Stop Bits */ +#define LCR_PEN 0x0800 /* Parity Enable */ +#define LCR_EPS 0x1000 /* Even Parity Select */ +#define LCR_SP 0x2000 /* Stick Parity */ +#define LCR_SB 0x4000 /* Set Break */ +#define LCR_DLAB 0x8000 /* Divisor Latch Access Bit */ + +/* MODEM Control Register */ + +#define MCR_DTR 0x0100 /* Data Terminal Ready */ +#define MCR_RTS 0x0200 /* Request to Send */ +#define MCR_OUT1 0x0400 /* Out 1 */ +#define MCR_IRQEN 0x0800 /* IRQ Enable */ +#define MCR_LOOP 0x1000 /* Loop */ + +/* Line Status Register */ + +#define LSR_DR 0x0100 /* Data Ready */ +#define LSR_OE 0x0200 /* Overrun Error */ +#define LSR_PE 0x0400 /* Parity Error */ +#define LSR_FE 0x0800 /* Framing Error */ +#define LSR_BI 0x1000 /* Break Interrupt */ +#define LSR_THRE 0x2000 /* Transmitter Holding Register Empty */ +#define LSR_TEMT 0x4000 /* Transmitter Empty */ +#define LSR_FIFOE 0x8000 /* Receiver FIFO error */ + +/* MODEM Status Register */ + +#define MSR_DCTS 0x0100 /* Delta Clear to Send */ +#define MSR_DDSR 0x0200 /* Delta Data Set Ready */ +#define MSR_TERI 0x0400 /* Trailing Edge Ring Indicator */ +#define MSR_DDCD 0x0800 /* Delta Data Carrier Detect */ +#define MSR_CTS 0x1000 /* Clear to Send */ +#define MSR_DSR 0x2000 /* Data Set Ready */ +#define MSR_RI 0x4000 /* Ring Indicator */ +#define MSR_DCD 0x8000 /* Data Carrier Detect */ + +/* Baud Rate Divisor */ + +#define UART_CLK (1843200) /* 1.8432 MHz */ +#define UART_BAUD(x) (UART_CLK / (16 * (x))) + +/* RTC register definition */ +#define RTC_SECONDS 0 +#define RTC_SECONDS_ALARM 1 +#define RTC_MINUTES 2 +#define RTC_MINUTES_ALARM 3 +#define RTC_HOURS 4 +#define RTC_HOURS_ALARM 5 +#define RTC_DAY_OF_WEEK 6 +#define RTC_DAY_OF_MONTH 7 +#define RTC_MONTH 8 +#define RTC_YEAR 9 +#define RTC_FREQ_SELECT 10 +# define RTC_UIP 0x80 +# define RTC_DIV_CTL 0x70 +/* This RTC can work under 32.768KHz clock only. */ +# define RTC_OSC_ENABLE 0x20 +# define RTC_OSC_DISABLE 0x00 +#define RTC_CONTROL 11 +# define RTC_SET 0x80 +# define RTC_PIE 0x40 +# define RTC_AIE 0x20 +# define RTC_UIE 0x10 +# define RTC_SQWE 0x08 +# define RTC_DM_BINARY 0x04 +# define RTC_24H 0x02 +# define RTC_DST_EN 0x01 + +#endif /* __ASM_SH_SMC37C93X_H */ diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h index ab5383e0aca..3bb876274f9 100644 --- a/include/asm-sh/system.h +++ b/include/asm-sh/system.h @@ -165,18 +165,18 @@ do { \ /* * Back to P1 area. */ -#define back_to_P1() \ -do { \ - unsigned long __dummy; \ - __asm__ __volatile__( \ - "nop;nop;nop;nop;nop;nop\n\t" \ - "mov.l 1f, %0\n\t" \ - "jmp @%0\n\t" \ - " nop\n\t" \ - ".balign 4\n" \ - "1: .long 2f\n" \ - "2:" \ - : "=&r" (__dummy)); \ +#define back_to_P1() \ +do { \ + unsigned long __dummy; \ + __asm__ __volatile__( \ + "nop;nop;nop;nop;nop;nop;nop\n\t" \ + "mov.l 1f, %0\n\t" \ + "jmp @%0\n\t" \ + " nop\n\t" \ + ".balign 4\n" \ + "1: .long 2f\n" \ + "2:" \ + : "=&r" (__dummy)); \ } while (0) /* For spinlocks etc */ diff --git a/include/asm-sh/unistd.h b/include/asm-sh/unistd.h index f927716b240..0594308fe6d 100644 --- a/include/asm-sh/unistd.h +++ b/include/asm-sh/unistd.h @@ -314,16 +314,16 @@ __syscall_return(type,__sc0); \ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \ type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ { \ -register long __sc0 __asm__ ("$r3") = __NR_##name; \ +register long __sc3 __asm__ ("$r3") = __NR_##name; \ register long __sc4 __asm__ ("$r4") = (long) arg1; \ register long __sc5 __asm__ ("$r5") = (long) arg2; \ register long __sc6 __asm__ ("$r6") = (long) arg3; \ register long __sc7 __asm__ ("$r7") = (long) arg4; \ -register long __sc2 __asm__ ("$r0") = (long) arg5; \ +register long __sc0 __asm__ ("$r0") = (long) arg5; \ __asm__ __volatile__ ("trapa #0x15" \ : "=z" (__sc0) \ : "0" (__sc0), "r" (__sc4), "r" (__sc5), "r" (__sc6), "r" (__sc7), \ - "r" (__sc7), "r" (__sc2) \ + "r" (__sc3) \ : "memory" ); \ __syscall_return(type,__sc0); \ } diff --git a/include/asm-sparc/bitops.h b/include/asm-sparc/bitops.h index 09a08baa2eb..b2c8acfffb3 100644 --- a/include/asm-sparc/bitops.h +++ b/include/asm-sparc/bitops.h @@ -1,4 +1,4 @@ -/* $Id: bitops.h,v 1.55 2000/02/09 03:28:32 davem Exp $ +/* $Id: bitops.h,v 1.56 2000/05/09 17:40:15 davem Exp $ * bitops.h: Bit string operations on the Sparc. * * Copyright 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/include/asm-sparc/ide.h b/include/asm-sparc/ide.h index 25451ae0e61..79b0e47a134 100644 --- a/include/asm-sparc/ide.h +++ b/include/asm-sparc/ide.h @@ -1,4 +1,4 @@ -/* $Id: ide.h,v 1.4 2000/03/12 03:56:12 davem Exp $ +/* $Id: ide.h,v 1.5 2000/05/22 07:29:43 davem Exp $ * ide.h: SPARC PCI specific IDE glue. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -65,7 +65,8 @@ static __inline__ void ide_init_default_hwifs(void) int index; for (index = 0; index < MAX_HWIFS; index++) { - ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, 0); + memset(&hw, 0, sizeof(hw_regs_t)); + ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); hw.irq = ide_default_irq(ide_default_io_base(index)); ide_register_hw(&hw, NULL); } diff --git a/include/asm-sparc/irq.h b/include/asm-sparc/irq.h index 1f40557b486..512a8e8186a 100644 --- a/include/asm-sparc/irq.h +++ b/include/asm-sparc/irq.h @@ -1,4 +1,4 @@ -/* $Id: irq.h,v 1.28 2000/01/22 06:06:58 zaitcev Exp $ +/* $Id: irq.h,v 1.29 2000/05/09 17:40:15 davem Exp $ * irq.h: IRQ registers on the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/include/asm-sparc/pgalloc.h b/include/asm-sparc/pgalloc.h index 7a2b81bebf1..c0850c9a9f3 100644 --- a/include/asm-sparc/pgalloc.h +++ b/include/asm-sparc/pgalloc.h @@ -1,4 +1,4 @@ -/* $Id: pgalloc.h,v 1.3 2000/02/03 10:13:31 jj Exp $ */ +/* $Id: pgalloc.h,v 1.4 2000/05/09 17:40:15 davem Exp $ */ #ifndef _SPARC_PGALLOC_H #define _SPARC_PGALLOC_H diff --git a/include/asm-sparc/system.h b/include/asm-sparc/system.h index 00e33e3bff4..69cc06de230 100644 --- a/include/asm-sparc/system.h +++ b/include/asm-sparc/system.h @@ -1,4 +1,4 @@ -/* $Id: system.h,v 1.81 2000/02/28 04:00:44 anton Exp $ */ +/* $Id: system.h,v 1.82 2000/05/09 17:40:15 davem Exp $ */ #include #ifndef __SPARC_SYSTEM_H diff --git a/include/asm-sparc/winmacro.h b/include/asm-sparc/winmacro.h index e760822574d..619f5e944d0 100644 --- a/include/asm-sparc/winmacro.h +++ b/include/asm-sparc/winmacro.h @@ -1,4 +1,4 @@ -/* $Id: winmacro.h,v 1.21 1999/08/14 03:52:13 anton Exp $ +/* $Id: winmacro.h,v 1.22 2000/05/09 17:40:15 davem Exp $ * winmacro.h: Window loading-unloading macros. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/include/asm-sparc64/delay.h b/include/asm-sparc64/delay.h index 4a9bfdb1c70..1dc63645316 100644 --- a/include/asm-sparc64/delay.h +++ b/include/asm-sparc64/delay.h @@ -1,4 +1,4 @@ -/* $Id: delay.h,v 1.8 2000/04/13 04:45:59 davem Exp $ +/* $Id: delay.h,v 1.9 2000/05/09 17:40:15 davem Exp $ * delay.h: Linux delay routines on the V9. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu). diff --git a/include/asm-sparc64/ide.h b/include/asm-sparc64/ide.h index 385baa7a471..471ee71a055 100644 --- a/include/asm-sparc64/ide.h +++ b/include/asm-sparc64/ide.h @@ -1,4 +1,4 @@ -/* $Id: ide.h,v 1.17 1999/12/15 22:18:49 davem Exp $ +/* $Id: ide.h,v 1.18 2000/05/22 07:29:43 davem Exp $ * ide.h: Ultra/PCI specific IDE glue. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -59,7 +59,8 @@ static __inline__ void ide_init_default_hwifs(void) int index; for (index = 0; index < MAX_HWIFS; index++) { - ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, 0); + memset(&hw, 0, sizeof(hw_regs_t)); + ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); hw.irq = ide_default_irq(ide_default_io_base(index)); ide_register_hw(&hw, NULL); } diff --git a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h index cff5236edf0..111ad568b06 100644 --- a/include/asm-sparc64/irq.h +++ b/include/asm-sparc64/irq.h @@ -1,4 +1,4 @@ -/* $Id: irq.h,v 1.17 1999/09/21 14:39:41 davem Exp $ +/* $Id: irq.h,v 1.18 2000/05/09 17:40:15 davem Exp $ * irq.h: IRQ registers on the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/include/asm-sparc64/oplib.h b/include/asm-sparc64/oplib.h index 963ce73e44f..cf6cbc58967 100644 --- a/include/asm-sparc64/oplib.h +++ b/include/asm-sparc64/oplib.h @@ -1,4 +1,4 @@ -/* $Id: oplib.h,v 1.12 1999/11/19 05:53:12 davem Exp $ +/* $Id: oplib.h,v 1.13 2000/05/09 17:40:15 davem Exp $ * oplib.h: Describes the interface and available routines in the * Linux Prom library. * diff --git a/include/asm-sparc64/processor.h b/include/asm-sparc64/processor.h index b731a18aa05..b7b124a56fd 100644 --- a/include/asm-sparc64/processor.h +++ b/include/asm-sparc64/processor.h @@ -1,4 +1,4 @@ -/* $Id: processor.h,v 1.63 2000/03/27 10:38:57 davem Exp $ +/* $Id: processor.h,v 1.64 2000/05/09 17:40:15 davem Exp $ * include/asm-sparc64/processor.h * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h index 895cd74df18..00303c23994 100644 --- a/include/asm-sparc64/system.h +++ b/include/asm-sparc64/system.h @@ -1,4 +1,4 @@ -/* $Id: system.h,v 1.58 2000/05/05 18:47:41 davem Exp $ */ +/* $Id: system.h,v 1.59 2000/05/09 17:40:15 davem Exp $ */ #ifndef __SPARC64_SYSTEM_H #define __SPARC64_SYSTEM_H diff --git a/include/asm-sparc64/timer.h b/include/asm-sparc64/timer.h index 8eb30d7d89a..4aa85bedef9 100644 --- a/include/asm-sparc64/timer.h +++ b/include/asm-sparc64/timer.h @@ -1,4 +1,4 @@ -/* $Id: timer.h,v 1.2 1998/03/15 17:23:52 ecd Exp $ +/* $Id: timer.h,v 1.3 2000/05/09 17:40:15 davem Exp $ * timer.h: System timer definitions for sun5. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index abf6c09ebf2..c7fd4cd191c 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -1,4 +1,4 @@ -/* $Id: unistd.h,v 1.42 2000/01/29 17:57:26 jj Exp $ */ +/* $Id: unistd.h,v 1.44 2000/05/16 16:42:33 jj Exp $ */ #ifndef _SPARC64_UNISTD_H #define _SPARC64_UNISTD_H @@ -246,7 +246,9 @@ #define __NR_setfsuid 228 /* Linux Specific */ #define __NR_setfsgid 229 /* Linux Specific */ #define __NR__newselect 230 /* Linux Specific */ -#define __NR_time 231 /* Linux Specific */ +#ifdef __KERNEL__ +#define __NR_time 231 /* Linux sparc32 */ +#endif /* #define __NR_oldstat 232 Linux Specific */ #define __NR_stime 233 /* Linux Specific */ /* #define __NR_oldfstat 234 Linux Specific */ diff --git a/include/linux/coda.h b/include/linux/coda.h index 6e1a939bedb..cbd042242ef 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -61,8 +61,9 @@ Mellon the rights to redistribute these changes without encumbrance. -/* Catch new _KERNEL defn for NetBSD */ -#ifdef __NetBSD__ +/* Catch new _KERNEL defn for NetBSD and DJGPP/__CYGWIN32__ */ +#if defined(__NetBSD__) || \ + ((defined(DJGPP) || defined(__CYGWIN32__)) && !defined(KERNEL)) #include #endif @@ -91,7 +92,6 @@ struct timespec { long ts_nsec; }; #else /* DJGPP but not KERNEL */ -#include #include typedef unsigned long long u_quad_t; #endif /* !KERNEL */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 7eccee7b996..12bbfbdc1b7 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -80,7 +80,7 @@ struct dentry_operations { int (*d_revalidate)(struct dentry *, int); int (*d_hash) (struct dentry *, struct qstr *); int (*d_compare) (struct dentry *, struct qstr *, struct qstr *); - void (*d_delete)(struct dentry *); + int (*d_delete)(struct dentry *); void (*d_release)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); }; @@ -160,8 +160,11 @@ extern void prune_icache(int); /* only used at mount-time */ extern struct dentry * d_alloc_root(struct inode *); -/* test whether root is busy without destroying dcache */ -extern int d_active_refs(struct dentry *); +/* - the ramfs-type tree */ +extern void d_genocide(struct dentry *); + +extern struct dentry *d_find_alias(struct inode *); +extern void d_prune_aliases(struct inode *); /* test whether we have any submounts in a subdir tree */ extern int have_submounts(struct dentry *); diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 748a9834cf6..88d3770b9ab 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -22,6 +22,8 @@ struct elevator_s int read_pendings; elevator_fn * elevator_fn; + + unsigned int queue_ID; }; #define ELEVATOR_DEFAULTS \ @@ -40,14 +42,14 @@ struct elevator_s typedef struct blkelv_ioctl_arg_s { - void * queue_ID; + int queue_ID; int read_latency; int write_latency; int max_bomb_segments; } blkelv_ioctl_arg_t; -#define BLKELVGET _IO(0x12,106) -#define BLKELVSET _IO(0x12,107) +#define BLKELVGET _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t)) +#define BLKELVSET _IOW(0x12,107,sizeof(blkelv_ioctl_arg_t)) extern int blkelvget_ioctl(elevator_t *, blkelv_ioctl_arg_t *); extern int blkelvset_ioctl(elevator_t *, const blkelv_ioctl_arg_t *); diff --git a/include/linux/fb.h b/include/linux/fb.h index f2bc40f4d0e..733f7fb0051 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -25,6 +25,8 @@ #define FBIOPUT_CON2FBMAP 0x4610 #define FBIOBLANK 0x4611 /* arg: 0 or vesa level + 1 */ #define FBIOGET_VBLANK _IOR('F', 0x12, struct fb_vblank) +#define FBIO_ALLOC 0x4613 +#define FBIO_FREE 0x4614 #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ #define FB_TYPE_PLANES 1 /* Non interleaved planes */ @@ -85,6 +87,7 @@ #define FB_ACCEL_IGS_CYBER2000 33 /* CyberPro 2000 */ #define FB_ACCEL_IGS_CYBER2010 34 /* CyberPro 2010 */ #define FB_ACCEL_IGS_CYBER5000 35 /* CyberPro 5000 */ +#define FB_ACCEL_SIS_GLAMOUR 36 /* SiS 300/630/540 */ struct fb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 4eb593aba06..d147dfe1c2a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -165,8 +165,8 @@ extern int max_super_blocks, nr_super_blocks; #define BLKSSZGET _IO(0x12,104)/* get block device sector size */ #if 0 #define BLKPG _IO(0x12,105)/* See blkpg.h */ -#define BLKELVGET _IO(0x12,106)/* elevator get */ -#define BLKELVSET _IO(0x12,107)/* elevator set */ +#define BLKELVGET _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t))/* elevator get */ +#define BLKELVSET _IOW(0x12,107,sizeof(blkelv_ioctl_arg_t))/* elevator set */ /* This was here just to show that the number is taken - probably all these _IO(0x12,*) ioctls should be moved to blkpg.h. */ #endif @@ -794,6 +794,8 @@ extern int unregister_filesystem(struct file_system_type *); extern struct vfsmount *kern_mount(struct file_system_type *); extern void kern_umount(struct vfsmount *); extern int may_umount(struct vfsmount *); +extern long do_mount(char *, char *, char *, unsigned long, void *); + extern int vfs_statfs(struct super_block *, struct statfs *); @@ -900,7 +902,7 @@ extern struct file_operations rdwr_pipe_fops; extern int fs_may_remount_ro(struct super_block *); -extern int try_to_free_buffers(struct page *); +extern int try_to_free_buffers(struct page *, int); extern void refile_buffer(struct buffer_head * buf); #define BUF_CLEAN 0 @@ -1116,6 +1118,7 @@ extern int page_follow_link(struct dentry *, struct nameidata *); extern struct inode_operations page_symlink_inode_operations; extern int vfs_readdir(struct file *, filldir_t, void *); +extern int dcache_readdir(struct file *, void *, filldir_t); extern struct super_block *get_super(kdev_t); struct super_block *get_empty_super(void); diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index e28f6bfff8b..e229fa2b409 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -181,6 +181,8 @@ struct hd_geometry { #define HDIO_GET_DMA 0x030b /* get use-dma flag */ #define HDIO_GET_NICE 0x030c /* get nice flags */ #define HDIO_GET_IDENTITY 0x030d /* get IDE identification info */ +#define HDIO_DRIVE_CMD_AEB 0x031e +#define HDIO_DRIVE_TASK 0x031e #define HDIO_DRIVE_CMD 0x031f /* execute a special drive command */ /* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */ @@ -207,7 +209,6 @@ struct hd_big_geometry { #define HDIO_GETGEO_BIG 0x0330 /* */ #define HDIO_GETGEO_BIG_RAW 0x0331 /* */ - #define __NEW_HD_DRIVE_ID /* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */ struct hd_driveid { @@ -272,7 +273,7 @@ struct hd_driveid { unsigned short CurAPMvalues; /* current APM values */ unsigned short word92; /* reserved (word 92) */ unsigned short hw_config; /* hardware config */ - unsigned short words94_125[31];/* reserved words 94-125 */ + unsigned short words94_125[32];/* reserved words 94-125 */ unsigned short last_lun; /* reserved (word 126) */ unsigned short word127; /* reserved (word 127) */ unsigned short dlf; /* device lock function @@ -293,8 +294,10 @@ struct hd_driveid { * 1 read-look-ahead * 0 write cache */ - unsigned short words130_159[30];/* reserved vendor words 130-159 */ - unsigned short words160_255[96];/* reserved words 160-255 */ + unsigned short words130_155[26];/* reserved vendor words 130-155 */ + unsigned short word156; + unsigned short words157_159[3];/* reserved vendor words 157-159 */ + unsigned short words160_255[95];/* reserved words 160-255 */ }; /* @@ -314,10 +317,6 @@ struct hd_driveid { */ #include -#ifdef CONFIG_BLK_DEV_HD -void hd_setup(char *, int *); -#endif /* CONFIG_BLK_DEV_HD */ - #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) int ide_register(int io_port, int ctl_port, int irq); void ide_unregister(unsigned int); diff --git a/include/linux/hdsmart.h b/include/linux/hdsmart.h dissimilarity index 78% index 509b591d283..7974a47fe58 100644 --- a/include/linux/hdsmart.h +++ b/include/linux/hdsmart.h @@ -1,139 +1,124 @@ -/* - * linux/include/linux/hdsmart.h - * - * Copyright (C) 1999-2000 Michael Cornwell - * Copyright (C) 2000 Andre Hedrick - * - * 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, or (at your option) - * any later version. - * - * You should have received a copy of the GNU General Public License - * (for example /usr/src/linux/COPYING); if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _LINUX_HDSMART_H -#define _LINUX_HDSMART_H - -/* smart_attribute is the vendor specific in SFF-8035 spec */ -struct ata_smart_attribute { - unsigned char id; - unsigned short status_flag; - unsigned char normalized; - unsigned char worse_normal; - unsigned char raw[6]; - unsigned char reserv; -} __attribute__ ((packed)); - -/* smart_values is format of the read drive Atrribute command */ -struct ata_smart_values { - unsigned short revnumber; - struct ata_smart_attribute vendor_attributes [30]; - unsigned char offline_data_collection_status; - unsigned char self_test_exec_status; - unsigned short total_time_to_complete_off_line; - unsigned char vendor_specific_366; - unsigned char offline_data_collection_capability; - unsigned short smart_capability; - unsigned char errorlog_capability; - unsigned char vendor_specific_371; - unsigned char short_test_completion_time; - unsigned char extend_test_completion_time; - unsigned char reserved_374_385 [12]; - unsigned char vendor_specific_386_509 [125]; - unsigned char chksum; -} __attribute__ ((packed)); - -/* Smart Threshold data structures */ -/* Vendor attribute of SMART Threshold */ -struct ata_smart_threshold_entry { - unsigned char id; - unsigned char normalized_threshold; - unsigned char reserved[10]; -} __attribute__ ((packed)); - -/* Format of Read SMART THreshold Command */ -struct ata_smart_thresholds { - unsigned short revnumber; - struct ata_smart_threshold_entry thres_entries[30]; - unsigned char reserved[149]; - unsigned char chksum; -} __attribute__ ((packed)); - -struct ata_smart_errorlog_command_struct { - unsigned char devicecontrolreg; - unsigned char featuresreg; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - unsigned char drive_head; - unsigned char commandreg; - unsigned int timestamp; -} __attribute__ ((packed)); - -struct ata_smart_errorlog_error_struct { - unsigned char error_condition; - unsigned char extended_error[14]; - unsigned char state; - unsigned short timestamp; -} __attribute__ ((packed)); - -struct ata_smart_errorlog_struct { - struct ata_smart_errorlog_command_struct commands[6]; - struct ata_smart_errorlog_error_struct error_struct; -} __attribute__ ((packed)); - -struct ata_smart_errorlog { - unsigned char revnumber; - unsigned char error_log_pointer; - struct ata_smart_errorlog_struct errorlog_struct[5]; - unsigned short ata_error_count; - unsigned short non_fatal_count; - unsigned short drive_timeout_count; - unsigned char reserved[53]; -} __attribute__ ((packed)); - -struct ata_smart_selftestlog_struct { - unsigned char selftestnumber; - unsigned char selfteststatus; - unsigned short timestamp; - unsigned char selftestfailurecheckpoint; - unsigned int lbafirstfailure; - unsigned char vendorspecific[15]; -} __attribute__ ((packed)); - -struct ata_smart_selftestlog { - unsigned short revnumber; - struct ata_smart_selftestlog_struct selftest_struct[21]; - unsigned char vendorspecific[2]; - unsigned char mostrecenttest; - unsigned char resevered[2]; - unsigned char chksum; -} __attribute__ ((packed)); - -#if !defined(__KERNEL__) || defined(_IDE_DISK_C) -/* smartctl version number */ -#define VERSION_MAJOR 1 -#define VERSION_MINOR 2 - -/* Number of ata device to scan */ -int numdevices; - -/* how often SMART is checks in seconds */ -int checktime = 1800; - -typedef struct atadevices_s { - int fd; - char devicename[14]; - int selftest; - struct hd_driveid drive; - struct ata_smart_values smartval; - struct ata_smart_thresholds smartthres; -} atadevices_t; - -#endif /* !defined(__KERNEL__) || defined(_IDE_DISK_C) */ - -#endif /* _LINUX_HDSMART_H */ +/* + * linux/include/linux/hdsmart.h + * + * Copyright (C) 1999-2000 Michael Cornwell + * Copyright (C) 2000 Andre Hedrick + * + * 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, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LINUX_HDSMART_H +#define _LINUX_HDSMART_H + +#define OFFLINE_FULL_SCAN 0 +#define SHORT_SELF_TEST 1 +#define EXTEND_SELF_TEST 2 +#define SHORT_CAPTIVE_SELF_TEST 129 +#define EXTEND_CAPTIVE_SELF_TEST 130 + +/* smart_attribute is the vendor specific in SFF-8035 spec */ +typedef struct ata_smart_attribute_s { + unsigned char id; + unsigned short status_flag; + unsigned char normalized; + unsigned char worse_normal; + unsigned char raw[6]; + unsigned char reserv; +} __attribute__ ((packed)) ata_smart_attribute_t; + +/* smart_values is format of the read drive Atrribute command */ +typedef struct ata_smart_values_s { + unsigned short revnumber; + ata_smart_attribute_t vendor_attributes [30]; + unsigned char offline_data_collection_status; + unsigned char self_test_exec_status; + unsigned short total_time_to_complete_off_line; + unsigned char vendor_specific_366; + unsigned char offline_data_collection_capability; + unsigned short smart_capability; + unsigned char errorlog_capability; + unsigned char vendor_specific_371; + unsigned char short_test_completion_time; + unsigned char extend_test_completion_time; + unsigned char reserved_374_385 [12]; + unsigned char vendor_specific_386_509 [125]; + unsigned char chksum; +} __attribute__ ((packed)) ata_smart_values_t; + +/* Smart Threshold data structures */ +/* Vendor attribute of SMART Threshold */ +typedef struct ata_smart_threshold_entry_s { + unsigned char id; + unsigned char normalized_threshold; + unsigned char reserved[10]; +} __attribute__ ((packed)) ata_smart_threshold_entry_t; + +/* Format of Read SMART THreshold Command */ +typedef struct ata_smart_thresholds_s { + unsigned short revnumber; + ata_smart_threshold_entry_t thres_entries[30]; + unsigned char reserved[149]; + unsigned char chksum; +} __attribute__ ((packed)) ata_smart_thresholds_t; + +typedef struct ata_smart_errorlog_command_struct_s { + unsigned char devicecontrolreg; + unsigned char featuresreg; + unsigned char sector_count; + unsigned char sector_number; + unsigned char cylinder_low; + unsigned char cylinder_high; + unsigned char drive_head; + unsigned char commandreg; + unsigned int timestamp; +} __attribute__ ((packed)) ata_smart_errorlog_command_struct_t; + +typedef struct ata_smart_errorlog_error_struct_s { + unsigned char error_condition; + unsigned char extended_error[14]; + unsigned char state; + unsigned short timestamp; +} __attribute__ ((packed)) ata_smart_errorlog_error_struct_t; + +typedef struct ata_smart_errorlog_struct_s { + ata_smart_errorlog_command_struct_t commands[6]; + ata_smart_errorlog_error_struct_t error_struct; +} __attribute__ ((packed)) ata_smart_errorlog_struct_t; + +typedef struct ata_smart_errorlog_s { + unsigned char revnumber; + unsigned char error_log_pointer; + ata_smart_errorlog_struct_t errorlog_struct[5]; + unsigned short ata_error_count; + unsigned short non_fatal_count; + unsigned short drive_timeout_count; + unsigned char reserved[53]; + unsigned char chksum; +} __attribute__ ((packed)) ata_smart_errorlog_t; + +typedef struct ata_smart_selftestlog_struct_s { + unsigned char selftestnumber; + unsigned char selfteststatus; + unsigned short timestamp; + unsigned char selftestfailurecheckpoint; + unsigned int lbafirstfailure; + unsigned char vendorspecific[15]; +} __attribute__ ((packed)) ata_smart_selftestlog_struct_t; + +typedef struct ata_smart_selftestlog_s { + unsigned short revnumber; + ata_smart_selftestlog_struct_t selftest_struct[21]; + unsigned char vendorspecific[2]; + unsigned char mostrecenttest; + unsigned char resevered[2]; + unsigned char chksum; +} __attribute__ ((packed)) ata_smart_selftestlog_t; + +#endif /* _LINUX_HDSMART_H */ diff --git a/include/linux/ide.h b/include/linux/ide.h index ea395aaa86f..8804c9777f9 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -64,6 +64,12 @@ void cmd640_dump_regs (void); #define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/ /* + * IDE_DRIVE_TASK is used to implement many features needed for raw tasks + */ +#define IDE_DRIVE_TASK 98 +#define IDE_DRIVE_CMD_AEB 98 + +/* * "No user-serviceable parts" beyond this point :) *****************************************************************************/ @@ -302,6 +308,10 @@ typedef struct ide_drive_s { char driver_req[10]; /* requests specific driver */ int last_lun; /* last logical unit */ int forced_lun; /* if hdxlun was given at boot */ + int lun; /* logical unit */ + byte init_speed; /* transfer rate set at boot */ + byte current_speed; /* current transfer rate set */ + byte dn; /* now wide spread use */ } ide_drive_t; /* @@ -336,7 +346,8 @@ typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *); * support all possible PIO settings. They may silently ignore * or round values as they see fit. */ -typedef void (ide_tuneproc_t)(ide_drive_t *, byte); +typedef void (ide_tuneproc_t) (ide_drive_t *, byte); +typedef int (ide_speedproc_t) (ide_drive_t *, byte); /* * This is used to provide support for strange interfaces @@ -374,6 +385,7 @@ typedef struct hwif_s { ide_drive_t drives[MAX_DRIVES]; /* drive info */ struct gendisk *gd; /* gendisk structure */ ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */ + ide_speedproc_t *speedproc; /* routine to retune DMA modes for drives */ ide_selectproc_t *selectproc; /* tweaks hardware to select drive */ ide_resetproc_t *resetproc; /* routine to reset controller after a disk reset */ ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */ @@ -409,6 +421,7 @@ typedef struct hwif_s { unsigned long last_time; /* time when previous rq was done */ #endif byte straight8; /* Alan's straight 8 check */ + void *hwif_data; /* extra hwif data */ } ide_hwif_t; /* @@ -728,14 +741,16 @@ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err); * Issue ATA command and wait for completion. */ int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf); +int ide_wait_cmd_task (ide_drive_t *drive, byte *buf); void ide_delay_50ms (void); int system_bus_clock(void); +byte ide_auto_reduce_xfer (ide_drive_t *drive); int ide_driveid_update (ide_drive_t *drive); -int ide_ata66_check (ide_drive_t *drive, int cmd, int nsect, int feature); +int ide_ata66_check (ide_drive_t *drive, byte cmd, byte nsect, byte feature); int ide_config_drive_speed (ide_drive_t *drive, byte speed); -int set_transfer (ide_drive_t *drive, int cmd, int nsect, int feature); +int set_transfer (ide_drive_t *drive, byte cmd, byte nsect, byte feature); /* * ide_system_bus_speed() returns what we think is the system VESA/PCI @@ -769,37 +784,10 @@ request_queue_t *ide_get_queue (kdev_t dev); */ int drive_is_flashcard (ide_drive_t *drive); -int ide_spin_wait_hwgroup(ide_drive_t *drive, unsigned long *flags); +int ide_spin_wait_hwgroup (ide_drive_t *drive); void ide_timer_expiry (unsigned long data); void ide_intr (int irq, void *dev_id, struct pt_regs *regs); -void do_ide0_request (request_queue_t * q); -#if MAX_HWIFS > 1 -void do_ide1_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 2 -void do_ide2_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 3 -void do_ide3_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 4 -void do_ide4_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 5 -void do_ide5_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 6 -void do_ide6_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 7 -void do_ide7_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 8 -void do_ide8_request (request_queue_t * q); -#endif -#if MAX_HWIFS > 9 -void do_ide9_request (request_queue_t * q); -#endif +void do_ide_request (request_queue_t * q); void ide_init_subdrivers (void); #ifndef _IDE_C diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 873a7d9d630..0129c67b9ec 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -16,12 +16,13 @@ #ifndef __LINUX_IF_PPPOX_H #define __LINUX_IF_PPPOX_H -#include -#include #include #include + #ifdef __KERNEL__ +#include +#include #include #include #include diff --git a/include/linux/lvm.h b/include/linux/lvm.h index 703d8f72e6f..7afbc117815 100644 --- a/include/linux/lvm.h +++ b/include/linux/lvm.h @@ -83,11 +83,13 @@ #include #ifndef __KERNEL__ +#define ____NOT_KERNEL____ #define __KERNEL__ +#endif #include +#ifdef ____NOT_KERNEL____ +#undef ____NOT_KERNEL____ #undef __KERNEL__ -#else -#include #endif #include diff --git a/include/linux/mount.h b/include/linux/mount.h index fcec956470d..61ab19b1fc5 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -28,6 +28,7 @@ struct vfsmount char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ char *mnt_dirname; /* Name of directory mounted on */ struct list_head mnt_list; + uid_t mnt_owner; }; static inline struct vfsmount *mntget(struct vfsmount *mnt) diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index be5ea2afaa3..aa1ad6f0fdf 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -54,4 +54,14 @@ #define NF_IP6_NUMHOOKS 5 +enum nf_ip_hook_priorities { + NF_IP6_PRI_FIRST = INT_MIN, + NF_IP6_PRI_CONNTRACK = -200, + NF_IP6_PRI_MANGLE = -150, + NF_IP6_PRI_NAT_DST = -100, + NF_IP6_PRI_FILTER = 0, + NF_IP6_PRI_NAT_SRC = 100, + NF_IP6_PRI_LAST = INT_MAX, +}; + #endif /*__LINUX_IP6_NETFILTER_H*/ diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h new file mode 100644 index 00000000000..f3617397cbe --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -0,0 +1,452 @@ +/* + * 25-Jul-1998 Major changes to allow for ip chain table + * + * 3-Jan-2000 Named tables to allow packet selection for different uses. + */ + +/* + * Format of an IP6 firewall descriptor + * + * src, dst, src_mask, dst_mask are always stored in network byte order. + * flags are stored in host byte order (of course). + * Port numbers are stored in HOST byte order. + */ + +#ifndef _IP6_TABLES_H +#define _IP6_TABLES_H + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#endif +#include + +#define IP6T_FUNCTION_MAXNAMELEN 30 +#define IP6T_TABLE_MAXNAMELEN 32 + +/* Yes, Virginia, you have to zero the padding. */ +struct ip6t_ip6 { + /* Source and destination IP6 addr */ + struct in6_addr src, dst; + /* Mask for src and dest IP6 addr */ + struct in6_addr smsk, dmsk; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + + /* ARGH, HopByHop uses 0, so can't do 0 = ANY, + instead IP6T_F_NOPROTO must be set */ + u_int16_t proto; + /* TOS to match iff flags & IP6T_F_TOS */ + u_int8_t tos; + + /* Flags word */ + u_int8_t flags; + /* Inverse flags */ + u_int8_t invflags; +}; + +/* FIXME: If alignment in kernel different from userspace? --RR */ +struct ip6t_entry_match +{ + union { + struct { + u_int16_t match_size; + + /* Used by userspace */ + char name[IP6T_FUNCTION_MAXNAMELEN]; + } user; + struct { + u_int16_t match_size; + + /* Used inside the kernel */ + struct ip6t_match *match; + } kernel; + + /* Total length */ + u_int16_t match_size; + } u; + + unsigned char data[0]; +}; + +struct ip6t_entry_target +{ + union { + struct { + u_int16_t target_size; + + /* Used by userspace */ + char name[IP6T_FUNCTION_MAXNAMELEN]; + } user; + struct { + u_int16_t target_size; + + /* Used inside the kernel */ + struct ip6t_target *target; + } kernel; + + /* Total length */ + u_int16_t target_size; + } u; + + unsigned char data[0]; +}; + +struct ip6t_standard_target +{ + struct ip6t_entry_target target; + int verdict; +}; + +struct ip6t_counters +{ + u_int64_t pcnt, bcnt; /* Packet and byte counters */ +}; + +/* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */ +#define IP6T_F_PROTO 0x01 /* Set if rule cares about upper + protocols */ +#define IP6T_F_TOS 0x02 /* Match the TOS. */ +#define IP6T_F_MASK 0x03 /* All possible flag bits mask. */ + +/* Values for "inv" field in struct ip6t_ip6. */ +#define IP6T_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +#define IP6T_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */ +#define IP6T_INV_TOS 0x04 /* Invert the sense of TOS. */ +#define IP6T_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */ +#define IP6T_INV_DSTIP 0x10 /* Invert the sense of DST OP. */ +#define IP6T_INV_FRAG 0x20 /* Invert the sense of FRAG. */ +#define IP6T_INV_PROTO 0x40 /* Invert the sense of PROTO. */ +#define IP6T_INV_MASK 0x7F /* All possible flag bits mask. */ + +/* This structure defines each of the firewall rules. Consists of 3 + parts which are 1) general IP header stuff 2) match specific + stuff 3) the target to perform if the rule matches */ +struct ip6t_entry +{ + struct ip6t_ip6 ipv6; + + /* Mark with fields that we care about. */ + unsigned int nfcache; + + /* Size of ipt_entry + matches */ + u_int16_t target_offset; + /* Size of ipt_entry + matches + target */ + u_int16_t next_offset; + + /* Back pointer */ + unsigned int comefrom; + + /* Packet and byte counters. */ + struct ip6t_counters counters; + + /* The matches (if any), then the target. */ + unsigned char elems[0]; +}; + +/* + * New IP firewall options for [gs]etsockopt at the RAW IP level. + * Unlike BSD Linux inherits IP options so you don't have to use + * a raw socket for this. Instead we check rights in the calls. */ +#define IP6T_BASE_CTL 64 /* base for firewall socket options */ + +#define IP6T_SO_SET_REPLACE (IP6T_BASE_CTL) +#define IP6T_SO_SET_ADD_COUNTERS (IP6T_BASE_CTL + 1) +#define IP6T_SO_SET_MAX IP6T_SO_SET_ADD_COUNTERS + +#define IP6T_SO_GET_INFO (IP6T_BASE_CTL) +#define IP6T_SO_GET_ENTRIES (IP6T_BASE_CTL + 1) +#define IP6T_SO_GET_MAX IP6T_SO_GET_ENTRIES + +/* CONTINUE verdict for targets */ +#define IP6T_CONTINUE 0xFFFFFFFF + +/* For standard target */ +#define IP6T_RETURN (-NF_MAX_VERDICT - 1) + +/* TCP matching stuff */ +struct ip6t_tcp +{ + u_int16_t spts[2]; /* Source port range. */ + u_int16_t dpts[2]; /* Destination port range. */ + u_int8_t option; /* TCP Option iff non-zero*/ + u_int8_t flg_mask; /* TCP flags mask byte */ + u_int8_t flg_cmp; /* TCP flags compare byte */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "inv" field in struct ipt_tcp. */ +#define IP6T_TCP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define IP6T_TCP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define IP6T_TCP_INV_FLAGS 0x04 /* Invert the sense of TCP flags. */ +#define IP6T_TCP_INV_OPTION 0x08 /* Invert the sense of option test. */ +#define IP6T_TCP_INV_MASK 0x0F /* All possible flags. */ + +/* UDP matching stuff */ +struct ip6t_udp +{ + u_int16_t spts[2]; /* Source port range. */ + u_int16_t dpts[2]; /* Destination port range. */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "invflags" field in struct ipt_udp. */ +#define IP6T_UDP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define IP6T_UDP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define IP6T_UDP_INV_MASK 0x03 /* All possible flags. */ + +/* ICMP matching stuff */ +struct ip6t_icmp +{ + u_int8_t type; /* type to match */ + u_int8_t code[2]; /* range of code */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "inv" field for struct ipt_icmp. */ +#define IP6T_ICMP_INV 0x01 /* Invert the sense of type/code test */ + +/* The argument to IP6T_SO_GET_INFO */ +struct ip6t_getinfo +{ + /* Which table: caller fills this in. */ + char name[IP6T_TABLE_MAXNAMELEN]; + + /* Kernel fills these in. */ + /* Which hook entry points are valid: bitmask */ + unsigned int valid_hooks; + + /* Hook entry points: one per netfilter hook. */ + unsigned int hook_entry[NF_IP6_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_IP6_NUMHOOKS]; + + /* Number of entries */ + unsigned int num_entries; + + /* Size of entries. */ + unsigned int size; +}; + +/* The argument to IP6T_SO_SET_REPLACE. */ +struct ip6t_replace +{ + /* Which table. */ + char name[IP6T_TABLE_MAXNAMELEN]; + + /* Which hook entry points are valid: bitmask. You can't + change this. */ + unsigned int valid_hooks; + + /* Number of entries */ + unsigned int num_entries; + + /* Total size of new entries */ + unsigned int size; + + /* Hook entry points. */ + unsigned int hook_entry[NF_IP6_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_IP6_NUMHOOKS]; + + /* Information about old entries: */ + /* Number of counters (must be equal to current number of entries). */ + unsigned int num_counters; + /* The old entries' counters. */ + struct ip6t_counters *counters; + + /* The entries (hang off end: not really an array). */ + struct ip6t_entry entries[0]; +}; + +/* The argument to IP6T_SO_ADD_COUNTERS. */ +struct ip6t_counters_info +{ + /* Which table. */ + char name[IP6T_TABLE_MAXNAMELEN]; + + unsigned int num_counters; + + /* The counters (actually `number' of these). */ + struct ip6t_counters counters[0]; +}; + +/* The argument to IP6T_SO_GET_ENTRIES. */ +struct ip6t_get_entries +{ + /* Which table: user fills this in. */ + char name[IP6T_TABLE_MAXNAMELEN]; + + /* User fills this in: total entry size. */ + unsigned int size; + + /* The entries. */ + unsigned char entries[0]; +}; + +/* Standard return verdict, or do jump. */ +#define IP6T_STANDARD_TARGET "" +/* Error verdict. */ +#define IP6T_ERROR_TARGET "ERROR" + +/* Helper functions */ +extern __inline__ struct ip6t_entry_target * +ip6t_get_target(struct ip6t_entry *e) +{ + return (void *)e + e->target_offset; +} + +/* fn returns 0 to continue iteration */ +#define IP6T_MATCH_ITERATE(e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ip6t_entry_match *__m; \ + \ + for (__i = sizeof(struct ip6t_entry); \ + __i < (e)->target_offset; \ + __i += __m->u.match_size) { \ + __m = (void *)(e) + __i; \ + \ + __ret = fn(__m , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* fn returns 0 to continue iteration */ +#define IP6T_ENTRY_ITERATE(entries, size, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ip6t_entry *__e; \ + \ + for (__i = 0; __i < (size); __i += __e->next_offset) { \ + __e = (void *)(entries) + __i; \ + \ + __ret = fn(__e , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* + * Main firewall chains definitions and global var's definitions. + */ + +#ifdef __KERNEL__ + +#include +extern void ip6t_init(void) __init; + +struct ip6t_match +{ + struct list_head list; + + const char name[IP6T_FUNCTION_MAXNAMELEN]; + + /* Return true or false: return FALSE and set *hotdrop = 1 to + force immediate packet drop. */ + int (*match)(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop); + + /* Called when user tries to insert an entry of this type. */ + /* Should return true or false. */ + int (*checkentry)(const char *tablename, + const struct ip6t_ip6 *ip, + void *matchinfo, + unsigned int matchinfosize, + unsigned int hook_mask); + + /* Called when entry of this type deleted. */ + void (*destroy)(void *matchinfo, unsigned int matchinfosize); + + /* Set this to THIS_MODULE if you are a module, otherwise NULL */ + struct module *me; +}; + +/* Registration hooks for targets. */ +struct ip6t_target +{ + struct list_head list; + + const char name[IP6T_FUNCTION_MAXNAMELEN]; + + /* Returns verdict. */ + unsigned int (*target)(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userdata); + + /* Called when user tries to insert an entry of this type: + hook_mask is a bitmask of hooks from which it can be + called. */ + /* Should return true or false. */ + int (*checkentry)(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask); + + /* Called when entry of this type deleted. */ + void (*destroy)(void *targinfo, unsigned int targinfosize); + + /* Set this to THIS_MODULE if you are a module, otherwise NULL */ + struct module *me; +}; + +extern int ip6t_register_target(struct ip6t_target *target); +extern void ip6t_unregister_target(struct ip6t_target *target); + +extern int ip6t_register_match(struct ip6t_match *match); +extern void ip6t_unregister_match(struct ip6t_match *match); + +/* Furniture shopping... */ +struct ip6t_table +{ + struct list_head list; + + /* A unique name... */ + char name[IP6T_TABLE_MAXNAMELEN]; + + /* Seed table: copied in register_table */ + struct ip6t_replace *table; + + /* What hooks you will enter on */ + unsigned int valid_hooks; + + /* Lock for the curtain */ + rwlock_t lock; + + /* Man behind the curtain... */ + struct ip6t_table_info *private; +}; + +extern int ip6t_register_table(struct ip6t_table *table); +extern void ip6t_unregister_table(struct ip6t_table *table); +extern unsigned int ip6t_do_table(struct sk_buff **pskb, + unsigned int hook, + const struct net_device *in, + const struct net_device *out, + struct ip6t_table *table, + void *userdata); + +#define IP6T_ALIGN(s) (((s) + (__alignof__(struct ip6t_entry)-1)) & ~(__alignof__(struct ip6t_entry)-1)) + +#endif /*__KERNEL__*/ +#endif /* _IP6_TABLES_H */ diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 0c84b76c4c7..ddd0563a558 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -98,7 +99,6 @@ do { \ /* Inode Flags */ #define NFS_USE_READDIRPLUS(inode) ((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0) -#define NFS_MONOTONE_COOKIES(inode) ((NFS_SERVER(inode)->flags & NFS_NONMONOTONE_COOKIES) ? 0 : 1) /* * These are the default flags for swap requests @@ -155,6 +155,17 @@ extern struct inode_operations nfs_file_inode_operations; extern struct file_operations nfs_file_operations; extern struct address_space_operations nfs_file_aops; +static __inline__ struct rpc_cred * +nfs_file_cred(struct file *file) +{ + struct rpc_cred *cred = (struct rpc_cred *)(file->private_data); +#ifdef RPC_DEBUG + if (cred && cred->cr_magic != RPCAUTH_CRED_MAGIC) + BUG(); +#endif + return cred; +} + /* * linux/fs/nfs/dir.c */ diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h index 8e11ef368f6..7b7df5b06f2 100644 --- a/include/linux/nfs_mount.h +++ b/include/linux/nfs_mount.h @@ -54,11 +54,4 @@ struct nfs_mount_data { #define NFS_MOUNT_NONLM 0x0200 /* 3 */ #define NFS_MOUNT_FLAGMASK 0xFFFF -/* - * Private flags - not to be set by mount program - */ -#ifdef __KERNEL__ -#define NFS_NONMONOTONE_COOKIES 0x00010000 -#endif /* __KERNEL__ */ - #endif diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 475fced7c0d..5f6572b2221 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -40,7 +40,7 @@ struct nfs_page { #define NFS_WBACK_BUSY(req) ((req)->wb_flags & PG_BUSY) -extern struct nfs_page *nfs_create_request(struct dentry *dentry, +extern struct nfs_page *nfs_create_request(struct file *file, struct page *page, unsigned int offset, unsigned int count); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index eca3e1b2d57..63c3e1c7bab 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -106,7 +106,6 @@ struct nfs_writeres { * Argument struct for decode_entry function */ struct nfs_entry { - struct page * page; __u64 ino; __u64 cookie, prev_cookie; @@ -115,8 +114,6 @@ struct nfs_entry { int eof; struct nfs_fh fh; struct nfs_fattr fattr; - unsigned long offset, - prev; }; /* @@ -326,10 +323,10 @@ struct nfs_rpc_ops { struct nfs_fh *, struct nfs_fattr *); int (*access) (struct dentry *, int , int); int (*readlink)(struct dentry *, void *, unsigned int); - int (*read) (struct dentry *, struct nfs_fattr *, + int (*read) (struct file *, struct nfs_fattr *, int, loff_t, unsigned int, void *buffer, int *eofp); - int (*write) (struct dentry *, struct nfs_fattr *, + int (*write) (struct file *, struct nfs_fattr *, int, loff_t, unsigned int, void *buffer, struct nfs_writeverf *verfp); int (*commit) (struct dentry *, struct nfs_fattr *, @@ -346,7 +343,7 @@ struct nfs_rpc_ops { int (*mkdir) (struct dentry *, struct qstr *, struct iattr *, struct nfs_fh *, struct nfs_fattr *); int (*rmdir) (struct dentry *, struct qstr *); - int (*readdir) (struct dentry *, u64 cookie, void *, unsigned int, + int (*readdir) (struct file *, u64 cookie, void *, unsigned int, int); int (*mknod) (struct dentry *, struct qstr *, struct iattr *, dev_t, struct nfs_fh *, struct nfs_fattr *); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index bfdf6c03a06..499afe2d617 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -790,9 +790,12 @@ #define PCI_DEVICE_ID_RENDITION_VERITE 0x0001 #define PCI_DEVICE_ID_RENDITION_VERITE2100 0x2000 -#define PCI_VENDOR_ID_RCC 0x1166 -#define PCI_DEVICE_ID_RCC_HE 0x0008 -#define PCI_DEVICE_ID_RCC_LE 0x0009 +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_DEVICE_ID_SERVERWORKS_HE 0x0008 +#define PCI_DEVICE_ID_SERVERWORKS_LE 0x0009 +#define PCI_DEVICE_ID_SERVERWORKS_CIOB30 0x0010 +#define PCI_DEVICE_ID_SERVERWORKS_CMIC_HE 0x0011 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 #define PCI_VENDOR_ID_TOSHIBA 0x1179 #define PCI_DEVICE_ID_TOSHIBA_601 0x0601 @@ -1176,7 +1179,15 @@ #define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 #define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 #define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 +#define PCI_DEVICE_ID_INTEL_82440MX_1 0x7194 +#define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198 +#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199 +#define PCI_DEVICE_ID_INTEL_82443MX_2 0x719a +#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b +#define PCI_DEVICE_ID_INTEL_82372FB_0 0x7600 #define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601 +#define PCI_DEVICE_ID_INTEL_82372FB_2 0x7602 +#define PCI_DEVICE_ID_INTEL_82372FB_3 0x7603 #define PCI_DEVICE_ID_INTEL_82454GX 0x84c4 #define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 #define PCI_DEVICE_ID_INTEL_82451NX 0x84ca @@ -1254,4 +1265,7 @@ #define PCI_DEVICE_ID_ARK_STINGARK 0xa099 #define PCI_DEVICE_ID_ARK_2000MT 0xa0a1 - +#define PCI_VENDOR_ID_SIS 0x1039 +#define PCI_DEVICE_ID_SIS_300 0x0300 +#define PCI_DEVICE_ID_SIS_540 0x5300 +#define PCI_DEVICE_ID_SIS_630 0x6300 diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index b7621b293f5..d0487c3df45 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -90,8 +90,6 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry); void proc_pid_delete_inode(struct inode *inode); int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir); -extern int proc_register(struct proc_dir_entry *, struct proc_dir_entry *); - extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent); extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent); @@ -167,7 +165,6 @@ extern inline void proc_net_remove(const char *name) #else -extern inline int proc_register(struct proc_dir_entry *a, struct proc_dir_entry *b) { return 0; } extern inline struct proc_dir_entry *proc_net_create(const char *name, mode_t mode, get_info_t *get_info) {return NULL;} extern inline void proc_net_remove(const char *name) {} diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h index 5df1ef295b2..9eb6c28bdf6 100644 --- a/include/linux/smb_fs.h +++ b/include/linux/smb_fs.h @@ -130,7 +130,6 @@ int smb_get_wsize(struct smb_sb_info *); int smb_newconn(struct smb_sb_info *, struct smb_conn_opt *); int smb_errno(struct smb_sb_info *); int smb_close(struct inode *); -void smb_close_dentry(struct dentry *); int smb_close_fileid(struct dentry *, __u16); int smb_open(struct dentry *, int); int smb_proc_read(struct dentry *, off_t, int, char *); diff --git a/include/linux/smb_fs_i.h b/include/linux/smb_fs_i.h index 4aea02c3cc7..8dca9066b9a 100644 --- a/include/linux/smb_fs_i.h +++ b/include/linux/smb_fs_i.h @@ -29,6 +29,7 @@ struct smb_inode_info { __u16 cache_valid; /* dircache valid? */ unsigned long oldmtime; /* last time refreshed */ unsigned long closed; /* timestamp when closed */ + unsigned openers; /* number of fileid users */ }; #endif diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index d106c881abb..242be9730f1 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -25,6 +25,9 @@ struct rpc_cred { unsigned long cr_expire; /* when to gc */ unsigned short cr_count; /* ref count */ unsigned short cr_flags; /* various flags */ +#ifdef RPC_DEBUG + unsigned long cr_magic; /* 0x0f4aa4f0 */ +#endif uid_t cr_uid; @@ -34,6 +37,8 @@ struct rpc_cred { #define RPCAUTH_CRED_UPTODATE 0x0002 #define RPCAUTH_CRED_DEAD 0x0004 +#define RPCAUTH_CRED_MAGIC 0x0f4aa4f0 + /* * Client authentication handle */ diff --git a/include/linux/umsdos_fs.p b/include/linux/umsdos_fs.p index de436f0e8ff..76436a479bc 100644 --- a/include/linux/umsdos_fs.p +++ b/include/linux/umsdos_fs.p @@ -108,11 +108,3 @@ int UMSDOS_rename (struct inode *old_dir, /* rdir.c 22/03/95 03.31.42 */ struct dentry *umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo); struct dentry *UMSDOS_rlookup (struct inode *dir, struct dentry *dentry); - -/* symlink.c 23/01/95 03.38.30 */ - -/* check.c */ -void checkd_inode (struct inode *inode); -void check_inode (struct inode *inode); -void check_dentry (struct dentry *dentry); -void check_dentry_path (struct dentry *dentry, const char *desc); diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 95eaa937a01..7fa98fcb5a9 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -31,6 +31,8 @@ #ifndef _LINUX_USBDEVICE_FS_H #define _LINUX_USBDEVICE_FS_H +#include + /* --------------------------------------------------------------------- */ #define USBDEVICE_SUPER_MAGIC 0x9fa2 diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index d360d41bc75..bba1e159e27 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -3,6 +3,7 @@ #include #include +#include #include @@ -17,16 +18,49 @@ struct vm_struct { struct vm_struct * next; }; -struct vm_struct * get_vm_area(unsigned long size, unsigned long flags); -void vfree(void * addr); -void * vmalloc_prot(unsigned long size, pgprot_t prot); -void * vmalloc_uncached(unsigned long size); -extern void * vmalloc(unsigned long size); +extern struct vm_struct * get_vm_area (unsigned long size, unsigned long flags); +extern void vfree(void * addr); +extern void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot); +extern long vread(char *buf, char *addr, unsigned long count); +extern void vmfree_area_pages(unsigned long address, unsigned long size); +extern int vmalloc_area_pages(unsigned long address, unsigned long size, + int gfp_mask, pgprot_t prot); +extern struct vm_struct * vmlist; + + +/* + * Allocate any pages + */ + +static inline void * vmalloc (unsigned long size) +{ + return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL); +} + +/* + * Allocate ISA addressable pages for broke crap + */ + +static inline void * vmalloc_dma (unsigned long size) +{ + return __vmalloc(size, GFP_KERNEL|GFP_DMA, PAGE_KERNEL); +} + +/* + * vmalloc 32bit PA addressable pages - eg for PCI 32bit devices + */ + +static inline void * vmalloc_32(unsigned long size) +{ + return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL); +} -long vread(char *buf, char *addr, unsigned long count); -void vmfree_area_pages(unsigned long address, unsigned long size); -int vmalloc_area_pages(unsigned long address, unsigned long size, pgprot_t prot); +/* + * vmlist_lock is a read-write spinlock that protects vmlist + * Used in mm/vmalloc.c (get_vm_area() and vfree()) and fs/proc/kcore.c. + */ +extern rwlock_t vmlist_lock; extern struct vm_struct * vmlist; #endif diff --git a/include/linux/wait.h b/include/linux/wait.h index 6d8f5dae61a..6ac1f0e88fe 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -27,7 +27,7 @@ extern int printk(const char *fmt, ...); #define WQ_BUG() do { \ printk("wq bug, forcing oops.\n"); \ - *(int*)0 = 0; \ + BUG(); \ } while (0) #define CHECK_MAGIC(x) if (x != (long)&(x)) \ diff --git a/include/linux/wrapper.h b/include/linux/wrapper.h index bc719bfc50e..bcfaff073a6 100644 --- a/include/linux/wrapper.h +++ b/include/linux/wrapper.h @@ -5,7 +5,6 @@ #define inode_handle struct inode #define select_table_handle select_table #define vm_area_handle struct vm_area_struct -#define file_operation_handle file_operations #define connect_wrapper(x) 0 #define current_got_fatal_signal() (signal_pending(current)) diff --git a/include/net/slhc.h b/include/net/slhc.h deleted file mode 100644 index c7b39db5560..00000000000 --- a/include/net/slhc.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __NET_SLHC_H -#define __NET_SLHC_H - -extern void slhc_install(void); - -#endif diff --git a/init/main.c b/init/main.c index 3cd42d9e8ff..32659ba5d8e 100644 --- a/init/main.c +++ b/init/main.c @@ -414,9 +414,18 @@ static int __init debug_kernel(char *str) return 1; } +static int __init quiet_kernel(char *str) +{ + if (*str) + return 0; + console_loglevel = 4; + return 1; +} + __setup("ro", readonly); __setup("rw", readwrite); __setup("debug", debug_kernel); +__setup("quiet", quiet_kernel); /* * This is a simple kernel command line parsing function: it parses @@ -741,8 +750,7 @@ static void __init do_basic_setup(void) #ifdef CONFIG_BLK_DEV_INITRD root_mountflags = real_root_mountflags; - if (mount_initrd && ROOT_DEV != real_root_dev - && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) { + if (mount_initrd && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) { int error; int i, pid; diff --git a/ipc/shm.c b/ipc/shm.c index 62076d18ac4..d6aaf031125 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -541,8 +541,6 @@ static int shm_unlink (struct inode *dir, struct dentry *dent) if (dent->d_name.len == SHM_FMT_LEN && memcmp (SHM_FMT, dent->d_name.name, SHM_FMT_LEN - 8) == 0) d_drop (dent); - else - d_delete (dent); return 0; } @@ -1251,6 +1249,8 @@ static int shm_remove_name(int id) struct inode *inode = dir->d_inode; down(&inode->i_zombie); error = shm_unlink(inode, dentry); + if (!error) + d_delete(dentry); up(&inode->i_zombie); dput(dentry); } @@ -1468,7 +1468,7 @@ static int shm_swap_preop(swp_entry_t *swap_entry) } /* - * Goes through counter = (shm_rss >> prio) present shm pages. + * Goes through counter = (shm_rss / (prio + 1)) present shm pages. */ static unsigned long swap_id; /* currently being swapped */ static unsigned long swap_idx; /* next to swap */ @@ -1483,7 +1483,7 @@ int shm_swap (int prio, int gfp_mask) struct page * page_map; zshm_swap(prio, gfp_mask); - counter = shm_rss >> prio; + counter = shm_rss / (prio + 1); if (!counter) return 0; if (shm_swap_preop(&swap_entry)) @@ -1809,7 +1809,7 @@ static void zshm_swap (int prio, int gfp_mask) int counter; struct page * page_map; - counter = zshm_rss >> prio; + counter = zshm_rss / (prio + 1); if (!counter) return; next: diff --git a/ipc/util.c b/ipc/util.c index 8771f73d11a..29819efbb02 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -159,25 +159,19 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id) void* ipc_alloc(int size) { void* out; - if(size > PAGE_SIZE) { - lock_kernel(); + if(size > PAGE_SIZE) out = vmalloc(size); - unlock_kernel(); - } else { + else out = kmalloc(size, GFP_KERNEL); - } return out; } void ipc_free(void* ptr, int size) { - if(size > PAGE_SIZE) { - lock_kernel(); + if(size > PAGE_SIZE) vfree(ptr); - unlock_kernel(); - } else { + else kfree(ptr); - } } /* diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index 4060c802ac9..3f3b5fc16fd 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -23,7 +23,7 @@ struct exec_domain default_exec_domain = { }; static struct exec_domain *exec_domains = &default_exec_domain; -static spinlock_t exec_domains_lock = SPIN_LOCK_UNLOCKED; +static rwlock_t exec_domains_lock = RW_LOCK_UNLOCKED; static asmlinkage void no_lcall7(int segment, struct pt_regs * regs) { @@ -48,15 +48,15 @@ static struct exec_domain *lookup_exec_domain(unsigned long personality) unsigned long pers = personality & PER_MASK; struct exec_domain *it; - spin_lock(&exec_domains_lock); + read_lock(&exec_domains_lock); for (it=exec_domains; it; it=it->next) if (pers >= it->pers_low && pers <= it->pers_high) { if (!try_inc_mod_count(it->module)) continue; - spin_unlock(&exec_domains_lock); + read_unlock(&exec_domains_lock); return it; } - spin_unlock(&exec_domains_lock); + read_unlock(&exec_domains_lock); /* Should never get this far. */ printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers); @@ -71,15 +71,15 @@ int register_exec_domain(struct exec_domain *it) return -EINVAL; if (it->next) return -EBUSY; - spin_lock(&exec_domains_lock); + write_lock(&exec_domains_lock); for (tmp=exec_domains; tmp; tmp=tmp->next) if (tmp == it) { - spin_unlock(&exec_domains_lock); + write_unlock(&exec_domains_lock); return -EBUSY; } it->next = exec_domains; exec_domains = it; - spin_unlock(&exec_domains_lock); + write_unlock(&exec_domains_lock); return 0; } @@ -88,17 +88,17 @@ int unregister_exec_domain(struct exec_domain *it) struct exec_domain ** tmp; tmp = &exec_domains; - spin_lock(&exec_domains_lock); + write_lock(&exec_domains_lock); while (*tmp) { if (it == *tmp) { *tmp = it->next; it->next = NULL; - spin_unlock(&exec_domains_lock); + write_unlock(&exec_domains_lock); return 0; } tmp = &(*tmp)->next; } - spin_unlock(&exec_domains_lock); + write_unlock(&exec_domains_lock); return -EINVAL; } @@ -148,11 +148,11 @@ int get_exec_domain_list(char * page) int len = 0; struct exec_domain * e; - spin_lock(&exec_domains_lock); + read_lock(&exec_domains_lock); for (e=exec_domains; e && len < PAGE_SIZE - 80; e=e->next) len += sprintf(page+len, "%d-%d\t%-16s\t[%s]\n", e->pers_low, e->pers_high, e->name, e->module ? e->module->name : "kernel"); - spin_unlock(&exec_domains_lock); + read_unlock(&exec_domains_lock); return len; } diff --git a/kernel/fork.c b/kernel/fork.c index aa350672a62..4300d242d23 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -674,7 +674,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) { p->p_opptr = current; - if (!(current->flags & PF_PTRACED)) + if (!(p->flags & PF_PTRACED)) p->p_pptr = current; } p->p_cptr = NULL; diff --git a/kernel/ksyms.c b/kernel/ksyms.c index af22705eced..4f60cf8dc41 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -118,6 +118,7 @@ EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kfree_s); EXPORT_SYMBOL(vmalloc); EXPORT_SYMBOL(vfree); +EXPORT_SYMBOL(__vmalloc); EXPORT_SYMBOL(mem_map); EXPORT_SYMBOL(remap_page_range); EXPORT_SYMBOL(max_mapnr); @@ -218,6 +219,9 @@ EXPORT_SYMBOL(posix_unblock_lock); EXPORT_SYMBOL(locks_mandatory_area); EXPORT_SYMBOL(dput); EXPORT_SYMBOL(have_submounts); +EXPORT_SYMBOL(d_genocide); +EXPORT_SYMBOL(d_find_alias); +EXPORT_SYMBOL(d_prune_aliases); EXPORT_SYMBOL(prune_dcache); EXPORT_SYMBOL(shrink_dcache_sb); EXPORT_SYMBOL(shrink_dcache_parent); @@ -248,6 +252,7 @@ EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(block_fsync); EXPORT_SYMBOL(block_symlink); EXPORT_SYMBOL(vfs_readdir); +EXPORT_SYMBOL(dcache_readdir); /* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */ EXPORT_SYMBOL(default_llseek); diff --git a/mm/filemap.c b/mm/filemap.c index 81f7d7ab90d..b1e2b8547fe 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -244,14 +244,19 @@ repeat: spin_unlock(&pagecache_lock); } +/* + * nr_dirty represents the number of dirty pages that we will write async + * before doing sync writes. We can only do sync writes if we can + * wait for IO (__GFP_IO set). + */ int shrink_mmap(int priority, int gfp_mask) { - int ret = 0, count; - LIST_HEAD(old); - struct list_head * page_lru, * dispose; + int ret = 0, count, nr_dirty; + struct list_head * page_lru; struct page * page = NULL; count = nr_lru_pages / (priority + 1); + nr_dirty = priority; /* we need pagemap_lru_lock for list_del() ... subtle code below */ spin_lock(&pagemap_lru_lock); @@ -259,25 +264,10 @@ int shrink_mmap(int priority, int gfp_mask) page = list_entry(page_lru, struct page, lru); list_del(page_lru); - dispose = &lru_cache; if (PageTestandClearReferenced(page)) goto dispose_continue; count--; - - /* - * I'm ambivalent on this one.. Should we try to - * maintain LRU on the LRU list, and put pages that - * are old at the end of the queue, even if that - * means that we'll re-scan then again soon and - * often waste CPU time? Or should be just let any - * pages we do not want to touch now for one reason - * or another percolate to be "young"? - * - dispose = &old; - * - */ - /* * Avoid unscalable SMP locking for pages we can * immediate tell are untouchable.. @@ -303,7 +293,8 @@ int shrink_mmap(int priority, int gfp_mask) * of zone - it's old. */ if (page->buffers) { - if (!try_to_free_buffers(page)) + int wait = ((gfp_mask & __GFP_IO) && (nr_dirty-- < 0)); + if (!try_to_free_buffers(page, wait)) goto unlock_continue; /* page was locked, inode can't go away under us */ if (!page->mapping) { @@ -362,7 +353,7 @@ unlock_continue: UnlockPage(page); page_cache_release(page); dispose_continue: - list_add(page_lru, dispose); + list_add(page_lru, &lru_cache); } goto out; @@ -377,8 +368,6 @@ made_buffer_progress: nr_lru_pages--; out: - list_splice(&old, lru_cache.prev); - spin_unlock(&pagemap_lru_lock); return ret; @@ -2319,7 +2308,8 @@ out: return error; } -struct page *read_cache_page(struct address_space *mapping, +static inline +struct page *__read_cache_page(struct address_space *mapping, unsigned long index, int (*filler)(void *,struct page*), void *data) @@ -2350,6 +2340,35 @@ repeat: return page; } +/* + * Read into the page cache. If a page already exists, + * and Page_Uptodate() is not set, try to fill the page. + */ +struct page *read_cache_page(struct address_space *mapping, + unsigned long index, + int (*filler)(void *,struct page*), + void *data) +{ + struct page *page = __read_cache_page(mapping, index, filler, data); + int err; + + if (IS_ERR(page) || Page_Uptodate(page)) + goto out; + + lock_page(page); + if (Page_Uptodate(page)) { + UnlockPage(page); + goto out; + } + err = filler(data, page); + if (err < 0) { + page_cache_release(page); + page = ERR_PTR(err); + } + out: + return page; +} + static inline struct page * __grab_cache_page(struct address_space *mapping, unsigned long index, struct page **cached_page) { diff --git a/mm/highmem.c b/mm/highmem.c index 11e03521eb5..7c9dbc69541 100644 --- a/mm/highmem.c +++ b/mm/highmem.c @@ -60,7 +60,7 @@ struct page * prepare_highmem_swapout(struct page * page) * ok, we can just forget about our highmem page since * we stored its data into the new regular_page. */ - __free_page(page); + page_cache_release(page); new_page = mem_map + MAP_NR(regular_page); LockPage(new_page); return new_page; @@ -78,7 +78,7 @@ struct page * replace_with_highmem(struct page * page) if (!highpage) return page; if (!PageHighMem(highpage)) { - __free_page(highpage); + page_cache_release(highpage); return page; } @@ -94,7 +94,7 @@ struct page * replace_with_highmem(struct page * page) * We can just forget the old page since * we stored its data into the new highmem-page. */ - __free_page(page); + page_cache_release(page); return highpage; } diff --git a/mm/memory.c b/mm/memory.c index e5a54892590..de7dc07f8c9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -156,7 +156,7 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, unsigned long address = vma->vm_start; unsigned long end = vma->vm_end; unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; - + src_pgd = pgd_offset(src, address)-1; dst_pgd = pgd_offset(dst, address)-1; @@ -878,7 +878,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct * vma, new_page = old_page; } spin_unlock(&mm->page_table_lock); - __free_page(new_page); + page_cache_release(new_page); return 1; /* Minor fault */ bad_wp_page: @@ -1022,7 +1022,7 @@ void swapin_readahead(swp_entry_t entry) /* Ok, do the async read-ahead now */ new_page = read_swap_cache_async(SWP_ENTRY(SWP_TYPE(entry), offset), 0); if (new_page != NULL) - __free_page(new_page); + page_cache_release(new_page); swap_free(SWP_ENTRY(SWP_TYPE(entry), offset)); } return; diff --git a/mm/slab.c b/mm/slab.c index 7dbc443fbea..64f33cb33ed 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1751,7 +1751,7 @@ bad_ptr: #if 1 /* FORCE A KERNEL DUMP WHEN THIS HAPPENS. SPEAK IN ALL CAPS. GET THE CALL CHAIN. */ -*(int *) 0 = 0; +BUG(); #endif null_ptr: diff --git a/mm/swap_state.c b/mm/swap_state.c index 347f87372e1..2405aba2ffb 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -136,7 +136,7 @@ void free_page_and_swap_cache(struct page *page) } UnlockPage(page); } - page_cache_release(page); + page_cache_release(page); } diff --git a/mm/swapfile.c b/mm/swapfile.c index c4b4733b7dd..55ef476a38a 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -377,7 +377,7 @@ static int try_to_unuse(unsigned int type) page we've been using. */ if (PageSwapCache(page)) delete_from_swap_cache(page); - __free_page(page); + page_cache_release(page); /* * Check for and clear any overflowed swap map counts. */ diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 99510c53be5..57f3ca56cc7 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -3,14 +3,17 @@ * * Copyright (C) 1993 Linus Torvalds * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 + * SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian , May 2000 */ #include #include +#include #include #include +rwlock_t vmlist_lock = RW_LOCK_UNLOCKED; struct vm_struct * vmlist = NULL; static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size) @@ -87,7 +90,8 @@ void vmfree_area_pages(unsigned long address, unsigned long size) flush_tlb_all(); } -static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned long size, pgprot_t prot) +static inline int alloc_area_pte (pte_t * pte, unsigned long address, + unsigned long size, int gfp_mask, pgprot_t prot) { unsigned long end; @@ -99,7 +103,7 @@ static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned lo struct page * page; if (!pte_none(*pte)) printk(KERN_ERR "alloc_area_pte: page already exists\n"); - page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + page = alloc_page(gfp_mask); if (!page) return -ENOMEM; set_pte(pte, mk_pte(page, prot)); @@ -109,7 +113,7 @@ static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned lo return 0; } -static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, pgprot_t prot) +static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, int gfp_mask, pgprot_t prot) { unsigned long end; @@ -121,7 +125,7 @@ static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo pte_t * pte = pte_alloc_kernel(pmd, address); if (!pte) return -ENOMEM; - if (alloc_area_pte(pte, address, end - address, prot)) + if (alloc_area_pte(pte, address, end - address, gfp_mask, prot)) return -ENOMEM; address = (address + PMD_SIZE) & PMD_MASK; pmd++; @@ -129,7 +133,8 @@ static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo return 0; } -int vmalloc_area_pages(unsigned long address, unsigned long size, pgprot_t prot) +inline int vmalloc_area_pages (unsigned long address, unsigned long size, + int gfp_mask, pgprot_t prot) { pgd_t * dir; unsigned long end = address + size; @@ -139,11 +144,11 @@ int vmalloc_area_pages(unsigned long address, unsigned long size, pgprot_t prot) do { pmd_t *pmd; pgd_t olddir = *dir; - + pmd = pmd_alloc_kernel(dir, address); if (!pmd) return -ENOMEM; - if (alloc_area_pmd(pmd, address, end - address, prot)) + if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot)) return -ENOMEM; if (pgd_val(olddir) != pgd_val(*dir)) set_pgdir(address, *dir); @@ -163,11 +168,13 @@ struct vm_struct * get_vm_area(unsigned long size, unsigned long flags) if (!area) return NULL; addr = VMALLOC_START; + write_lock(&vmlist_lock); for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { if (size + addr < (unsigned long) tmp->addr) break; addr = tmp->size + (unsigned long) tmp->addr; if (addr > VMALLOC_END-size) { + write_unlock(&vmlist_lock); kfree(area); return NULL; } @@ -177,6 +184,7 @@ struct vm_struct * get_vm_area(unsigned long size, unsigned long flags) area->size = size + PAGE_SIZE; area->next = *p; *p = area; + write_unlock(&vmlist_lock); return area; } @@ -190,18 +198,21 @@ void vfree(void * addr) printk(KERN_ERR "Trying to vfree() bad address (%p)\n", addr); return; } + write_lock(&vmlist_lock); for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { if (tmp->addr == addr) { *p = tmp->next; vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); kfree(tmp); + write_unlock(&vmlist_lock); return; } } + write_unlock(&vmlist_lock); printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n", addr); } -void * vmalloc_prot(unsigned long size, pgprot_t prot) +void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot) { void * addr; struct vm_struct *area; @@ -217,7 +228,7 @@ void * vmalloc_prot(unsigned long size, pgprot_t prot) return NULL; } addr = area->addr; - if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, prot)) { + if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, gfp_mask, prot)) { vfree(addr); BUG(); return NULL; @@ -225,11 +236,6 @@ void * vmalloc_prot(unsigned long size, pgprot_t prot) return addr; } -void * vmalloc(unsigned long size) -{ - return vmalloc_prot (size, PAGE_KERNEL); -} - long vread(char *buf, char *addr, unsigned long count) { struct vm_struct *tmp; @@ -240,6 +246,7 @@ long vread(char *buf, char *addr, unsigned long count) if ((unsigned long) addr + count < count) count = -(unsigned long) addr; + read_lock(&vmlist_lock); for (tmp = vmlist; tmp; tmp = tmp->next) { vaddr = (char *) tmp->addr; if (addr >= vaddr + tmp->size - PAGE_SIZE) @@ -247,7 +254,7 @@ long vread(char *buf, char *addr, unsigned long count) while (addr < vaddr) { if (count == 0) goto finished; - put_user('\0', buf); + *buf = '\0'; buf++; addr++; count--; @@ -256,12 +263,13 @@ long vread(char *buf, char *addr, unsigned long count) do { if (count == 0) goto finished; - put_user(*addr, buf); + *buf = *addr; buf++; addr++; count--; } while (--n > 0); } finished: + read_unlock(&vmlist_lock); return buf - buf_start; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 8734cc45968..1919c096122 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -48,6 +48,9 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un if ((page-mem_map >= max_mapnr) || PageReserved(page)) goto out_failed; + if (mm->swap_cnt) + mm->swap_cnt--; + /* Don't look at this pte if it's been accessed recently. */ if (pte_young(pte)) { /* @@ -76,10 +79,9 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un set_pte(page_table, swp_entry_to_pte(entry)); drop_pte: UnlockPage(page); - mm->swap_cnt--; vma->vm_mm->rss--; flush_tlb_page(vma, address); - __free_page(page); + page_cache_release(page); goto out_failed; } @@ -142,7 +144,6 @@ drop_pte: struct file *file = vma->vm_file; if (file) get_file(file); pte_clear(page_table); - mm->swap_cnt--; vma->vm_mm->rss--; flush_tlb_page(vma, address); vmlist_access_unlock(vma->vm_mm); @@ -151,7 +152,7 @@ drop_pte: if (file) fput(file); if (!error) goto out_free_success; - __free_page(page); + page_cache_release(page); return error; } @@ -174,7 +175,6 @@ drop_pte: add_to_swap_cache(page, entry); /* Put the swap entry into the pte after the page is in swapcache */ - mm->swap_cnt--; vma->vm_mm->rss--; set_pte(page_table, swp_entry_to_pte(entry)); flush_tlb_page(vma, address); @@ -184,7 +184,7 @@ drop_pte: rw_swap_page(WRITE, page, 0); out_free_success: - __free_page(page); + page_cache_release(page); return 1; out_swap_free: swap_free(entry); @@ -363,7 +363,7 @@ static int swap_out(unsigned int priority, int gfp_mask) * Think of swap_cnt as a "shadow rss" - it tells us which process * we want to page out (always try largest first). */ - counter = (nr_threads << 1) >> (priority >> 1); + counter = (nr_threads << 2) >> (priority >> 2); if (counter < 1) counter = 1; @@ -430,16 +430,17 @@ out: * latency. */ #define FREE_COUNT 8 -#define SWAP_COUNT 8 +#define SWAP_COUNT 16 static int do_try_to_free_pages(unsigned int gfp_mask) { int priority; int count = FREE_COUNT; + int swap_count; /* Always trim SLAB caches when memory gets low. */ kmem_cache_reap(gfp_mask); - priority = 6; + priority = 64; do { while (shrink_mmap(priority, gfp_mask)) { if (!--count) @@ -471,12 +472,11 @@ static int do_try_to_free_pages(unsigned int gfp_mask) * put in the swap cache), so we must not count this * as a "count" success. */ - { - int swap_count = SWAP_COUNT; - while (swap_out(priority, gfp_mask)) - if (--swap_count < 0) - break; - } + swap_count = SWAP_COUNT; + while (swap_out(priority, gfp_mask)) + if (--swap_count < 0) + break; + } while (--priority >= 0); /* Always end on a shrink_mmap.. */ @@ -484,8 +484,8 @@ static int do_try_to_free_pages(unsigned int gfp_mask) if (!--count) goto done; } - - return 0; + /* We return 1 if we are freed some page */ + return (count != FREE_COUNT); done: return 1; @@ -538,16 +538,18 @@ int kswapd(void *unused) int i; for(i = 0; i < MAX_NR_ZONES; i++) { zone_t *zone = pgdat->node_zones+ i; + if (tsk->need_resched) + schedule(); if (!zone->size || !zone->zone_wake_kswapd) continue; - something_to_do = 1; + if (zone->free_pages < zone->pages_low) + something_to_do = 1; do_try_to_free_pages(GFP_KSWAPD); } - run_task_queue(&tq_disk); pgdat = pgdat->node_next; } while (pgdat); - if (tsk->need_resched || !something_to_do) { + if (!something_to_do) { tsk->state = TASK_INTERRUPTIBLE; interruptible_sleep_on(&kswapd_wait); } diff --git a/net/Makefile b/net/Makefile index afdfbb7129d..dce68b62709 100644 --- a/net/Makefile +++ b/net/Makefile @@ -10,7 +10,7 @@ MOD_SUB_DIRS := ipv4 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \ netrom rose lapb x25 wanrouter netlink sched packet sunrpc \ - econet irda decnet atm khttpd ipv4/netfilter + econet irda decnet atm khttpd ipv4/netfilter ipv6/netfilter SUB_DIRS := core ethernet sched MOD_LIST_NAME := NET_MISC_MODULES @@ -36,9 +36,16 @@ endif ifeq ($(CONFIG_IPV6),y) SUB_DIRS += ipv6 +ifeq ($(CONFIG_NETFILTER),y) +SUB_DIRS += ipv6/netfilter +MOD_SUB_DIRS += ipv6/netfilter +endif else ifeq ($(CONFIG_IPV6),m) MOD_SUB_DIRS += ipv6 + ifeq ($(CONFIG_NETFILTER),y) + MOD_SUB_DIRS += ipv6/netfilter + endif endif endif diff --git a/net/core/dev.c b/net/core/dev.c index ff5ff69dd71..bd3670a93c1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -79,7 +79,6 @@ #include #include #include -#include #include #include #include diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 234e9c18254..173506c3d44 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.72 2000/04/13 00:55:54 davem Exp $ + * Version: $Id: skbuff.c,v 1.73 2000/05/22 07:29:44 davem Exp $ * * Fixes: * Alan Cox : Fixed the worst of the load balancer bugs. @@ -90,7 +90,7 @@ void skb_over_panic(struct sk_buff *skb, int sz, void *here) { printk("skput:over: %p:%d put:%d dev:%s", here, skb->len, sz, skb->dev ? skb->dev->name : ""); - *(int*)0 = 0; + BUG(); } /** @@ -107,7 +107,7 @@ void skb_under_panic(struct sk_buff *skb, int sz, void *here) { printk("skput:under: %p:%d put:%d dev:%s", here, skb->len, sz, skb->dev ? skb->dev->name : ""); - *(int*)0 = 0; + BUG(); } static __inline__ struct sk_buff *skb_head_from_pool(void) @@ -172,7 +172,7 @@ struct sk_buff *alloc_skb(unsigned int size,int gfp_mask) if (++count < 5) { printk(KERN_ERR "alloc_skb called nonatomically " "from interrupt %p\n", NET_CALLER(size)); - *(int*)0 = 0; + BUG(); } gfp_mask &= ~__GFP_WAIT; } @@ -273,7 +273,7 @@ void __kfree_skb(struct sk_buff *skb) if (skb->list) { printk(KERN_WARNING "Warning: kfree_skb passed an skb still " "on a list (from %p).\n", NET_CALLER(skb)); - *(int*)0 = 0; + BUG(); } dst_release(skb->dst); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 50f09f1ec1e..b51d1c4e97e 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -268,10 +268,10 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int dev->priv = (void*)(dev+1); nt = (struct ip_tunnel*)dev->priv; nt->dev = dev; - strcpy(dev->name, nt->parms.name); dev->init = ipgre_tunnel_init; dev->new_style = 1; memcpy(&nt->parms, parms, sizeof(*parms)); + strcpy(dev->name, nt->parms.name); if (dev->name[0] == 0) { int i; for (i=1; i<100; i++) { diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index ed0c9f3e964..4069795fb1c 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -1,7 +1,7 @@ /* * Linux NET3: IP/IP protocol decoder. * - * Version: $Id: ipip.c,v 1.33 2000/05/05 02:17:17 davem Exp $ + * Version: $Id: ipip.c,v 1.34 2000/05/22 08:12:19 davem Exp $ * * Authors: * Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95 @@ -237,10 +237,10 @@ struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int create) dev->priv = (void*)(dev+1); nt = (struct ip_tunnel*)dev->priv; nt->dev = dev; - strcpy(dev->name, nt->parms.name); dev->init = ipip_tunnel_init; dev->new_style = 1; memcpy(&nt->parms, parms, sizeof(*parms)); + strcpy(dev->name, nt->parms.name); if (dev->name[0] == 0) { int i; for (i=1; i<100; i++) { diff --git a/net/ipv6/Config.in b/net/ipv6/Config.in index 80498a9fec6..dd313de812b 100644 --- a/net/ipv6/Config.in +++ b/net/ipv6/Config.in @@ -12,3 +12,7 @@ if [ "$CONFIG_NETLINK" = "y" ]; then fi #bool ' IPv6: flow policy support' CONFIG_RT6_POLICY #bool ' IPv6: firewall support' CONFIG_IPV6_FIREWALL + +if [ "$CONFIG_NETFILTER" != "n" ]; then + source net/ipv6/netfilter/Config.in +fi diff --git a/net/ipv6/netfilter/Config.in b/net/ipv6/netfilter/Config.in new file mode 100644 index 00000000000..626e1634d0d --- /dev/null +++ b/net/ipv6/netfilter/Config.in @@ -0,0 +1,49 @@ +# +# IP netfilter configuration +# +mainmenu_option next_comment +comment ' IPv6: Netfilter Configuration' + +#tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP6_NF_CONNTRACK +#if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then +# dep_tristate ' FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK +#fi + +#if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NETLINK" = "y" ]; then +# tristate 'Userspace queueing via NETLINK (EXPERIMENTAL)' CONFIG_IP6_NF_QUEUE +#fi +tristate 'IP6 tables support (required for filtering/masq/NAT)' CONFIG_IP6_NF_IPTABLES +if [ "$CONFIG_IP6_NF_IPTABLES" != "n" ]; then +# The simple matches. + dep_tristate ' limit match support' CONFIG_IP6_NF_MATCH_LIMIT $CONFIG_IP6_NF_IPTABLES +# dep_tristate ' MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES + dep_tristate ' netfilter MARK match support' CONFIG_IP6_NF_MATCH_MARK $CONFIG_IP6_NF_IPTABLES +# dep_tristate ' Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES +# dep_tristate ' TOS match support' CONFIG_IP6_NF_MATCH_TOS $CONFIG_IP6_NF_IPTABLES +# if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then +# dep_tristate ' Connection state match support' CONFIG_IP6_NF_MATCH_STATE $CONFIG_IP6_NF_CONNTRACK $CONFIG_IP6_NF_IPTABLES +# fi +# if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +# dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_UNCLEAN $CONFIG_IP6_NF_IPTABLES +# dep_tristate ' Owner match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OWNER $CONFIG_IP6_NF_IPTABLES +# fi + +# The targets + dep_tristate ' Packet filtering' CONFIG_IP6_NF_FILTER $CONFIG_IP6_NF_IPTABLES + + if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then +# dep_tristate ' REJECT target support' CONFIG_IP6_NF_TARGET_REJECT $CONFIG_IP6_NF_FILTER +# if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +# dep_tristate ' MIRROR target support (EXPERIMENTAL)' CONFIG_IP6_NF_TARGET_MIRROR $CONFIG_IP6_NF_FILTER +# fi + fi + + dep_tristate ' Packet mangling' CONFIG_IP6_NF_MANGLE $CONFIG_IP6_NF_IPTABLES + if [ "$CONFIG_IP6_NF_MANGLE" != "n" ]; then +# dep_tristate ' TOS target support' CONFIG_IP6_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE + dep_tristate ' MARK target support' CONFIG_IP6_NF_TARGET_MARK $CONFIG_IP6_NF_MANGLE + fi + #dep_tristate ' LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_IPTABLES +fi + +endmenu diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile new file mode 100644 index 00000000000..3dfbefc264d --- /dev/null +++ b/net/ipv6/netfilter/Makefile @@ -0,0 +1,180 @@ +# +# Makefile for the netfilter modules on top of IPv6. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := netfilter.o +MOD_LIST_NAME := IPV6_MODULES +M_OBJS := + +IP6_NF_CONNTRACK_OBJ:=ip6_conntrack_core.o ip6_conntrack_proto_generic.o ip6_conntrack_proto_tcp.o ip6_conntrack_proto_udp.o ip6_conntrack_proto_icmp.o + +# Link order matters here. +ifeq ($(CONFIG_IP6_NF_CONNTRACK),y) +O_OBJS += ip6_conntrack_standalone.o $(IP6_NF_CONNTRACK_OBJ) +else + ifeq ($(CONFIG_IP6_NF_CONNTRACK),m) + MI_OBJS += $(IP6_NF_CONNTRACK_OBJ) + MIX_OBJS += ip6_conntrack_standalone.o + M_OBJS += ip6_conntrack.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_FTP),y) +O_OBJS += ip6_conntrack_ftp.o +else + ifeq ($(CONFIG_IP6_NF_FTP),m) + MX_OBJS += ip6_conntrack_ftp.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_IPTABLES),y) +O_OBJS += ip6_tables.o +else + ifeq ($(CONFIG_IP6_NF_IPTABLES),m) + MX_OBJS += ip6_tables.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_LIMIT),y) +O_OBJS += ip6t_limit.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_LIMIT),m) + M_OBJS += ip6t_limit.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_MARK),y) +O_OBJS += ip6t_mark.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_MARK),m) + M_OBJS += ip6t_mark.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_MAC),y) +O_OBJS += ip6t_mac.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_MAC),m) + M_OBJS += ip6t_mac.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_MULTIPORT),y) +O_OBJS += ip6t_multiport.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_MULTIPORT),m) + M_OBJS += ip6t_multiport.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_OWNER),y) +O_OBJS += ip6t_owner.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_OWNER),m) + M_OBJS += ip6t_owner.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_TOS),y) +O_OBJS += ip6t_tos.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_TOS),m) + M_OBJS += ip6t_tos.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_STATE),y) +O_OBJS += ip6t_state.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_STATE),m) + M_OBJS += ip6t_state.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_MATCH_UNCLEAN),y) +O_OBJS += ip6t_unclean.o +else + ifeq ($(CONFIG_IP6_NF_MATCH_UNCLEAN),m) + M_OBJS += ip6t_unclean.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_FILTER),y) +O_OBJS += ip6table_filter.o +else + ifeq ($(CONFIG_IP6_NF_FILTER),m) + M_OBJS += ip6table_filter.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_TARGET_REJECT),y) +O_OBJS += ip6t_REJECT.o +else + ifeq ($(CONFIG_IP6_NF_TARGET_REJECT),m) + M_OBJS += ip6t_REJECT.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_TARGET_MIRROR),y) +O_OBJS += ip6t_MIRROR.o +else + ifeq ($(CONFIG_IP6_NF_TARGET_MIRROR),m) + M_OBJS += ip6t_MIRROR.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_TARGET_TOS),y) +O_OBJS += ip6t_TOS.o +else + ifeq ($(CONFIG_IP6_NF_TARGET_TOS),m) + M_OBJS += ip6t_TOS.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_TARGET_MARK),y) +O_OBJS += ip6t_MARK.o +else + ifeq ($(CONFIG_IP6_NF_TARGET_MARK),m) + M_OBJS += ip6t_MARK.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_TARGET_REDIRECT),y) +O_OBJS += ip6t_REDIRECT.o +else + ifeq ($(CONFIG_IP6_NF_TARGET_REDIRECT),m) + M_OBJS += ip6t_REDIRECT.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_TARGET_LOG),y) +O_OBJS += ip6t_LOG.o +else + ifeq ($(CONFIG_IP6_NF_TARGET_LOG),m) + M_OBJS += ip6t_LOG.o + endif +endif + +ifeq ($(CONFIG_IP6_NF_QUEUE),y) +O_OBJS += ip6_queue.o +else + ifeq ($(CONFIG_IP6_NF_QUEUE),m) + M_OBJS += ip6_queue.o + endif +endif + +include $(TOPDIR)/Rules.make + +ip6_conntrack.o: ip6_conntrack_standalone.o $(IP6_NF_CONNTRACK_OBJ) + $(LD) -r -o $@ $(IP6_NF_CONNTRACK_OBJ) ip6_conntrack_standalone.o + +ip6fwadm.o: ipfwadm_core.o $(IP6_NF_COMPAT_LAYER) + $(LD) -r -o $@ ip6fwadm_core.o $(IP6_NF_COMPAT_LAYER) + +ip6chains.o: ip6chains_core.o $(IP6_NF_COMPAT_LAYER) + $(LD) -r -o $@ ip6chains_core.o $(IP6_NF_COMPAT_LAYER) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c new file mode 100644 index 00000000000..2d9c356e9cc --- /dev/null +++ b/net/ipv6/netfilter/ip6_tables.c @@ -0,0 +1,1795 @@ +/* + * Packet matching code. + * + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define IPV6_HDR_LEN (sizeof(struct ipv6hdr)) + +/*#define DEBUG_IP_FIREWALL*/ +/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */ +/*#define DEBUG_IP_FIREWALL_USER*/ + +#ifdef DEBUG_IP_FIREWALL +#define dprintf(format, args...) printk(format , ## args) +#else +#define dprintf(format, args...) +#endif + +#ifdef DEBUG_IP_FIREWALL_USER +#define duprintf(format, args...) printk(format , ## args) +#else +#define duprintf(format, args...) +#endif + +#ifdef CONFIG_NETFILTER_DEBUG +#define IP_NF_ASSERT(x) \ +do { \ + if (!(x)) \ + printk("IP_NF_ASSERT: %s:%s:%u\n", \ + __FUNCTION__, __FILE__, __LINE__); \ +} while(0) +#else +#define IP_NF_ASSERT(x) +#endif +#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) + +/* Mutex protects lists (only traversed in user context). */ +static DECLARE_MUTEX(ip6t_mutex); + +/* Must have mutex */ +#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0) +#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0) +#include +#include + +#if 0 +/* All the better to debug you with... */ +#define static +#define inline +#endif + +/* Locking is simple: we assume at worst case there will be one packet + in user context and one from bottom halves (or soft irq if Alexey's + softnet patch was applied). + + We keep a set of rules for each CPU, so we can avoid write-locking + them; doing a readlock_bh() stops packets coming through if we're + in user context. + + To be cache friendly on SMP, we arrange them like so: + [ n-entries ] + ... cache-align padding ... + [ n-entries ] + + Hence the start of any table is given by get_table() below. */ + +/* The table itself */ +struct ip6t_table_info +{ + /* Size per table */ + unsigned int size; + /* Number of entries: FIXME. --RR */ + unsigned int number; + + /* Entry points and underflows */ + unsigned int hook_entry[NF_IP6_NUMHOOKS]; + unsigned int underflow[NF_IP6_NUMHOOKS]; + + char padding[SMP_ALIGN((NF_IP6_NUMHOOKS*2+2)*sizeof(unsigned int))]; + + /* ip6t_entry tables: one per CPU */ + char entries[0]; +}; + +static LIST_HEAD(ip6t_target); +static LIST_HEAD(ip6t_match); +static LIST_HEAD(ip6t_tables); +#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0) + +#ifdef CONFIG_SMP +#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*cpu_number_map(p)) +#else +#define TABLE_OFFSET(t,p) 0 +#endif + +#if 0 +#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0) +#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; }) +#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0) +#endif + +static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask, + struct in6_addr addr2) +{ + int i; + for( i = 0; i < 16; i++){ + if((addr1.s6_addr[i] & mask.s6_addr[i]) != + (addr2.s6_addr[i] & mask.s6_addr[i])) + return 1; + } + return 0; +} + +/* takes in current header and pointer to the header */ +/* if another header exists, sets hdrptr to the next header + and returns the new header value, else returns 0 */ +static u_int8_t ip6_nexthdr(u_int8_t currenthdr, u_int8_t *hdrptr) +{ + int i; + u_int8_t hdrlen, nexthdr = 0; + switch(currenthdr){ + case IPPROTO_AH: + /* whoever decided to do the length of AUTH for ipv6 + in 32bit units unlike other headers should be beaten... + repeatedly...with a large stick...no, an even LARGER + stick...no, you're still not thinking big enough */ + nexthdr = *hdrptr; + hdrlen = hdrptr[i] * 4 + 8; + hdrptr = hdrptr + hdrlen; + break; + /*stupid rfc2402 */ + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + nexthdr = *hdrptr; + hdrlen = hdrptr[1] * 8 + 8; + hdrptr = hdrptr + hdrlen; + break; + case IPPROTO_FRAGMENT: + nexthdr = *hdrptr; + hdrptr = hdrptr + 8; + break; + } + return nexthdr; + +} + +/* Returns whether matches rule or not. */ +static inline int +ip6_packet_match(const struct ipv6hdr *ipv6, + const char *indev, + const char *outdev, + const struct ip6t_ip6 *ip6info, + int isfrag) +{ + size_t i; + unsigned long ret; + +#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg)) + + if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src), + IP6T_INV_SRCIP) + || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst), + IP6T_INV_DSTIP)) { + dprintf("Source or dest mismatch.\n"); +/* + dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr, + ipinfo->smsk.s_addr, ipinfo->src.s_addr, + ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : ""); + dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr, + ipinfo->dmsk.s_addr, ipinfo->dst.s_addr, + ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/ + return 0; + } + + /* Look for ifname matches; this should unroll nicely. */ + for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { + ret |= (((const unsigned long *)indev)[i] + ^ ((const unsigned long *)ip6info->iniface)[i]) + & ((const unsigned long *)ip6info->iniface_mask)[i]; + } + + if (FWINV(ret != 0, IP6T_INV_VIA_IN)) { + dprintf("VIA in mismatch (%s vs %s).%s\n", + indev, ip6info->iniface, + ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":""); + return 0; + } + + for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { + ret |= (((const unsigned long *)outdev)[i] + ^ ((const unsigned long *)ip6info->outiface)[i]) + & ((const unsigned long *)ip6info->outiface_mask)[i]; + } + + if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) { + dprintf("VIA out mismatch (%s vs %s).%s\n", + outdev, ip6info->outiface, + ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":""); + return 0; + } + +/* ... might want to do something with class and flowlabel here ... */ + + /* look for the desired protocol header */ + if((ip6info->flags & IP6T_F_PROTO)) { + u_int8_t currenthdr = ipv6->nexthdr; + u_int8_t *hdrptr; + hdrptr = (u_int8_t *)(ipv6 + 1); + do { + if (ip6info->proto == currenthdr) { + if(ip6info->invflags & IP6T_INV_PROTO) + return 0; + return 1; + } + currenthdr = ip6_nexthdr(currenthdr, hdrptr); + } while(currenthdr); + if (!(ip6info->invflags & IP6T_INV_PROTO)) + return 0; + } + return 1; +} + +/* should be ip6 safe */ +static inline int +ip6_checkentry(const struct ip6t_ip6 *ipv6) +{ + if (ipv6->flags & ~IP6T_F_MASK) { + duprintf("Unknown flag bits set: %08X\n", + ipv6->flags & ~IP6T_F_MASK); + return 0; + } + if (ipv6->invflags & ~IP6T_INV_MASK) { + duprintf("Unknown invflag bits set: %08X\n", + ipv6->invflags & ~IP6T_INV_MASK); + return 0; + } + return 1; +} + +static unsigned int +ip6t_error(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + if (net_ratelimit()) + printk("ip6_tables: error: `%s'\n", (char *)targinfo); + + return NF_DROP; +} + +static inline +int do_match(struct ip6t_entry_match *m, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Stop iteration if it doesn't match */ + if (!m->u.kernel.match->match(skb, in, out, m->data, + offset, hdr, datalen, hotdrop)) + return 1; + else + return 0; +} + +static inline struct ip6t_entry * +get_entry(void *base, unsigned int offset) +{ + return (struct ip6t_entry *)(base + offset); +} + +/* Returns one of the generic firewall policies, like NF_ACCEPT. */ +unsigned int +ip6t_do_table(struct sk_buff **pskb, + unsigned int hook, + const struct net_device *in, + const struct net_device *out, + struct ip6t_table *table, + void *userdata) +{ + static const char nulldevname[IFNAMSIZ] = { 0 }; + u_int16_t offset = 0; + struct ipv6hdr *ipv6; + void *protohdr; + u_int16_t datalen; + int hotdrop = 0; + /* Initializing verdict to NF_DROP keeps gcc happy. */ + unsigned int verdict = NF_DROP; + const char *indev, *outdev; + void *table_base; + struct ip6t_entry *e, *back; + + /* Initialization */ + ipv6 = (*pskb)->nh.ipv6h; + protohdr = (u_int32_t *)ipv6 + IPV6_HDR_LEN; + datalen = (*pskb)->len - IPV6_HDR_LEN; + indev = in ? in->name : nulldevname; + outdev = out ? out->name : nulldevname; + + /* We handle fragments by dealing with the first fragment as + * if it was a normal packet. All other fragments are treated + * normally, except that they will NEVER match rules that ask + * things we don't know, ie. tcp syn flag or ports). If the + * rule is also a fragment-specific rule, non-fragments won't + * match it. */ + + read_lock_bh(&table->lock); + IP_NF_ASSERT(table->valid_hooks & (1 << hook)); + table_base = (void *)table->private->entries + + TABLE_OFFSET(table->private, smp_processor_id()); + e = get_entry(table_base, table->private->hook_entry[hook]); + +#ifdef CONFIG_NETFILTER_DEBUG + /* Check noone else using our table */ + if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac + && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) { + printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n", + smp_processor_id(), + table->name, + &((struct ip6t_entry *)table_base)->comefrom, + ((struct ip6t_entry *)table_base)->comefrom); + } + ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001; +#endif + + /* For return from builtin chain */ + back = get_entry(table_base, table->private->underflow[hook]); + + do { + IP_NF_ASSERT(e); + IP_NF_ASSERT(back); + (*pskb)->nfcache |= e->nfcache; + if (ip6_packet_match(ipv6, indev, outdev, &e->ipv6, offset)) { + struct ip6t_entry_target *t; + + if (IP6T_MATCH_ITERATE(e, do_match, + *pskb, in, out, + offset, protohdr, + datalen, &hotdrop) != 0) + goto no_match; + + ADD_COUNTER(e->counters, ntohs(ipv6->payload_len) + IPV6_HDR_LEN, 1); + + t = ip6t_get_target(e); + IP_NF_ASSERT(t->u.kernel.target); + /* Standard target? */ + if (!t->u.kernel.target->target) { + int v; + + v = ((struct ip6t_standard_target *)t)->verdict; + if (v < 0) { + /* Pop from stack? */ + if (v != IP6T_RETURN) { + verdict = (unsigned)(-v) - 1; + break; + } + e = back; + back = get_entry(table_base, + back->comefrom); + continue; + } + if (table_base + v + != (void *)e + e->next_offset) { + /* Save old back ptr in next entry */ + struct ip6t_entry *next + = (void *)e + e->next_offset; + next->comefrom + = (void *)back - table_base; + /* set back pointer to next entry */ + back = next; + } + + e = get_entry(table_base, v); + } else { + /* Targets which reenter must return + abs. verdicts */ +#ifdef CONFIG_NETFILTER_DEBUG + ((struct ip6t_entry *)table_base)->comefrom + = 0xeeeeeeec; +#endif + verdict = t->u.kernel.target->target(pskb, + hook, + in, out, + t->data, + userdata); + +#ifdef CONFIG_NETFILTER_DEBUG + if (((struct ip6t_entry *)table_base)->comefrom + != 0xeeeeeeec + && verdict == IP6T_CONTINUE) { + printk("Target %s reentered!\n", + t->u.kernel.target->name); + verdict = NF_DROP; + } + ((struct ip6t_entry *)table_base)->comefrom + = 0x57acc001; +#endif + /* Target might have changed stuff. */ + ipv6 = (*pskb)->nh.ipv6h; + protohdr = (u_int32_t *)ipv6 + IPV6_HDR_LEN; + datalen = (*pskb)->len - IPV6_HDR_LEN; + + if (verdict == IP6T_CONTINUE) + e = (void *)e + e->next_offset; + else + /* Verdict */ + break; + } + } else { + + no_match: + e = (void *)e + e->next_offset; + } + } while (!hotdrop); + +#ifdef CONFIG_NETFILTER_DEBUG + ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac; +#endif + read_unlock_bh(&table->lock); + +#ifdef DEBUG_ALLOW_ALL + return NF_ACCEPT; +#else + if (hotdrop) + return NF_DROP; + else return verdict; +#endif +} + +/* If it succeeds, returns element and locks mutex */ +static inline void * +find_inlist_lock_noload(struct list_head *head, + const char *name, + int *error, + struct semaphore *mutex) +{ + void *ret; + +#if 1 + duprintf("find_inlist: searching for `%s' in %s.\n", + name, head == &ip6t_target ? "ip6t_target" + : head == &ip6t_match ? "ip6t_match" + : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN"); +#endif + + *error = down_interruptible(mutex); + if (*error != 0) + return NULL; + + ret = list_named_find(head, name); + if (!ret) { + *error = -ENOENT; + up(mutex); + } + return ret; +} + +#ifndef CONFIG_KMOD +#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m)) +#else +static void * +find_inlist_lock(struct list_head *head, + const char *name, + const char *prefix, + int *error, + struct semaphore *mutex) +{ + void *ret; + + ret = find_inlist_lock_noload(head, name, error, mutex); + if (!ret) { + char modulename[IP6T_FUNCTION_MAXNAMELEN + strlen(prefix) + 1]; + strcpy(modulename, prefix); + strcat(modulename, name); + duprintf("find_inlist: loading `%s'.\n", modulename); + request_module(modulename); + ret = find_inlist_lock_noload(head, name, error, mutex); + } + + return ret; +} +#endif + +static inline struct ip6t_table * +find_table_lock(const char *name, int *error, struct semaphore *mutex) +{ + return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex); +} + +static inline struct ip6t_match * +find_match_lock(const char *name, int *error, struct semaphore *mutex) +{ + return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex); +} + +static inline struct ip6t_target * +find_target_lock(const char *name, int *error, struct semaphore *mutex) +{ + return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex); +} + +/* All zeroes == unconditional rule. */ +static inline int +unconditional(const struct ip6t_ip6 *ipv6) +{ + unsigned int i; + + for (i = 0; i < sizeof(*ipv6); i++) + if (((char *)ipv6)[i]) + break; + + return (i == sizeof(*ipv6)); +} + +/* Figures out from what hook each rule can be called: returns 0 if + there are loops. Puts hook bitmask in comefrom. */ +static int +mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks) +{ + unsigned int hook; + + /* No recursion; use packet counter to save back ptrs (reset + to 0 as we leave), and comefrom to save source hook bitmask */ + for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) { + unsigned int pos = newinfo->hook_entry[hook]; + struct ip6t_entry *e + = (struct ip6t_entry *)(newinfo->entries + pos); + + if (!(valid_hooks & (1 << hook))) + continue; + + /* Set initial back pointer. */ + e->counters.pcnt = pos; + + for (;;) { + struct ip6t_standard_target *t + = (void *)ip6t_get_target(e); + + if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) { + printk("iptables: loop hook %u pos %u %08X.\n", + hook, pos, e->comefrom); + return 0; + } + e->comefrom + |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS)); + + /* Unconditional return/END. */ + if (e->target_offset == sizeof(struct ip6t_entry) + && (strcmp(t->target.u.user.name, + IP6T_STANDARD_TARGET) == 0) + && t->verdict < 0 + && unconditional(&e->ipv6)) { + unsigned int oldpos, size; + + /* Return: backtrack through the last + big jump. */ + do { + e->comefrom ^= (1<comefrom + & (1 << NF_IP6_NUMHOOKS)) { + duprintf("Back unset " + "on hook %u " + "rule %u\n", + hook, pos); + } +#endif + oldpos = pos; + pos = e->counters.pcnt; + e->counters.pcnt = 0; + + /* We're at the start. */ + if (pos == oldpos) + goto next; + + e = (struct ip6t_entry *) + (newinfo->entries + pos); + } while (oldpos == pos + e->next_offset); + + /* Move along one */ + size = e->next_offset; + e = (struct ip6t_entry *) + (newinfo->entries + pos + size); + e->counters.pcnt = pos; + pos += size; + } else { + int newpos = t->verdict; + + if (strcmp(t->target.u.user.name, + IP6T_STANDARD_TARGET) == 0 + && newpos >= 0) { + /* This a jump; chase it. */ + duprintf("Jump rule %u -> %u\n", + pos, newpos); + } else { + /* ... this is a fallthru */ + newpos = pos + e->next_offset; + } + e = (struct ip6t_entry *) + (newinfo->entries + newpos); + e->counters.pcnt = pos; + pos = newpos; + } + } + next: + duprintf("Finished chain %u\n", hook); + } + return 1; +} + +static inline int +cleanup_match(struct ip6t_entry_match *m, unsigned int *i) +{ + if (i && (*i)-- == 0) + return 1; + + if (m->u.kernel.match->destroy) + m->u.kernel.match->destroy(m->data, + m->u.match_size - sizeof(*m)); + + if (m->u.kernel.match->me) + __MOD_DEC_USE_COUNT(m->u.kernel.match->me); + + return 0; +} + +static inline int +standard_check(const struct ip6t_entry_target *t, + unsigned int max_offset) +{ + struct ip6t_standard_target *targ = (void *)t; + + /* Check standard info. */ + if (t->u.target_size + != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) { + duprintf("standard_check: target size %u != %u\n", + t->u.target_size, + IP6T_ALIGN(sizeof(struct ip6t_standard_target))); + return 0; + } + + if (targ->verdict >= 0 + && targ->verdict > max_offset - sizeof(struct ip6t_entry)) { + duprintf("ip6t_standard_check: bad verdict (%i)\n", + targ->verdict); + return 0; + } + + if (targ->verdict < -NF_MAX_VERDICT - 1) { + duprintf("ip6t_standard_check: bad negative verdict (%i)\n", + targ->verdict); + return 0; + } + return 1; +} + +static inline int +check_match(struct ip6t_entry_match *m, + const char *name, + const struct ip6t_ip6 *ipv6, + unsigned int hookmask, + unsigned int *i) +{ + int ret; + struct ip6t_match *match; + + match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex); + if (!match) { + // duprintf("check_match: `%s' not found\n", m->u.name); + return ret; + } + if (match->me) + __MOD_INC_USE_COUNT(match->me); + m->u.kernel.match = match; + up(&ip6t_mutex); + + if (m->u.kernel.match->checkentry + && !m->u.kernel.match->checkentry(name, ipv6, m->data, + m->u.match_size - sizeof(*m), + hookmask)) { + if (m->u.kernel.match->me) + __MOD_DEC_USE_COUNT(m->u.kernel.match->me); + duprintf("ip_tables: check failed for `%s'.\n", + m->u.kernel.match->name); + return -EINVAL; + } + + (*i)++; + return 0; +} + +static struct ip6t_target ip6t_standard_target; + +static inline int +check_entry(struct ip6t_entry *e, const char *name, unsigned int size, + unsigned int *i) +{ + struct ip6t_entry_target *t; + struct ip6t_target *target; + int ret; + unsigned int j; + + if (!ip6_checkentry(&e->ipv6)) { + duprintf("ip_tables: ip check failed %p %s.\n", e, name); + return -EINVAL; + } + + j = 0; + ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j); + if (ret != 0) + goto cleanup_matches; + + t = ip6t_get_target(e); + target = find_target_lock(t->u.user.name, &ret, &ip6t_mutex); + if (!target) { + // duprintf("check_entry: `%s' not found\n", t->u.name); + return ret; + } + if (target->me) + __MOD_INC_USE_COUNT(target->me); + t->u.kernel.target = target; + up(&ip6t_mutex); + + if (t->u.kernel.target == &ip6t_standard_target) { + if (!standard_check(t, size)) { + ret = -EINVAL; + goto cleanup_matches; + } + } else if (t->u.kernel.target->checkentry + && !t->u.kernel.target->checkentry(name, e, t->data, + t->u.target_size + - sizeof(*t), + e->comefrom)) { + if (t->u.kernel.target->me) + __MOD_DEC_USE_COUNT(t->u.kernel.target->me); + duprintf("ip_tables: check failed for `%s'.\n", + t->u.kernel.target->name); + ret = -EINVAL; + goto cleanup_matches; + } + + (*i)++; + return 0; + + cleanup_matches: + IP6T_MATCH_ITERATE(e, cleanup_match, &j); + return ret; +} + +static inline int +check_entry_size_and_hooks(struct ip6t_entry *e, + struct ip6t_table_info *newinfo, + unsigned char *base, + unsigned char *limit, + const unsigned int *hook_entries, + const unsigned int *underflows, + unsigned int *i) +{ + unsigned int h; + + if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 + || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } + + if (e->next_offset + < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) { + duprintf("checking: element %p size %u\n", + e, e->next_offset); + return -EINVAL; + } + + /* Check hooks & underflows */ + for (h = 0; h < NF_IP6_NUMHOOKS; h++) { + if ((unsigned char *)e - base == hook_entries[h]) + newinfo->hook_entry[h] = hook_entries[h]; + if ((unsigned char *)e - base == underflows[h]) + newinfo->underflow[h] = underflows[h]; + } + + /* FIXME: underflows must be unconditional, standard verdicts + < 0 (not IP6T_RETURN). --RR */ + + /* Clear counters and comefrom */ + e->counters = ((struct ip6t_counters) { 0, 0 }); + e->comefrom = 0; + + (*i)++; + return 0; +} + +static inline int +cleanup_entry(struct ip6t_entry *e, unsigned int *i) +{ + struct ip6t_entry_target *t; + + if (i && (*i)-- == 0) + return 1; + + /* Cleanup all matches */ + IP6T_MATCH_ITERATE(e, cleanup_match, NULL); + t = ip6t_get_target(e); + if (t->u.kernel.target->destroy) + t->u.kernel.target->destroy(t->data, + t->u.target_size - sizeof(*t)); + if (t->u.kernel.target->me) + __MOD_DEC_USE_COUNT(t->u.kernel.target->me); + + return 0; +} + +/* Checks and translates the user-supplied table segment (held in + newinfo) */ +static int +translate_table(const char *name, + unsigned int valid_hooks, + struct ip6t_table_info *newinfo, + unsigned int size, + unsigned int number, + const unsigned int *hook_entries, + const unsigned int *underflows) +{ + unsigned int i; + int ret; + + newinfo->size = size; + newinfo->number = number; + + /* Init all hooks to impossible value. */ + for (i = 0; i < NF_IP6_NUMHOOKS; i++) { + newinfo->hook_entry[i] = 0xFFFFFFFF; + newinfo->underflow[i] = 0xFFFFFFFF; + } + + duprintf("translate_table: size %u\n", newinfo->size); + i = 0; + /* Walk through entries, checking offsets. */ + ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, + check_entry_size_and_hooks, + newinfo, + newinfo->entries, + newinfo->entries + size, + hook_entries, underflows, &i); + if (ret != 0) + return ret; + + if (i != number) { + duprintf("translate_table: %u not %u entries\n", + i, number); + return -EINVAL; + } + + /* Check hooks all assigned */ + for (i = 0; i < NF_IP6_NUMHOOKS; i++) { + /* Only hooks which are valid */ + if (!(valid_hooks & (1 << i))) + continue; + if (newinfo->hook_entry[i] == 0xFFFFFFFF) { + duprintf("Invalid hook entry %u %u\n", + i, hook_entries[i]); + return -EINVAL; + } + if (newinfo->underflow[i] == 0xFFFFFFFF) { + duprintf("Invalid underflow %u %u\n", + i, underflows[i]); + return -EINVAL; + } + } + + if (!mark_source_chains(newinfo, valid_hooks)) + return -ELOOP; + + /* Finally, each sanity check must pass */ + i = 0; + ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, + check_entry, name, size, &i); + + if (ret != 0) { + IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, + cleanup_entry, &i); + return ret; + } + + /* And one copy for every other CPU */ + for (i = 1; i < smp_num_cpus; i++) { + memcpy(newinfo->entries + SMP_ALIGN(newinfo->size*i), + newinfo->entries, + SMP_ALIGN(newinfo->size)); + } + + return ret; +} + +static struct ip6t_table_info * +replace_table(struct ip6t_table *table, + unsigned int num_counters, + struct ip6t_table_info *newinfo, + int *error) +{ + struct ip6t_table_info *oldinfo; + +#ifdef CONFIG_NETFILTER_DEBUG + { + struct ip6t_entry *table_base; + unsigned int i; + + for (i = 0; i < smp_num_cpus; i++) { + table_base = + (void *)newinfo->entries + + TABLE_OFFSET(newinfo, i); + + table_base->comefrom = 0xdead57ac; + } + } +#endif + + /* Do the substitution. */ + write_lock_bh(&table->lock); + /* Check inside lock: is the old number correct? */ + if (num_counters != table->private->number) { + duprintf("num_counters != table->private->number (%u/%u)\n", + num_counters, table->private->number); + write_unlock_bh(&table->lock); + *error = -EAGAIN; + return NULL; + } + oldinfo = table->private; + table->private = newinfo; + write_unlock_bh(&table->lock); + + return oldinfo; +} + +/* Gets counters. */ +static inline int +add_entry_to_counter(const struct ip6t_entry *e, + struct ip6t_counters total[], + unsigned int *i) +{ + ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt); + + (*i)++; + return 0; +} + +static void +get_counters(const struct ip6t_table_info *t, + struct ip6t_counters counters[]) +{ + unsigned int cpu; + unsigned int i; + + for (cpu = 0; cpu < smp_num_cpus; cpu++) { + i = 0; + IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu), + t->size, + add_entry_to_counter, + counters, + &i); + } +} + +static int +copy_entries_to_user(unsigned int total_size, + struct ip6t_table *table, + void *userptr) +{ + unsigned int off, num, countersize; + struct ip6t_entry *e; + struct ip6t_counters *counters; + int ret = 0; + + /* We need atomic snapshot of counters: rest doesn't change + (other than comefrom, which userspace doesn't care + about). */ + countersize = sizeof(struct ip6t_counters) * table->private->number; + counters = vmalloc(countersize); + + if (counters == NULL) + return -ENOMEM; + + /* First, sum counters... */ + memset(counters, 0, countersize); + write_lock_bh(&table->lock); + get_counters(table->private, counters); + write_unlock_bh(&table->lock); + + /* ... then copy entire thing from CPU 0... */ + if (copy_to_user(userptr, table->private->entries, total_size) != 0) { + ret = -EFAULT; + goto free_counters; + } + + /* FIXME: use iterator macros --RR */ + /* ... then go back and fix counters and names */ + for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){ + unsigned int i; + struct ip6t_entry_match *m; + struct ip6t_entry_target *t; + + e = (struct ip6t_entry *)(table->private->entries + off); + if (copy_to_user(userptr + off + + offsetof(struct ip6t_entry, counters), + &counters[num], + sizeof(counters[num])) != 0) { + ret = -EFAULT; + goto free_counters; + } + + for (i = sizeof(struct ip6t_entry); + i < e->target_offset; + i += m->u.match_size) { + m = (void *)e + i; + + if (copy_to_user(userptr + off + i + + offsetof(struct ip6t_entry_match, + u.user.name), + m->u.kernel.match->name, + strlen(m->u.kernel.match->name)+1) + != 0) { + ret = -EFAULT; + goto free_counters; + } + } + + t = ip6t_get_target(e); + if (copy_to_user(userptr + off + e->target_offset + + offsetof(struct ip6t_entry_target, + u.user.name), + t->u.kernel.target->name, + strlen(t->u.kernel.target->name)+1) != 0) { + ret = -EFAULT; + goto free_counters; + } + } + + free_counters: + vfree(counters); + return ret; +} + +static int +get_entries(const struct ip6t_get_entries *entries, + struct ip6t_get_entries *uptr) +{ + int ret; + struct ip6t_table *t; + + t = find_table_lock(entries->name, &ret, &ip6t_mutex); + if (t) { + duprintf("t->private->number = %u\n", + t->private->number); + if (entries->size == t->private->size) + ret = copy_entries_to_user(t->private->size, + t, uptr->entries); + else { + duprintf("get_entries: I've got %u not %u!\n", + t->private->size, + entries->size); + ret = -EINVAL; + } + up(&ip6t_mutex); + } else + duprintf("get_entries: Can't find %s!\n", + entries->name); + + return ret; +} + +static int +do_replace(void *user, unsigned int len) +{ + int ret; + struct ip6t_replace tmp; + struct ip6t_table *t; + struct ip6t_table_info *newinfo, *oldinfo; + struct ip6t_counters *counters; + + if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) + return -EFAULT; + + newinfo = vmalloc(sizeof(struct ip6t_table_info) + + SMP_ALIGN(tmp.size) * smp_num_cpus); + if (!newinfo) + return -ENOMEM; + + if (copy_from_user(newinfo->entries, user + sizeof(tmp), + tmp.size) != 0) { + ret = -EFAULT; + goto free_newinfo; + } + + counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters)); + if (!counters) { + ret = -ENOMEM; + goto free_newinfo; + } + memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters)); + + ret = translate_table(tmp.name, tmp.valid_hooks, + newinfo, tmp.size, tmp.num_entries, + tmp.hook_entry, tmp.underflow); + if (ret != 0) + goto free_newinfo_counters; + + duprintf("ip_tables: Translated table\n"); + + t = find_table_lock(tmp.name, &ret, &ip6t_mutex); + if (!t) + goto free_newinfo_counters_untrans; + + /* You lied! */ + if (tmp.valid_hooks != t->valid_hooks) { + duprintf("Valid hook crap: %08X vs %08X\n", + tmp.valid_hooks, t->valid_hooks); + ret = -EINVAL; + goto free_newinfo_counters_untrans_unlock; + } + + oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret); + if (!oldinfo) + goto free_newinfo_counters_untrans_unlock; + + /* Get the old counters. */ + get_counters(oldinfo, counters); + /* Decrease module usage counts and free resource */ + IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL); + vfree(oldinfo); + /* Silent error: too late now. */ + copy_to_user(tmp.counters, counters, + sizeof(struct ip6t_counters) * tmp.num_counters); + vfree(counters); + up(&ip6t_mutex); + return 0; + + free_newinfo_counters_untrans_unlock: + up(&ip6t_mutex); + free_newinfo_counters_untrans: + IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL); + free_newinfo_counters: + vfree(counters); + free_newinfo: + vfree(newinfo); + return ret; +} + +/* We're lazy, and add to the first CPU; overflow works its fey magic + * and everything is OK. */ +static inline int +add_counter_to_entry(struct ip6t_entry *e, + const struct ip6t_counters addme[], + unsigned int *i) +{ +#if 0 + duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n", + *i, + (long unsigned int)e->counters.pcnt, + (long unsigned int)e->counters.bcnt, + (long unsigned int)addme[*i].pcnt, + (long unsigned int)addme[*i].bcnt); +#endif + + ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); + + (*i)++; + return 0; +} + +static int +do_add_counters(void *user, unsigned int len) +{ + unsigned int i; + struct ip6t_counters_info tmp, *paddc; + struct ip6t_table *t; + int ret; + + if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) + return -EFAULT; + + if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters)) + return -EINVAL; + + paddc = vmalloc(len); + if (!paddc) + return -ENOMEM; + + if (copy_from_user(paddc, user, len) != 0) { + ret = -EFAULT; + goto free; + } + + t = find_table_lock(tmp.name, &ret, &ip6t_mutex); + if (!t) + goto free; + + write_lock_bh(&t->lock); + if (t->private->number != paddc->num_counters) { + ret = -EINVAL; + goto unlock_up_free; + } + + i = 0; + IP6T_ENTRY_ITERATE(t->private->entries, + t->private->size, + add_counter_to_entry, + paddc->counters, + &i); + unlock_up_free: + write_unlock_bh(&t->lock); + up(&ip6t_mutex); + free: + vfree(paddc); + + return ret; +} + +static int +do_ip6t_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len) +{ + int ret; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (cmd) { + case IP6T_SO_SET_REPLACE: + ret = do_replace(user, len); + break; + + case IP6T_SO_SET_ADD_COUNTERS: + ret = do_add_counters(user, len); + break; + + default: + duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd); + ret = -EINVAL; + } + + return ret; +} + +static int +do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len) +{ + int ret; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (cmd) { + case IP6T_SO_GET_INFO: { + char name[IP6T_TABLE_MAXNAMELEN]; + struct ip6t_table *t; + + if (*len != sizeof(struct ip6t_getinfo)) { + duprintf("length %u != %u\n", *len, + sizeof(struct ip6t_getinfo)); + ret = -EINVAL; + break; + } + + if (copy_from_user(name, user, sizeof(name)) != 0) { + ret = -EFAULT; + break; + } + t = find_table_lock(name, &ret, &ip6t_mutex); + if (t) { + struct ip6t_getinfo info; + + info.valid_hooks = t->valid_hooks; + memcpy(info.hook_entry, t->private->hook_entry, + sizeof(info.hook_entry)); + memcpy(info.underflow, t->private->underflow, + sizeof(info.underflow)); + info.num_entries = t->private->number; + info.size = t->private->size; + strcpy(info.name, name); + + if (copy_to_user(user, &info, *len) != 0) + ret = -EFAULT; + else + ret = 0; + + up(&ip6t_mutex); + } + } + break; + + case IP6T_SO_GET_ENTRIES: { + struct ip6t_get_entries get; + + if (*len < sizeof(get)) { + duprintf("get_entries: %u < %u\n", *len, sizeof(get)); + ret = -EINVAL; + } else if (copy_from_user(&get, user, sizeof(get)) != 0) { + ret = -EFAULT; + } else if (*len != sizeof(struct ip6t_get_entries) + get.size) { + duprintf("get_entries: %u != %u\n", *len, + sizeof(struct ip6t_get_entries) + get.size); + ret = -EINVAL; + } else + ret = get_entries(&get, user); + break; + } + + default: + duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd); + ret = -EINVAL; + } + + return ret; +} + +/* Registration hooks for targets. */ +int +ip6t_register_target(struct ip6t_target *target) +{ + int ret; + + MOD_INC_USE_COUNT; + ret = down_interruptible(&ip6t_mutex); + if (ret != 0) + return ret; + + if (!list_named_insert(&ip6t_target, target)) { + duprintf("ip6t_register_target: `%s' already in list!\n", + target->name); + ret = -EINVAL; + MOD_DEC_USE_COUNT; + } + up(&ip6t_mutex); + return ret; +} + +void +ip6t_unregister_target(struct ip6t_target *target) +{ + down(&ip6t_mutex); + LIST_DELETE(&ip6t_target, target); + up(&ip6t_mutex); + MOD_DEC_USE_COUNT; +} + +int +ip6t_register_match(struct ip6t_match *match) +{ + int ret; + + MOD_INC_USE_COUNT; + ret = down_interruptible(&ip6t_mutex); + if (ret != 0) { + MOD_DEC_USE_COUNT; + return ret; + } + if (list_named_insert(&ip6t_match, match)) { + ret = 0; + } else { + duprintf("ip6t_register_match: `%s' already in list!\n", + match->name); + MOD_DEC_USE_COUNT; + ret = -EINVAL; + } + up(&ip6t_mutex); + + return ret; +} + +void +ip6t_unregister_match(struct ip6t_match *match) +{ + down(&ip6t_mutex); + LIST_DELETE(&ip6t_match, match); + up(&ip6t_mutex); + MOD_DEC_USE_COUNT; +} + +int ip6t_register_table(struct ip6t_table *table) +{ + int ret; + struct ip6t_table_info *newinfo; + static struct ip6t_table_info bootstrap + = { 0, 0, { 0 }, { 0 }, { }, { } }; + + MOD_INC_USE_COUNT; + newinfo = vmalloc(sizeof(struct ip6t_table_info) + + SMP_ALIGN(table->table->size) * smp_num_cpus); + if (!newinfo) { + ret = -ENOMEM; + MOD_DEC_USE_COUNT; + return ret; + } + memcpy(newinfo->entries, table->table->entries, table->table->size); + + ret = translate_table(table->name, table->valid_hooks, + newinfo, table->table->size, + table->table->num_entries, + table->table->hook_entry, + table->table->underflow); + if (ret != 0) { + vfree(newinfo); + MOD_DEC_USE_COUNT; + return ret; + } + + ret = down_interruptible(&ip6t_mutex); + if (ret != 0) { + vfree(newinfo); + MOD_DEC_USE_COUNT; + return ret; + } + + /* Don't autoload: we'd eat our tail... */ + if (list_named_find(&ip6t_tables, table->name)) { + ret = -EEXIST; + goto free_unlock; + } + + /* Simplifies replace_table code. */ + table->private = &bootstrap; + if (!replace_table(table, 0, newinfo, &ret)) + goto free_unlock; + + duprintf("table->private->number = %u\n", + table->private->number); + + table->lock = RW_LOCK_UNLOCKED; + list_prepend(&ip6t_tables, table); + + unlock: + up(&ip6t_mutex); + return ret; + + free_unlock: + vfree(newinfo); + MOD_DEC_USE_COUNT; + goto unlock; +} + +void ip6t_unregister_table(struct ip6t_table *table) +{ + down(&ip6t_mutex); + LIST_DELETE(&ip6t_tables, table); + up(&ip6t_mutex); + + /* Decrease module usage counts and free resources */ + IP6T_ENTRY_ITERATE(table->private->entries, table->private->size, + cleanup_entry, NULL); + vfree(table->private); + MOD_DEC_USE_COUNT; +} + +/* Returns 1 if the port is matched by the range, 0 otherwise */ +static inline int +port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert) +{ + int ret; + + ret = (port >= min && port <= max) ^ invert; + return ret; +} + +static int +tcp_find_option(u_int8_t option, + const struct tcphdr *tcp, + u_int16_t datalen, + int invert, + int *hotdrop) +{ + unsigned int i = sizeof(struct tcphdr); + const u_int8_t *opt = (u_int8_t *)tcp; + + duprintf("tcp_match: finding option\n"); + /* If we don't have the whole header, drop packet. */ + if (tcp->doff * 4 > datalen) { + *hotdrop = 1; + return 0; + } + + while (i < tcp->doff * 4) { + if (opt[i] == option) return !invert; + if (opt[i] < 2) i++; + else i += opt[i+1]?:1; + } + + return invert; +} + +static int +tcp_match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct tcphdr *tcp = hdr; + const struct ip6t_tcp *tcpinfo = matchinfo; + + /* To quote Alan: + + Don't allow a fragment of TCP 8 bytes in. Nobody normal + causes this. Its a cracker trying to break in by doing a + flag overwrite to pass the direction checks. + */ + + if (offset == 1) { + duprintf("Dropping evil TCP offset=1 frag.\n"); + *hotdrop = 1; + return 0; + } else if (offset == 0 && datalen < sizeof(struct tcphdr)) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + duprintf("Dropping evil TCP offset=0 tinygram.\n"); + *hotdrop = 1; + return 0; + } + + /* FIXME: Try tcp doff >> packet len against various stacks --RR */ + +#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg)) + + /* Must not be a fragment. */ + return !offset + && port_match(tcpinfo->spts[0], tcpinfo->spts[1], + ntohs(tcp->source), + !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)) + && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], + ntohs(tcp->dest), + !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)) + && FWINVTCP((((unsigned char *)tcp)[13] + & tcpinfo->flg_mask) + == tcpinfo->flg_cmp, + IP6T_TCP_INV_FLAGS) + && (!tcpinfo->option + || tcp_find_option(tcpinfo->option, tcp, datalen, + tcpinfo->invflags + & IP6T_TCP_INV_OPTION, + hotdrop)); +} + +/* Called when user tries to insert an entry of this type. */ +static int +tcp_checkentry(const char *tablename, + const struct ip6t_ip6 *ipv6, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + const struct ip6t_tcp *tcpinfo = matchinfo; + + /* Must specify proto == TCP, and no unknown invflags */ + return ipv6->proto == IPPROTO_TCP + && !(ipv6->invflags & IP6T_INV_PROTO) + && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp)) + && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK); +} + +static int +udp_match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct udphdr *udp = hdr; + const struct ip6t_udp *udpinfo = matchinfo; + + if (offset == 0 && datalen < sizeof(struct udphdr)) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + duprintf("Dropping evil UDP tinygram.\n"); + *hotdrop = 1; + return 0; + } + + /* Must not be a fragment. */ + return !offset + && port_match(udpinfo->spts[0], udpinfo->spts[1], + ntohs(udp->source), + !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT)) + && port_match(udpinfo->dpts[0], udpinfo->dpts[1], + ntohs(udp->dest), + !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT)); +} + +/* Called when user tries to insert an entry of this type. */ +static int +udp_checkentry(const char *tablename, + const struct ip6t_ip6 *ipv6, + void *matchinfo, + unsigned int matchinfosize, + unsigned int hook_mask) +{ + const struct ip6t_udp *udpinfo = matchinfo; + + /* Must specify proto == UDP, and no unknown invflags */ + if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) { + duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto, + IPPROTO_UDP); + return 0; + } + if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) { + duprintf("ip6t_udp: matchsize %u != %u\n", + matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp))); + return 0; + } + if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) { + duprintf("ip6t_udp: unknown flags %X\n", + udpinfo->invflags); + return 0; + } + + return 1; +} + +/* Returns 1 if the type and code is matched by the range, 0 otherwise */ +static inline int +icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, + u_int8_t type, u_int8_t code, + int invert) +{ + return (type == test_type && code >= min_code && code <= max_code) + ^ invert; +} + +static int +icmp_match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct icmphdr *icmp = hdr; + const struct ip6t_icmp *icmpinfo = matchinfo; + + if (offset == 0 && datalen < 2) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + duprintf("Dropping evil ICMP tinygram.\n"); + *hotdrop = 1; + return 0; + } + + /* Must not be a fragment. */ + return !offset + && icmp_type_code_match(icmpinfo->type, + icmpinfo->code[0], + icmpinfo->code[1], + icmp->type, icmp->code, + !!(icmpinfo->invflags&IP6T_ICMP_INV)); +} + +/* Called when user tries to insert an entry of this type. */ +static int +icmp_checkentry(const char *tablename, + const struct ip6t_ip6 *ipv6, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + const struct ip6t_icmp *icmpinfo = matchinfo; + + /* Must specify proto == ICMP, and no unknown invflags */ + return ipv6->proto == IPPROTO_ICMP + && !(ipv6->invflags & IP6T_INV_PROTO) + && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp)) + && !(icmpinfo->invflags & ~IP6T_ICMP_INV); +} + +/* The built-in targets: standard (NULL) and error. */ +static struct ip6t_target ip6t_standard_target += { { NULL, NULL }, IP6T_STANDARD_TARGET, NULL, NULL, NULL }; +static struct ip6t_target ip6t_error_target += { { NULL, NULL }, IP6T_ERROR_TARGET, ip6t_error, NULL, NULL }; + +static struct nf_sockopt_ops ip6t_sockopts += { { NULL, NULL }, PF_INET6, IP6T_BASE_CTL, IP6T_SO_SET_MAX+1, do_ip6t_set_ctl, + IP6T_BASE_CTL, IP6T_SO_GET_MAX+1, do_ip6t_get_ctl, 0, NULL }; + +static struct ip6t_match tcp_matchstruct += { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL }; +static struct ip6t_match udp_matchstruct += { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL }; +static struct ip6t_match icmp_matchstruct += { { NULL, NULL }, "icmp", &icmp_match, &icmp_checkentry, NULL }; + +#ifdef CONFIG_PROC_FS +static inline int print_name(const struct ip6t_table *t, + off_t start_offset, char *buffer, int length, + off_t *pos, unsigned int *count) +{ + if ((*count)++ >= start_offset) { + unsigned int namelen; + + namelen = sprintf(buffer + *pos, "%s\n", t->name); + if (*pos + namelen > length) { + /* Stop iterating */ + return 1; + } + *pos += namelen; + } + return 0; +} + +static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length) +{ + off_t pos = 0; + unsigned int count = 0; + + if (down_interruptible(&ip6t_mutex) != 0) + return 0; + + LIST_FIND(&ip6t_tables, print_name, struct ip6t_table *, + offset, buffer, length, &pos, &count); + + up(&ip6t_mutex); + + /* `start' hack - see fs/proc/generic.c line ~105 */ + *start=(char *)((unsigned long)count-offset); + return pos; +} +#endif /*CONFIG_PROC_FS*/ + +static int __init init(void) +{ + int ret; + + /* Noone else will be downing sem now, so we won't sleep */ + down(&ip6t_mutex); + list_append(&ip6t_target, &ip6t_standard_target); + list_append(&ip6t_target, &ip6t_error_target); + list_append(&ip6t_match, &tcp_matchstruct); + list_append(&ip6t_match, &udp_matchstruct); + list_append(&ip6t_match, &icmp_matchstruct); + up(&ip6t_mutex); + + /* Register setsockopt */ + ret = nf_register_sockopt(&ip6t_sockopts); + if (ret < 0) { + duprintf("Unable to register sockopts.\n"); + return ret; + } + +#ifdef CONFIG_PROC_FS + if (!proc_net_create("ip6_tables_names", 0, ip6t_get_tables)) { + nf_unregister_sockopt(&ip6t_sockopts); + return -ENOMEM; + } +#endif + + printk("ip6_tables: (c)2000 Netfilter core team\n"); + return 0; +} + +static void __exit fini(void) +{ + nf_unregister_sockopt(&ip6t_sockopts); +#ifdef CONFIG_PROC_FS + proc_net_remove("ip6_tables_names"); +#endif +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv6/netfilter/ip6t_MARK.c b/net/ipv6/netfilter/ip6t_MARK.c new file mode 100644 index 00000000000..dd8bb322691 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_MARK.c @@ -0,0 +1,66 @@ +/* This is a module which is used for setting the NFMARK field of an skb. */ +#include +#include +#include +#include + +#include +#include + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ipt_mark_target_info *markinfo = targinfo; + + if((*pskb)->nfmark != markinfo->mark) { + (*pskb)->nfmark = markinfo->mark; + (*pskb)->nfcache |= NFC_ALTERED; + } + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_mark_target_info))) { + printk(KERN_WARNING "MARK: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_mark_target_info))); + return 0; + } + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "MARK: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_mark_reg += { { NULL, NULL }, "MARK", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_mark_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_mark_reg); +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv6/netfilter/ip6t_limit.c b/net/ipv6/netfilter/ip6t_limit.c new file mode 100644 index 00000000000..0d79a9a8bd7 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_limit.c @@ -0,0 +1,135 @@ +/* Kernel module to control the rate + * + * Jérôme de Vivie + * Hervé Eychenne + * + * 2 September 1999: Changed from the target RATE to the match + * `limit', removed logging. Did I mention that + * Alexey is a fucking genius? + * Rusty Russell (rusty@rustcorp.com.au). */ +#include +#include +#include +#include + +#include +#include + +/* The algorithm used is the Simple Token Bucket Filter (TBF) + * see net/sched/sch_tbf.c in the linux source tree + */ + +static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED; + +/* Rusty: This is my (non-mathematically-inclined) understanding of + this algorithm. The `average rate' in jiffies becomes your initial + amount of credit `credit' and the most credit you can ever have + `credit_cap'. The `peak rate' becomes the cost of passing the + test, `cost'. + + `prev' tracks the last packet hit: you gain one credit per jiffy. + If you get credit balance more than this, the extra credit is + discarded. Every time the match passes, you lose `cost' credits; + if you don't have that many, the test fails. + + See Alexey's formal explanation in net/sched/sch_tbf.c. + + To avoid underflow, we multiply by 128 (ie. you get 128 credits per + jiffy). Hence a cost of 2^32-1, means one pass per 32768 seconds + at 1024HZ (or one every 9 hours). A cost of 1 means 12800 passes + per second at 100HZ. */ + +#define CREDITS_PER_JIFFY 128 + +static int +ipt_limit_match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + struct ipt_rateinfo *r = ((struct ipt_rateinfo *)matchinfo)->master; + unsigned long now = jiffies; + + spin_lock_bh(&limit_lock); + r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY; + if (r->credit > r->credit_cap) + r->credit = r->credit_cap; + + if (r->credit >= r->cost) { + /* We're not limited. */ + r->credit -= r->cost; + spin_unlock_bh(&limit_lock); + return 1; + } + + spin_unlock_bh(&limit_lock); + return 0; +} + +/* Precision saver. */ +static u_int32_t +user2credits(u_int32_t user) +{ + /* If multiplying would overflow... */ + if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) + /* Divide first. */ + return (user / IPT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; + + return (user * HZ * CREDITS_PER_JIFFY) / IPT_LIMIT_SCALE; +} + +static int +ipt_limit_checkentry(const char *tablename, + const struct ip6t_ip6 *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_rateinfo *r = matchinfo; + + if (matchsize != IP6T_ALIGN(sizeof(struct ipt_rateinfo))) + return 0; + + /* Check for overflow. */ + if (r->burst == 0 + || user2credits(r->avg * r->burst) < user2credits(r->avg)) { + printk("Call rusty: overflow in ipt_limit: %u/%u\n", + r->avg, r->burst); + return 0; + } + + /* User avg in seconds * IPT_LIMIT_SCALE: convert to jiffies * + 128. */ + r->prev = jiffies; + r->credit = user2credits(r->avg * r->burst); /* Credits full. */ + r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */ + r->cost = user2credits(r->avg); + + /* For SMP, we only want to use one set of counters. */ + r->master = r; + + return 1; +} + +static struct ip6t_match ipt_limit_reg += { { NULL, NULL }, "limit", ipt_limit_match, ipt_limit_checkentry, NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ip6t_register_match(&ipt_limit_reg)) + return -EINVAL; + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&ipt_limit_reg); +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv6/netfilter/ip6t_mark.c b/net/ipv6/netfilter/ip6t_mark.c new file mode 100644 index 00000000000..babe202a4e9 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_mark.c @@ -0,0 +1,50 @@ +/* Kernel module to match NFMARK values. */ +#include +#include + +#include +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_mark_info *info = matchinfo; + + return ((skb->nfmark & info->mask) == info->mark) ^ info->invert; +} + +static int +checkentry(const char *tablename, + const struct ip6t_ip6 *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IP6T_ALIGN(sizeof(struct ipt_mark_info))) + return 0; + + return 1; +} + +static struct ip6t_match mark_match += { { NULL, NULL }, "mark", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ip6t_register_match(&mark_match); +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&mark_match); +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c new file mode 100644 index 00000000000..cc40383858f --- /dev/null +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -0,0 +1,183 @@ +/* + * This is the 1999 rewrite of IP Firewalling, aiming for kernel 2.3.x. + * + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + */ +#include +#include + +#define FILTER_VALID_HOOKS ((1 << NF_IP6_LOCAL_IN) | (1 << NF_IP6_FORWARD) | (1 << NF_IP6_LOCAL_OUT)) + +/* Standard entry. */ +struct ip6t_standard +{ + struct ip6t_entry entry; + struct ip6t_standard_target target; +}; + +struct ip6t_error_target +{ + struct ip6t_entry_target target; + char errorname[IP6T_FUNCTION_MAXNAMELEN]; +}; + +struct ip6t_error +{ + struct ip6t_entry entry; + struct ip6t_error_target target; +}; + +static struct +{ + struct ip6t_replace repl; + struct ip6t_standard entries[3]; + struct ip6t_error term; +} initial_table __initdata += { { "filter", FILTER_VALID_HOOKS, 4, + sizeof(struct ip6t_standard) * 3 + sizeof(struct ip6t_error), + { [NF_IP6_LOCAL_IN] 0, + [NF_IP6_FORWARD] sizeof(struct ip6t_standard), + [NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) * 2 }, + { [NF_IP6_LOCAL_IN] 0, + [NF_IP6_FORWARD] sizeof(struct ip6t_standard), + [NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) * 2 }, + 0, NULL, { } }, + { + /* LOCAL_IN */ + { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, + 0, + sizeof(struct ip6t_entry), + sizeof(struct ip6t_standard), + 0, { 0, 0 }, { } }, + { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, + -NF_ACCEPT - 1 } }, + /* FORWARD */ + { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, + 0, + sizeof(struct ip6t_entry), + sizeof(struct ip6t_standard), + 0, { 0, 0 }, { } }, + { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, + -NF_ACCEPT - 1 } }, + /* LOCAL_OUT */ + { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, + 0, + sizeof(struct ip6t_entry), + sizeof(struct ip6t_standard), + 0, { 0, 0 }, { } }, + { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, + -NF_ACCEPT - 1 } } + }, + /* ERROR */ + { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, + 0, + sizeof(struct ip6t_entry), + sizeof(struct ip6t_error), + 0, { 0, 0 }, { } }, + { { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, + { } }, + "ERROR" + } + } +}; + +static struct ip6t_table packet_filter += { { NULL, NULL }, "filter", &initial_table.repl, + FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL }; + +/* The work comes in here from netfilter.c. */ +static unsigned int +ip6t_hook(unsigned int hook, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ip6t_do_table(pskb, hook, in, out, &packet_filter, NULL); +} + +static unsigned int +ip6t_local_out_hook(unsigned int hook, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ +#if 0 + /* root is playing with raw sockets. */ + if ((*pskb)->len < sizeof(struct iphdr) + || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { + if (net_ratelimit()) + printk("ip6t_hook: happy cracking.\n"); + return NF_ACCEPT; + } +#endif + + return ip6t_do_table(pskb, hook, in, out, &packet_filter, NULL); +} + +static struct nf_hook_ops ip6t_ops[] += { { { NULL, NULL }, ip6t_hook, PF_INET6, NF_IP6_LOCAL_IN, NF_IP6_PRI_FILTER }, + { { NULL, NULL }, ip6t_hook, PF_INET6, NF_IP6_FORWARD, NF_IP6_PRI_FILTER }, + { { NULL, NULL }, ip6t_local_out_hook, PF_INET6, NF_IP6_LOCAL_OUT, + NF_IP6_PRI_FILTER } +}; + +/* Default to forward because I got too much mail already. */ +static int forward = NF_ACCEPT; +MODULE_PARM(forward, "i"); + +static int __init init(void) +{ + int ret; + + if (forward < 0 || forward > NF_MAX_VERDICT) { + printk("iptables forward must be 0 or 1\n"); + return -EINVAL; + } + + /* Entry 1 is the FORWARD hook */ + initial_table.entries[1].target.verdict = -forward - 1; + + /* Register table */ + ret = ip6t_register_table(&packet_filter); + if (ret < 0) + return ret; + + /* Register hooks */ + ret = nf_register_hook(&ip6t_ops[0]); + if (ret < 0) + goto cleanup_table; + + ret = nf_register_hook(&ip6t_ops[1]); + if (ret < 0) + goto cleanup_hook0; + + ret = nf_register_hook(&ip6t_ops[2]); + if (ret < 0) + goto cleanup_hook1; + + return ret; + + cleanup_hook1: + nf_unregister_hook(&ip6t_ops[1]); + cleanup_hook0: + nf_unregister_hook(&ip6t_ops[0]); + cleanup_table: + ip6t_unregister_table(&packet_filter); + + return ret; +} + +static void __exit fini(void) +{ + unsigned int i; + + for (i = 0; i < sizeof(ip6t_ops)/sizeof(struct nf_hook_ops); i++) + nf_unregister_hook(&ip6t_ops[i]); + + ip6t_unregister_table(&packet_filter); +} + +module_init(init); +module_exit(fini); diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index b8c0a7be877..7eb578a60b6 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -84,6 +84,11 @@ rpcauth_init_credcache(struct rpc_auth *auth) static inline void rpcauth_crdestroy(struct rpc_auth *auth, struct rpc_cred *cred) { +#ifdef RPC_DEBUG + if (cred->cr_magic != RPCAUTH_CRED_MAGIC) + BUG(); + cred->cr_magic = 0; +#endif if (auth->au_ops->crdestroy) auth->au_ops->crdestroy(cred); else @@ -190,8 +195,13 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags) } spin_unlock(&rpc_credcache_lock); - if (!cred) + if (!cred) { cred = auth->au_ops->crcreate(taskflags); +#ifdef RPC_DEBUG + if (cred) + cred->cr_magic = RPCAUTH_CRED_MAGIC; +#endif + } if (cred) rpcauth_insert_credcache(auth, cred); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 973e8435c97..0a2a58c34c1 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.95 2000/05/09 04:48:37 davem Exp $ + * Version: $Id: af_unix.c,v 1.96 2000/05/12 23:51:26 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. diff --git a/scripts/cramfs/mkcramfs.c b/scripts/cramfs/mkcramfs.c index 51dbbd35b9f..36b12df49ae 100644 --- a/scripts/cramfs/mkcramfs.c +++ b/scripts/cramfs/mkcramfs.c @@ -45,7 +45,7 @@ static void usage(void) /* The kernel assumes PAGE_CACHE_SIZE as block size. */ static unsigned int blksize = PAGE_CACHE_SIZE; -static int warn_dev, warn_gid, warn_link, warn_namelen, warn_size, warn_uid; +static int warn_dev, warn_gid, warn_namelen, warn_size, warn_uid; #ifndef MIN # define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) @@ -224,21 +224,6 @@ static unsigned int parse_directory(struct entry *root_entry, const char *name, perror("mmap"); exit(5); } - if (st.st_nlink > 1) { - /* TODO: Although cramfs doesn't - support hard links, we could still - share data offset values between - different inodes (safe because - read-only). This would give at - least the space saving of hard - links. Just keep a hash mapping - onto struct - entry*. Alternatively, steal some - code from Roger Wolff's `same' - program, which creates a hash of - file data contents. */ - warn_link = 1; - } } close(fd); } else if (S_ISLNK(st.st_mode)) { @@ -618,11 +603,6 @@ int main(int argc, char **argv) if (warn_namelen) /* (can't happen when reading from ext2fs) */ fprintf(stderr, /* bytes, not chars: think UTF8. */ "warning: filenames truncated to 255 bytes.\n"); - if (warn_link) - fprintf(stderr, - "warning: cramfs cannot represent hard links. You may want to change hard links in\n" - " %s with symlinks to other files in %s.\n", - dirname, dirname); if (warn_size) fprintf(stderr, "warning: file sizes truncated to %luMB (minus 1 byte).\n", diff --git a/scripts/header.tk b/scripts/header.tk index 6a88f688106..f2189e23992 100644 --- a/scripts/header.tk +++ b/scripts/header.tk @@ -312,7 +312,7 @@ proc option_name {w mnum line text helpidx} { $w.x$line.l configure -activefore [cget $w.x$line.l -fg] \ -activeback [cget $w.x$line.l -bg] button $w.x$line.help -text "Help" -relief raised \ - -command "dohelp .dohelp $helpidx" + -command "dohelp .dohelp $helpidx .menu$mnum" pack $w.x$line.help -side right -fill y pack $w.x$line.l -side right -fill both -expand on } @@ -325,9 +325,9 @@ proc toggle_switch2 {w mnum line text variable} { -relief groove -width 2 -command "update_active" radiobutton $w.x$line.n -text "n" -variable $variable -value 0 \ -relief groove -width 2 -command "update_active" - + option_name $w $mnum $line $text $variable - + pack $w.x$line.n $w.x$line.m $w.x$line.y -side right -fill y } @@ -339,9 +339,9 @@ proc toggle_switch3 {w mnum line text variable} { -relief groove -width 2 -command "update_active" radiobutton $w.x$line.n -text "n" -variable $variable -value 0 \ -relief groove -width 2 -command "update_active" - + option_name $w $mnum $line $text $variable - + global CONFIG_MODULES if {($CONFIG_MODULES == 0)} then { $w.x$line.m configure -state disabled @@ -400,6 +400,17 @@ proc minimenu { w mnum line text variable helpidx } { pack $w.x$line -anchor w -fill both -expand on } +proc menusplit {w m n} { + if { $n > 2 } then { + set menuoptsize [expr [$m yposition 2] - [$m yposition 1]] + set maxsize [winfo screenheight $w] + set splitpoint [expr $maxsize * 4 / 5 / $menuoptsize - 1] + for {set i [expr $splitpoint + 1]} {$i <= $n} {incr i $splitpoint} { + $m entryconfigure $i -columnbreak 1 + } + } +} + proc submenu { w mnum line text subnum } { frame $w.x$line button $w.x$line.l -text "" -width 15 -relief groove @@ -412,11 +423,20 @@ proc submenu { w mnum line text subnum } { pack $w.x$line -anchor w -fill both -expand on } -proc comment {w line text } { -#nothing done for comments now. +proc comment {w mnum line text } { + frame $w.x$line + button $w.x$line.l -text "" -width 15 -relief groove + $w.x$line.l configure -activefore [cget $w.x$line.l -fg] \ + -activeback [cget $w.x$line.l -bg] -state disabled + button $w.x$line.m -text "$text" -relief groove -anchor w + $w.x$line.m configure -activefore [cget $w.x$line.m -fg] \ + -activeback [cget $w.x$line.m -bg] + pack $w.x$line.l -side left -fill both + pack $w.x$line.m -anchor w -side right -fill both -expand on + pack $w.x$line -anchor w -fill both -expand on } -proc dohelp {w var } { +proc dohelp {w var parent} { catch {destroy $w} toplevel $w -class Dialog @@ -442,45 +462,73 @@ ${var}:\\ " Documentation/Configure.help] set found [expr [string length "$message"] > 0] } - + frame $w.f1 + pack $w.f1 -fill both -expand on + + # Do the OK button + # + set oldFocus [focus] + frame $w.f2 + button $w.f2.ok -text "OK" \ + -width 10 -command "destroy $w; catch {focus $oldFocus}" + pack $w.f2.ok -side bottom -pady 6 -anchor n + pack $w.f2 -side bottom -padx 10 -anchor s + + scrollbar $w.f1.vscroll -command "$w.f1.canvas yview" + pack $w.f1.vscroll -side right -fill y + + canvas $w.f1.canvas -relief flat -borderwidth 0 \ + -yscrollcommand "$w.f1.vscroll set" + frame $w.f1.f + pack $w.f1.canvas -side right -fill y -expand on if { $found == 0 } then { if { $filefound == 0 } then { - message $w.f1.m -width 750 -aspect 300 -relief flat -text \ + message $w.f1.f.m -width 750 -aspect 300 -relief flat -text \ "No help available - unable to open file Documentation/Configure.help. This file should have come with your kernel." } else { - message $w.f1.m -width 400 -aspect 300 -relief flat -text \ + message $w.f1.f.m -width 400 -aspect 300 -relief flat -text \ "No help available for $var" } label $w.f1.bm -bitmap error wm title $w "RTFM" } else { - text $w.f1.m -width 73 -relief flat -wrap word - $w.f1.m insert 0.0 $message - $w.f1.m conf -state disabled -height [$w.f1.m index end] + text $w.f1.f.m -width 73 -relief flat -wrap word + $w.f1.f.m insert 0.0 $message + $w.f1.f.m conf -state disabled -height [$w.f1.f.m index end] label $w.f1.bm -bitmap info wm title $w "Configuration help" } - pack $w.f1.bm $w.f1.m -side left -padx 10 - pack $w.f1 -side top - set oldFocus [focus] - - # Do the OK button - # - frame $w.f2 - button $w.f2.ok -text "OK" \ - -width 10 -command "destroy $w; catch {focus $oldFocus}" - pack $w.f2.ok -side bottom -pady 6 -anchor n - pack $w.f2 -side bottom -padx 10 -anchor s + pack $w.f1.f.m -side left + pack $w.f1.bm $w.f1.f -side left -padx 10 - # Finish off the window - # focus $w - global winx; global winy - set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30] + set winx [expr [winfo x $parent]+20] + set winy [expr [winfo y $parent]+20] wm geometry $w +$winx+$winy + set sizok [expr [winfo reqheight $w.f2.ok] + 12] + set maxy [expr [winfo screenheight .] * 3 / 4] + set canvtotal [winfo reqheight $w.f1.f.m] + if [expr $sizok + $canvtotal < $maxy] { + set sizy $canvtotal + } else { + set sizy [expr $maxy - $sizok] + } + $w.f1.canvas configure -height $sizy -width [winfo reqwidth $w.f1.f.m] \ + -scrollregion "0 0 [winfo reqwidth $w.f1.f.m] \ + [winfo reqheight $w.f1.f.m]" + $w.f1.canvas create window 0 0 -anchor nw -window $w.f1.f + update idletasks + + set maxy [winfo screenheight .] + if [expr $sizok + $canvtotal < $maxy] { + set sizy [expr $sizok + $canvtotal] + } else { + set sizy $maxy + } + wm maxsize $w [winfo width $w] $sizy } proc wrapup {w } { diff --git a/scripts/mkdep.c b/scripts/mkdep.c index 55a9a15a399..aa46a07baa9 100644 --- a/scripts/mkdep.c +++ b/scripts/mkdep.c @@ -300,7 +300,6 @@ void use_config(const char * name, int len) * m|#\s*include\s*<(.*?>"| * m|#\s*(?define|undef)\s*CONFIG_(\w*)| * m|(?!\w)CONFIG_| - * m|__SMP__| * * About 98% of the CPU time is spent here, and most of that is in * the 'start' paragraph. Because the current characters are @@ -325,7 +324,6 @@ __start: CASE('"', dquote); CASE('#', pound); CASE('C', cee); - CASE('_', underscore); goto start; /* / */ @@ -464,18 +462,6 @@ cee_CONFIG_word: goto cee_CONFIG_word; use_config(map_dot, next - map_dot - 1); goto __start; - -/* __SMP__ */ -underscore: - GETNEXT NOTCASE('_', __start); - GETNEXT NOTCASE('S', __start); - GETNEXT NOTCASE('M', __start); - GETNEXT NOTCASE('P', __start); - GETNEXT NOTCASE('_', __start); - GETNEXT NOTCASE('_', __start); - use_config("SMP", 3); - goto __start; - } } diff --git a/scripts/tail.tk b/scripts/tail.tk index 7ca01f25235..3627424519e 100644 --- a/scripts/tail.tk +++ b/scripts/tail.tk @@ -55,6 +55,16 @@ pack .f0.right.store .f0.right.load .f0.right.quit .f0.right.save \ pack .f0.left .f0.middle .f0.right -side left -padx 5 -pady 0 -fill y pack .f0 -padx 5 -pady 5 +update idletasks +set winy [expr 10 + [winfo reqheight .f0]] +set scry [lindex [wm maxsize .] 1] +set winx [expr 10 + [winfo reqwidth .f0]] +set scrx [lindex [wm maxsize .] 0] +if {$winx < $scrx} then {set maxx -1} else {set maxx $winx} +if {$winy < $scry} then {set maxy -1} else {set maxy $winy} +.f0 configure -width $winx -height $winy +wm maxsize . $maxx $maxy + # # If we cannot write our config files, disable the write button. # diff --git a/scripts/tkgen.c b/scripts/tkgen.c index e016218d728..18d968eff8c 100644 --- a/scripts/tkgen.c +++ b/scripts/tkgen.c @@ -265,6 +265,8 @@ void generate_if( struct kconfig * cfg, struct condition * ocond, || cfg->token == token_define_int || cfg->token == token_define_string || cfg->token == token_define_tristate || cfg->token == token_unset ) return; + if ( cfg->token == token_comment && line_num == -1 ) + return; } else { @@ -464,6 +466,7 @@ void generate_if( struct kconfig * cfg, struct condition * ocond, menu_num, line_num ); break; + case token_comment: case token_mainmenu_option: if ( line_num >= 0 ) { @@ -1135,6 +1138,7 @@ void dump_tk_script( struct kconfig * scfg ) case token_bool: case token_choice_header: case token_choice_item: + case token_comment: case token_dep_bool: case token_dep_tristate: case token_dep_mbool: @@ -1189,6 +1193,8 @@ void dump_tk_script( struct kconfig * scfg ) { int menu_line = 0; int nr_submenu = imenu; + int menu_name_omitted = 0; + int opt_count = 0; clear_globalflags(); start_proc( menu_first[imenu]->label, imenu, @@ -1210,6 +1216,21 @@ void dump_tk_script( struct kconfig * scfg ) cfg = menu_last[nr_submenu]; break; + case token_comment: + if ( !cfg->menu_line && !menu_name_omitted ) + { + cfg->menu_line = -1; + menu_name_omitted = 1; + } + else + { + menu_name_omitted = 1; + cfg->menu_line = menu_line++; + printf( "\tcomment $w.config.f %d %d \"%s\"\n", + cfg->menu_number, cfg->menu_line, cfg->label ); + } + break; + case token_bool: cfg->menu_line = menu_line++; printf( "\tbool $w.config.f %d %d \"%s\" %s\n", @@ -1227,8 +1248,10 @@ void dump_tk_script( struct kconfig * scfg ) printf( "\tminimenu $w.config.f %d %d \"%s\" tmpvar_%d %s\n", cfg->menu_number, cfg->menu_line, cfg->label, -(cfg->nameindex), vartable[cfg->next->nameindex].name ); - printf( "\tmenu $w.config.f.x%d.x.menu\n", cfg->menu_line ); + printf( "\tmenu $w.config.f.x%d.x.menu -title \"%s\"\n", + cfg->menu_line, cfg->label ); cfg1 = cfg; + opt_count = 0; break; case token_choice_item: @@ -1236,6 +1259,12 @@ void dump_tk_script( struct kconfig * scfg ) printf( "\t$w.config.f.x%d.x.menu add radiobutton -label \"%s\" -variable tmpvar_%d -value \"%s\" -command \"update_active\"\n", cfg1->menu_line, cfg->label, -(cfg1->nameindex), cfg->label ); + opt_count++; + if ( cfg->next && cfg->next->token != token_choice_item ) { + /* last option in the menu */ + printf( "\tmenusplit $w $w.config.f.x%d.x.menu %d\n", + cfg1->menu_line, opt_count ); + } break; case token_dep_bool: diff --git a/scripts/tkparse.c b/scripts/tkparse.c index bf27a3a8b4c..e17f109a49d 100644 --- a/scripts/tkparse.c +++ b/scripts/tkparse.c @@ -130,7 +130,7 @@ static const char * get_string( const char * pnt, char ** label ) static const char * get_qstring( const char * pnt, char ** label ) { char quote_char; - char newlabel [1024]; + char newlabel [2048]; char * pnt1; /* advance to the open quote */ @@ -705,7 +705,7 @@ static void tokenize_line( const char * pnt ) */ static void do_source( const char * filename ) { - char buffer [1024]; + char buffer [2048]; FILE * infile; const char * old_file; int old_lineno; -- 2.11.4.GIT