From 9336d46c64d8c87f84d372ebfad5dc9750aebf5f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:13:22 -0500 Subject: [PATCH] Import 2.1.43pre1 --- CREDITS | 12 + Documentation/00-INDEX | 2 + Documentation/Changes | 91 +- Documentation/Configure.help | 326 +- Documentation/binfmt_misc.txt | 83 + Documentation/transname.txt | 264 + MAINTAINERS | 6 + Makefile | 2 +- arch/alpha/config.in | 4 +- arch/alpha/kernel/osf_sys.c | 45 +- arch/alpha/math-emu/fp-emul.c | 28 +- arch/i386/config.in | 2 + arch/i386/defconfig | 5 + arch/i386/kernel/head.S | 4 +- arch/i386/kernel/irq.c | 19 +- arch/i386/kernel/irq.h | 14 - arch/i386/kernel/smp.c | 14 +- arch/i386/kernel/time.c | 2 +- arch/i386/kernel/traps.c | 2 - arch/m68k/amiga/amifb.c | 6 + arch/m68k/amiga/amikeyb.c | 14 +- arch/m68k/amiga/amisound.c | 8 +- arch/m68k/amiga/config.c | 1 + arch/m68k/atari/ataints.c | 43 +- arch/m68k/atari/atakeyb.c | 71 +- arch/m68k/console/fbcon.c | 4 +- arch/m68k/fpsp040/skeleton.S | 77 +- arch/m68k/kernel/console.c | 74 +- arch/m68k/kernel/entry.S | 160 +- arch/m68k/kernel/setup.c | 4 +- arch/m68k/kernel/traps.c | 2 +- arch/m68k/mm/init.c | 3 +- arch/m68k/mm/memory.c | 90 +- arch/sparc/config.in | 1 + arch/sparc/defconfig | 4 + arch/sparc64/config.in | 3 + arch/sparc64/kernel/entry.S | 497 +- arch/sparc64/kernel/etrap.S | 30 +- arch/sparc64/kernel/hack.S | 6 +- arch/sparc64/kernel/head.S | 4 +- arch/sparc64/kernel/ioctl32.c | 502 +- arch/sparc64/kernel/process.c | 46 +- arch/sparc64/kernel/rtrap.S | 159 +- arch/sparc64/kernel/signal.c | 10 +- arch/sparc64/kernel/signal32.c | 67 +- arch/sparc64/kernel/sys_sparc32.c | 1199 ++- arch/sparc64/kernel/systbls.S | 10 +- arch/sparc64/kernel/traps.c | 164 +- arch/sparc64/kernel/ttable.S | 6 +- arch/sparc64/kernel/winfixup.S | 280 +- arch/sparc64/lib/checksum.S | 76 +- arch/sparc64/lib/copy_from_user.S | 16 +- arch/sparc64/lib/copy_to_user.S | 16 +- arch/sparc64/lib/memset.S | 8 +- arch/sparc64/lib/strlen_user.S | 12 +- arch/sparc64/lib/strncpy_from_user.S | 6 +- arch/sparc64/mm/fault.c | 39 +- arch/sparc64/vmlinux.lds | 1 + drivers/ap1000/ringbuf.c | 1 - drivers/block/genhd.c | 141 +- drivers/block/ide.c | 40 +- drivers/block/ide.h | 2 +- drivers/block/loop.c | 2 +- drivers/block/md.c | 5 +- drivers/char/Makefile | 3 + drivers/char/atarimouse.c | 2 +- drivers/char/console.c | 10 +- drivers/char/consolemap.c | 2 +- drivers/char/dsp56k.c | 7 +- drivers/char/fbmem.c | 2 +- drivers/char/keyboard.c | 37 +- drivers/char/lp_m68k.c | 1 - drivers/char/mem.c | 97 +- drivers/char/misc.c | 4 +- drivers/char/pc_keyb.c | 414 +- drivers/char/pc_keyb.h | 107 + drivers/char/psaux.c | 571 +- drivers/char/rtc.c | 62 +- drivers/char/selection.c | 14 +- drivers/char/softdog.c | 4 + drivers/char/sysrq.c | 248 + drivers/char/tga.c | 10 +- drivers/char/tty_io.c | 6 +- drivers/char/vc_screen.c | 4 +- drivers/char/vga.c | 10 +- drivers/char/vt.c | 8 +- drivers/isdn/hisax/callc.c | 8 +- drivers/net/Config.in | 9 +- drivers/net/arcnet.c | 4 +- drivers/net/baycom.c | 12 +- drivers/net/defxx.c | 50 +- drivers/net/hdlcdrv.c | 22 +- drivers/net/net_init.c | 2 +- drivers/net/soundmodem/Makefile | 9 +- drivers/net/soundmodem/gentbl.c | 291 +- drivers/net/soundmodem/sm.c | 92 +- drivers/net/soundmodem/sm.h | 63 +- drivers/net/soundmodem/sm_afsk1200.c | 249 +- drivers/net/soundmodem/sm_afsk2400_7.c | 297 + drivers/net/soundmodem/sm_afsk2400_8.c | 297 + drivers/net/soundmodem/sm_fsk9600.c | 236 +- drivers/net/soundmodem/sm_hapn4800.c | 443 +- drivers/net/soundmodem/sm_sbc.c | 326 +- drivers/net/soundmodem/sm_wss.c | 399 +- drivers/net/soundmodem/smdma.h | 210 + drivers/pci/pci.c | 48 +- drivers/sbus/char/bwtwo.c | 12 +- drivers/sbus/char/cgfourteen.c | 11 +- drivers/sbus/char/cgsix.c | 11 +- drivers/sbus/char/cgthree.c | 11 +- drivers/sbus/char/creator.c | 7 +- drivers/sbus/char/leo.c | 11 +- drivers/sbus/char/suncons.c | 12 +- drivers/sbus/char/sunfb.c | 12 +- drivers/sbus/char/sunkbd.c | 6 +- drivers/sbus/char/tcx.c | 11 +- drivers/sbus/char/vfc_dev.c | 2 +- drivers/sbus/char/weitek.c | 11 +- drivers/scsi/Config.in | 10 +- drivers/scsi/Makefile | 14 +- drivers/scsi/README.aic7xxx | 6 +- drivers/scsi/aic7xxx.c | 7615 ++++++++++------- drivers/scsi/aic7xxx.h | 7 +- drivers/scsi/aic7xxx.seq | 1127 --- drivers/scsi/aic7xxx/Makefile | 77 + drivers/scsi/aic7xxx/aic7xxx.reg | 1113 +++ drivers/scsi/aic7xxx/aic7xxx.seq | 1150 +++ drivers/scsi/aic7xxx/aic7xxx_asm.c | 482 ++ drivers/scsi/aic7xxx/aic7xxx_asm.h | 69 + drivers/scsi/aic7xxx/bsd_q.h | 157 + drivers/scsi/aic7xxx/gram.y | 1305 +++ drivers/scsi/aic7xxx/scan.l | 248 + drivers/scsi/aic7xxx/scsi_message.h | 41 + drivers/scsi/aic7xxx/sequencer.h | 90 + drivers/scsi/aic7xxx/symbol.c | 451 + drivers/scsi/aic7xxx/symbol.h | 148 + drivers/scsi/aic7xxx_asm.c | 734 -- drivers/scsi/aic7xxx_proc.c | 42 +- drivers/scsi/aic7xxx_reg.h | 1154 +-- drivers/scsi/amiga7xx.c | 16 +- drivers/scsi/g_NCR5380.c | 5 +- drivers/scsi/script_asm.pl | 70 +- drivers/sound/soundcard.c | 2 +- fs/Config.in | 21 + fs/Makefile | 16 +- fs/affs/dir.c | 1 - fs/affs/file.c | 2 - fs/affs/inode.c | 6 +- fs/affs/namei.c | 11 +- fs/affs/symlink.c | 85 - fs/attr.c | 99 + fs/autofs/dir.c | 1 - fs/autofs/init.c | 5 +- fs/autofs/root.c | 1 - fs/autofs/symlink.c | 34 - fs/binfmt_aout.c | 1 + fs/binfmt_elf.c | 3 +- fs/binfmt_misc.c | 505 ++ fs/buffer.c | 11 +- fs/dcache.c | 1223 ++- fs/devices.c | 2 - fs/dquot.c | 6 +- fs/exec.c | 6 +- fs/ext2/balloc.c | 5 + fs/ext2/dir.c | 14 +- fs/ext2/file.c | 1 - fs/ext2/ialloc.c | 6 +- fs/ext2/namei.c | 38 +- fs/ext2/super.c | 12 +- fs/ext2/symlink.c | 57 +- fs/fat/dir.c | 5 +- fs/fat/file.c | 2 - fs/fat/mmap.c | 2 +- fs/fifo.c | 1 - fs/filesystems.c | 5 + fs/hpfs/hpfs_fs.c | 7 +- fs/inode.c | 1164 +-- fs/isofs/dir.c | 4 - fs/isofs/file.c | 1 - fs/isofs/inode.c | 5 +- fs/isofs/namei.c | 64 +- fs/isofs/symlink.c | 42 - fs/minix/bitmap.c | 6 +- fs/minix/dir.c | 1 - fs/minix/file.c | 1 - fs/minix/inode.c | 5 +- fs/minix/namei.c | 15 +- fs/minix/symlink.c | 44 - fs/msdos/msdosfs_syms.c | 5 +- fs/msdos/namei.c | 19 +- fs/namei.c | 1470 +++- fs/nametrans.c | 310 + fs/ncpfs/dir.c | 7 +- fs/ncpfs/file.c | 1 - fs/ncpfs/inode.c | 8 +- fs/ncpfs/mmap.c | 2 +- fs/nfs/dir.c | 23 +- fs/nfs/file.c | 3 +- fs/nfs/inode.c | 8 +- fs/nfs/nfsroot.c | 15 +- fs/nfs/proc.c | 4 +- fs/nfs/read.c | 2 +- fs/nfs/symlink.c | 56 - fs/nfs/write.c | 4 +- fs/nfsd/export.c | 2 +- fs/nfsd/nfsctl.c | 6 +- fs/nfsd/vfs.c | 59 +- fs/open.c | 119 +- fs/pipe.c | 3 +- fs/proc/Makefile | 5 +- fs/proc/arbitrary.c | 58 + fs/proc/array.c | 11 +- fs/proc/base.c | 1 - fs/proc/fd.c | 1 - fs/proc/generic.c | 2 - fs/proc/kmsg.c | 1 - fs/proc/link.c | 52 +- fs/proc/mem.c | 1 - fs/proc/net.c | 1 - fs/proc/omirr.c | 297 + fs/proc/openpromfs.c | 11 +- fs/proc/procfs_syms.c | 5 +- fs/proc/root.c | 45 +- fs/proc/scsi.c | 1 - fs/read_write.c | 7 + fs/readdir.c | 173 +- fs/romfs/inode.c | 62 +- fs/select.c | 34 +- fs/smbfs/dir.c | 7 +- fs/smbfs/file.c | 1 - fs/smbfs/inode.c | 8 +- fs/smbfs/mmap.c | 2 +- fs/stat.c | 18 +- fs/super.c | 108 +- fs/sysv/dir.c | 1 - fs/sysv/file.c | 1 - fs/sysv/ialloc.c | 7 +- fs/sysv/inode.c | 6 +- fs/sysv/namei.c | 15 +- fs/sysv/symlink.c | 44 - fs/ufs/ufs_dir.c | 20 +- fs/ufs/ufs_file.c | 3 +- fs/ufs/ufs_inode.c | 7 +- fs/ufs/ufs_super.c | 7 +- fs/ufs/ufs_symlink.c | 76 +- fs/umsdos/dir.c | 11 +- fs/umsdos/emd.c | 5 +- fs/umsdos/file.c | 2 - fs/umsdos/inode.c | 13 +- fs/umsdos/ioctl.c | 8 +- fs/umsdos/namei.c | 62 +- fs/umsdos/rdir.c | 12 +- fs/umsdos/symlink.c | 64 +- fs/vfat/namei.c | 20 +- include/asm-alpha/byteorder.h | 160 +- include/asm-alpha/fpu.h | 20 + include/asm-alpha/keyboard.h | 4 - include/asm-alpha/namei.h | 9 +- include/asm-alpha/processor.h | 1 + include/asm-i386/bugs.h | 12 +- include/asm-i386/byteorder.h | 79 +- include/asm-i386/keyboard.h | 4 - include/asm-i386/namei.h | 9 +- include/asm-m68k/byteorder.h | 68 +- include/asm-m68k/entry.h | 176 + include/asm-m68k/namei.h | 9 +- include/asm-m68k/semaphore.h | 37 +- include/asm-mips/keyboard.h | 4 - include/asm-ppc/byteorder.h | 72 +- include/asm-ppc/keyboard.h | 4 - include/asm-sparc/byteorder.h | 55 +- include/asm-sparc/namei.h | 73 +- include/asm-sparc/spinlock.h | 2 +- include/asm-sparc64/bitops.h | 74 +- include/asm-sparc64/byteorder.h | 85 +- include/asm-sparc64/checksum.h | 76 +- include/asm-sparc64/fpumacro.h | 43 +- include/asm-sparc64/head.h | 35 +- include/asm-sparc64/namei.h | 73 +- include/asm-sparc64/pgtable.h | 68 +- include/asm-sparc64/psrcompat.h | 4 +- include/asm-sparc64/pstate.h | 30 +- include/asm-sparc64/system.h | 36 +- include/asm-sparc64/uaccess.h | 30 +- include/asm-sparc64/vuid_event.h | 2 - include/linux/affs_fs.h | 3 +- include/linux/atalk.h | 2 + include/linux/binfmts.h | 1 + .../char => include/linux}/console_struct.h | 1 + {drivers/char => include/linux}/consolemap.h | 0 include/linux/dalloc.h | 103 + include/linux/dlists.h | 108 + include/linux/ext2_fs.h | 2 +- include/linux/fs.h | 330 +- .../char/diacr.h => include/linux/kbd_diacr.h | 0 {drivers/char => include/linux}/kbd_kern.h | 0 include/linux/kbd_ll.h | 12 + include/linux/linkage.h | 5 + include/linux/minix_fs.h | 2 +- include/linux/msdos_fs.h | 6 +- include/linux/nametrans.h | 69 + include/linux/nfsd/nfsfh.h | 2 +- include/linux/omirr.h | 17 + include/linux/pci.h | 53 +- include/linux/proc_fs.h | 14 +- include/linux/sched.h | 7 +- {drivers/char => include/linux}/selection.h | 4 +- include/linux/socket.h | 4 +- include/linux/sysctl.h | 51 +- include/linux/sysrq.h | 25 + include/linux/sysv_fs.h | 2 +- include/linux/umsdos_fs.p | 3 +- {drivers/char => include/linux}/vt_kern.h | 0 include/linux/wrapper.h | 10 +- include/net/checksum.h | 1 + include/net/sock.h | 9 + init/main.c | 15 + kernel/exit.c | 9 +- kernel/fork.c | 65 +- kernel/ksyms.c | 11 +- kernel/panic.c | 5 +- kernel/sched.c | 2 + kernel/sys.c | 9 + kernel/sysctl.c | 41 +- mm/filemap.c | 3 +- mm/mlock.c | 6 +- mm/mmap.c | 4 +- mm/mprotect.c | 6 +- mm/mremap.c | 2 +- mm/page_alloc.c | 2 + mm/slab.c | 106 +- mm/swapfile.c | 6 +- net/Config.in | 4 + net/Makefile | 8 + net/README | 1 + net/appletalk/aarp.c | 39 +- net/appletalk/ddp.c | 44 +- net/appletalk/sysctl_net_atalk.c | 46 +- net/ax25/af_ax25.c | 17 +- net/core/dev.c | 2 +- net/core/sock.c | 16 +- net/core/sysctl_net_core.c | 5 + net/ipv4/af_inet.c | 6 +- net/ipv4/devinet.c | 2 +- net/ipv4/fib.c | 24 +- net/ipv4/igmp.c | 13 +- net/ipv4/ip_fragment.c | 8 +- net/ipv4/ipmr.c | 2 +- net/ipv4/packet.c | 7 +- net/ipv4/sysctl_net_ipv4.c | 66 +- net/ipv4/tcp.c | 8 +- net/ipv4/tcp_input.c | 24 +- net/ipv4/tcp_timer.c | 24 +- net/ipv6/af_inet6.c | 3 +- net/ipv6/tcp_ipv6.c | 8 +- net/ipx/af_ipx.c | 48 +- net/netrom/af_netrom.c | 2 - net/netsyms.c | 11 + net/protocols.c | 10 +- net/rose/af_rose.c | 2 - net/socket.c | 11 +- net/sysctl_net.c | 7 - net/unix/af_unix.c | 19 +- net/unix/sysctl_net_unix.c | 9 + net/wanrouter/wanproc.c | 2 - net/x25/af_x25.c | 1 - scripts/Configure | 22 + scripts/header.tk | 23 + scripts/mkdep.c | 1 - scripts/tkcond.c | 4 + scripts/tkgen.c | 28 + scripts/tkparse.c | 10 + scripts/tkparse.h | 1 + 373 files changed, 23949 insertions(+), 11874 deletions(-) create mode 100644 Documentation/binfmt_misc.txt create mode 100644 Documentation/transname.txt create mode 100644 drivers/char/pc_keyb.h create mode 100644 drivers/char/sysrq.c create mode 100644 drivers/net/soundmodem/sm_afsk2400_7.c create mode 100644 drivers/net/soundmodem/sm_afsk2400_8.c create mode 100644 drivers/net/soundmodem/smdma.h delete mode 100644 drivers/scsi/aic7xxx.seq create mode 100644 drivers/scsi/aic7xxx/Makefile create mode 100644 drivers/scsi/aic7xxx/aic7xxx.reg create mode 100644 drivers/scsi/aic7xxx/aic7xxx.seq create mode 100644 drivers/scsi/aic7xxx/aic7xxx_asm.c create mode 100644 drivers/scsi/aic7xxx/aic7xxx_asm.h create mode 100644 drivers/scsi/aic7xxx/bsd_q.h create mode 100644 drivers/scsi/aic7xxx/gram.y create mode 100644 drivers/scsi/aic7xxx/scan.l create mode 100644 drivers/scsi/aic7xxx/scsi_message.h create mode 100644 drivers/scsi/aic7xxx/sequencer.h create mode 100644 drivers/scsi/aic7xxx/symbol.c create mode 100644 drivers/scsi/aic7xxx/symbol.h delete mode 100644 drivers/scsi/aic7xxx_asm.c create mode 100644 fs/attr.c create mode 100644 fs/binfmt_misc.c create mode 100644 fs/nametrans.c create mode 100644 fs/proc/arbitrary.c create mode 100644 fs/proc/omirr.c create mode 100644 include/asm-m68k/entry.h rename {drivers/char => include/linux}/console_struct.h (99%) rename {drivers/char => include/linux}/consolemap.h (100%) create mode 100644 include/linux/dalloc.h create mode 100644 include/linux/dlists.h rename drivers/char/diacr.h => include/linux/kbd_diacr.h (100%) rename {drivers/char => include/linux}/kbd_kern.h (100%) create mode 100644 include/linux/kbd_ll.h create mode 100644 include/linux/nametrans.h create mode 100644 include/linux/omirr.h rename {drivers/char => include/linux}/selection.h (99%) create mode 100644 include/linux/sysrq.h rename {drivers/char => include/linux}/vt_kern.h (100%) diff --git a/CREDITS b/CREDITS index 76fed44b118b..96beb8066a3c 100644 --- a/CREDITS +++ b/CREDITS @@ -508,6 +508,13 @@ S: 906-1001 Bay St. S: Toronto, Ontario, M5S 3A6 S: Canada +N: Richard Günther +E: richard.guenther@student.uni-tuebingen.de +D: binfmt_misc +S: Fichtenweg 3/511 +S: 72076 Tübingen +S: Germany + N: Danny ter Haar E: dth@cistron.nl D: /proc/procinfo, reboot on panic , kernel pre-patch tester ;) @@ -1557,6 +1564,11 @@ S: 820 4th St. N. S: Fargo, North Dakota 58122 S: USA +N: Steven Whitehouse +E: SteveW@ACM.org +D: Linux DECnet project: http://eeshack3.swan.ac.uk/~gw7rrm/DECnet/index.html +D: Minor debugging of other networking protocols. + N: Hans-Joachim Widmaier E: hjw@zvw.de D: AFFS rewrite diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 63466ffd52a7..86a2391ea7e3 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -18,6 +18,8 @@ IO-mapping.txt - how to access I/O mapped memory from within device drivers. SMP.txt - notes, and "To Fix" list for multi-processor Linux. (see smp.tex) +binfmt_misc.txt + - info on the kernel support for extra binary formats. cdrom/ - directory with information on the CD-ROM drivers that Linux has. devices.tex diff --git a/Documentation/Changes b/Documentation/Changes index 0b840fee610f..143d90eecc29 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -29,28 +29,30 @@ English-language HTML version. Also, don't forget http://www.linuxhq.com/ for all your Linux kernel needs. -Last updated: May 12, 1997. +Last updated: May 31, 1997. Current Author: Chris Ricker (gt1355b@prism.gatech.edu). Current Minimal Requirements **************************** Upgrade to at *least* these software revisions before thinking you've -encountered a bug! - -- Kernel modules modutils-2.1.34 -- Gnu C 2.7.2.1 -- Binutils 2.8.0.3 -- Linux C Library 5.4.23 -- Dynamic Linker (ld.so) 1.8.5 -- Linux C++ Library 2.7.2.1 -- Procps 1.01 -- Mount 2.6g -- Net-tools 1.41 +encountered a bug! If you're unsure what version you're currently +running, the suggested command should tell you. + +- Kernel modules modutils-2.1.34 ; insmod -v +- Gnu C 2.7.2.1 ; gcc --version +- Binutils 2.8.1.0.1 ; ld -v +- Linux C Library 5.4.23 ; ls -l /lib/libc.so.* +- Dynamic Linker (ld.so) 1.8.5 ; ldd -v +- Linux C++ Library 2.7.2.1 ; ls -l /usr/lib/libg++.so.* +- Procps 1.01 ; ps --version +- Procinfo 0.11 ; procinfo -v +- Mount 2.6g ; mount --version +- Net-tools 1.41 ; hostname -V - Loadlin 1.6a -- Sh-utils 1.16 -- Autofs 0.3.0 -- NFS 0.4.21 +- Sh-utils 1.16 ; expr --v +- Autofs 0.3.3 ; automount --version +- NFS 0.4.21 ; showmount --version Upgrade notes ************* @@ -86,6 +88,9 @@ available, so if you need to upgrade, use it. If you get a release later than 1.8.5, avoid 1.8.10 as it introduces a few bugs that are fixed in later releases. + If you upgrade to libc-5.4.x, you may also need to upgrade ypbind if +you're using NIS. + Modules ======= @@ -134,6 +139,15 @@ presence. For support for new features like IPv6, upgrade to the latest net-tools. +Memory +====== + + As of 2.1.41, the format of /proc/meminfo has changed. This broke +many memory utils, which have to be upgraded. Get the new procinfo and +procps (which, AFAIK, is not yet available) to fix this. Until you +upgrade, programs which read /proc/meminfo will seg-fault or give an +error. + Mount and network file systems ============================== @@ -178,37 +192,18 @@ parallel port may no longer be where you expect it; for example, LPT1 /dev/lp0 with the new Plug-and-Play driver. If printing breaks with the new driver, try checking your lpd configuration. -How to know the version of the installed programs -************************************************* - - There are some simple methods useful to know the version of the -installed programs and libraries. - -Binutils: ld -v -Gnu C: gcc -v or gcc --version -Kbd: dumpkeys -h -Ld.so: ldd -v -Libc: ls -l /lib/libc.so.* -Libc++: ls -l /usr/lib/libg++.so.* -Modutils: insmod -V -Mount: mount --version -Net-tools: hostname -V -Procps: ps --version -RPM: rpm --version -Sh-utils: expr --v - Where to get the files ********************** Binutils ======== -The 2.8.0.3 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.8.0.3.bin.tar.gz -ftp://sunsite.unc.edu/pub/Linux/GCC/binutils-2.8.0.3.bin.tar.gz +The 2.8.1.0.1 release: +ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.8.1.0.1.bin.tar.gz +ftp://sunsite.unc.edu/pub/Linux/GCC/binutils-2.8.1.0.1.bin.tar.gz Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.8.0.3 -ftp://sunsite.unc.edu/pub/Linux/GCC/release.binutils-2.8.0.3 +ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.8.1.0.1 +ftp://sunsite.unc.edu/pub/Linux/GCC/release.binutils-2.8.1.0.1 Gnu C ===== @@ -258,9 +253,15 @@ Procps utilities ================ The 1.01 release: -ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.01.tgz +ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.01.tar.gz ftp://sunsite.unc.edu/pub/Linux/system/status/ps/procps-1.01.tgz +Procinfo utilities +================== + +The 0.11 release: +ftp://ftp.cistron.nl/pub/people/svm/procinfo-0.11.tar.gz + RPM utilities ============= @@ -304,8 +305,8 @@ ftp://ftp.win.tue.nl/pub/linux/util/mount-2.6g.tar.gz Autofs ====== -The 0.3.0 release: -ftp://ftp.kernel.org/pub/linux/daemons/autofs/autofs-0.3.0.tar.gz +The 0.3.3 release: +ftp://ftp.kernel.org/pub/linux/daemons/autofs/autofs-0.3.3.tar.gz NFS === @@ -321,6 +322,12 @@ The 0.41 release: ftp://ftp.london.uk.eu.org/pub/ipv6/net-tools-1.41.tar.gz ftp://ftp.cs-ipv6.lancs.ac.uk/pub/Code/Linux/Net_Tools/net-tools-1.41.tar.gz +Ypbind +====== + +The 3.2 release: +ftp://ftp.uni-paderborn.de/pub/linux/local/yp/ypbind-3.2.tar.gz + Other Info ========== diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 669b645f1e75..f4b8ef406bc6 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -795,6 +795,20 @@ CONFIG_BINFMT_JAVA will be called binfmt_java.o. If you don't know what to answer at this point then answer Y. +Kernel support for MISC binaries +CONFIG_BINFMT_MISC + This enables the possibility to plug wrapper-driven binary formats + into the kernel. You will like this especially when you use programs + that need an interpreter to run like Java, Python or Emacs-Lisp. And + you don't need CONFIG_BINFMT_JAVA or CONFIG_BINFMT_EM86, as this is + a more general feature. + You can do other nice things, too. Read Documentation/binfmt_misc.txt + to learn how to use this feature. + You must enable CONFIG_PROC_FS to use this part of the kernel. + You may answer M for module support and later load the module when + you have use for it. + If you don't know what to answer at this point, say Y. + Processor type CONFIG_M386 This is the processor type of your CPU. This information is used for @@ -1623,20 +1637,67 @@ CONFIG_SCSI_AHA1740 want to compile it as a module, say M here and read Documentation/modules.txt. -Adaptec AHA274X/284X/294X support +Adaptec AIC7xxx support (includes 274x/284x/294x) CONFIG_SCSI_AIC7XXX Information about this SCSI host adapter is contained in drivers/scsi/README.aic7xxx and in the SCSI-HOWTO, available via ftp - (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it - doesn't work out of the box, you may have to change some settings in - drivers/scsi/aic7xxx.h. It has been reported that the "wide - negotiation" on these cards is not quite working and should be - disabled. Note that the AHA2920 SCSI host adapter is *not* supported - by this driver; choose "Future Domain 16xx SCSI support" instead. If - you want to compile this driver a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called aic7xxx.o. + (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that + the AHA2920 SCSI host adapter is *not* supported by this driver; choose + "Future Domain 16xx SCSI support" instead. If you want to compile this + driver a module ( = code which can be inserted in and removed from the + running kernel whenever you want), say M here and read Documentation/ + modules.txt. The module will be called aic7xxx.o. + +Enable tagged command queueing +CONFIG_AIC7XXX_TAGGED_QUEUEING + This option allows you to enable tagged command queueing for this + driver. Some scsi devices do not properly support this + feature. Tagged command queueing will improve performance. + +Maximum number of commands per LUN + CONFIG_AIC7XXX_CMDS_PER_LUN + This option allows you to set the maximum number of commands queued + per LUN. If tagged queueing is enabled, then you may want to try + increasing AIC7XXX_CMDS_PER_LUN to more than 2. By default, we limit + the SCBs per LUN to 2 with or without tagged queueing enabled. If + tagged queueing is disabled, the sequencer will keep the 2nd SCB in + the input queue until the first one completes - so it is OK to to have + more than 1 SCB queued. If tagged queueing is enabled, then the + sequencer will attempt to send the 2nd SCB to the device while the + first SCB is executing and the device is disconnected. For adapters + limited to 4 SCBs, you may want to actually decrease the commands per + LUN to 1, if you often have more than 2 devices active at the same + time. This will allocate 1 SCB for each device and ensure that there + will always be a free SCB for up to 4 devices active at the same time. + When SCB paging is enabled, set the commands per LUN to 8 or higher + (see SCB paging support below). Note that if AIC7XXX_CMDS_PER_LUN is + not defined and tagged queueing is enabled, the driver will attempt to + set the commands per LUN using its own heuristic based on the number + of available SCBs. + +Enable SCB paging +CONFIG_AIC7XXX_PAGE_ENABLE + This option enables SCB paging. This will increase performance when + tagged queueing is enabled. Note that you should increase the + AIC7XXX_CMDS_PER_LUN to 8 as most tagged queueing devices allow at + least this many + +Use external SCB bank +CONFIG_AIC7XXX_USE_EXT_SCBRAM + This option enables use of the external bank of 255 SCBs. For 3985 + adapters, this will also enable sharing of the SCB array across all + three controllers. + +Collect statistics to report in /proc +CONFIG_AIC7XXX_PROC_STATS + This option enables collection of SCSI transfer statistics for the + /proc filesystem. Do NOT enable this when running on kernels version + 1.2.x and below. This does affect performance since it has to + maintain statistics. + +Delay in seconds after SCSI bus reset +CONFIG_AIC7XXX_RESET_DELAY + This option sets the delay in seconds after a SCSI bus reset. BusLogic SCSI support CONFIG_SCSI_BUSLOGIC @@ -2371,7 +2432,8 @@ CONFIG_SOUNDMODEM_SBC compatible cards. If you have a dual mode card (i.e. a WSS cards with a SoundBlaster emulation) you should say N here and Y to "Soundcard modem support for WSS and Crystal cards", below, because - this usually results in better performance. + this usually results in better performance. This option also supports + SB16/32/64 in full duplex mode. Soundcard modem support for WSS and Crystal cards CONFIG_SOUNDMODEM_WSS @@ -2389,26 +2451,27 @@ CONFIG_SOUNDMODEM_AFSK1200 compatible to popular modems using TCM3105 or AM7911. The demodulator requires about 12% of the CPU power of a Pentium 75 CPU per channel. -Soundmodem 1200 baud AFSK using floating point -CONFIG_SOUNDMODEM_AFSK1200_FP - This option enables floating point calculations to be used for the - AFSK1200 baud modem. The Intel Pentium is a perverted chip because - integer multiplications are, although easier to implement in silicon, - an order of magnitude slower than floating point calculations. - Enabling this option uses a highly optimized assembler routine for - correlations, modeled after the one published by Phil Karn, KA9Q. - This reduces the computing power needed on Intel Pentium chips to - about 50%. On the other hand, Pentium clones with faster integer - multiply and slower floating point multiply will probably take - longer with this option turned on. As a rule of thumb, enable it for - Intel Pentium and Pentium Pro processors, and disable it for - anything else. - I (sailer@ife.ee.ethz.ch) am very interested in figures. If you are - willing to give me a feedback, please compile the driver once with - this option enabled and once with it disabled, and send me the cycle - counter numbers obtained with both compilations, and your exact - chip. The cycle counter numbers can be obtained with a recent - sethdlc utility. +Soundcard modem support for 2400 baud AFSK modulation (7.3728MHz crystal) +CONFIG_SOUNDMODEM_AFSK2400_7 + This option enables the soundmodem driver 2400 baud AFSK modem, + compatible to TCM3105 modems (over-)clocked with a 7.3728MHz crystal. + Note that the availability of this driver does _not_ imply that I + recommend building such links. It is only here since users especially + in eastern europe have asked me to do so. In fact this modulation scheme + has many disadvantages, mainly its incompatibility with many transceiver + designs and the fact that the TCM3105 (if used) is operated widely outside + its specifications. + +Soundcard modem support for 2400 baud AFSK modulation (8MHz crystal) +CONFIG_SOUNDMODEM_AFSK2400_8 + This option enables the soundmodem driver 2400 baud AFSK modem, + compatible to TCM3105 modems (over-)clocked with an 8MHz crystal. + Note that the availability of this driver does _not_ imply that I + recommend building such links. It is only here since users especially + in eastern europe have asked me to do so. In fact this modulation scheme + has many disadvantages, mainly its incompatibility with many transceiver + designs and the fact that the TCM3105 (if used) is operated widely outside + its specifications. Soundcard modem support for 4800 baud HAPN-1 modulation CONFIG_SOUNDMODEM_HAPN4800 @@ -2426,27 +2489,6 @@ CONFIG_SOUNDMODEM_FSK9600 can only use one protocol at a time, depending on what the other end can understand). -Soundcard modem support using floating point arithmetic -CONFIG_SOUNDMODEM_FLOAT - This option enables floating point calculations to be used for the - AFSK1200 baud modem. The Intel Pentium is a perverted chip because - integer multiplications are, although easier to implement in silicon, - an order of a magnitude slower than floating point calculations. - Enabling this option uses a highly optimized assembler routine for - correlations, modeled after the one published by Phil Karn, KA9Q. - This reduces the computing power needed on Intel Pentium chips to - about 50%. On the other hand, Pentium clones with faster integer - multiply and slower floating point multiply will probably take - longer with this option turned on. As a rule of thumb, enable it for - Intel Pentium and Pentium Pro processors, and disable it for - anything else. - I (sailer@ife.ee.ethz.ch) am very interested in figures. If you are - willing to give me a feedback, please compile the driver once with - this option enabled and once with it disabled, and send me the cycle - counter numbers obtained with both compilations, and your exact - chip. The cycle counter numbers can be obtained by a recent sethdlc - utility. - Serial port KISS driver for AX.25 CONFIG_MKISS KISS is the protocol used to send IP traffic over AX.25 radio @@ -3617,6 +3659,41 @@ CONFIG_ISP16_CDI be called isp16.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +New inode implementation +CONFIG_NEW_INODE + This option should disappear in future when the new inode cache + becomes standard. For the meantime, you may switch back to the old + code if you have trouble with the new one. + +New dcache implementation +CONFIG_NEW_DCACHE + The new dcache is a generic one. In contrast to the old dcache, + it operates on the VFS level, independently from the concrete + filesystems. One benefit of this option is that all symlinks + in /proc//fd now point to the real file location, with + full absolute path. + The main benefit of this option is the *basket*: + Any deleted file "foo" will be automatically renamed to + ".deleted-.foo" in a non-permanent way, so that it remains + accessible as long as the machine is not rebooted. Currently, a + maximum number of 100 files can be kept in the basket (the limit + is because it eats up inodes). If space gets low on the filesystem, + the basket will (should) be automatically cleared until enough + space is available. This currently works only with ext2, but other + filesystems need only little modification to support this also. + Please check whether (and in which cases) you get + speedups by the new code. Before making measurements, be sure that + all debugging code is #undef'ed from fs/ndcache.c, fs/ninode.c, + and fs/namei.c . + +Preload dcache +CONFIG_DCACHE_PRELOAD + Preloading will create dcache entries when a directory is scanned + (e.g. with ls) the *first* time. This should speed up successive + inode lookups, but also can consume large amounts of memory. + Please report speedups (or slowdowns due to the memory usage if they + occur) to schoebel@informatik.uni-stuttgart.de . + Quota support CONFIG_QUOTA If you say Y here, you will be able to set per user limits for disk @@ -3627,6 +3704,138 @@ CONFIG_QUOTA quota support is only useful for multi user systems. If unsure, say N. +Online mirror support +CONFIG_OMIRR + omirr is a package for _symmetric_ mirroring of files over the + internet. In contrast to rdist, the online mirror daemon (omirrd) + is running all the time and transfers any changes on the file system + as soon as possible to all other servers. Symmetric means that all + servers have equal rights in changing a file: the last changer of a + file will win. This is the same behaviour as multiple processes + operating on a global file system. In effect, omirr can do the same + as nfs mounts, but will have better performance since the data is + stored on local disks. In contrast to a cache filesystem which has a + dedicated master copy, broken connections and/or servers are no problem + for continuing work on the remaining ones, because there is no master + copy. You must say Y if you want to use omirrd, but you should (but + need not) say N if you don't (for performance reasons). + +Filename translation support +CONFIG_TRANS_NAMES + Normally used only when you want diskless clients to mount the root + filesystem of the server. If unsure, or if you don't have clients, select N. + When selected, filenames, directory names etc become context-sensitive. + If you have a file named "/etc/config#host=banana#", it will appear + (by default) as hardlinked to "/etc/config" on host "banana", while on host + "mango" another file "/etc/config#host=mango#" will appear as been + hardlinked to "/etc/config". The default behaviour can be changed + by setting the _first_ environment variable NAMETRANS to a colon-separated + list of suffixes which are tried in the specified order. For example, + in 'env - NAMETRANS=#host=mango#:#ktype=diskless# "`env`" command ...' the + command will see the same files as if it had been executed on host "mango" + with a diskless kernel. Using NAMETRANS supersedes _all_ default + translations. Thus translations can be generally switched off by an + empty list, e.g. 'env - NAMETRANS= "`env`" command ...'. + Note that some system utililies like tar, dump, restore should + be used with translation switched off, in order to avoid doubled + space in archive files and when extracting from them. Also, make sure + that nfsd, mountd (and similar ones like samba daemons) run without + translation, in order to avoid doubled (or even wrong) translation + at the server and at the client. You can automatically force the creation + of context-dependent filenames if there exists a template filename like + "/etc/mtab#host=CREATE#". As soon as a process running on "mango" tries + to create a file "/etc/mtab", the version "/etc/mtab#host=mango#" is + created instead (which appears in turn as hardlinked to "/etc/mtab"). + Note that if you want to make "/etc/fstab" context-dependend, you should + execute "touch /etc/mtab#host=CREATE#" and + "touch /etc/mtab.tmp#host=CREATE#", because mount, umount and others + running on different hosts would otherwise try to create one shared + /etc/mtab which would result in a clash. Also one should execute + "touch /etc/nologin#host=CREATE#" to prevent global side effects from + shutdown resp. runlevel. + +Restrict translation to gid +CONFIG_TRANS_RESTRICT + When selected, default translations are carried out only if the parent + directory of the context-sensitive file belongs to a specific group id + (gid). Trying to translate names everywhere will decrease performance of + file openings. Normally translations are used only in system configuration + files but not in ordinary user filespace. So you should change the gid of + directories containing context-dependent files to some special group like + "adm" (group id 4) and enable this option. As a result, users will not + notice any performance degradation resulting from filename translation. + Note that translations resulting from the first environment variable + "NAMETRANS=..." are always carried out regardless of the gid of directories. + Beware, before turning on this option make sure all directories containing + context-dependent files belong to the special group, or system + initialization may fail. In unsure, select N. + +Group id (gid) for translation restriction +CONFIG_TRANS_GID + Default name translations will be carried out only inside directories + belonging to the group id (gid) you can specify here. + Default is 4 (group "adm"). + +Nodename (hostname) translation +CONFIG_TR_NODENAME + Enables translation of name suffixes like in "/etc/config#host=banana#". + The syntax is #host=#. The hostname can be queried + with the command "uname -n". Normally this option is used heavily when + translation is enabled. If unsure, say Y. + +Kernelname translation +CONFIG_TR_KERNNAME + Enables translation of name suffixes like in "/etc/config#kname=default#". + The string is hard compiled into the kernel by the following option. + Useful if your kernel does not know the hostname at boot time, and there + is no way to tell the hostname by lilo or bootp. Please avoid using this + option and prefer CONFIG_TR_NODENAME wherever possible. When mounting + the root over nfs, the own hostname must be known at boot time anyway; + this option is just for special use. + Note that the default translations are tried in the order as occuring + in the configuration, that is 1) host 2) kname 3) ktype 4) machine + 5) system. If unsure, say Y. + +String for kernelname translation +CONFIG_KERNNAME + Enter the string you want to compile into the kernel. The string + will be used as context in context-depenant file like + "/etc/config#kname=#". + +Kerneltype translation +CONFIG_TR_KERNTYPE + Enables translation of name suffixes like in "/etc/config#ktype=default#". + The syntax is #ktype=#. The string is hard compiled + in the kernel by the following option. Use if you want to create + different kernels with different behaviour. For example, use the string + "default" on your server, and use "diskless" on all your diskless clients + (and perhaps "dataless" on dataless clients). This way you can avoid + dozens of "config#host=# with same contents and you have no + effort when new machines are added. If unsure, say Y. + +String for kerneltype translation +CONFIG_KERNTYPE + Enter the string you want to compile into the kernel. The string + will be used as context in context-depenant file like + "/etc/config#ktype=default#". If your kernel is to be used on a server, + you probably can use "default" here. If your kernel is intended for + a diskless client, you probably should enter "diskless" here. + +Machine type translation +CONFIG_TR_MACHINE + Enables translation of name suffixes like in "/etc/config#machine=i486#". + The syntax is #machine=#. The machine types can be queried + with the command "uname -m". Normally used only on multi-architecture + installations. If unsure, say Y. + +System name translation +CONFIG_TR_SYSNAME + Enables translation of name suffixes like in "/etc/config#system=Linux#". + The syntax is #system=#. The system name can be queried + with the command "uname -s". Currently only supportet by Linux, but + hopefully other operating systems will pick up the idea of context-dependent + translations. If unsure, say Y. + Minix fs support CONFIG_MINIX_FS Minix is a simple operating system used in many classes about @@ -4702,6 +4911,17 @@ CONFIG_PROFILE_SHIFT said Y to "Kernel profiling support", you must be a kernel hacker and hence you know what this is about :-) +Magic System Request Key support +CONFIG_MAGIC_SYSRQ + This is for kernel hackers who want to have some control over the + system even if the system crashes during kernel debugging (e.g., to + flush the disks, reboot the system immediately or dump some status + information). This is accomplished by pressing various keys while + holding SysRq (Alt+PrintScreen). As you are expected to be a kernel + hacker to use this, the simple rule about learning what do the keys + mean is "Use the source, Luke!" -- read drivers/char/sysrq.c. + Don't say Y unless you really know what does this hack do. + ISDN subsystem CONFIG_ISDN ISDN ("Integrated Services Digital Networks", called RNIS in France) diff --git a/Documentation/binfmt_misc.txt b/Documentation/binfmt_misc.txt new file mode 100644 index 000000000000..81d49b2bcaa2 --- /dev/null +++ b/Documentation/binfmt_misc.txt @@ -0,0 +1,83 @@ + Kernel Support for miscellaneous (your favourite) Binary Formats v1.1 + ==================================================================== + +This Kernel feature allows to invoke almost (for restrictions see below) every +program by simply typing it's name in the shell. +This includes for example compiled Java(TM), Python or Emacs programs. + +To achieve this you must tell binfmt_misc which interpreter has to be invoked with +which binary. Binfmt_misc recognises the binary-type by matching some bytes at the +beginning of the file with a magic byte sequence (masking out specified bits) you +have supplied. Binfmt_misc can also recognise a filename extension (aka .com) and +optionally strip it off. + +To actually register a new binary type, you have to set up a string looking like +:name:type:offset:magic:mask:interpreter: (where you can choose the ':' upon your +needs) and echo it to /proc/sys/fs/binfmt_misc/register. +Here is what the fields mean: + - 'name' is an identifier string. A new /proc file will be created with this + this name below /proc/sys/fs/binfmt_misc + - 'type' is the type of recognition. Give 'M' for magic and 'E' for extension. + Give the corresponding lowercase letter to let binfmt_misc strip of the + filename extension. + - 'offset' is the offset of the magic/mask in the file counted in bytes. This + defaults to 0 if you omit it (i.e. you write ':name:type::magic...') + - 'magic' is the byte sequence binfmt_misc is matching for. The magic string + may contain hex-encoded characters like \x0a or \xA4. In a shell environment + you will have to write \\x0a to prevent the shell from eating your \. + If you chose filename extension matching, this is the extension to be + recognised (the \x0a specials are not allowed). Extension matching is case + sensitive! + - 'mask' is an (optional, defaults to all 0xff) mask. You can mask out some bits + from matching by supplying a string like magic and as long as magic. The + mask is anded with the byte sequence of the file. + - 'interpreter' is the program that should be invoked with the binary as first + argument (specify the full path) +There are some restrictions: + - the whole register string may not exceed 255 characters + - the magic must resist in the first 128 bytes of the file, i.e. offset+size(magic) + has to be less than 128 + - the interpreter string may not exceed 127 characters +You may want to add the binary formats in one of your /etc/rc scripts during boot-up. +Read the manual of your init program to figure out how to do this right. + + +A few examples (assumed you are in /proc/sys/fs/binfmt_misc): + +- enable Java(TM)-support (like binfmt_java): + echo ":Java:M::\xca\xfe\xba\xbe::/usr/local/bin/java:" > register + echo :Applet:M::\<\!--applet::/usr/local/bin/appletviewer: > register +- enable support for em86 (like binfmt_em86, for Alpha AXP only): + echo ":i386:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register + echo ":i486:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register +- enable support for packed DOS applications (pre-configured dosemu hdimages): + echo ":DEXE:M::\x0eDEX::/usr/bin/dosexec:" > register +- enable support for DOS/Windows executables (using mzloader and dosemu/wine): + echo ":DOSWin:M::MZ::/usr/sbin/mzloader:" > register + echo ":DOS:E::com::/usr/sbin/mzloader:" > register + + +You can enable/disable binfmt_misc or one binary type by echoing 0 (to disable) +or 1 (to enable) to /proc/sys/fs/binfmt_misc/status or /proc/.../the_name. +Catting the file tells you the current status of binfmt_misc/the entry. + +You can remove one entry or all entries by echoing -1 to /proc/.../the_name +or /proc/sys/fs/binfmt_misc/status. + + +HINTS: +====== + +If your interpreter does not look at the PATH to determine the full name of the +program, you need to invoke a wrapper-script (like the following for java) first: + +#!/bin/sh +FOO=`which $1` || exit 1 +shift +/usr/local/bin/java $FOO ${1+$@} + + +There is a web page about binfmt_misc at +http://www.anatom.uni-tuebingen.de/~richi/linux/binfmt_misc.html + +Richard Günther, richard.guenther@student.uni-tuebingen.de diff --git a/Documentation/transname.txt b/Documentation/transname.txt new file mode 100644 index 000000000000..ed0a82177860 --- /dev/null +++ b/Documentation/transname.txt @@ -0,0 +1,264 @@ +Transname version 1.9 (C) 1997 Thomas Schoebel-Theuer + +transname enables diskless clients, X-terminals etc to mount the +*root filesystem* of the server. This make administration of +large pools a lot easier. + +Wherefore is linux-2.0.21-transname.patch? + +Currently different diskless clients must have their root / on different +directories on the server, beause each client has _some_ different +configuration files. However, most files (typically about 99%) have the same +contents on the clients and on the server, but have to be replicated +(and maintained separately) just because of the 1% differences. +This duplication causes very large efforts in practise, since at least +the /etc directory has to be duplicated for every client. Even in /etc +many files are identical, for example sendmail.cf, initrc scripts and +others. Maintaining a large pool means to ensure coherence amoung the +duplicates. Classical methods like symlinks are unconvenient +for this task because they have to be valid in the view of mounted +filesystems at all clients, not at the server. + +Linux transname overcomes this problem by allowing filenames +to be context-dependend. For example, if you have a file /etc/config +that should differ on the hosts "myserver" and "myclient", you just +create two different files named /etc/config#host=myserver# and +/etc/config#host=myclient# . On host "myserver", the file +/etc/config#host=myserver# will appear as if it were hardlinked to +file /etc/config (without the #...=...# suffix). On host "myclient", +the corresponding other file will appear as /etc/config. So you +can access the right file contents under the _same_ name, depending +on which host you are working. + +A similar concept can be found in elder HP-UX versions, but with +so-called "hidden directories" which don't allow contemporary viewing +all versions by default. In contrast, transname shows all context-dependent +files in the dir listing and they can be edited using the +fully qualified name. + +Transname was developped for and is used at our Linux pool at the +University of Stuttgart with good results. Maintainance of the pool is +at a minimum, and adding new clients is a child's play. No worry with +keeping up mail configurations, newly installed tools, changed /etc/services, +/etc/shells, /etc/resolv.conf and many, many others. In contrast to a +sophisticated symlink solution, adding a new file to the /etc directory +is seen immediately by all clients. + +An example for the use of linux-2.0-transname.patch: + +For example, you can make your /etc/fstab context-dependend. If you want +to do that, you should create an /etc/fstab#ktype=default# for the +server and an /etc/fstab#ktype=diskless# for all clients. This is because +your clients may not yet know their own hostname when they attempt to mount +the root filesystem. You can compile in the kerneltypes "default" and +"diskless" into different kernels for servers and clients. Of course, +if your clients boot via bootp and know their names when mounting the root, +you can use /etc/fstab#host=myclient# instead. But at least servers +booting from disk normally dont know their hostname at root mount time, +so you can mix methods and use /etc/fstab#ktype=default# for the server, +/etc/fstab#ktype=diskless# for the majority of the clients and +/etc/fstab#host=myclient# for some specific client, because translation +of #host=...# is given precedence over #ktype=...# by default. + +This sort of name translation works with any underlying file system +and with any inode type (i.e. with directories, symlinks, devices etc), +because it is implemented in the VFS layer of the kernel. Currently, +five types of default translations are supported: + + * #host=# depends on the hostname, see "uname -n" + * #kname=# works with a hard compiled-in string + * #ktype=# works with a hard compiled-in string + * #machine=# depends on architecture, see "uname -m" + * #system=# currently only supported by Linux, see "uname -s" + +Others may be added in future. + +The current translation are displayed at boot time in the kernel messages +for easier debugging, and can be retrieved by reading +/proc/sys/kernel/nametrans which is a special file containing the currently +valid translations. + +The default translations change whenever the hostname(1) is set or changed. +Thus, the hostname is not set (or set to the name "(none)") at boot time +before init(8) sets it. So, if you want to use the hostname before that +moment, there are three ways: + + a) set the hostname before via bootp or similar. + b) use the compiled-in translations kname and ktype solely. + c) set all translations by lilo (or on the boot command line) with + kernel parameter nametrans=#host=banana#:#ktype=diskless# , + thus overriding and hiding the default (built-in) translations. + +Note that by supplying the colon-separated list of at most 16 suffixes, you +can also use other translation types that are not defined in the default +translations. However, you must ensure that the syntax #...=...# is correct. +The specified contexts will be tried in the specified order *instead* of the +default translations. + +You can override the default (or parameter-supplied) translations at runtime +by executing +echo "#host=$HOST#:#ktype=diskless#:#myconfig=something#" > /proc/sys/kernel/nametrans +However, after doing this (or setting as kernel parameter) the built-in +default translations have no effect any more, thus changing the hostname +will not be reflected in the overridden translations. You can switch +back to the default translations by executing +echo "" > /proc/sys/kernel/nametrans + +Another drawback is that administration tools currently are not aware of +context-dependend files, so you cannot switch between contexts inside +one tool session. However, you can simulate administration sessions +on the server as if they were running on some client. To do this, +you have to set an environment variable NAMETRANS which has to be the +*first* environment variable in the list. For example, you can execute +'env - NAMETRANS=#host=mango#:#ktype=diskless# "`env`" command ...' +where the command will see the same files as if it had been executed on host +"mango" with a "diskless" kernel. To switch off translations entirely, use +an empty list, e.g. 'env - NAMETRANS= "`env`" command ...'. + +Hopefully the creators of administration tools and maintainers of Linux +distributions will support changing environments in future, so that +maintaining different views will be very easy. + +Some hints: + +Archivers like tar, dump, restore should be used with translation +switched off, in order to avoid doubled space in archive files and when +extracting from them. Also, make sure that nfsd, mountd (and similar ones +like samba daemons) run without translation, in order to avoid doubled +(or even wrong) translation at the server and at the client. You can +automatically force the creation of context-dependent filenames if there +exists a template filename like /etc/mtab#host=CREATE#. As soon as a +process running on "mango" tries to create a file /etc/mtab, the version +/etc/mtab#host=mango# is created instead (which appears in turn as +hardlinked to /etc/mtab). Note that if you want to make /etc/fstab +context-dependend, you should execute "touch /etc/mtab#host=CREATE#" and +"touch /etc/mtab.tmp#host=CREATE#", because mount, umount and others +running on different hosts would otherwise try to create one shared +/etc/mtab which would result in a clash. Also one should execute +"touch /etc/nologin#host=CREATE#" to prevent global side effects from +shutdown resp. runlevel. + +Which files you have to make context-dependent will differ for different +needs and different applications. Hopefully some day a standard will +cover the most common cases and the mist common Linux distributions. +A HOWTO on this subject is in preparation. + +How to install linux-2.0.21-transname.patch? + +First of all, keep a backup of your kernel on your disk. Second, keep a +floppy with a miniroot handy, so you can boot from the floppy, mount +your harddisk root filesystem and change the names of your configuration +files back to their old names in case of emergency. + +Then, make a kernel with transname support enabled. With "make config" +or "make xconfig", just go to the section "filesystems". Take a look at +the help texts that are associated with the transname options, they tell +you further hints not mentioned in this README. Then build your kernel as +usual, install it with a *new* kernel-filename, add a *new* entry to +/etc/lilo.conf and run lilo. **DONT CHANGE** any configuration files for the +first reboot! + +Just reboot the new kernel and play a little bit around with +creating context-dependend filenames in your home directory. +Try all modes including setting NAMETRANS to different values. + +As an example for the changes necessary on our LST-1.8-based Linux pool, +here is the output of the command +find / /tmp -xdev -name "*#*#" -print | sort -u | xargs ls -ld + +-r--r--r-- 1 root root 1725 Dec 21 1995 /etc/X11R6/xdm/xdm-config#host=eiche# +-r--r--r-- 3 root root 9509 Feb 15 17:35 /etc/XF86Config#host=balsa# +-r--r--r-- 1 root root 9401 Feb 15 17:34 /etc/XF86Config#host=eiche# +-rw-r--r-- 1 root root 9820 Feb 21 17:00 /etc/XF86Config#host=fichte# +-rw-r--r-- 1 root root 9822 Feb 14 15:45 /etc/XF86Config#host=laerche# +-r--r--r-- 3 root root 9509 Feb 15 17:35 /etc/XF86Config#host=mahagoni# +-r--r--r-- 3 root root 9509 Feb 15 17:35 /etc/XF86Config#host=palisander# +-r--r--r-- 2 root root 9509 Feb 15 17:41 /etc/XF86Config#host=pcbs10# +-r--r--r-- 2 root root 9509 Feb 15 17:41 /etc/XF86Config#host=pcbs11# +-rw-r--r-- 1 root root 586 Jun 11 23:13 /etc/fstab#ktype=default# +-rw-r--r-- 1 root root 242 May 29 17:35 /etc/fstab#ktype=diskless# +-rw------- 1 root root 338 Jun 14 16:37 /etc/lilo.conf#host=eiche# +-rw------- 1 root root 5236 Dec 16 1995 /etc/lst.cnf#host=balsa# +-rw------- 1 root root 5254 Dec 16 1995 /etc/lst.cnf#host=eiche# +-rw------- 1 root root 5236 Dec 19 1995 /etc/lst.cnf#host=fichte# +-rw------- 1 root root 5236 Jan 11 13:47 /etc/lst.cnf#host=laerche# +-rw------- 1 root root 5236 Feb 14 16:57 /etc/lst.cnf#host=mahagoni# +-rw------- 1 root root 5236 Jan 4 1996 /etc/lst.cnf#host=palisander# +-rw------- 1 root root 5236 Feb 15 13:57 /etc/lst.cnf#host=pcbs10# +-rw------- 1 root root 5236 Feb 14 17:06 /etc/lst.cnf#host=pcbs11# +-rw-r--r-- 1 root root 0 Dec 18 1995 /etc/mtab#host=CREATE# +-rw-r--r-- 1 root root 157 Jun 23 21:16 /etc/mtab#host=balsa# +-rw-r--r-- 1 root root 466 Jul 1 16:15 /etc/mtab#host=eiche# +-rw-r--r-- 1 root root 239 Jul 4 11:10 /etc/mtab#host=fichte# +-rw-r--r-- 1 root root 239 Jun 18 14:17 /etc/mtab#host=laerche# +-rw-r--r-- 1 root root 239 May 23 10:50 /etc/mtab#host=mahagoni# +-rw-r--r-- 1 root root 239 Jul 3 10:36 /etc/mtab#host=palisander# +-rw-r--r-- 1 root root 47 Feb 15 14:57 /etc/mtab#host=pcbs10# +-rw-r--r-- 1 root root 47 Feb 14 20:04 /etc/mtab#host=pcbs11# +-rw-r--r-- 1 root root 0 Dec 18 1995 /etc/mtab.tmp#host=CREATE# +-rw-r--r-- 1 root root 0 Dec 19 1995 /etc/nologin#host=CREATE# +---------- 1 root root 115 Feb 15 14:57 /etc/nologin#host=pcbs10# +---------- 1 root root 115 Feb 14 20:04 /etc/nologin#host=pcbs11# +-rw-r--r-- 1 root root 4818 Dec 16 1995 /etc/system.cnf#host=balsa# +lrwxrwxrwx 1 root root 25 Dec 22 1995 /etc/system.cnf#host=eiche# -> system.cnf#ktype=default# +-rw-r--r-- 1 root root 4821 Dec 19 1995 /etc/system.cnf#host=fichte# +-rw-r--r-- 1 root root 4824 Jan 11 13:47 /etc/system.cnf#host=laerche# +-rw-r--r-- 1 root root 4827 Feb 14 16:57 /etc/system.cnf#host=mahagoni# +-rw-r--r-- 1 root root 4833 Jan 4 1996 /etc/system.cnf#host=palisander# +-rw-r--r-- 1 root root 4840 Feb 15 14:10 /etc/system.cnf#host=pcbs10# +-rw-r--r-- 1 root root 4846 Feb 14 18:23 /etc/system.cnf#host=pcbs11# +-rw-r--r-- 1 root root 4818 Dec 13 1995 /etc/system.cnf#ktype=default# +drwxrwxrwt 16 root root 3072 Jul 4 14:29 /tmp#ktype=default# +lrwxrwxrwx 1 root root 26 Jul 4 14:22 /tmp#ktype=diskless# -> /tmp#ktype=default#/client +-rw-rw-rw- 1 root root 0 Feb 15 14:57 /tmp/client#host=CREATE# +drwxrwxrwx 4 root root 1024 Jun 28 12:15 /tmp/client#host=balsa# +drwxrwxrwx 3 root root 1024 Jul 4 11:10 /tmp/client#host=fichte# +drwxrwxrwx 3 root root 1024 Jun 18 14:18 /tmp/client#host=laerche# +drwxrwxrwx 3 root root 1024 May 24 13:06 /tmp/client#host=mahagoni# +drwxrwxrwx 3 root root 1024 Jul 3 10:37 /tmp/client#host=palisander# +drwxrwxrwx 4 root root 1024 Feb 15 14:57 /tmp/client#host=pcbs10# +drwxrwxrwx 3 root root 1024 Feb 20 06:43 /tmp/client#host=pcbs11# +lrwxrwxrwx 1 root root 9 Feb 15 13:58 /usr/X11R6/bin/X#host=pcbs10# -> XF86_SVGA +lrwxrwxrwx 1 root root 9 Feb 14 17:37 /usr/X11R6/bin/X#host=pcbs11# -> XF86_SVGA +lrwxrwxrwx 1 root root 7 Feb 14 17:15 /usr/X11R6/bin/X#ktype=diskless# -> XF86_S3 +drwxr-xr-x 22 root root 1024 Dec 13 1995 /var#host=balsa# +drwxr-xr-x 23 root root 1024 Jan 12 14:22 /var#host=eiche# +drwxr-xr-x 22 root root 1024 Dec 13 1995 /var#host=fichte# +drwxr-xr-x 22 root root 1024 Dec 13 1995 /var#host=laerche# +drwxr-xr-x 22 root root 1024 Dec 13 1995 /var#host=mahagoni# +drwxr-xr-x 22 root root 1024 Dec 13 1995 /var#host=palisander# +drwxr-xr-x 22 root root 1024 Dec 13 1995 /var#host=pcbs10# +drwxr-xr-x 22 root root 1024 Dec 13 1995 /var#host=pcbs11# + +Notes: The /tmp directory has an own filesystem on server "eiche", +in order to prevent users from filling the whole filestore (we dont use +quotas). Each client needs a different /tmp because of possible name clashes. +Also, the whole /var hierarchy is kept differently to prevent any risk, but +that could be optimized perhaps. Note that nfsd and mountd have been +replaced by a script which switches off translations, in the style + +-rwxr-xr-x 2 root root 70 Mar 22 12:54 /usr/sbin/rpc.mountd +-rwxr-xr-x 1 root root 32772 Jun 11 1995 /usr/sbin/rpc.mountd.notrans +-rwxr-xr-x 2 root root 70 Mar 22 12:54 /usr/sbin/rpc.nfsd +-rwxr-xr-x 1 root root 45060 Jun 11 1995 /usr/sbin/rpc.nfsd.notrans + +where /usr/sbin/rpc.mountd has the contents + +#!/bin/sh +exec /usr/bin/env - NAMETRANS= "`/usr/bin/env`" $0.notrans $* + +Of course, that could be improved, but is a quick hack to get things work. + +Enjoy, + +-- Thomas + + +The author can be contacted under + schoebel@informatik.uni-stuttgart.de +or snailmail + Thomas Schoebel-Theuer + Institut fuer Informatik + Breitwiesenstr. 20-22 + D-70565 Stuttgart + diff --git a/MAINTAINERS b/MAINTAINERS index f796f89e871c..0c1384bc5195 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -125,6 +125,12 @@ M: net-patches@lxorguk.ukuu.org.uk L: netatalk@umich.edu S: Maintained +DECnet NETWORK LAYER +P: Steven Whitehouse +M: SteveW@ACM.org +L: netdev@roxanne.nuclecu.unam.mx +S: Maintained + AX.25 NETWORK LAYER P: Jon Naylor M: jsn@cs.nott.ac.uk diff --git a/Makefile b/Makefile index 02a941f72fc6..82175e671004 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 42 +SUBLEVEL = 43 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) diff --git a/arch/alpha/config.in b/arch/alpha/config.in index e62ec31d030a..774686ebdac7 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -94,10 +94,11 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF -tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA fi +tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86 tristate 'Parallel port support' CONFIG_PNP_PARPORT endmenu @@ -172,4 +173,5 @@ bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index ef95764926cb..3b3d8574b944 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -295,7 +295,7 @@ asmlinkage int osf_statfs(char *path, struct osf_statfs *buffer, unsigned long b retval = verify_area(VERIFY_WRITE, buffer, bufsiz); if (retval) goto out; - retval = namei(path, &inode); + retval = namei(NAM_FOLLOW_LINK, path, &inode); if (retval) goto out; retval = -ENOSYS; @@ -376,7 +376,7 @@ static int getdev(const char *name, int rdonly, struct inode **ino) struct file_operations *fops; int retval; - retval = namei(name, &inode); + retval = namei(NAM_FOLLOW_LINK, name, &inode); if (retval) return retval; if (!S_ISBLK(inode->i_mode)) { @@ -845,14 +845,12 @@ asmlinkage unsigned long osf_getsysinfo(unsigned long op, void *buffer, unsigned long nbytes, int *start, void *arg) { - extern unsigned long rdfpcr(void); unsigned long w; switch (op) { case GSI_IEEE_FP_CONTROL: - /* build and return current fp control word: */ - w = current->tss.flags & IEEE_TRAP_ENABLE_MASK; - w |= ((rdfpcr() >> 52) << 17) & IEEE_STATUS_MASK; + /* Return current software fp control & status bits. */ + w = current->tss.flags & IEEE_SW_MASK; if (put_user(w, (unsigned long *) buffer)) return -EFAULT; return 0; @@ -883,16 +881,32 @@ asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes, int *start, void *arg) { - unsigned long v, w, i; - switch (op) { - case SSI_IEEE_FP_CONTROL: - /* update trap enable bits: */ - if (get_user(w, (unsigned long *) buffer)) + case SSI_IEEE_FP_CONTROL: { + unsigned long swcr, fpcr; + + /* + * Alpha Architecture Handbook 4.7.7.3: + * To be fully IEEE compiant, we must track the current IEEE + * exception state in software, because spurrious bits can be + * set in the trap shadow of a software-complete insn. + */ + + /* Update softare trap enable bits. */ + if (get_user(swcr, (unsigned long *)buffer)) return -EFAULT; - current->tss.flags &= ~IEEE_TRAP_ENABLE_MASK; - current->tss.flags |= (w & IEEE_TRAP_ENABLE_MASK); + current->tss.flags &= ~IEEE_SW_MASK; + current->tss.flags |= swcr & IEEE_SW_MASK; + + /* Update the real fpcr. For exceptions that are disabled in + software but have not been seen, enable the exception in + hardware so that we can update our software status mask. */ + fpcr = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK); + fpcr = ieee_swcr_to_fpcr(swcr | (~swcr & IEEE_STATUS_MASK)>>16); + wrfpcr(fpcr); + return 0; + } case SSI_IEEE_STATE_AT_SIGNAL: case SSI_IEEE_IGNORE_STATE_AT_SIGNAL: @@ -903,7 +917,9 @@ asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, */ break; - case SSI_NVPAIRS: + case SSI_NVPAIRS: { + unsigned long v, w, i; + for (i = 0; i < nbytes; ++i) { if (get_user(v, 2*i + (unsigned int *)buffer)) return -EFAULT; @@ -922,6 +938,7 @@ asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, } } return 0; + } default: break; diff --git a/arch/alpha/math-emu/fp-emul.c b/arch/alpha/math-emu/fp-emul.c index 38df501fdb36..f1ff5a2beb98 100644 --- a/arch/alpha/math-emu/fp-emul.c +++ b/arch/alpha/math-emu/fp-emul.c @@ -13,9 +13,9 @@ #define OPC_INTL 0x11 #define OPC_INTS 0x12 #define OPC_INTM 0x13 -#define OPC_FLTV 0x14 -#define OPC_FLTI 0x15 -#define OPC_FLTL 0x16 +#define OPC_FLTV 0x15 +#define OPC_FLTI 0x16 +#define OPC_FLTL 0x17 #define OPC_MISC 0x18 @@ -298,19 +298,26 @@ alpha_fp_emul (unsigned long pc) * * - Set the appropriate bits in the FPCR * - If the specified exception is enabled in the FPCR, - * return. The caller (mxr_signal_handler) will dispatch + * return. The caller (entArith) will dispatch * the appropriate signal to the translated program. + * + * In addition, properly track the exception state in software + * as described in the Alpha Architectre Handbook section 4.7.7.3. */ if (res) { - fpcr |= FPCR_SUM | res; + /* Record exceptions in software control word. */ + current->tss.flags = fpcw |= res >> 35; + + /* Update hardware control register */ + fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); + fpcr |= ieee_swcr_to_fpcr(fpcw | (~fpcw&IEEE_STATUS_MASK)>>16); wrfpcr(fpcr); - if (((res & FPCR_INV) && (fpcw & IEEE_TRAP_ENABLE_INV)) || - ((res & FPCR_DZE) && (fpcw & IEEE_TRAP_ENABLE_DZE)) || - ((res & FPCR_OVF) && (fpcw & IEEE_TRAP_ENABLE_OVF)) || - ((res & FPCR_UNF) && (fpcw & IEEE_TRAP_ENABLE_UNF)) || - ((res & FPCR_INE) && (fpcw & IEEE_TRAP_ENABLE_INE))) + + /* Do we generate a signal? */ + if (res >> 51 & fpcw & IEEE_TRAP_ENABLE_MASK) return 0; } + /* * Whoo-kay... we got this far, and we're not generating a signal * to the translated program. All that remains is to write the @@ -326,6 +333,7 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) { unsigned long trigger_pc = regs->pc - 4; unsigned long insn, opcode, rc; + /* * Turn off the bits corresponding to registers that are the * target of instructions that set bits in the exception diff --git a/arch/i386/config.in b/arch/i386/config.in index f95590407445..1612b614f193 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -34,6 +34,7 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA fi @@ -117,4 +118,5 @@ bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu diff --git a/arch/i386/defconfig b/arch/i386/defconfig index a27b6ebce4cf..ab30a25c81f3 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -25,6 +25,7 @@ CONFIG_SYSVIPC=y CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y # CONFIG_M386 is not set # CONFIG_M486 is not set # CONFIG_M586 is not set @@ -190,6 +191,9 @@ CONFIG_EEXPRESS_PRO100=y # Filesystems # # CONFIG_QUOTA is not set +# CONFIG_DCACHE_PRELOAD is not set +# CONFIG_OMIRR is not set +# CONFIG_TRANS_NAMES is not set CONFIG_MINIX_FS=y CONFIG_EXT2_FS=y CONFIG_FAT_FS=y @@ -241,3 +245,4 @@ CONFIG_82C710_MOUSE=y # Kernel hacking # # CONFIG_PROFILE is not set +# CONFIG_MAGIC_SYSRQ is not set diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index a42b87b1bb95..bd4bf56cfa38 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -532,8 +532,8 @@ ENTRY(gdt) .quad 0x0000000000000000 /* not used */ .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ - .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ - .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ + .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ + .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ .quad 0x0000000000000000 /* not used */ .quad 0x0000000000000000 /* not used */ .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index e5fb5acb1dc5..eedb1d0fe3c6 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -142,21 +142,6 @@ void enable_irq(unsigned int irq_nr) * the operations that are needed to keep the AT interrupt-controller * happy. They are also written to be fast - and to disable interrupts * as little as humanly possible. - * - * NOTE! These macros expand to three different handlers for each line: one - * complete handler that does all the fancy stuff (including signal handling), - * and one fast handler that is meant for simple IRQ's that want to be - * atomic. The specific handler is chosen depending on the SA_INTERRUPT - * flag when installing a handler. Finally, one "bad interrupt" handler, that - * is used when no handler is present. - * - * The timer interrupt is handled specially to insure that the jiffies - * variable is updated at all times. Specifically, the timer interrupt is - * just like the complete handlers except that it is invoked with interrupts - * disabled and should never re-enable them. If other interrupts were - * allowed to be processed while the timer interrupt is active, then the - * other interrupts would have to avoid using the jiffies variable for delay - * and interval timing operations to avoid hanging the system. */ #if NR_IRQS != 16 @@ -539,6 +524,9 @@ asmlinkage void do_IRQ(struct pt_regs regs) status = 0; action = *(irq + irq_action); if (action) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + do { status |= action->flags; action->handler(irq, action->dev_id, ®s); @@ -546,7 +534,6 @@ asmlinkage void do_IRQ(struct pt_regs regs) } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); - __cli(); spin_lock(&irq_controller_lock); unmask_irq(irq); diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 1f9e893999f6..7d70264baa16 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -9,24 +9,10 @@ #ifdef __SMP__ -#undef INIT_STUCK -#define INIT_STUCK 200000000 - -#undef STUCK -#define STUCK \ -if (!--stuck) {printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n",irq,cpu,global_irq_holder); stuck = INIT_STUCK;} - static inline void irq_enter(int cpu, int irq) { - int stuck = INIT_STUCK; - hardirq_enter(cpu); while (test_bit(0,&global_irq_lock)) { - if ((unsigned char) cpu == global_irq_holder) { - printk("BAD! Local interrupts enabled, global disabled\n"); - break; - } - STUCK; /* nothing */; } } diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 1dc615501b51..5a020b723551 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -870,6 +870,8 @@ __initfunc(static void do_boot_cpu(int i)) *((volatile unsigned long *)phys_to_virt(8192)) = 0; } +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; /* * Cycle through the processors sending APIC IPI's to boot each. @@ -912,8 +914,15 @@ __initfunc(void smp_boot_cpus(void)) * of here now! */ - if (!smp_found_config) + if (!smp_found_config) { + /* + * For SMP-simulation on one CPU to work, we must initialize these + * values for the single CPU here: + */ + prof_counter[0] = prof_multiplier[0] = 1; + return; + } /* * Map the local APIC into kernel space @@ -1302,9 +1311,6 @@ void smp_flush_tlb(void) * value into /proc/profile. */ -unsigned int prof_multiplier[NR_CPUS]; -unsigned int prof_counter[NR_CPUS]; - void smp_local_timer_interrupt(struct pt_regs * regs) { int cpu = smp_processor_id(); diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index e45cc727916e..a08c9c49cdcc 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -515,7 +515,7 @@ unsigned long get_cmos_time(void) return mktime(year, mon, day, hour, min, sec); } -static struct irqaction irq0 = { timer_interrupt, 0, 0, "timer", NULL, NULL}; +static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; __initfunc(void time_init(void)) diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 696e370046a0..b397fc76d047 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -191,8 +191,6 @@ spinlock_t die_lock; spin_lock_irq(&die_lock); printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); -do { int i=2000000000; while (i) i--; } while (0); -do { int i=2000000000; while (i) i--; } while (0); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } diff --git a/arch/m68k/amiga/amifb.c b/arch/m68k/amiga/amifb.c index eb72970d7e5b..15e424ea71f5 100644 --- a/arch/m68k/amiga/amifb.c +++ b/arch/m68k/amiga/amifb.c @@ -1307,6 +1307,7 @@ static void ami_rebuild_copper(void); */ extern unsigned short ami_intena_vals[]; +extern void amiga_init_sound(void); /* * Support for Graphics Boards @@ -1809,6 +1810,11 @@ __initfunc(struct fb_info *amiga_fb_init(long *mem_start)) int err, tag, i; u_long chipptr; + /* + * Our beloved beeper + */ + amiga_init_sound(); + /* * Check for a Graphics Board */ diff --git a/arch/m68k/amiga/amikeyb.c b/arch/m68k/amiga/amikeyb.c index 1058270dd019..06fe55d29202 100644 --- a/arch/m68k/amiga/amikeyb.c +++ b/arch/m68k/amiga/amikeyb.c @@ -23,18 +23,17 @@ #include #include #include +#include #include #include #include -extern void handle_scancode(unsigned char); - #define AMIKEY_CAPS (0x62) #define BREAK_MASK (0x80) #define RESET_WARNING (0xf0) /* before rotation */ -static u_short amiplain_map[NR_KEYS] = { +static u_short amiplain_map[NR_KEYS] __initdata = { 0xf060, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf05c, 0xf200, 0xf300, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, @@ -178,15 +177,13 @@ static unsigned char rep_scancode; static void amikeyb_rep(unsigned long ignore); static struct timer_list amikeyb_rep_timer = {NULL, NULL, 0, 0, amikeyb_rep}; -extern struct pt_regs *pt_regs; - static void amikeyb_rep(unsigned long ignore) { unsigned long flags; save_flags(flags); cli(); - pt_regs = NULL; + kbd_pt_regs = NULL; amikeyb_rep_timer.expires = jiffies + key_repeat_rate; amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; @@ -202,7 +199,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) static int reset_warning = 0; /* save frame for register dump */ - pt_regs = (struct pt_regs *)fp; + kbd_pt_regs = fp; /* get and invert scancode (keyboard is active low) */ scancode = ~ciaa.sdr; @@ -302,14 +299,13 @@ __initfunc(int amiga_keyb_init(void)) return -EIO; /* setup key map */ - key_maps[0] = amiplain_map; + memcpy(plain_map, amiplain_map, sizeof(plain_map)); key_maps[1] = amishift_map; key_maps[2] = amialtgr_map; key_maps[4] = amictrl_map; key_maps[5] = amishift_ctrl_map; key_maps[8] = amialt_map; key_maps[12] = amictrl_alt_map; - memcpy(plain_map, amiplain_map, sizeof(plain_map)); /* * Initialize serial data direction. diff --git a/arch/m68k/amiga/amisound.c b/arch/m68k/amiga/amisound.c index d5656d17051d..72d2091f58ff 100644 --- a/arch/m68k/amiga/amisound.c +++ b/arch/m68k/amiga/amisound.c @@ -40,7 +40,7 @@ u_short amiga_audio_period = MAX_PERIOD; static u_long clock_constant; -__initfunc(static void init_sound(void)) +__initfunc(void amiga_init_sound(void)) { snd_data = amiga_chip_alloc(sizeof(sine_data)); if (!snd_data) { @@ -58,14 +58,8 @@ static struct timer_list sound_timer = { NULL, NULL, 0, 0, nosound }; void amiga_mksound( unsigned int hz, unsigned int ticks ) { - static int inited = 0; unsigned long flags; - if (!inited) { - init_sound(); - inited = 1; - } - if (!snd_data) return; diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index 7b6bd208f074..e36016306238 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -78,6 +78,7 @@ static void amiga_wait_key(void); extern struct consw fb_con; extern struct fb_info *amiga_fb_init(long *); extern void zorro_init(void); +extern void amiga_init_sound(void); static void amiga_savekmsg_init(void); static void amiga_mem_console_write(const char *b, unsigned int count); static void amiga_serial_console_write(const char *s, unsigned int count); diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c index b2887f7da437..189095cfcde1 100644 --- a/arch/m68k/atari/ataints.c +++ b/arch/m68k/atari/ataints.c @@ -48,6 +48,7 @@ #include #include #include +#include /* @@ -161,22 +162,6 @@ static int free_vme_vec_bitmap = 0; #define IRQ_NAME(nr) atari_slow_irq_##nr##_handler(void) -#define MFP_MK_BASE "0xfa13" - -/* This must agree with entry.S. */ -#define ORIG_DO "0x24" -#define FORMATVEC "0x32" -#define SR "0x2C" -#define SAVE_ALL \ - "clrl %%sp@-;" /* stk_adj */ \ - "pea -1:w;" /* orig d0 = -1 */ \ - "movel %%d0,%%sp@-;" /* d0 */ \ - "moveml %%d1-%%d5/%%a0-%%a2,%%sp@-" -#define GET_CURRENT(tmp) \ - "movel %%sp,"#tmp";" \ - "andw #-8192,"#tmp";" \ - "movel "#tmp",%%a2" - #define BUILD_SLOW_IRQ(n) \ asmlinkage void IRQ_NAME(n); \ /* Dummy function to allow asm with operands. */ \ @@ -184,29 +169,31 @@ void atari_slow_irq_##n##_dummy (void) { \ __asm__ (ALIGN_STR "\n" \ SYMBOL_NAME_STR(atari_slow_irq_) #n "_handler:\t" \ " addql #1,"SYMBOL_NAME_STR(local_irq_count)"\n" \ - SAVE_ALL "\n" \ + SAVE_ALL_INT "\n" \ GET_CURRENT(%%d0) "\n" \ -" andb #~(1<<(" #n "&7))," /* mask this interrupt */ \ - "("MFP_MK_BASE"+(((" #n "&8)^8)>>2)+((" #n "&16)<<3)):w\n" \ -" bfextu %%sp@("SR"){#5,#3},%%d0\n" /* get old IPL from stack frame */ \ +" andb #~(1<<(%c3&7)),%a4:w\n" /* mask this interrupt */ \ + /* get old IPL from stack frame */ \ +" bfextu %%sp@(%c2){#5,#3},%%d0\n" \ " movew %%sr,%%d1\n" \ " bfins %%d0,%%d1{#21,#3}\n" \ " movew %%d1,%%sr\n" /* set IPL = previous value */ \ " addql #1,%a0\n" \ -" lea "SYMBOL_NAME_STR(irq_handler)"+("#n"+8)*8,%%a0\n" \ +" lea %a1,%%a0\n" \ " pea %%sp@\n" /* push addr of frame */ \ " movel %%a0@(4),%%sp@-\n" /* push handler data */ \ -" pea (" #n "+8)\n" /* push int number */ \ +" pea (%c3+8)\n" /* push int number */ \ " movel %%a0@,%%a0\n" \ " jbsr %%a0@\n" /* call the handler */ \ " addql #8,%%sp\n" \ " addql #4,%%sp\n" \ " orw #0x0600,%%sr\n" \ " andw #0xfeff,%%sr\n" /* set IPL = 6 again */ \ -" orb #(1<<(" #n "&7))," /* now unmask the int again */ \ - "("MFP_MK_BASE"+(((" #n "&8)^8)>>2)+((" #n "&16)<<3)):w\n" \ +" orb #(1<<(%c3&7)),%a4:w\n" /* now unmask the int again */ \ " jbra "SYMBOL_NAME_STR(ret_from_interrupt)"\n" \ - : : "i" (&kstat.interrupts[n+8]) \ + : : "i" (&kstat.interrupts[n+8]), "i" (&irq_handler[n+8]), \ + "n" (PT_OFF_SR), "n" (n), \ + "i" (n & 8 ? (n & 16 ? &tt_mfp.int_mk_a : &mfp.int_mk_a) \ + : (n & 16 ? &tt_mfp.int_mk_b : &mfp.int_mk_b)) \ ); \ } @@ -288,10 +275,10 @@ SYMBOL_NAME_STR(atari_fast_irq_handler) ": orw #0x700,%%sr /* disable all interrupts */ "SYMBOL_NAME_STR(atari_prio_irq_handler) ":\t addql #1,"SYMBOL_NAME_STR(local_irq_count)"\n" - SAVE_ALL "\n" + SAVE_ALL_INT "\n" GET_CURRENT(%%d0) " /* get vector number from stack frame and convert to source */ - bfextu %%sp@(" FORMATVEC "){#4,#10},%%d0 + bfextu %%sp@(%c1){#4,#10},%%d0 subw #(0x40-8),%%d0 jpl 1f addw #(0x40-8-0x18),%%d0 @@ -307,7 +294,7 @@ SYMBOL_NAME_STR(atari_fast_irq_handler) ": addql #8,%%sp addql #4,%%sp jbra "SYMBOL_NAME_STR(ret_from_interrupt) - : : "i" (&kstat.interrupts) + : : "i" (&kstat.interrupts), "n" (PT_OFF_FORMATVEC) ); } diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index beccf9a84b01..d5d45be01e8f 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -31,7 +32,6 @@ #include #include -extern void handle_scancode(unsigned char); extern int ovsc_switchmode; extern unsigned char mach_keyboard_type; static void atakeyb_rep( unsigned long ignore ); @@ -99,7 +99,7 @@ static unsigned long broken_keys[128/(sizeof(unsigned long)*8)] = { 0, }; * - Keypad Left/Right Parenthesis mapped to new K_PPAREN[LR] */ -static u_short ataplain_map[NR_KEYS] = { +static u_short ataplain_map[NR_KEYS] __initdata = { 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf008, 0xf009, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, @@ -139,7 +139,7 @@ static u_short atashift_map[NR_KEYS] = { static u_short atactrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, - 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf07f, 0xf200, 0xf008, 0xf200, + 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, @@ -158,18 +158,18 @@ static u_short atactrl_map[NR_KEYS] = { static u_short atashift_ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf008, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf201, 0xf702, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf700, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700, 0xf200, - 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf117, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf07f, 0xf700, 0xf200, + 0xf703, 0xf200, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf117, 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200, - 0xf600, 0xf200, 0xf115, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307, + 0xf200, 0xf1ff, 0xf11b, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307, 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303, 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 @@ -207,7 +207,7 @@ static u_short atashift_alt_map[NR_KEYS] = { 0xf118, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200, 0xf119, 0xf200, 0xf115, 0xf87f, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307, + 0xf200, 0xf1ff, 0xf11b, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307, 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303, 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 @@ -215,7 +215,7 @@ static u_short atashift_alt_map[NR_KEYS] = { static u_short atactrl_alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf800, 0xf81b, 0xf81c, 0xf81d, 0xf81e, - 0xf81f, 0xf87f, 0xf200, 0xf200, 0xf87f, 0xf200, 0xf808, 0xf200, + 0xf81f, 0xf87f, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf201, 0xf702, 0xf801, 0xf813, 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, @@ -224,7 +224,7 @@ static u_short atactrl_alt_map[NR_KEYS] = { 0xf703, 0xf800, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114, 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200, - 0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf600, 0xf200, 0xf115, 0xf87f, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf1ff, 0xf202, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307, 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303, @@ -234,18 +234,18 @@ static u_short atactrl_alt_map[NR_KEYS] = { static u_short atashift_ctrl_alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf808, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf201, 0xf702, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf700, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700, 0xf200, - 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf117, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf200, + 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, + 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813, + 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, + 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf87f, 0xf700, 0xf200, + 0xf703, 0xf200, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf117, 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200, - 0xf600, 0xf200, 0xf115, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf600, 0xf200, 0xf115, 0xf87f, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307, + 0xf200, 0xf1ff, 0xf11b, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307, 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303, 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 @@ -277,12 +277,10 @@ static unsigned int key_repeat_rate = DEFAULT_KEYB_REP_RATE; static unsigned char rep_scancode; static struct timer_list atakeyb_rep_timer = { NULL, NULL, 0, 0, atakeyb_rep }; -extern struct pt_regs *pt_regs; - static void atakeyb_rep( unsigned long ignore ) { - pt_regs = NULL; + kbd_pt_regs = NULL; /* Disable keyboard for the time we call handle_scancode(), else a race * in the keyboard tty queue may happen */ @@ -327,7 +325,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) int break_flag; /* save frame for register dump */ - pt_regs = (struct pt_regs *)fp; + kbd_pt_regs = fp; repeat: if (acia.mid_ctrl & ACIA_IRQ) @@ -420,14 +418,14 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) * make codes instead. Therefore, simply ignore * break_flag... * */ - int keyval = ataplain_map[scancode], keytyp; - + int keyval = plain_map[scancode], keytyp; + set_bit( scancode, broken_keys ); self_test_last_rcv = jiffies; - keyval = ataplain_map[scancode]; + keyval = plain_map[scancode]; keytyp = KTYP(keyval) - 0xf0; keyval = KVAL(keyval); - + printk( KERN_WARNING "Key with scancode %d ", scancode ); if (keytyp == KT_LATIN || keytyp == KT_LETTER) { if (keyval < ' ') @@ -440,7 +438,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) } else if (test_bit( scancode, broken_keys )) break; - + if (break_flag) { del_timer( &atakeyb_rep_timer ); rep_scancode = 0; @@ -808,7 +806,7 @@ void atari_kbd_leds (unsigned int leds) __initfunc(int atari_keyb_init(void)) { /* setup key map */ - key_maps[0] = ataplain_map; + memcpy (plain_map, ataplain_map, sizeof(plain_map)); key_maps[1] = atashift_map; key_maps[2] = 0; /* ataaltgr_map */ key_maps[4] = atactrl_map; @@ -817,7 +815,6 @@ __initfunc(int atari_keyb_init(void)) key_maps[9] = atashift_alt_map; key_maps[12] = atactrl_alt_map; key_maps[13] = atashift_ctrl_alt_map; - memcpy (plain_map, ataplain_map, sizeof(plain_map)); keymap_count = 8; /* say that we don't have an AltGr key */ diff --git a/arch/m68k/console/fbcon.c b/arch/m68k/console/fbcon.c index 376249c90d14..62457c0cbaf2 100644 --- a/arch/m68k/console/fbcon.c +++ b/arch/m68k/console/fbcon.c @@ -69,8 +69,8 @@ #include #include -#include "../../../drivers/char/vt_kern.h" /* vt_cons and vc_resize_con() */ -#include "../../../drivers/char/console_struct.h" +#include +#include /* Import console_blanked from console.c */ diff --git a/arch/m68k/fpsp040/skeleton.S b/arch/m68k/fpsp040/skeleton.S index 466cf3fbc4f6..719bcc9ce927 100644 --- a/arch/m68k/fpsp040/skeleton.S +++ b/arch/m68k/fpsp040/skeleton.S @@ -39,6 +39,7 @@ | #include +#include |SKELETON idnt 2,1 | Motorola 040 Floating Point Software Package @@ -51,24 +52,6 @@ .include "fpsp.h" -/* - * This has to match entry.S - */ -LOFF_ORIG_D0 = 0x24 - -#define curptr a2 - -#define SAVE_ALL \ - clrl %sp@-; /* stk_adj */ \ - movel %d0,%sp@-; /* orig d0 */ \ - movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; - -#define GET_CURRENT(tmp) \ - movel %sp,tmp; \ - andw &-8192,tmp; \ - movel tmp,%curptr; - |xref b1238_fix | @@ -86,11 +69,7 @@ real_dz: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -181,11 +160,7 @@ inex_done: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -211,11 +186,7 @@ ovfl_done: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -241,11 +212,7 @@ unfl_done: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -267,11 +234,7 @@ real_snan: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -293,11 +256,7 @@ real_operr: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -325,11 +284,7 @@ real_bsun: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -350,11 +305,7 @@ fline: jmp fpsp_fline real_fline: - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -376,11 +327,7 @@ real_unsupp: frestore (%sp)+ unlk %a6 - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -435,9 +382,7 @@ Lnotkern: bne Lmustsched rte Lmustsched: - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | indicate stack frame not for syscall + SAVE_ALL_INT GET_CURRENT(%d0) bral SYMBOL_NAME(ret_from_exception) | deliver signals, reschedule etc.. diff --git a/arch/m68k/kernel/console.c b/arch/m68k/kernel/console.c index 468ae0b5958b..31608e904f60 100644 --- a/arch/m68k/kernel/console.c +++ b/arch/m68k/kernel/console.c @@ -109,7 +109,6 @@ #include #include #include -#include #include #include #include @@ -119,17 +118,18 @@ #include #include #include +#include #include #include #include #include -#include "../../../drivers/char/kbd_kern.h" -#include "../../../drivers/char/vt_kern.h" -#include "../../../drivers/char/consolemap.h" -#include "../../../drivers/char/selection.h" -#include "../../../drivers/char/console_struct.h" +#include +#include +#include +#include +#include #ifndef MIN @@ -159,6 +159,8 @@ static void set_vesa_blanking(unsigned long arg); extern void vesa_blank(void); extern void vesa_unblank(void); extern void compute_shiftstate(void); +extern void reset_palette(int currcons); +extern void set_palette(void); void poke_blanked_console(void); void do_blank_screen(int); @@ -257,6 +259,7 @@ struct consw *conswitchp; #define ulcolor (vc_cons[currcons].d->vc_ulcolor) #define halfcolor (vc_cons[currcons].d->vc_halfcolor) #define tab_stop (vc_cons[currcons].d->vc_tab_stop) +#define palette (vc_cons[currcons].d->vc_palette) #define bell_pitch (vc_cons[currcons].d->vc_bell_pitch) #define bell_duration (vc_cons[currcons].d->vc_bell_duration) #define sw (vc_cons[currcons].d->vc_sw) @@ -539,6 +542,14 @@ void vc_disallocate(unsigned int currcons) static unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15 }; +/* the default colour table, for VGA+ colour systems */ +int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa, + 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff}; +int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa, + 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff}; +int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa, + 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff}; + /* * gotoxy() must verify all boundaries, because the arguments * might also be negative. If the given position is out of @@ -1655,7 +1666,7 @@ static int do_con_write(struct tty_struct * tty, int from_user, if (nextx == cols) { sw->con_putc(vc_cons[currcons].d, *putcs_buf, y, x); - ((unsigned short *)pos)--; + pos--; need_wrap = decawm; continue; } @@ -1837,9 +1848,7 @@ static int do_con_write(struct tty_struct * tty, int from_user, vc_state = ESpalette; continue; } else if (c=='R') { /* reset palette */ -#if 0 reset_palette (currcons); -#endif vc_state = ESnormal; } else vc_state = ESnormal; @@ -1848,7 +1857,6 @@ static int do_con_write(struct tty_struct * tty, int from_user, if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; if (npar==7) { -#if 0 int i = par[0]*3, j = 1; palette[i] = 16*par[j++]; palette[i++] += par[j++]; @@ -1857,7 +1865,6 @@ static int do_con_write(struct tty_struct * tty, int from_user, palette[i] = 16*par[j++]; palette[i] += par[j]; set_palette() ; -#endif vc_state = ESnormal; } } else @@ -2283,7 +2290,7 @@ static void console_bh(void) * Reads the information preserved by setup.s to determine the current display * type and sets everything accordingly. */ -unsigned long con_init(unsigned long kmem_start) +__initfunc(unsigned long con_init(unsigned long kmem_start)) { const char *display_desc = "????"; unsigned int currcons = 0; @@ -2617,22 +2624,61 @@ static int set_get_font(char * arg, int set, int ch512) * map, 3 bytes per colour, 16 colours, range from 0 to 255. */ +static int set_get_cmap(unsigned char *arg, int set) +{ + int i, j, k; + + for (i = 0; i < 16; i++) + if (set) { + get_user(default_red[i], arg++); + get_user(default_grn[i], arg++); + get_user(default_blu[i], arg++); + } else { + put_user(default_red[i], arg++); + put_user(default_grn[i], arg++); + put_user(default_blu[i], arg++); + } + if (set) { + for (i = 0; i < MAX_NR_CONSOLES; i++) + if (vc_cons_allocated(i)) + for (j = k = 0; j < 16; j++) { + vc_cons[i].d->vc_palette[k++] = + default_red[j]; + vc_cons[i].d->vc_palette[k++] = + default_grn[j]; + vc_cons[i].d->vc_palette[k++] = + default_blu[j]; + } + set_palette(); + } + return 0; +} + int con_set_cmap (unsigned char *arg) { - return -EINVAL; + return set_get_cmap (arg, 1); } int con_get_cmap (unsigned char *arg) { - return -EINVAL; + return set_get_cmap (arg, 0); } void reset_palette(int currcons) { + int j, k; + for (j = k = 0; j < 16; j++) { + palette[k++] = default_red[j]; + palette[k++] = default_grn[j]; + palette[k++] = default_blu[j]; + } + set_palette() ; } void set_palette(void) { + if (vt_cons[fg_console]->vc_mode != KD_GRAPHICS) + conswitchp->con_set_palette(vc_cons[fg_console].d, color_table); } /* diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index ef5ef46d6e36..33542ca96264 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -22,24 +22,6 @@ * NOTE: This code handles signal-recognition, which happens every time * after a timer-interrupt and after each system call. * - * Stack layout in 'ret_from_exception': - * - * This allows access to the syscall arguments in registers d1-d5 - * - * 0(sp) - d1 - * 4(sp) - d2 - * 8(sp) - d3 - * C(sp) - d4 - * 10(sp) - d5 - * 14(sp) - a0 - * 18(sp) - a1 - * 1C(sp) - a2 - * 20(sp) - d0 - * 24(sp) - orig_d0 - * 28(sp) - stack adjustment - * 2C(sp) - sr - * 2E(sp) - pc - * 32(sp) - format & vector */ /* @@ -48,94 +30,12 @@ * number 0 in the 'current_set' list. */ -/* - * 97/05/14 Andreas: Register %a2 is now set to the current task throughout - * the whole kernel. - */ - #include #include #include +#include #include #include -#ifdef CONFIG_KGDB -#include -.globl SYMBOL_NAME(kgdb_registers) -#endif - -#define curptr a2 - -LENOSYS = 38 - -/* - * these are offsets into the task-struct - */ -LTASK_STATE = 0 -LTASK_COUNTER = 4 -LTASK_PRIORITY = 8 -LTASK_SIGNAL = 12 -LTASK_BLOCKED = 16 -LTASK_FLAGS = 20 - -/* the following macro is used when enabling interrupts */ -#if defined(MACH_ATARI_ONLY) - /* block out HSYNC on the atari */ -#define ALLOWINT 0xfbff -#define MAX_NOINT_IPL 3 -#else - /* portable version */ -#define ALLOWINT 0xf8ff -#define MAX_NOINT_IPL 0 -#endif /* machine compilation types */ - -LD0 = 0x20 -LORIG_D0 = 0x24 -LSR = 0x2C -LFORMATVEC = 0x32 - -/* - * This defines the normal kernel pt-regs layout. - * - * regs a3-a6 and d6-d7 are preserved by C code - * the kernel doesn't mess with usp unless it needs to - */ -#ifndef CONFIG_KGDB -#define SAVE_ALL \ - clrl %sp@-; /* stk_adj */ \ - movel %d0,%sp@-; /* orig d0 */ \ - movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; -#else -/* Need to save the "missing" registers for kgdb... - */ -#define SAVE_ALL \ - clrl %sp@-; /* stk_adj */ \ - movel %d0,%sp@-; /* orig d0 */ \ - movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; \ - moveml %d6-%d7,SYMBOL_NAME(kgdb_registers)+GDBOFFA_D6; \ - moveml %a3-%a6,SYMBOL_NAME(kgdb_registers)+GDBOFFA_A3; -#endif - -#define RESTORE_ALL \ - moveml %sp@+,%a0-%a1/%curptr/%d1-%d5; \ - movel %sp@+,%d0; \ - addql #4,%sp; /* orig d0 */ \ - addl %sp@+,%sp; /* stk adj */ \ - rte - -#define SWITCH_STACK_SIZE (6*4+4) /* includes return address */ - -#define SAVE_SWITCH_STACK \ - moveml %a3-%a6/%d6-%d7,%sp@- - -#define RESTORE_SWITCH_STACK \ - moveml %sp@+,%a3-%a6/%d6-%d7 - -#define GET_CURRENT(tmp) \ - movel %sp,tmp; \ - andw &-8192,tmp; \ - movel tmp,%curptr; .globl SYMBOL_NAME(system_call), SYMBOL_NAME(buserr), SYMBOL_NAME(trap) .globl SYMBOL_NAME(resume), SYMBOL_NAME(ret_from_exception) @@ -146,12 +46,7 @@ LFORMATVEC = 0x32 .text ENTRY(buserr) - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall - + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(buserr_c) @@ -159,11 +54,7 @@ ENTRY(buserr) jra SYMBOL_NAME(ret_from_exception) ENTRY(trap) - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) @@ -180,18 +71,18 @@ ENTRY(reschedule) jmp SYMBOL_NAME(schedule) badsys: - movel #-LENOSYS,LD0(%sp) + movel #-LENOSYS,LPT_OFF_D0(%sp) jra SYMBOL_NAME(ret_from_exception) do_trace: - movel #-LENOSYS,LD0(%sp) | needed for strace + movel #-LENOSYS,LPT_OFF_D0(%sp) | needed for strace subql #4,%sp SAVE_SWITCH_STACK jbsr SYMBOL_NAME(syscall_trace) RESTORE_SWITCH_STACK addql #4,%sp jbsr @(SYMBOL_NAME(sys_call_table),%d2:l:4)@(0) - movel %d0,%sp@(LD0) | save the return value + movel %d0,%sp@(LPT_OFF_D0) | save the return value subql #4,%sp | dummy return address SAVE_SWITCH_STACK jbsr SYMBOL_NAME(syscall_trace) @@ -202,7 +93,7 @@ SYMBOL_NAME_LABEL(ret_from_signal) jra SYMBOL_NAME(ret_from_exception) ENTRY(system_call) - SAVE_ALL + SAVE_ALL_SYS movel %d0,%d2 GET_CURRENT(%d0) @@ -213,19 +104,20 @@ ENTRY(system_call) cmpl #NR_syscalls,%d2 jcc badsys - btst #5,%curptr@(LTASK_FLAGS+3) | PF_TRACESYS + btst #LPF_TRACESYS_BIT,%curptr@(LTASK_FLAGS+LPF_TRACESYS_OFF) jne do_trace jbsr @(SYMBOL_NAME(sys_call_table),%d2:l:4)@(0) - movel %d0,%sp@(LD0) | save the return value + movel %d0,%sp@(LPT_OFF_D0) | save the return value SYMBOL_NAME_LABEL(ret_from_exception) - btst #5,%sp@(LSR) | check if returning to kernel + btst #5,%sp@(LPT_OFF_SR) | check if returning to kernel bnes 2f | if so, skip resched, signals tstl SYMBOL_NAME(need_resched) jne SYMBOL_NAME(reschedule) cmpl #SYMBOL_NAME(task),%curptr | task[0] cannot have signals jeq 2f - bclr #5,%curptr@(LTASK_FLAGS+1) | check for delayed trace + | check for delayed trace + bclr #LPF_DTRACE_BIT,%curptr@(LTASK_FLAGS+LPF_DTRACE_OFF) jne do_delayed_trace 5: tstl %curptr@(LTASK_STATE) | state @@ -236,7 +128,7 @@ SYMBOL_NAME_LABEL(ret_from_exception) movel %curptr@(LTASK_BLOCKED),%d0 movel %d0,%d1 | save blocked in d1 for sig handling notl %d0 - btst #4,%curptr@(LTASK_FLAGS+3) | PF_PTRACED + btst #LPF_PTRACED_BIT,%curptr@(LTASK_FLAGS+LPF_PTRACED_OFF) jeq 1f moveq #-1,%d0 | let the debugger see all signals 1: andl %curptr@(LTASK_SIGNAL),%d0 @@ -255,10 +147,10 @@ Lsignal_return: RESTORE_ALL do_delayed_trace: - bclr #7,%sp@(LSR) | clear trace bit in SR + bclr #7,%sp@(LPT_OFF_SR) | clear trace bit in SR pea 1 | send SIGTRAP - movel %a0,%sp@- - pea 5 + movel %curptr,%sp@- + pea LSIGTRAP jbsr SYMBOL_NAME(send_sig) addql #8,%sp addql #4,%sp @@ -268,15 +160,11 @@ do_delayed_trace: ** This is the main interrupt handler, responsible for calling process_int() */ SYMBOL_NAME_LABEL(inthandler) - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field - | signifies that the stack frame - | is NOT for syscall + SAVE_ALL_INT GET_CURRENT(%d0) addql #1,SYMBOL_NAME(local_irq_count) | put exception # in d0 - bfextu %sp@(LFORMATVEC){#4,#10},%d0 + bfextu %sp@(LPT_OFF_FORMATVEC){#4,#10},%d0 movel %sp,%sp@- movel %d0,%sp@- | put vector # on stack @@ -290,7 +178,7 @@ SYMBOL_NAME_LABEL(ret_from_interrupt) RESTORE_ALL 1: #if 1 - bfextu %sp@(LSR){#5,#3},%d0 | Check for nested interrupt. + bfextu %sp@(LPT_OFF_SR){#5,#3},%d0 | Check for nested interrupt. #if MAX_NOINT_IPL > 0 cmpiw #MAX_NOINT_IPL,%d0 #endif @@ -347,14 +235,6 @@ ENTRY(sys_sigreturn) RESTORE_SWITCH_STACK rts -LFLUSH_I_AND_D = 0x00000808 -LTSS_KSP = 0 -LTSS_USP = 4 -LTSS_SR = 8 -LTSS_FS = 10 -LTSS_CRP = 12 -LTSS_FPCTXT = 24 - SYMBOL_NAME_LABEL(resume) /* * Beware - when entering resume, offset of tss is in d1, @@ -460,8 +340,10 @@ SYMBOL_NAME_LABEL(resume) #if defined (CONFIG_M68060) /* is it a '060 ? */ +#if !defined(CPU_M68060_ONLY) btst #3,SYMBOL_NAME(m68k_cputype)+3 beqs 2f +#endif /* clear user entries in the branch cache */ movec %cacr,%d0 orl #0x00200000,%d0 diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index def50a747dcb..c27f7f32020a 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -142,8 +142,8 @@ __initfunc(static void m68k_parse_bootinfo(const struct bi_record *record)) } } - __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, - unsigned long * memory_end_p)) +__initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, + unsigned long * memory_end_p)) { unsigned long memory_start, memory_end; extern int _etext, _edata, _end; diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 25be40007876..44e45b9a08a8 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -201,7 +201,7 @@ static inline void access_error060 (struct frame *fp) if ((!(fslw & MMU060_ERR_BITS)) && !(fslw & MMU060_SEE)) return; } - + if (fslw & (MMU060_DESC_ERR | MMU060_WP)) { unsigned long errorcode; unsigned long addr = fp->un.fmt4.effaddr; diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index b46037f80693..02dff0eeaeb2 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -296,7 +296,8 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; * The parameters are pointers to where to stick the starting and ending * addresses of available kernel virtual memory. */ -__initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)) +__initfunc(unsigned long paging_init(unsigned long start_mem, + unsigned long end_mem)) { int chunk; unsigned long mem_avail = 0; diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index 77eb2dbfe789..dea7695e849f 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -552,39 +552,26 @@ unsigned long mm_ptov (unsigned long paddr) void cache_clear (unsigned long paddr, int len) { if (CPU_IS_040_OR_060) { + int tmp; + /* * cwe need special treatment for the first page, in case it * is not page-aligned. */ - if (paddr & (PAGE_SIZE - 1)){ + if ((tmp = -paddr & (PAGE_SIZE - 1))) { pushcl040(paddr); - if (len <= PAGE_SIZE){ - if (((paddr + len - 1) ^ paddr) & PAGE_MASK) { - pushcl040(paddr + len - 1); - } + if ((len -= tmp) <= 0) return; - }else{ - len -=PAGE_SIZE; - paddr += PAGE_SIZE; - } + paddr += tmp; } - - while (len > PAGE_SIZE) { -#if 0 - pushcl040(paddr); -#else + tmp = PAGE_SIZE; + while ((len -= tmp) >= 0) { clear040(paddr); -#endif - len -= PAGE_SIZE; - paddr += PAGE_SIZE; + paddr += tmp; } - if (len > 0) { + if ((len += tmp)) + /* a page boundary gets crossed at the end */ pushcl040(paddr); - if (((paddr + len - 1) ^ paddr) & PAGE_MASK) { - /* a page boundary gets crossed at the end */ - pushcl040(paddr + len - 1); - } - } } else /* 68030 or 68020 */ asm volatile ("movec %/cacr,%/d0\n\t" @@ -605,26 +592,19 @@ void cache_clear (unsigned long paddr, int len) void cache_push (unsigned long paddr, int len) { if (CPU_IS_040_OR_060) { + int tmp = PAGE_SIZE; + /* * on 68040 or 68060, push cache lines for pages in the range; * on the '040 this also invalidates the pushed lines, but not on * the '060! */ - while (len > PAGE_SIZE) { - pushcli040(paddr); - len -= PAGE_SIZE; - paddr += PAGE_SIZE; - } - if (len > 0) { + len += paddr & (PAGE_SIZE - 1); + do { pushcli040(paddr); - if (((paddr + len - 1) ^ paddr) & PAGE_MASK) { - /* a page boundary gets crossed at the end */ - pushcli040(paddr + len - 1); - } - } - } - - + paddr += tmp; + } while ((len -= tmp) > 0); + } /* * 68030/68020 have no writeback cache. On the other hand, * cache_push is actually a superset of cache_clear (the lines @@ -654,34 +634,24 @@ void cache_push (unsigned long paddr, int len) void cache_push_v (unsigned long vaddr, int len) { if (CPU_IS_040) { + int tmp = PAGE_SIZE; + /* on 68040, push cache lines for pages in the range */ - while (len > PAGE_SIZE) { - pushv040(vaddr); - len -= PAGE_SIZE; - vaddr += PAGE_SIZE; - } - if (len > 0) { + len += vaddr & (PAGE_SIZE - 1); + do { pushv040(vaddr); - if (((vaddr + len - 1) ^ vaddr) & PAGE_MASK) { - /* a page boundary gets crossed at the end */ - pushv040(vaddr + len - 1); - } - } - } + vaddr += tmp; + } while ((len -= tmp) > 0); + } else if (CPU_IS_060) { + int tmp = PAGE_SIZE; + /* on 68040, push cache lines for pages in the range */ - while (len > PAGE_SIZE) { - pushv060(vaddr); - len -= PAGE_SIZE; - vaddr += PAGE_SIZE; - } - if (len > 0) { + len += vaddr & (PAGE_SIZE - 1); + do { pushv060(vaddr); - if (((vaddr + len - 1) ^ vaddr) & PAGE_MASK) { - /* a page boundary gets crossed at the end */ - pushv060(vaddr + len - 1); - } - } + vaddr += tmp; + } while ((len -= tmp) > 0); } /* 68030/68020 have no writeback cache; still need to clear icache. */ else /* 68030 or 68020 */ diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 0005004bebdc..85566667f4fb 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -54,6 +54,7 @@ bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA fi endmenu diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index ff91819b0110..a6df2dcc0788 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -42,6 +42,7 @@ SUN_FB_CGFOURTEEN=y SUN_FB_BWTWO=y SUN_FB_LEO=y TADPOLE_FB_WEITEK=y +SUN_FB_CREATOR=y # # Misc Linux/SPARC drivers @@ -177,6 +178,9 @@ CONFIG_MYRI_SBUS=m # Filesystems # CONFIG_QUOTA=y +# CONFIG_DCACHE_PRELOAD is not set +# CONFIG_OMIRR is not set +# CONFIG_TRANS_NAMES is not set CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y CONFIG_FAT_FS=m diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index c8cdc0134a12..6354eddedee2 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -59,6 +59,9 @@ fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +fi endmenu mainmenu_option next_comment diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 579fbb4c2e61..0d95e1b75b25 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.27 1997/05/27 19:30:11 jj Exp $ +/* $Id: entry.S,v 1.31 1997/06/02 06:33:25 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -18,7 +18,7 @@ #include #include -/* define SYSCALL_TRACING */ +/* #define SYSCALL_TRACING */ #define curptr g6 @@ -39,82 +39,84 @@ * it will not get updated properly. */ sparc64_dtlb_prot_catch: - wr %g0, ASI_DMMU, %asi - rdpr %pstate, %g1 - wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate - rdpr %tl, %g2 - ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 - ldxa [%g0 + TLB_SFSR] %asi, %g4 - cmp %g2, 1 - stxa %g0, [%g0 + TLB_SFSR] %asi - bgu,a %icc, winfix_trampoline - rdpr %tpc, %g5 - ba,pt %xcc, etrap - rd %pc, %g7 - b,a,pt %xcc, 1f + wr %g0, ASI_DMMU, %asi + rdpr %pstate, %g1 + wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate + rdpr %tl, %g3 + ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 + ldxa [%g0 + TLB_SFSR] %asi, %g4 + cmp %g3, 1 + stxa %g0, [%g0 + TLB_SFSR] %asi + bgu,a,pn %icc, winfix_trampoline + rdpr %tpc, %g3 + ba,pt %xcc, etrap + rd %pc, %g7 + b,a,pt %xcc, 1f sparc64_dtlb_refbit_catch: - srlx %g5, 9, %g4 - and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 - cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9) - be,a,pt %xcc, 2f - mov 1, %g4 - wr %g0, ASI_DMMU, %asi - rdpr %pstate, %g1 - wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate - rdpr %tl, %g2 - ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 - cmp %g2, 1 - clr %g4 ! sfsr not updated for tlb misses - bgu,a %icc, winfix_trampoline - rdpr %tpc, %g5 - ba,pt %xcc, etrap - rd %pc, %g7 + srlx %g5, 9, %g4 + and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 + cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9) + be,a,pt %xcc, 2f + mov 1, %g4 + wr %g0, ASI_DMMU, %asi + rdpr %pstate, %g1 + wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate + rdpr %tl, %g3 + ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 + cmp %g3, 1 + clr %g4 ! sfsr not updated for tlb misses + bgu,a,pn %icc, winfix_trampoline + rdpr %tpc, %g3 + ba,pt %xcc, etrap + rd %pc, %g7 1: - mov %l5, %o4 ! raw tag access - mov %l4, %o5 ! raw sfsr - srlx %l5, PAGE_SHIFT, %o3 - clr %o1 ! text_fault == 0 - sllx %o3, PAGE_SHIFT, %o3 ! address - and %l4, 0x4, %o2 ! write == sfsr.W - call do_sparc64_fault - add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr - ba,a,pt %xcc, rtrap + mov %l5, %o4 ! raw tag access + mov %l4, %o5 ! raw sfsr + srlx %l5, PAGE_SHIFT, %o3 + clr %o1 ! text_fault == 0 + sllx %o3, PAGE_SHIFT, %o3 ! address + and %l4, 0x4, %o2 ! write == sfsr.W + call do_sparc64_fault + add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr + ba,pt %xcc, rtrap + clr %l6 sparc64_itlb_refbit_catch: - srlx %g5, 9, %g4 - and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 - cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9) - be,a,pt %xcc, 3f - mov 1, %g4 - rdpr %pstate, %g1 - wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate - ba,pt %xcc, etrap - rd %pc, %g7 - - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %o3 - mov 1, %o1 ! text_fault == 1 - clr %o2 ! write == 0 - clr %o4 ! tag access (N/A) - clr %o5 ! raw sfsr (N/A) - call do_sparc64_fault - add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr - ba,a,pt %xcc, rtrap + srlx %g5, 9, %g4 + and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 + cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9) + be,a,pt %xcc, 3f + mov 1, %g4 + rdpr %pstate, %g1 + wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate + ba,pt %xcc, etrap + rd %pc, %g7 + + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %o3 + mov 1, %o1 ! text_fault == 1 + clr %o2 ! write == 0 + clr %o4 ! tag access (N/A) + clr %o5 ! raw sfsr (N/A) + call do_sparc64_fault + add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr + ba,pt %xcc, rtrap + clr %l6 2: - sllx %g4, 63, %g4 ! _PAGE_VALID - or %g5, _PAGE_ACCESSED, %g5 - or %g5, %g4, %g5 - stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE - stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load + sllx %g4, 63, %g4 ! _PAGE_VALID + or %g5, _PAGE_ACCESSED, %g5 + or %g5, %g4, %g5 + stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE + stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load retry 3: - sllx %g4, 63, %g4 ! _PAGE_VALID - or %g5, _PAGE_ACCESSED, %g5 - or %g5, %g4, %g5 - stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE - stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load + sllx %g4, 63, %g4 ! _PAGE_VALID + or %g5, _PAGE_ACCESSED, %g5 + or %g5, %g4, %g5 + stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE + stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load retry /* Note check out head.h, this code isn't even used for UP, @@ -131,268 +133,285 @@ sparc64_itlb_refbit_catch: .align 4 .globl do_ivec do_ivec: - ldxa [%g0] ASI_INTR_RECEIVE, %g1 - andcc %g1, 0x20, %g0 - be,pn %xcc, do_ivec_return - mov 0x40, %g2 + ldxa [%g0] ASI_INTR_RECEIVE, %g1 + andcc %g1, 0x20, %g0 + be,pn %xcc, do_ivec_return + mov 0x40, %g2 /* Load up Interrupt Vector Data 0 register. */ - sethi %uhi(ivector_to_mask), %g4 - ldxa [%g2] ASI_UDB_INTR_R, %g3 - or %g4, %ulo(ivector_to_mask), %g4 - and %g3, 0x7ff, %g3 - sllx %g4, 32, %g4 - sethi %hi(ivector_to_mask), %g5 - sllx %g3, 3, %g3 - or %g5, %lo(ivector_to_mask), %g5 - add %g5, %g4, %g4 - ldx [%g4 + %g3], %g2 - brz,pn %g2, do_ivec_spurious + sethi %uhi(ivector_to_mask), %g4 + ldxa [%g2] ASI_UDB_INTR_R, %g3 + or %g4, %ulo(ivector_to_mask), %g4 + and %g3, 0x7ff, %g3 + sllx %g4, 32, %g4 + sethi %hi(ivector_to_mask), %g5 + sllx %g3, 3, %g3 + or %g5, %lo(ivector_to_mask), %g5 + add %g5, %g4, %g4 + ldx [%g4 + %g3], %g2 + brz,pn %g2, do_ivec_spurious nop /* No branches, worse case we don't know about this interrupt * yet, so we would just write a zero into the softint register * which is completely harmless. */ - wr %g2, 0x0, %set_softint + wr %g2, 0x0, %set_softint do_ivec_return: /* Acknowledge the UPA */ - stxa %g0, [%g0] ASI_INTR_RECEIVE - membar #Sync + stxa %g0, [%g0] ASI_INTR_RECEIVE + membar #Sync retry do_ivec_spurious: - stxa %g0, [%g0] ASI_INTR_RECEIVE - rdpr %pstate, %g1 - wrpr %g1, PSTATE_IG | PSTATE_AG, %pstate - ba,pt %xcc, etrap - rd %pc, %g7 - call report_spurious_ivec - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ba,pt %xcc, rtrap - nop + stxa %g0, [%g0] ASI_INTR_RECEIVE + rdpr %pstate, %g1 + wrpr %g1, PSTATE_IG | PSTATE_AG, %pstate + ba,pt %xcc, etrap + rd %pc, %g7 + call report_spurious_ivec + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 + + .globl do_mna +do_mna: + rdpr %tl, %g3 + cmp %g3, 1 + bgu,a,pn %icc, winfix_mna + rdpr %tpc, %g3 + ba,pt %xcc, etrap + rd %pc, %g7 + call mem_address_unaligned + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 -breakpoint_t: - .asciz "Breakpoint Trap %lx\n" - .align 4 .globl breakpoint_trap breakpoint_trap: - mov %o0, %o1 - sethi %hi(breakpoint_t), %o0 - or %o0, %lo(breakpoint_t), %o0 - call prom_printf - add %o0, %g4, %o0 - call prom_cmdline + call sparc_breakpoint + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap nop - ba,a,pt %xcc, rtrap .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall .globl sys_sigsuspend, sys_sigreturn .globl sys32_execve, sys_ptrace sys_pipe: - sethi %hi(sparc_pipe), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc_pipe), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + sethi %hi(sparc_pipe), %g1 + add %g1, %g4, %g1 + jmpl %g1 + %lo(sparc_pipe), %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 sys_nis_syscall: - sethi %hi(c_sys_nis_syscall), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(c_sys_nis_syscall), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + sethi %hi(c_sys_nis_syscall), %g1 + add %g1, %g4, %g1 + jmpl %g1 + %lo(c_sys_nis_syscall), %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 sys_execve: - sethi %hi(sparc_execve), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + sethi %hi(sparc_execve), %g1 + add %g1, %g4, %g1 + jmpl %g1 + %lo(sparc_execve), %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 sys32_execve: - sethi %hi(sparc32_execve), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc32_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + sethi %hi(sparc32_execve), %g1 + add %g1, %g4, %g1 + jmpl %g1 + %lo(sparc32_execve), %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 sys_sigpause: /* NOTE: %o0 has a correct value already */ - call do_sigpause - add %sp, STACK_BIAS + REGWIN_SZ, %o1 + call do_sigpause + add %sp, STACK_BIAS + REGWIN_SZ, %o1 - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace nop - call syscall_trace - nop - ba,a,pt %xcc, rtrap + ba,pt %xcc, rtrap + clr %l6 sys_sigsuspend: - call do_sigsuspend - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_sigsuspend + add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - nop - call syscall_trace + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace nop - ba,a,pt %xcc, rtrap + ba,pt %xcc, rtrap + clr %l6 sys_sigreturn: - call do_sigreturn - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_sigreturn + add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace nop - call syscall_trace - nop - ba,a,pt %xcc, rtrap + ba,pt %xcc, rtrap + clr %l6 sys_ptrace: - call do_ptrace - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_ptrace + add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - nop - call syscall_trace + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace nop - ba,a,pt %xcc, rtrap + ba,pt %xcc, rtrap + clr %l6 - /* This is how fork() was meant to be done, 10 instruction entry. -DaveM */ + /* This is how fork() was meant to be done, 12 instruction entry. -DaveM */ .globl sys_fork, sys_vfork, sys_clone sys_fork: sys_vfork: - mov SIGCHLD, %o0 - clr %o1 + mov SIGCHLD, %o0 + clr %o1 sys_clone: - mov %o7, %l5 + mov %o7, %l5 + save %sp, -REGWIN_SZ, %sp flushw - rdpr %cwp, %o4 - add %sp, STACK_BIAS + REGWIN_SZ, %o2 - movrz %o1, %fp, %o1 + restore %g0, %g0, %g0 + rdpr %cwp, %o4 + add %sp, STACK_BIAS + REGWIN_SZ, %o2 + movrz %o1, %fp, %o1 /* Don't try this at home. */ - stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0] - call do_fork - mov %l5, %o7 + stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0] + call do_fork + mov %l5, %o7 linux_sparc_ni_syscall: - sethi %hi(sys_ni_syscall), %l7 - or %l7, %lo(sys_ni_syscall), %l7 - ba,pt %xcc,syscall_is_too_hard - add %l7, %g4, %l7 + sethi %hi(sys_ni_syscall), %l7 + or %l7, %lo(sys_ni_syscall), %l7 + ba,pt %xcc,syscall_is_too_hard + add %l7, %g4, %l7 linux_fast_syscall: - andn %l7, 3, %l7 - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - jmpl %l7 + %g0, %g0 - mov %i3, %o3 + andn %l7, 3, %l7 + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + jmpl %l7 + %g0, %g0 + mov %i3, %o3 linux_syscall_trace: - call syscall_trace + call syscall_trace nop - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - mov %i3, %o3 - ba,pt %xcc, 2f - mov %i4, %o4 + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + mov %i3, %o3 + ba,pt %xcc, 2f + mov %i4, %o4 .globl ret_from_syscall ret_from_syscall: - ba,pt %xcc, ret_sys_call - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 + ba,pt %xcc, ret_sys_call + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 /* Linux native and SunOS system calls enter here... */ .align 4 .globl linux_sparc_syscall linux_sparc_syscall: /* Direct access to user regs, must faster. */ - cmp %g1, NR_SYSCALLS - add %l7, %g4, %l7 - bgeu,pn %xcc, linux_sparc_ni_syscall - sll %g1, 3, %l4 - ldx [%l7 + %l4], %l7 - andcc %l7, 1, %g0 - bne,pn %icc, linux_fast_syscall + cmp %g1, NR_SYSCALLS + add %l7, %g4, %l7 + bgeu,pn %xcc, linux_sparc_ni_syscall + sll %g1, 3, %l4 + ldx [%l7 + %l4], %l7 + andcc %l7, 1, %g0 + bne,pn %icc, linux_fast_syscall /* Just do the next insn in the delay slot */ .globl syscall_is_too_hard syscall_is_too_hard: #ifdef SYSCALL_TRACING /* Debugging... */ - mov %g1, %o0 ! o0=scall, o1=ptregs - call syscall_trace_entry - add %sp, STACK_BIAS + REGWIN_SZ, %o1 + mov %g1, %o0 ! o0=scall, o1=ptregs + call syscall_trace_entry + add %sp, STACK_BIAS + REGWIN_SZ, %o1 #endif - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - - ldx [%curptr + AOFF_task_flags], %l5 - mov %i3, %o3 - mov %i4, %o4 - andcc %l5, 0x20, %g0 - bne,pn %icc, linux_syscall_trace - mov %i0, %l5 + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + + ldx [%curptr + AOFF_task_flags], %l5 + mov %i3, %o3 + mov %i4, %o4 + andcc %l5, 0x20, %g0 + bne,pn %icc, linux_syscall_trace + mov %i0, %l5 2: - call %l7 - mov %i5, %o5 + call %l7 + mov %i5, %o5 #ifdef SYSCALL_TRACING /* Debugging... */ - call syscall_trace_exit ! o0=sysret, o1=ptregs - add %sp, STACK_BIAS + REGWIN_SZ, %o1 + call syscall_trace_exit ! o0=sysret, o1=ptregs + add %sp, STACK_BIAS + REGWIN_SZ, %o1 #endif - stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] .globl ret_sys_call ret_sys_call: - ldx [%curptr + AOFF_task_flags], %l6 - mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3 - cmp %o0, -ENOIOCTLCMD - sllx %g2, 32, %g2 - bgeu,pn %xcc, 1f - andcc %l6, 0x20, %l6 + ldx [%curptr + AOFF_task_flags], %l6 + ldx [%curptr + AOFF_task_tss + AOFF_thread_flags], %l2 + mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 + and %l2, SPARC_FLAG_32BIT, %l2 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3 + brnz,a,pn %l2, 1f + sra %o0, 0, %o0 +1: + cmp %o0, -ENOIOCTLCMD + sllx %g2, 32, %g2 + bgeu,pn %xcc, 1f + andcc %l6, 0x20, %l6 /* System call success, clear Carry condition code. */ - andn %g3, %g2, %g3 - clr %l6 - stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] - bne,pn %icc, linux_syscall_trace2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 /* pc = npc */ - add %l1, 0x4, %l2 /* npc = npc+4 */ - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - ba,pt %xcc, rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] + andn %g3, %g2, %g3 + clr %l6 + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] + bne,pn %icc, linux_syscall_trace2 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc + add %l1, 0x4, %l2 !npc = npc+4 + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] + ba,pt %xcc, rtrap + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] 1: /* System call failure, set Carry condition code. * Also, get abs(errno) to return to the process. */ - sub %g0, %o0, %o0 - or %g3, %g2, %g3 - stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] - mov 1, %l6 - stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] - bne,pn %icc, linux_syscall_trace2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 /* pc = npc */ - add %l1, 0x4, %l2 /* npc = npc+4 */ - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - ba,pt %xcc, rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] + sub %g0, %o0, %o0 + or %g3, %g2, %g3 + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + mov 1, %l6 + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] + bne,pn %icc, linux_syscall_trace2 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc + add %l1, 0x4, %l2 !npc = npc+4 + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] + ba,pt %xcc, rtrap + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] linux_syscall_trace2: - call syscall_trace - add %l1, 0x4, %l2 /* npc = npc+4 */ - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - ba,pt %xcc, rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] + call syscall_trace + add %l1, 0x4, %l2 /* npc = npc+4 */ + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] + ba,pt %xcc, rtrap + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] /* End of entry.S */ diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S index 0c166ec2595a..efb1b48fcf73 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.18 1997/05/19 05:58:51 davem Exp $ +/* $Id: etrap.S,v 1.21 1997/06/02 06:33:28 davem Exp $ * etrap.S: Preparing for entry into the kernel on Sparc V9. * * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -22,7 +22,7 @@ .text .align 32 - .globl etrap, etrap_irq + .globl etrap, etrap_irq, etraptl1 etrap: rdpr %pil, %g2 etrap_irq: @@ -45,13 +45,14 @@ etrap_irq: stx %g3, [%g2 + REGWIN_SZ + PT_V9_TNPC] stx %g1, [%g2 + REGWIN_SZ + PT_V9_Y] - rdpr %pstate, %g1 - save %g2, -STACK_BIAS, %sp - bne,pn %xcc, 1f + save %g2, -STACK_BIAS, %sp ! The ordering of these two instructions + rdpr %pstate, %g1 ! is critical, see winfixup.S for details + bne,pn %xcc, 2f rdpr %canrestore, %g3 rdpr %wstate, %g6 - wrpr %g0, 0, %canrestore + wrpr %g0, 7, %cleanwin + wrpr %g0, 0, %canrestore sll %g6, 3, %g6 wrpr %g3, 0, %otherwin wrpr %g6, %wstate @@ -59,17 +60,17 @@ etrap_irq: sllx %g3, 32, %g3 mov PRIMARY_CONTEXT, %g2 stxa %g0, [%g2] ASI_DMMU + flush %g3 -1: - wrpr %g0, 0x0, %tl +2: wrpr %g0, 0x0, %tl mov %g1, %l1 mov %g4, %l4 mov %g5, %l5 mov %g7, %l2 wrpr %l1, PSTATE_AG, %pstate stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1] - stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] + stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3] stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4] stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5] @@ -77,8 +78,8 @@ etrap_irq: stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7] stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] - stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] + stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3] stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4] stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5] @@ -86,16 +87,13 @@ etrap_irq: stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate sethi %uhi(KERNBASE), %g4 - rd %pic, %g6 + rd %pic, %g6 jmpl %l2 + 0x4, %g0 sllx %g4, 32, %g4 - - .globl etraptl1 etraptl1: rdpr %tstate, %g1 + sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 ba,pt %xcc, 1b - sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 - nop - nop + andcc %g1, TSTATE_PRIV, %g0 nop diff --git a/arch/sparc64/kernel/hack.S b/arch/sparc64/kernel/hack.S index 6303bd9e9ff9..84322139544e 100644 --- a/arch/sparc64/kernel/hack.S +++ b/arch/sparc64/kernel/hack.S @@ -24,16 +24,12 @@ do_fpother_tl1: retl;nop do_iae_tl1: retl;nop .globl do_ill_tl1 do_ill_tl1: retl;nop - .globl do_irq -do_irq: retl;nop .globl do_irq_tl1 do_irq_tl1: retl;nop .globl do_lddfmna do_lddfmna: retl;nop .globl do_lddfmna_tl1 do_lddfmna_tl1: retl;nop - .globl do_mna_tl1 -do_mna_tl1: retl;nop .globl do_paw do_paw: retl;nop .globl do_paw_tl1 @@ -51,7 +47,7 @@ do_vaw_tl1: retl;nop .globl floppy_hardint floppy_hardint: retl;nop .globl get_cpuid -get_cpuid: retl;nop +get_cpuid: retl;mov 0, %o0 .globl getcc getcc: retl;nop .globl halt diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 4babe3eb4ffb..3844c24c3842 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.30 1997/05/18 22:52:12 davem Exp $ +/* $Id: head.S,v 1.31 1997/05/30 22:35:28 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -55,7 +55,7 @@ ramdisk_image: .word 0 ramdisk_size: .word 0 - .word reboot_command + .xword reboot_command /* We must be careful, 32-bit OpenBOOT will get confused if it * tries to save away a register window to a 64-bit kernel diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 3db6fa94596f..d3792dec6a8d 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.3 1997/05/27 19:30:13 jj Exp $ +/* $Id: ioctl32.c,v 1.8 1997/06/04 13:05:15 jj Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -7,22 +7,369 @@ * ioctls. */ +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include -/* As gcc will warn about casting u32 to some ptr, we have to cast it to unsigned long first, and that's what is A() for. - * You just do (void *)A(x), instead of having to type (void *)((unsigned long)x) or instead of just (void *)x, which will - * produce warnings */ +/* As gcc will warn about casting u32 to some ptr, we have to cast it to + * unsigned long first, and that's what is A() for. + * You just do (void *)A(x), instead of having to type (void *)((unsigned long)x) + * or instead of just (void *)x, which will produce warnings. + */ #define A(x) ((unsigned long)x) extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +static int w_long(unsigned int fd, unsigned int cmd, u32 arg) +{ + unsigned long old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)A(arg))) + return -EFAULT; + return err; +} +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +static inline int dev_ifconf(unsigned int fd, u32 arg) +{ + struct ifconf32 ifc32; + struct ifconf ifc; + struct ifreq32 *ifr32; + struct ifreq *ifr; + unsigned long old_fs; + unsigned int i, j; + int err; + + if (copy_from_user(&ifc32, (struct ifconf32 *)A(arg), sizeof(struct ifconf32))) + return -EFAULT; + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * sizeof (struct ifreq); + ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); + if (!ifc.ifc_buf) return -ENOMEM; + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) { + if (copy_from_user(ifr++, ifr32++, sizeof (struct ifreq32))) { + kfree (ifc.ifc_buf); + return -EFAULT; + } + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc); + set_fs (old_fs); + if (!err) { + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0, j = 0; i < ifc32.ifc_len && j < ifc.ifc_len; + i += sizeof (struct ifreq32), j += sizeof (struct ifreq)) { + if (copy_to_user(ifr32++, ifr++, sizeof (struct ifreq32))) { + err = -EFAULT; + break; + } + } + if (!err) { + if (i <= ifc32.ifc_len) + ifc32.ifc_len = i; + else + ifc32.ifc_len = i - sizeof (struct ifreq32); + if (copy_to_user((struct ifconf32 *)A(arg), &ifc32, sizeof(struct ifconf32))) + err = -EFAULT; + } + } + kfree (ifc.ifc_buf); + return err; +} + +static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct ifreq ifr; + unsigned long old_fs; + int err; + + if (cmd == SIOCSIFMAP) { + if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(ifr.ifr_name)) || + __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) || + __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_end)) || + __get_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.base_addr)) || + __get_user(ifr.ifr_map.irq, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.irq)) || + __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.dma)) || + __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.port))) + return -EFAULT; + } else { + if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(struct ifreq32))) + return -EFAULT; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + switch (cmd) { + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOGIFINDEX: + case SIOCGIFADDR: + case SIOCGIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCGIFNETMASK: + if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + case SIOCGIFMAP: + if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(ifr.ifr_name)) || + __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) || + __put_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_end)) || + __put_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.base_addr)) || + __put_user(ifr.ifr_map.irq, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.irq)) || + __put_user(ifr.ifr_map.dma, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.dma)) || + __put_user(ifr.ifr_map.port, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.port))) + return -EFAULT; + break; + } + } + return err; +} + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +static inline int routing_ioctl(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct rtentry r; + char devname[16]; + u32 rtdev; + int ret; + unsigned long old_fs = get_fs(); + + if (get_user (r.rt_pad1, &(((struct rtentry32 *)A(arg))->rt_pad1)) || + copy_from_user (&r.rt_dst, &(((struct rtentry32 *)A(arg))->rt_dst), 3 * sizeof(struct sockaddr)) || + __get_user (r.rt_flags, &(((struct rtentry32 *)A(arg))->rt_flags)) || + __get_user (r.rt_pad2, &(((struct rtentry32 *)A(arg))->rt_pad2)) || + __get_user (r.rt_pad3, &(((struct rtentry32 *)A(arg))->rt_pad3)) || + __get_user (r.rt_tos, &(((struct rtentry32 *)A(arg))->rt_tos)) || + __get_user (r.rt_class, &(((struct rtentry32 *)A(arg))->rt_class)) || + __get_user (r.rt_pad4, &(((struct rtentry32 *)A(arg))->rt_pad4)) || + __get_user (r.rt_metric, &(((struct rtentry32 *)A(arg))->rt_metric)) || + __get_user (r.rt_mtu, &(((struct rtentry32 *)A(arg))->rt_mtu)) || + __get_user (r.rt_window, &(((struct rtentry32 *)A(arg))->rt_window)) || + __get_user (r.rt_irtt, &(((struct rtentry32 *)A(arg))->rt_irtt)) || + __get_user (rtdev, &(((struct rtentry32 *)A(arg))->rt_dev)) || + (rtdev && copy_from_user (devname, (char *)A(rtdev), 15))) + return -EFAULT; + if (rtdev) { + r.rt_dev = devname; devname[15] = 0; + } else + r.rt_dev = 0; + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long)&r); + set_fs (old_fs); + return ret; +} + +struct nlmsghdr32 { + u32 nlmsg_len; /* Length of message including header */ + u32 nlmsg_type; /* Message type */ + u32 nlmsg_seq; /* Sequence number */ + u32 nlmsg_pid; /* Sending process PID */ + unsigned char nlmsg_data[0]; +}; + +struct in_rtmsg32 { + struct in_addr rtmsg_prefix; + struct in_addr rtmsg_gateway; + unsigned rtmsg_flags; + u32 rtmsg_mtu; + u32 rtmsg_window; + unsigned short rtmsg_rtt; + short rtmsg_metric; + unsigned char rtmsg_tos; + unsigned char rtmsg_class; + unsigned char rtmsg_prefixlen; + unsigned char rtmsg_reserved; + int rtmsg_ifindex; +}; + +struct in_ifmsg32 { + struct sockaddr ifmsg_lladdr; + struct in_addr ifmsg_prefix; + struct in_addr ifmsg_brd; + unsigned ifmsg_flags; + u32 ifmsg_mtu; + short ifmsg_metric; + unsigned char ifmsg_prefixlen; + unsigned char ifmsg_reserved; + int ifmsg_index; + char ifmsg_name[16]; +}; + +static inline int rtmsg_ioctl(unsigned int fd, u32 arg) +{ + struct { + struct nlmsghdr n; + union { + struct in_rtmsg rt; + struct in_ifmsg iff; + struct in_rtctlmsg ctl; + struct in_rtrulemsg rule; + } u; + } nn; + char *p; + int ret; + unsigned long old_fs = get_fs(); + + if (get_user (nn.n.nlmsg_len, &(((struct nlmsghdr32 *)A(arg))->nlmsg_len)) || + __get_user (nn.n.nlmsg_type, &(((struct nlmsghdr32 *)A(arg))->nlmsg_type)) || + __get_user (nn.n.nlmsg_seq, &(((struct nlmsghdr32 *)A(arg))->nlmsg_seq)) || + __get_user (nn.n.nlmsg_pid, &(((struct nlmsghdr32 *)A(arg))->nlmsg_pid)) || + __get_user (nn.n.nlmsg_data[0], &(((struct nlmsghdr32 *)A(arg))->nlmsg_data[0]))) + return -EFAULT; + p = ((char *)(&nn.n)) + sizeof(struct nlmsghdr); + arg += sizeof(struct nlmsghdr32); + switch (nn.n.nlmsg_type) { + case RTMSG_NEWRULE: + case RTMSG_DELRULE: + if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtrulemsg) + - sizeof(struct in_rtmsg) + sizeof(struct in_rtmsg32)) + return -EINVAL; + if (copy_from_user (p, (struct in_rtrulemsg *)A(arg), sizeof(struct in_rtrulemsg) - sizeof(struct in_rtmsg))) + return -EFAULT; + nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtrulemsg); + p += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg); + arg += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg); + goto newroute; + case RTMSG_NEWROUTE: + case RTMSG_DELROUTE: + if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtmsg)) + return -EINVAL; + nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtmsg); +newroute: + if (copy_from_user (p, (struct in_rtmsg32 *)A(arg), 2*sizeof(struct in_addr) + sizeof(unsigned)) || + __get_user (((struct in_rtmsg *)p)->rtmsg_mtu, &((struct in_rtmsg32 *)A(arg))->rtmsg_mtu) || + __get_user (((struct in_rtmsg *)p)->rtmsg_window, &((struct in_rtmsg32 *)A(arg))->rtmsg_window) || + copy_from_user (&(((struct in_rtmsg *)p)->rtmsg_rtt), &((struct in_rtmsg32 *)A(arg))->rtmsg_rtt, + 2 * sizeof(short) + 4 + sizeof(int))) + return -EFAULT; + break; + case RTMSG_NEWDEVICE: + case RTMSG_DELDEVICE: + if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_ifmsg)) + return -EINVAL; + nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_ifmsg); + if (copy_from_user (p, (struct in_ifmsg32 *)A(arg), + sizeof(struct sockaddr) + 2*sizeof(struct in_addr) + sizeof(unsigned)) || + __get_user (((struct in_ifmsg *)p)->ifmsg_mtu, &((struct in_ifmsg32 *)A(arg))->ifmsg_mtu) || + copy_from_user (&(((struct in_ifmsg *)p)->ifmsg_metric), &((struct in_ifmsg32 *)A(arg))->ifmsg_metric, + sizeof(short) + 2 + sizeof(int) + 16)) + return -EFAULT; + break; + case RTMSG_CONTROL: + if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtctlmsg)) + return -EINVAL; + nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtctlmsg); + if (copy_from_user (p, (struct in_rtctlmsg *)A(arg), sizeof(struct in_rtctlmsg))) + return -EFAULT; + break; + } + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, SIOCRTMSG, (long)&(nn.n)); + set_fs (old_fs); + return ret; +} + +struct hd_geometry32 { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + u32 start; +}; + +static inline int hdio_getgeo(unsigned int fd, u32 arg) +{ + unsigned long old_fs = get_fs(); + struct hd_geometry geo; + int err; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, HDIO_GETGEO, (unsigned long)&geo); + set_fs (old_fs); + if (!err) { + if (copy_to_user ((struct hd_geometry32 *)A(arg), &geo, 4) || + __put_user (geo.start, &(((struct hd_geometry32 *)A(arg))->start))) + return -EFAULT; + } + return err; +} + asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) { struct file * filp; @@ -35,16 +382,149 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) error = sys_ioctl (fd, cmd, (unsigned long)arg); goto out; } - error = 0; + error = -EFAULT; switch (cmd) { - default: - error = sys_ioctl (fd, cmd, (unsigned long)arg); - goto out; + case SIOCGIFCONF: + error = dev_ifconf(fd, arg); + goto out; + + case SIOCGIFFLAGS: + case SIOCSIFFLAGS: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + case SIOCGIFMTU: + case SIOCSIFMTU: + case SIOCGIFMEM: + case SIOCSIFMEM: + case SIOCGIFHWADDR: + case SIOCSIFHWADDR: + case SIOCADDMULTI: + case SIOCDELMULTI: + case SIOGIFINDEX: + case SIOCGIFMAP: + case SIOCSIFMAP: + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + error = dev_ifsioc(fd, cmd, arg); + goto out; + + case SIOCADDRT: + case SIOCDELRT: + error = routing_ioctl(fd, cmd, arg); + goto out; + + case SIOCRTMSG: + error = rtmsg_ioctl(fd, arg); + goto out; + + case HDIO_GETGEO: + error = hdio_getgeo(fd, arg); + goto out; + + case BLKRAGET: + case BLKGETSIZE: + error = w_long(fd, cmd, arg); + goto out; + + /* List here exlicitly which ioctl's are known to have + * compatable types passed or none at all... + */ + + /* Bit T */ + case TCGETA: + case TCSETA: + case TCSETAW: + case TCSETAF: + case TCSBRK: + case TCXONC: + case TCFLSH: + case TCGETS: + case TCSETS: + case TCSETSW: + case TCSETSF: + case TIOCLINUX: + + /* Little t */ + case TIOCGETD: + case TIOCSETD: + case TIOCEXCL: + case TIOCNXCL: + case TIOCCONS: + case TIOCGSOFTCAR: + case TIOCSSOFTCAR: + case TIOCSWINSZ: + case TIOCGWINSZ: + case TIOCMGET: + case TIOCMBIC: + case TIOCMBIS: + case TIOCMSET: + case TIOCPKT: + case TIOCNOTTY: + case TIOCSTI: + case TIOCOUTQ: + case TIOCSPGRP: + case TIOCGPGRP: + case TIOCSCTTY: + + /* Little f */ + case FIOCLEX: + case FIONCLEX: + case FIOASYNC: + case FIONBIO: + case FIONREAD: /* This is also TIOCINQ */ + + /* 0x12 */ + case BLKRRPART: + case BLKFLSBUF: + case BLKRASET: + + /* 0x09 */ + case REGISTER_DEV: + case START_MD: + case STOP_MD: + + /* Big K */ + case PIO_FONT: + case GIO_FONT: + case KDSIGACCEPT: + case KDGETKEYCODE: + case KDSETKEYCODE: + + /* Socket level stuff */ + case FIOSETOWN: + case SIOCSPGRP: + case FIOGETOWN: + case SIOCGPGRP: + case SIOCATMARK: + case SIOCGSTAMP: + case SIOCSIFLINK: + case SIOCSIFENCAP: + case SIOCGIFENCAP: + case SIOCSIFBR: + case SIOCGIFBR: + case SIOCSARP: + case SIOCGARP: + case SIOCDARP: + case SIOCADDDLCI: + case SIOCDELDLCI: + error = sys_ioctl (fd, cmd, (unsigned long)arg); + goto out; + break; + + default: + printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + error = -EINVAL; + goto out; + break; } out: - if (error == -EINVAL) { - printk ("sys32_ioctl on %016lx's %08x returns EINVAL\n", filp->f_op ? (long)filp->f_op->ioctl : 0UL, cmd); - } unlock_kernel(); return error; } diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 593c1efc65ca..cc8183618d57 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.12 1997/05/23 09:35:43 jj Exp $ +/* $Id: process.c,v 1.17 1997/06/02 06:33:32 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -317,6 +317,13 @@ void exit_thread(void) #else if(current->flags & PF_USEDFPU) { #endif + fprs_write(FPRS_FEF); + if(current->tss.flags & SPARC_FLAG_32BIT) + fpsave32((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); + else + fpsave((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); #ifndef __SMP__ last_task_used_math = NULL; #else @@ -338,6 +345,13 @@ void flush_thread(void) #else if(current->flags & PF_USEDFPU) { #endif + fprs_write(FPRS_FEF); + if(current->tss.flags & SPARC_FLAG_32BIT) + fpsave32((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); + else + fpsave((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); #ifndef __SMP__ last_task_used_math = NULL; #else @@ -424,6 +438,8 @@ clone_stackframe(struct sparc_stackf *dst, struct sparc_stackf *src) return sp; } +/* #define DEBUG_WINFIXUPS */ + /* Standard stuff. */ static inline void shift_window_buffer(int first_win, int last_win, struct thread_struct *tp) @@ -440,12 +456,15 @@ static inline void shift_window_buffer(int first_win, int last_win, void synchronize_user_stack(void) { struct thread_struct *tp = ¤t->tss; - unsigned long window = tp->w_saved; + unsigned long window; flush_user_windows(); - if(window) { + if((window = tp->w_saved) != 0) { int winsize = REGWIN_SZ; +#ifdef DEBUG_WINFIXUPS + printk("sus(%d", (int)window); +#endif if(tp->flags & SPARC_FLAG_32BIT) winsize = REGWIN32_SZ; @@ -459,18 +478,26 @@ void synchronize_user_stack(void) tp->w_saved--; } } while(window--); +#ifdef DEBUG_WINFIXUPS + printk(")"); +#endif } } void fault_in_user_windows(struct pt_regs *regs) { struct thread_struct *tp = ¤t->tss; - unsigned long window = tp->w_saved; + unsigned long window; int winsize = REGWIN_SZ; if(tp->flags & SPARC_FLAG_32BIT) winsize = REGWIN32_SZ; - if(window) { + flush_user_windows(); + window = tp->w_saved; +#ifdef DEBUG_WINFIXUPS + printk("fiuw(%d", (int)window); +#endif + if(window != 0) { window -= 1; do { unsigned long sp = tp->rwbuf_stkptrs[window]; @@ -481,6 +508,9 @@ void fault_in_user_windows(struct pt_regs *regs) } while(window--); } current->tss.w_saved = 0; +#ifdef DEBUG_WINFIXUPS + printk(")"); +#endif } /* Copy a Sparc thread. The fork() return value conventions @@ -504,19 +534,17 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, struct reg_window *new_stack, *old_stack; unsigned long stack_offset; -#if 0 #ifndef __SMP__ if(last_task_used_math == current) { #else if(current->flags & PF_USEDFPU) { #endif - put_psr(get_psr() | PSR_EF); - fpsave(&p->tss.float_regs[0], &p->tss.fsr); + fprs_write(FPRS_FEF); + fpsave((unsigned long *)&p->tss.float_regs[0], &p->tss.fsr); #ifdef __SMP__ current->flags &= ~PF_USEDFPU; #endif } -#endif /* Calculate offset to stack_frame & pt_regs */ stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ); diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index 0f1dceb33b9f..165b17ef0d37 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.18 1997/05/27 06:28:05 davem Exp $ +/* $Id: rtrap.S,v 1.21 1997/06/02 07:26:54 davem Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -17,109 +17,114 @@ .text .align 32 - .globl rtrap -rtrap: - sethi %hi(bh_active), %l2 - or %l2, %lo(bh_active), %l2 - sethi %hi(bh_mask), %l1 - or %l1, %lo(bh_mask), %l1 - ldx [%l2 + %g4], %l3 - ldx [%l1 + %g4], %l4 - andcc %l3, %l4, %g0 - nop + .globl rtrap_clr_l6, rtrap +rtrap_clr_l6: + ba,pt %xcc, rtrap + clr %l6 +rtrap: sethi %hi(bh_active), %l2 + or %l2, %lo(bh_active), %l2 + sethi %hi(bh_mask), %l1 + or %l1, %lo(bh_mask), %l1 + ldx [%l2 + %g4], %l3 + ldx [%l1 + %g4], %l4 - be,pt %xcc, 2f + andcc %l3, %l4, %g0 + be,pt %xcc, 2f nop - call do_bottom_half + call do_bottom_half nop -2: ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %l1 - sethi %hi(0xf << 20), %l4 - andcc %l1, TSTATE_PRIV, %l3 - and %l1, %l4, %l4 +2: ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %l1 + sethi %hi(0xf << 20), %l4 + andcc %l1, TSTATE_PRIV, %l3 + + and %l1, %l4, %l4 + rdpr %pstate, %l7 + andn %l1, %l4, %l1 + be,pt %icc, to_user + andn %l7, PSTATE_IE, %l7 +3: ldx [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1], %g1 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2], %g2 - rdpr %pstate, %l7 - andn %l1, %l4, %l1 - be,pt %icc, to_user - andn %l7, PSTATE_IE, %l7 -3: ldx [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1], %g1 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2], %g2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3], %g3 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3], %g3 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4], %g4 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5], %g5 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6], %g6 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7], %g7 + wrpr %l7, PSTATE_AG, %pstate + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %i0 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1], %i1 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4], %g4 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5], %g5 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6], %g6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7], %g7 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %i0 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1], %i1 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2], %i2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3], %i3 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2], %i2 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3], %i3 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4], %i4 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5], %i5 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6], %i6 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7], %i7 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_Y], %o3 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %l2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4], %i4 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5], %i5 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6], %i6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7], %i7 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_Y], %o3 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %l2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %o2 - wr %o3, %g0, %y + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %o2 + wr %o3, %g0, %y + srl %l4, 20, %l4 + wrpr %l4, 0x0, %pil + wrpr %g0, 0x1, %tl + wrpr %l1, %g0, %tstate + wrpr %l2, %g0, %tpc + mov PRIMARY_CONTEXT, %l7 - srl %l4, 20, %l4 - wrpr %l7, %g0, %pstate - wrpr %l4, 0x0, %pil - wrpr %g0, 0x1, %tl - wrpr %l1, %g0, %tstate - wrpr %l2, %g0, %tpc - brnz,pn %l3, 1f - wrpr %o2, %g0, %tnpc + wrpr %o2, %g0, %tnpc + brnz,a,pn %l3, 1f + restore + sethi %uhi(KERNBASE), %l5 + sllx %l5, 32, %l5 + stxa %l0, [%l7] ASI_DMMU + flush %l5 + rdpr %wstate, %l1 - mov PRIMARY_CONTEXT, %l7 - sethi %uhi(KERNBASE), %l5 - sllx %l5, 32, %l5 - stxa %l6, [%l7] ASI_DMMU - flush %l5 - rdpr %wstate, %l1 - rdpr %otherwin, %l2 - srl %l1, 3, %l1 + rdpr %otherwin, %l2 + srl %l1, 3, %l1 + wrpr %l2, %g0, %canrestore + wrpr %l1, %g0, %wstate + wrpr %g0, %g0, %otherwin + restore + rdpr %canrestore, %g1 + wrpr %g1, 0x0, %cleanwin - wrpr %l2, %g0, %canrestore - wrpr %l1, %g0, %wstate - wrpr %g0, %g0, %otherwin -1: restore - retry +1: retry to_user: sethi %hi(need_resched), %l0 or %l0, %lo(need_resched), %l0 ld [%l0 + %g4], %l0 - wrpr %l7, PSTATE_IE, %pstate brz,pt %l0, check_signal ldx [%g6 + AOFF_task_signal], %l0 + nop + call schedule nop - ldx [%g6 + AOFF_task_signal], %l0 - nop + ba,pt %xcc, check_signal + ldx [%g6 + AOFF_task_signal], %l0 check_signal: ldx [%g6 + AOFF_task_blocked], %o0 - - or %l7, PSTATE_AG, %l7 ! Will need this for setting back wstate andncc %l0, %o0, %g0 - be,pt %xcc, check_user_wins - mov %l5, %o2 + be,a,pt %xcc, check_user_wins + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 + + mov %l5, %o2 mov %l6, %o3 call do_signal add %sp, STACK_BIAS + REGWIN_SZ, %o1 -check_user_wins: -#if 0 - call user_rtrap_report - add %sp, STACK_BIAS + REGWIN_SZ, %o0 -#endif ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - + clr %l6 +check_user_wins: brz,pt %o2, 3b - add %sp, STACK_BIAS + REGWIN_SZ, %o1 + nop + call fault_in_user_windows - add %o7, 3b-.-4, %o7 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,a,pt %xcc, 3b + nop nop nop nop diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index f81e3009379f..fe4615a6b6d5 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.4 1997/05/27 06:28:05 davem Exp $ +/* $Id: signal.c,v 1.6 1997/05/29 12:44:48 jj Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -193,13 +193,17 @@ save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { #ifdef __SMP__ if (current->flags & PF_USEDFPU) { - fpsave(¤t->tss.float_regs[0], ¤t->tss.fsr); + fprs_write(FPRS_FEF); + fpsave((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); regs->tstate &= ~(TSTATE_PEF); current->flags &= ~(PF_USEDFPU); } #else if (current == last_task_used_math) { - fpsave((unsigned long *)¤t->tss.float_regs[0], ¤t->tss.fsr); + fprs_write(FPRS_FEF); + fpsave((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); last_task_used_math = 0; regs->tstate &= ~(TSTATE_PEF); } diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 33892065f4da..c0454658b53a 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.10 1997/05/27 06:28:07 davem Exp $ +/* $Id: signal32.c,v 1.13 1997/06/01 05:46:09 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -139,35 +139,51 @@ restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu) void do_new_sigreturn32(struct pt_regs *regs) { struct new_signal_frame32 *sf; - unsigned int psr, i; + unsigned int psr; unsigned pc, npc, fpu_save, mask; sf = (struct new_signal_frame32 *) regs->u_regs [UREG_FP]; + /* 1. Make sure we are not getting garbage from the user */ - if (verify_area (VERIFY_READ, sf, sizeof (*sf))){ - goto segv; - } - if (((unsigned long) sf) & 3){ + if (verify_area (VERIFY_READ, sf, sizeof (*sf)) || + (((unsigned long) sf) & 3)) goto segv; - } + get_user(pc, &sf->info.si_regs.pc); __get_user(npc, &sf->info.si_regs.npc); - if ((pc | npc) & 3){ + + if ((pc | npc) & 3) goto segv; - } + regs->tpc = pc; regs->tnpc = npc; /* 2. Restore the state */ __get_user(regs->y, &sf->info.si_regs.y); __get_user(psr, &sf->info.si_regs.psr); - for (i = 0; i < 16; i++) - __get_user(regs->u_regs[i], &sf->info.si_regs.u_regs[i]); + + __get_user(regs->u_regs[UREG_G1], &sf->info.si_regs.u_regs[UREG_G1]); + __get_user(regs->u_regs[UREG_G2], &sf->info.si_regs.u_regs[UREG_G2]); + __get_user(regs->u_regs[UREG_G3], &sf->info.si_regs.u_regs[UREG_G3]); + __get_user(regs->u_regs[UREG_G4], &sf->info.si_regs.u_regs[UREG_G4]); + __get_user(regs->u_regs[UREG_G5], &sf->info.si_regs.u_regs[UREG_G5]); + __get_user(regs->u_regs[UREG_G6], &sf->info.si_regs.u_regs[UREG_G6]); + __get_user(regs->u_regs[UREG_G7], &sf->info.si_regs.u_regs[UREG_G7]); + __get_user(regs->u_regs[UREG_I0], &sf->info.si_regs.u_regs[UREG_I0]); + __get_user(regs->u_regs[UREG_I1], &sf->info.si_regs.u_regs[UREG_I1]); + __get_user(regs->u_regs[UREG_I2], &sf->info.si_regs.u_regs[UREG_I2]); + __get_user(regs->u_regs[UREG_I3], &sf->info.si_regs.u_regs[UREG_I3]); + __get_user(regs->u_regs[UREG_I4], &sf->info.si_regs.u_regs[UREG_I4]); + __get_user(regs->u_regs[UREG_I5], &sf->info.si_regs.u_regs[UREG_I5]); + __get_user(regs->u_regs[UREG_I6], &sf->info.si_regs.u_regs[UREG_I6]); + __get_user(regs->u_regs[UREG_I7], &sf->info.si_regs.u_regs[UREG_I7]); /* User can only change condition codes and FPU enabling in %tstate. */ regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF); regs->tstate |= psr_to_tstate_icc(psr); - if (psr & PSR_EF) regs->tstate |= TSTATE_PEF; + + if (psr & PSR_EF) + regs->tstate |= TSTATE_PEF; __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -193,11 +209,12 @@ asmlinkage void do_sigreturn32(struct pt_regs *regs) scptr = (struct sigcontext32 *) regs->u_regs[UREG_I0]; /* Check sanity of the user arg. */ if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext32)) || - (((unsigned long) scptr) & 3)) { + (((unsigned long) scptr) & 3)) goto segv; - } + __get_user(pc, &scptr->sigc_pc); __get_user(npc, &scptr->sigc_npc); + if((pc | npc) & 3) goto segv; /* Nice try. */ @@ -241,7 +258,6 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, int old_status = current->tss.sstk_info.cur_status; unsigned psr; int i; - u32 temp; synchronize_user_stack(); sframep = (struct signal_sframe32 *) regs->u_regs[UREG_FP]; @@ -285,7 +301,10 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, } else #endif + /* XXX Perhaps we need a copy_in_user()? -DaveM */ for (i = 0; i < 16; i++) { + u32 temp; + get_user (temp, (((u32 *)(regs->u_regs[UREG_FP]))+i)); put_user (temp, (((u32 *)sframep)+i)); } @@ -315,13 +334,17 @@ save_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu) { #ifdef __SMP__ if (current->flags & PF_USEDFPU) { - fpsave32(¤t->tss.float_regs[0], ¤t->tss.fsr); + fprs_write(FPRS_FEF); + fpsave32((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); regs->tstate &= ~(TSTATE_PEF); current->flags &= ~(PF_USEDFPU); } #else if (current == last_task_used_math) { - fpsave32((unsigned long *)¤t->tss.float_regs[0], ¤t->tss.fsr); + fprs_write(FPRS_FEF); + fpsave32((unsigned long *)¤t->tss.float_regs[0], + ¤t->tss.fsr); last_task_used_math = 0; regs->tstate &= ~(TSTATE_PEF); } @@ -338,7 +361,7 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, { struct new_signal_frame32 *sf; int sigframe_size; - u32 psr, tmp; + u32 psr; int i; /* 1. Make sure everything is clean */ @@ -349,12 +372,12 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, sf = (struct new_signal_frame32 *)(regs->u_regs[UREG_FP] - sigframe_size); - if (invalid_frame_pointer (sf, sigframe_size)){ + if (invalid_frame_pointer (sf, sigframe_size)) { lock_kernel (); do_exit(SIGILL); } - if (current->tss.w_saved != 0){ + if (current->tss.w_saved != 0) { printk ("%s[%d]: Invalid user stack frame for " "signal delivery.\n", current->comm, current->pid); lock_kernel (); @@ -378,7 +401,11 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, } __put_user(oldmask, &sf->info.si_mask); + + /* XXX Perhaps we need a copy_in_user()? -DaveM */ for (i = 0; i < sizeof(struct reg_window32)/4; i++) { + u32 tmp; + __get_user(tmp, (((u32 *)regs->u_regs[UREG_FP])+i)); __put_user(tmp, (((u32 *)sf)+i)); } diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index c54036de6d96..59815b7a81ea 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,7 +1,8 @@ -/* $Id: sys_sparc32.c,v 1.18 1997/05/27 06:28:08 davem Exp $ +/* $Id: sys_sparc32.c,v 1.26 1997/06/04 13:05:21 jj Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * * These routines maintain argument size conversion between 32bit and 64bit * environment. @@ -28,6 +29,7 @@ #include #include #include +#include #include #include @@ -42,112 +44,7 @@ */ #define A(x) ((unsigned long)x) -extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); -extern asmlinkage unsigned long sys_brk(unsigned long brk); -extern asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off); -extern asmlinkage int sys_bdflush(int func, long data); -extern asmlinkage int sys_uselib(const char * library); -extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg); -extern asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev); -extern asmlinkage int sys_mkdir(const char * pathname, int mode); -extern asmlinkage int sys_rmdir(const char * pathname); -extern asmlinkage int sys_unlink(const char * pathname); -extern asmlinkage int sys_symlink(const char * oldname, const char * newname); -extern asmlinkage int sys_link(const char * oldname, const char * newname); -extern asmlinkage int sys_rename(const char * oldname, const char * newname); -extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); -extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); -extern asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf); -extern asmlinkage int sys_truncate(const char * path, unsigned long length); -extern asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length); -extern asmlinkage int sys_utime(char * filename, struct utimbuf * times); -extern asmlinkage int sys_utimes(char * filename, struct timeval * utimes); -extern asmlinkage int sys_access(const char * filename, int mode); -extern asmlinkage int sys_chdir(const char * filename); -extern asmlinkage int sys_chroot(const char * filename); -extern asmlinkage int sys_chmod(const char * filename, mode_t mode); -extern asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group); -extern asmlinkage int sys_open(const char * filename,int flags,int mode); -extern asmlinkage int sys_creat(const char * pathname, int mode); -extern asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin); -extern asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, loff_t *result, unsigned int origin); -extern asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count); -extern asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count); -extern asmlinkage long sys_readv(unsigned long fd, const struct iovec * vector, unsigned long count); -extern asmlinkage long sys_writev(unsigned long fd, const struct iovec * vector, unsigned long count); -extern asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp); -extern asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout); -extern asmlinkage int sys_newstat(char * filename, struct stat * statbuf); -extern asmlinkage int sys_newlstat(char * filename, struct stat * statbuf); -extern asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf); -extern asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz); -extern asmlinkage int sys_sysfs(int option, ...); -extern asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf); -extern asmlinkage int sys_umount(char * name); -extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, unsigned long new_flags, void *data); -extern asmlinkage int sys_syslog(int type, char * bug, int count); -extern asmlinkage int sys_personality(unsigned long personality); -extern asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); -extern asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options); -extern asmlinkage int sys_sysinfo(struct sysinfo *info); -extern asmlinkage int sys_getitimer(int which, struct itimerval *value); -extern asmlinkage int sys_setitimer(int which, struct itimerval *value, struct itimerval *ovalue); -extern asmlinkage int sys_sched_setscheduler(pid_t pid, int policy, struct sched_param *param); -extern asmlinkage int sys_sched_setparam(pid_t pid, struct sched_param *param); -extern asmlinkage int sys_sched_getparam(pid_t pid, struct sched_param *param); -extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); -extern asmlinkage int sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); -extern asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset); -extern asmlinkage int sys_sigpending(sigset_t *set); -extern asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler); -extern asmlinkage int sys_reboot(int magic1, int magic2, int cmd, void * arg); -extern asmlinkage int sys_acct(const char *name); -extern asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); -extern asmlinkage long sys_times(struct tms * tbuf); -extern asmlinkage int sys_getgroups(int gidsetsize, gid_t *grouplist); -extern asmlinkage int sys_setgroups(int gidsetsize, gid_t *grouplist); -extern asmlinkage int sys_newuname(struct new_utsname * name); -extern asmlinkage int sys_olduname(struct oldold_utsname * name); -extern asmlinkage int sys_sethostname(char *name, int len); -extern asmlinkage int sys_gethostname(char *name, int len); -extern asmlinkage int sys_setdomainname(char *name, int len); -extern asmlinkage int sys_getrlimit(unsigned int resource, struct rlimit *rlim); -extern asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim); -extern asmlinkage int sys_getrusage(int who, struct rusage *ru); -extern asmlinkage int sys_time(int * tloc); -extern asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz); -extern asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz); -extern asmlinkage int sys_adjtimex(struct timex *txc_p); -extern asmlinkage int sys_msync(unsigned long start, size_t len, int flags); -extern asmlinkage int sys_mlock(unsigned long start, size_t len); -extern asmlinkage int sys_munlock(unsigned long start, size_t len); -extern asmlinkage int sys_munmap(unsigned long addr, size_t len); -extern asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot); -extern asmlinkage unsigned long sys_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags); -extern asmlinkage int sys_swapoff(const char * specialfile); -extern asmlinkage int sys_swapon(const char * specialfile, int swap_flags); -extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); -extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen); -extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); -extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len); -extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len); -extern asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags); -extern asmlinkage int sys_sendto(int fd, void * buff, size_t len, unsigned flags, struct sockaddr *addr, int addr_len); -extern asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags); -extern asmlinkage int sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags, struct sockaddr *addr, int *addr_len); -extern asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen); -extern asmlinkage int sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen); -extern asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned flags); -extern asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags); -extern asmlinkage int sys_socketcall(int call, unsigned long *args); -extern asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp); -extern asmlinkage int sys_listen(int fd, int backlog); -extern asmlinkage int sys_socket(int family, int type, int protocol); -extern asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2]); -extern asmlinkage int sys_shutdown(int fd, int how); - -/* - * In order to reduce some races, while at the same time doing additional +/* In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the * kernel data space before using them.. * @@ -168,8 +65,7 @@ static inline int do_getname32(u32 filename, char *page) return retval; } -/* - * This is a single page for faster getname. +/* This is a single page for faster getname. * If the page is available when entering getname, use it. * If the page is not available, call __get_free_page instead. * This works even though do_getname can block (think about it). @@ -209,6 +105,8 @@ int getname32(u32 filename, char **result) return retval; } +extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); + asmlinkage int sys32_ioperm(u32 from, u32 num, int on) { return sys_ioperm((unsigned long)from, (unsigned long)num, on); @@ -571,22 +469,56 @@ out: return err; } -asmlinkage unsigned long sys32_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off) +extern asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long off); + +asmlinkage unsigned long sys32_mmap(u32 addr, u32 len, u32 prot, + u32 flags, u32 fd, u32 off) { - return sys_mmap((unsigned long)addr, (unsigned long)len, (unsigned long)prot, (unsigned long)flags, + return sys_mmap((unsigned long)addr, (unsigned long)len, + (unsigned long)prot, (unsigned long)flags, (unsigned long)fd, (unsigned long)off); } +extern asmlinkage int sys_bdflush(int func, long data); + asmlinkage int sys32_bdflush(int func, s32 data) { return sys_bdflush(func, (long)data); } +extern asmlinkage int sys_uselib(const char * library); + asmlinkage int sys32_uselib(u32 library) { return sys_uselib((const char *)A(library)); } +static inline int get_flock(struct flock *kfl, struct flock32 *ufl) +{ + if(get_user(kfl->l_type, &ufl->l_type) || + __get_user(kfl->l_whence, &ufl->l_whence) || + __get_user(kfl->l_start, &ufl->l_start) || + __get_user(kfl->l_len, &ufl->l_len) || + __get_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +static inline int put_flock(struct flock *kfl, struct flock32 *ufl) +{ + if(__put_user(kfl->l_type, &ufl->l_type) || + __put_user(kfl->l_whence, &ufl->l_whence) || + __put_user(kfl->l_start, &ufl->l_start) || + __put_user(kfl->l_len, &ufl->l_len) || + __put_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg); + asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, u32 arg) { switch (cmd) { @@ -598,20 +530,12 @@ asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, u32 arg) unsigned long old_fs; long ret; - if (get_user (f.l_type, &(((struct flock32 *)A(arg))->l_type)) || - __get_user (f.l_whence, &(((struct flock32 *)A(arg))->l_whence)) || - __get_user (f.l_start, &(((struct flock32 *)A(arg))->l_start)) || - __get_user (f.l_len, &(((struct flock32 *)A(arg))->l_len)) || - __get_user (f.l_pid, &(((struct flock32 *)A(arg))->l_pid))) + if(get_flock(&f, (struct flock32 *)A(arg))) return -EFAULT; old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_fcntl(fd, cmd, (unsigned long)&f); set_fs (old_fs); - if (__put_user (f.l_type, &(((struct flock32 *)A(arg))->l_type)) || - __put_user (f.l_whence, &(((struct flock32 *)A(arg))->l_whence)) || - __put_user (f.l_start, &(((struct flock32 *)A(arg))->l_start)) || - __put_user (f.l_len, &(((struct flock32 *)A(arg))->l_len)) || - __put_user (f.l_pid, &(((struct flock32 *)A(arg))->l_pid))) + if(put_flock(&f, (struct flock32 *)A(arg))) return -EFAULT; return ret; } @@ -620,36 +544,50 @@ asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, u32 arg) } } +extern asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev); + asmlinkage int sys32_mknod(u32 filename, int mode, __kernel_dev_t32 dev) { return sys_mknod((const char *)A(filename), mode, dev); } +extern asmlinkage int sys_mkdir(const char * pathname, int mode); + asmlinkage int sys32_mkdir(u32 pathname, int mode) { return sys_mkdir((const char *)A(pathname), mode); } +extern asmlinkage int sys_rmdir(const char * pathname); + asmlinkage int sys32_rmdir(u32 pathname) { return sys_rmdir((const char *)A(pathname)); } +extern asmlinkage int sys_unlink(const char * pathname); + asmlinkage int sys32_unlink(u32 pathname) { return sys_unlink((const char *)A(pathname)); } +extern asmlinkage int sys_symlink(const char * oldname, const char * newname); + asmlinkage int sys32_symlink(u32 oldname, u32 newname) { return sys_symlink((const char *)A(oldname), (const char *)A(newname)); } +extern asmlinkage int sys_link(const char * oldname, const char * newname); + asmlinkage int sys32_link(u32 oldname, u32 newname) { return sys_link((const char *)A(oldname), (const char *)A(newname)); } +extern asmlinkage int sys_rename(const char * oldname, const char * newname); + asmlinkage int sys32_rename(u32 oldname, u32 newname) { return sys_rename((const char *)A(oldname), (const char *)A(newname)); @@ -666,12 +604,15 @@ struct dqblk32 { __kernel_time_t32 dqb_itime; }; +extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); + asmlinkage int sys32_quotactl(int cmd, u32 special, int id, u32 addr) { int cmds = cmd >> SUBCMDSHIFT; int err; struct dqblk d; unsigned long old_fs; + char *spec; switch (cmds) { case Q_GETQUOTA: @@ -679,57 +620,73 @@ asmlinkage int sys32_quotactl(int cmd, u32 special, int id, u32 addr) case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)A(addr), sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct dqblk32 *)A(addr), + sizeof (struct dqblk32))) return -EFAULT; d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; break; default: - return sys_quotactl(cmd, (const char *)A(special), id, (caddr_t)A(addr)); + return sys_quotactl(cmd, (const char *)A(special), + id, (caddr_t)A(addr)); } + err = getname32 (special, &spec); + if (err) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)A(special), id, (caddr_t)A(addr)); + err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)A(addr)); set_fs (old_fs); + putname32 (spec); if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; ((struct dqblk32 *)&d)->dqb_itime = i; ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)A(addr), &d, sizeof (struct dqblk32))) + if (copy_to_user ((struct dqblk32 *)A(addr), &d, + sizeof (struct dqblk32))) return -EFAULT; } return err; } -static int put_statfs (u32 buf, struct statfs *s) +static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) { - if (put_user (s->f_type, &(((struct statfs32 *)A(buf))->f_type)) || - __put_user (s->f_bsize, &(((struct statfs32 *)A(buf))->f_bsize)) || - __put_user (s->f_blocks, &(((struct statfs32 *)A(buf))->f_blocks)) || - __put_user (s->f_bfree, &(((struct statfs32 *)A(buf))->f_bfree)) || - __put_user (s->f_bavail, &(((struct statfs32 *)A(buf))->f_bavail)) || - __put_user (s->f_files, &(((struct statfs32 *)A(buf))->f_files)) || - __put_user (s->f_ffree, &(((struct statfs32 *)A(buf))->f_ffree)) || - __put_user (s->f_namelen, &(((struct statfs32 *)A(buf))->f_namelen)) || - __put_user (s->f_fsid.val[0], &(((struct statfs32 *)A(buf))->f_fsid.val[0])) || - __put_user (s->f_fsid.val[1], &(((struct statfs32 *)A(buf))->f_fsid.val[1]))) + if (put_user (kbuf->f_type, &ubuf->f_type) || + __put_user (kbuf->f_bsize, &ubuf->f_bsize) || + __put_user (kbuf->f_blocks, &ubuf->f_blocks) || + __put_user (kbuf->f_bfree, &ubuf->f_bfree) || + __put_user (kbuf->f_bavail, &ubuf->f_bavail) || + __put_user (kbuf->f_files, &ubuf->f_files) || + __put_user (kbuf->f_ffree, &ubuf->f_ffree) || + __put_user (kbuf->f_namelen, &ubuf->f_namelen) || + __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) || + __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1])) return -EFAULT; return 0; } +extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); + asmlinkage int sys32_statfs(u32 path, u32 buf) { int ret; struct statfs s; unsigned long old_fs = get_fs(); + char *pth; - set_fs (KERNEL_DS); - ret = sys_statfs((const char *)A(path), &s); - set_fs (old_fs); - if (put_statfs(buf, &s)) return -EFAULT; + ret = getname32 (path, &pth); + if (!ret) { + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)pth, &s); + set_fs (old_fs); + putname32 (pth); + if (put_statfs((struct statfs32 *)A(buf), &s)) + return -EFAULT; + } return ret; } +extern asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf); + asmlinkage int sys32_fstatfs(unsigned int fd, u32 buf) { int ret; @@ -739,20 +696,27 @@ asmlinkage int sys32_fstatfs(unsigned int fd, u32 buf) set_fs (KERNEL_DS); ret = sys_fstatfs(fd, &s); set_fs (old_fs); - if (put_statfs(buf, &s)) return -EFAULT; + if (put_statfs((struct statfs32 *)A(buf), &s)) + return -EFAULT; return ret; } +extern asmlinkage int sys_truncate(const char * path, unsigned long length); + asmlinkage int sys32_truncate(u32 path, u32 length) { return sys_truncate((const char *)A(path), (unsigned long)length); } +extern asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length); + asmlinkage int sys32_ftruncate(unsigned int fd, u32 length) { return sys_ftruncate(fd, (unsigned long)length); } +extern asmlinkage int sys_utime(char * filename, struct utimbuf * times); + asmlinkage int sys32_utime(u32 filename, u32 times) { struct utimbuf32 { __kernel_time_t32 actime, modtime; }; @@ -777,63 +741,91 @@ asmlinkage int sys32_utime(u32 filename, u32 times) return ret; } +extern asmlinkage int sys_utimes(char * filename, struct timeval * utimes); + asmlinkage int sys32_utimes(u32 filename, u32 utimes) { /* struct timeval is the same :)) */ return sys_utimes((char *)A(filename), (struct timeval *)A(utimes)); } +extern asmlinkage int sys_access(const char * filename, int mode); + asmlinkage int sys32_access(u32 filename, int mode) { return sys_access((const char *)A(filename), mode); } +extern asmlinkage int sys_chdir(const char * filename); + asmlinkage int sys32_chdir(u32 filename) { return sys_chdir((const char *)A(filename)); } +extern asmlinkage int sys_chroot(const char * filename); + asmlinkage int sys32_chroot(u32 filename) { return sys_chroot((const char *)A(filename)); } +extern asmlinkage int sys_chmod(const char * filename, mode_t mode); + asmlinkage int sys32_chmod(u32 filename, __kernel_mode_t32 mode) { return sys_chmod((const char *)A(filename), mode); } +extern asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group); + asmlinkage int sys32_chown(u32 filename, __kernel_uid_t32 user, __kernel_gid_t32 group) { return sys_chown((const char *)A(filename), user, group); } +extern asmlinkage int sys_open(const char * filename,int flags,int mode); + asmlinkage int sys32_open(u32 filename, int flags, int mode) { return sys_open((const char *)A(filename), flags, mode); } +extern asmlinkage int sys_creat(const char * pathname, int mode); + asmlinkage int sys32_creat(u32 pathname, int mode) { return sys_creat((const char *)A(pathname), mode); } +extern asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin); + asmlinkage long sys32_lseek(unsigned int fd, s32 offset, unsigned int origin) { return sys_lseek(fd, (off_t)offset, origin); } -asmlinkage int sys32_llseek(unsigned int fd, u32 offset_high, u32 offset_low, u32 result, unsigned int origin) +extern asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, + unsigned long offset_low, + loff_t *result, unsigned int origin); + +asmlinkage int sys32_llseek(unsigned int fd, u32 offset_high, + u32 offset_low, u32 result, unsigned int origin) { /* loff_t is the same :)) */ - return sys_llseek(fd, (unsigned long)offset_high, (unsigned long)offset_low, (loff_t *)A(result), origin); + return sys_llseek(fd, (unsigned long)offset_high, (unsigned long)offset_low, + (loff_t *)A(result), origin); } +extern asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count); + asmlinkage long sys32_read(unsigned int fd, u32 buf, u32 count) { return sys_read(fd, (char *)A(buf), (unsigned long)count); } +extern asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count); + asmlinkage long sys32_write(unsigned int fd, u32 buf, u32 count) { return sys_write(fd, (const char *)A(buf), (unsigned long)count); @@ -841,86 +833,146 @@ asmlinkage long sys32_write(unsigned int fd, u32 buf, u32 count) struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; -asmlinkage long sys32_readv(u32 fd, u32 vector, u32 count) +typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long); + +static long do_readv_writev32(int type, struct inode *inode, struct file *file, + const struct iovec32 *vector, u32 count) { - struct iovec *v; - struct iovec vf[UIO_FASTIOV]; - u32 i; - long ret; - unsigned long old_fs; - - if (!count) return 0; if (count > UIO_MAXIOV) return -EINVAL; - if (count <= UIO_FASTIOV) - v = vf; - else { - lock_kernel (); - v = kmalloc (count * sizeof (struct iovec), GFP_KERNEL); - if (!v) { - ret = -ENOMEM; - goto out; - } + unsigned long tot_len; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack, *ivp; + long retval, i; + IO_fn_t fn; + + /* First get the "struct iovec" from user memory and + * verify all the pointers + */ + if (!count) + return 0; + if(verify_area(VERIFY_READ, vector, sizeof(struct iovec32)*count)) + return -EFAULT; + if (count > UIO_MAXIOV) + return -EINVAL; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return -ENOMEM; } - for (i = 0; i < count; i++) { - if (__get_user ((unsigned long)(v[i].iov_base), &((((struct iovec32 *)A(vector))+i)->iov_base)) || - __get_user (v[i].iov_len, &((((struct iovec32 *)A(vector))+i)->iov_len))) { - ret = -EFAULT; - goto out; - } + + tot_len = 0; + i = count; + ivp = iov; + while(i > 0) { + u32 len; + u32 buf; + + __get_user(len, &vector->iov_len); + __get_user(buf, &vector->iov_base); + tot_len += len; + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t) len; + vector++; + ivp++; + i--; } - old_fs = get_fs(); - set_fs (KERNEL_DS); - ret = sys_readv((unsigned long)fd, v, (unsigned long)count); - set_fs (old_fs); -out: - if (count > UIO_FASTIOV) { - kfree (v); - unlock_kernel (); + + retval = locks_verify_area((type == VERIFY_READ) ? + FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, + inode, file, file->f_pos, tot_len); + if (retval) { + if (iov != iovstack) + kfree(iov); + return retval; } - return ret; -} -asmlinkage long sys32_writev(u32 fd, u32 vector, u32 count) -{ - struct iovec *v; - struct iovec vf[UIO_FASTIOV]; - u32 i; - long ret; - unsigned long old_fs; - - if (!count) return 0; if (count > UIO_MAXIOV) return -EINVAL; - if (count <= UIO_FASTIOV) - v = vf; - else { - lock_kernel (); - v = kmalloc (count * sizeof (struct iovec), GFP_KERNEL); - if (!v) { - ret = -ENOMEM; - goto out; - } + /* Then do the actual IO. Note that sockets need to be handled + * specially as they have atomicity guarantees and can handle + * iovec's natively + */ + if (inode->i_sock) { + int err; + err = sock_readv_writev(type, inode, file, iov, count, tot_len); + if (iov != iovstack) + kfree(iov); + return err; } - for (i = 0; i < count; i++) { - if (__get_user ((unsigned long)(v[i].iov_base), &((((struct iovec32 *)A(vector))+i)->iov_base)) || - __get_user (v[i].iov_len, &((((struct iovec32 *)A(vector))+i)->iov_len))) { - ret = -EFAULT; - goto out; - } + + if (!file->f_op) { + if (iov != iovstack) + kfree(iov); + return -EINVAL; } - old_fs = get_fs(); - set_fs (KERNEL_DS); - ret = sys_writev((unsigned long)fd, v, (unsigned long)count); - set_fs (old_fs); -out: - if (count > UIO_FASTIOV) { - kfree (v); - unlock_kernel (); + /* VERIFY_WRITE actually means a read, as we write to user space */ + fn = file->f_op->read; + if (type == VERIFY_READ) + fn = (IO_fn_t) file->f_op->write; + ivp = iov; + while (count > 0) { + void * base; + int len, nr; + + base = ivp->iov_base; + len = ivp->iov_len; + ivp++; + count--; + nr = fn(inode, file, base, len); + if (nr < 0) { + if (retval) + break; + retval = nr; + break; + } + retval += nr; + if (nr != len) + break; } - return ret; + if (iov != iovstack) + kfree(iov); + return retval; +} + +asmlinkage long sys32_readv(int fd, u32 vector, u32 count) +{ + struct file *file; + struct inode *inode; + long err = -EBADF; + + lock_kernel(); + if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + goto out; + if (!(file->f_mode & 1)) + goto out; + err = do_readv_writev32(VERIFY_WRITE, inode, file, + (struct iovec32 *)A(vector), count); +out: + unlock_kernel(); + return err; +} + +asmlinkage long sys32_writev(int fd, u32 vector, u32 count) +{ + int error = -EBADF; + struct file *file; + struct inode *inode; + + lock_kernel(); + if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + goto out; + if (!(file->f_mode & 2)) + goto out; + down(&inode->i_sem); + error = do_readv_writev32(VERIFY_READ, inode, file, + (struct iovec32 *)A(vector), count); + up(&inode->i_sem); +out: + unlock_kernel(); + return error; } /* readdir & getdents */ #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) +#define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) struct old_linux_dirent32 { u32 d_ino; @@ -934,7 +986,8 @@ struct readdir_callback32 { int count; }; -static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) +static int fillonedir(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino) { struct readdir_callback32 * buf = (struct readdir_callback32 *) __buf; struct old_linux_dirent32 * dirent; @@ -963,7 +1016,8 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count) error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; - error = verify_area(VERIFY_WRITE, (void *)A(dirent), sizeof(struct old_linux_dirent32)); + error = verify_area(VERIFY_WRITE, (void *)A(dirent), + sizeof(struct old_linux_dirent32)); if (error) goto out; buf.count = 0; @@ -1052,84 +1106,124 @@ out: /* end of readdir & getdents */ +extern asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, + fd_set *exp, struct timeval *tvp); + asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp) { struct timeval kern_tv, *ktvp; unsigned long old_fs; char *p; - u32 *q; + u32 *q, *Inp, *Outp, *Exp; int i, ret = -EINVAL, nn; - u32 *Inp, *Outp, *Exp; - if (n < 0 || n > PAGE_SIZE*2) return -EINVAL; + if (n < 0 || n > PAGE_SIZE*2) + return -EINVAL; + lock_kernel (); p = (char *)__get_free_page (GFP_KERNEL); - if (!p) goto out; + if (!p) + goto out; + q = (u32 *)p; - nn = (n + 8 * sizeof(unsigned long) - 1) / (8 * sizeof (unsigned long)); - Inp = (u32 *)A(inp); Outp = (u32 *)A(outp); Exp = (u32 *)A(exp); + Inp = (u32 *)A(inp); + Outp = (u32 *)A(outp); + Exp = (u32 *)A(exp); + ret = -EFAULT; - for (i = 0; i < ret; i++, Inp += 2, Outp += 2, Exp += 2, q += 2) { - if (__get_user (q[1], Inp) || - __get_user (q[0], Inp+1) || - __get_user (q[1+PAGE_SIZE/4], Outp) || - __get_user (q[PAGE_SIZE/4], Outp+1) || - __get_user (q[1+PAGE_SIZE/2], Exp) || - __get_user (q[PAGE_SIZE/2], Exp+1)) + + nn = (n + (8 * sizeof(unsigned long)) - 1) / (8 * sizeof (unsigned long)); + for (i = 0; i < nn; i++, Inp += 2, Outp += 2, Exp += 2, q += 2) { + if(inp && (__get_user (q[1], Inp) || __get_user (q[0], Inp+1))) + goto out; + if(outp && (__get_user (q[1+(PAGE_SIZE/4/sizeof(u32))], Outp) || + __get_user (q[(PAGE_SIZE/4/sizeof(u32))], Outp+1))) + goto out; + if(exp && (__get_user (q[1+(PAGE_SIZE/2/sizeof(u32))], Exp) || + __get_user (q[(PAGE_SIZE/2/sizeof(u32))], Exp+1))) goto out; } + ktvp = NULL; if(tvp) { if(copy_from_user(&kern_tv, (struct timeval *)A(tvp), sizeof(*ktvp))) goto out; ktvp = &kern_tv; } + old_fs = get_fs (); set_fs (KERNEL_DS); - ret = sys_select(n, (fd_set *)p, (fd_set *)(p + PAGE_SIZE/4), (fd_set *)(p + PAGE_SIZE/2), ktvp); + q = (u32 *) p; + ret = sys_select(n, + inp ? (fd_set *)&q[0] : (fd_set *)0, + outp ? (fd_set *)&q[PAGE_SIZE/4/sizeof(u32)] : (fd_set *)0, + exp ? (fd_set *)&q[PAGE_SIZE/2/sizeof(u32)] : (fd_set *)0, + ktvp); set_fs (old_fs); + + if(tvp && !(current->personality & STICKY_TIMEOUTS)) + copy_to_user((struct timeval *)A(tvp), &kern_tv, sizeof(*ktvp)); + q = (u32 *)p; - Inp = (u32 *)A(inp); Outp = (u32 *)A(outp); Exp = (u32 *)A(exp); - for (i = 0; i < ret; i++, Inp += 2, Outp += 2, Exp += 2, q += 2) { - if (__put_user (q[1], Inp) || - __put_user (q[0], Inp+1) || - __put_user (q[1+PAGE_SIZE/4], Outp) || - __put_user (q[PAGE_SIZE/4], Outp+1) || - __put_user (q[1+PAGE_SIZE/2], Exp) || - __put_user (q[PAGE_SIZE/2], Exp+1)) { + Inp = (u32 *)A(inp); + Outp = (u32 *)A(outp); + Exp = (u32 *)A(exp); + + if(ret < 0) + goto out; + + for (i = 0; + i < nn; + i++, Inp += 2, Outp += 2, Exp += 2, q += 2) { + if(inp && (__put_user (q[1], Inp) || __put_user (q[0], Inp+1))) { + ret = -EFAULT; + goto out; + } + if(outp && (__put_user (q[1+(PAGE_SIZE/4/sizeof(u32))], Outp) || + __put_user (q[(PAGE_SIZE/4/sizeof(u32))], Outp+1))) { + ret = -EFAULT; + goto out; + } + if(exp && (__put_user (q[1+(PAGE_SIZE/2/sizeof(u32))], Exp) || + __put_user (q[(PAGE_SIZE/2/sizeof(u32))], Exp+1))) { ret = -EFAULT; goto out; } } out: free_page ((unsigned long)p); + unlock_kernel(); return ret; } +extern asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout); + asmlinkage int sys32_poll(u32 ufds, unsigned int nfds, int timeout) { return sys_poll((struct pollfd *)A(ufds), nfds, timeout); } -static inline int putstat(u32 statbuf, struct stat *s) -{ - if (put_user (s->st_dev, &(((struct stat32 *)A(statbuf))->st_dev)) || - __put_user (s->st_ino, &(((struct stat32 *)A(statbuf))->st_ino)) || - __put_user (s->st_mode, &(((struct stat32 *)A(statbuf))->st_mode)) || - __put_user (s->st_nlink, &(((struct stat32 *)A(statbuf))->st_nlink)) || - __put_user (s->st_uid, &(((struct stat32 *)A(statbuf))->st_uid)) || - __put_user (s->st_gid, &(((struct stat32 *)A(statbuf))->st_gid)) || - __put_user (s->st_rdev, &(((struct stat32 *)A(statbuf))->st_rdev)) || - __put_user (s->st_size, &(((struct stat32 *)A(statbuf))->st_size)) || - __put_user (s->st_atime, &(((struct stat32 *)A(statbuf))->st_atime)) || - __put_user (s->st_mtime, &(((struct stat32 *)A(statbuf))->st_mtime)) || - __put_user (s->st_ctime, &(((struct stat32 *)A(statbuf))->st_ctime)) || - __put_user (s->st_blksize, &(((struct stat32 *)A(statbuf))->st_blksize)) || - __put_user (s->st_blocks, &(((struct stat32 *)A(statbuf))->st_blocks))) +static inline int putstat(struct stat32 *ubuf, struct stat *kbuf) +{ + if (put_user (kbuf->st_dev, &ubuf->st_dev) || + __put_user (kbuf->st_ino, &ubuf->st_ino) || + __put_user (kbuf->st_mode, &ubuf->st_mode) || + __put_user (kbuf->st_nlink, &ubuf->st_nlink) || + __put_user (kbuf->st_uid, &ubuf->st_uid) || + __put_user (kbuf->st_gid, &ubuf->st_gid) || + __put_user (kbuf->st_rdev, &ubuf->st_rdev) || + __put_user (kbuf->st_size, &ubuf->st_size) || + __put_user (kbuf->st_atime, &ubuf->st_atime) || + __put_user (kbuf->st_mtime, &ubuf->st_mtime) || + __put_user (kbuf->st_ctime, &ubuf->st_ctime) || + __put_user (kbuf->st_blksize, &ubuf->st_blksize) || + __put_user (kbuf->st_blocks, &ubuf->st_blocks)) return -EFAULT; return 0; } +extern asmlinkage int sys_newstat(char * filename, struct stat * statbuf); + asmlinkage int sys32_newstat(u32 filename, u32 statbuf) { int ret; @@ -1143,11 +1237,14 @@ asmlinkage int sys32_newstat(u32 filename, u32 statbuf) ret = sys_newstat(filenam, &s); set_fs (old_fs); putname32 (filenam); - if (putstat (statbuf, &s)) return -EFAULT; + if (putstat ((struct stat32 *)A(statbuf), &s)) + return -EFAULT; } return ret; } +extern asmlinkage int sys_newlstat(char * filename, struct stat * statbuf); + asmlinkage int sys32_newlstat(u32 filename, u32 statbuf) { int ret; @@ -1161,11 +1258,14 @@ asmlinkage int sys32_newlstat(u32 filename, u32 statbuf) ret = sys_newlstat(filenam, &s); set_fs (old_fs); putname32 (filenam); - if (putstat (statbuf, &s)) return -EFAULT; + if (putstat ((struct stat32 *)A(statbuf), &s)) + return -EFAULT; } return ret; } +extern asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf); + asmlinkage int sys32_newfstat(unsigned int fd, u32 statbuf) { int ret; @@ -1175,15 +1275,20 @@ asmlinkage int sys32_newfstat(unsigned int fd, u32 statbuf) set_fs (KERNEL_DS); ret = sys_newfstat(fd, &s); set_fs (old_fs); - if (putstat (statbuf, &s)) return -EFAULT; + if (putstat ((struct stat32 *)A(statbuf), &s)) + return -EFAULT; return ret; } +extern asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz); + asmlinkage int sys32_readlink(u32 path, u32 buf, int bufsiz) { return sys_readlink((const char *)A(path), (char *)A(buf), bufsiz); } +extern asmlinkage int sys_sysfs(int option, ...); + asmlinkage int sys32_sysfs(int option, ...) { va_list args; @@ -1207,28 +1312,39 @@ asmlinkage int sys32_sysfs(int option, ...) return ret; } +extern asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf); + asmlinkage int sys32_ustat(dev_t dev, u32 ubuf) { /* ustat is the same :)) */ return sys_ustat(dev, (struct ustat *)A(ubuf)); } +extern asmlinkage int sys_umount(char * name); + asmlinkage int sys32_umount(u32 name) { return sys_umount((char *)A(name)); } +extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void *data); + asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags, u32 data) { return sys_mount((char *)A(dev_name), (char *)A(dir_name), (char *)A(type), (unsigned long)new_flags, (void *)A(data)); } +extern asmlinkage int sys_syslog(int type, char * bug, int count); + asmlinkage int sys32_syslog(int type, u32 bug, int count) { return sys_syslog(type, (char *)A(bug), count); } +extern asmlinkage int sys_personality(unsigned long personality); + asmlinkage int sys32_personality(u32 personality) { return sys_personality((unsigned long)personality); @@ -1277,6 +1393,9 @@ static int put_rusage (u32 ru, struct rusage *r) return 0; } +extern asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, + int options, struct rusage * ru); + asmlinkage int sys32_wait4(__kernel_pid_t32 pid, u32 stat_addr, int options, u32 ru) { if (!ru) @@ -1284,16 +1403,21 @@ asmlinkage int sys32_wait4(__kernel_pid_t32 pid, u32 stat_addr, int options, u32 else { struct rusage r; int ret; + unsigned int status; unsigned long old_fs = get_fs(); set_fs (KERNEL_DS); - ret = sys_wait4(pid, (unsigned int *)A(stat_addr), options, &r); + ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r); set_fs (old_fs); if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, (unsigned int *)A(stat_addr))) + return -EFAULT; return ret; } } +extern asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options); + asmlinkage int sys32_waitpid(__kernel_pid_t32 pid, u32 stat_addr, int options) { return sys_waitpid(pid, (unsigned int *)A(stat_addr), options); @@ -1312,6 +1436,8 @@ struct sysinfo32 { char _f[22]; }; +extern asmlinkage int sys_sysinfo(struct sysinfo *info); + asmlinkage int sys32_sysinfo(u32 info) { struct sysinfo s; @@ -1336,28 +1462,41 @@ asmlinkage int sys32_sysinfo(u32 info) return ret; } +extern asmlinkage int sys_getitimer(int which, struct itimerval *value); + asmlinkage int sys32_getitimer(int which, u32 value) { /* itimerval is the same :)) */ return sys_getitimer(which, (struct itimerval *)A(value)); } +extern asmlinkage int sys_setitimer(int which, struct itimerval *value, + struct itimerval *ovalue); + asmlinkage int sys32_setitimer(int which, u32 value, u32 ovalue) { - return sys_setitimer(which, (struct itimerval *)A(value), (struct itimerval *)A(ovalue)); + return sys_setitimer(which, (struct itimerval *)A(value), + (struct itimerval *)A(ovalue)); } +extern asmlinkage int sys_sched_setscheduler(pid_t pid, int policy, + struct sched_param *param); + asmlinkage int sys32_sched_setscheduler(__kernel_pid_t32 pid, int policy, u32 param) { /* sched_param is the same :)) */ return sys_sched_setscheduler(pid, policy, (struct sched_param *)A(param)); } +extern asmlinkage int sys_sched_setparam(pid_t pid, struct sched_param *param); + asmlinkage int sys32_sched_setparam(__kernel_pid_t32 pid, u32 param) { return sys_sched_setparam(pid, (struct sched_param *)A(param)); } +extern asmlinkage int sys_sched_getparam(pid_t pid, struct sched_param *param); + asmlinkage int sys32_sched_getparam(__kernel_pid_t32 pid, u32 param) { return sys_sched_getparam(pid, (struct sched_param *)A(param)); @@ -1368,6 +1507,8 @@ struct timespec32 { s32 tv_nsec; }; +extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); + asmlinkage int sys32_sched_rr_get_interval(__kernel_pid_t32 pid, u32 interval) { struct timespec t; @@ -1383,6 +1524,8 @@ asmlinkage int sys32_sched_rr_get_interval(__kernel_pid_t32 pid, u32 interval) return ret; } +extern asmlinkage int sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); + asmlinkage int sys32_nanosleep(u32 rqtp, u32 rmtp) { struct timespec t; @@ -1403,6 +1546,8 @@ asmlinkage int sys32_nanosleep(u32 rqtp, u32 rmtp) return ret; } +extern asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset); + asmlinkage int sys32_sigprocmask(int how, u32 set, u32 oset) { sigset_t s; @@ -1417,6 +1562,8 @@ asmlinkage int sys32_sigprocmask(int how, u32 set, u32 oset) return ret; } +extern asmlinkage int sys_sigpending(sigset_t *set); + asmlinkage int sys32_sigpending(u32 set) { sigset_t s; @@ -1430,21 +1577,29 @@ asmlinkage int sys32_sigpending(u32 set) return ret; } +extern asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler); + asmlinkage unsigned long sys32_signal(int signum, u32 handler) { return sys_signal(signum, (__sighandler_t)A(handler)); } +extern asmlinkage int sys_reboot(int magic1, int magic2, int cmd, void * arg); + asmlinkage int sys32_reboot(int magic1, int magic2, int cmd, u32 arg) { return sys_reboot(magic1, magic2, cmd, (void *)A(arg)); } +extern asmlinkage int sys_acct(const char *name); + asmlinkage int sys32_acct(u32 name) { return sys_acct((const char *)A(name)); } +extern asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); + asmlinkage int sys32_getresuid(u32 ruid, u32 euid, u32 suid) { uid_t a, b, c; @@ -1468,6 +1623,8 @@ struct tms32 { __kernel_clock_t32 tms_cstime; }; +extern asmlinkage long sys_times(struct tms * tbuf); + asmlinkage long sys32_times(u32 tbuf) { struct tms t; @@ -1486,6 +1643,8 @@ asmlinkage long sys32_times(u32 tbuf) return ret; } +extern asmlinkage int sys_getgroups(int gidsetsize, gid_t *grouplist); + asmlinkage int sys32_getgroups(int gidsetsize, u32 grouplist) { gid_t gl[NGROUPS]; @@ -1502,6 +1661,8 @@ asmlinkage int sys32_getgroups(int gidsetsize, u32 grouplist) return ret; } +extern asmlinkage int sys_setgroups(int gidsetsize, gid_t *grouplist); + asmlinkage int sys32_setgroups(int gidsetsize, u32 grouplist) { gid_t gl[NGROUPS]; @@ -1519,27 +1680,37 @@ asmlinkage int sys32_setgroups(int gidsetsize, u32 grouplist) return ret; } +extern asmlinkage int sys_newuname(struct new_utsname * name); + asmlinkage int sys32_newuname(u32 name) { /* utsname is the same :)) */ return sys_newuname((struct new_utsname *)A(name)); } +extern asmlinkage int sys_olduname(struct oldold_utsname * name); + asmlinkage int sys32_olduname(u32 name) { return sys_olduname((struct oldold_utsname *)A(name)); } +extern asmlinkage int sys_sethostname(char *name, int len); + asmlinkage int sys32_sethostname(u32 name, int len) { return sys_sethostname((char *)A(name), len); } +extern asmlinkage int sys_gethostname(char *name, int len); + asmlinkage int sys32_gethostname(u32 name, int len) { return sys_gethostname((char *)A(name), len); } +extern asmlinkage int sys_setdomainname(char *name, int len); + asmlinkage int sys32_setdomainname(u32 name, int len) { return sys_setdomainname((char *)A(name), len); @@ -1550,6 +1721,8 @@ struct rlimit32 { s32 rlim_max; }; +extern asmlinkage int sys_getrlimit(unsigned int resource, struct rlimit *rlim); + asmlinkage int sys32_getrlimit(unsigned int resource, u32 rlim) { struct rlimit r; @@ -1566,6 +1739,8 @@ asmlinkage int sys32_getrlimit(unsigned int resource, u32 rlim) return ret; } +extern asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim); + asmlinkage int sys32_setrlimit(unsigned int resource, u32 rlim) { struct rlimit r; @@ -1582,6 +1757,8 @@ asmlinkage int sys32_setrlimit(unsigned int resource, u32 rlim) return ret; } +extern asmlinkage int sys_getrusage(int who, struct rusage *ru); + asmlinkage int sys32_getrusage(int who, u32 ru) { struct rusage r; @@ -1595,17 +1772,23 @@ asmlinkage int sys32_getrusage(int who, u32 ru) return ret; } +extern asmlinkage int sys_time(int * tloc); + asmlinkage int sys32_time(u32 tloc) { return sys_time((int *)A(tloc)); } +extern asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz); + asmlinkage int sys32_gettimeofday(u32 tv, u32 tz) { /* both timeval and timezone are ok :)) */ return sys_gettimeofday((struct timeval *)A(tv), (struct timezone *)A(tz)); } +extern asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz); + asmlinkage int sys32_settimeofday(u32 tv, u32 tz) { return sys_settimeofday((struct timeval *)A(tv), (struct timezone *)A(tz)); @@ -1636,6 +1819,8 @@ struct timex32 { int :32; int :32; int :32; int :32; }; +extern asmlinkage int sys_adjtimex(struct timex *txc_p); + asmlinkage int sys32_adjtimex(u32 txc_p) { struct timex t; @@ -1680,98 +1865,154 @@ asmlinkage int sys32_adjtimex(u32 txc_p) return ret; } +extern asmlinkage int sys_msync(unsigned long start, size_t len, int flags); + asmlinkage int sys32_msync(u32 start, __kernel_size_t32 len, int flags) { return sys_msync((unsigned long)start, (size_t)len, flags); } +extern asmlinkage int sys_mlock(unsigned long start, size_t len); + asmlinkage int sys32_mlock(u32 start, __kernel_size_t32 len) { return sys_mlock((unsigned long)start, (size_t)len); } +extern asmlinkage int sys_munlock(unsigned long start, size_t len); + asmlinkage int sys32_munlock(u32 start, __kernel_size_t32 len) { return sys_munlock((unsigned long)start, (size_t)len); } +extern asmlinkage unsigned long sys_brk(unsigned long brk); + asmlinkage unsigned long sparc32_brk(u32 brk) { return sys_brk((unsigned long)brk); } +extern asmlinkage int sys_munmap(unsigned long addr, size_t len); + asmlinkage int sys32_munmap(u32 addr, __kernel_size_t32 len) { return sys_munmap((unsigned long)addr, (size_t)len); } +extern asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot); + asmlinkage int sys32_mprotect(u32 start, __kernel_size_t32 len, u32 prot) { return sys_mprotect((unsigned long)start, (size_t)len, (unsigned long)prot); } +extern asmlinkage unsigned long sys_mremap(unsigned long addr, unsigned long old_len, + unsigned long new_len, unsigned long flags); + asmlinkage unsigned long sys32_mremap(u32 addr, u32 old_len, u32 new_len, u32 flags) { - return sys_mremap((unsigned long)addr, (unsigned long)old_len, (unsigned long)new_len, (unsigned long)flags); + return sys_mremap((unsigned long)addr, (unsigned long)old_len, + (unsigned long)new_len, (unsigned long)flags); } +extern asmlinkage int sys_swapoff(const char * specialfile); + asmlinkage int sys32_swapoff(u32 specialfile) { return sys_swapoff((const char *)A(specialfile)); } +extern asmlinkage int sys_swapon(const char * specialfile, int swap_flags); + asmlinkage int sys32_swapon(u32 specialfile, int swap_flags) { return sys_swapon((const char *)A(specialfile), swap_flags); } -asmlinkage int sys32_bind(int fd, u32 umyaddr, int addrlen) +extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); + +asmlinkage inline int sys32_bind(int fd, u32 umyaddr, int addrlen) { /* sockaddr is the same :)) */ return sys_bind(fd, (struct sockaddr *)A(umyaddr), addrlen); } -asmlinkage int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen) +extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, + int *upeer_addrlen); + +asmlinkage inline int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen) { - return sys_accept(fd, (struct sockaddr *)A(upeer_sockaddr), (int *)A(upeer_addrlen)); + return sys_accept(fd, (struct sockaddr *)A(upeer_sockaddr), + (int *)A(upeer_addrlen)); } -asmlinkage int sys32_connect(int fd, u32 uservaddr, int addrlen) +extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); + +asmlinkage inline int sys32_connect(int fd, u32 uservaddr, int addrlen) { return sys_connect(fd, (struct sockaddr *)A(uservaddr), addrlen); } +extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); + asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len) { - return sys_getsockname(fd, (struct sockaddr *)A(usockaddr), (int *)A(usockaddr_len)); + return sys_getsockname(fd, (struct sockaddr *)A(usockaddr), + (int *)A(usockaddr_len)); } +extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); + asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len) { - return sys_getpeername(fd, (struct sockaddr *)A(usockaddr), (int *)A(usockaddr_len)); + return sys_getpeername(fd, (struct sockaddr *)A(usockaddr), + (int *)A(usockaddr_len)); } -asmlinkage int sys32_send(int fd, u32 buff, __kernel_size_t32 len, unsigned flags) +extern asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags); + +asmlinkage inline int sys32_send(int fd, u32 buff, + __kernel_size_t32 len, unsigned flags) { return sys_send(fd, (void *)A(buff), (size_t)len, flags); } -asmlinkage int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len, unsigned flags, u32 addr, int addr_len) +extern asmlinkage int sys_sendto(int fd, void * buff, size_t len, unsigned flags, + struct sockaddr *addr, int addr_len); + +asmlinkage inline int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags, u32 addr, int addr_len) { - return sys_sendto(fd, (void *)A(buff), (size_t)len, flags, (struct sockaddr *)A(addr), addr_len); + return sys_sendto(fd, (void *)A(buff), (size_t)len, flags, + (struct sockaddr *)A(addr), addr_len); } -asmlinkage int sys32_recv(int fd, u32 ubuf, __kernel_size_t32 size, unsigned flags) +extern asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags); + +asmlinkage inline int sys32_recv(int fd, u32 ubuf, + __kernel_size_t32 size, unsigned flags) { return sys_recv(fd, (void *)A(ubuf), (size_t)size, flags); } -asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, unsigned flags, u32 addr, u32 addr_len) +extern asmlinkage int sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags, + struct sockaddr *addr, int *addr_len); + +asmlinkage inline int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags, u32 addr, u32 addr_len) { - return sys_recvfrom(fd, (void *)A(ubuf), (size_t)size, flags, (struct sockaddr *)A(addr), (int *)A(addr_len)); + return sys_recvfrom(fd, (void *)A(ubuf), (size_t)size, flags, + (struct sockaddr *)A(addr), (int *)A(addr_len)); } -asmlinkage int sys32_setsockopt(int fd, int level, int optname, u32 optval, int optlen) +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +asmlinkage inline int sys32_setsockopt(int fd, int level, int optname, + u32 optval, int optlen) { /* XXX handle ip_fw32->ip_fw conversion for IP firewalling and accounting. Do it using some macro in ip_sockglue.c @@ -1779,11 +2020,54 @@ asmlinkage int sys32_setsockopt(int fd, int level, int optname, u32 optval, int return sys_setsockopt(fd, level, optname, (char *)A(optval), optlen); } -asmlinkage int sys32_getsockopt(int fd, int level, int optname, u32 optval, u32 optlen) +extern asmlinkage int sys_getsockopt(int fd, int level, int optname, + char *optval, int *optlen); + +asmlinkage inline int sys32_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen) { return sys_getsockopt(fd, level, optname, (char *)A(optval), (int *)A(optlen)); } +/* XXX This really belongs in some header file... -DaveM */ +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - + 16 for IP, 16 for IPX, + 24 for IPv6, + about 80 for AX.25 */ + +/* XXX These as well... */ +extern __inline__ struct socket *socki_lookup(struct inode *inode) +{ + return &inode->u.socket_i; +} + +extern __inline__ struct socket *sockfd_lookup(int fd, int *err) +{ + struct file *file; + struct inode *inode; + + if (!(file = fget(fd))) + { + *err = -EBADF; + return NULL; + } + + inode = file->f_inode; + if (!inode || !inode->i_sock || !socki_lookup(inode)) + { + *err = -ENOTSOCK; + fput(file,inode); + return NULL; + } + + return socki_lookup(inode); +} + +extern __inline__ void sockfd_put(struct socket *sock) +{ + fput(sock->file,sock->inode); +} + struct msghdr32 { u32 msg_name; int msg_namelen; @@ -1801,207 +2085,270 @@ struct cmsghdr32 { unsigned char cmsg_data[0]; }; -asmlinkage int sys32_sendmsg(int fd, u32 msg, unsigned flags) +static inline int iov_from_user32_to_kern(struct iovec *kiov, + struct iovec32 *uiov32, + int niov) { - struct msghdr m; - int count; - struct iovec *v; - struct iovec vf[UIO_FASTIOV]; - u32 i, vector; - long ret; - unsigned long old_fs; - - if (get_user ((long)m.msg_name, &(((struct msghdr32 *)A(msg))->msg_name)) || - __get_user (m.msg_namelen, &(((struct msghdr32 *)A(msg))->msg_namelen)) || - __get_user (vector, &(((struct msghdr32 *)A(msg))->msg_iov)) || - __get_user (m.msg_iovlen, &(((struct msghdr32 *)A(msg))->msg_iovlen)) || - __get_user ((long)m.msg_control, &(((struct msghdr32 *)A(msg))->msg_control)) || - __get_user (m.msg_controllen, &(((struct msghdr32 *)A(msg))->msg_controllen)) || - __get_user (m.msg_flags, &(((struct msghdr32 *)A(msg))->msg_flags))) - return -EFAULT; - - count = m.msg_iovlen; - if (!count) return 0; if (count > UIO_MAXIOV) return -EINVAL; - if (count <= UIO_FASTIOV) - v = vf; - else { - lock_kernel (); - v = kmalloc (count * sizeof (struct iovec), GFP_KERNEL); - if (!v) { - ret = -ENOMEM; - goto out; + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; } + return tot_len; +} - for (i = 0; i < count; i++) { - if (__get_user ((unsigned long)(v[i].iov_base), &((((struct iovec32 *)A(vector))+i)->iov_base)) || - __get_user (v[i].iov_len, &((((struct iovec32 *)A(vector))+i)->iov_len))) { - ret = -EFAULT; - goto out; +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, + struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + + if(get_user(tmp1, &umsg->msg_name) || + get_user(tmp2, &umsg->msg_iov) || + get_user(tmp3, &umsg->msg_control)) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + if(get_user(kmsg->msg_namelen, &umsg->msg_namelen) || + get_user(kmsg->msg_controllen, &umsg->msg_controllen) || + get_user(kmsg->msg_flags, &umsg->msg_flags)) + return -EFAULT; + + return 0; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; } - - m.msg_iov = v; - if (m.msg_controllen) { - /* XXX Handle msg_control stuff... Or should that go into ip_sockglue.c etc.? */ - } - old_fs = get_fs(); - set_fs (KERNEL_DS); - ret = sys_sendmsg(fd, &m, flags); - set_fs (old_fs); -out: - if (count > UIO_FASTIOV) { - kfree (v); - unlock_kernel (); - } - return ret; + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; } -asmlinkage int sys32_recvmsg(int fd, u32 msg, unsigned int flags) +asmlinkage int sys32_sendmsg(int fd, u32 user_msg, unsigned user_flags) { - struct msghdr m; - int count; - struct iovec *v; - struct iovec vf[UIO_FASTIOV]; - u32 i, vector; - long ret; - unsigned long old_fs; - - if (get_user ((long)m.msg_name, &(((struct msghdr32 *)A(msg))->msg_name)) || - __get_user (m.msg_namelen, &(((struct msghdr32 *)A(msg))->msg_namelen)) || - __get_user (vector, &(((struct msghdr32 *)A(msg))->msg_iov)) || - __get_user (m.msg_iovlen, &(((struct msghdr32 *)A(msg))->msg_iovlen)) || - __get_user ((long)m.msg_control, &(((struct msghdr32 *)A(msg))->msg_control)) || - __get_user (m.msg_controllen, &(((struct msghdr32 *)A(msg))->msg_controllen)) || - __get_user (m.msg_flags, &(((struct msghdr32 *)A(msg))->msg_flags))) + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + struct msghdr kern_msg; + int err; + int total_len; + unsigned char *ctl_buf = ctl; + + if(msghdr_from_user32_to_kern(&kern_msg, (struct msghdr32 *)A(user_msg))) return -EFAULT; - - count = m.msg_iovlen; - if (!count) return 0; if (count > UIO_MAXIOV) return -EINVAL; - if (count <= UIO_FASTIOV) - v = vf; - else { - lock_kernel (); - v = kmalloc (count * sizeof (struct iovec), GFP_KERNEL); - if (!v) { - ret = -ENOMEM; - goto out; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + total_len = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if(total_len < 0) + return total_len; + if(kern_msg.msg_controllen) { + struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control; + unsigned long *kcmsg; + __kernel_size_t32 cmlen; + + if(kern_msg.msg_controllen > sizeof(ctl) && + kern_msg.msg_controllen <= 256) { + ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL); + if(!ctl_buf) { + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); + return -ENOBUFS; + } } - } - - for (i = 0; i < count; i++) { - if (__get_user ((unsigned long)(v[i].iov_base), &((((struct iovec32 *)A(vector))+i)->iov_base)) || - __get_user (v[i].iov_len, &((((struct iovec32 *)A(vector))+i)->iov_len))) { - ret = -EFAULT; - goto out; + __get_user(cmlen, &ucmsg->cmsg_len); + kcmsg = (unsigned long *) ctl_buf; + *kcmsg++ = (unsigned long)cmlen; + if(copy_from_user(kcmsg, &ucmsg->cmsg_level, + kern_msg.msg_controllen - sizeof(__kernel_size_t32))) { + if(ctl_buf != ctl) + kfree_s(ctl_buf, kern_msg.msg_controllen); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); + return -EFAULT; } + kern_msg.msg_control = ctl_buf; } - - m.msg_iov = v; + kern_msg.msg_flags = user_flags; - if (m.msg_controllen) { - /* XXX Handle msg_control stuff... Or should that go into ip_sockglue.c etc.? */ + lock_kernel(); + if(current->files->fd[fd]->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + if((sock = sockfd_lookup(fd, &err)) != NULL) { + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); } - old_fs = get_fs(); - set_fs (KERNEL_DS); - ret = sys_recvmsg(fd, &m, flags); - set_fs (old_fs); - if (ret >= 0) { - /* XXX Handle msg_control stuff... */ - if (put_user (m.msg_flags, &(((struct msghdr32 *)A(msg))->msg_flags)) || - __put_user (m.msg_controllen, &(((struct msghdr32 *)A(msg))->msg_controllen))) - return -EFAULT; + unlock_kernel(); + + if(ctl_buf != ctl) + kfree_s(ctl_buf, kern_msg.msg_controllen); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); + return err; +} + +asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + if(msghdr_from_user32_to_kern(&kern_msg, (struct msghdr32 *)A(user_msg))) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &((struct msghdr32 *)A(user_msg))->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if(err < 0) + return err; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + lock_kernel(); + if(current->files->fd[fd]->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + if((sock = sockfd_lookup(fd, &err)) != NULL) { + err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); + if(err >= 0) + len = err; + sockfd_put(sock); } -out: - if (count > UIO_FASTIOV) { - kfree (v); - unlock_kernel (); + unlock_kernel(); + + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(err >= 0) { + err = __put_user(kern_msg.msg_flags, + &((struct msghdr32 *)A(user_msg))->msg_flags); + if(!err) { + /* XXX Convert cmsg back into userspace 32-bit format... */ + err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr, + &((struct msghdr32 *)A(user_msg))->msg_controllen); + } } - return ret; + if(err < 0) + return err; + return len; } +/* Argument list sizes for sys_socketcall */ +#define AL(x) ((x) * sizeof(u32)) +static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), + AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; +#undef AL + +extern asmlinkage int sys_socket(int family, int type, int protocol); +extern asmlinkage int sys_socketpair(int family, int type, int protocol, + int usockvec[2]); +extern asmlinkage int sys_shutdown(int fd, int how); +extern asmlinkage int sys_listen(int fd, int backlog); + asmlinkage int sys32_socketcall(int call, u32 args) { - static unsigned char nargs[18]={0,3,3,3,2,3,3,3, - 4,4,4,6,6,2,5,5,3,3}; u32 a[6]; u32 a0,a1; - int err = -EINVAL; - int i; - lock_kernel(); - if(call<1||call>SYS_RECVMSG) - goto out; - err = -EFAULT; - - for (i = 0; i < nargs[call]; i++, args += sizeof (u32)) - if (get_user(a[i], (u32 *)A(args))) - goto out; - + if (callSYS_RECVMSG) + return -EINVAL; + if (copy_from_user(a, (u32 *)A(args), nargs[call])) + return -EFAULT; a0=a[0]; a1=a[1]; switch(call) { case SYS_SOCKET: - err = sys_socket(a0, a1, a[2]); - break; + return sys_socket(a0, a1, a[2]); case SYS_BIND: - err = sys32_bind(a0, a1, a[2]); - break; + return sys32_bind(a0, a1, a[2]); case SYS_CONNECT: - err = sys32_connect(a0, a1, a[2]); - break; + return sys32_connect(a0, a1, a[2]); case SYS_LISTEN: - err = sys_listen(a0, a1); - break; + return sys_listen(a0, a1); case SYS_ACCEPT: - err = sys32_accept(a0, a1, a[2]); - break; + return sys32_accept(a0, a1, a[2]); case SYS_GETSOCKNAME: - err = sys32_getsockname(a0, a1, a[2]); - break; + return sys32_getsockname(a0, a1, a[2]); case SYS_GETPEERNAME: - err = sys32_getpeername(a0, a1, a[2]); - break; + return sys32_getpeername(a0, a1, a[2]); case SYS_SOCKETPAIR: - err = sys_socketpair(a0, a1, a[2], (int *)A(a[3])); - break; + return sys_socketpair(a0, a1, a[2], (int *)A(a[3])); case SYS_SEND: - err = sys32_send(a0, a1, a[2], a[3]); - break; + return sys32_send(a0, a1, a[2], a[3]); case SYS_SENDTO: - err = sys32_sendto(a0, a1, a[2], a[3], a[4], a[5]); - break; + return sys32_sendto(a0, a1, a[2], a[3], a[4], a[5]); case SYS_RECV: - err = sys32_recv(a0, a1, a[2], a[3]); - break; + return sys32_recv(a0, a1, a[2], a[3]); case SYS_RECVFROM: - err = sys32_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); - break; + return sys32_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); case SYS_SHUTDOWN: - err = sys_shutdown(a0,a1); - break; + return sys_shutdown(a0,a1); case SYS_SETSOCKOPT: - err = sys32_setsockopt(a0, a1, a[2], a[3], a[4]); - break; + return sys32_setsockopt(a0, a1, a[2], a[3], a[4]); case SYS_GETSOCKOPT: - err = sys32_getsockopt(a0, a1, a[2], a[3], a[4]); - break; + return sys32_getsockopt(a0, a1, a[2], a[3], a[4]); case SYS_SENDMSG: - err = sys32_sendmsg(a0, a1, a[2]); - break; + return sys32_sendmsg(a0, a1, a[2]); case SYS_RECVMSG: - err = sys32_recvmsg(a0, a1, a[2]); - break; - default: - err = -EINVAL; - break; + return sys32_recvmsg(a0, a1, a[2]); } -out: - unlock_kernel(); - return err; + return -EINVAL; } extern void check_pending(int signum); @@ -2060,6 +2407,8 @@ out: return err; } +extern asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp); + asmlinkage int sys32_nfsservctl(int cmd, u32 argp, u32 resp) { /* XXX handle argp and resp args */ @@ -2204,6 +2553,12 @@ asmlinkage int sparc32_execve(struct pt_regs *regs) return error; } +/* Modules will be supported with 64bit modutils only */ +asmlinkage int sys32_no_modules(void) +{ + return -ENOSYS; +} + struct ncp_mount_data32 { int version; unsigned int ncp_fd; diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 02707186af0d..a74d0ffbdee0 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.11 1997/05/27 19:30:20 jj Exp $ +/* $Id: systbls.S,v 1.13 1997/06/04 13:05:29 jj Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -53,20 +53,20 @@ sys_call_table32: .xword sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall /*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents .xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys_nis_syscall +/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_no_modules .xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname -/*190*/ .xword sys_nis_syscall, sys32_personality, sys_prof, sys_break, sys_lock +/*190*/ .xword sys32_no_modules, sys32_personality, sys_prof, sys_break, sys_lock .xword sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask /*200*/ .xword sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir .xword sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall /*210*/ .xword sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo .xword sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex -/*220*/ .xword sys32_sigprocmask, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getpgid +/*220*/ .xword sys32_sigprocmask, sys32_no_modules, sys32_no_modules, sys32_no_modules, sys_getpgid .xword sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid /*230*/ .xword sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall .xword sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall /*240*/ .xword sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys_nanosleep + .xword sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys32_nanosleep /*250*/ .xword sys32_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl .xword sys_aplib, sys_nis_syscall diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 6f96408ad41f..824a3ddb4974 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,7 +1,7 @@ -/* $Id: traps.c,v 1.13 1997/05/27 19:30:08 jj Exp $ +/* $Id: traps.c,v 1.19 1997/06/05 06:22:49 davem Exp $ * arch/sparc/kernel/traps.c * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ @@ -23,6 +23,7 @@ #include #include #include +#include /* #define SYSCALL_TRACING */ /* #define VERBOSE_SYSCALL_TRACING */ @@ -122,7 +123,8 @@ void syscall_trace_entry(unsigned long g1, struct pt_regs *regs) int i; #endif - printk("SYS[%s:%d]: <%d> ", current->comm, current->pid, (int)g1); + printk("SYS[%s:%d]: PC(%016lx) <%3d> ", + current->comm, current->pid, regs->tpc, (int)g1); #ifdef VERBOSE_SYSCALL_TRACING sdp = NULL; for(i = 0; i < NUM_SDESC_ENTRIES; i++) @@ -151,7 +153,7 @@ void syscall_trace_entry(unsigned long g1, struct pt_regs *regs) unsigned long syscall_trace_exit(unsigned long retval, struct pt_regs *regs) { - printk("ret[%08x]\n", (unsigned int) retval); + printk("ret[%016lx]\n", retval); return retval; } #endif /* SYSCALL_TRACING */ @@ -254,25 +256,143 @@ void do_iae(struct pt_regs *regs) barrier(); } +static unsigned long init_fsr = 0x0UL; +static unsigned int init_fregs[64] __attribute__ ((aligned (64))) = + { ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, + ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, + ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, + ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, + ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, + ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, + ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, + ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U }; + void do_fpdis(struct pt_regs *regs) { - printk("FPDIS: at %016lx\n", regs->tpc); - while(1) - barrier(); + lock_kernel(); + + regs->tstate |= TSTATE_PEF; + fprs_write(FPRS_FEF); + + /* This is allowed now because the V9 ABI varargs passes floating + * point args in floating point registers, so vsprintf() and sprintf() + * cause problems. Luckily we never actually pass floating point values + * to those routines in the kernel and the code generated just does + * stores of them to the stack. Therefore, for the moment this fix + * is sufficient. -DaveM + */ + if(regs->tstate & TSTATE_PRIV) + goto out; + +#ifndef __SMP__ + if(last_task_used_math == current) + goto out; + if(last_task_used_math) { + struct task_struct *fptask = last_task_used_math; + + if(fptask->tss.flags & SPARC_FLAG_32BIT) + fpsave32((unsigned long *)&fptask->tss.float_regs[0], + &fptask->tss.fsr); + else + fpsave((unsigned long *)&fptask->tss.float_regs[0], + &fptask->tss.fsr); + } + last_task_used_math = current; + if(current->used_math) { + if(current->tss.flags & SPARC_FLAG_32BIT) + fpload32(¤t->tss.float_regs[0], + ¤t->tss.fsr); + else + fpload(¤t->tss.float_regs[0], + ¤t->tss.fsr); + } else { + /* Set inital sane state. */ + fpload(&init_fregs[0], &init_fsr); + current->used_math = 1; + } +#else + if(!current->used_math) { + fpload(&init_fregs[0], &init_fsr); + current->used_math = 1; + } else { + if(current->tss.flags & SPARC_FLAG_32BIT) + fpload32(¤t->tss.float_regs[0], + ¤t->tss.fsr); + else + fpload(¤t->tss.float_regs[0], + ¤t->tss.fsr); + } + current->flags |= PF_USEDFPU; +#endif +#ifndef __SMP__ +out: +#endif + unlock_kernel(); +} + +static unsigned long fake_regs[32] __attribute__ ((aligned (8))); +static unsigned long fake_fsr; + +void do_fpe_common(struct pt_regs *regs) +{ + static int calls = 0; +#ifndef __SMP__ + struct task_struct *fpt = last_task_used_math; +#else + struct task_struct *fpt = current; +#endif + + lock_kernel(); + fprs_write(FPRS_FEF); + +#ifndef __SMP__ + if(!fpt) { +#else + if(!(fpt->flags & PF_USEDFPU)) { +#endif + fpsave(&fake_regs[0], &fake_fsr); + regs->tstate &= ~(TSTATE_PEF); + goto out; + } + if(fpt->tss.flags & SPARC_FLAG_32BIT) + fpsave32((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr); + else + fpsave((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr); + fpt->tss.sig_address = regs->tpc; + fpt->tss.sig_desc = SUBSIG_FPERROR; +#ifdef __SMP__ + fpt->flags &= ~PF_USEDFPU; +#endif + if(regs->tstate & TSTATE_PRIV) { + printk("WARNING: FPU exception from kernel mode. at pc=%016lx\n", + regs->tpc); + regs->tpc = regs->tnpc; + regs->tnpc += 4; + calls++; + if(calls > 2) + die_if_kernel("Too many Penguin-FPU traps from kernel mode", + regs); + goto out; + } + send_sig(SIGFPE, fpt, 1); +#ifndef __SMP__ + last_task_used_math = NULL; +#endif + regs->tstate &= ~TSTATE_PEF; + if(calls > 0) + calls = 0; +out: + unlock_kernel(); } void do_fpieee(struct pt_regs *regs) { - printk("FPIEEE: at %016lx\n", regs->tpc); - while(1) - barrier(); + do_fpe_common(regs); } void do_fpother(struct pt_regs *regs) { - printk("FPOTHER: at %016lx\n", regs->tpc); - while(1) - barrier(); + do_fpe_common(regs); } void do_tof(struct pt_regs *regs) @@ -352,23 +472,29 @@ void do_illegal_instruction(struct pt_regs *regs) printk("Ill instr. at pc=%016lx ", pc); get_user(insn, ((unsigned int *)pc)); printk("insn=[%08x]\n", insn); + show_regs(regs); } #endif current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_ILLINST; send_sig(SIGILL, current, 1); unlock_kernel(); - - while(1) - barrier(); } -void do_mna(struct pt_regs *regs) +void mem_address_unaligned(struct pt_regs *regs) { printk("AIEEE: do_mna at %016lx\n", regs->tpc); show_regs(regs); - while(1) - barrier(); + if(regs->tstate & TSTATE_PRIV) { + printk("MNA from kernel, spinning\n"); + sti(); + while(1) + barrier(); + } else { + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_PRIVINST; + send_sig(SIGBUS, current, 1); + } } void do_privop(struct pt_regs *regs) diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 326382c3f64c..8db708f076dd 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.12 1997/05/17 08:22:30 davem Exp $ +/* $Id: ttable.S,v 1.13 1997/06/02 06:33:34 davem Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -30,7 +30,7 @@ tl0_dax: ACCESS_EXCEPTION_TRAP(data_access_exception) tl0_resv031: BTRAP(0x31) tl0_dae: TRAP(do_dae) tl0_resv033: BTRAP(0x33) -tl0_mna: TRAP(do_mna) +tl0_mna: TRAP_NOSAVE(do_mna) tl0_lddfmna: TRAP(do_lddfmna) tl0_stdfmna: TRAP(do_stdfmna) tl0_privact: TRAP(do_privact) @@ -163,7 +163,7 @@ tl1_dax: ACCESS_EXCEPTION_TRAPTL1(data_access_exception) tl1_resv031: BTRAPTL1(0x31) tl1_dae: TRAPTL1(do_dae_tl1) tl1_resv033: BTRAPTL1(0x33) -tl1_mna: TRAPTL1(do_mna_tl1) +tl1_mna: TRAP_NOSAVE(do_mna) tl1_lddfmna: TRAPTL1(do_lddfmna_tl1) tl1_stdfmna: TRAPTL1(do_stdfmna_tl1) tl1_privact: BTRAPTL1(0x37) diff --git a/arch/sparc64/kernel/winfixup.S b/arch/sparc64/kernel/winfixup.S index a8293c4534f1..2ac19a440260 100644 --- a/arch/sparc64/kernel/winfixup.S +++ b/arch/sparc64/kernel/winfixup.S @@ -1,4 +1,4 @@ -/* $Id: winfixup.S,v 1.3 1997/05/18 22:52:26 davem Exp $ +/* $Id: winfixup.S,v 1.8 1997/06/02 06:33:35 davem Exp $ * * winfixup.S: Handle cases where user stack pointer is found to be bogus. * @@ -10,6 +10,7 @@ #include #include #include +#include #include .text @@ -28,74 +29,223 @@ */ .globl winfix_trampoline, fill_fixup, spill_fixup fill_fixup: - ba,pt %xcc, etrap - rd %pc, %g7 - mov %l5, %o4 - mov %l4, %o5 - srlx %l5, PAGE_SHIFT, %o3 - clr %o1 - sllx %o3, PAGE_SHIFT, %o3 - and %l4, 0x4, %o2 - - call do_sparc64_fault - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ba,a,pt %xcc, rtrap + rdpr %tstate, %g1 + andcc %g1, TSTATE_PRIV, %g0 + be,pt %xcc, window_scheisse_from_user_common + and %g1, TSTATE_CWP, %g1 + + /* This is the extremely complex case, but it does happen from + * time to time if things are just right. Essentially the restore + * done in rtrap right before going back to user mode, with tl=1 + * and that levels trap stack registers all setup, took a fill trap, + * the user stack was not mapped in the tlb, and tlb miss occurred, + * the pte found was not valid, and a simple ref bit watch update + * could not satisfy the miss, so we got here. + * + * We must carefully unwind the state so we get back to tl=0, preserve + * all the register values we were going to give to the user. Luckily + * most things are where they need to be, we also have the address + * which triggered the fault handy as well. + * + * First, get into the window where the original restore was executed. + */ + + rdpr %wstate, %g2 ! Grab user mode wstate. + wrpr %g1, %cwp ! Get into the right window. + sll %g2, 3, %g2 ! NORMAL-->OTHER + wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. + + wrpr %g2, 0x0, %wstate ! This must be consistant. + wrpr %g0, 0x0, %otherwin ! We know this. + sethi %uhi(KERNBASE), %g2 ! Set this up + sllx %g2, 32, %g2 ! for the iflush + mov PRIMARY_CONTEXT, %g1 ! Change contexts... + stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus. + flush %g2 ! Flush instruction buffers + rdpr %pstate, %l1 ! Prepare to change globals. + mov %g4, %o5 ! Setup args for + mov %g5, %o4 ! final call to do_sparc64_fault. + + wrpr %g0, 0x0, %tl ! Out of trap levels. + wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate + sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg. + rd %pic, %g6 ! Get current as well. + b,pt %xcc, window_scheisse_merge ! And merge. + sllx %g4, 32, %g4 ! Finish med-any reg setup. + + /* Be very careful about usage of the alternate globals here. + * You cannot touch %g4/%g5 as that has the fault information + * should this be from usermode. Also be careful for the case + * where we get here from the save instruction in etrap.S when + * coming from either user or kernel (does not matter which, it + * is the same problem in both cases). Essentially this means + * do not touch %g7 or %g2 so we handle the two cases fine. + */ +spill_fixup: + rd %pic, %g1 + ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6 + andcc %g6, SPARC_FLAG_32BIT, %g0 + ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6 + sll %g6, 3, %g3 + add %g1, %g3, %g3 + stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] + sll %g6, 7, %g3 + + bne,pt %xcc, 1f + add %g1, %g3, %g3 + stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + stx %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + stx %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + stx %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + + stx %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + stx %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + stx %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x40] + stx %i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x48] + stx %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x50] + stx %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x58] + stx %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x60] + stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] + + stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] + stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] + b,pt %xcc, 2f + add %g6, 1, %g6 +1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + std %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + + std %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + add %g6, 1, %g6 +2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] + rdpr %tstate, %g1 + nop + + andcc %g1, TSTATE_PRIV, %g0 + saved + and %g1, TSTATE_CWP, %g1 + be,a,pn %xcc, window_scheisse_from_user_common + or %g4, 0x4, %g4 ! we know it was a write + retry +window_scheisse_from_user_common: nop + wrpr %g1, %cwp + + ba,pt %xcc, etrap + rd %pc, %g7 + mov %l5, %o4 + mov %l4, %o5 +window_scheisse_merge: + srlx %o4, PAGE_SHIFT, %o3 + clr %o1 + sllx %o3, PAGE_SHIFT, %o3 + and %o5, 0x4, %o2 + + call do_sparc64_fault + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 winfix_trampoline: - andn %g5, 0x7f, %g5 - add %g5, 0x7c, %g5 - wrpr %g5, %tnpc + andn %g3, 0x7f, %g3 + add %g3, 0x7c, %g3 + wrpr %g3, %tnpc done -spill_fixup: - rd %pic, %g1 - ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g2 - sll %g2, 3, %g5 - ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g7 - add %g1, %g5, %g5 - andcc %g7, SPARC_FLAG_32BIT, %g0 - stx %sp, [%g5 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] - sll %g2, 5, %g5 - - bne,pt %xcc, 1f - add %g1, %g5, %g5 - stx %l0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] - stx %l1, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] - stx %l2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] - stx %l3, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] - stx %l4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] - stx %l5, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] - - stx %l6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] - stx %l7, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] - stx %i0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x40] - stx %i1, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x48] - stx %i2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x50] - stx %i3, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x58] - stx %i4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x60] - stx %i5, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] - - stx %i6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] - stx %i7, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] - b,a,pt %xcc, 2f - add %g2, 1, %g2 -1: - std %l0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] - std %l2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] - std %l4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] - std %l6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] - - std %i0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] - std %i2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] - std %i4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] - std %i6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] - add %g2, 1, %g2 -2: - stx %g2, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] - rdpr %tstate, %g1 + .globl winfix_mna, fill_fixup_mna, spill_fixup_mna +winfix_mna: + andn %g3, 0x7f, %g3 + add %g3, 0x78, %g3 + wrpr %g3, %tnpc + done +fill_fixup_mna: + rdpr %tstate, %g1 + andcc %g1, TSTATE_PRIV, %g0 + be,pt %xcc, window_mna_from_user_common + and %g1, TSTATE_CWP, %g1 + rdpr %wstate, %g2 ! Grab user mode wstate. + wrpr %g1, %cwp ! Get into the right window. + sll %g2, 3, %g2 ! NORMAL-->OTHER + wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. + wrpr %g2, 0x0, %wstate ! This must be consistant. + wrpr %g0, 0x0, %otherwin ! We know this. + sethi %uhi(KERNBASE), %g2 ! Set this up + sllx %g2, 32, %g2 ! for the iflush + mov PRIMARY_CONTEXT, %g1 ! Change contexts... + stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus. + flush %g2 ! Flush instruction buffers + rdpr %pstate, %l1 ! Prepare to change globals. + mov %g4, %o5 ! Setup args for + mov %g5, %o4 ! final call to do_sparc64_fault. + wrpr %g0, 0x0, %tl ! Out of trap levels. + wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate + sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg. + rd %pic, %g6 ! Get current as well. + b,pt %xcc, window_mna_merge ! And merge. + sllx %g4, 32, %g4 ! Finish med-any reg setup. +spill_fixup_mna: + rd %pic, %g1 + ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6 + andcc %g6, SPARC_FLAG_32BIT, %g0 + ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6 + sll %g6, 3, %g3 + add %g1, %g3, %g3 + stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] + sll %g6, 7, %g3 + + bne,pt %xcc, 1f + add %g1, %g3, %g3 + stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + stx %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + stx %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + stx %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + + stx %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + stx %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + stx %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x40] + stx %i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x48] + stx %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x50] + stx %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x58] + stx %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x60] + stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] + + stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] + stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] + b,pt %xcc, 2f + add %g6, 1, %g6 +1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + std %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + + std %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + add %g6, 1, %g6 +2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] + rdpr %tstate, %g1 nop - andcc %g1, TSTATE_PRIV, %g0 - be,pn %xcc, fill_fixup - saved + andcc %g1, TSTATE_PRIV, %g0 + saved + be,pn %xcc, window_mna_from_user_common + and %g1, TSTATE_CWP, %g1 retry +window_mna_from_user_common: + wrpr %g1, %cwp + ba,pt %xcc, etrap + rd %pc, %g7 +window_mna_merge: + call mem_address_unaligned + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 + diff --git a/arch/sparc64/lib/checksum.S b/arch/sparc64/lib/checksum.S index b63f0d6e8dc8..10eebb8df335 100644 --- a/arch/sparc64/lib/checksum.S +++ b/arch/sparc64/lib/checksum.S @@ -71,8 +71,9 @@ csum_partial_end_cruft: or %o5, %o4, %o4 ! coalese with hword (if any) 6: addcc %o4, %o2, %o2 ! add to sum 1: sllx %g4, 32, %g4 ! give gfp back + addc %g0, %o2, %o0 ! add final carry into retval retl ! get outta here - addc %g0, %o2, %o0 ! add final carry into retval + srl %o0, 0, %o0 /* Also do alignment out of band to get better cache patterns. */ csum_partial_fix_alignment: @@ -82,7 +83,9 @@ csum_partial_fix_alignment: */ .globl csum_partial csum_partial: /* %o0=buf, %o1=len, %o2=sum */ + srl %o1, 0, %o1 ! doof scheiss andcc %o0, 0x7, %g0 ! alignment problems? + srl %o2, 0, %o2 be,pt %icc, csum_partial_fix_aligned ! yep, handle it andn %o1, 0x7f, %o3 ! num loop iterations cmp %o1, 6 @@ -154,31 +157,31 @@ __csum_partial_copy_start: 99: ba,pt %xcc, 30f; \ a, b, %o3; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 99b; \ + .align 8; \ + .xword 98b, 99b; \ .text; \ .align 4 #define EX2(x,y,z) \ 98: x,y; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 30f; \ + .align 8; \ + .xword 98b, 30f; \ .text; \ .align 4 #define EX3(x,y,z) \ 98: x,y; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 96f; \ + .align 8; \ + .xword 98b, 96f; \ .text; \ .align 4 #define EXT(start,end,handler,z) \ .section __ex_table,z##alloc; \ - .align 4; \ - .word start, 0, end, handler; \ + .align 8; \ + .xword start, 0, end, handler; \ .text; \ .align 4 @@ -189,12 +192,12 @@ __csum_partial_copy_start: * please check the fixup code below as well. */ #define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [src + off + 0x00], t0; \ - ldd [src + off + 0x08], t2; \ + ldda [src + off + 0x00] %asi, t0; \ + ldda [src + off + 0x08] %asi, t2; \ addccc t0, sum, sum; \ - ldd [src + off + 0x10], t4; \ + ldda [src + off + 0x10] %asi, t4; \ addccc t1, sum, sum; \ - ldd [src + off + 0x18], t6; \ + ldda [src + off + 0x18] %asi, t6; \ addccc t2, sum, sum; \ std t0, [dst + off + 0x00]; \ addccc t3, sum, sum; \ @@ -211,10 +214,10 @@ __csum_partial_copy_start: * Viking MXCC into streaming mode. Ho hum... */ #define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [src + off + 0x00], t0; \ - ldd [src + off + 0x08], t2; \ - ldd [src + off + 0x10], t4; \ - ldd [src + off + 0x18], t6; \ + ldda [src + off + 0x00] %asi, t0; \ + ldda [src + off + 0x08] %asi, t2; \ + ldda [src + off + 0x10] %asi, t4; \ + ldda [src + off + 0x18] %asi, t6; \ st t0, [dst + off + 0x00]; \ addccc t0, sum, sum; \ st t1, [dst + off + 0x04]; \ @@ -234,8 +237,8 @@ __csum_partial_copy_start: /* Yuck, 6 superscalar cycles... */ #define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3) \ - ldd [src - off - 0x08], t0; \ - ldd [src - off - 0x00], t2; \ + ldda [src - off - 0x08] %asi, t0; \ + ldda [src - off - 0x00] %asi, t2; \ addccc t0, sum, sum; \ st t0, [dst - off - 0x08]; \ addccc t1, sum, sum; \ @@ -250,7 +253,7 @@ cc_end_cruft: andcc %o3, 8, %g0 ! begin checks for that code be,pn %icc, 1f and %o3, 4, %g5 - EX(ldd [%o0 + 0x00], %g2, and %o3, 0xf,#) + EX(ldda [%o0 + 0x00] %asi, %g2, and %o3, 0xf,#) add %o1, 8, %o1 addcc %g2, %g7, %g7 add %o0, 8, %o0 @@ -260,7 +263,7 @@ cc_end_cruft: EX2(st %g3, [%o1 - 0x04],#) 1: brz,pt %g5, 1f andcc %o3, 3, %o3 - EX(ld [%o0 + 0x00], %g2, add %o3, 4,#) + EX(lda [%o0 + 0x00] %asi, %g2, add %o3, 4,#) add %o1, 4, %o1 addcc %g2, %g7, %g7 EX2(st %g2, [%o1 - 0x04],#) @@ -272,20 +275,21 @@ cc_end_cruft: subcc %o3, 2, %o3 ba,pt %xcc, 4f clr %o4 -2: EX(lduh [%o0 + 0x00], %o4, add %o3, 2,#) +2: EX(lduha [%o0 + 0x00] %asi, %o4, add %o3, 2,#) add %o0, 2, %o0 EX2(sth %o4, [%o1 + 0x00],#) be,pn %icc, 6f add %o1, 2, %o1 sll %o4, 16, %o4 -4: EX(ldub [%o0 + 0x00], %o5, add %g0, 1,#) +4: EX(lduba [%o0 + 0x00] %asi, %o5, add %g0, 1,#) EX2(stb %o5, [%o1 + 0x00],#) sll %o5, 8, %o5 or %o5, %o4, %o4 6: addcc %o4, %g7, %g7 1: sllx %g4, 32, %g4 + addc %g0, %g7, %o0 retl - addc %g0, %g7, %o0 + srl %o0, 0, %o0 /* 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 @@ -295,7 +299,9 @@ cc_end_cruft: .globl __csum_partial_copy_sparc_generic __csum_partial_copy_sparc_generic: /* %o0=src, %o1=dest, %g1=len, %g7=sum */ + srl %g7, 0, %g7 ! you neve know... xor %o0, %o1, %o4 ! get changing bits + srl %g1, 0, %g1 ! doof scheiss andcc %o4, 3, %g0 ! check for mismatched alignment bne,pn %icc, ccslow ! better this than unaligned/fixups andcc %o0, 7, %g0 ! need to align things? @@ -309,7 +315,7 @@ __csum_partial_copy_sparc_generic: andcc %o0, 0x2, %g0 be,pn %icc, 1f andcc %o0, 0x4, %g0 - EX(lduh [%o0 + 0x00], %g4, add %g1, 0,#) + EX(lduha [%o0 + 0x00] %asi, %g4, add %g1, 0,#) sub %g1, 2, %g1 EX2(sth %g4, [%o1 + 0x00],#) add %o0, 2, %o0 @@ -325,7 +331,7 @@ __csum_partial_copy_sparc_generic: or %g3, %g7, %g7 1: be,pt %icc, 3f andn %g1, 0x7f, %g2 - EX(ld [%o0 + 0x00], %g4, add %g1, 0,#) + EX(lda [%o0 + 0x00] %asi, %g4, add %g1, 0,#) sub %g1, 4, %g1 EX2(st %g4, [%o1 + 0x00],#) add %o0, 4, %o0 @@ -372,8 +378,9 @@ cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5) ccte: bne,pn %icc, cc_end_cruft ! something left, handle it out of band sethi %uhi(KERNBASE), %g4 ! restore gfp mov %g7, %o0 ! give em the computed checksum + sllx %g4, 32, %g4 ! finish gfp restoration retl ! return - sllx %g4, 32, %g4 ! finish gfp restoration + srl %o0, 0, %o0 ccdbl: CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) @@ -394,7 +401,7 @@ ccslow: mov 0, %g5 be,a,pt %icc, 1f srl %g1, 1, %o3 sub %g1, 1, %g1 - EX(ldub [%o0], %g5, add %g1, 1,#) + EX(lduba [%o0] %asi, %g5, add %g1, 1,#) add %o0, 1, %o0 EX2(stb %g5, [%o1],#) srl %g1, 1, %o3 @@ -404,7 +411,7 @@ ccslow: mov 0, %g5 andcc %o0, 2, %g0 be,a,pt %icc, 1f srl %o3, 1, %o3 - EX(lduh [%o0], %o4, add %g1, 0,#) + EX(lduha [%o0] %asi, %o4, add %g1, 0,#) sub %g1, 2, %g1 srl %o4, 8, %g2 sub %o3, 1, %o3 @@ -416,7 +423,7 @@ ccslow: mov 0, %g5 add %o1, 2, %o1 1: brz,a,pn %o3, 2f andcc %g1, 2, %g0 - EX3(ld [%o0], %o4,#) + EX3(lda [%o0] %asi, %o4,#) 5: srl %o4, 24, %g2 srl %o4, 16, %g3 EX2(stb %g2, [%o1],#) @@ -430,7 +437,7 @@ ccslow: mov 0, %g5 add %o1, 4, %o1 ! is worthy). Maybe some day - with the sll/srl subcc %o3, 1, %o3 ! tricks bne,a,pt %icc, 5b - EX3(ld [%o0], %o4,#) + EX3(lda [%o0] %asi, %o4,#) sll %g5, 16, %g2 srl %g5, 16, %g5 srl %g2, 16, %g2 @@ -438,7 +445,7 @@ ccslow: mov 0, %g5 add %g2, %g5, %g5 2: be,a,pt %icc, 3f andcc %g1, 1, %g0 - EX(lduh [%o0], %o4, and %g1, 3,#) + EX(lduha [%o0] %asi, %o4, and %g1, 3,#) andcc %g1, 1, %g0 srl %o4, 8, %g2 add %o0, 2, %o0 @@ -448,7 +455,7 @@ ccslow: mov 0, %g5 add %o1, 2, %o1 3: be,a,pt %icc, 1f sll %g5, 16, %o4 - EX(ldub [%o0], %g2, add %g0, 1,#) + EX(lduba [%o0] %asi, %g2, add %g0, 1,#) sll %g2, 8, %o4 EX2(stb %g2, [%o1],#) add %g5, %o4, %g5 @@ -463,8 +470,9 @@ ccslow: mov 0, %g5 sll %g2, 8, %g2 or %g2, %o4, %g5 4: addcc %g7, %g5, %g7 + addc %g0, %g7, %o0 retl - addc %g0, %g7, %o0 + srl %o0, 0, %o0 __csum_partial_copy_end: .section .fixup,#alloc,#execinstr diff --git a/arch/sparc64/lib/copy_from_user.S b/arch/sparc64/lib/copy_from_user.S index 50ec7bb3dc3f..196435aedfa7 100644 --- a/arch/sparc64/lib/copy_from_user.S +++ b/arch/sparc64/lib/copy_from_user.S @@ -27,8 +27,8 @@ retl; \ a, b, %o0; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 99b; \ + .align 8; \ + .xword 98b, 99b; \ .text; \ .align 4 @@ -41,23 +41,23 @@ retl; \ a, b, %o0; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 99b; \ + .align 8; \ + .xword 98b, 99b; \ .text; \ .align 4 #define EXO2(x,y,z) \ 98: x,##y; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 97f; \ + .align 8; \ + .xword 98b, 97f; \ .text; \ .align 4 #define EXT(start,end,handler,z) \ .section __ex_table,z##alloc; \ - .align 4; \ - .word start, 0, end, handler; \ + .align 8; \ + .xword start, 0, end, handler; \ .text; \ .align 4 diff --git a/arch/sparc64/lib/copy_to_user.S b/arch/sparc64/lib/copy_to_user.S index 733953743d55..cc6db141f4f3 100644 --- a/arch/sparc64/lib/copy_to_user.S +++ b/arch/sparc64/lib/copy_to_user.S @@ -27,8 +27,8 @@ retl; \ a, b, %o0; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 99b; \ + .align 8; \ + .xword 98b, 99b; \ .text; \ .align 4 @@ -41,23 +41,23 @@ retl; \ a, b, %o0; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 99b; \ + .align 8; \ + .xword 98b, 99b; \ .text; \ .align 4 #define EXO2(x,y,z) \ 98: x,##y; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 97f; \ + .align 8; \ + .xword 98b, 97f; \ .text; \ .align 4 #define EXT(start,end,handler,z) \ .section __ex_table,z##alloc; \ - .align 4; \ - .word start, 0, end, handler; \ + .align 8; \ + .xword start, 0, end, handler; \ .text; \ .align 4 diff --git a/arch/sparc64/lib/memset.S b/arch/sparc64/lib/memset.S index 55de4ea9d5ad..713c78ca8355 100644 --- a/arch/sparc64/lib/memset.S +++ b/arch/sparc64/lib/memset.S @@ -17,15 +17,15 @@ 99: ba,pt %xcc, 30f; \ a, b, %o0; \ .section __ex_table,z##alloc; \ - .align 4; \ - .word 98b, 99b; \ + .align 8; \ + .xword 98b, 99b; \ .text; \ .align 4 #define EXT(start,end,handler,z) \ .section __ex_table,z##alloc; \ - .align 4; \ - .word start, 0, end, handler; \ + .align 8; \ + .xword start, 0, end, handler; \ .text; \ .align 4 diff --git a/arch/sparc64/lib/strlen_user.S b/arch/sparc64/lib/strlen_user.S index 30beee3ffd34..4d57aed64348 100644 --- a/arch/sparc64/lib/strlen_user.S +++ b/arch/sparc64/lib/strlen_user.S @@ -92,10 +92,10 @@ __strlen_user: clr %o0 .section __ex_table,#alloc - .align 4 + .align 8 - .word 10b, 30b - .word 11b, 30b - .word 12b, 30b - .word 13b, 30b - .word 14b, 30b + .xword 10b, 30b + .xword 11b, 30b + .xword 12b, 30b + .xword 13b, 30b + .xword 14b, 30b diff --git a/arch/sparc64/lib/strncpy_from_user.S b/arch/sparc64/lib/strncpy_from_user.S index e0fb0f09b12e..7a5dc768faf8 100644 --- a/arch/sparc64/lib/strncpy_from_user.S +++ b/arch/sparc64/lib/strncpy_from_user.S @@ -49,6 +49,6 @@ __strncpy_from_user: mov -EFAULT, %o0 .section __ex_table,#alloc - .align 4 - .word 10b, 4b - .word 11b, 4b + .align 8 + .xword 10b, 4b + .xword 11b, 4b diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index e23e736a9227..6df923a4bf2f 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.9 1997/05/19 05:58:54 davem Exp $ +/* $Id: fault.c,v 1.11 1997/06/01 05:46:15 davem Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -135,6 +135,7 @@ asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, } /* #define FAULT_TRACER */ +/* #define FAULT_TRACER_VERBOSE */ asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write, unsigned long address, unsigned long tag, @@ -150,12 +151,23 @@ asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write static unsigned long last_addr = 0; static int rcnt = 0; - printk("FAULT(PC[%016lx],t[%d],w[%d],addr[%016lx])\n", +#ifdef FAULT_TRACER_VERBOSE + printk("FAULT(PC[%016lx],t[%d],w[%d],addr[%016lx])...", regs->tpc, text_fault, write, address); - if(address == last_addr && rcnt++ > 5) { - printk("Wheee lotsa bogus faults, something wrong, spinning\n"); - while(1) - barrier(); +#else + printk("F[%016lx:%016lx:w(%d)", regs->tpc, address, write); +#endif + if(address == last_addr) { + if(rcnt++ > 15) { + printk("Wheee lotsa bogus faults, something wrong, spinning\n"); + __asm__ __volatile__("flushw"); + printk("o7[%016lx] i7[%016lx]\n", + regs->u_regs[UREG_I7], + ((struct reg_window *)(regs->u_regs[UREG_FP]+STACK_BIAS))->ins[7]); + sti(); + while(1) + barrier(); + } } else rcnt = 0; last_addr = address; #endif @@ -205,6 +217,13 @@ bad_area: goto out; } if(from_user) { +#if 1 + unsigned long cpc; + __asm__ __volatile__("mov %%i7, %0" : "=r" (cpc)); + printk("[%s:%d] SIGSEGV pc[%016lx] addr[%016lx] w[%d] sfsr[%016lx] " + "caller[%016lx]\n", current->comm, current->pid, regs->tpc, + address, write, sfsr, cpc); +#endif tsk->tss.sig_address = address; tsk->tss.sig_desc = SUBSIG_NOMAPPING; send_sig(SIGSEGV, tsk, 1); @@ -213,4 +232,12 @@ bad_area: unhandled_fault (address, tsk, regs); out: unlock_kernel(); +#ifdef FAULT_TRACER +#ifdef FAULT_TRACER_VERBOSE + printk(" done\n"); +#else + printk("]"); +#endif +#endif } + diff --git a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds index f8ba23528528..d2d0cac34138 100644 --- a/arch/sparc64/vmlinux.lds +++ b/arch/sparc64/vmlinux.lds @@ -26,6 +26,7 @@ SECTIONS _edata = .; PROVIDE (edata = .); .fixup : { *(.fixup) } + . = ALIGN(16); __start___ex_table = .; __ex_table : { *(__ex_table) } __stop___ex_table = .; diff --git a/drivers/ap1000/ringbuf.c b/drivers/ap1000/ringbuf.c index b8bcbb541c6c..ee33a03d4cea 100644 --- a/drivers/ap1000/ringbuf.c +++ b/drivers/ap1000/ringbuf.c @@ -318,7 +318,6 @@ struct inode_operations proc_ringbuf_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ - NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 55b0722a2716..5fc88c4b0683 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -380,7 +380,7 @@ check_table: && (q->sector & 63) == 1 && (q->end_sector & 63) == 63) { unsigned int heads = q->end_head + 1; - if (heads == 32 || heads == 64 || heads == 128) { + if (heads == 32 || heads == 64 || heads == 128 || heads == 255) { (void) ide_xlate_1024(dev, heads, " [PTBL]"); break; @@ -672,6 +672,141 @@ rdb_done: } #endif /* CONFIG_AMIGA_PARTITION */ +#ifdef CONFIG_ATARI_PARTITION +#include + +/* ++guenther: this should be settable by the user ("make config")?. + */ +#define ICD_PARTS + +static int atari_partition (struct gendisk *hd, kdev_t dev, + unsigned long first_sector) +{ + int minor = current_minor, m_lim = current_minor + hd->max_p; + struct buffer_head *bh; + struct rootsector *rs; + struct partition_info *pi; + ulong extensect; +#ifdef ICD_PARTS + int part_fmt = 0; /* 0:unknown, 1:AHDI, 2:ICD/Supra */ +#endif + + bh = bread (dev, 0, get_ptable_blocksize(dev)); + if (!bh) + { + printk (" unable to read block 0\n"); + return -1; + } + + rs = (struct rootsector *) bh->b_data; + pi = &rs->part[0]; + printk (" AHDI"); + for (; pi < &rs->part[4] && minor < m_lim; minor++, pi++) + { + if (pi->flg & 1) + /* active partition */ + { + if (memcmp (pi->id, "XGM", 3) == 0) + /* extension partition */ + { + struct rootsector *xrs; + struct buffer_head *xbh; + ulong partsect; + +#ifdef ICD_PARTS + part_fmt = 1; +#endif + printk(" XGM<"); + partsect = extensect = pi->st; + while (1) + { + xbh = bread (dev, partsect / 2, 1024); + if (!xbh) + { + printk (" block %ld read failed\n", partsect); + brelse(bh); + return 0; + } + if (partsect & 1) + xrs = (struct rootsector *) &xbh->b_data[512]; + else + xrs = (struct rootsector *) &xbh->b_data[0]; + + /* ++roman: sanity check: bit 0 of flg field must be set */ + if (!(xrs->part[0].flg & 1)) { + printk( "\nFirst sub-partition in extended partition is not valid!\n" ); + break; + } + + add_partition(hd, minor, partsect + xrs->part[0].st, + xrs->part[0].siz); + + if (!(xrs->part[1].flg & 1)) { + /* end of linked partition list */ + brelse( xbh ); + break; + } + if (memcmp( xrs->part[1].id, "XGM", 3 ) != 0) { + printk( "\nID of extended partition is not XGM!\n" ); + brelse( xbh ); + break; + } + + partsect = xrs->part[1].st + extensect; + brelse (xbh); + minor++; + if (minor >= m_lim) { + printk( "\nMaximum number of partitions reached!\n" ); + break; + } + } + printk(" >"); + } + else + { + /* we don't care about other id's */ + add_partition (hd, minor, pi->st, pi->siz); + } + } + } +#ifdef ICD_PARTS + if ( part_fmt!=1 ) /* no extended partitions -> test ICD-format */ + { + pi = &rs->icdpart[0]; + /* sanity check: no ICD format if first partition invalid */ + if (memcmp (pi->id, "GEM", 3) == 0 || + memcmp (pi->id, "BGM", 3) == 0 || + memcmp (pi->id, "LNX", 3) == 0 || + memcmp (pi->id, "SWP", 3) == 0 || + memcmp (pi->id, "RAW", 3) == 0 ) + { + printk(" ICD<"); + for (; pi < &rs->icdpart[8] && minor < m_lim; minor++, pi++) + { + /* accept only GEM,BGM,RAW,LNX,SWP partitions */ + if (pi->flg & 1 && + (memcmp (pi->id, "GEM", 3) == 0 || + memcmp (pi->id, "BGM", 3) == 0 || + memcmp (pi->id, "LNX", 3) == 0 || + memcmp (pi->id, "SWP", 3) == 0 || + memcmp (pi->id, "RAW", 3) == 0) ) + { + part_fmt = 2; + add_partition (hd, minor, pi->st, pi->siz); + } + } + printk(" >"); + } + } +#endif + brelse (bh); + + printk ("\n"); + + return 1; +} +#endif /* CONFIG_ATARI_PARTITION */ + static void check_partition(struct gendisk *hd, kdev_t dev) { static int first_time = 1; @@ -708,6 +843,10 @@ static void check_partition(struct gendisk *hd, kdev_t dev) #ifdef CONFIG_AMIGA_PARTITION if(amiga_partition(hd, dev, first_sector)) return; +#endif +#ifdef CONFIG_ATARI_PARTITION + if(atari_partition(hd, dev, first_sector)) + return; #endif printk(" unknown partition table\n"); } diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 89759dd32ca9..1b293c5391fa 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide.c Version 6.02 Mar 11, 1997 + * linux/drivers/block/ide.c Version 6.03 June 4, 1997 * * Copyright (C) 1994-1997 Linus Torvalds & authors (see below) */ @@ -280,6 +280,7 @@ * support HDIO_GETGEO for floppies * Version 6.02 fix ide_ack_intr() call * check partition table on floppies + * Version 6.03 handle bad status bit sequencing in ide_wait_stat() * * Some additional driver compile-time options are in ide.h * @@ -1044,27 +1045,24 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou byte stat; unsigned long flags; -test: - udelay(1); /* spec allows drive 400ns to change "BUSY" */ - if (OK_STAT((stat = GET_STAT()), good, bad)) - return 0; /* fast exit for most frequent case */ - if (!(stat & BUSY_STAT)) { - ide_error(drive, "status error", stat); - return 1; - } - - save_flags(flags); - ide_sti(); - timeout += jiffies; - do { - if (!((stat = GET_STAT()) & BUSY_STAT)) { - restore_flags(flags); - goto test; + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ + if ((stat = GET_STAT()) & BUSY_STAT) { + save_flags(flags); + ide_sti(); + timeout += jiffies; + while ((stat = GET_STAT()) & BUSY_STAT) { + if (jiffies > timeout) { + restore_flags(flags); + ide_error(drive, "status timeout", stat); + return 1; + } } - } while (jiffies <= timeout); - - restore_flags(flags); - ide_error(drive, "status timeout", GET_STAT()); + restore_flags(flags); + } + udelay(1); /* allow status to settle, then read it again */ + if (OK_STAT((stat = GET_STAT()), good, bad)) + return 0; + ide_error(drive, "status error", stat); return 1; } diff --git a/drivers/block/ide.h b/drivers/block/ide.h index 2eb6eb13a589..fddbc455236c 100644 --- a/drivers/block/ide.h +++ b/drivers/block/ide.h @@ -134,7 +134,7 @@ typedef unsigned char byte; /* used everywhere */ #define BAD_W_STAT (BAD_R_STAT | WRERR_STAT) #define BAD_STAT (BAD_R_STAT | DRQ_STAT) #define DRIVE_READY (READY_STAT | SEEK_STAT) -#define DATA_READY (DRIVE_READY | DRQ_STAT) +#define DATA_READY (DRQ_STAT) /* * Some more useful definitions diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 212efece2cb8..53fef9b744f2 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -318,7 +318,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) } lo->lo_inode = inode; - lo->lo_inode->i_count++; + atomic_inc(&lo->lo_inode->i_count); lo->transfer = NULL; figure_loop_size(lo); MOD_INC_USE_COUNT; diff --git a/drivers/block/md.c b/drivers/block/md.c index 078e1e1ee09f..12cb6dcf066c 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -202,9 +202,10 @@ static int do_md_stop (int minor, struct inode *inode) { int i; - if (inode->i_count>1 || md_dev[minor].busy>1) /* ioctl : one open channel */ + if (atomic_read(&inode->i_count)>1 || md_dev[minor].busy>1) /* ioctl : one open channel */ { - printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, inode->i_count, md_dev[minor].busy); + printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, + atomic_read(&inode->i_count), md_dev[minor].busy); return -EBUSY; } diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 551ccba680bb..bffa1aace30d 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -44,6 +44,9 @@ endif ifneq ($(ARCH),m68k) L_OBJS += pc_keyb.o defkeymap.o endif +ifdef CONFIG_MAGIC_SYSRQ +L_OBJS += sysrq.o +endif endif ifeq ($(CONFIG_ATARI_DSP56K),y) diff --git a/drivers/char/atarimouse.c b/drivers/char/atarimouse.c index 18debb66b345..950cb1546059 100644 --- a/drivers/char/atarimouse.c +++ b/drivers/char/atarimouse.c @@ -176,7 +176,7 @@ __initfunc(int atari_mouse_init(void)) #define MIN_THRESHOLD 1 #define MAX_THRESHOLD 20 /* more seems not reasonable... */ -void atari_mouse_setup( char *str, int *ints ) +__initfunc(void atari_mouse_setup( char *str, int *ints )) { if (ints[0] < 1) { printk( "atari_mouse_setup: no arguments!\n" ); diff --git a/drivers/char/console.c b/drivers/char/console.c index e45e2d67d3c9..3c1e7f27634c 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -111,11 +111,11 @@ #include #include -#include "kbd_kern.h" -#include "vt_kern.h" -#include "consolemap.h" -#include "selection.h" -#include "console_struct.h" +#include +#include +#include +#include +#include #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c index 878ac0d72f7b..5b0e61feaf85 100644 --- a/drivers/char/consolemap.c +++ b/drivers/char/consolemap.c @@ -13,7 +13,7 @@ #include #include #include -#include "consolemap.h" +#include static unsigned short translations[][256] = { /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index ef97f72557f7..5928869e9480 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -488,7 +488,7 @@ static int dsp56k_open(struct inode *inode, struct file *file) return 0; } -static void dsp56k_release(struct inode *inode, struct file *file) +static int dsp56k_release(struct inode *inode, struct file *file) { int dev = MINOR(inode->i_rdev) & 0x0f; @@ -501,12 +501,13 @@ static void dsp56k_release(struct inode *inode, struct file *file) break; default: printk("DSP56k driver: Unknown minor device: %d\n", dev); - return; + return -ENXIO; } #ifdef MODULE MOD_DEC_USE_COUNT; -#endif /* MODULE */ +#endif + return 0; } static struct file_operations dsp56k_fops = { diff --git a/drivers/char/fbmem.c b/drivers/char/fbmem.c index b267f84d3125..7db3b5dba114 100644 --- a/drivers/char/fbmem.c +++ b/drivers/char/fbmem.c @@ -222,7 +222,7 @@ fb_mmap(struct inode *inode, struct file *file, struct vm_area_struct * vma) vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 0185c2bdaf1f..66523964a5a9 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -17,8 +17,11 @@ * Modified to provide 'generic' keyboard support by Hamish Macdonald * Merge with the m68k keyboard driver and split-off of the PC low-level * parts by Geert Uytterhoeven, May 1997 + * + * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) */ +#include #include #include #include @@ -30,9 +33,10 @@ #include #include -#include "kbd_kern.h" -#include "diacr.h" -#include "vt_kern.h" +#include +#include +#include +#include #define SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -82,7 +86,7 @@ static int dead_key_next = 0; * the variable must be global, or a new procedure must be created to * return the value. I chose the former way. */ -/*static*/ int shift_state = 0; +int shift_state = 0; static int npadch = -1; /* -1 or number assembled on pad */ static unsigned char diacr = 0; static char rep = 0; /* flag telling character repeat */ @@ -140,8 +144,14 @@ const int NR_TYPES = SIZE(max_vals); static void put_queue(int); static unsigned char handle_diacr(unsigned char); -/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ -struct pt_regs * pt_regs; +/* kbd_pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ +struct pt_regs * kbd_pt_regs; + +#ifdef CONFIG_MAGIC_SYSRQ +#define SYSRQ_KEY 0x54 +extern void handle_sysrq(int, struct pt_regs *, struct kbd_struct *, struct tty_struct *); +static int sysrq_pressed; +#endif /* * Many other routines do put_queue, but I think either @@ -223,6 +233,17 @@ void handle_scancode(unsigned char scancode) } else rep = test_and_set_bit(keycode, key_down); +#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ + if (keycode == SYSRQ_KEY) { + sysrq_pressed = !up_flag; + return; + } else if (sysrq_pressed) { + if (!up_flag) + handle_sysrq(keycode, kbd_pt_regs, kbd, tty); + return; + } +#endif + if (kbd->kbdmode == VC_MEDIUMRAW) { /* soon keycodes will require more than one byte */ put_queue(keycode + up_flag); @@ -346,8 +367,8 @@ static void caps_on(void) static void show_ptregs(void) { - if (pt_regs) - show_regs(pt_regs); + if (kbd_pt_regs) + show_regs(kbd_pt_regs); } static void hold(void) diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c index 17405c191b25..117dc3fe7933 100644 --- a/drivers/char/lp_m68k.c +++ b/drivers/char/lp_m68k.c @@ -462,7 +462,6 @@ static struct file_operations lp_fops = { EXPORT_SYMBOL(lp_table); EXPORT_SYMBOL(lp_irq); EXPORT_SYMBOL(lp_interrupt); -EXPORT_SYMBOL(lp_init); EXPORT_SYMBOL(register_parallel); EXPORT_SYMBOL(unregister_parallel); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 56d537fb3cfe..499132bf8940 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -35,6 +35,33 @@ void isdn_init(void); void pcwatchdog_init(void); #endif +static long do_write_mem(struct file * file, + void *p, unsigned long realp, + const char * buf, unsigned long count) +{ + unsigned long written; + + written = 0; +#if defined(__sparc__) || defined(__mc68000__) + /* we don't have page 0 mapped on sparc and m68k.. */ + if (realp < PAGE_SIZE) { + unsigned long sz = PAGE_SIZE-realp; + if (sz > count) sz = count; + /* Hmm. Do something? */ + buf+=sz; + p+=sz; + count-=sz; + written+=sz; + } +#endif + if (copy_from_user(p, buf, count) < 0) + return -EFAULT; + written += count; + file->f_pos += written; + return count; +} + + /* * This funcion reads the *physical* memory. The f_pos points directly to the * memory location. @@ -45,7 +72,7 @@ static long read_mem(struct inode * inode, struct file * file, unsigned long p = file->f_pos; unsigned long end_mem; unsigned long read; - + end_mem = __pa(high_memory); if (p >= end_mem) return 0; @@ -54,15 +81,22 @@ static long read_mem(struct inode * inode, struct file * file, read = 0; #if defined(__sparc__) || defined(__mc68000__) /* we don't have page 0 mapped on sparc and m68k.. */ - while (p < PAGE_SIZE && count > 0) { - put_user(0,buf); - buf++; - p++; - count--; - read++; + if (p < PAGE_SIZE) { + unsigned long sz = PAGE_SIZE-p; + if (sz > count) + sz = count; + if (sz > 0) { + if (clear_user(buf, sz)) + return -EFAULT; + buf += sz; + p += sz; + count -= sz; + read += sz; + } } #endif - copy_to_user(buf, __va(p), count); + if (copy_to_user(buf, __va(p), count) < 0) + return -EFAULT; read += count; file->f_pos += read; return read; @@ -73,35 +107,19 @@ static long write_mem(struct inode * inode, struct file * file, { unsigned long p = file->f_pos; unsigned long end_mem; - unsigned long written; end_mem = __pa(high_memory); if (p >= end_mem) return 0; if (count > end_mem - p) count = end_mem - p; - written = 0; -#if defined(__sparc__) || defined(__mc68000__) - /* we don't have page 0 mapped on sparc and m68k.. */ - while (p < PAGE_SIZE && count > 0) { - /* Hmm. Do something? */ - buf++; - p++; - count--; - written++; - } -#endif - copy_from_user(__va(p), buf, count); - written += count; - file->f_pos += written; - return count; + return do_write_mem(file,__va(p),p,buf,count); } static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_struct * vma) { unsigned long offset = vma->vm_offset; - if (offset & ~PAGE_MASK) return -ENXIO; #if defined(__i386__) @@ -117,7 +135,7 @@ static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_str if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } @@ -166,27 +184,12 @@ static long write_kmem(struct inode * inode, struct file * file, const char * buf, unsigned long count) { unsigned long p = file->f_pos; - unsigned long written; if (p >= (unsigned long) high_memory) return 0; if (count > (unsigned long) high_memory - p) count = (unsigned long) high_memory - p; - written = 0; -#if defined(__sparc__) || defined(__mc68000__) - /* we don't have page 0 mapped on sparc and m68k.. */ - while (p < PAGE_SIZE && count > 0) { - /* Hmm. Do something? */ - buf++; - p++; - count--; - written++; - } -#endif - copy_from_user((char *) p, buf, count); - written += count; - file->f_pos += written; - return count; + return do_write_mem(file,(void*)p,p,buf,count); } static long read_port(struct inode * inode, struct file * file, @@ -195,8 +198,11 @@ static long read_port(struct inode * inode, struct file * file, unsigned int i = file->f_pos; char * tmp = buf; + if (verify_area(VERIFY_WRITE,buf,count)) + return -EFAULT; while (count-- > 0 && i < 65536) { - put_user(inb(i),tmp); + if (__put_user(inb(i),tmp) < 0) + return -EFAULT; i++; tmp++; } @@ -210,9 +216,12 @@ static long write_port(struct inode * inode, struct file * file, unsigned int i = file->f_pos; const char * tmp = buf; + if (verify_area(VERIFY_READ,buf,count)) + return -EFAULT; while (count-- > 0 && i < 65536) { char c; - get_user(c, tmp); + if (__get_user(c, tmp)) + return -EFAULT; outb(c,i); i++; tmp++; diff --git a/drivers/char/misc.c b/drivers/char/misc.c index d632e4788631..ed06f63146b4 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -44,8 +44,8 @@ #include #endif -#include /* needed by selection.h */ -#include "selection.h" /* export its symbols */ +#include +#include #ifdef CONFIG_KERNELD #include #endif diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index 89516b0dea55..ecec83ce9c8f 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -1,20 +1,10 @@ /* * linux/drivers/char/pc_keyb.c * - * Written for linux by Johan Myreen as a translation from - * the assembly version by Linus (with diacriticals added) - * - * Some additional features added by Christoph Niemann (ChN), March 1993 - * - * Loadable keymaps by Risto Kankkunen, May 1993 - * - * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 - * Added decr/incr_console, dynamic keymaps, Unicode support, - * dynamic function/string keys, led setting, Sept 1994 - * `Sticky' modifier keys, 951006. - * 11-11-96: SAK should now work in the raw mode (Martin Mares) - * * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 + * See keyboard.c for the whole history. + * + * Major cleanup by Martin Mares, May 1997 */ #include @@ -24,46 +14,181 @@ #include #include #include +#include #include #include +#include +#include + +/* Some configuration switches are present in the include file... */ + +#include "pc_keyb.h" /* - * On non-x86 hardware we do a full keyboard controller - * initialization, in case the bootup software hasn't done - * it. On a x86, the BIOS will already have initialized the - * keyboard. + * In case we run on a non-x86 hardware we need to initialize both the keyboard + * controller and the keyboard. On a x86, the BIOS will already have initialized + * them. */ + #ifdef INIT_KBD -static int initialize_kbd(void); -#endif -#include -#include +__initfunc(static int kbd_wait_for_input(void)) +{ + int n; + int status, data; + + n = KBD_TIMEOUT; + do { + status = inb(KBD_STATUS_REG); + /* + * Wait for input data to become available. This bit will + * then be cleared by the following read of the DATA + * register. + */ + + if (!(status & KBD_STAT_OBF)) + continue; + + data = inb(KBD_DATA_REG); + + /* + * Check to see if a timeout error has occurred. This means + * that transmission was started but did not complete in the + * normal time cycle. PERR is set when a parity error occurred + * in the last transmission. + */ + if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) { + continue; + } + return (data & 0xff); + } while (--n); + return -1; /* timed-out if fell through to here... */ +} + +__initfunc(static void kbd_write(int address, int data)) +{ + int status; + + do { + status = inb(KBD_STATUS_REG); + } while (status & KBD_STAT_IBF); + outb(data, address); +} + +__initfunc(static char *initialize_kbd2(void)) +{ + /* Flush any pending input. */ + + while (kbd_wait_for_input() != -1) + ; + + /* + * Test the keyboard interface. + * This seems to be the only way to get it going. + * If the test is successful a x55 is placed in the input buffer. + */ + + kbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST); + if (kbd_wait_for_input() != 0x55) + return "Keyboard failed self test"; + + /* + * Perform a keyboard interface test. This causes the controller + * to test the keyboard clock and data lines. The results of the + * test are placed in the input buffer. + */ + + kbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST); + if (kbd_wait_for_input() != 0x00) + return "Keyboard interface failed self test"; -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ + /* Enable the keyboard by allowing the keyboard clock to run. */ + + kbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_ENABLE); + + /* + * Reset keyboard. If the read times out + * then the assumption is that no keyboard is + * plugged into the machine. + * This defaults the keyboard to scan-code set 2. + */ + + kbd_write(KBD_DATA_REG, KBD_CMD_RESET); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Keyboard reset failed, no ACK"; + if (kbd_wait_for_input() != KBD_REPLY_POR) + return "Keyboard reset failed, no POR"; + + /* + * Set keyboard controller mode. During this, the keyboard should be + * in the disabled state. + */ + + kbd_write(KBD_DATA_REG, KBD_CMD_DISABLE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Disable keyboard: no ACK"; + + kbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE); + kbd_write(KBD_DATA_REG, KBD_MODE_KBD_INT + | KBD_MODE_SYS + | KBD_MODE_DISABLE_MOUSE + | KBD_MODE_KCC); + + kbd_write(KBD_DATA_REG, KBD_CMD_ENABLE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Enable keyboard: no ACK"; + + /* + * Finally, set the typematic rate to maximum. + */ + + kbd_write(KBD_DATA_REG, KBD_CMD_SET_RATE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Set rate: no ACK"; + kbd_write(KBD_DATA_REG, 0x00); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Set rate: no ACK"; + + return NULL; +} + +__initfunc(static void initialize_kbd(void)) +{ + unsigned long flags; + char *msg; + + save_flags(flags); cli(); + msg = initialize_kbd2(); + restore_flags(flags); + + if (msg) + printk(KERN_WARNING "initialize_kbd: %s\n", msg); +} + +#endif /* INIT_KBD */ + +unsigned char kbd_read_mask = KBD_STAT_OBF; /* Modified by psaux.c */ /* used only by send_data - set by keyboard_interrupt */ static volatile unsigned char reply_expected = 0; static volatile unsigned char acknowledge = 0; static volatile unsigned char resend = 0; -/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ +/* + * Wait for keyboard controller input buffer is empty. + */ + static inline void kb_wait(void) { int i; - for (i=0; i<0x100000; i++) - if ((inb_p(0x64) & 0x02) == 0) + for (i=0; i + */ + +/* + * Configuration Switches + */ + +#define KBD_REPORT_ERR /* Report keyboard errors */ +#define KBD_REPORT_UNKN /* Report unknown scan codes */ +#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ +#define KBD_TIMEOUT 0x100000 /* Timeout for sending of commands */ + +/* + * Internal variables of the driver + */ + +extern unsigned char kbd_read_mask; +extern unsigned char aux_device_present; + +/* + * Keyboard Controller Registers + */ + +#define KBD_STATUS_REG 0x64 /* Status register (R) */ +#define KBD_CNTL_REG 0x64 /* Controller command register (W) */ +#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */ + +/* + * Keyboard Controller Commands + */ + +#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ +#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ +#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ +#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ +#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ +#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ +#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ +#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ +#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ +#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ +#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ + +/* + * Keyboard Commands + */ + +#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */ +#define KBD_CMD_RESET 0xFF /* Reset */ + +/* + * Keyboard Replies + */ + +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ + +/* + * Status Register Bits + */ + +#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ +#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ +#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ +#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ +#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ +#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ +#define KBD_STAT_PERR 0x80 /* Parity error */ + +#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF) + +/* + * Controller Mode Register Bits + */ + +#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generage IRQ1 */ +#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ +#define KBD_MODE_SYS 0x04 /* The system flag (?) */ +#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ +#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ +#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ +#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ +#define KBD_MODE_RFU 0x80 + +/* + * Mouse Commands + */ + +#define AUX_SET_RES 0xE8 /* Set resolution */ +#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ +#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ +#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ +#define AUX_SET_STREAM 0xEA /* Set stream mode */ +#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ +#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ +#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ +#define AUX_RESET 0xFF /* Reset aux device */ diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index 28239579c012..edf88356febb 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -28,6 +28,8 @@ * * Fixed keyboard lockups at open time * 3-Jul-96, 22-Aug-96 Roman Hodek + * + * Cleanup by Martin Mares, 01-Jun-97 (now uses the new PC kbd include) */ /* Uncomment the following line if your mouse needs initialization. */ @@ -54,66 +56,15 @@ #include -#define PSMOUSE_MINOR 1 /* minor device # for this mouse */ - -/* aux controller ports */ -#define AUX_INPUT_PORT 0x60 /* Aux device output buffer */ -#define AUX_OUTPUT_PORT 0x60 /* Aux device input buffer */ -#define AUX_COMMAND 0x64 /* Aux device command buffer */ -#define AUX_STATUS 0x64 /* Aux device status reg */ - -/* aux controller status bits */ -#define AUX_OBUF_FULL 0x21 /* output buffer (from device) full */ -#define AUX_IBUF_FULL 0x02 /* input buffer (to device) full */ - -/* aux controller commands */ -#define AUX_CMD_WRITE 0x60 /* value to write to controller */ -#define AUX_MAGIC_WRITE 0xd4 /* value to send aux device data */ +#include "pc_keyb.h" -#define AUX_INTS_ON 0x47 /* enable controller interrupts */ -#define AUX_INTS_OFF 0x65 /* disable controller interrupts */ - -#define AUX_DISABLE 0xa7 /* disable aux */ -#define AUX_ENABLE 0xa8 /* enable aux */ - -/* aux device commands */ -#define AUX_SET_RES 0xe8 /* set resolution */ -#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */ -#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */ -#define AUX_GET_SCALE 0xe9 /* get scaling factor */ -#define AUX_SET_STREAM 0xea /* set stream mode */ -#define AUX_SET_SAMPLE 0xf3 /* set sample rate */ -#define AUX_ENABLE_DEV 0xf4 /* enable aux device */ -#define AUX_DISABLE_DEV 0xf5 /* disable aux device */ -#define AUX_RESET 0xff /* reset aux device */ +/* + * Generic declarations for both PS2 and 82C710 + */ -#define MAX_RETRIES 60 /* some aux operations take long time*/ -#if defined(__alpha__) && !defined(CONFIG_PCI) -# define AUX_IRQ 9 /* Jensen is odd indeed */ -#else -# define AUX_IRQ 12 -#endif +#define PSMOUSE_MINOR 1 /* Minor device # for this mouse */ #define AUX_BUF_SIZE 2048 -/* 82C710 definitions */ - -#define QP_DATA 0x310 /* Data Port I/O Address */ -#define QP_STATUS 0x311 /* Status Port I/O Address */ - -#define QP_DEV_IDLE 0x01 /* Device Idle */ -#define QP_RX_FULL 0x02 /* Device Char received */ -#define QP_TX_IDLE 0x04 /* Device XMIT Idle */ -#define QP_RESET 0x08 /* Device Reset */ -#define QP_INTS_ON 0x10 /* Device Interrupt On */ -#define QP_ERROR_FLAG 0x20 /* Device Error */ -#define QP_CLEAR 0x40 /* Device Clear */ -#define QP_ENABLE 0x80 /* Device Enable */ - -#define QP_IRQ 12 - -extern unsigned char aux_device_present; -extern unsigned char kbd_read_mask; /* from keyboard.c */ - struct aux_queue { unsigned long head; unsigned long tail; @@ -126,20 +77,84 @@ static struct aux_queue *queue; static int aux_ready = 0; static int aux_count = 0; static int aux_present = 0; -static int poll_aux_status(void); -static int poll_aux_status_nosleep(void); -static int fasync_aux(struct inode *inode, struct file *filp, int on); -#ifdef CONFIG_82C710_MOUSE -static int qp_present = 0; -static int qp_count = 0; -static int qp_data = QP_DATA; -static int qp_status = QP_STATUS; +/* + * Shared subroutines + */ -static int poll_qp_status(void); -static int probe_qp(void); +static unsigned int get_from_queue(void) +{ + unsigned int result; + unsigned long flags; + + save_flags(flags); + cli(); + result = queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); + restore_flags(flags); + return result; +} + + +static inline int queue_empty(void) +{ + return queue->head == queue->tail; +} + +static int fasync_aux(struct inode *inode, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(inode, filp, on, &queue->fasync); + if (retval < 0) + return retval; + return 0; +} + +/* + * PS/2 Aux Device + */ + +#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) +#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) + +#define MAX_RETRIES 60 /* some aux operations take long time*/ +#if defined(__alpha__) && !defined(CONFIG_PCI) +# define AUX_IRQ 9 /* Jensen is odd indeed */ +#else +# define AUX_IRQ 12 #endif +/* + * Status polling + */ + +static int poll_aux_status(void) +{ + int retries=0; + + while ((inb(KBD_STATUS_REG) & (KBD_STAT_IBF | KBD_STAT_OBF)) && retries < MAX_RETRIES) { + if ((inb_p(KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) + inb_p(KBD_DATA_REG); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (5*HZ + 99) / 100; + schedule(); + retries++; + } + return !(retries==MAX_RETRIES); +} + +static int poll_aux_status_nosleep(void) +{ + int retries = 0; + + while ((inb(KBD_STATUS_REG) & (KBD_STAT_IBF | KBD_STAT_OBF)) && retries < 1000000) { + if ((inb_p(KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) + inb_p(KBD_DATA_REG); + retries++; + } + return !(retries == 1000000); +} /* * Write to aux device @@ -148,28 +163,32 @@ static int probe_qp(void); static void aux_write_dev(int val) { poll_aux_status(); - outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); /* write magic cookie */ + outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); /* Write magic cookie */ poll_aux_status(); - outb_p(val,AUX_OUTPUT_PORT); /* write data */ + outb_p(val, KBD_DATA_REG); /* Write data */ +} + +__initfunc(static void aux_write_dev_nosleep(int val)) +{ + poll_aux_status_nosleep(); + outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); + poll_aux_status_nosleep(); + outb_p(val, KBD_DATA_REG); } /* * Write to device & handle returned ack */ -#if defined INITIALIZE_DEVICE -static int aux_write_ack(int val) -{ - int retries = 0; - poll_aux_status_nosleep(); - outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); - poll_aux_status_nosleep(); - outb_p(val,AUX_OUTPUT_PORT); +#ifdef INITIALIZE_DEVICE +__initfunc(static int aux_write_ack(int val)) +{ + aux_write_dev_nosleep(val); poll_aux_status_nosleep(); - if ((inb(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL) + if ((inb(KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) { - return (inb(AUX_INPUT_PORT)); + return (inb(KBD_DATA_REG)); } return 0; } @@ -182,33 +201,11 @@ static int aux_write_ack(int val) static void aux_write_cmd(int val) { poll_aux_status(); - outb_p(AUX_CMD_WRITE,AUX_COMMAND); + outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG); poll_aux_status(); - outb_p(val,AUX_OUTPUT_PORT); + outb_p(val, KBD_DATA_REG); } - -static unsigned int get_from_queue(void) -{ - unsigned int result; - unsigned long flags; - - save_flags(flags); - cli(); - result = queue->buf[queue->tail]; - queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); - restore_flags(flags); - return result; -} - - -static inline int queue_empty(void) -{ - return queue->head == queue->tail; -} - - - /* * Interrupt from the auxiliary device: a character * is waiting in the keyboard/aux controller. @@ -219,33 +216,10 @@ static void aux_interrupt(int cpl, void *dev_id, struct pt_regs * regs) int head = queue->head; int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1); - if ((inb(AUX_STATUS) & AUX_OBUF_FULL) != AUX_OBUF_FULL) + if ((inb(KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF) return; - add_mouse_randomness(queue->buf[head] = inb(AUX_INPUT_PORT)); - if (head != maxhead) { - head++; - head &= AUX_BUF_SIZE-1; - } - queue->head = head; - aux_ready = 1; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); - wake_up_interruptible(&queue->proc_list); -} - -/* - * Interrupt handler for the 82C710 mouse port. A character - * is waiting in the 82C710. - */ - -#ifdef CONFIG_82C710_MOUSE -static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs) -{ - int head = queue->head; - int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1); - - add_mouse_randomness(queue->buf[head] = inb(qp_data)); + add_mouse_randomness(queue->buf[head] = inb(KBD_DATA_REG)); if (head != maxhead) { head++; head &= AUX_BUF_SIZE-1; @@ -256,8 +230,6 @@ static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs) kill_fasync(queue->fasync, SIGIO); wake_up_interruptible(&queue->proc_list); } -#endif - static int release_aux(struct inode * inode, struct file * file) { @@ -266,9 +238,9 @@ static int release_aux(struct inode * inode, struct file * file) return 0; /* disable kbd bh to avoid mixing of cmd bytes */ disable_bh(KEYBOARD_BH); - aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */ + aux_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ poll_aux_status(); - outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */ + outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */ poll_aux_status(); /* reenable kbd bh */ enable_bh(KEYBOARD_BH); @@ -281,36 +253,6 @@ static int release_aux(struct inode * inode, struct file * file) return 0; } -#ifdef CONFIG_82C710_MOUSE -static int release_qp(struct inode * inode, struct file * file) -{ - unsigned char status; - - fasync_aux(inode, file, 0); - if (!--qp_count) { - if (!poll_qp_status()) - printk("Warning: Mouse device busy in release_qp()\n"); - status = inb_p(qp_status); - outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status); - if (!poll_qp_status()) - printk("Warning: Mouse device busy in release_qp()\n"); - free_irq(QP_IRQ, NULL); - MOD_DEC_USE_COUNT; - } - return 0; -} -#endif - -static int fasync_aux(struct inode *inode, struct file *filp, int on) -{ - int retval; - - retval = fasync_helper(inode, filp, on, &queue->fasync); - if (retval < 0) - return retval; - return 0; -} - /* * Install interrupt handler. * Enable auxiliary device. @@ -339,9 +281,9 @@ static int open_aux(struct inode * inode, struct file * file) /* disable kbd bh to avoid mixing of cmd bytes */ disable_bh(KEYBOARD_BH); poll_aux_status(); - outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */ - aux_write_dev(AUX_ENABLE_DEV); /* enable aux device */ - aux_write_cmd(AUX_INTS_ON); /* enable controller ints */ + outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */ + aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ + aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */ poll_aux_status(); /* reenable kbd bh */ enable_bh(KEYBOARD_BH); @@ -350,7 +292,112 @@ static int open_aux(struct inode * inode, struct file * file) return 0; } +/* + * Write to the aux device. + */ + +static long write_aux(struct inode * inode, struct file * file, + const char * buffer, unsigned long count) +{ + int retval = 0; + + if (count) { + int written = 0; + + /* disable kbd bh to avoid mixing of cmd bytes */ + disable_bh(KEYBOARD_BH); + + do { + char c; + if (!poll_aux_status()) + break; + outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); + if (!poll_aux_status()) + break; + get_user(c, buffer++); + outb_p(c, KBD_DATA_REG); + written++; + } while (--count); + /* reenable kbd bh */ + enable_bh(KEYBOARD_BH); + retval = -EIO; + if (written) { + retval = written; + inode->i_mtime = CURRENT_TIME; + } + } + + return retval; +} + +/* + * 82C710 Interface + */ + #ifdef CONFIG_82C710_MOUSE + +#define QP_DATA 0x310 /* Data Port I/O Address */ +#define QP_STATUS 0x311 /* Status Port I/O Address */ + +#define QP_DEV_IDLE 0x01 /* Device Idle */ +#define QP_RX_FULL 0x02 /* Device Char received */ +#define QP_TX_IDLE 0x04 /* Device XMIT Idle */ +#define QP_RESET 0x08 /* Device Reset */ +#define QP_INTS_ON 0x10 /* Device Interrupt On */ +#define QP_ERROR_FLAG 0x20 /* Device Error */ +#define QP_CLEAR 0x40 /* Device Clear */ +#define QP_ENABLE 0x80 /* Device Enable */ + +#define QP_IRQ 12 + +static int qp_present = 0; +static int qp_count = 0; +static int qp_data = QP_DATA; +static int qp_status = QP_STATUS; + +static int poll_qp_status(void); +static int probe_qp(void); + +/* + * Interrupt handler for the 82C710 mouse port. A character + * is waiting in the 82C710. + */ + +static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs) +{ + int head = queue->head; + int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1); + + add_mouse_randomness(queue->buf[head] = inb(qp_data)); + if (head != maxhead) { + head++; + head &= AUX_BUF_SIZE-1; + } + queue->head = head; + aux_ready = 1; + if (queue->fasync) + kill_fasync(queue->fasync, SIGIO); + wake_up_interruptible(&queue->proc_list); +} + +static int release_qp(struct inode * inode, struct file * file) +{ + unsigned char status; + + fasync_aux(inode, file, 0); + if (!--qp_count) { + if (!poll_qp_status()) + printk("Warning: Mouse device busy in release_qp()\n"); + status = inb_p(qp_status); + outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status); + if (!poll_qp_status()) + printk("Warning: Mouse device busy in release_qp()\n"); + free_irq(QP_IRQ, NULL); + MOD_DEC_USE_COUNT; + } + return 0; +} + /* * Install interrupt handler. * Enable the device, enable interrupts. @@ -394,48 +441,7 @@ static int open_qp(struct inode * inode, struct file * file) MOD_INC_USE_COUNT; return 0; } -#endif - -/* - * Write to the aux device. - */ - -static long write_aux(struct inode * inode, struct file * file, - const char * buffer, unsigned long count) -{ - int retval = 0; - - if (count) { - int written = 0; - - /* disable kbd bh to avoid mixing of cmd bytes */ - disable_bh(KEYBOARD_BH); - - do { - char c; - if (!poll_aux_status()) - break; - outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); - if (!poll_aux_status()) - break; - get_user(c, buffer++); - outb_p(c, AUX_OUTPUT_PORT); - written++; - } while (--count); - /* reenable kbd bh */ - enable_bh(KEYBOARD_BH); - retval = -EIO; - if (written) { - retval = written; - inode->i_mtime = CURRENT_TIME; - } - } - - return retval; -} - -#ifdef CONFIG_82C710_MOUSE /* * Write to the 82C710 mouse device. */ @@ -455,8 +461,64 @@ static long write_qp(struct inode * inode, struct file * file, inode->i_mtime = CURRENT_TIME; return count; } + +/* + * Wait for device to send output char and flush any input char. + */ + +static int poll_qp_status(void) +{ + int retries=0; + + while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE)) + != (QP_DEV_IDLE|QP_TX_IDLE) + && retries < MAX_RETRIES) { + + if (inb_p(qp_status)&(QP_RX_FULL)) + inb_p(qp_data); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (5*HZ + 99) / 100; + schedule(); + retries++; + } + return !(retries==MAX_RETRIES); +} + +/* + * Function to read register in 82C710. + */ + +static inline unsigned char read_710(unsigned char index) +{ + outb_p(index, 0x390); /* Write index */ + return inb_p(0x391); /* Read the data */ +} + +/* + * See if we can find a 82C710 device. Read mouse address. + */ + +__initfunc(static int probe_qp(void)) +{ + outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ + outb_p(0xaa, 0x3fa); /* Inverse of 55 */ + outb_p(0x36, 0x3fa); /* Address the chip */ + outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ + outb_p(0x1b, 0x2fa); /* Inverse of e4 */ + if (read_710(0x0f) != 0xe4) /* Config address found? */ + return 0; /* No: no 82C710 here */ + qp_data = read_710(0x0d)*4; /* Get mouse I/O address */ + qp_status = qp_data+1; + outb_p(0x0f, 0x390); + outb_p(0x0f, 0x391); /* Close config mode */ + return 1; +} + #endif +/* + * Generic part continues... + */ /* * Put bytes from input queue to buffer. @@ -497,7 +559,6 @@ repeat: return 0; } - static unsigned int aux_poll(struct file *file, poll_table * wait) { poll_wait(&queue->proc_list, wait); @@ -506,7 +567,6 @@ static unsigned int aux_poll(struct file *file, poll_table * wait) return 0; } - struct file_operations psaux_fops = { NULL, /* seek */ read_aux, @@ -521,7 +581,6 @@ struct file_operations psaux_fops = { fasync_aux, }; - /* * Initialize driver. First check for a 82C710 chip; if found * forget about the Aux port and use the *_qp functions. @@ -548,7 +607,7 @@ __initfunc(int psaux_init(void)) printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n"); aux_present = 1; #ifdef CONFIG_VT - kbd_read_mask = AUX_OBUF_FULL; + kbd_read_mask = AUX_STAT_OBF; #endif } else { return -EIO; @@ -559,8 +618,8 @@ __initfunc(int psaux_init(void)) queue->head = queue->tail = 0; queue->proc_list = NULL; if (!qp_found) { -#if defined INITIALIZE_DEVICE - outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */ +#ifdef INITIALIZE_DEVICE + outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */ aux_write_ack(AUX_SET_SAMPLE); aux_write_ack(100); /* 100 samples/sec */ aux_write_ack(AUX_SET_RES); @@ -568,11 +627,8 @@ __initfunc(int psaux_init(void)) aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ poll_aux_status_nosleep(); #endif /* INITIALIZE_DEVICE */ - outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */ - poll_aux_status_nosleep(); - outb_p(AUX_CMD_WRITE,AUX_COMMAND); - poll_aux_status_nosleep(); /* Disable interrupts */ - outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); /* on the controller */ + outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */ + aux_write_dev_nosleep(AUX_INTS_OFF); /* Disable controller interrupts */ } return 0; } @@ -580,7 +636,7 @@ __initfunc(int psaux_init(void)) #ifdef MODULE int init_module(void) { - return psaux_init(); /*?? Bjorn */ + return psaux_init(); } void cleanup_module(void) @@ -589,84 +645,3 @@ void cleanup_module(void) kfree(queue); } #endif - -static int poll_aux_status(void) -{ - int retries=0; - - while ((inb(AUX_STATUS)&0x03) && retries < MAX_RETRIES) { - if ((inb_p(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL) - inb_p(AUX_INPUT_PORT); - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (5*HZ + 99) / 100; - schedule(); - retries++; - } - return !(retries==MAX_RETRIES); -} - -static int poll_aux_status_nosleep(void) -{ - int retries = 0; - - while ((inb(AUX_STATUS)&0x03) && retries < 1000000) { - if ((inb_p(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL) - inb_p(AUX_INPUT_PORT); - retries++; - } - return !(retries == 1000000); -} - -#ifdef CONFIG_82C710_MOUSE -/* - * Wait for device to send output char and flush any input char. - */ - -static int poll_qp_status(void) -{ - int retries=0; - - while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE)) - != (QP_DEV_IDLE|QP_TX_IDLE) - && retries < MAX_RETRIES) { - - if (inb_p(qp_status)&(QP_RX_FULL)) - inb_p(qp_data); - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (5*HZ + 99) / 100; - schedule(); - retries++; - } - return !(retries==MAX_RETRIES); -} - -/* - * Function to read register in 82C710. - */ - -static inline unsigned char read_710(unsigned char index) -{ - outb_p(index, 0x390); /* Write index */ - return inb_p(0x391); /* Read the data */ -} - -/* - * See if we can find a 82C710 device. Read mouse address. - */ - -__initfunc(static int probe_qp(void)) -{ - outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ - outb_p(0xaa, 0x3fa); /* Inverse of 55 */ - outb_p(0x36, 0x3fa); /* Address the chip */ - outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ - outb_p(0x1b, 0x2fa); /* Inverse of e4 */ - if (read_710(0x0f) != 0xe4) /* Config address found? */ - return 0; /* No: no 82C710 here */ - qp_data = read_710(0x0d)*4; /* Get mouse I/O address */ - qp_status = qp_data+1; - outb_p(0x0f, 0x390); - outb_p(0x0f, 0x391); /* Close config mode */ - return 1; -} -#endif diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 3235ebeaa7a3..a8614999d3a8 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -43,6 +43,7 @@ * this driver.) */ +#include #include #include #include @@ -156,10 +157,6 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, if (count < sizeof(unsigned long)) return -EINVAL; - retval = verify_area(VERIFY_WRITE, buf, sizeof(unsigned long)); - if (retval) - return retval; - add_wait_queue(&rtc_wait, &wait); current->state = TASK_INTERRUPTIBLE; @@ -183,8 +180,7 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, data = rtc_irq_data; rtc_irq_data = 0; restore_flags(flags); - copy_to_user(buf, &data, sizeof(unsigned long)); - retval = sizeof(unsigned long); + retval = put_user(data, (unsigned long *)buf)) ?: sizeof(unsigned long); } current->state = TASK_RUNNING; @@ -198,6 +194,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, { unsigned long flags; + struct rtc_time wtime; switch (cmd) { case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ @@ -254,18 +251,9 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, * means "don't care" or "match all". Only the tm_hour, * tm_min, and tm_sec values are filled in. */ - int retval; - struct rtc_time alm_tm; - - retval = verify_area(VERIFY_WRITE, (struct rtc_time*)arg, sizeof(struct rtc_time)); - if (retval != 0 ) - return retval; - - get_rtc_alm_time(&alm_tm); - copy_to_user((struct rtc_time*)arg, &alm_tm, sizeof(struct rtc_time)); - - return 0; + get_rtc_alm_time(&wtime); + break; } case RTC_ALM_SET: /* Store a time into the alarm */ { @@ -278,11 +266,8 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned char hrs, min, sec; struct rtc_time alm_tm; - retval = verify_area(VERIFY_READ, (struct rtc_time*)arg, sizeof(struct rtc_time)); - if (retval != 0 ) - return retval; - - copy_from_user(&alm_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)); + if (copy_from_user(&alm_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) + return -EFAULT; hrs = alm_tm.tm_hour; min = alm_tm.tm_min; @@ -315,16 +300,8 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, } case RTC_RD_TIME: /* Read the time/date from RTC */ { - int retval; - struct rtc_time rtc_tm; - - retval = verify_area(VERIFY_WRITE, (struct rtc_time*)arg, sizeof(struct rtc_time)); - if (retval !=0 ) - return retval; - - get_rtc_time(&rtc_tm); - copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time)); - return 0; + get_rtc_time(&wtime); + break; } case RTC_SET_TIME: /* Set the RTC */ { @@ -338,11 +315,8 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (!suser()) return -EACCES; - retval = verify_area(VERIFY_READ, (struct rtc_time*)arg, sizeof(struct rtc_time)); - if (retval !=0 ) - return retval; - - copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)); + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) + return -EFAULT; yrs = rtc_tm.tm_year + 1900 + ARCFUDGE; mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ @@ -403,14 +377,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, } case RTC_IRQP_READ: /* Read the periodic IRQ rate. */ { - int retval; - - retval = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long)); - if (retval != 0) - return retval; - - copy_to_user((unsigned long*)arg, &rtc_freq, sizeof(unsigned long)); - return 0; + return put_user(rtc_freq, (unsigned long *)arg); } case RTC_IRQP_SET: /* Set periodic IRQ rate. */ { @@ -451,6 +418,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: return -EINVAL; } + return copy_to_user(arg, &wtime, sizeof wtime) ? -EFAULT : 0; } /* @@ -536,11 +504,11 @@ __initfunc(int rtc_init(void)) { unsigned long flags; - printk("Real Time Clock Driver v%s\n", RTC_VERSION); + printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION); if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL)) { /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ - printk("rtc: IRQ %d is not free.\n", RTC_IRQ); + printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ); return -EIO; } misc_register(&rtc_dev); diff --git a/drivers/char/selection.c b/drivers/char/selection.c index b3f6d157037e..b34d73665c51 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -21,9 +21,9 @@ #include -#include "vt_kern.h" -#include "consolemap.h" -#include "selection.h" +#include +#include +#include #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -101,9 +101,9 @@ int sel_loadlut(const unsigned long arg) } /* does screen address p correspond to character at LH/RH edge of screen? */ -static inline int atedge(const int p) +static inline int atedge(const int p, int size_row) { - return (!(p % video_size_row) || !((p + 2) % video_size_row)); + return (!(p % size_row) || !((p + 2) % size_row)); } /* constrain v such that v <= u */ @@ -227,9 +227,9 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) /* select to end of line if on trailing space */ if (new_sel_end > new_sel_start && - !atedge(new_sel_end) && isspace(sel_pos(new_sel_end))) { + !atedge(new_sel_end, size_row) && isspace(sel_pos(new_sel_end))) { for (pe = new_sel_end + 2; ; pe += 2) - if (!isspace(sel_pos(pe)) || atedge(pe)) + if (!isspace(sel_pos(pe)) || atedge(pe, size_row)) break; if (isspace(sel_pos(pe))) new_sel_end = pe; diff --git a/drivers/char/softdog.c b/drivers/char/softdog.c index d16227d83fa7..2eda6ef10672 100644 --- a/drivers/char/softdog.c +++ b/drivers/char/softdog.c @@ -42,6 +42,10 @@ static int soft_margin = TIMER_MARGIN; /* in seconds */ +#ifdef MODULE +MODULE_PARM(soft_margin,"i"); +#endif + /* * Our timer */ diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c new file mode 100644 index 000000000000..e2044c086590 --- /dev/null +++ b/drivers/char/sysrq.c @@ -0,0 +1,248 @@ +/* -*- linux-c -*- + * + * $Id: sysrq.c,v 1.2 1997/05/31 18:33:11 mj Exp $ + * + * Linux Magic System Request Key Hacks + * + * (c) 1997 Martin Mares + * based on ideas by Pavel Machek + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_APM +#include +#endif + +extern void wakeup_bdflush(int); +extern void reset_vc(unsigned int); +extern int console_loglevel; +extern struct vfsmount *vfsmntlist; + +#ifdef __sparc__ +extern void halt_now(void); +#endif + +/* Send a signal to all user processes */ + +static void send_sig_all(int sig, int even_init) +{ + struct task_struct *p; + + for_each_task(p) { + if (p->pid && p->mm != &init_mm) { /* Not swapper nor kernel thread */ + if (p->pid == 1 && even_init) /* Ugly hack to kill init */ + p->pid = 0x8000; + force_sig(sig, p); + } + } +} + +/* + * This function is called by the keyboard handler when SysRq is pressed + * and any other keycode arrives. + */ + +void handle_sysrq(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) +{ + int orig_log_level = console_loglevel; + + console_loglevel = 7; + printk(KERN_INFO "SysRq: "); + switch (key) { + case 19: /* R -- Reset raw mode */ + kbd->kbdmode = VC_XLATE; + printk("Keyboard mode set to XLATE\n"); + break; + case 30: /* A -- SAK */ + printk("SAK\n"); + do_SAK(tty); + reset_vc(fg_console); + break; + case 48: /* B -- boot immediately */ + printk("Resetting\n"); + machine_restart(NULL); + break; +#ifdef __sparc__ + case 35: /* H -- halt immediately */ + printk("Halting\n"); + halt_now(); + break; +#endif +#ifdef CONFIG_APM + case 24: /* O -- power off */ + printk("Power off\n"); + apm_set_power_state(APM_STATE_OFF); + break; +#endif + case 31: /* S -- emergency sync */ + printk("Emergency Sync\n"); + emergency_sync_scheduled = EMERG_SYNC; + wakeup_bdflush(0); + break; + case 22: /* U -- emergency remount R/O */ + printk("Emergency Remount R/O\n"); + emergency_sync_scheduled = EMERG_REMOUNT; + wakeup_bdflush(0); + break; + case 25: /* P -- show PC */ + printk("Show Regs\n"); + if (pt_regs) + show_regs(pt_regs); + break; + case 20: /* T -- show task info */ + printk("Show State\n"); + show_state(); + break; + case 50: /* M -- show memory info */ + printk("Show Memory\n"); + show_mem(); + break; + case 2 ... 11: /* 0-9 -- set console logging level */ + key -= 2; + if (key == 10) + key = 0; + orig_log_level = key; + printk("Log level set to %d\n", key); + break; + case 18: /* E -- terminate all user processes */ + printk("Terminate All Tasks\n"); + send_sig_all(SIGTERM, 0); + orig_log_level = 8; /* We probably have killed syslogd */ + break; + case 37: /* K -- kill all user processes */ + printk("Kill All Tasks\n"); + send_sig_all(SIGKILL, 0); + orig_log_level = 8; + break; + case 38: /* L -- kill all processes including init */ + printk("Kill ALL Tasks (even init)\n"); + send_sig_all(SIGKILL, 1); + orig_log_level = 8; + break; + default: /* Unknown: help */ + printk("unRaw sAk Boot " +#ifdef __sparc__ + "Halt " +#endif +#ifdef CONFIG_APM + "Off " +#endif + "Sync Unmount showPc showTasks showMem loglevel0-8 tErm Kill killalL\n"); + } + + console_loglevel = orig_log_level; +} + +/* Aux routines for the syncer */ + +static void all_files_read_only(void) /* Kill write permissions of all files */ +{ + struct file *file; + + for (file = inuse_filps; file; file = file->f_next) + if (file->f_inode && file->f_count && S_ISREG(file->f_inode->i_mode)) + file->f_mode &= ~2; +} + +static int is_local_disk(kdev_t dev) /* Guess if the device is a local hard drive */ +{ + unsigned int major = MAJOR(dev); + + switch (major) { + case IDE0_MAJOR: + case IDE1_MAJOR: + case IDE2_MAJOR: + case IDE3_MAJOR: + case SCSI_DISK_MAJOR: + return 1; + default: + return 0; + } +} + +static void go_sync(kdev_t dev, int remount_flag) +{ + printk(KERN_INFO "%sing device %04x ... ", + remount_flag ? "Remount" : "Sync", + dev); + + if (remount_flag) { /* Remount R/O */ + struct super_block *sb = get_super(dev); + struct vfsmount *vfsmnt; + int ret, flags; + + if (!sb) { + printk("Superblock not found\n"); + return; + } + if (sb->s_flags & MS_RDONLY) { + printk("R/O\n"); + return; + } + quota_off(dev, -1); + fsync_dev(dev); + flags = MS_RDONLY; + if (sb->s_op && sb->s_op->remount_fs) { + ret = sb->s_op->remount_fs(sb, &flags, NULL); + if (ret) + printk("error %d\n", ret); + else { + sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK); + if ((vfsmnt = lookup_vfsmnt(sb->s_dev))) + vfsmnt->mnt_flags = sb->s_flags; + printk("OK\n"); + } + } else + printk("nothing to do\n"); + } else { + fsync_dev(dev); /* Sync only */ + printk("OK\n"); + } +} + +/* + * Emergency Sync or Unmount. We cannot do it directly, so we set a special + * flag and wake up the bdflush kernel thread which immediately calls this function. + * We process all mounted hard drives first to recover from crashed experimental + * block devices and malfunctional network filesystems. + */ + +int emergency_sync_scheduled; + +void do_emergency_sync(void) +{ + struct vfsmount *mnt; + int remount_flag; + + lock_kernel(); + remount_flag = (emergency_sync_scheduled == EMERG_REMOUNT); + emergency_sync_scheduled = 0; + + if (remount_flag) + all_files_read_only(); + + for (mnt = vfsmntlist; mnt; mnt = mnt->mnt_next) + if (is_local_disk(mnt->mnt_dev)) + go_sync(mnt->mnt_dev, remount_flag); + + for (mnt = vfsmntlist; mnt; mnt = mnt->mnt_next) + if (!is_local_disk(mnt->mnt_dev) && MAJOR(mnt->mnt_dev)) + go_sync(mnt->mnt_dev, remount_flag); + + unlock_kernel(); + printk(KERN_INFO "Done.\n"); +} diff --git a/drivers/char/tga.c b/drivers/char/tga.c index a85f4cba4fae..f4e7b48ab44f 100644 --- a/drivers/char/tga.c +++ b/drivers/char/tga.c @@ -33,11 +33,11 @@ #include #include -#include "kbd_kern.h" -#include "vt_kern.h" -#include "consolemap.h" -#include "selection.h" -#include "console_struct.h" +#include +#include +#include +#include +#include extern struct console vt_console_driver; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 12109e524981..c08e44a275ec 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -77,9 +77,9 @@ #include #include -#include "kbd_kern.h" -#include "vt_kern.h" -#include "selection.h" +#include +#include +#include #ifdef CONFIG_KERNELD #include diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index cb91431dc4cf..603250b81ccf 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -31,9 +31,9 @@ #include #include #include +#include +#include #include -#include "vt_kern.h" -#include "selection.h" #undef attr #undef org diff --git a/drivers/char/vga.c b/drivers/char/vga.c index bfec037ed4dd..2e1195ec6d14 100644 --- a/drivers/char/vga.c +++ b/drivers/char/vga.c @@ -60,11 +60,11 @@ #include #include -#include "kbd_kern.h" -#include "vt_kern.h" -#include "consolemap.h" -#include "selection.h" -#include "console_struct.h" +#include +#include +#include +#include +#include #define CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */ #define CAN_LOAD_PALETTE /* undefine if the user must not do this */ diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 4a48c6e636f4..714128de7dfd 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -26,10 +26,10 @@ #include #include -#include "kbd_kern.h" -#include "vt_kern.h" -#include "diacr.h" -#include "selection.h" +#include +#include +#include +#include char vt_dont_switch = 0; extern struct tty_driver console_driver; diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index dc29bdf794fc..91c868942082 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 1.29 1997/04/23 20:09:49 fritz Exp $ +/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,6 +7,9 @@ * Fritz Elfert * * $Log: callc.c,v $ + * Revision 1.30 1997/05/29 10:40:43 keil + * chanp->impair was uninitialised + * * Revision 1.29 1997/04/23 20:09:49 fritz * Removed tmp, used by removed debugging code. * @@ -109,7 +112,7 @@ extern long mod_use_count_; #endif #endif /* MODULE */ -const char *l4_revision = "$Revision: 1.29 $"; +const char *l4_revision = "$Revision: 1.30 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -1504,6 +1507,7 @@ init_chan(int chan, struct IsdnCardState *csta, int hscx, chanp->debug = 0; chanp->Flags = 0; chanp->leased = 0; + chanp->impair = 0; init_is(chanp, ces); chanp->fi.fsm = &callcfsm; diff --git a/drivers/net/Config.in b/drivers/net/Config.in index e42af773451d..289bd534a65a 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -139,10 +139,15 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then bool 'Soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC bool 'Soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS bool 'Soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200 + bool 'Soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7 + bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8 bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 - if [ "$CONFIG_M586" = "y" -o "$CONFIG_M686" = "y" ]; then - bool 'Soundmodem using floating point' CONFIG_SOUNDMODEM_FLOAT + if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then + bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 + fi + if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then + bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 fi fi fi diff --git a/drivers/net/arcnet.c b/drivers/net/arcnet.c index 7a8069fd6e8f..c9f13150c8aa 100644 --- a/drivers/net/arcnet.c +++ b/drivers/net/arcnet.c @@ -751,8 +751,8 @@ __initfunc(int arcnet_probe(struct device *dev)) * FIXME: grab all devices in one shot and eliminate the big static array. */ -static int ports[(0x3f0 - 0x200) / 16 + 1] __initdata; -static u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1] __initdata; +static int ports[(0x3f0 - 0x200) / 16 + 1] __initdata = { 0 }; +static u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1] __initdata = { 0 }; __initfunc(int arcnet_probe(struct device *dev)) { diff --git a/drivers/net/baycom.c b/drivers/net/baycom.c index 7569d66e7360..74c041342b82 100644 --- a/drivers/net/baycom.c +++ b/drivers/net/baycom.c @@ -68,6 +68,7 @@ * History: * 0.1 26.06.96 Adapted from baycom.c and made network driver interface * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) + * 0.3 26.04.96 init code/data tagged */ /*****************************************************************************/ @@ -89,7 +90,6 @@ #include #include #include -#include /* --------------------------------------------------------------------- */ @@ -133,6 +133,14 @@ extern inline int copy_to_user(void *to, const void *from, unsigned long n) } #endif +#if LINUX_VERSION_CODE >= 0x20123 +#include +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + /* --------------------------------------------------------------------- */ #define BAYCOM_DEBUG @@ -1001,7 +1009,7 @@ MODULE_DESCRIPTION("Baycom ser12, par96 and picpar amateur radio modem driver"); #endif -int init_module(void) +__initfunc(int init_module(void)) { baycom_ports[0].mode = mode; baycom_ports[0].iobase = iobase; diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c index 659e71f327a7..b841a01bbce4 100644 --- a/drivers/net/defxx.c +++ b/drivers/net/defxx.c @@ -229,7 +229,11 @@ static const char *version = "defxx.c:v1.04 09/16/96 Lawrence V. Stefani (stefa #define DYNAMIC_BUFFERS 1 #define SKBUFF_RX_COPYBREAK 200 -#define NEW_SKB_SIZE (PI_RCV_DATA_K_SIZE_MAX) +/* + * NEW_SKB_SIZE = PI_RCV_DATA_K_SIZE_MAX+128 to allow 128 byte + * alignment for compatibility with old EISA boards. + */ +#define NEW_SKB_SIZE (PI_RCV_DATA_K_SIZE_MAX+128) /* Define global routines */ @@ -2944,6 +2948,12 @@ void dfx_rcv_init( bp->descr_block_virt->rcv_data[i+j].long_0 = (u32) (PI_RCV_DESCR_M_SOP | ((PI_RCV_DATA_K_SIZE_MAX / PI_ALIGN_K_RCV_DATA_BUFF) << PI_RCV_DESCR_V_SEG_LEN)); newskb = dev_alloc_skb(NEW_SKB_SIZE); + /* + * align to 128 bytes for compatibility with + * the old EISA boards. + */ + newskb->data = (char *)((unsigned long) + (newskb->data+127) & ~128); bp->descr_block_virt->rcv_data[i+j].long_1 = virt_to_bus(newskb->data); /* * p_rcv_buff_va is only used inside the @@ -3012,8 +3022,6 @@ void dfx_rcv_queue_process( u32 descr, pkt_len; /* FMC descriptor field and packet length */ struct sk_buff *skb; /* pointer to a sk_buff to hold incoming packet data */ - static int testing_dyn; - /* Service all consumed LLC receive frames */ p_type_2_cons = (PI_TYPE_2_CONSUMER *)(&bp->cons_block_virt->xmt_rcv_data); @@ -3056,18 +3064,12 @@ void dfx_rcv_queue_process( newskb = dev_alloc_skb(NEW_SKB_SIZE); if (newskb){ rx_in_place = 1; -#define JES_TESTING -#ifdef JES_TESTING - if(testing_dyn++ < 5) - printk("Skipping a memcpy\n"); + + newskb->data = (char *)((unsigned long)(newskb->data+127) & ~128); skb = (struct sk_buff *)bp->p_rcv_buff_va[entry]; skb->data += RCV_BUFF_K_PADDING; bp->p_rcv_buff_va[entry] = (char *)newskb; bp->descr_block_virt->rcv_data[entry].long_1 = virt_to_bus(newskb->data); -#else - memcpy(newskb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3); - skb = newskb; -#endif } else skb = 0; } else @@ -3240,12 +3242,14 @@ int dfx_xmt_queue_pkt( p_xmt_descr = &(bp->descr_block_virt->xmt_data[prod]); /* - * Get pointer to auxiliary queue entry to contain information for this packet. + * Get pointer to auxiliary queue entry to contain information + * for this packet. * - * Note: The current xmt producer index will become the current xmt completion - * index when we complete this packet later on. So, we'll get the - * pointer to the next auxiliary queue entry now before we bump the - * producer index. + * Note: The current xmt producer index will become the + * current xmt completion index when we complete this + * packet later on. So, we'll get the pointer to the + * next auxiliary queue entry now before we bump the + * producer index. */ p_xmt_drv_descr = &(bp->xmt_drv_descr_blk[prod++]); /* also bump producer index */ @@ -3290,15 +3294,15 @@ int dfx_xmt_queue_pkt( * Verify that descriptor is actually available * * Note: If descriptor isn't available, return 1 which tells - * the upper layer to requeue the packet for later - * transmission. + * the upper layer to requeue the packet for later + * transmission. * * We need to ensure that the producer never reaches the - * completion, except to indicate that the queue is empty. + * completion, except to indicate that the queue is empty. */ if (prod == bp->rcv_xmt_reg.index.xmt_comp) - return(1); /* requeue packet for later */ + return(1); /* requeue packet for later */ /* * Save info for this packet for xmt done indication routine @@ -3311,9 +3315,9 @@ int dfx_xmt_queue_pkt( * one (1) for each completed packet. * * Note: If this assumption changes and we're presented with - * an inconsistent number of transmit fragments for packet - * data, we'll need to modify this code to save the current - * transmit producer index. + * an inconsistent number of transmit fragments for packet + * data, we'll need to modify this code to save the current + * transmit producer index. */ p_xmt_drv_descr->p_skb = skb; diff --git a/drivers/net/hdlcdrv.c b/drivers/net/hdlcdrv.c index 467980406a4b..666bf385889c 100644 --- a/drivers/net/hdlcdrv.c +++ b/drivers/net/hdlcdrv.c @@ -32,6 +32,7 @@ * (copy_{to,from}_user) * 0.2 21.11.96 various small changes * 0.3 03.03.97 fixed (hopefully) IP not working with ax.25 as a module + * 0.4 16.04.97 init code/data tagged */ /*****************************************************************************/ @@ -119,6 +120,23 @@ extern __inline__ void dev_init_buffers(struct device *dev) /* --------------------------------------------------------------------- */ +#if LINUX_VERSION_CODE >= 0x20123 +#include +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x20125 +#define test_and_set_bit set_bit +#define test_and_clear_bit clear_bit +#endif + +/* --------------------------------------------------------------------- */ + /* * The name of the card. Is used for messages and in the requests for * io regions, irqs and dma channels @@ -998,10 +1016,10 @@ MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); /* --------------------------------------------------------------------- */ -int init_module(void) +__initfunc(int init_module(void)) { printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n"); - printk(KERN_INFO "hdlcdrv: version 0.3 compiled " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "hdlcdrv: version 0.4 compiled " __TIME__ " " __DATE__ "\n"); #if LINUX_VERSION_CODE < 0x20115 register_symtab(&hdlcdrv_syms); #endif diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c index 9d8e30761cae..ef15a14da4d0 100644 --- a/drivers/net/net_init.c +++ b/drivers/net/net_init.c @@ -248,7 +248,7 @@ void fddi_setup(struct device *dev) #endif -#ifdef CONFIG_ATALK +#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) static int ltalk_change_mtu(struct device *dev, int mtu) { diff --git a/drivers/net/soundmodem/Makefile b/drivers/net/soundmodem/Makefile index 119118128527..5259b724ca27 100644 --- a/drivers/net/soundmodem/Makefile +++ b/drivers/net/soundmodem/Makefile @@ -21,6 +21,12 @@ endif ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y) O_OBJS += sm_afsk1200.o endif +ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_7),y) +O_OBJS += sm_afsk2400_7.o +endif +ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_8),y) +O_OBJS += sm_afsk2400_8.o +endif ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y) O_OBJS += sm_afsk2666.o endif @@ -39,7 +45,8 @@ M_OBJS := $(O_TARGET) gentbl: gentbl.c $(HOSTCC) -Wall $< -o $@ -lm -TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2666.h sm_tbl_psk4800.h +TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h +TBLHDR += sm_tbl_afsk2666.h sm_tbl_psk4800.h TBLHDR += sm_tbl_hapn4800.h sm_tbl_fsk9600.h $(TBLHDR): gentbl diff --git a/drivers/net/soundmodem/gentbl.c b/drivers/net/soundmodem/gentbl.c index d617865e3d8f..cb8cb246ff8a 100644 --- a/drivers/net/soundmodem/gentbl.c +++ b/drivers/net/soundmodem/gentbl.c @@ -31,27 +31,44 @@ /* -------------------------------------------------------------------- */ -#define OFFSCOSTABBITS 6 -#define OFFSCOSTABSIZE (1<>%d)&0x%x]\n\n", - 16-OFFSCOSTABBITS, OFFSCOSTABSIZE-1); + 16-nbits, (1<>%d)&0x%x]\n" + "#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits, + (1<= 0; o--) { fprintf(f, "\t{\n"); for (i = 0; i < AFSK26_NUMCAR; i++) { j = cfreq[i]; fprintf(f, "\t\t{{ "); - for (l = AFSK26_DEMCORRLEN-1, - k = (j * o)/AFSK26_RXOVER; l >= 0; - l--, k = (k+j)&0xffffu) - fprintf(f, "%6d%s", (int) - (AFSK26_AMPL(i)* - window[l*AFSK26_RXOVER+o]* - cos(M_PI*k/32768.0)), - l ? ", " : " }, { "); - for (l = AFSK26_DEMCORRLEN-1, - k = (j * o)/AFSK26_RXOVER; l >= 0; - l--, k = (k+j)&0xffffu) - fprintf(f, "%6d%s", (int) - (AFSK26_AMPL(i)* - window[l*AFSK26_RXOVER+o]* - sin(M_PI*k/32768.0)), - l ? ", " : " }}"); + for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumi = 0; l >= 0; + l--, k = (k+j)&0xffffu) { + sumi += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]* + cos(M_PI*k/32768.0)); + fprintf(f, "%6d%s", v, l ? ", " : " }, { "); + } + for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumq = 0; l >= 0; + l--, k = (k+j)&0xffffu) { + sumq += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]* + sin(M_PI*k/32768.0)); + fprintf(f, "%6d%s", v, l ? ", " : " }}"); + } if (i < 1) fprintf(f, ","); - fprintf(f, "\n"); + fprintf(f, "\n#define AFSK26_DEM_SUM_I_%d_%d %d\n" + "#define AFSK26_DEM_SUM_Q_%d_%d %d\n", + AFSK26_RXOVER-1-o, i, sumi, AFSK26_RXOVER-1-o, i, sumq); } fprintf(f, "\t}%s\n", o ? "," : ""); } @@ -308,28 +340,6 @@ static void gentbl_afsk2666(FILE *f) /* -------------------------------------------------------------------- */ -#define COSTABBITS 8 - -static void gentbl_costab(FILE *f) -{ - int i; - - fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n" - "static const short costab[%d] = {", (1<>%d)&0x%x]\n" - "#define SIN(x) COS((x)+0xc000)\n\n", 16-COSTABBITS, - (1<\n\n"); +} + /* -------------------------------------------------------------------- */ void main(int argc, char *argv[]) @@ -596,20 +690,23 @@ void main(int argc, char *argv[]) if (!(f = fopen("sm_tbl_afsk1200.h", "w"))) exit(1); gentbl_banner(f); - gentbl_offscostab(f); + gentbl_needs_config(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); gentbl_afsk1200(f); fclose(f); if (!(f = fopen("sm_tbl_afsk2666.h", "w"))) exit(1); gentbl_banner(f); - gentbl_offscostab(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); gentbl_afsk2666(f); fclose(f); if (!(f = fopen("sm_tbl_psk4800.h", "w"))) exit(1); gentbl_banner(f); gentbl_psk4800(f); - gentbl_costab(f); + gentbl_costab(f, 8); gentbl_atantab(f); fclose(f); if (!(f = fopen("sm_tbl_hapn4800.h", "w"))) @@ -622,6 +719,22 @@ void main(int argc, char *argv[]) gentbl_banner(f); gentbl_fsk9600(f); fclose(f); + if (!(f = fopen("sm_tbl_afsk2400_8.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_needs_config(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); + gentbl_afsk2400(f, 8000000); + fclose(f); + if (!(f = fopen("sm_tbl_afsk2400_7.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_needs_config(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); + gentbl_afsk2400(f, 7372800); + fclose(f); exit(0); } diff --git a/drivers/net/soundmodem/sm.c b/drivers/net/soundmodem/sm.c index d55bc0c042d8..ac1fbac48cf1 100644 --- a/drivers/net/soundmodem/sm.c +++ b/drivers/net/soundmodem/sm.c @@ -38,6 +38,7 @@ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) * 0.4 21.01.97 Separately compileable soundcard/modem modules * 0.5 03.03.97 fixed LPT probing (check_lpt result was interpreted the wrong way round) + * 0.6 16.04.97 init code/data tagged */ /*****************************************************************************/ @@ -56,7 +57,6 @@ #include #include #include -#include #include "sm.h" /* --------------------------------------------------------------------- */ @@ -101,18 +101,32 @@ extern inline int copy_to_user(void *to, const void *from, unsigned long n) } #endif +#if LINUX_VERSION_CODE >= 0x20123 +#include +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + /* --------------------------------------------------------------------- */ -const char sm_drvname[] = "soundmodem"; -static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "soundmodem: version 0.5 compiled " __TIME__ " " __DATE__ "\n"; +/*static*/ const char sm_drvname[] = "soundmodem"; +static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "soundmodem: version 0.6 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ -const struct modem_tx_info *sm_modem_tx_table[] = { +/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = { #ifdef CONFIG_SOUNDMODEM_AFSK1200 &sm_afsk1200_tx, #endif /* CONFIG_SOUNDMODEM_AFSK1200 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_7 + &sm_afsk2400_7_tx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_8 + &sm_afsk2400_8_tx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */ #ifdef CONFIG_SOUNDMODEM_AFSK2666 &sm_afsk2666_tx, #endif /* CONFIG_SOUNDMODEM_AFSK2666 */ @@ -132,10 +146,16 @@ const struct modem_tx_info *sm_modem_tx_table[] = { NULL }; -const struct modem_rx_info *sm_modem_rx_table[] = { +/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = { #ifdef CONFIG_SOUNDMODEM_AFSK1200 &sm_afsk1200_rx, #endif /* CONFIG_SOUNDMODEM_AFSK1200 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_7 + &sm_afsk2400_7_rx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_8 + &sm_afsk2400_8_rx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */ #ifdef CONFIG_SOUNDMODEM_AFSK2666 &sm_afsk2666_rx, #endif /* CONFIG_SOUNDMODEM_AFSK2666 */ @@ -344,7 +364,7 @@ void sm_output_status(struct sm_state *sm) int invert_dcd = 0; int invert_ptt = 0; - int ptt = hdlcdrv_ptt(&sm->hdrv) ^ invert_ptt; + int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt; int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd; if (sm->hdrv.ptt_out.flags & SP_SER) { @@ -457,9 +477,9 @@ static int sm_open(struct device *dev) return err; sm_output_open(sm); MOD_INC_USE_COUNT; - printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u\n", + printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n", sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name, - sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma); + sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2); return 0; } @@ -664,7 +684,56 @@ static int sm_ioctl(struct device *dev, struct ifreq *ifr, /* --------------------------------------------------------------------- */ +#ifdef __i386__ + +int sm_x86_capability = 0; + +__initfunc(static void i386_capability(void)) +{ + unsigned long flags; + unsigned long fl1; + union { + struct { + unsigned int ebx, edx, ecx; + } r; + unsigned char s[13]; + } id; + unsigned int eax; + + save_flags(flags); + flags |= 0x200000; + restore_flags(flags); + save_flags(flags); + fl1 = flags; + flags &= ~0x200000; + restore_flags(flags); + save_flags(flags); + if (!(fl1 & 0x200000) || (flags & 0x200000)) { + printk(KERN_WARNING "%s: cpu does not support CPUID\n", sm_drvname); + return; + } + __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) : + "0" (0)); + id.s[12] = 0; + if (eax < 1) { + printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability " + "list\n", sm_drvname, id.s); + return; + } + printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s); + __asm__ ("cpuid" : "=a" (eax), "=d" (sm_x86_capability) : "0" (1) : "ebx", "ecx"); + printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, + eax & 15, sm_x86_capability); +} +#endif /* __i386__ */ + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +__initfunc(static int sm_init(void)) +#else /* MODULE */ __initfunc(int sm_init(void)) +#endif /* MODULE */ { int i, j, found = 0; char set_hw = 1; @@ -672,6 +741,9 @@ __initfunc(int sm_init(void)) char ifname[HDLCDRV_IFNAMELEN]; printk(sm_drvinfo); +#ifdef __i386__ + i386_capability(); +#endif /* __i386__ */ /* * register net devices */ @@ -745,7 +817,7 @@ MODULE_DESCRIPTION("Soundcard amateur radio modem driver"); #endif -int init_module(void) +__initfunc(int init_module(void)) { if (mode) { if (iobase == -1) diff --git a/drivers/net/soundmodem/sm.h b/drivers/net/soundmodem/sm.h index 2cce82992d35..25bbc8ba9d8c 100644 --- a/drivers/net/soundmodem/sm.h +++ b/drivers/net/soundmodem/sm.h @@ -30,16 +30,11 @@ /* ---------------------------------------------------------------------- */ -#include #include #include #define SM_DEBUG -/* --------------------------------------------------------------------- */ - -#define DMA_MODE_AUTOINIT 0x10 - /* ---------------------------------------------------------------------- */ /* * Information that need to be kept for each board. @@ -56,6 +51,18 @@ struct sm_state { /* * Hardware (soundcard) access routines state */ + struct { + void *ibuf; + unsigned int ifragsz; + unsigned int ifragptr; + unsigned int i16bit; + void *obuf; + unsigned int ofragsz; + unsigned int ofragptr; + unsigned int o16bit; + int ptt_cnt; + } dma; + union { long hw[32/sizeof(long)]; } hw; @@ -101,9 +108,9 @@ struct modem_tx_info { unsigned int loc_storage; int srate; int bitrate; - unsigned int dmabuflenmodulo; - void (*modulator)(struct sm_state *, unsigned char *, int); - void (*init)(struct sm_state *); + void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int); + void (*modulator_s16)(struct sm_state *, short *, unsigned int); + void (*init)(struct sm_state *); }; struct modem_rx_info { @@ -111,10 +118,11 @@ struct modem_rx_info { unsigned int loc_storage; int srate; int bitrate; - unsigned int dmabuflenmodulo; + unsigned int overlap; unsigned int sperbit; - void (*demodulator)(struct sm_state *, unsigned char *, int); - void (*init)(struct sm_state *); + void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int); + void (*demodulator_s16)(struct sm_state *, const short *, unsigned int); + void (*init)(struct sm_state *); }; /* ---------------------------------------------------------------------- */ @@ -281,30 +289,41 @@ extern inline unsigned int lcm(unsigned int x, unsigned int y) */ -#if defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) +#ifdef __i386__ + +extern int sm_x86_capability; + +#define HAS_RDTSC (sm_x86_capability & 0x10) /* * only do 32bit cycle counter arithmetic; we hope we won't overflow :-) * in fact, overflowing modems would require over 2THz clock speeds :-) */ -#define time_exec(var,cmd) \ -({ \ - unsigned int cnt1, cnt2, cnt3; \ - __asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3)); \ - cmd; \ - __asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3)); \ - var = cnt2-cnt1; \ +#define time_exec(var,cmd) \ +({ \ + if (HAS_RDTSC) { \ + unsigned int cnt1, cnt2, cnt3; \ + __asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3)); \ + cmd; \ + __asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3)); \ + var = cnt2-cnt1; \ + } else { \ + cmd; \ + } \ }) -#else /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */ + +#else /* __i386__ */ #define time_exec(var,cmd) cmd -#endif /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */ +#endif /* __i386__ */ /* --------------------------------------------------------------------- */ extern const struct modem_tx_info sm_afsk1200_tx; +extern const struct modem_tx_info sm_afsk2400_7_tx; +extern const struct modem_tx_info sm_afsk2400_8_tx; extern const struct modem_tx_info sm_afsk2666_tx; extern const struct modem_tx_info sm_psk4800_tx; extern const struct modem_tx_info sm_hapn4800_8_tx; @@ -315,6 +334,8 @@ extern const struct modem_tx_info sm_fsk9600_4_tx; extern const struct modem_tx_info sm_fsk9600_5_tx; extern const struct modem_rx_info sm_afsk1200_rx; +extern const struct modem_rx_info sm_afsk2400_7_rx; +extern const struct modem_rx_info sm_afsk2400_8_rx; extern const struct modem_rx_info sm_afsk2666_rx; extern const struct modem_rx_info sm_psk4800_rx; extern const struct modem_rx_info sm_hapn4800_8_rx; diff --git a/drivers/net/soundmodem/sm_afsk1200.c b/drivers/net/soundmodem/sm_afsk1200.c index 0519c5e793e4..64b20a57c591 100644 --- a/drivers/net/soundmodem/sm_afsk1200.c +++ b/drivers/net/soundmodem/sm_afsk1200.c @@ -38,174 +38,176 @@ struct demod_state_afsk12 { int dcd_sum0, dcd_sum1, dcd_sum2; unsigned int dcd_time; unsigned char last_rxbit; - union { - signed char c[8]; - float f[8]; - } filt; }; struct mod_state_afsk12 { unsigned int shreg; unsigned char tx_bit; unsigned int bit_pll; + unsigned int dds_inc; + unsigned int txphase; }; /* --------------------------------------------------------------------- */ -static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen) +static const int dds_inc[2] = { + AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE, + AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE +}; + +static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf, + unsigned int buflen) { struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m); - static const int dds_inc[2] = { AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE, - AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE }; - int j, k; - - for (; buflen >= 8; buflen -= 8) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit ^ - (!(st->shreg & 1))) & 1; - st->shreg >>= 1; - k = dds_inc[st->tx_bit & 1]; - for (j = 0; j < 8; j++) { - *buf++ = OFFSCOS(st->bit_pll); - st->bit_pll += k; + + for (; buflen > 0; buflen--) { + if (!((st->txphase++) & 7)) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; } + st->dds_inc = dds_inc[st->tx_bit & 1]; + *buf++ = OFFSCOS(st->bit_pll); + st->bit_pll += st->dds_inc; } } /* --------------------------------------------------------------------- */ +static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m); -/* - * should eventually move to an asm header file - */ - - -#if defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686)) - + for (; buflen > 0; buflen--) { + if (!((st->txphase++) & 7)) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + } + st->dds_inc = dds_inc[st->tx_bit & 1]; + *buf++ = COS(st->bit_pll); + st->bit_pll += st->dds_inc; + } +} +/* --------------------------------------------------------------------- */ -#define ENV_STORAGE unsigned char fpu_save[108]; +extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum) +{ + int sum = -0x80 * csum; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); -#define ENV_SAVE asm("fsave %0;\n\tfninit;\n\t" : "=m" (*fpu_save) : : "memory"); -#define ENV_RESTORE asm("frstor %0;\n\t" : : "m" (*fpu_save)); + sum >>= 7; + return sum * sum; +} -static inline float convolution8(const float *st, const float *coeff) +extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum) { - float f; - - /* - * from Phil Karn, KA9Q's home page - */ - asm volatile ("flds (%1);\n\t" - "fmuls (%2);\n\t" - "flds 4(%1);\n\t" - "fmuls 4(%2);\n\t" - "flds 8(%1);\n\t" - "fmuls 8(%2);\n\t" - "fxch %%st(2);\n\t" - "faddp;\n\t" - "flds 12(%1);\n\t" - "fmuls 12(%2);\n\t" - "fxch %%st(2);\n\t" - "faddp;\n\t" - "flds 16(%1);\n\t" - "fmuls 16(%2);\n\t" - "fxch %%st(2);\n\t" - "faddp;\n\t" - "flds 20(%1);\n\t" - "fmuls 20(%2);\n\t" - "fxch %%st(2);\n\t" - "faddp;\n\t" - "flds 24(%1);\n\t" - "fmuls 24(%2);\n\t" - "fxch %%st(2);\n\t" - "faddp;\n\t" - "flds 28(%1);\n\t" - "fmuls 28(%2);\n\t" - "fxch %%st(2);\n\t" - "faddp;\n\t" - "faddp;\n\t" - "fmul %%st(0),%%st;\n\t" : - "=t" (f) : - "r" (st), - "r" (coeff) : "memory"); - - return f; + int sum = 0; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + + sum >>= 15; + return sum * sum; } -static inline int do_filter_1200(struct demod_state_afsk12 *st, unsigned char newval) +extern __inline__ int do_filter_1200_u8(const unsigned char *buf) { - float sum; - - memmove(st->filt.f+1, st->filt.f,sizeof(st->filt.f) - sizeof(st->filt.f[0])); - st->filt.f[0] = (((int)newval)-0x80); - - sum = convolution8(st->filt.f, afsk12_tx_lo_i_f); - sum += convolution8(st->filt.f, afsk12_tx_lo_q_f); - sum -= convolution8(st->filt.f, afsk12_tx_hi_i_f); - sum -= convolution8(st->filt.f, afsk12_tx_hi_q_f); + int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I); + sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q); + sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I); + sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q); return sum; } -#else /* defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */ - -#define ENV_STORAGE -#define ENV_SAVE -#define ENV_RESTORE - -static inline void datamove8(signed char *st, unsigned char newval) +extern __inline__ int do_filter_1200_s16(const short *buf) { - memmove(st+1, st, 7); - *st = newval - 0x80; + int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I); + sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q); + sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I); + sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q); + return sum; } -static inline int convolution8(const signed char *st, const signed char *coeff) -{ - int sum = (st[0] * coeff[0]); - - sum += (st[1] * coeff[1]); - sum += (st[2] * coeff[2]); - sum += (st[3] * coeff[3]); - sum += (st[4] * coeff[4]); - sum += (st[5] * coeff[5]); - sum += (st[6] * coeff[6]); - sum += (st[7] * coeff[7]); +/* --------------------------------------------------------------------- */ - sum >>= 7; - return sum * sum; -} +static const int pll_corr[2] = { -0x1000, 0x1000 }; -static inline int do_filter_1200(struct demod_state_afsk12 *st, unsigned char newval) +static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) { + struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); + int j; int sum; + unsigned char newsample; - datamove8(st->filt.c, newval); - - sum = convolution8(st->filt.c, afsk12_tx_lo_i); - sum += convolution8(st->filt.c, afsk12_tx_lo_q); - sum -= convolution8(st->filt.c, afsk12_tx_hi_i); - sum -= convolution8(st->filt.c, afsk12_tx_hi_q); - return sum; + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_1200_u8(buf); + st->dcd_shreg <<= 1; + st->bit_pll += 0x2000; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + st->bit_pll += pll_corr + [st->bit_pll < 0x9000]; + j = 4 * hweight8(st->dcd_shreg & 0x38) + - hweight16(st->dcd_shreg & 0x7c0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, (((int)*buf)-0x80) << 8, sum); + } } -#endif /* defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */ - - /* --------------------------------------------------------------------- */ -static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen) +static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen) { struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; int j; int sum; unsigned char newsample; - ENV_STORAGE; - ENV_SAVE; for (; buflen > 0; buflen--, buf++) { - sum = do_filter_1200(st, *buf); + sum = do_filter_1200_s16(buf); st->dcd_shreg <<= 1; st->bit_pll += 0x2000; newsample = (sum > 0); @@ -240,9 +242,8 @@ static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen st->shreg = 0x10000; } } - diag_add(sm, (((int)*buf)-0x80) << 8, sum); + diag_add(sm, *buf, sum); } - ENV_RESTORE; } /* --------------------------------------------------------------------- */ @@ -259,13 +260,13 @@ static void demod_init_1200(struct sm_state *sm) const struct modem_tx_info sm_afsk1200_tx = { "afsk1200", sizeof(struct mod_state_afsk12), - AFSK12_SAMPLE_RATE, 1200, AFSK12_SAMPLE_RATE/1200, modulator_1200, NULL + AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL }; const struct modem_rx_info sm_afsk1200_rx = { "afsk1200", sizeof(struct demod_state_afsk12), - AFSK12_SAMPLE_RATE, 1200, AFSK12_SAMPLE_RATE/1200, - AFSK12_SAMPLE_RATE/1200, demodulator_1200, demod_init_1200 + AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200, + demodulator_1200_u8, demodulator_1200_s16, demod_init_1200 }; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/soundmodem/sm_afsk2400_7.c b/drivers/net/soundmodem/sm_afsk2400_7.c new file mode 100644 index 000000000000..36e0f328f10c --- /dev/null +++ b/drivers/net/soundmodem/sm_afsk2400_7.c @@ -0,0 +1,297 @@ +/*****************************************************************************/ + +/* + * sm_afsk2400_7.c -- soundcard radio modem driver, 2400 baud AFSK modem + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +/* + * This driver is intended to be compatible with TCM3105 modems + * overclocked to 7.3728MHz. The mark and space frequencies therefore + * lie at 3658 and 1996 Hz. + * Note that I do _not_ recommend the building of such links, I provide + * this only for the users who live in the coverage area of such + * a "legacy" link. + */ + +#include +#include "sm.h" +#include "sm_tbl_afsk2400_7.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_afsk24 { + unsigned int shreg; + unsigned int bit_pll; + unsigned char last_sample; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; +}; + +struct mod_state_afsk24 { + unsigned int shreg; + unsigned char tx_bit; + unsigned int bit_pll; + unsigned int tx_seq; + unsigned int phinc; +}; + +/* --------------------------------------------------------------------- */ + +static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE, + AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE }; + +static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = OFFSCOS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = COS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum) +{ + int sum = -0x80 * csum; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coeff[9]); + sum += (st[-10] * coeff[10]); + sum += (st[-11] * coeff[11]); + sum += (st[-12] * coeff[12]); + sum += (st[-13] * coeff[13]); + + sum >>= 7; + return sum * sum; +} + +extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum) +{ + int sum = 0; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coeff[9]); + sum += (st[-10] * coeff[10]); + sum += (st[-11] * coeff[11]); + sum += (st[-12] * coeff[12]); + sum += (st[-13] * coeff[13]); + + sum >>= 15; + return sum * sum; +} + +extern __inline__ int do_filter_2400_u8(const unsigned char *buf) +{ + int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); + sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); + sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); + sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); + return sum; +} + +extern __inline__ int do_filter_2400_s16(const short *buf) +{ + int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); + sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); + sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); + sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); + return sum; +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_2400_u8(buf); + st->dcd_shreg <<= 1; + st->bit_pll += AFSK24_BITPLL_INC; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) + st->bit_pll += AFSK24_BITPLL_INC/2; + else + st->bit_pll -= AFSK24_BITPLL_INC/2; + j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) + - hweight16(st->dcd_shreg & 0x1e0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, (((int)*buf)-0x80) << 8, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_2400_s16(buf); + st->dcd_shreg <<= 1; + st->bit_pll += AFSK24_BITPLL_INC; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) + st->bit_pll += AFSK24_BITPLL_INC/2; + else + st->bit_pll -= AFSK24_BITPLL_INC/2; + j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) + - hweight16(st->dcd_shreg & 0x1e0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, *buf, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_2400(struct sm_state *sm) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + + st->dcd_time = 120; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_afsk2400_7_tx = { + "afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, + modulator_2400_u8, modulator_2400_s16, NULL +}; + +const struct modem_rx_info sm_afsk2400_7_rx = { + "afsk2400_7", sizeof(struct demod_state_afsk24), + AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, + demodulator_2400_u8, demodulator_2400_s16, demod_init_2400 +}; + +/* --------------------------------------------------------------------- */ diff --git a/drivers/net/soundmodem/sm_afsk2400_8.c b/drivers/net/soundmodem/sm_afsk2400_8.c new file mode 100644 index 000000000000..8234815d6373 --- /dev/null +++ b/drivers/net/soundmodem/sm_afsk2400_8.c @@ -0,0 +1,297 @@ +/*****************************************************************************/ + +/* + * sm_afsk2400_8.c -- soundcard radio modem driver, 2400 baud AFSK modem + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +/* + * This driver is intended to be compatible with TCM3105 modems + * overclocked to 8MHz. The mark and space frequencies therefore + * lie at 3970 and 2165 Hz. + * Note that I do _not_ recommend the building of such links, I provide + * this only for the users who live in the coverage area of such + * a "legacy" link. + */ + +#include +#include "sm.h" +#include "sm_tbl_afsk2400_8.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_afsk24 { + unsigned int shreg; + unsigned int bit_pll; + unsigned char last_sample; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; +}; + +struct mod_state_afsk24 { + unsigned int shreg; + unsigned char tx_bit; + unsigned int bit_pll; + unsigned int tx_seq; + unsigned int phinc; +}; + +/* --------------------------------------------------------------------- */ + +static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE, + AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE }; + +static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = OFFSCOS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = COS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum) +{ + int sum = -0x80 * csum; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coeff[9]); + sum += (st[-10] * coeff[10]); + sum += (st[-11] * coeff[11]); + sum += (st[-12] * coeff[12]); + sum += (st[-13] * coeff[13]); + + sum >>= 7; + return sum * sum; +} + +extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum) +{ + int sum = 0; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coeff[9]); + sum += (st[-10] * coeff[10]); + sum += (st[-11] * coeff[11]); + sum += (st[-12] * coeff[12]); + sum += (st[-13] * coeff[13]); + + sum >>= 15; + return sum * sum; +} + +extern __inline__ int do_filter_2400_u8(const unsigned char *buf) +{ + int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); + sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); + sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); + sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); + return sum; +} + +extern __inline__ int do_filter_2400_s16(const short *buf) +{ + int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); + sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); + sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); + sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); + return sum; +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_2400_u8(buf); + st->dcd_shreg <<= 1; + st->bit_pll += AFSK24_BITPLL_INC; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) + st->bit_pll += AFSK24_BITPLL_INC/2; + else + st->bit_pll -= AFSK24_BITPLL_INC/2; + j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) + - hweight16(st->dcd_shreg & 0x1e0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, (((int)*buf)-0x80) << 8, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_2400_s16(buf); + st->dcd_shreg <<= 1; + st->bit_pll += AFSK24_BITPLL_INC; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) + st->bit_pll += AFSK24_BITPLL_INC/2; + else + st->bit_pll -= AFSK24_BITPLL_INC/2; + j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) + - hweight16(st->dcd_shreg & 0x1e0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, *buf, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_2400(struct sm_state *sm) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + + st->dcd_time = 120; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_afsk2400_8_tx = { + "afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, + modulator_2400_u8, modulator_2400_s16, NULL +}; + +const struct modem_rx_info sm_afsk2400_8_rx = { + "afsk2400_8", sizeof(struct demod_state_afsk24), + AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, + demodulator_2400_u8, demodulator_2400_s16, demod_init_2400 +}; + +/* --------------------------------------------------------------------- */ diff --git a/drivers/net/soundmodem/sm_fsk9600.c b/drivers/net/soundmodem/sm_fsk9600.c index 8fbf9d2187f3..bc2fb53b15d3 100644 --- a/drivers/net/soundmodem/sm_fsk9600.c +++ b/drivers/net/soundmodem/sm_fsk9600.c @@ -45,6 +45,8 @@ struct mod_state_fsk96 { unsigned int shreg; unsigned long scram; unsigned char tx_bit; + unsigned char *txtbl; + unsigned int txphase; }; /* --------------------------------------------------------------------- */ @@ -62,30 +64,57 @@ struct mod_state_fsk96 { /* --------------------------------------------------------------------- */ -static void modulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen) +static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) { struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - int j; - const unsigned char *cp; - - for (; buflen >= 4; buflen -= 4) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - cp = fsk96_txfilt_4 + (st->tx_bit & 0xff); - for (j = 0; j < 4; j++, cp += 0x100) - *buf++ = *cp; + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); + } + if (st->txphase >= 4) + st->txphase = 0; + *buf++ = *st->txtbl; + st->txtbl += 0x100; } } /* --------------------------------------------------------------------- */ -static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen) +static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); + } + if (st->txphase >= 4) + st->txphase = 0; + *buf++ = ((*st->txtbl)-0x80) << 8; + st->txtbl += 0x100; + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) { struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); static const int pll_corr[2] = { -0x1000, 0x1000 }; @@ -133,30 +162,105 @@ static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int bufl /* --------------------------------------------------------------------- */ -static void modulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen) +static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + st->dcd_shreg <<= 1; + st->bit_pll += 0x4000; + curbit = (*buf >= 0); + if (st->last_sample ^ curbit) { + st->dcd_shreg |= 1; + st->bit_pll += pll_corr[st->bit_pll < 0xa000]; + st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - + !!(st->dcd_shreg & 0x10); + } + st->last_sample = curbit; + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->descram = (st->descram << 1) | curbit; + descx = st->descram ^ (st->descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + st->shreg >>= 1; + st->shreg |= (!(descx & 1)) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, *buf); + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) { struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - int j; - const unsigned char *cp; - - for (; buflen >= 5; buflen -= 5) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - cp = fsk96_txfilt_5 + (st->tx_bit & 0xff); - for (j = 0; j < 5; j++, cp += 0x100) - *buf++ = *cp; + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); + } + if (st->txphase >= 5) + st->txphase = 0; + *buf++ = *st->txtbl; + st->txtbl += 0x100; } } /* --------------------------------------------------------------------- */ -static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen) +static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); + } + if (st->txphase >= 5) + st->txphase = 0; + *buf++ = ((*st->txtbl)-0x80)<<8; + st->txtbl += 0x100; + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) { struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); static const int pll_corr[2] = { -0x1000, 0x1000 }; @@ -204,6 +308,54 @@ static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int bufl /* --------------------------------------------------------------------- */ +static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + st->dcd_shreg <<= 1; + st->bit_pll += 0x3333; + curbit = (*buf >= 0); + if (st->last_sample ^ curbit) { + st->dcd_shreg |= 1; + st->bit_pll += pll_corr[st->bit_pll < 0x9999]; + st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - + hweight8(st->dcd_shreg & 0x70); + } + st->last_sample = curbit; + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->descram = (st->descram << 1) | curbit; + descx = st->descram ^ (st->descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + st->shreg >>= 1; + st->shreg |= (!(descx & 1)) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, *buf); + } +} + +/* --------------------------------------------------------------------- */ + static void demod_init_9600(struct sm_state *sm) { struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); @@ -215,25 +367,25 @@ static void demod_init_9600(struct sm_state *sm) /* --------------------------------------------------------------------- */ const struct modem_tx_info sm_fsk9600_4_tx = { - "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600, 4, - modulator_9600_4, NULL + "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600, + modulator_9600_4_u8, modulator_9600_4_s16, NULL }; const struct modem_rx_info sm_fsk9600_4_rx = { - "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 4, 4, - demodulator_9600_4, demod_init_9600 + "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4, + demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600 }; /* --------------------------------------------------------------------- */ const struct modem_tx_info sm_fsk9600_5_tx = { - "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600, 5, - modulator_9600_5, NULL + "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600, + modulator_9600_5_u8, modulator_9600_5_s16, NULL }; const struct modem_rx_info sm_fsk9600_5_rx = { - "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 5, 5, - demodulator_9600_5, demod_init_9600 + "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5, + demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600 }; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/soundmodem/sm_hapn4800.c b/drivers/net/soundmodem/sm_hapn4800.c index 9472d5e06b10..f6babcd9d323 100644 --- a/drivers/net/soundmodem/sm_hapn4800.c +++ b/drivers/net/soundmodem/sm_hapn4800.c @@ -49,133 +49,290 @@ struct demod_state_hapn48 { unsigned int dcd_shreg; int dcd_sum0, dcd_sum1, dcd_sum2; unsigned int dcd_time; - int inphist[5]; int lvlhi, lvllo; }; struct mod_state_hapn48 { unsigned int shreg; unsigned char tx_bit; + unsigned int tx_seq; + const unsigned char *tbl; }; /* --------------------------------------------------------------------- */ -static void modulator_hapn4800_10(struct sm_state *sm, unsigned char *buf, int buflen) +static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) { struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - int j; - const unsigned char *cp; - - for (; buflen >= 10; buflen -= 10) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - cp = hapn48_txfilt_10 + (st->tx_bit & 0xf); - for (j = 0; j < 10; j++, cp += 0x10) - *buf++ = *cp; + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; } } /* --------------------------------------------------------------------- */ -static void modulator_hapn4800_8(struct sm_state *sm, unsigned char *buf, int buflen) +static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen) { struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - int j; - const unsigned char *cp; - - for (; buflen >= 8; buflen -= 8) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - cp = hapn48_txfilt_8 + (st->tx_bit & 0xf); - for (j = 0; j < 8; j++, cp += 0x10) - *buf++ = *cp; + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; } } /* --------------------------------------------------------------------- */ -static void modulator_hapn4800_pm10(struct sm_state *sm, unsigned char *buf, int buflen) +static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) { struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - int j; - const unsigned char *cp; - - for (; buflen >= 10; buflen -= 10) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - cp = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); - for (j = 0; j < 10; j++, cp += 0x10) - *buf++ = *cp; + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; } } /* --------------------------------------------------------------------- */ -static void modulator_hapn4800_pm8(struct sm_state *sm, unsigned char *buf, int buflen) +static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen) { struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - int j; - const unsigned char *cp; - - for (; buflen >= 8; buflen -= 8) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - cp = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); - for (j = 0; j < 8; j++, cp += 0x10) - *buf++ = *cp; + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); + static const int pll_corr[2] = { -0x800, 0x800 }; + int curst, cursync; + int inv; + + for (; buflen > 0; buflen--, buf++) { + inv = ((int)(buf[-2])-0x80) << 8; + st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ + st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; + if (buflen & 1) + st->dcd_shreg <<= 1; + st->bit_pll += 0x199a; + curst = cursync = 0; + if (inv > st->lvlhi >> 1) { + curst = 1; + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { + curst = -1; + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); + } + if (cursync) { + st->dcd_shreg |= cursync; + st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu]; + st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - + hweight32(st->dcd_shreg & 0xe739ce70); + } + hdlcdrv_channelbit(&sm->hdrv, cursync); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->last_bit2 = st->last_bit; + if (curst < 0) + st->last_bit = 0; + else if (curst > 0) + st->last_bit = 1; + st->shreg >>= 1; + st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, inv); } } /* --------------------------------------------------------------------- */ -static void demodulator_hapn4800_10(struct sm_state *sm, unsigned char *buf, int buflen) +static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen) { struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); static const int pll_corr[2] = { -0x800, 0x800 }; int curst, cursync; + int inv; for (; buflen > 0; buflen--, buf++) { - st->inphist[4] = st->inphist[3]; - st->inphist[3] = st->inphist[2]; - st->inphist[2] = st->inphist[1]; - st->inphist[1] = st->inphist[0]; - st->inphist[0] = ((int)(*buf)-0x80) << 8; + inv = buf[-2]; st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (st->inphist[2] > st->lvlhi) - st->lvlhi = st->inphist[2]; - if (st->inphist[2] < st->lvllo) - st->lvllo = st->inphist[2]; + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; if (buflen & 1) st->dcd_shreg <<= 1; st->bit_pll += 0x199a; curst = cursync = 0; - if (st->inphist[2] > st->lvlhi >> 1) { + if (inv > st->lvlhi >> 1) { curst = 1; - cursync = (st->inphist[2] > st->inphist[1] && - st->inphist[2] > st->inphist[3] && - st->inphist[2] > st->inphist[0] && - st->inphist[2] > st->inphist[4]); - } else if (st->inphist[2] < st->lvllo >> 1) { + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { curst = -1; - cursync = (st->inphist[2] < st->inphist[1] && - st->inphist[2] < st->inphist[3] && - st->inphist[2] < st->inphist[0] && - st->inphist[2] < st->inphist[4]); + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); } if (cursync) { st->dcd_shreg |= cursync; @@ -208,46 +365,104 @@ static void demodulator_hapn4800_10(struct sm_state *sm, unsigned char *buf, int } diag_trigger(sm); } - diag_add_one(sm, st->inphist[2]); + diag_add_one(sm, inv); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); + static const int pll_corr[2] = { -0x800, 0x800 }; + int curst, cursync; + int inv; + + for (; buflen > 0; buflen--, buf++) { + inv = ((int)(buf[-2])-0x80) << 8; + st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ + st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; + if (buflen & 1) + st->dcd_shreg <<= 1; + st->bit_pll += 0x2000; + curst = cursync = 0; + if (inv > st->lvlhi >> 1) { + curst = 1; + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { + curst = -1; + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); + } + if (cursync) { + st->dcd_shreg |= cursync; + st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u]; + st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - + hweight32(st->dcd_shreg & 0xbbbbbbbb); + } + hdlcdrv_channelbit(&sm->hdrv, cursync); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->last_bit2 = st->last_bit; + if (curst < 0) + st->last_bit = 0; + else if (curst > 0) + st->last_bit = 1; + st->shreg >>= 1; + st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, inv); } } /* --------------------------------------------------------------------- */ -static void demodulator_hapn4800_8(struct sm_state *sm, unsigned char *buf, int buflen) +static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen) { struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); static const int pll_corr[2] = { -0x800, 0x800 }; int curst, cursync; + int inv; for (; buflen > 0; buflen--, buf++) { - st->inphist[4] = st->inphist[3]; - st->inphist[3] = st->inphist[2]; - st->inphist[2] = st->inphist[1]; - st->inphist[1] = st->inphist[0]; - st->inphist[0] = ((int)(*buf)-0x80) << 8; + inv = buf[-2]; st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (st->inphist[2] > st->lvlhi) - st->lvlhi = st->inphist[2]; - if (st->inphist[2] < st->lvllo) - st->lvllo = st->inphist[2]; + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; if (buflen & 1) st->dcd_shreg <<= 1; st->bit_pll += 0x2000; curst = cursync = 0; - if (st->inphist[2] > st->lvlhi >> 1) { + if (inv > st->lvlhi >> 1) { curst = 1; - cursync = (st->inphist[2] > st->inphist[1] && - st->inphist[2] > st->inphist[3] && - st->inphist[2] > st->inphist[0] && - st->inphist[2] > st->inphist[4]); - } else if (st->inphist[2] < st->lvllo >> 1) { + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { curst = -1; - cursync = (st->inphist[2] < st->inphist[1] && - st->inphist[2] < st->inphist[3] && - st->inphist[2] < st->inphist[0] && - st->inphist[2] < st->inphist[4]); + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); } if (cursync) { st->dcd_shreg |= cursync; @@ -280,7 +495,7 @@ static void demodulator_hapn4800_8(struct sm_state *sm, unsigned char *buf, int } diag_trigger(sm); } - diag_add_one(sm, st->inphist[2]); + diag_add_one(sm, inv); } } @@ -297,51 +512,49 @@ static void demod_init_hapn4800(struct sm_state *sm) /* --------------------------------------------------------------------- */ const struct modem_tx_info sm_hapn4800_8_tx = { - "hapn4800", sizeof(struct mod_state_hapn48), - 38400, 4800, 8, modulator_hapn4800_8, NULL + "hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800, + modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL }; const struct modem_rx_info sm_hapn4800_8_rx = { - "hapn4800", sizeof(struct demod_state_hapn48), - 38400, 4800, 8, 8, demodulator_hapn4800_8, demod_init_hapn4800 + "hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, + demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 }; /* --------------------------------------------------------------------- */ const struct modem_tx_info sm_hapn4800_10_tx = { - "hapn4800", sizeof(struct mod_state_hapn48), - 48000, 4800, 10, - modulator_hapn4800_10, NULL + "hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800, + modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL }; const struct modem_rx_info sm_hapn4800_10_rx = { - "hapn4800", sizeof(struct demod_state_hapn48), - 48000, 4800, 10, 10, demodulator_hapn4800_10, demod_init_hapn4800 + "hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, + demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 }; /* --------------------------------------------------------------------- */ const struct modem_tx_info sm_hapn4800_pm8_tx = { - "hapn4800pm", sizeof(struct mod_state_hapn48), - 38400, 4800, 8, modulator_hapn4800_pm8, NULL + "hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800, + modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL }; const struct modem_rx_info sm_hapn4800_pm8_rx = { - "hapn4800pm", sizeof(struct demod_state_hapn48), - 38400, 4800, 8, 8, demodulator_hapn4800_8, demod_init_hapn4800 + "hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, + demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 }; /* --------------------------------------------------------------------- */ const struct modem_tx_info sm_hapn4800_pm10_tx = { - "hapn4800pm", sizeof(struct mod_state_hapn48), - 48000, 4800, 10, - modulator_hapn4800_pm10, NULL + "hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800, + modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL }; const struct modem_rx_info sm_hapn4800_pm10_rx = { - "hapn4800pm", sizeof(struct demod_state_hapn48), - 48000, 4800, 10, 10, demodulator_hapn4800_10, demod_init_hapn4800 + "hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, + demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 }; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/soundmodem/sm_sbc.c b/drivers/net/soundmodem/sm_sbc.c index 1011dca97efd..2a2f874aa224 100644 --- a/drivers/net/soundmodem/sm_sbc.c +++ b/drivers/net/soundmodem/sm_sbc.c @@ -26,12 +26,14 @@ */ #include +#include #include #include #include #include #include #include "sm.h" +#include "smdma.h" /* --------------------------------------------------------------------- */ @@ -81,12 +83,6 @@ struct sc_state_sbc { unsigned char revhi, revlo; unsigned char fmt[2]; unsigned int sr[2]; - unsigned int dmabuflen; - unsigned char *dmabuf; - unsigned char *dmabuf2; - unsigned char dmabufidx; - unsigned char dma2bufidx; - unsigned char ptt; }; #define SCSTATE ((struct sc_state_sbc *)(&sm->hw)) @@ -136,6 +132,7 @@ struct sc_state_sbc { #define SBC4_OUT8_AI 0xc6 #define SBC4_IN8_AI 0xce #define SBC4_MODE_UNS_MONO 0x00 +#define SBC4_MODE_SIGN_MONO 0x10 #define SBC4_OUT16_AI 0xb6 #define SBC4_IN16_AI 0xbe @@ -260,9 +257,8 @@ static int config_resources(struct device *dev, struct sm_state *sm, int fdx) realdma = inb(DSP_MIXER_DATA(dev->base_addr)); restore_flags(flags); if ((~realirq) & irqreg || (~realdma) & dmareg) { - printk(KERN_ERR "%s: sbc resource registers cannot be set; " - "PnP device and IRQ/DMA specified wrongly?\n", - sm_drvname); + printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device " + "and IRQ/DMA specified wrongly?\n", sm_drvname); return -EINVAL; } return 0; @@ -292,41 +288,31 @@ static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send) { SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT } }; static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI }; - static const unsigned char dmamode[2] = { - DMA_MODE_READ | DMA_MODE_AUTOINIT, DMA_MODE_WRITE | DMA_MODE_AUTOINIT - }; static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON }; - unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf); + unsigned int nsamps; send = !!send; if (!reset_dsp(dev)) { printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); return; } - if ((dmabufaddr & 0xffff) + SCSTATE->dmabuflen > 0x10000) - panic("sm: DMA buffer violates DMA boundary!"); save_flags(flags); cli(); sbc_int_ack_8bit(dev); write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */ write_dsp(dev, SCSTATE->fmt[send]); write_dsp(dev, sbcskr[send]); - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - set_dma_mode(dev->dma, dmamode[send]); - set_dma_addr(dev->dma, dmabufaddr); - set_dma_count(dev->dma, SCSTATE->dmabuflen); - enable_dma(dev->dma); + nsamps = dma_setup(sm, send, dev->dma) - 1; sbc_int_ack_8bit(dev); if (SCSTATE->revhi >= 4) { write_dsp(dev, sbc4mode[send]); write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); + write_dsp(dev, nsamps & 0xff); + write_dsp(dev, nsamps >> 8); } else { write_dsp(dev, SBC_BLOCKSIZE); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); + write_dsp(dev, nsamps & 0xff); + write_dsp(dev, nsamps >> 8); write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]); /* hispeed mode if sample rate > 13kHz */ } @@ -339,53 +325,41 @@ static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev = (struct device *)dev_id; struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned char new_ptt; - unsigned char *buf; + unsigned int curfrag; if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) return; - new_ptt = hdlcdrv_ptt(&sm->hdrv); + cli(); sbc_int_ack_8bit(dev); - buf = SCSTATE->dmabuf; - if (SCSTATE->dmabufidx) - buf += SCSTATE->dmabuflen/2; - SCSTATE->dmabufidx = !SCSTATE->dmabufidx; + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag); + enable_dma(dev->dma); sm_int_freq(sm); sti(); - if (new_ptt && !SCSTATE->ptt) { - /* starting to transmit */ - disable_dma(dev->dma); - SCSTATE->dmabufidx = 0; - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2)); - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, SCSTATE->dmabuf, - SCSTATE->dmabuflen/2)); - setup_dma_dsp(dev, sm, 1); - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, SCSTATE->dmabuf + - SCSTATE->dmabuflen/2, - SCSTATE->dmabuflen/2)); - } else if (SCSTATE->ptt == 1 && !new_ptt) { + if (sm->dma.ptt_cnt <= 0) { + dma_receive(sm, curfrag); + if (hdlcdrv_ptt(&sm->hdrv)) { + /* starting to transmit */ + disable_dma(dev->dma); + dma_start_transmit(sm); + setup_dma_dsp(dev, sm, 1); + dma_transmit(sm); + } + } else if (dma_end_transmit(sm, curfrag)) { /* stopping transmission */ disable_dma(dev->dma); - SCSTATE->dmabufidx = 0; + sti(); + dma_init_receive(sm); setup_dma_dsp(dev, sm, 0); - SCSTATE->ptt = 0; - } else if (SCSTATE->ptt) { - SCSTATE->ptt--; - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, buf, SCSTATE->dmabuflen/2)); } else { - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2)); + dma_transmit(sm); hdlcdrv_arbitrate(dev, &sm->hdrv); } - if (new_ptt) - SCSTATE->ptt = 2; sm_output_status(sm); hdlcdrv_transmitter(dev, &sm->hdrv); hdlcdrv_receiver(dev, &sm->hdrv); + } /* --------------------------------------------------------------------- */ @@ -393,6 +367,7 @@ static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs) static int sbc_open(struct device *dev, struct sm_state *sm) { int err; + unsigned int dmasz, u; if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) { printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", @@ -409,12 +384,17 @@ static int sbc_open(struct device *dev, struct sm_state *sm) /* * check if a card is available */ - if (!reset_dsp(dev)) + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", + sm_drvname, dev->base_addr); return -ENODEV; + } write_dsp(dev, SBC_GET_REVISION); if (!read_dsp(dev, &SCSTATE->revhi) || !read_dsp(dev, &SCSTATE->revlo)) return -ENODEV; + printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, + SCSTATE->revhi, SCSTATE->revlo); if (SCSTATE->revhi < 2) { printk(KERN_ERR "%s: your card is an antiquity, at least DSP " "rev 2.00 required\n", sm_drvname); @@ -435,9 +415,19 @@ static int sbc_open(struct device *dev, struct sm_state *sm) /* * initialize some variables */ - if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) + dma_init_receive(sm); + dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; + if (sm->dma.i16bit) + dmasz <<= 1; + u = NUM_FRAGMENTS * sm->dma.ofragsz; + if (sm->dma.o16bit) + u <<= 1; + if (u > dmasz) + dmasz = u; + if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) return -ENOMEM; - SCSTATE->dmabufidx = SCSTATE->ptt = 0; + dma_init_transmit(sm); + dma_init_receive(sm); memset(&sm->m, 0, sizeof(sm->m)); memset(&sm->d, 0, sizeof(sm->d)); @@ -447,13 +437,13 @@ static int sbc_open(struct device *dev, struct sm_state *sm) sm->mode_rx->init(sm); if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + kfree_s(sm->dma.obuf, dmasz); return -EBUSY; } if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, sm->hwdrv->hw_name, dev)) { free_dma(dev->dma); - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + kfree_s(sm->dma.obuf, dmasz); return -EBUSY; } request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name); @@ -475,7 +465,7 @@ static int sbc_close(struct device *dev, struct sm_state *sm) free_irq(dev->irq, dev); free_dma(dev->dma); release_region(dev->base_addr, SBC_EXTENT); - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + kfree(sm->dma.obuf); return 0; } @@ -486,7 +476,6 @@ static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode) char *cp = strchr(mode, '.'); const struct modem_tx_info **mtp = sm_modem_tx_table; const struct modem_rx_info **mrp; - int dv; if (!strcmp(mode, "off")) { sm->mode_tx = NULL; @@ -507,12 +496,16 @@ static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode) continue; if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100) continue; + if (!(*mtp)->modulator_u8) + continue; for (mrp = sm_modem_rx_table; *mrp; mrp++) { if ((*mrp)->loc_storage > sizeof(sm->d)) { printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", sm_drvname, (*mrp)->name, (*mrp)->loc_storage); continue; } + if (!(*mrp)->demodulator_u8) + continue; if ((*mrp)->name && !strcmp((*mrp)->name, cp) && (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) { sm->mode_tx = *mtp; @@ -521,11 +514,11 @@ static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode) sm->mode_rx->srate); SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/ sm->mode_tx->srate); - dv = lcm(sm->mode_tx->dmabuflenmodulo, - sm->mode_rx->dmabuflenmodulo); - SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1; - SCSTATE->dmabuflen /= dv; - SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */ + sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; + sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + sm->dma.i16bit = sm->dma.o16bit = 0; return 0; } } @@ -633,50 +626,61 @@ const struct hardware_info sm_hw_sbc = { static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm) { unsigned long flags; - unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf); - unsigned long dmabuf2addr = virt_to_bus(SCSTATE->dmabuf2); + unsigned int isamps, osamps; if (!reset_dsp(dev)) { printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); return; } - if (((dmabufaddr & 0xffff) + SCSTATE->dmabuflen > 0x10000) || - ((dmabuf2addr & 0xffff) + 2*(SCSTATE->dmabuflen) > 0x10000)) - panic("sm: DMA buffer violates DMA boundary!"); save_flags(flags); cli(); sbc_int_ack_8bit(dev); sbc_int_ack_16bit(dev); /* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */ - write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */ - write_dsp(dev, SCSTATE->fmt[0]); + write_dsp(dev, SBC_SAMPLE_RATE_IN); + write_dsp(dev, SCSTATE->sr[0] >> 8); + write_dsp(dev, SCSTATE->sr[0] & 0xff); + write_dsp(dev, SBC_SAMPLE_RATE_OUT); + write_dsp(dev, SCSTATE->sr[1] >> 8); + write_dsp(dev, SCSTATE->sr[1] & 0xff); write_dsp(dev, SBC_SPEAKER_ON); - /* - * DMA channel 1 (8bit) does input (capture), - * DMA channel 2 (16bit) does output (playback) - */ - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(dev->dma, dmabufaddr); - set_dma_count(dev->dma, SCSTATE->dmabuflen); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(sm->hdrv.ptt_out.dma2, dmabuf2addr); - set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen); - enable_dma(dev->dma); - enable_dma(sm->hdrv.ptt_out.dma2); - sbc_int_ack_8bit(dev); - sbc_int_ack_16bit(dev); - write_dsp(dev, SBC4_IN8_AI); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); - write_dsp(dev, SBC4_OUT16_AI); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); + if (sm->dma.o16bit) { + /* + * DMA channel 1 (8bit) does input (capture), + * DMA channel 2 (16bit) does output (playback) + */ + isamps = dma_setup(sm, 0, dev->dma) - 1; + osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1; + sbc_int_ack_8bit(dev); + sbc_int_ack_16bit(dev); + write_dsp(dev, SBC4_IN8_AI); + write_dsp(dev, SBC4_MODE_UNS_MONO); + write_dsp(dev, isamps & 0xff); + write_dsp(dev, isamps >> 8); + write_dsp(dev, SBC4_OUT16_AI); + write_dsp(dev, SBC4_MODE_SIGN_MONO); + write_dsp(dev, osamps & 0xff); + write_dsp(dev, osamps >> 8); + } else { + /* + * DMA channel 1 (8bit) does output (playback), + * DMA channel 2 (16bit) does input (capture) + */ + isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; + osamps = dma_setup(sm, 1, dev->dma) - 1; + sbc_int_ack_8bit(dev); + sbc_int_ack_16bit(dev); + write_dsp(dev, SBC4_OUT8_AI); + write_dsp(dev, SBC4_MODE_UNS_MONO); + write_dsp(dev, osamps & 0xff); + write_dsp(dev, osamps >> 8); + write_dsp(dev, SBC4_IN16_AI); + write_dsp(dev, SBC4_MODE_SIGN_MONO); + write_dsp(dev, isamps & 0xff); + write_dsp(dev, isamps >> 8); + } + dma_init_receive(sm); + dma_init_transmit(sm); restore_flags(flags); } @@ -686,41 +690,58 @@ static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev = (struct device *)dev_id; struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned char *buf; - unsigned char *buf2; - unsigned char intsrc; + unsigned char intsrc, pbint = 0, captint = 0; + unsigned int ocfrag, icfrag; + unsigned long flags; if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) return; - buf = SCSTATE->dmabuf; - buf2 = SCSTATE->dmabuf2; + save_flags(flags); + cli(); outb(0x82, DSP_MIXER_ADDR(dev->base_addr)); intsrc = inb(DSP_MIXER_DATA(dev->base_addr)); if (intsrc & 0x01) { sbc_int_ack_8bit(dev); - if (SCSTATE->dmabufidx) - buf += SCSTATE->dmabuflen/2; - SCSTATE->dmabufidx = !SCSTATE->dmabufidx; + if (sm->dma.o16bit) { + captint = 1; + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + dma_ptr(sm, 0, dev->dma, &icfrag); + enable_dma(dev->dma); + } else { + pbint = 1; + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + dma_ptr(sm, 1, dev->dma, &ocfrag); + enable_dma(dev->dma); + } } if (intsrc & 0x02) { sbc_int_ack_16bit(dev); - if (SCSTATE->dma2bufidx) - buf2 += SCSTATE->dmabuflen/2; - SCSTATE->dma2bufidx = !SCSTATE->dma2bufidx; + if (sm->dma.o16bit) { + pbint = 1; + disable_dma(sm->hdrv.ptt_out.dma2); + clear_dma_ff(sm->hdrv.ptt_out.dma2); + dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag); + enable_dma(sm->hdrv.ptt_out.dma2); + } else { + captint = 1; + disable_dma(sm->hdrv.ptt_out.dma2); + clear_dma_ff(sm->hdrv.ptt_out.dma2); + dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag); + enable_dma(sm->hdrv.ptt_out.dma2); + } } + restore_flags(flags); sm_int_freq(sm); sti(); - if (intsrc & 0x02) { - if ((SCSTATE->ptt = hdlcdrv_ptt(&sm->hdrv))) - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, buf2, SCSTATE->dmabuflen/2)); - else - time_exec(sm->debug_vals.mod_cyc, - memset(buf2, 0x80, SCSTATE->dmabuflen/2)); + if (pbint) { + if (dma_end_transmit(sm, ocfrag)) + dma_clear_transmit(sm); + dma_transmit(sm); } - if (intsrc & 0x01) { - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2)); + if (captint) { + dma_receive(sm, icfrag); hdlcdrv_arbitrate(dev, &sm->hdrv); } sm_output_status(sm); @@ -749,12 +770,17 @@ static int sbcfdx_open(struct device *dev, struct sm_state *sm) /* * check if a card is available */ - if (!reset_dsp(dev)) + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", + sm_drvname, dev->base_addr); return -ENODEV; + } write_dsp(dev, SBC_GET_REVISION); if (!read_dsp(dev, &SCSTATE->revhi) || !read_dsp(dev, &SCSTATE->revlo)) return -ENODEV; + printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, + SCSTATE->revhi, SCSTATE->revlo); if (SCSTATE->revhi < 4) { printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname); return -ENODEV; @@ -766,13 +792,14 @@ static int sbcfdx_open(struct device *dev, struct sm_state *sm) /* * initialize some variables */ - if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) + if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) return -ENOMEM; - if (!(SCSTATE->dmabuf2 = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { + kfree(sm->dma.ibuf); return -ENOMEM; } - SCSTATE->dmabufidx = SCSTATE->dma2bufidx = SCSTATE->ptt = 0; + dma_init_transmit(sm); + dma_init_receive(sm); memset(&sm->m, 0, sizeof(sm->m)); memset(&sm->d, 0, sizeof(sm->d)); @@ -782,20 +809,20 @@ static int sbcfdx_open(struct device *dev, struct sm_state *sm) sm->mode_rx->init(sm); if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); return -EBUSY; } if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); free_dma(dev->dma); return -EBUSY; } if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, sm->hwdrv->hw_name, dev)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); free_dma(dev->dma); free_dma(sm->hdrv.ptt_out.dma2); return -EBUSY; @@ -821,8 +848,8 @@ static int sbcfdx_close(struct device *dev, struct sm_state *sm) free_dma(dev->dma); free_dma(sm->hdrv.ptt_out.dma2); release_region(dev->base_addr, SBC_EXTENT); - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); return 0; } @@ -833,7 +860,6 @@ static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) char *cp = strchr(mode, '.'); const struct modem_tx_info **mtp = sm_modem_tx_table; const struct modem_rx_info **mrp; - int dv; if (!strcmp(mode, "off")) { sm->mode_tx = NULL; @@ -867,13 +893,25 @@ static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) sm->mode_rx = *mrp; SCSTATE->sr[0] = sm->mode_rx->srate; SCSTATE->sr[1] = sm->mode_tx->srate; - dv = lcm(sm->mode_tx->dmabuflenmodulo, - sm->mode_rx->dmabuflenmodulo); - if (dv & 1) - dv <<= 1; /* dmabuflen must be a multiple of 4 */ - SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1; - SCSTATE->dmabuflen /= dv; - SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */ + sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; + sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) { + sm->dma.i16bit = 1; + sm->dma.o16bit = 0; + sm->dma.ifragsz <<= 1; + } else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) { + sm->dma.i16bit = 0; + sm->dma.o16bit = 1; + sm->dma.ofragsz <<= 1; + } else { + printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, + sm->mode_rx->name, sm->mode_tx->name); + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return -EINVAL; + } return 0; } } diff --git a/drivers/net/soundmodem/sm_wss.c b/drivers/net/soundmodem/sm_wss.c index 0123a9aa4c57..021ecc165211 100644 --- a/drivers/net/soundmodem/sm_wss.c +++ b/drivers/net/soundmodem/sm_wss.c @@ -26,12 +26,14 @@ */ #include +#include #include #include #include #include #include #include "sm.h" +#include "smdma.h" /* --------------------------------------------------------------------- */ @@ -81,12 +83,6 @@ struct sc_state_wss { unsigned char revwss, revid, revv, revcid; unsigned char fmt[2]; unsigned char crystal; - unsigned int dmabuflen; - unsigned char *dmabuf; - unsigned char dmabufidx; - unsigned char ptt; - /* Full Duplex extensions */ - unsigned char *dmabuf2; }; #define SCSTATE ((struct sc_state_wss *)(&sm->hw)) @@ -156,8 +152,8 @@ static int wss_srate_index(int srate) /* --------------------------------------------------------------------- */ -static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, - unsigned char fmt, char fdx, char fullcalib) +static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, unsigned char fmt, + unsigned char fmt2, char fdx, char fullcalib) { unsigned long time; unsigned long flags; @@ -167,7 +163,7 @@ static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, /* Clock and data format register */ write_codec(dev, 0x48, fmt); if (SCSTATE->crystal) { - write_codec(dev, 0x5c, fmt & 0xf0); + write_codec(dev, 0x5c, fmt2 & 0xf0); /* MCE and interface config reg */ write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0)); } else @@ -306,7 +302,7 @@ static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, write_codec(dev, 0x1d, 0x00); /* right out no att */ } - if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], fdx, 1)) + if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1)) goto codec_err; write_codec(dev, 0, reg0); /* left input control */ @@ -345,18 +341,14 @@ static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send) { unsigned long flags; static const unsigned char codecmode[2] = { 0x0e, 0x0d }; - static const unsigned char dmamode[2] = { - DMA_MODE_READ | DMA_MODE_AUTOINIT, - DMA_MODE_WRITE | DMA_MODE_AUTOINIT - }; unsigned char oldcodecmode; long abrt; - unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf); + unsigned char fmt; + unsigned int numsamps; - if ((dmabufaddr & 0xffffu) + SCSTATE->dmabuflen > 0x10000) - panic("%s: DMA buffer violates DMA boundary!", sm_drvname); send = !!send; - save_flags(flags); + fmt = SCSTATE->fmt[send]; + save_flags(flags); cli(); /* * perform the final DMA sequence to disable the codec request @@ -365,28 +357,18 @@ static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send) write_codec(dev, 9, 0xc); /* disable codec */ wss_ack_int(dev); if (read_codec(dev, 11) & 0x10) { - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - set_dma_mode(dev->dma, dmamode[oldcodecmode & 1]); - set_dma_addr(dev->dma, dmabufaddr); - set_dma_count(dev->dma, SCSTATE->dmabuflen); - enable_dma(dev->dma); + dma_setup(sm, oldcodecmode & 1, dev->dma); abrt = 0; while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000)); } - disable_dma(dev->dma); - if (read_codec(dev, 0x8) != SCSTATE->fmt[send]) - wss_set_codec_fmt(dev, sm, SCSTATE->fmt[send], 0, 0); - clear_dma_ff(dev->dma); - set_dma_mode(dev->dma, dmamode[send]); - set_dma_addr(dev->dma, dmabufaddr); - set_dma_count(dev->dma, SCSTATE->dmabuflen); - enable_dma(dev->dma); - write_codec(dev, 15, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_codec(dev, 14, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); + if (read_codec(dev, 0x8) != fmt) + wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); + numsamps = dma_setup(sm, send, dev->dma) - 1; + write_codec(dev, 15, numsamps & 0xff); + write_codec(dev, 14, numsamps >> 8); if (SCSTATE->crystal) { - write_codec(dev, 31, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_codec(dev, 30, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); + write_codec(dev, 31, numsamps & 0xff); + write_codec(dev, 30, numsamps >> 8); } write_codec(dev, 9, codecmode[send]); restore_flags(flags); @@ -398,74 +380,45 @@ static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev = (struct device *)dev_id; struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned char new_ptt; - unsigned char *buf; - int dmares; + unsigned int curfrag; + unsigned int nums; if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || sm->hdrv.magic != HDLCDRV_MAGIC) return; - new_ptt = hdlcdrv_ptt(&sm->hdrv); cli(); wss_ack_int(dev); disable_dma(dev->dma); clear_dma_ff(dev->dma); - dmares = get_dma_residue(dev->dma); - if (dmares <= 0) - dmares = SCSTATE->dmabuflen; - buf = SCSTATE->dmabuf; - if (dmares > SCSTATE->dmabuflen/2) { - buf += SCSTATE->dmabuflen/2; - dmares -= SCSTATE->dmabuflen/2; - } -#ifdef SM_DEBUG - if (!sm->debug_vals.dma_residue || - dmares < sm->debug_vals.dma_residue) - sm->debug_vals.dma_residue = dmares; -#endif /* SM_DEBUG */ - dmares--; - write_codec(dev, 15, dmares & 0xff); - write_codec(dev, 14, dmares >> 8); + nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1; + write_codec(dev, 15, nums & 0xff); + write_codec(dev, 14, nums >> 8); if (SCSTATE->crystal) { - write_codec(dev, 31, dmares & 0xff); - write_codec(dev, 30, dmares >> 8); + write_codec(dev, 31, nums & 0xff); + write_codec(dev, 30, nums >> 8); } enable_dma(dev->dma); sm_int_freq(sm); sti(); - if (new_ptt && !SCSTATE->ptt) { - /* starting to transmit */ - disable_dma(dev->dma); - sti(); - SCSTATE->dmabufidx = 0; - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2)); - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, SCSTATE->dmabuf, - SCSTATE->dmabuflen/2)); - setup_dma_wss(dev, sm, 1); - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, SCSTATE->dmabuf + - SCSTATE->dmabuflen/2, - SCSTATE->dmabuflen/2)); - } else if (SCSTATE->ptt == 1 && !new_ptt) { + if (sm->dma.ptt_cnt <= 0) { + dma_receive(sm, curfrag); + if (hdlcdrv_ptt(&sm->hdrv)) { + /* starting to transmit */ + disable_dma(dev->dma); + dma_start_transmit(sm); + setup_dma_wss(dev, sm, 1); + dma_transmit(sm); + } + } else if (dma_end_transmit(sm, curfrag)) { /* stopping transmission */ disable_dma(dev->dma); sti(); - SCSTATE->dmabufidx = 0; + dma_init_receive(sm); setup_dma_wss(dev, sm, 0); - SCSTATE->ptt = 0; - } else if (SCSTATE->ptt) { - SCSTATE->ptt--; - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, buf, SCSTATE->dmabuflen/2)); } else { - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2)); + dma_transmit(sm); hdlcdrv_arbitrate(dev, &sm->hdrv); } - if (new_ptt) - SCSTATE->ptt = 2; sm_output_status(sm); hdlcdrv_transmitter(dev, &sm->hdrv); hdlcdrv_receiver(dev, &sm->hdrv); @@ -475,6 +428,8 @@ static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs) static int wss_open(struct device *dev, struct sm_state *sm) { + unsigned int dmasz, u; + if (sizeof(sm->m) < sizeof(struct sc_state_wss)) { printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", sizeof(struct sc_state_wss), sizeof(sm->m)); @@ -495,9 +450,19 @@ static int wss_open(struct device *dev, struct sm_state *sm) /* * initialize some variables */ - if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) + dma_init_receive(sm); + dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; + if (sm->dma.i16bit) + dmasz <<= 1; + u = NUM_FRAGMENTS * sm->dma.ofragsz; + if (sm->dma.o16bit) + u <<= 1; + if (u > dmasz) + dmasz = u; + if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) return -ENOMEM; - SCSTATE->dmabufidx = SCSTATE->ptt = 0; + dma_init_transmit(sm); + dma_init_receive(sm); memset(&sm->m, 0, sizeof(sm->m)); memset(&sm->d, 0, sizeof(sm->d)); @@ -507,13 +472,13 @@ static int wss_open(struct device *dev, struct sm_state *sm) sm->mode_rx->init(sm); if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + kfree_s(sm->dma.obuf, dmasz); return -EBUSY; } if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, sm->hwdrv->hw_name, dev)) { free_dma(dev->dma); - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + kfree_s(sm->dma.obuf, dmasz); return -EBUSY; } request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); @@ -535,7 +500,7 @@ static int wss_close(struct device *dev, struct sm_state *sm) free_irq(dev->irq, dev); free_dma(dev->dma); release_region(dev->base_addr, WSS_EXTENT); - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + kfree(sm->dma.obuf); return 0; } @@ -546,7 +511,7 @@ static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode) char *cp = strchr(mode, '.'); const struct modem_tx_info **mtp = sm_modem_tx_table; const struct modem_rx_info **mrp; - int i, j, dv; + int i, j; if (!strcmp(mode, "off")) { sm->mode_tx = NULL; @@ -579,11 +544,57 @@ static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode) sm->mode_rx = *mrp; SCSTATE->fmt[0] = j; SCSTATE->fmt[1] = i; - dv = lcm(sm->mode_tx->dmabuflenmodulo, - sm->mode_rx->dmabuflenmodulo); - SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1; - SCSTATE->dmabuflen /= dv; - SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */ + sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; + sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + /* prefer same data format if possible to minimize switching times */ + sm->dma.i16bit = sm->dma.o16bit = 2; + if (sm->mode_rx->srate == sm->mode_tx->srate) { + if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16) + sm->dma.i16bit = sm->dma.o16bit = 1; + else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8) + sm->dma.i16bit = sm->dma.o16bit = 0; + } + if (sm->dma.i16bit == 2) { + if (sm->mode_rx->demodulator_s16) + sm->dma.i16bit = 1; + else if (sm->mode_rx->demodulator_u8) + sm->dma.i16bit = 0; + } + if (sm->dma.o16bit == 2) { + if (sm->mode_tx->modulator_s16) + sm->dma.o16bit = 1; + else if (sm->mode_tx->modulator_u8) + sm->dma.o16bit = 0; + } + if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { + printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, + sm->mode_rx->name, sm->mode_tx->name); + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return -EINVAL; + } +#ifdef __BIG_ENDIAN + /* big endian 16bit only works on crystal cards... */ + if (sm->dma.i16bit) { + SCSTATE->fmt[0] |= 0xc0; + sm->dma.ifragsz <<= 1; + } + if (sm->dma.o16bit) { + SCSTATE->fmt[1] |= 0xc0; + sm->dma.ofragsz <<= 1; + } +#else /* __BIG_ENDIAN */ + if (sm->dma.i16bit) { + SCSTATE->fmt[0] |= 0x40; + sm->dma.ifragsz <<= 1; + } + if (sm->dma.o16bit) { + SCSTATE->fmt[1] |= 0x40; + sm->dma.ofragsz <<= 1; + } +#endif /* __BIG_ENDIAN */ return 0; } } @@ -663,12 +674,8 @@ static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm) unsigned long flags; unsigned char oldcodecmode, codecdma; long abrt; - unsigned long dmabufaddr1 = virt_to_bus(SCSTATE->dmabuf); - unsigned long dmabufaddr2 = virt_to_bus(SCSTATE->dmabuf2); - - if (((dmabufaddr1 & 0xffffu) + SCSTATE->dmabuflen > 0x10000) || - ((dmabufaddr2 & 0xffffu) + SCSTATE->dmabuflen > 0x10000)) - panic("%s: DMA buffer violates DMA boundary!", sm_drvname); + unsigned int osamps, isamps; + save_flags(flags); cli(); /* @@ -678,39 +685,19 @@ static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm) write_codec(dev, 9, 0); /* disable codec DMA */ wss_ack_int(dev); if ((codecdma = read_codec(dev, 11)) & 0x10) { - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(dev->dma, dmabufaddr1); - set_dma_count(dev->dma, SCSTATE->dmabuflen); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(sm->hdrv.ptt_out.dma2, dmabufaddr2); - set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen); - enable_dma(dev->dma); - enable_dma(sm->hdrv.ptt_out.dma2); + dma_setup(sm, 1, dev->dma); + dma_setup(sm, 0, sm->hdrv.ptt_out.dma2); abrt = 0; - while (((codecdma = read_codec(dev, 11)) & 0x10) || - ((++abrt) >= 0x10000)); + while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000)); } - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(dev->dma, dmabufaddr1); - set_dma_count(dev->dma, SCSTATE->dmabuflen); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(sm->hdrv.ptt_out.dma2, dmabufaddr2); - set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen); - enable_dma(dev->dma); - enable_dma(sm->hdrv.ptt_out.dma2); - write_codec(dev, 15, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_codec(dev, 14, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); + wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1); + osamps = dma_setup(sm, 1, dev->dma) - 1; + isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; + write_codec(dev, 15, osamps & 0xff); + write_codec(dev, 14, osamps >> 8); if (SCSTATE->crystal) { - write_codec(dev, 31, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff); - write_codec(dev, 30, ((SCSTATE->dmabuflen >> 1) - 1) >> 8); + write_codec(dev, 31, isamps & 0xff); + write_codec(dev, 30, isamps >> 8); } write_codec(dev, 9, 3); restore_flags(flags); @@ -722,68 +709,74 @@ static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev = (struct device *)dev_id; struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned char *buf1; - unsigned char *buf2; unsigned long flags; - int dmares1, dmares2; + unsigned char cry_int_src; + unsigned icfrag, ocfrag, isamps, osamps; if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || sm->hdrv.magic != HDLCDRV_MAGIC) return; save_flags(flags); cli(); - if (SCSTATE->crystal && (!(read_codec(dev, 0x18) & 0x20))) { - /* only regard Crystal Playback interrupts! */ + if (SCSTATE->crystal) { + /* Crystal has an essentially different interrupt handler! */ + cry_int_src = read_codec(dev, 0x18); wss_ack_int(dev); + if (cry_int_src & 0x10) { /* playback interrupt */ + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; + write_codec(dev, 15, osamps & 0xff); + write_codec(dev, 14, osamps >> 8); + enable_dma(dev->dma); + } + if (cry_int_src & 0x20) { /* capture interrupt */ + disable_dma(sm->hdrv.ptt_out.dma2); + clear_dma_ff(sm->hdrv.ptt_out.dma2); + isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; + write_codec(dev, 31, isamps & 0xff); + write_codec(dev, 30, isamps >> 8); + enable_dma(sm->hdrv.ptt_out.dma2); + } + restore_flags(flags); + sm_int_freq(sm); + sti(); + if (cry_int_src & 0x10) { + if (dma_end_transmit(sm, ocfrag)) + dma_clear_transmit(sm); + dma_transmit(sm); + } + if (cry_int_src & 0x20) { + dma_receive(sm, icfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); + } + sm_output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); return; } wss_ack_int(dev); disable_dma(dev->dma); disable_dma(sm->hdrv.ptt_out.dma2); clear_dma_ff(dev->dma); - dmares1 = get_dma_residue(dev->dma); clear_dma_ff(sm->hdrv.ptt_out.dma2); - dmares2 = get_dma_residue(sm->hdrv.ptt_out.dma2); - if (dmares1 <= 0) - dmares1 = SCSTATE->dmabuflen; - buf1 = SCSTATE->dmabuf; - if (dmares1 > SCSTATE->dmabuflen/2) { - buf1 += SCSTATE->dmabuflen/2; - dmares1 -= SCSTATE->dmabuflen/2; - } - if (dmares2 <= 0) - dmares2 = SCSTATE->dmabuflen; - buf2 = SCSTATE->dmabuf2; - if (dmares2 > SCSTATE->dmabuflen/2) { - buf2 += SCSTATE->dmabuflen/2; - dmares2 -= SCSTATE->dmabuflen/2; - } -#ifdef SM_DEBUG - if (!sm->debug_vals.dma_residue || - dmares1 < sm->debug_vals.dma_residue) - sm->debug_vals.dma_residue = dmares1; -#endif /* SM_DEBUG */ - dmares1--; - dmares2--; - write_codec(dev, 15, dmares1 & 0xff); - write_codec(dev, 14, dmares1 >> 8); + osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; + isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; + write_codec(dev, 15, osamps & 0xff); + write_codec(dev, 14, osamps >> 8); if (SCSTATE->crystal) { - write_codec(dev, 31, dmares2 & 0xff); - write_codec(dev, 30, dmares2 >> 8); + write_codec(dev, 31, isamps & 0xff); + write_codec(dev, 30, isamps >> 8); } enable_dma(dev->dma); enable_dma(sm->hdrv.ptt_out.dma2); restore_flags(flags); sm_int_freq(sm); sti(); - if ((SCSTATE->ptt = hdlcdrv_ptt(&sm->hdrv))) - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator(sm, buf1, SCSTATE->dmabuflen/2)); - else - time_exec(sm->debug_vals.mod_cyc, - memset(buf1, 0x80, SCSTATE->dmabuflen/2)); - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator(sm, buf2, SCSTATE->dmabuflen/2)); + if (dma_end_transmit(sm, ocfrag)) + dma_clear_transmit(sm); + dma_transmit(sm); + dma_receive(sm, icfrag); hdlcdrv_arbitrate(dev, &sm->hdrv); sm_output_status(sm); hdlcdrv_transmitter(dev, &sm->hdrv); @@ -809,13 +802,14 @@ static int wssfdx_open(struct device *dev, struct sm_state *sm) /* * initialize some variables */ - if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) + if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) return -ENOMEM; - if (!(SCSTATE->dmabuf2 = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); + if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { + kfree(sm->dma.ibuf); return -ENOMEM; } - SCSTATE->dmabufidx = SCSTATE->ptt = 0; + dma_init_transmit(sm); + dma_init_receive(sm); memset(&sm->m, 0, sizeof(sm->m)); memset(&sm->d, 0, sizeof(sm->d)); @@ -825,20 +819,20 @@ static int wssfdx_open(struct device *dev, struct sm_state *sm) sm->mode_rx->init(sm); if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); return -EBUSY; } if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); free_dma(dev->dma); return -EBUSY; } if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, sm->hwdrv->hw_name, dev)) { - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); free_dma(dev->dma); free_dma(sm->hdrv.ptt_out.dma2); return -EBUSY; @@ -858,13 +852,14 @@ static int wssfdx_close(struct device *dev, struct sm_state *sm) * disable interrupts */ disable_dma(dev->dma); + disable_dma(sm->hdrv.ptt_out.dma2); write_codec(dev, 9, 0xc); /* disable codec */ free_irq(dev->irq, dev); free_dma(dev->dma); free_dma(sm->hdrv.ptt_out.dma2); release_region(dev->base_addr, WSS_EXTENT); - kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen); - kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); return 0; } @@ -875,7 +870,7 @@ static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) char *cp = strchr(mode, '.'); const struct modem_tx_info **mtp = sm_modem_tx_table; const struct modem_rx_info **mrp; - int i, dv; + int i; if (!strcmp(mode, "off")) { sm->mode_tx = NULL; @@ -907,11 +902,37 @@ static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) sm->mode_tx = *mtp; sm->mode_rx = *mrp; SCSTATE->fmt[0] = SCSTATE->fmt[1] = i; - dv = lcm(sm->mode_tx->dmabuflenmodulo, - sm->mode_rx->dmabuflenmodulo); - SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1; - SCSTATE->dmabuflen /= dv; - SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */ + sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + sm->dma.i16bit = sm->dma.o16bit = 2; + if (sm->mode_rx->demodulator_s16) { + sm->dma.i16bit = 1; + sm->dma.ifragsz <<= 1; +#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ + SCSTATE->fmt[0] |= 0xc0; +#else /* __BIG_ENDIAN */ + SCSTATE->fmt[0] |= 0x40; +#endif /* __BIG_ENDIAN */ + } else if (sm->mode_rx->demodulator_u8) + sm->dma.i16bit = 0; + if (sm->mode_tx->modulator_s16) { + sm->dma.o16bit = 1; + sm->dma.ofragsz <<= 1; +#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ + SCSTATE->fmt[1] |= 0xc0; +#else /* __BIG_ENDIAN */ + SCSTATE->fmt[1] |= 0x40; +#endif /* __BIG_ENDIAN */ + } else if (sm->mode_tx->modulator_u8) + sm->dma.o16bit = 0; + if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { + printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, + sm->mode_rx->name, sm->mode_tx->name); + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return -EINVAL; + } return 0; } } diff --git a/drivers/net/soundmodem/smdma.h b/drivers/net/soundmodem/smdma.h new file mode 100644 index 000000000000..27cea09e768c --- /dev/null +++ b/drivers/net/soundmodem/smdma.h @@ -0,0 +1,210 @@ +/*****************************************************************************/ + +/* + * smdma.h -- soundcard radio modem driver dma buffer routines. + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +#ifndef _SMDMA_H +#define _SMDMA_H + +/* ---------------------------------------------------------------------- */ + +#include "sm.h" + +/* ---------------------------------------------------------------------- */ + +#define DMA_MODE_AUTOINIT 0x10 +#define NUM_FRAGMENTS 4 + +/* --------------------------------------------------------------------- */ +/* + * ===================== DMA buffer management =========================== + */ + +/* + * returns the number of samples per fragment + */ +extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr) +{ + if (send) { + disable_dma(dmanr); + clear_dma_ff(dmanr); + set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf)); + set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS); + enable_dma(dmanr); + if (sm->dma.o16bit) + return sm->dma.ofragsz/2; + return sm->dma.ofragsz; + } else { + disable_dma(dmanr); + clear_dma_ff(dmanr); + set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT); + set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf)); + set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS); + enable_dma(dmanr); + if (sm->dma.i16bit) + return sm->dma.ifragsz/2; + return sm->dma.ifragsz; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr, + unsigned int *curfrag) +{ + unsigned int dmaptr, sz, frg, offs; + + dmaptr = get_dma_residue(dmanr); + if (send) { + sz = sm->dma.ofragsz * NUM_FRAGMENTS; + if (dmaptr == 0 || dmaptr > sz) + dmaptr = sz; + dmaptr--; + frg = dmaptr / sm->dma.ofragsz; + offs = (dmaptr % sm->dma.ofragsz) + 1; + *curfrag = NUM_FRAGMENTS - 1 - frg; +#ifdef SM_DEBUG + if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) + sm->debug_vals.dma_residue = offs; +#endif /* SM_DEBUG */ + if (sm->dma.o16bit) + return offs/2; + return offs; + } else { + sz = sm->dma.ifragsz * NUM_FRAGMENTS; + if (dmaptr == 0 || dmaptr > sz) + dmaptr = sz; + dmaptr--; + frg = dmaptr / sm->dma.ifragsz; + offs = (dmaptr % sm->dma.ifragsz) + 1; + *curfrag = NUM_FRAGMENTS - 1 - frg; +#ifdef SM_DEBUG + if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) + sm->debug_vals.dma_residue = offs; +#endif /* SM_DEBUG */ + if (sm->dma.i16bit) + return offs/2; + return offs; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag) +{ + unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS; + + sm->dma.ofragptr = curfrag; + if (sm->dma.ptt_cnt <= 0) { + sm->dma.ptt_cnt = 0; + return 0; + } + sm->dma.ptt_cnt -= diff; + if (sm->dma.ptt_cnt <= 0) { + sm->dma.ptt_cnt = 0; + return -1; + } + return 0; +} + +extern __inline__ void dma_transmit(struct sm_state *sm) +{ + void *p; + + while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) { + p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz * + ((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS); + if (sm->dma.o16bit) { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2)); + } else { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz)); + } + sm->dma.ptt_cnt++; + } +} + +extern __inline__ void dma_init_transmit(struct sm_state *sm) +{ + sm->dma.ofragptr = 0; + sm->dma.ptt_cnt = 0; +} + +extern __inline__ void dma_start_transmit(struct sm_state *sm) +{ + sm->dma.ofragptr = 0; + if (sm->dma.o16bit) { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2)); + } else { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz)); + } + sm->dma.ptt_cnt = 1; +} + +extern __inline__ void dma_clear_transmit(struct sm_state *sm) +{ + sm->dma.ptt_cnt = 0; + memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS); +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag) +{ + void *p; + + while (sm->dma.ifragptr != curfrag) { + if (sm->dma.ifragptr) + p = (unsigned char *)sm->dma.ibuf + + sm->dma.ifragsz * sm->dma.ifragptr; + else { + p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz; + memcpy(p, sm->dma.ibuf, sm->dma.ifragsz); + } + if (sm->dma.o16bit) { + time_exec(sm->debug_vals.demod_cyc, + sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2)); + } else { + time_exec(sm->debug_vals.demod_cyc, + sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz)); + } + sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS; + } +} + +extern __inline__ void dma_init_receive(struct sm_state *sm) +{ + sm->dma.ifragptr = 0; +} + +/* --------------------------------------------------------------------- */ +#endif /* _SMDMA_H */ + + + diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 2aae7a2db393..7118164b6802 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -56,7 +56,9 @@ struct pci_dev_info dev_info[] = { DEVICE( ATI, ATI_68800, "68800AX"), DEVICE( ATI, ATI_215CT222, "215CT222"), DEVICE( ATI, ATI_210888CX, "210888CX"), + DEVICE( ATI, ATI_215GT, "Mach64 GT (Rage II)"), DEVICE( ATI, ATI_210888GX, "210888GX"), + DEVICE( ATI, ATI_264VT, "Mach64 VT"), DEVICE( VLSI, VLSI_82C592, "82C592-FC1"), DEVICE( VLSI, VLSI_82C593, "82C593-FC1"), DEVICE( VLSI, VLSI_82C594, "82C594-AFC2"), @@ -76,6 +78,7 @@ struct pci_dev_info dev_info[] = { DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), DEVICE( DEC, DEC_FDDI, "DEFPA"), DEVICE( DEC, DEC_TULIP_PLUS, "DC21041"), + DEVICE( DEC, DEC_21142, "DC21142"), DEVICE( DEC, DEC_21052, "DC21052"), DEVICE( DEC, DEC_21152, "DC21152"), DEVICE( CIRRUS, CIRRUS_7548, "GD 7548"), @@ -83,10 +86,14 @@ struct pci_dev_info dev_info[] = { DEVICE( CIRRUS, CIRRUS_5434_4, "GD 5434"), DEVICE( CIRRUS, CIRRUS_5434_8, "GD 5434"), DEVICE( CIRRUS, CIRRUS_5436, "GD 5436"), + DEVICE( CIRRUS, CIRRUS_5446, "GD 5446"), + DEVICE( CIRRUS, CIRRUS_5464, "GD 5464"), DEVICE( CIRRUS, CIRRUS_6729, "CL 6729"), DEVICE( CIRRUS, CIRRUS_7542, "CL 7542"), DEVICE( CIRRUS, CIRRUS_7543, "CL 7543"), + DEVICE( CIRRUS, CIRRUS_7541, "CL 7541"), DEVICE( IBM, IBM_82G2675, "82G2675"), + DEVICE( IBM, IBM_82351, "82351"), DEVICE( WD, WD_7197, "WD 7197"), DEVICE( AMD, AMD_LANCE, "79C970"), DEVICE( AMD, AMD_SCSI, "53C974"), @@ -101,6 +108,7 @@ struct pci_dev_info dev_info[] = { DEVICE( CT, CT_65545, "65545"), DEVICE( CT, CT_65548, "65548"), DEVICE( CT, CT_65550, "65550"), + DEVICE( CT, CT_65554, "65554"), DEVICE( MIRO, MIRO_36050, "ZR36050"), DEVICE( FD, FD_36C70, "TMC-18C30"), DEVICE( SI, SI_6201, "6201"), @@ -112,7 +120,10 @@ struct pci_dev_info dev_info[] = { DEVICE( SI, SI_601, "85C601"), DEVICE( SI, SI_5511, "85C5511"), DEVICE( SI, SI_5513, "85C5513"), + DEVICE( SI, SI_5571, "5571"), + DEVICE( SI, SI_7001, "7001"), DEVICE( HP, HP_J2585A, "J2585A"), + DEVICE( HP, HP_J2585B, "J2585B"), DEVICE( PCTECH, PCTECH_RZ1000, "RZ1000 (buggy)"), DEVICE( PCTECH, PCTECH_RZ1001, "RZ1001 (buggy?)"), DEVICE( DPT, DPT, "SmartCache/Raid"), @@ -128,6 +139,9 @@ struct pci_dev_info dev_info[] = { DEVICE( BUSLOGIC, BUSLOGIC_FLASHPOINT, "FlashPoint"), DEVICE( OAK, OAK_OTI107, "OTI107"), DEVICE( WINBOND2, WINBOND2_89C940,"NE2000-PCI"), + DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"), + DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"), + DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"), DEVICE( PROMISE, PROMISE_5300, "DC5030"), DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), @@ -155,11 +169,13 @@ struct pci_dev_info dev_info[] = { DEVICE( ACC, ACC_2056, "2056"), DEVICE( WINBOND, WINBOND_83769, "W83769F"), DEVICE( WINBOND, WINBOND_82C105, "SL82C105"), + DEVICE( WINBOND, WINBOND_83C553, "W83C553"), DEVICE( 3COM, 3COM_3C590, "3C590 10bT"), DEVICE( 3COM, 3COM_3C595TX, "3C595 100bTX"), DEVICE( 3COM, 3COM_3C595T4, "3C595 100bT4"), DEVICE( 3COM, 3COM_3C595MII, "3C595 100b-MII"), DEVICE( 3COM, 3COM_3C900TPO, "3C900 10bTPO"), + DEVICE( 3COM, 3COM_3C900COMBO,"3C900 10b Combo"), DEVICE( 3COM, 3COM_3C905TX, "3C905 100bTX"), DEVICE( AL, AL_M1445, "M1445"), DEVICE( AL, AL_M1449, "M1449"), @@ -171,18 +187,24 @@ struct pci_dev_info dev_info[] = { DEVICE( AL, AL_M4803, "M4803"), DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2070, "Magicgraph NM2070"), DEVICE( ASP, ASP_ABP940, "ABP940"), + DEVICE( ASP, ASP_ABP940U, "ABP940U"), DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( CERN, CERN_SPSB_PCI, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( IMS, IMS_8849, "8849"), DEVICE( TEKRAM2, TEKRAM2_690c, "DC690c"), + DEVICE( TUNDRA, TUNDRA_CA91C042,"CA91C042 Universe"), DEVICE( AMCC, AMCC_MYRINET, "Myrinet PCI (M2-PCI-32)"), + DEVICE( AMCC, AMCC_S5933, "S5933"), DEVICE( INTERG, INTERG_1680, "IGA-1680"), DEVICE( INTERG, INTERG_1682, "IGA-1682"), DEVICE( REALTEK, REALTEK_8029, "8029"), DEVICE( INIT, INIT_320P, "320 P"), DEVICE( VIA, VIA_82C505, "VT 82C505"), DEVICE( VIA, VIA_82C561, "VT 82C561"), + DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C576, "VT 82C576 3V"), + DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"), + DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C416, "VT 82C416MV"), DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"), DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"), @@ -200,19 +222,22 @@ struct pci_dev_info dev_info[] = { DEVICE( VORTEX, VORTEX_GDT6555, "GDT 6555"), DEVICE( EF, EF_ATM_FPGA, "155P-MF1 (FPGA)"), DEVICE( EF, EF_ATM_ASIC, "155P-MF1 (ASIC)"), - DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"), DEVICE( FORE, FORE_PCA200PC, "PCA-200PC"), DEVICE( FORE, FORE_PCA200E, "PCA-200E"), + DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"), DEVICE( PLX, PLX_9060, "PCI9060 i960 bridge"), DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"), DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"), DEVICE( VMIC, VMIC_VME, "VMIVME-7587"), DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"), DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"), + DEVICE( TOSHIBA, TOSHIBA_601, "Laptop"), DEVICE( ZEITNET, ZEITNET_1221, "1221"), DEVICE( ZEITNET, ZEITNET_1225, "1225"), + DEVICE( OMEGA, OMEGA_PCMCIA, "PCMCIA"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), + DEVICE( ZORAN, ZORAN_36120, "ZR36120"), DEVICE( COMPEX, COMPEX_RL2000, "ReadyLink 2000"), DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"), DEVICE( RP, RP8INTF, "RocketPort 8 Intf"), @@ -222,6 +247,8 @@ struct pci_dev_info dev_info[] = { DEVICE( CYCLADES, CYCLOM_Y_Hi, "Cyclom-Y above 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Lo, "Cyclom-Z below 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Hi, "Cyclom-Z above 1Mbyte"), + DEVICE( 3DFX, 3DFX_VOODOO, "Voodoo"), + DEVICE( SIGMADES, SIGMADES_6425, "REALmagic64/GX"), DEVICE( OPTIBASE, OPTIBASE_FORGE, "MPEG Forge"), DEVICE( OPTIBASE, OPTIBASE_FUSION,"MPEG Fusion"), DEVICE( OPTIBASE, OPTIBASE_VPLEX, "VideoPlex"), @@ -230,6 +257,9 @@ struct pci_dev_info dev_info[] = { DEVICE( SYMPHONY, SYMPHONY_101, "82C101"), DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"), DEVICE( 3DLABS, 3DLABS_300SX, "GLINT 300SX"), + DEVICE( 3DLABS, 3DLABS_DELTA, "GLINT Delta"), + DEVICE( 3DLABS, 3DLABS_PERMEDIA,"PERMEDIA"), + DEVICE( AVANCE, AVANCE_ALG2064, "ALG2064i"), DEVICE( AVANCE, AVANCE_2302, "ALG-2302"), DEVICE( S3, S3_ViRGE, "ViRGE"), DEVICE( S3, S3_TRIO, "Trio32/Trio64"), @@ -248,6 +278,8 @@ struct pci_dev_info dev_info[] = { DEVICE( INTEL, INTEL_82378, "82378IB"), DEVICE( INTEL, INTEL_82430, "82430ZX Aries"), BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00), + DEVICE( INTEL, INTEL_82092AA_0,"82092AA PCMCIA bridge"), + DEVICE( INTEL, INTEL_82092AA_1,"82092AA EIDE"), DEVICE( INTEL, INTEL_7116, "SAA7116"), DEVICE( INTEL, INTEL_82596, "82596"), DEVICE( INTEL, INTEL_82865, "82865"), @@ -255,15 +287,19 @@ struct pci_dev_info dev_info[] = { DEVICE( INTEL, INTEL_82437, "82437"), DEVICE( INTEL, INTEL_82371_0, "82371 Triton PIIX"), DEVICE( INTEL, INTEL_82371_1, "82371 Triton PIIX"), - DEVICE( INTEL, INTEL_430MX_0, "Triton I"), - DEVICE( INTEL, INTEL_430MX_1, "Triton I"), + DEVICE( INTEL, INTEL_82371MX, "430MX - 82371MX MPIIX"), + DEVICE( INTEL, INTEL_82437MX, "430MX - 82437MX MTSC"), DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"), + DEVICE( INTEL, INTEL_82439TX, "82439TX"), + DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4"), DEVICE( INTEL, INTEL_82371AB, "82371AB 430TX PIIX4"), + DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4"), + DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 Power Management"), DEVICE( INTEL, INTEL_P6, "Orion P6"), DEVICE( INTEL, INTEL_P6_2, "82450GX Orion P6"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), @@ -508,6 +544,7 @@ const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_BUSLOGIC: return "BusLogic"; case PCI_VENDOR_ID_OAK: return "OAK"; case PCI_VENDOR_ID_WINBOND2: return "Winbond"; + case PCI_VENDOR_ID_MOTOROLA: return "Motorola"; case PCI_VENDOR_ID_PROMISE: return "Promise Technology"; case PCI_VENDOR_ID_N9: return "Number Nine"; case PCI_VENDOR_ID_UMC: return "UMC"; @@ -531,6 +568,7 @@ const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_CERN: return "CERN"; case PCI_VENDOR_ID_IMS: return "IMS"; case PCI_VENDOR_ID_TEKRAM2: return "Tekram"; + case PCI_VENDOR_ID_TUNDRA: return "Tundra"; case PCI_VENDOR_ID_AMCC: return "AMCC"; case PCI_VENDOR_ID_INTERG: return "Intergraphics"; case PCI_VENDOR_ID_REALTEK: return "Realtek"; @@ -547,10 +585,14 @@ const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_MUTECH: return "Mutech"; case PCI_VENDOR_ID_TOSHIBA: return "Toshiba"; case PCI_VENDOR_ID_ZEITNET: return "ZeitNet"; + case PCI_VENDOR_ID_OMEGA: return "Omega Micro"; case PCI_VENDOR_ID_SPECIALIX: return "Specialix"; + case PCI_VENDOR_ID_ZORAN: return "Zoran"; case PCI_VENDOR_ID_COMPEX: return "Compex"; case PCI_VENDOR_ID_RP: return "Comtrol"; case PCI_VENDOR_ID_CYCLADES: return "Cyclades"; + case PCI_VENDOR_ID_3DFX: return "3Dfx"; + case PCI_VENDOR_ID_SIGMADES: return "Sigma Designs"; case PCI_VENDOR_ID_OPTIBASE: return "Optibase"; case PCI_VENDOR_ID_SYMPHONY: return "Symphony"; case PCI_VENDOR_ID_TEKRAM: return "Tekram"; diff --git a/drivers/sbus/char/bwtwo.c b/drivers/sbus/char/bwtwo.c index be68d09c9b22..ae81e12604bf 100644 --- a/drivers/sbus/char/bwtwo.c +++ b/drivers/sbus/char/bwtwo.c @@ -1,4 +1,4 @@ -/* $Id: bwtwo.c,v 1.13 1997/04/14 17:04:55 jj Exp $ +/* $Id: bwtwo.c,v 1.16 1997/06/04 08:27:26 davem Exp $ * bwtwo.c: bwtwo console driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -15,9 +15,11 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include + #include "fb.h" #include "cg_common.h" @@ -91,7 +93,7 @@ bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, vma->vm_page_prot, fb->space); if (r) return -EAGAIN; vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/sbus/char/cgfourteen.c b/drivers/sbus/char/cgfourteen.c index d42a4343cea4..2cb4c21c9669 100644 --- a/drivers/sbus/char/cgfourteen.c +++ b/drivers/sbus/char/cgfourteen.c @@ -1,4 +1,4 @@ -/* $Id: cgfourteen.c,v 1.19 1997/04/14 17:04:57 jj Exp $ +/* $Id: cgfourteen.c,v 1.22 1997/06/04 08:27:27 davem Exp $ * cgfourteen.c: Sun SparcStation console support. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -23,9 +23,10 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include #include "fb.h" #define CG14_MCR_INTENABLE_SHIFT 7 @@ -272,7 +273,7 @@ cg14_mmap (struct inode *inode, struct file *file, page += map_size; } vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c index 5f91c1308cad..e53fcf09e80e 100644 --- a/drivers/sbus/char/cgsix.c +++ b/drivers/sbus/char/cgsix.c @@ -1,4 +1,4 @@ -/* $Id: cgsix.c,v 1.27 1997/04/14 17:04:55 jj Exp $ +/* $Id: cgsix.c,v 1.30 1997/06/04 08:27:28 davem Exp $ * cgsix.c: cgsix frame buffer driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -15,9 +15,10 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include #include "fb.h" #include "cg_common.h" @@ -296,7 +297,7 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, page += map_size; } vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/sbus/char/cgthree.c b/drivers/sbus/char/cgthree.c index ac1265c61323..0e1446c0e5ce 100644 --- a/drivers/sbus/char/cgthree.c +++ b/drivers/sbus/char/cgthree.c @@ -1,4 +1,4 @@ -/* $Id: cgthree.c,v 1.18 1997/04/16 17:51:09 jj Exp $ +/* $Id: cgthree.c,v 1.21 1997/06/04 08:27:29 davem Exp $ * cgtree.c: cg3 frame buffer driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -18,9 +18,10 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include #include "fb.h" #include "cg_common.h" @@ -131,7 +132,7 @@ cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, page += map_size; } vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/sbus/char/creator.c b/drivers/sbus/char/creator.c index 9bf72a3b9bdc..4ff2caf31e2b 100644 --- a/drivers/sbus/char/creator.c +++ b/drivers/sbus/char/creator.c @@ -15,9 +15,10 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include #include "fb.h" __initfunc(void creator_setup (fbinfo_t *fb, int slot, int con_node, unsigned long creator, int creator_io)) diff --git a/drivers/sbus/char/leo.c b/drivers/sbus/char/leo.c index b3ec23867dc0..61e646e9fd17 100644 --- a/drivers/sbus/char/leo.c +++ b/drivers/sbus/char/leo.c @@ -1,4 +1,4 @@ -/* $Id: leo.c,v 1.15 1997/04/14 17:04:54 jj Exp $ +/* $Id: leo.c,v 1.18 1997/06/04 08:27:30 davem Exp $ * leo.c: SUNW,leo 24/8bit frame buffer driver * * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -17,9 +17,10 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include #include "fb.h" #include "cg_common.h" @@ -221,7 +222,7 @@ leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, page += map_size; } vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/sbus/char/suncons.c b/drivers/sbus/char/suncons.c index d1a2dfd45040..1d3815dd33d1 100644 --- a/drivers/sbus/char/suncons.c +++ b/drivers/sbus/char/suncons.c @@ -1,4 +1,4 @@ -/* $Id: suncons.c,v 1.62 1997/05/02 22:32:32 davem Exp $ +/* $Id: suncons.c,v 1.63 1997/05/31 18:33:25 mj Exp $ * * suncons.c: Sun SparcStation console support. * @@ -76,11 +76,11 @@ #include #include -#include "../../char/kbd_kern.h" -#include "../../char/vt_kern.h" -#include "../../char/consolemap.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +#include +#include +#include +#include +#include #include "fb.h" diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c index e803344bdb58..68856c9eee7c 100644 --- a/drivers/sbus/char/sunfb.c +++ b/drivers/sbus/char/sunfb.c @@ -1,4 +1,4 @@ -/* $Id: sunfb.c,v 1.22 1997/04/03 08:47:56 davem Exp $ +/* $Id: sunfb.c,v 1.23 1997/05/31 18:33:26 mj Exp $ * sunfb.c: Sun generic frame buffer support. * * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -33,11 +33,11 @@ #include #include -#include "../../char/kbd_kern.h" -#include "../../char/vt_kern.h" -#include "../../char/consolemap.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +#include +#include +#include +#include +#include #include "fb.h" diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index 8e398f345c17..1ddaaecd42aa 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -25,9 +25,9 @@ #include #include -#include "../../char/kbd_kern.h" -#include "../../char/diacr.h" -#include "../../char/vt_kern.h" +#include +#include +#include #define SIZE(x) (sizeof(x)/sizeof((x)[0])) diff --git a/drivers/sbus/char/tcx.c b/drivers/sbus/char/tcx.c index 2ecabda624b0..db66383ac407 100644 --- a/drivers/sbus/char/tcx.c +++ b/drivers/sbus/char/tcx.c @@ -1,4 +1,4 @@ -/* $Id: tcx.c,v 1.12 1997/04/14 17:04:51 jj Exp $ +/* $Id: tcx.c,v 1.15 1997/06/04 08:27:32 davem Exp $ * tcx.c: SUNW,tcx 24/8bit frame buffer driver * * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -16,9 +16,10 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include #include "fb.h" #include "cg_common.h" @@ -171,7 +172,7 @@ tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, page += map_size; } vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 20e04258b8fd..00cdfe35c2f5 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -581,7 +581,7 @@ static int vfc_mmap(struct inode *inode, struct file *file, vma->vm_page_prot, dev->which_io); if(ret) return -EAGAIN; vma->vm_inode=inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } diff --git a/drivers/sbus/char/weitek.c b/drivers/sbus/char/weitek.c index 0fa0cb5fc394..d2ac4d135346 100644 --- a/drivers/sbus/char/weitek.c +++ b/drivers/sbus/char/weitek.c @@ -1,4 +1,4 @@ -/* $Id: weitek.c,v 1.9 1997/04/14 17:04:57 jj Exp $ +/* $Id: weitek.c,v 1.12 1997/06/04 08:27:34 davem Exp $ * weitek.c: Tadpole P9100/P9000 console driver * * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk) @@ -15,9 +15,10 @@ #include #include -#include "../../char/vt_kern.h" -#include "../../char/selection.h" -#include "../../char/console_struct.h" +/* These must be included after asm/fbio.h */ +#include +#include +#include #include "fb.h" #include "cg_common.h" @@ -82,7 +83,7 @@ weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma, page += map_size; } vma->vm_inode = inode; - inode->i_count++; + atomic_inc(&inode->i_count); return 0; } #endif diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index b57345019446..9178871dfe22 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -21,7 +21,15 @@ dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI -dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI +dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI +if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then + bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y + int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8 + bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N + bool ' Use external SCB bank' CONFIG_AIC7XXX_USE_EXT_SCBRAM N + bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N + int ' delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15 +fi dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index e6f98e4e4930..ed2b0a46ac05 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -421,12 +421,16 @@ BusLogic.o: BusLogic.c FlashPoint.c aha152x.o: aha152x.c $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c -aic7xxx_asm: aic7xxx_asm.c - $(HOSTCC) -o $@ aic7xxx_asm.c +aic7xxx/aic7xxx_asm: aic7xxx/aic7xxx_asm.c + (cd ./aic7xxx; make aic7xxx_asm) -aic7xxx.c: aic7xxx_seq.h -aic7xxx_seq.h: aic7xxx_asm aic7xxx.seq - ./aic7xxx_asm -o $@ aic7xxx.seq +aic7xxx_seq.h aic7xxx_reg.h: aic7xxx/aic7xxx_asm aic7xxx/aic7xxx.seq + ./aic7xxx/aic7xxx_asm -nostdinc -I- -Iaic7xxx -o aic7xxx_seq.h \ + -r aic7xxx_reg.h aic7xxx/aic7xxx.seq + +aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h + $(CC) $(CFLAGS) -c -o $@ aic7xxx.c + (cd aic7xxx; make clean_most) seagate.o: seagate.c $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c diff --git a/drivers/scsi/README.aic7xxx b/drivers/scsi/README.aic7xxx index ccbf4a6e2ab2..21da229cc353 100644 --- a/drivers/scsi/README.aic7xxx +++ b/drivers/scsi/README.aic7xxx @@ -1,5 +1,5 @@ AIC7xxx Driver for Linux - April 15, 1996 + June 11, 1997 Introduction ------------------------ @@ -31,6 +31,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD ----------------------- AIC-777x AIC-785x + AIC-786x AIC-787x AIC-788x @@ -58,6 +59,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD Dan Eischen deischen@iworks.InterWorks.org (Linux Driver Co-maintainer) Dean Gehnert deang@teleport.com (Linux FTP/patch maintainer) Jess Johnson jester@frenzy.com (AIC7xxx FAQ author) + Doug Ledford dledford@dialnet.net (Stress tester/bug squasher) Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original author of the driver. John has since retired from the project. Thanks @@ -111,5 +113,5 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD Dean W. Gehnert deang@teleport.com -$Revision: 3.0 $ +$Revision: 3.1 $ diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index 96e66defdd8a..5026f841c2a1 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -1,5 +1,3 @@ -#define EXPERIMENTAL_FLAGS 0 - /*+M************************************************************************* * Adaptec AIC7xxx device driver for Linux. * @@ -37,13 +35,24 @@ * Parts of this driver are based on the FreeBSD driver by Justin * T. Gibbs. * + * Thanks also go to (in alphabetical order) the following: + * + * Rory Bolt - Sequencer bug fixes + * Jay Estabrook - Initial DEC Alpha support + * Doug Ledford - Much needed abort/reset bug fixes + * Kai Makisara - DMAing of SCBs + * * A Boot time option was also added for not resetting the scsi bus. * - * Form: aic7xxx=extended,no_reset + * Form: aic7xxx=extended + * aic7xxx=no_reset + * aic7xxx=ultra + * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level + * aic7xxx=verbose * - * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96 + * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97 * - * $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $ + * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $ *-M*************************************************************************/ #ifdef MODULE @@ -67,7 +76,15 @@ #include "scsi.h" #include "hosts.h" #include "aic7xxx.h" + +#ifndef u_int8_t +typedef unsigned char u_int8_t; +#endif +#include "aic7xxx/bsd_q.h" +#include "aic7xxx/sequencer.h" +#include "aic7xxx/scsi_message.h" #include "aic7xxx_reg.h" +#include "aic7xxx_seq.h" #include #include /* for kmalloc() */ @@ -79,16 +96,20 @@ */ #define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a)) -static struct proc_dir_entry proc_scsi_aic7xxx = { +struct proc_dir_entry proc_scsi_aic7xxx = { PROC_SCSI_AIC7XXX, 7, "aic7xxx", - S_IFDIR | S_IRUGO | S_IXUGO, 2 + S_IFDIR | S_IRUGO | S_IXUGO, 2, + 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "$Revision: 4.0 $" +#define AIC7XXX_C_VERSION "$Revision: 4.1 $" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) -#define MIN(a,b) ((a < b) ? a : b) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define ALL_TARGETS -1 +#define ALL_CHANNELS '\0' +#define ALL_LUNS -1 #ifndef TRUE # define TRUE 1 #endif @@ -107,11 +128,8 @@ static struct proc_dir_entry proc_scsi_aic7xxx = { * support because all PCI dependent code is bracketed with * "#ifdef CONFIG_PCI ... #endif CONFIG_PCI". * - * o Twin bus support - this has been tested and does work. - * - * o DMAing of SCBs - thanks to Kai Makisara, this now works. - * This define is now taken out and DMAing of SCBs is always - * performed (8/12/95 - DE). + * o Twin bus support - this has been tested and does work. It is + * not an option anymore. * * o Tagged queueing - this driver is capable of tagged queueing * but I am unsure as to how well the higher level driver implements @@ -140,16 +158,16 @@ static struct proc_dir_entry proc_scsi_aic7xxx = { * LUN using its own heuristic based on the number of available * SCBs. * - * o 3985 support - The 3985 adapter is much like the 3940, but - * has three 7870 controllers as opposed to two for the 3940. - * It will get probed and recognized as three different adapters, - * but all three controllers can share the same external bank of - * 255 SCBs. If you enable AIC7XXX_SHARE_SCBS, then the driver - * will attempt to share the common bank of SCBs between the three - * controllers of the 3985. This is experimental and hasn't - * been tested. By default, we do not share the bank of SCBs, - * and force the controllers to use their own internal bank of - * 16 SCBs. Please let us know if sharing the SCB array works. + * o 3985 support - The 3985 adapter is much like the 3940, but has + * three 7870 controllers as opposed to two for the 3940. It will + * be probed and recognized as three different adapters, but all + * three controllers can share the same external bank of 255 SCBs. + * If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt + * to use and share the common bank of SCBs between the three + * controllers of the 3985. This is experimental and hasn't been + * been tested. By default, we do not use external SCB RAM, and + * force the controllers to use their own internal bank of 16 SCBs. + * Please let us know if using the external SCB array works. * * o SCB paging support - SCB paging is enabled by defining * AIC7XXX_PAGE_ENABLE. Support for this was taken from the @@ -162,43 +180,54 @@ static struct proc_dir_entry proc_scsi_aic7xxx = { * Note that sharing of IRQs is not an option any longer. Linux supports * it so we support it. * - * Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96 + * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96 */ -/* Uncomment this for testing twin bus support. */ -#define AIC7XXX_TWIN_SUPPORT - /* Uncomment this for tagged queueing. */ -/* #define AIC7XXX_TAGGED_QUEUEING */ +#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING +#define AIC7XXX_TAGGED_QUEUEING +#endif /* * You can try raising me if tagged queueing is enabled, or lowering * me if you only have 4 SCBs. */ -/* #define AIC7XXX_CMDS_PER_LUN 8 */ +#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN +#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN +#endif /* Set this to the delay in seconds after SCSI bus reset. */ +#ifdef CONFIG_AIC7XXX_RESET_DELAY +#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY +#else #define AIC7XXX_RESET_DELAY 15 +#endif /* - * Uncomment the following define for collection of SCSI transfer statistics - * for the /proc filesystem. + * Control collection of SCSI transfer statistics for the /proc filesystem. * * NOTE: Do NOT enable this when running on kernels version 1.2.x and below. * NOTE: This does affect performance since it has to maintain statistics. */ -/* #define AIC7XXX_PROC_STATS */ +#ifdef CONFIG_AIC7XXX_PROC_STATS +#define AIC7XXX_PROC_STATS +#endif /* - * Uncomment the following to enable SCB paging. + * Enable SCB paging. */ -/* #define AIC7XXX_PAGE_ENABLE */ +#ifdef CONFIG_AIC7XXX_PAGE_ENABLE +#define AIC7XXX_PAGE_ENABLE +#endif /* - * Uncomment the following to enable sharing of the external bank - * of 255 SCBs for the 3985. + * Uncomment the following to enable use of the external bank + * of 255 SCBs. For 3985 adapters, this will also enable sharing + * of the SCB array across all three controllers. */ -#define AIC7XXX_SHARE_SCBS +#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM +#define AIC7XXX_USE_EXT_SCBRAM +#endif /* * For debugging the abort/reset code. @@ -210,6 +239,86 @@ static struct proc_dir_entry proc_scsi_aic7xxx = { */ #define AIC7XXX_DEBUG +/* + * Set this for defining the number of tagged commands on a device + * by device, and controller by controller basis. The first set + * of tagged commands will be used for the first detected aic7xxx + * controller, the second set will be used for the second detected + * aic7xxx controller, and so on. These values will *only* be used + * for targets that are tagged queueing capable; these values will + * be ignored in all other cases. The tag_commands is an array of + * 16 to allow for wide and twin adapters. Twin adapters will use + * indexes 0-7 for channel 0, and indexes 8-15 for channel 1. + * + * *** Determining commands per LUN *** + * + * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its + * own algorithm to determine the commands/LUN. If SCB paging is + * enabled, the commands/LUN is 8. When SCB paging is not enabled, + * then commands/LUN is 8 for adapters with 16 or more hardware SCBs + * and 4 commands/LUN for adapters with 3 or 4 SCBs. + * + */ +/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */ + +#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE +typedef struct +{ + unsigned char tag_commands[16]; /* Allow for wide/twin channel adapters. */ +} adapter_tag_info_t; + +/* + * Make a define that will tell the driver to use it's own algorithm + * for determining commands/LUN (see Determining commands per LUN + * above). + */ +#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +/* + * Modify this as you see fit for your system. By setting tag_enable + * to 0, the driver will use it's own algorithm for determining the + * number of commands to use (see above). When -1, the driver will + * not enable tagged queueing for that particular device. When positive + * (> 0) the values in the array are used for the queue_depth. + * + * In this example, the first line will enable tagged queueing for all + * the devices on the first probed aic7xxx adapter and tells the driver + * to use it's own algorithm for determining commands/LUN. + * + * The second line enables tagged queueing with 4 commands/LUN for IDs + * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the + * driver to use its own algorithm for ID 1. + * + * The third line is the same as the first line. + * + * The fourth line disables tagged queueing for devices 0 and 3. It + * enables tagged queueing for the other IDs, with 16 commands/LUN + * for IDs 1 and 4, 128 commands/LUN for ID 8, and 4 commands/LUN for + * IDs 2, 5-7, and 9-15. + */ +adapter_tag_info_t aic7xxx_tag_info[] = +{ + {DEFAULT_TAG_COMMANDS}, + {4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}, + {DEFAULT_TAG_COMMANDS}, + {-1, 16, 4, -1, 16, 4, 4, 4, 128, 4, 4, 4, 4, 4, 4, 4} +}; +#endif + +/* + * Don't define this unless you have problems with the driver + * interrupt handler. The old method would register the drivers + * interrupt handler as a "fast" type interrupt handler that would + * lock out other interrupts. Since this driver can spend a lot + * of time in the interrupt handler, this is _not_ a good idea. + * It also conflicts with some of the more common ethernet drivers + * that don't use fast interrupts. Currently, Linux does not allow + * IRQ sharing unless both drivers can agree on the type of interrupt + * handler. + */ +/* #define AIC7XXX_OLD_ISR_TYPE */ + + /* * Controller type and options */ @@ -232,14 +341,15 @@ typedef enum { AIC_7882, /* PCI aic7882 on 3940 Ultra */ AIC_7883, /* PCI aic7883 on 3985 Ultra */ AIC_7884 /* PCI aic7884 on 294x Ultra Differential */ -} aha_type; +} aha_chip_type; typedef enum { AIC_777x, /* AIC-7770 based */ - AIC_785x, /* AIC-7850 based */ + AIC_785x, /* AIC-7850 based (3 SCBs)*/ + AIC_786x, /* AIC-7860 based (7850 ultra) */ AIC_787x, /* AIC-7870 based */ - AIC_788x /* AIC-7880 based */ -} aha_chip_type; + AIC_788x /* AIC-7880 based (ultra) */ +} aha_chip_class_type; typedef enum { AIC_SINGLE, /* Single Channel */ @@ -269,24 +379,24 @@ typedef enum { * Don't forget to change this when changing the types! */ static const char *board_names[] = { - "", /* AIC_NONE */ - "AIC-7770", /* AIC_7770 */ - "AHA-2740", /* AIC_7771 */ - "AHA-2840", /* AIC_284x */ - "AIC-7850", /* AIC_7850 */ - "AIC-7855", /* AIC_7855 */ - "AIC-7850 Ultra", /* AIC_7860 */ - "AHA-2940A Ultra", /* AIC_7861 */ - "AIC-7870", /* AIC_7870 */ - "AHA-2940", /* AIC_7871 */ - "AHA-3940", /* AIC_7872 */ - "AHA-3985", /* AIC_7873 */ - "AHA-2940 Differential", /* AIC_7874 */ - "AIC-7880 Ultra", /* AIC_7880 */ - "AHA-2940 Ultra", /* AIC_7881 */ - "AHA-3940 Ultra", /* AIC_7882 */ - "AHA-3985 Ultra", /* AIC_7883 */ - "AHA-2940 Ultra Differential" /* AIC_7884 */ + "AIC-7xxx Unknown", /* AIC_NONE */ + "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */ + "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */ + "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */ + "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */ + "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */ + "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */ + "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */ + "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */ + "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */ + "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */ + "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */ + "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */ + "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */ + "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */ + "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */ + "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */ + "Adaptec AHA-2944 Ultra SCSI host adapter" /* AIC_7884 */ }; /* @@ -310,12 +420,17 @@ static const char *board_names[] = { */ #define DID_RETRY_COMMAND DID_ERROR +#define HSCSIID 0x07 +#define HWSCSIID 0x0F +#define SCSI_RESET 0x040 + /* * EISA/VL-bus stuff */ #define MINSLOT 1 #define MAXSLOT 15 #define SLOTBASE(x) ((x) << 12) +#define BASE_TO_SLOT(x) ((x) >> 12) /* * Standard EISA Host ID regs (Offset from slot base) @@ -333,14 +448,6 @@ static const char *board_names[] = { #define INTDEF 0x5C /* Interrupt Definition Register */ -/* - * Some defines for the HCNTRL register. - */ -#define REQ_PAUSE IRQMS | INTEN | PAUSE -#define UNPAUSE_274X IRQMS | INTEN -#define UNPAUSE_284X INTEN -#define UNPAUSE_294X IRQMS | INTEN - /* * AIC-78X0 PCI registers */ @@ -377,7 +484,7 @@ static const char *board_names[] = { * each word, while the C56 and C66 (4096 bits) use 8 bits to * address each word. */ -typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type; +typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type; /* * @@ -417,15 +524,15 @@ struct seeprom_config { /* * Host Adapter Control Bits */ -/* UNUSED 0x0001 */ +#define CFAUTOTERM 0x0001 /* Perform Auto termination */ #define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */ #define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */ #define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */ -#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */ +#define CFSTERM 0x0004 /* SCSI low byte termination */ #define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */ #define CFSPARITY 0x0010 /* SCSI parity */ #define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */ -#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */ +#define CFRESETB 0x0040 /* reset SCSI bus at boot */ /* UNUSED 0xFF80 */ unsigned short adapter_control; /* word 17 */ @@ -448,36 +555,17 @@ struct seeprom_config { unsigned short checksum; /* word 31 */ }; +#define SELBUS_MASK 0x0a +#define SELNARROW 0x00 +#define SELBUSB 0x08 +#define SINGLE_BUS 0x00 -#define SCSI_RESET 0x040 - -/* - * Pause the sequencer and wait for it to actually stop - this - * is important since the sequencer can disable pausing for critical - * sections. - */ -#define PAUSE_SEQUENCER(p) \ - synchronize_irq(); \ - outb(p->pause, HCNTRL + p->base); \ - while ((inb(HCNTRL + p->base) & PAUSE) == 0) \ - ; \ - -/* - * Unpause the sequencer. Unremarkable, yet done often enough to - * warrant an easy way to do it. - */ -#define UNPAUSE_SEQUENCER(p) \ - outb(p->unpause, HCNTRL + p->base) - -/* - * Restart the sequencer program from address zero - */ -#define RESTART_SEQUENCER(p) \ - do { \ - outb(SEQRESET | FASTMODE, SEQCTL + p->base); \ - } while (inb(SEQADDR0 + p->base) != 0 && \ - inb(SEQADDR1 + p->base) != 0); \ - UNPAUSE_SEQUENCER(p); +#define SCB_TARGET(scb) \ + (((scb)->hscb->target_channel_lun & TID) >> 4) +#define SCB_LUN(scb) \ + ((scb)->hscb->target_channel_lun & LID) +#define SCB_IS_SCSIBUS_B(scb) \ + (((scb)->hscb->target_channel_lun & SELBUSB) != 0) /* * If an error occurs during a data transfer phase, run the command @@ -540,12 +628,6 @@ static struct Scsi_Host *aic7xxx_boards[NR_IRQS + 1]; */ static int aic7xxx_spurious_count; -/* - * The driver keeps up to four scb structures per card in memory. Only the - * first 25 bytes of the structure are valid for the hardware, the rest used - * for driver level bookkeeping. - */ - /* * As of Linux 2.1, the mid-level SCSI code uses virtual addresses * in the scatter-gather lists. We need to convert the virtual @@ -561,18 +643,47 @@ struct hw_scatterlist { */ #define MAX_SG 256 -struct aic7xxx_scb { +/* + * The maximum number of SCBs we could have for ANY type + * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE + * SEQUENCER CODE IF THIS IS MODIFIED! + */ +#define AIC7XXX_MAXSCB 255 + +/* + * We keep a pool of kernel memory for hardware scatter/gather + * arrays. One page (PAGE_SIZE) of kernel memory is allocated + * at a time, and SG arrays are taken from this pool. An SG + * array is 2048 bytes (256 * 8) long. A page of memory is either + * 4096 (i386) or 8192 (alpha) bytes from which SG arrays can be + * taken without fragmenting memory. + * + * The number of free SG arrays left in the SG pool is kept in + * aic7xxx_sg_arrays_free. The pool of SG arrays is kept in + * aic7xxx_next_sg_array; after each each allocation of an SG array, + * this variable is updated to point to the next available SG array + * in the pool. When aic7xxx_sg_arrays_free is zero, a new page is + * allocated for the pool. See aic7xxx_allocate_sglist() for + * further details. + * + * The SG memory pool is global to the driver, and is not per + * instance of the driver. + */ +static struct hw_scatterlist *aic7xxx_next_sg_array = NULL; +static int aic7xxx_sg_arrays_free = 0; + +struct aic7xxx_hwscb { /* ------------ Begin hardware supported fields ---------------- */ /* 0*/ unsigned char control; /* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */ /* 2*/ unsigned char target_status; /* 3*/ unsigned char SG_segment_count; -/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed)); +/* 4*/ unsigned int SG_list_pointer; /* 8*/ unsigned char residual_SG_segment_count; -/* 9*/ unsigned char residual_data_count[3] __attribute__ ((packed)); -/*12*/ unsigned char data_pointer[4] __attribute__ ((packed)); -/*16*/ unsigned int data_count __attribute__ ((packed)); /* must be 32 bits */ -/*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed)); +/* 9*/ unsigned char residual_data_count[3]; +/*12*/ unsigned int data_pointer; +/*16*/ unsigned int data_count; +/*20*/ unsigned int SCSI_cmd_pointer; /*24*/ unsigned char SCSI_cmd_length; /*25*/ u_char tag; /* Index into our kernel SCB array. * Also used as the tag for tagged I/O @@ -580,29 +691,47 @@ struct aic7xxx_scb { #define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download * via PIO to initialize a transaction. */ -/*26*/ u_char next; /* Used to thread SCBs awaiting selection +/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection * or disconnected down in the sequencer. */ - /*-----------------end of hardware supported fields----------------*/ - Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ - struct aic7xxx_scb *q_next; /* next scb in queue */ -#define SCB_FREE 0x00 -#define SCB_ACTIVE 0x01 -#define SCB_ABORTED 0x02 -#define SCB_DEVICE_RESET 0x04 -#define SCB_IMMED 0x08 -#define SCB_SENSE 0x10 -#define SCB_QUEUED_FOR_DONE 0x40 -#define SCB_PAGED_OUT 0x80 -#define SCB_WAITINGQ 0x100 -#define SCB_ASSIGNEDQ 0x200 -#define SCB_SENTORDEREDTAG 0x400 -#define SCB_IN_PROGRESS (SCB_ACTIVE | SCB_PAGED_OUT | \ - SCB_WAITINGQ | SCB_ASSIGNEDQ) - int state; /* current state of scb */ - unsigned int position; /* Position in scb array */ - struct hw_scatterlist sg_list[MAX_SG]; /* SG list in adapter format */ - unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */ +/*27*/ unsigned char prev; +/*28*/ unsigned int pad; /* + * Unused by the kernel, but we require + * the padding so that the array of + * hardware SCBs is alligned on 32 byte + * boundaries so the sequencer can index + */ +}; + +typedef enum { + SCB_FREE = 0x0000, + SCB_ACTIVE = 0x0001, + SCB_ABORTED = 0x0002, + SCB_DEVICE_RESET = 0x0004, + SCB_SENSE = 0x0008, + SCB_TIMEDOUT = 0x0010, + SCB_QUEUED_FOR_DONE = 0x0020, + SCB_RECOVERY_SCB = 0x0040, + SCB_WAITINGQ = 0x0080, + SCB_ASSIGNEDQ = 0x0100, + SCB_SENTORDEREDTAG = 0x0200, + SCB_MSGOUT_SDTR = 0x0400, + SCB_MSGOUT_WDTR = 0x0800, + SCB_ABORT = 0x1000, + SCB_QUEUED_ABORT = 0x2000 +} scb_flag_type; + +struct aic7xxx_scb { + struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ + Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ + struct aic7xxx_scb *q_next; /* next scb in queue */ + scb_flag_type flags; /* current state of scb */ + struct hw_scatterlist *sg_list; /* SG list in adapter format */ + unsigned char sg_count; + unsigned char sense_cmd[6]; /* + * Allocate 6 characters for + * sense command. + */ }; /* @@ -627,20 +756,17 @@ static unsigned char generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 }; typedef struct { + struct aic7xxx_hwscb *hscbs; scb_queue_type free_scbs; /* * SCBs assigned to free slot on * card (no paging required) */ - int numscbs; /* current number of scbs */ - int activescbs; /* active scbs */ -} scb_usage_type; - -/* - * The maximum number of SCBs we could have for ANY type - * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE - * SEQUENCER CODE IF THIS IS MODIFIED! - */ -#define AIC7XXX_MAXSCB 255 + unsigned char numscbs; /* current number of scbs */ + unsigned char maxhscbs; /* hardware scbs */ + unsigned char maxscbs; /* max scbs including pageable scbs */ + struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; + unsigned int reserve[100]; +} scb_data_type; /* * Define a structure used for each host adapter, only one per IRQ. @@ -648,17 +774,26 @@ typedef struct { struct aic7xxx_host { struct Scsi_Host *host; /* pointer to scsi host */ int host_no; /* SCSI host number */ + int instance; /* aic7xxx instance number */ + int scsi_id; /* host adapter SCSI ID */ + int scsi_id_b; /* channel B for twin adapters */ + int irq; /* IRQ for this adapter */ int base; /* card base address */ - int maxhscbs; /* hardware SCBs */ - int maxscbs; /* max SCBs (including pageable) */ -#define A_SCANNED 0x0001 -#define B_SCANNED 0x0002 -#define EXTENDED_TRANSLATION 0x0004 -#define HAVE_SEEPROM 0x0008 -#define ULTRA_ENABLED 0x0010 -#define PAGE_ENABLED 0x0020 -#define IN_ISR 0x0040 -#define USE_DEFAULTS 0x0080 + unsigned int mbase; /* I/O memory address */ + volatile unsigned char *maddr; /* memory mapped address */ +#define A_SCANNED 0x0001 +#define B_SCANNED 0x0002 +#define EXTENDED_TRANSLATION 0x0004 +#define FLAGS_CHANNEL_B_PRIMARY 0x0008 +#define MULTI_CHANNEL 0x0010 +#define ULTRA_ENABLED 0x0020 +#define PAGE_ENABLED 0x0040 +#define USE_DEFAULTS 0x0080 +#define BIOS_ENABLED 0x0100 +#define IN_ISR 0x0200 +#define IN_TIMEOUT 0x0400 +#define SHARED_SCBDATA 0x0800 +#define HAVE_SEEPROM 0x1000 unsigned int flags; unsigned int isr_count; /* Interrupt count */ unsigned short needsdtr_copy; /* default config */ @@ -669,36 +804,22 @@ struct aic7xxx_host { unsigned short wdtr_pending; unsigned short orderedtag; unsigned short discenable; /* Targets allowed to disconnect */ - aha_type type; /* card type */ - aha_chip_type chip_type; /* chip base type */ + aha_chip_type chip_type; /* card type */ + aha_chip_class_type chip_class; aha_bus_type bus_type; /* normal/twin/wide bus */ - char * mbase; /* I/O memory address */ - unsigned char chan_num; /* for 3940/3985, channel number */ + unsigned char chan_num; /* for 39xx, channel number */ unsigned char unpause; /* unpause value for HCNTRL */ unsigned char pause; /* pause value for HCNTRL */ unsigned char qcntmask; - struct seeprom_config seeprom; + unsigned char qfullcount; + unsigned char curqincnt; struct Scsi_Host *next; /* allow for multiple IRQs */ - struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; /* active commands */ - struct aic7xxx_scb *pagedout_ntscbs[16]; /* - * paged-out, non-tagged scbs - * indexed by target. - */ - scb_queue_type page_scbs; /* - * SCBs that will require paging - * before use (no assigned slot) - */ + unsigned char activescbs; /* active scbs */ scb_queue_type waiting_scbs; /* - * SCBs waiting to be paged and - * started. + * SCBs waiting for space in + * the QINFIFO. */ - scb_queue_type assigned_scbs; /* - * SCBs that were waiting but have - * have now been assigned a slot - * by aic7xxx_free_scb - */ - scb_usage_type scb_usage; - scb_usage_type *scb_link; + scb_data_type *scb_data; struct aic7xxx_cmd_queue { Scsi_Cmnd *head; @@ -710,6 +831,7 @@ struct aic7xxx_host { #define BUS_DEVICE_RESET_PENDING 0x02 int flags; int commands_sent; + int active_cmds; } device_status[16]; #ifdef AIC7XXX_PROC_STATS /* @@ -735,34 +857,10 @@ struct aic7xxx_host { #endif /* AIC7XXX_PROC_STATS */ }; -struct aic7xxx_host_config { - int irq; /* IRQ number */ - int mbase; /* memory base address*/ - int base; /* I/O base address*/ - int maxhscbs; /* hardware SCBs */ - int maxscbs; /* max SCBs (including pageable) */ - int unpause; /* unpause value for HCNTRL */ - int pause; /* pause value for HCNTRL */ - int scsi_id; /* host SCSI ID */ - int scsi_id_b; /* host SCSI ID B channel for twin cards */ - unsigned int flags; /* used the same as struct aic7xxx_host flags */ - int chan_num; /* for 3940/3985, channel number */ - unsigned char busrtime; /* bus release time */ - unsigned char bus_speed; /* bus speed */ - unsigned char qcntmask; - aha_type type; /* card type */ - aha_chip_type chip_type; /* chip base type */ - aha_bus_type bus_type; /* normal/twin/wide bus */ - aha_status_type bios; /* BIOS is enabled/disabled */ - aha_status_type parity; /* bus parity enabled/disabled */ - aha_status_type low_term; /* bus termination low byte */ - aha_status_type high_term; /* bus termination high byte (wide cards only) */ -}; - /* * Valid SCSIRATE values. (p. 3-17) - * Provides a mapping of transfer periods in ns to the proper value to - * stick in the scsiscfr reg to use that transfer rate. + * Provides a mapping of transfer periods in ns/4 to the proper value to + * stick in the SCSIRATE reg to use that transfer rate. */ static struct { short period; @@ -771,17 +869,17 @@ static struct { short rate; const char *english; } aic7xxx_syncrates[] = { - { 50, 0x100, "20.0" }, - { 62, 0x110, "16.0" }, - { 75, 0x120, "13.4" }, - { 100, 0x000, "10.0" }, - { 125, 0x010, "8.0" }, - { 150, 0x020, "6.67" }, - { 175, 0x030, "5.7" }, - { 200, 0x040, "5.0" }, - { 225, 0x050, "4.4" }, - { 250, 0x060, "4.0" }, - { 275, 0x070, "3.6" } + { 12, 0x100, "20.0" }, + { 15, 0x110, "16.0" }, + { 18, 0x120, "13.4" }, + { 25, 0x000, "10.0" }, + { 31, 0x010, "8.0" }, + { 37, 0x020, "6.67" }, + { 43, 0x030, "5.7" }, + { 50, 0x040, "5.0" }, + { 56, 0x050, "4.4" }, + { 62, 0x060, "4.0" }, + { 68, 0x070, "3.6" } }; static int num_aic7xxx_syncrates = @@ -790,166 +888,51 @@ static int num_aic7xxx_syncrates = #ifdef CONFIG_PCI static int number_of_3940s = 0; static int number_of_3985s = 0; -#ifdef AIC7XXX_SHARE_SCBS -static scb_usage_type *shared_3985_scbs = NULL; -#endif -#endif CONFIG_PCI +#endif /* CONFIG_PCI */ #ifdef AIC7XXX_DEBUG -static void -debug_config(struct aic7xxx_host_config *p) -{ - int scsi_conf; - unsigned char brelease; - unsigned char dfthresh; - - static int DFT[] = { 0, 50, 75, 100 }; - static int SST[] = { 256, 128, 64, 32 }; - static const char *BUSW[] = { "", "-TWIN", "-WIDE" }; - - scsi_conf = inb(SCSICONF + p->base); - - /* - * Scale the Data FIFO Threshhold and the Bus Release Time; they are - * stored in formats compatible for writing to sequencer registers. - */ - dfthresh = p->bus_speed >> 6; - - if (p->chip_type == AIC_777x) - { - brelease = p->busrtime >> 2; - } - else - { - brelease = p->busrtime; - } - if (brelease == 0) - { - brelease = 2; - } - - switch (p->type) - { - case AIC_7770: - case AIC_7771: - printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type], - p->base >> 12); - break; - - case AIC_284x: - printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type], - p->base >> 12); - break; - - case AIC_7850: - case AIC_7855: - case AIC_7860: - case AIC_7861: - case AIC_7870: - case AIC_7871: - case AIC_7872: - case AIC_7873: - case AIC_7874: - case AIC_7880: - case AIC_7881: - case AIC_7882: - case AIC_7883: - case AIC_7884: - printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type], - BUSW[p->bus_type], p->base, p->mbase); - break; - - default: - panic("aic7xxx: (debug_config) internal error.\n"); - } - - printk(" irq %d\n" - " bus release time %d bclks\n" - " data fifo threshold %d%%\n", - p->irq, - brelease, - DFT[dfthresh]); - - printk(" SCSI CHANNEL A:\n" - " scsi id %d\n" - " scsi selection timeout %d ms\n" - " scsi bus reset at power-on %sabled\n", - scsi_conf & 0x07, - SST[(scsi_conf >> 3) & 0x03], - (scsi_conf & 0x40) ? "en" : "dis"); - - if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN)) - { - /* - * Set the parity for 7770 based cards. - */ - p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED; - } - if (p->parity != AIC_UNKNOWN) - { - printk(" scsi bus parity %sabled\n", - (p->parity == AIC_ENABLED) ? "en" : "dis"); - } - - if ((p->type == AIC_7770) || (p->type == AIC_7771)) - { - p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED; - } - if (p->low_term != AIC_UNKNOWN) - { - printk(" scsi bus termination (low byte) %sabled\n", - (p->low_term == AIC_ENABLED) ? "en" : "dis"); - } - if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN)) - { - printk(" scsi bus termination (high byte) %sabled\n", - (p->high_term == AIC_ENABLED) ? "en" : "dis"); - } -} - #if 0 static void debug_scb(struct aic7xxx_scb *scb) { - printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n", - scb->control, scb->target_channel_lun, scb->SG_segment_count, - (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) | - (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0], - (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) | - (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0], - scb->SCSI_cmd_length); - printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n", - (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status, - scb->residual_SG_segment_count, - ((scb->residual_data_count[2] << 16) | - (scb->residual_data_count[1] << 8) | - (scb->residual_data_count[0])); - printk("data ptr 0x%x, data count %d, next waiting %d\n", - (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) | - (scb->data_pointer[1] << 8) | scb->data_pointer[0], - scb->data_count, scb->next_waiting); - printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n", - (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state, - scb->position); + struct aic7xxx_hwscb *hscb = scb->hscb; + + printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n", + scb, + hscb->control, + hscb->target_channel_lun, + hscb->SCSI_cmd_length, + hscb->SCSI_cmd_pointer ); + printk(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n", + hscb->data_count, + hscb->data_pointer, + hscb->SG_segment_count, + hscb->SG_list_pointer); + printk(" sg_addr:%lx sg_len:%ld\n", + hscb->sg_list[0].address, + hscb->sg_list[0].length); } #endif #else -# define debug_config(x) # define debug_scb(x) #endif AIC7XXX_DEBUG -#define TCL_OF_SCB(x) (((x)->target_channel_lun >> 4) & 0xf), \ - (((x)->target_channel_lun >> 3) & 0x01), \ - ((x)->target_channel_lun & 0x07) +#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ + (((scb->hscb)->target_channel_lun >> 3) & 0x01), \ + ((scb->hscb)->target_channel_lun & 0x07) + +#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ + (((scb->hscb)->target_channel_lun >> 3) & 0x01) + +#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1) -#define TARGET_INDEX(x) ((x)->target | ((x)->channel << 3)) +#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3)) /* * XXX - these options apply unilaterally to _all_ 274x/284x/294x - * cards in the system. This should be fixed, but then, - * does anyone really have more than one in a machine? + * cards in the system. This should be fixed. */ static unsigned int aic7xxx_extended = 0; /* extended translation on? */ static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */ @@ -959,6 +942,53 @@ static int aic7xxx_irq_trigger = -1; /* * 1 use level triggered */ static int aic7xxx_enable_ultra = 0; /* enable ultra SCSI speeds */ +static int aic7xxx_verbose = 0; /* verbose messages */ + + +/**************************************************************************** + * + * These functions are not used yet, but when we do memory mapped + * IO, we'll use them then. + * + ***************************************************************************/ +static inline unsigned char +aic_inb(struct aic7xxx_host *p, long port) +{ + if (p->maddr != NULL) + return (p->maddr[port]); + else + return (inb(p->base + port)); +} + +static inline void +aic_outb(struct aic7xxx_host *p, unsigned char val, long port) +{ + if (p->maddr != NULL) + p->maddr[port] = val; + else + outb(val, p->base + port); +} + +static inline void +aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size) +{ + if (p->maddr != NULL) + { + __asm __volatile(" + cld; + 1: lodsb; + movb %%al,(%0); + loop 1b" : + : + "r" ((p)->maddr + (port)), + "S" ((valp)), "c" ((size)) : + "%esi", "%ecx", "%eax"); + } + else + { + outsb(p->base + port, valp, size); + } +} /*+F************************************************************************* * Function: @@ -983,6 +1013,7 @@ aic7xxx_setup(char *s, int *dummy) { "no_reset", &aic7xxx_no_reset }, { "irq_trigger", &aic7xxx_irq_trigger }, { "ultra", &aic7xxx_enable_ultra }, + { "verbose", &aic7xxx_verbose }, { NULL, NULL } }; @@ -1008,150 +1039,328 @@ aic7xxx_setup(char *s, int *dummy) /*+F************************************************************************* * Function: - * aic7xxx_loadseq + * pause_sequencer * * Description: - * Load the sequencer code into the controller memory. + * Pause the sequencer and wait for it to actually stop - this + * is important since the sequencer can disable pausing for critical + * sections. *-F*************************************************************************/ -static void -aic7xxx_loadseq(int base) +static inline void +pause_sequencer(struct aic7xxx_host *p) { - static unsigned char seqprog[] = { - /* - * Each sequencer instruction is 29 bits - * long (fill in the excess with zeroes) - * and has to be loaded from least -> most - * significant byte, so this table has the - * byte ordering reversed. - */ -# include "aic7xxx_seq.h" - }; - - /* - * When the AIC-7770 is paused (as on chip reset), the - * sequencer address can be altered and a sequencer - * program can be loaded by writing it, byte by byte, to - * the sequencer RAM port - the Adaptec documentation - * recommends using REP OUTSB to do this, hence the inline - * assembly. Since the address autoincrements as we load - * the program, reset it back to zero afterward. Disable - * sequencer RAM parity error detection while loading, and - * make sure the LOADRAM bit is enabled for loading. - */ - outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base); - - outsb(SEQRAM + base, seqprog, sizeof(seqprog)); - - /* - * WARNING! This is a magic sequence! After extensive - * experimentation, it seems that you MUST turn off the - * LOADRAM bit before you play with SEQADDR again, else - * you will end up with parity errors being flagged on - * your sequencer program. (You would also think that - * turning off LOADRAM and setting SEQRESET to reset the - * address to zero would work, but you need to do it twice - * for it to take effect on the address. Timing problem?) - */ - do { - /* - * Actually, reset it until - * the address shows up as - * zero just to be safe.. - */ - outb(SEQRESET | FASTMODE, SEQCTL + base); - } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0)); + outb(p->pause, p->base + HCNTRL); + while ((inb(p->base + HCNTRL) & PAUSE) == 0); + { + ; + } } /*+F************************************************************************* * Function: - * aic7xxx_delay + * unpause_sequencer * * Description: - * Delay for specified amount of time. + * Unpause the sequencer. Unremarkable, yet done often enough to + * warrant an easy way to do it. *-F*************************************************************************/ -static void -aic7xxx_delay(int seconds) +static inline void +unpause_sequencer(struct aic7xxx_host *p, int unpause_always) { - unsigned long i; - - i = jiffies + (seconds * HZ); /* compute time to stop */ - - while (jiffies < i) + if (unpause_always || + ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)) { - ; /* Do nothing! */ + outb(p->unpause, p->base + HCNTRL); } } /*+F************************************************************************* * Function: - * rcs_version + * restart_sequencer * * Description: - * Return a string containing just the RCS version number from either - * an Id or Revision RCS clause. + * Restart the sequencer program from address zero. This assumes + * that the sequencer is already paused. *-F*************************************************************************/ -static const char * -rcs_version(const char *version_info) +static inline void +restart_sequencer(struct aic7xxx_host *p) { - static char buf[10]; - char *bp, *ep; + /* Set the sequencer address to 0. */ + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); - bp = NULL; - strcpy(buf, "????"); - if (!strncmp(version_info, "$Id: ", 5)) - { - if ((bp = strchr(version_info, ' ')) != NULL) - { - bp++; - if ((bp = strchr(bp, ' ')) != NULL) - { - bp++; - } - } - } - else + /* + * Reset and unpause the sequencer. The reset is suppose to + * start the sequencer running, but we do an unpause to make + * sure. + */ + outb(SEQRESET | FASTMODE, p->base + SEQCTL); + + unpause_sequencer(p, /*unpause_always*/ TRUE); +} + + +/*+F************************************************************************* + * Function: + * aic7xxx_next_patch + * + * Description: + * Find the next patch to download. + *-F*************************************************************************/ +static struct patch * +aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr) +{ + while (cur_patch != NULL) { - if (!strncmp(version_info, "$Revision: ", 11)) + if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE)) + || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE)) + || (instrptr >= cur_patch->end)) { - if ((bp = strchr(version_info, ' ')) != NULL) + /* + * Either we want to keep this section of code, or we have consumed + * this patch. Skip to the next patch. + */ + cur_patch++; + if (cur_patch->options == 0) { - bp++; + /* Out of patches. */ + cur_patch = NULL; } } - } - - if (bp != NULL) - { - if ((ep = strchr(bp, ' ')) != NULL) + else { - register int len = ep - bp; - - strncpy(buf, bp, len); - buf[len] = '\0'; + /* Found an OK patch. */ + break; } } - - return buf; + return (cur_patch); } + /*+F************************************************************************* * Function: - * aic7xxx_info + * aic7xxx_download_instr * * Description: - * Return a string describing the driver. + * Find the next patch to download. *-F*************************************************************************/ -const char * -aic7xxx_info(struct Scsi_Host *notused) +static void +aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr) { - static char buffer[128]; + unsigned char opcode; + struct ins_format3 *instr; - strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) "); - strcat(buffer, rcs_version(AIC7XXX_C_VERSION)); - strcat(buffer, "/"); - strcat(buffer, rcs_version(AIC7XXX_H_VERSION)); - strcat(buffer, "/"); + instr = (struct ins_format3 *) &seqprog[instrptr * 4]; + /* Pull the opcode */ + opcode = instr->opcode_addr >> 1; + switch (opcode) + { + case AIC_OP_JMP: + case AIC_OP_JC: + case AIC_OP_JNC: + case AIC_OP_CALL: + case AIC_OP_JNE: + case AIC_OP_JNZ: + case AIC_OP_JE: + case AIC_OP_JZ: + { + int address_offset; + struct ins_format3 new_instr; + unsigned int address; + struct patch *patch; + int i; + + address_offset = 0; + new_instr = *instr; /* Strucure copy */ + address = new_instr.address; + address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8; + for (i = 0; i < NUMBER(patches); i++) + { + patch = &patches[i]; + if ((((patch->options & options) == 0) && (patch->negative == FALSE)) || + (((patch->options & options) != 0) && (patch->negative == TRUE))) + { + if (address >= patch->end) + { + address_offset += patch->end - patch->begin; + } + } + } + address -= address_offset; + new_instr.address = address &0xFF; + new_instr.opcode_addr &= ~ADDR_HIGH_BIT; + new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT; + outsb(p->base + SEQRAM, &new_instr.immediate, 4); + break; + } + + case AIC_OP_OR: + case AIC_OP_AND: + case AIC_OP_XOR: + case AIC_OP_ADD: + case AIC_OP_ADC: + case AIC_OP_ROL: + outsb(p->base + SEQRAM, &instr->immediate, 4); + break; + + default: + panic("aic7xxx: Unknown opcode encountered in sequencer program."); + break; + } +} + + +/*+F************************************************************************* + * Function: + * aic7xxx_loadseq + * + * Description: + * Load the sequencer code into the controller memory. + *-F*************************************************************************/ +static void +aic7xxx_loadseq(struct aic7xxx_host *p) +{ + int options; + struct patch *cur_patch; + int i; + int downloaded; + + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Downloading sequencer code..."); + } + options = 1; /* Code for all options. */ + downloaded = 0; + if ((p->flags & ULTRA_ENABLED) != 0) + options |= ULTRA; + if (p->bus_type == AIC_TWIN) + options |= TWIN_CHANNEL; + if (p->scb_data->maxscbs > p->scb_data->maxhscbs) + options |= SCB_PAGING; + + cur_patch = patches; + outb(PERRORDIS | LOADRAM, p->base + SEQCTL); + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); + + for (i = 0; i < sizeof(seqprog) / 4; i++) + { + cur_patch = aic7xxx_next_patch(cur_patch, options, i); + if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i)) + { + /* Skip this instruction for this configuration. */ + continue; + } + aic7xxx_download_instr(p, options, i); + downloaded++; + } + + outb(FASTMODE, p->base + SEQCTL); + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); + + if (aic7xxx_verbose) + { + printk(" %d instructions downloaded\n", downloaded); + } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_delay + * + * Description: + * Delay for specified amount of time. We use udelay because the timer + * interrupt is not guaranteed to be enabled. This will cause an + * infinite loop since jiffies (clock ticks) is not updated. + *-F*************************************************************************/ +static void +aic7xxx_delay(int seconds) +{ + int i; + + /* + * Call udelay() for 1 millisecond inside a loop for + * the requested amount of seconds. + */ + for (i=0; i < seconds*1000; i++) + { + udelay(1000); /* Delay for 1 millisecond. */ + } +} + +/*+F************************************************************************* + * Function: + * rcs_version + * + * Description: + * Return a string containing just the RCS version number from either + * an Id or Revision RCS clause. + *-F*************************************************************************/ +const char * +rcs_version(const char *version_info) +{ + static char buf[10]; + char *bp, *ep; + + bp = NULL; + strcpy(buf, "????"); + if (!strncmp(version_info, "$Id: ", 5)) + { + if ((bp = strchr(version_info, ' ')) != NULL) + { + bp++; + if ((bp = strchr(bp, ' ')) != NULL) + { + bp++; + } + } + } + else + { + if (!strncmp(version_info, "$Revision: ", 11)) + { + if ((bp = strchr(version_info, ' ')) != NULL) + { + bp++; + } + } + } + + if (bp != NULL) + { + if ((ep = strchr(bp, ' ')) != NULL) + { + register int len = ep - bp; + + strncpy(buf, bp, len); + buf[len] = '\0'; + } + } + + return buf; +} + +/*+F************************************************************************* + * Function: + * aic7xxx_info + * + * Description: + * Return a string describing the driver. + *-F*************************************************************************/ +const char * +aic7xxx_info(struct Scsi_Host *notused) +{ + static char buffer[128]; + + strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) "); + strcat(buffer, rcs_version(AIC7XXX_C_VERSION)); + strcat(buffer, "/"); + strcat(buffer, rcs_version(AIC7XXX_H_VERSION)); +#if 0 + strcat(buffer, "/"); strcat(buffer, rcs_version(AIC7XXX_SEQ_VER)); +#endif return buffer; } @@ -1161,9 +1370,12 @@ aic7xxx_info(struct Scsi_Host *notused) * aic7xxx_length * * Description: - * How much data should be transferred for this SCSI command? Stop - * at segment sg_last if it's a scatter-gather command so we can - * compute underflow easily. + * How much data should be transferred for this SCSI command? Assume + * all segments are to be transferred except for the last sg_last + * segments. This will allow us to compute underflow easily. To + * calculate the total length of the command, use sg_last = 0. To + * calculate the length of all but the last 2 SG segments, use + * sg_last = 2. *-F*************************************************************************/ static unsigned aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) @@ -1177,7 +1389,7 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) if (cmd->use_sg) { - for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++) + for (i = length = 0; i < segments; i++) { length += sg[i].length; } @@ -1199,9 +1411,9 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) *-F*************************************************************************/ static void aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate, - short period, unsigned char offset, int target, char channel) + unsigned char *period, unsigned char *offset, int target, char channel) { - int i; + int i = num_aic7xxx_syncrates; unsigned long ultra_enb_addr; unsigned char ultra_enb, sxfrctl0; @@ -1209,11 +1421,11 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate, * If the offset is 0, then the device is requesting asynchronous * transfers. */ - if (offset != 0) + if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0) { for (i = 0; i < num_aic7xxx_syncrates; i++) { - if ((aic7xxx_syncrates[i].period - period) >= 0) + if (*period <= aic7xxx_syncrates[i].period) { /* * Watch out for Ultra speeds when ultra is not enabled and @@ -1229,99 +1441,57 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate, */ continue; } - *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F); + *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F); + *period = aic7xxx_syncrates[i].period; - /* - * Ensure Ultra mode is set properly for this target. - */ - ultra_enb_addr = ULTRA_ENB; - if ((channel == 'B') || (target > 7)) + if (aic7xxx_verbose) { - ultra_enb_addr++; + printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " + "offset %d.\n", p->host_no, target, channel, + aic7xxx_syncrates[i].english, *offset); } - ultra_enb = inb(p->base + ultra_enb_addr); - sxfrctl0 = inb(p->base + SXFRCTL0); - if (aic7xxx_syncrates[i].rate & ULTRA_SXFR) - { - ultra_enb |= 0x01 << (target & 0x07); - sxfrctl0 |= ULTRAEN; - } - else - { - ultra_enb &= ~(0x01 << (target & 0x07)); - sxfrctl0 &= ~ULTRAEN; - } - outb(ultra_enb, p->base + ultra_enb_addr); - outb(sxfrctl0, p->base + SXFRCTL0); - - printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " - "offset %d.\n", p->host_no, target, channel, - aic7xxx_syncrates[i].english, offset); - return; + break; } } } - /* - * Default to asynchronous transfer - */ - *scsirate = 0; - printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n", - p->host_no, target, channel); -} - -/*+F************************************************************************* - * Function: - * aic7xxx_putscb - * - * Description: - * Transfer a SCB to the controller. - *-F*************************************************************************/ -static inline void -aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) -{ - int base = p->base; - - outb(SCBAUTO, SCBCNT + base); + if (i >= num_aic7xxx_syncrates) + { + /* + * Use asynchronous transfers. + */ + *scsirate = 0; + *period = 0; + *offset = 0; + if (aic7xxx_verbose) + { + printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n", + p->host_no, target, channel); + } + } /* - * By turning on the SCB auto increment, any reference - * to the SCB I/O space postincrements the SCB address - * we're looking at. So turn this on and dump the relevant - * portion of the SCB to the card. - * - * We can do 16bit transfers on all but 284x. + * Ensure Ultra mode is set properly for this target. */ - if (p->type == AIC_284x) + ultra_enb_addr = ULTRA_ENB; + if ((channel == 'B') || (target > 7)) + { + ultra_enb_addr++; + } + ultra_enb = inb(p->base + ultra_enb_addr); + sxfrctl0 = inb(p->base + SXFRCTL0); + if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR)) { - outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE); + ultra_enb |= 0x01 << (target & 0x07); + sxfrctl0 |= FAST20; } else { - outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4); + ultra_enb &= ~(0x01 << (target & 0x07)); + sxfrctl0 &= ~FAST20; } - - outb(0, SCBCNT + base); -} - -/*+F************************************************************************* - * Function: - * aic7xxx_getscb - * - * Description: - * Get a SCB from the controller. - *-F*************************************************************************/ -static inline void -aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) -{ - int base = p->base; - - /* - * This is almost identical to aic7xxx_putscb(). - */ - outb(SCBAUTO, SCBCNT + base); - insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE); - outb(0, SCBCNT + base); + outb(ultra_enb, p->base + ultra_enb_addr); + outb(sxfrctl0, p->base + SXFRCTL0); } /*+F************************************************************************* @@ -1373,6 +1543,47 @@ scbq_remove_head(scb_queue_type *queue) queue->tail = NULL; } +/*+F************************************************************************* + * Function: + * scbq_remove + * + * Description: + * Removes an SCB from the list. + * + *-F*************************************************************************/ +static inline void +scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb) +{ + if (queue->head == scb) + { + /* At beginning of queue, remove from head. */ + scbq_remove_head(queue); + } + else + { + struct aic7xxx_scb *curscb = queue->head; + + /* + * Search until the next scb is the one we're looking for, or + * we run out of queue. + */ + while ((curscb != NULL) && (curscb->q_next != scb)) + { + curscb = curscb->q_next; + } + if (curscb != NULL) + { + /* Found it. */ + curscb->q_next = scb->q_next; + if (scb->q_next == NULL) + { + /* Update the tail when removing the tail. */ + queue->tail = curscb; + } + } + } +} + /*+F************************************************************************* * Function: * scbq_insert_tail @@ -1404,23 +1615,87 @@ scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb) * to be reset and all devices on that channel must be aborted. *-F*************************************************************************/ static int -aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel) +aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel, + int lun, unsigned char tag) { - int targ = (scb->target_channel_lun >> 4) & 0x0F; - char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F; + char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + int slun = scb->hscb->target_channel_lun & 0x07; + int match; #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n", - target, channel, targ, chan); + printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n", + scb->cmd->device->host->host_no, target, channel, targ, chan); #endif - if (target == ALL_TARGETS) + match = ((chan == channel) || (channel == ALL_CHANNELS)); + if (match != 0) + match = ((targ == target) || (target == ALL_TARGETS)); + if (match != 0) + match = ((lun == slun) || (lun == ALL_LUNS)); + if (match != 0) + match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL)); + + return (match); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_add_curscb_to_free_list + * + * Description: + * Adds the current scb (in SCBPTR) to the list of free SCBs. + *-F*************************************************************************/ +static void +aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p) +{ + /* + * Invalidate the tag so that aic7xxx_find_scb doesn't think + * it's active + */ + outb(SCB_LIST_NULL, p->base + SCB_TAG); + + outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT); + outb(inb(p->base + SCBPTR), p->base + FREE_SCBH); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_rem_scb_from_disc_list + * + * Description: + * Removes the current SCB from the disconnected list and adds it + * to the free list. + *-F*************************************************************************/ +static unsigned char +aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr) +{ + unsigned char next; + unsigned char prev; + + outb(scbptr, p->base + SCBPTR); + next = inb(p->base + SCB_NEXT); + prev = inb(p->base + SCB_PREV); + + outb(0, p->base + SCB_CONTROL); + + aic7xxx_add_curscb_to_free_list(p); + + if (prev != SCB_LIST_NULL) { - return (chan == channel); + outb(prev, p->base + SCBPTR); + outb(next, p->base + SCB_NEXT); } else { - return ((chan == channel) && (targ == target)); + outb(next, p->base + DISCONNECTED_SCBH); + } + + if (next != SCB_LIST_NULL) + { + outb(next, p->base + SCBPTR); + outb(prev, p->base + SCB_PREV); } + return next; } /*+F************************************************************************* @@ -1428,120 +1703,209 @@ aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel) * aic7xxx_busy_target * * Description: - * Set the specified target active. + * Set the specified target busy. *-F*************************************************************************/ static void -aic7xxx_busy_target(unsigned char target, char channel, int base) +aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target, + char channel, unsigned char scbid) { - unsigned char active; - unsigned long active_port = ACTIVE_A + base; + unsigned char active_scb; + unsigned char info_scb; + unsigned int scb_offset; - if ((target > 0x07) || (channel == 'B')) - { - /* - * targets on the Second channel or above id 7 store info in byte two - * of ACTIVE - */ - active_port++; - } - active = inb(active_port); - active |= (0x01 << (target & 0x07)); - outb(active, active_port); + info_scb = target / 4; + if (channel == 'B') + info_scb = info_scb + 2; + + active_scb = inb(p->base + SCBPTR); + outb(info_scb, p->base + SCBPTR); + scb_offset = SCB_BUSYTARGETS + (target & 0x03); + outb(scbid, p->base + scb_offset); + outb(active_scb, p->base + SCBPTR); } /*+F************************************************************************* * Function: - * aic7xxx_unbusy_target + * aic7xxx_index_busy_target * * Description: - * Set the specified target inactive. + * Returns the index of the busy target, and optionally sets the + * target inactive. *-F*************************************************************************/ -static void -aic7xxx_unbusy_target(unsigned char target, char channel, int base) +static unsigned char +aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target, + char channel, int unbusy) { - unsigned char active; - unsigned long active_port = ACTIVE_A + base; + unsigned char active_scb; + unsigned char info_scb; + unsigned char busy_scbid; + unsigned int scb_offset; - if ((target > 0x07) || (channel == 'B')) + info_scb = target / 4; + if (channel == 'B') + info_scb = info_scb + 2; + + active_scb = inb(p->base + SCBPTR); + outb(info_scb, p->base + SCBPTR); + scb_offset = SCB_BUSYTARGETS + (target & 0x03); + busy_scbid = inb(p->base + scb_offset); + if (unbusy) { - /* - * targets on the Second channel or above id 7 store info in byte two - * of ACTIVE - */ - active_port++; + outb(SCB_LIST_NULL, p->base + scb_offset); } - active = inb(active_port); - active &= ~(0x01 << (target & 0x07)); - outb(active, active_port); + outb(active_scb, p->base + SCBPTR); + return (busy_scbid); } /*+F************************************************************************* * Function: - * aic7xxx_allocate_scb + * aic7xxx_find_scb * * Description: - * Get a free SCB either from one already assigned to a hardware - * slot, or one that will require an SCB to be paged out before - * use. If there are none, attempt to allocate a new one. + * Look through the SCB array of the card and attempt to find the + * hardware SCB that corresponds to the passed in SCB. Return + * SCB_LIST_NULL if unsuccessful. This routine assumes that the + * card is already paused. *-F*************************************************************************/ -static struct aic7xxx_scb * -aic7xxx_allocate_scb(struct aic7xxx_host *p) +static unsigned char +aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - struct aic7xxx_scb *scbp = NULL; - int maxscbs; + unsigned char saved_scbptr; + unsigned char curindex; - scbp = p->scb_link->free_scbs.head; - if (scbp != NULL) - { - scbq_remove_head(&p->scb_link->free_scbs); - } - else + saved_scbptr = inb(p->base + SCBPTR); + curindex = 0; + for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++) { - /* - * This should always be NULL if paging is not enabled. - */ - scbp = p->page_scbs.head; - if (scbp != NULL) + outb(curindex, p->base + SCBPTR); + if (inb(p->base + SCB_TAG) == scb->hscb->tag) { - scbq_remove_head(&p->page_scbs); + break; } - else + } + outb(saved_scbptr, p->base + SCBPTR); + if (curindex >= p->scb_data->maxhscbs) + { + curindex = SCB_LIST_NULL; + } + + return (curindex); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_allocate_sglist + * + * Description: + * Allocate a hardware SG array. + *-F*************************************************************************/ +static struct hw_scatterlist * +aic7xxx_allocate_sglist(void) +{ + int alloc_size; + struct hw_scatterlist *sg = NULL; + + if (aic7xxx_next_sg_array == NULL) + { + alloc_size = sizeof (struct hw_scatterlist) * MAX_SG; + aic7xxx_sg_arrays_free = PAGE_SIZE / alloc_size; + alloc_size = alloc_size * aic7xxx_sg_arrays_free; + if (alloc_size == 0) + { + panic("aic7xxx: SG list doesn't fit in a page.\n"); + } +#ifdef DMA_KMALLOC + aic7xxx_next_sg_array = (struct hw_scatterlist *) + kmalloc(alloc_size, GFP_ATOMIC | GFP_DMA); +#else + aic7xxx_next_sg_array = (struct hw_scatterlist *) + kmalloc(alloc_size, GFP_ATOMIC); +#endif + } + if (aic7xxx_next_sg_array != NULL) + { + sg = aic7xxx_next_sg_array; + aic7xxx_sg_arrays_free = aic7xxx_sg_arrays_free - 1; + if (aic7xxx_sg_arrays_free == 0) + { + aic7xxx_next_sg_array = NULL; + } + else { /* - * Set limit the SCB allocation to the maximum number of - * hardware SCBs if paging is not enabled; otherwise use - * the maximum (255). + * Point to the next available SG array in the pool. */ - if (p->flags & PAGE_ENABLED) - maxscbs = p->maxscbs; - else - maxscbs = p->maxhscbs; - if (p->scb_link->numscbs < maxscbs) - { - int scb_index = p->scb_link->numscbs; - int scb_size = sizeof(struct aic7xxx_scb); + aic7xxx_next_sg_array = &(aic7xxx_next_sg_array[MAX_SG]); + } + } + return (sg); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_allocate_scb + * + * Description: + * Get an SCB from the free list or by allocating a new one. + *-F*************************************************************************/ +static struct aic7xxx_scb * +aic7xxx_allocate_scb(struct aic7xxx_host *p) +{ + struct aic7xxx_scb *scbp = NULL; + struct aic7xxx_hwscb *hscbp = NULL; +#ifdef AGRESSIVE + long processor_flags; + + save_flags(processor_flags); + cli(); +#endif + + scbp = p->scb_data->free_scbs.head; + if (scbp != NULL) + { + scbq_remove_head(&p->scb_data->free_scbs); + } + else + { + if (p->scb_data->numscbs < p->scb_data->maxscbs) + { + int scb_index = p->scb_data->numscbs; + int scb_size = sizeof(struct aic7xxx_scb); - p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA); - scbp = (p->scb_array[scb_index]); - if (scbp != NULL) + scbp = kmalloc(scb_size, GFP_ATOMIC); + if (scbp != NULL) + { + memset(scbp, 0, sizeof(struct aic7xxx_scb)); + hscbp = &p->scb_data->hscbs[scb_index]; + scbp->hscb = hscbp; + scbp->sg_list = aic7xxx_allocate_sglist(); + if (scbp->sg_list == NULL) { - memset(scbp, 0, sizeof(*scbp)); - scbp->tag = scb_index; - if (scb_index < p->maxhscbs) - scbp->position = scb_index; - else - scbp->position = SCB_LIST_NULL; - p->scb_link->numscbs++; + kfree(scbp); + } + else + { + memset(hscbp, 0, sizeof(struct aic7xxx_hwscb)); + hscbp->tag = scb_index; + p->scb_data->numscbs++; + /* + * Place in the scb array; never is removed. + */ + p->scb_data->scb_array[scb_index] = scbp; } } } } +#ifdef AIC7XXX_DEBUG if (scbp != NULL) { -#ifdef AIC7XXX_DEBUG - p->scb_link->activescbs++; -#endif + p->activescbs++; } +#endif + +#ifdef AGRESSIVE + restore_flags(processor_flags); +#endif return (scbp); } @@ -1581,6 +1945,7 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p) cmd = p->completeq.head; p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble; cmd->host_scribble = NULL; + p->device_status[TARGET_INDEX(cmd)].active_cmds--; cmd->scsi_done(cmd); } p->completeq.tail = NULL; @@ -1591,53 +1956,29 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p) * aic7xxx_free_scb * * Description: - * Free the scb and update the page, waiting, free scb lists. + * Free the scb and insert into the free scb list. *-F*************************************************************************/ static void aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - struct aic7xxx_scb *wscb; + struct aic7xxx_hwscb *hscb; + long flags; + + hscb = scb->hscb; + save_flags(flags); + cli(); - scb->state = SCB_FREE; + scb->flags = SCB_FREE; scb->cmd = NULL; - scb->control = 0; - scb->state = 0; + hscb->control = 0; + hscb->target_status = 0; - if (scb->position == SCB_LIST_NULL) - { - scbq_insert_head(&p->page_scbs, scb); - } - else - { - /* - * If there are any SCBS on the waiting queue, assign the slot of this - * "freed" SCB to the first one. We'll run the waiting queues after - * all command completes for a particular interrupt are completed or - * when we start another command. - */ - wscb = p->waiting_scbs.head; - if (wscb != NULL) - { - scbq_remove_head(&p->waiting_scbs); - wscb->position = scb->position; - scbq_insert_tail(&p->assigned_scbs, wscb); - wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ; - - /* - * The "freed" SCB will need to be assigned a slot before being - * used, so put it in the page_scbs queue. - */ - scb->position = SCB_LIST_NULL; - scbq_insert_head(&p->page_scbs, scb); - } - else - { - scbq_insert_head(&p->scb_link->free_scbs, scb); - } + scbq_insert_head(&p->scb_data->free_scbs, scb); #ifdef AIC7XXX_DEBUG - p->scb_link->activescbs--; /* For debugging purposes. */ + p->activescbs--; /* For debugging purposes. */ #endif - } + + restore_flags(flags); } /*+F************************************************************************* @@ -1652,68 +1993,113 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { Scsi_Cmnd *cmd = scb->cmd; + if (scb->flags & SCB_RECOVERY_SCB) + { + p->flags &= ~IN_TIMEOUT; + } + if (cmd->result == DID_OK) + { + if (scb->flags & SCB_ABORTED) + { + cmd->result = (DID_RESET << 16); + } + } + if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0) + { + unsigned short mask; + + mask = 0x01 << TARGET_INDEX(scb->cmd); + if (scb->flags & SCB_MSGOUT_WDTR) + { + p->wdtr_pending &= ~mask; + } + if (scb->flags & SCB_MSGOUT_SDTR) + { + p->sdtr_pending &= ~mask; + } + } aic7xxx_free_scb(p, scb); aic7xxx_queue_cmd_complete(p, cmd); +#ifdef AIC7XXX_PROC_STATS + { + int actual; + + /* + * XXX: we should actually know how much actually transferred + * XXX: for each command, but apparently that's too difficult. + */ + actual = aic7xxx_length(cmd, 0); + if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0) + && (aic7xxx_error(cmd) == 0)) + { + struct aic7xxx_xferstats *sp; + long *ptr; + int x; + + sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07]; + sp->xfers++; + + if (cmd->request.cmd == WRITE) + { + sp->w_total++; + sp->w_total512 += (actual >> 9); + ptr = sp->w_bins; + } + else + { + sp->r_total++; + sp->r_total512 += (actual >> 9); + ptr = sp->r_bins; + } + for (x = 9; x <= 17; x++) + { + if (actual < (1 << x)) + { + ptr[x - 9]++; + break; + } + } + if (x > 17) + { + ptr[x - 9]++; + } + } + } +#endif /* AIC7XXX_PROC_STATS */ } /*+F************************************************************************* * Function: - * aic7xxx_done_aborted_scbs + * aic7xxx_run_done_queue * * Description: - * Calls the scsi_done() for the Scsi_Cmnd of each scb in the - * aborted list, and adds each scb to the free list. + * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the + * aborted list, and adds each scb to the free list. If complete + * is TRUE, we also process the commands complete list. *-F*************************************************************************/ static void -aic7xxx_done_aborted_scbs(struct aic7xxx_host *p) +aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete) { - Scsi_Cmnd *cmd; struct aic7xxx_scb *scb; int i; - for (i = 0; i < p->scb_link->numscbs; i++) + for (i = 0; i < p->scb_data->numscbs; i++) { - scb = (p->scb_array[i]); - if (scb->state & SCB_QUEUED_FOR_DONE) + scb = p->scb_data->scb_array[i]; + if (scb->flags & SCB_QUEUED_FOR_DONE) { #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n", - scb->position, TCL_OF_SCB(scb)); + printk("(scsi%d:%d:%d) Aborting scb %d\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); #endif - /* - * Process the command after marking the scb as free - * and adding it to the free list. - */ - cmd = scb->cmd; - p->device_status[TARGET_INDEX(cmd)].flags = 0; - aic7xxx_free_scb(p, scb); - cmd->scsi_done(cmd); /* call the done function */ + aic7xxx_done(p, scb); } } -} - -/*+F************************************************************************* - * Function: - * aic7xxx_add_waiting_scb - * - * Description: - * Add this SCB to the head of the "waiting for selection" list. - *-F*************************************************************************/ -static void -aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb) -{ - unsigned char next; - unsigned char curscb; - - curscb = inb(SCBPTR + base); - next = inb(WAITING_SCBH + base); - - outb(scb->position, SCBPTR + base); - outb(next, SCB_NEXT + base); - outb(scb->position, WAITING_SCBH + base); - - outb(curscb, SCBPTR + base); + if (complete) + { + aic7xxx_done_cmds_complete(p); + } } /*+F************************************************************************* @@ -1726,26 +2112,23 @@ aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb) *-F*************************************************************************/ static unsigned char aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb, - unsigned char prev) + unsigned char scbpos, unsigned char prev) { unsigned char curscb, next; - int target = (scb->target_channel_lun >> 4) & 0x0F; - char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; - int base = p->base; /* * Select the SCB we want to abort and pull the next pointer out of it. */ - curscb = inb(SCBPTR + base); - outb(scb->position, SCBPTR + base); - next = inb(SCB_NEXT + base); + curscb = inb(p->base + SCBPTR); + outb(scbpos, p->base + SCBPTR); + next = inb(p->base + SCB_NEXT); /* * Clear the necessary fields */ - outb(0, SCB_CONTROL + base); - outb(SCB_LIST_NULL, SCB_NEXT + base); - aic7xxx_unbusy_target(target, channel, base); + outb(0, p->base + SCB_CONTROL); + + aic7xxx_add_curscb_to_free_list(p); /* * Update the waiting list @@ -1755,27 +2138,97 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb, /* * First in the list */ - outb(next, WAITING_SCBH + base); + outb(next, p->base + WAITING_SCBH); } else { /* * Select the scb that pointed to us and update its next pointer. */ - outb(prev, SCBPTR + base); - outb(next, SCB_NEXT + base); + outb(prev, p->base + SCBPTR); + outb(next, p->base + SCB_NEXT); } /* * Point us back at the original scb position and inform the SCSI * system that the command has been aborted. */ - outb(curscb, SCBPTR + base); - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + outb(curscb, p->base + SCBPTR); + scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + scb->flags &= ~SCB_ACTIVE; scb->cmd->result = (DID_RESET << 16); return (next); } +/*+F************************************************************************* + * Function: + * aic7xxx_search_qinfifo + * + * Description: + * Search the queue-in FIFO for matching SCBs and conditionally + * requeue. Returns the number of matching SCBs. + *-F*************************************************************************/ +static int +aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel, + int lun, unsigned char tag, int flags, int requeue) +{ + unsigned char saved_queue[AIC7XXX_MAXSCB]; + int queued = inb(p->base + QINCNT) & p->qcntmask; + int i; + int found; + struct aic7xxx_scb *scbp; + scb_queue_type removed_scbs; + + found = 0; + scbq_init (&removed_scbs); + for (i = 0; i < (queued - found); i++) + { + saved_queue[i] = inb(p->base + QINFIFO); + scbp = p->scb_data->scb_array[saved_queue[i]]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + /* + * We found an scb that needs to be removed. + */ + if (requeue) + { + scbq_insert_head(&removed_scbs, scbp); + } + else + { + scbp->flags = flags; + scbp->flags &= ~SCB_ACTIVE; + /* + * XXX - Don't know what error to use here. + */ + aic7xxx_error(scbp->cmd) = DID_RESET; + } + i--; + found++; + } + } + /* Now put the saved scbs back. */ + for (queued = 0; queued < i; queued++) + outb(saved_queue[queued], p->base + QINFIFO); + + if (requeue) + { + scbp = removed_scbs.head; + while (scbp != NULL) + { + scbq_remove_head(&removed_scbs); + /* + * XXX - Shouldn't we be adding this to the free list? + */ + scbq_insert_head(&p->waiting_scbs, scbp); + scbp->flags |= SCB_WAITINGQ; + scbp = removed_scbs.head; + } + } + + return (found); +} + /*+F************************************************************************* * Function: * aic7xxx_reset_device @@ -1785,162 +2238,310 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb, * all active and queued scbs for that target/channel. *-F*************************************************************************/ static int -aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel) +aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel, + int lun, unsigned char tag) { - int base = p->base; - struct aic7xxx_scb *scb; + struct aic7xxx_scb *scbp; unsigned char active_scb; int i = 0; - int found = 0; + int found; /* * Restore this when we're done */ - active_scb = inb(SCBPTR + base); + active_scb = inb(p->base + SCBPTR); #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n", - target, channel, active_scb); + printk("(scsi%d:%d:%d) Reset device, active_scb %d\n", + p->host_no, target, CHAN_TO_INT(channel), active_scb); #endif + /* - * Search the QINFIFO. + * Deal with the busy target and linked next issues. */ { - int saved_queue[AIC7XXX_MAXSCB]; - int queued = inb(QINCNT + base) & p->qcntmask; + int min_target, max_target; + unsigned char busy_scbid; - for (i = 0; i < (queued - found); i++) + /* Make all targets 'relative' to bus A. */ + if (target == ALL_TARGETS) { - saved_queue[i] = inb(QINFIFO + base); - outb(saved_queue[i], SCBPTR + base); - scb = (p->scb_array[inb(SCB_TAG + base)]); - if (aic7xxx_match_scb(scb, target, channel)) + switch (channel) { - /* - * We found an scb that needs to be aborted. - */ -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n", - saved_queue[i], TCL_OF_SCB(scb)); -#endif - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; - scb->cmd->result = (DID_RESET << 16); - outb(0, SCB_CONTROL + base); - i--; - found++; + case 'A': + min_target = 0; + max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15; + break; + case 'B': + min_target = 8; + max_target = 15; + break; + case ALL_CHANNELS: + default: + min_target = 0; + max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15; + break; } } - /* - * Now put the saved scbs back. - */ - for (queued = 0; queued < i; queued++) + else + { + min_target = target + channel == 'B' ? 8 : 0; + max_target = min_target; + } + + for (i = min_target; i <= max_target; i++) { - outb(saved_queue[queued], QINFIFO + base); + busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE); + if (busy_scbid < p->scb_data->numscbs) + { + struct aic7xxx_scb *busy_scb; + struct aic7xxx_scb *next_scb; + unsigned char next_scbid; + + busy_scb = p->scb_data->scb_array[busy_scbid]; + + next_scbid = busy_scb->hscb->data_count >> 24; + + if (next_scbid == SCB_LIST_NULL) + { + busy_scbid = aic7xxx_find_scb(p, busy_scb); + + if (busy_scbid != SCB_LIST_NULL) + { + outb(busy_scbid, p->base + SCBPTR); + next_scbid = inb(p->base + SCB_LINKED_NEXT); + } + } + + if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag)) + { + aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE); + } + + if (next_scbid != SCB_LIST_NULL) + { + next_scb = p->scb_data->scb_array[next_scbid]; + if (aic7xxx_match_scb(next_scb, target, channel, lun, tag)) + { + continue; + } + /* Requeue for later processing */ + scbq_insert_head(&p->waiting_scbs, next_scb); + next_scb->flags |= SCB_WAITINGQ; + } + } } } + found = aic7xxx_search_qinfifo(p, target, channel, lun, tag, + SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE); + /* * Search waiting for selection list. */ { - unsigned char next, prev; + unsigned char next, prev, scb_index; - next = inb(WAITING_SCBH + base); /* Start at head of list. */ + next = inb(p->base + WAITING_SCBH); /* Start at head of list. */ prev = SCB_LIST_NULL; while (next != SCB_LIST_NULL) { - outb(next, SCBPTR + base); - scb = (p->scb_array[inb(SCB_TAG + base)]); - /* - * Select the SCB. - */ - if (aic7xxx_match_scb(scb, target, channel)) + outb(next, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + if (scb_index >= p->scb_data->numscbs) + { + panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n", + scb_index, p->scb_data->numscbs); + } + scbp = p->scb_data->scb_array[scb_index]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) { - next = aic7xxx_abort_waiting_scb(p, scb, prev); + unsigned char linked_next; + + next = aic7xxx_abort_waiting_scb(p, scbp, next, prev); + linked_next = inb(p->base + SCB_LINKED_NEXT); + if (linked_next != SCB_LIST_NULL) + { + struct aic7xxx_scb *next_scb; + /* + * Requeue the waiting SCB via the waiting list. + */ + next_scb = p->scb_data->scb_array[linked_next]; + if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag)) + { + scbq_insert_head(&p->waiting_scbs, next_scb); + next_scb->flags |= SCB_WAITINGQ; + } + } found++; } else { prev = next; - next = inb(SCB_NEXT + base); + next = inb(p->base + SCB_NEXT); + } + } + } + + /* + * Go through disconnected list and remove any entries we have queued + * for completion, zeroing their control byte too. + */ + { + unsigned char next, prev, scb_index; + + next = inb(p->base + DISCONNECTED_SCBH); + prev = SCB_LIST_NULL; + + while (next != SCB_LIST_NULL) + { + outb(next, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + if (scb_index > p->scb_data->numscbs) + { + panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, " + "num scbs = %d.\n", scb_index, p->scb_data->numscbs); + } + scbp = p->scb_data->scb_array[scb_index]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + next = aic7xxx_rem_scb_from_disc_list(p, next); + } + else + { + prev = next; + next = inb(p->base + SCB_NEXT); + } + } + } + + /* + * Go through the hardware SCB array looking for commands that + * were active but not on any list. + */ + for (i = 0; i < p->scb_data->maxhscbs; i++) + { + unsigned char scbid; + + outb(i, p->base + SCBPTR); + scbid = inb(p->base + SCB_TAG); + if (scbid < p->scb_data->numscbs) + { + scbp = p->scb_data->scb_array[scbid]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + aic7xxx_add_curscb_to_free_list(p); } } } /* * Go through the entire SCB array now and look for commands for - * for this target that are active. These are other (most likely + * for this target that are stillactive. These are other (most likely * tagged) commands that were disconnected when the reset occurred. */ - for (i = 0; i < p->scb_link->numscbs; i++) + for (i = 0; i < p->scb_data->numscbs; i++) { - scb = (p->scb_array[i]); - if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel)) + scbp = p->scb_data->scb_array[i]; + if (((scbp->flags & SCB_ACTIVE) != 0) && + aic7xxx_match_scb(scbp, target, channel, lun, tag)) { - /* - * Ensure the target is "free" - */ - aic7xxx_unbusy_target(target, channel, base); - if (! (scb->state & SCB_PAGED_OUT)) + scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + scbp->flags &= ~SCB_ACTIVE; + aic7xxx_error(scbp->cmd) = DID_RESET; + + found++; + + if ((scbp->flags & SCB_WAITINGQ) != 0) { - outb(scb->position, SCBPTR + base); - outb(0, SCB_CONTROL + base); + scbq_remove(&p->waiting_scbs, scbp); + scbp->flags &= ~SCB_WAITINGQ; } - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; - scb->cmd->result = (DID_RESET << 16); - found++; } } - outb(active_scb, SCBPTR + base); + outb(active_scb, p->base + SCBPTR); return (found); } /*+F************************************************************************* * Function: - * aic7xxx_reset_current_bus + * aic7xxx_clear_intstat * * Description: - * Reset the current SCSI bus. + * Clears the interrupt status. *-F*************************************************************************/ static void -aic7xxx_reset_current_bus(int base) +aic7xxx_clear_intstat(struct aic7xxx_host *p) { - outb(SCSIRSTO, SCSISEQ + base); - udelay(1000); - outb(0, SCSISEQ + base); + /* Clear any interrupt conditions this may have caused. */ + outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0); + outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR | + CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); } /*+F************************************************************************* * Function: - * aic7xxx_reset_channel + * aic7xxx_reset_current_bus * * Description: - * Reset the channel. + * Reset the current SCSI bus. *-F*************************************************************************/ -static int -aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset) +static void +aic7xxx_reset_current_bus(struct aic7xxx_host *p) { - int base = p->base; - unsigned char sblkctl; - char cur_channel; - unsigned long offset, offset_max; - int found; + unsigned char scsiseq; - /* - * Clean up all the state information for the - * pending transactions on this bus. + /* Disable reset interrupts. */ + outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1); + + /* Turn on the bus reset. */ + scsiseq = inb(p->base + SCSISEQ); + outb(scsiseq | SCSIRSTO, p->base + SCSISEQ); + + udelay(1000); + + /* Turn off the bus reset. */ + outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ); + + aic7xxx_clear_intstat(p); + + /* Re-enable reset interrupts. */ + outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1); + + udelay(1000); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_reset_channel + * + * Description: + * Reset the channel. + *-F*************************************************************************/ +static int +aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset) +{ + unsigned long offset, offset_max; + int found; + unsigned char sblkctl; + char cur_channel; + + pause_sequencer(p); + /* + * Clean up all the state information for the pending transactions + * on this bus. */ - found = aic7xxx_reset_device(p, ALL_TARGETS, channel); + found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL); if (channel == 'B') { p->needsdtr |= (p->needsdtr_copy & 0xFF00); p->sdtr_pending &= 0x00FF; - outb(0, ACTIVE_B + base); - offset = TARG_SCRATCH + base + 8; - offset_max = TARG_SCRATCH + base + 16; + offset = TARG_SCRATCH + 8; + offset_max = TARG_SCRATCH + 16; } else { @@ -1950,132 +2551,100 @@ aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset) p->needwdtr = p->needwdtr_copy; p->sdtr_pending = 0x0; p->wdtr_pending = 0x0; - outb(0, ACTIVE_A + base); - outb(0, ACTIVE_B + base); - offset = TARG_SCRATCH + base; - offset_max = TARG_SCRATCH + base + 16; + offset = TARG_SCRATCH; + offset_max = TARG_SCRATCH + 16; } else { + /* Channel A */ p->needsdtr |= (p->needsdtr_copy & 0x00FF); p->sdtr_pending &= 0xFF00; - outb(0, ACTIVE_A + base); - offset = TARG_SCRATCH + base; - offset_max = TARG_SCRATCH + base + 8; + offset = TARG_SCRATCH; + offset_max = TARG_SCRATCH + 8; } } + while (offset < offset_max) { /* - * Revert to async/narrow transfers - * until we renegotiate. + * Revert to async/narrow transfers until we renegotiate. */ u_char targ_scratch; - targ_scratch = inb(offset); + + targ_scratch = inb(p->base + offset); targ_scratch &= SXFR; - outb(targ_scratch, offset); + outb(targ_scratch, p->base + offset); offset++; } /* * Reset the bus and unpause/restart the controller */ - - /* - * Case 1: Command for another bus is active - */ - sblkctl = inb(SBLKCTL + base); + sblkctl = inb(p->base + SBLKCTL); cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A'; if (cur_channel != channel) { + /* + * Case 1: Command for another bus is active + */ #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n", - channel); + printk("scsi%d: Stealthily resetting channel %c\n", + p->host_no, channel); #endif /* - * Stealthily reset the other bus without upsetting the current bus + * Stealthily reset the other bus without upsetting the current bus. */ - outb(sblkctl ^ SELBUSB, SBLKCTL + base); + outb(sblkctl ^ SELBUSB, p->base + SBLKCTL); + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); if (initiate_reset) { - aic7xxx_reset_current_bus(base); + aic7xxx_reset_current_bus(p); + /* + * Cause the mid-level SCSI code to delay any further + * queueing by the bus settle time for us. + */ + p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); } - outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - outb(sblkctl, SBLKCTL + base); - - UNPAUSE_SEQUENCER(p); + outb(0, p->base + SCSISEQ); + aic7xxx_clear_intstat(p); + outb(sblkctl, p->base + SBLKCTL); + unpause_sequencer(p, /* unpause_always */ FALSE); } - /* - * Case 2: A command from this bus is active or we're idle - */ else { + /* + * Case 2: A command from this bus is active or we're idle. + */ #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Resetting current channel %c\n", - channel); + printk("scsi%d: Resetting current channel %c\n", + p->host_no, channel); #endif + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); if (initiate_reset) { - aic7xxx_reset_current_bus(base); + aic7xxx_reset_current_bus(p); + /* + * Cause the mid-level SCSI code to delay any further + * queueing by the bus settle time for us. + */ +#if 0 + p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); +#endif } - outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - RESTART_SEQUENCER(p); + outb(0, p->base + SCSISEQ); + aic7xxx_clear_intstat(p); + restart_sequencer(p); #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n"); + printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no); #endif } - /* - * Cause the mid-level SCSI code to delay any further - * queueing by the bus settle time for us. - */ - p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); - /* * Now loop through all the SCBs that have been marked for abortion, * and call the scsi_done routines. */ - aic7xxx_done_aborted_scbs(p); - return found; -} - -/*+F************************************************************************* - * Function: - * aic7xxx_page_scb - * - * Description: - * Swap in_scbp for out_scbp down in the cards SCB array. - * We assume that the SCB for out_scbp is already selected in SCBPTR. - * - *-F*************************************************************************/ -static inline void -aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp, - struct aic7xxx_scb *in_scbp) -{ - int index; - - /* Page-out */ -#if 0 -printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n", - out_scbp->cmd->target, in_scbp->cmd->target); -#endif - aic7xxx_getscb(p, out_scbp); - out_scbp->state |= SCB_PAGED_OUT; - if (!(out_scbp->control & TAG_ENB)) - { - /* Stick in non-tagged array */ - index = (out_scbp->target_channel_lun >> 4) | - (out_scbp->target_channel_lun & SELBUSB); - p->pagedout_ntscbs[index] = out_scbp; - } - - /* Page-in */ - in_scbp->position = out_scbp->position; - out_scbp->position = SCB_LIST_NULL; - aic7xxx_putscb(p, in_scbp); - in_scbp->state &= ~SCB_PAGED_OUT; + aic7xxx_run_done_queue(p, /*complete*/ TRUE); + return (found); } /*+F************************************************************************* @@ -2083,1159 +2652,1326 @@ printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n", * aic7xxx_run_waiting_queues * * Description: - * Scan the assigned_scbs and waiting_scbs queues. For scbs in the - * assigned_scbs queue, we download and start them. For scbs in the - * waiting_scbs queue, we page in as many as we can being careful - * not to cause a deadlock for a reconnecting target. - * + * Scan the awaiting_scbs queue downloading and starting as many + * scbs as we can. *-F*************************************************************************/ static inline void aic7xxx_run_waiting_queues(struct aic7xxx_host *p) { struct aic7xxx_scb *scb; - u_char cur_scb, intstat; - u_long base = p->base; - long flags; - if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL)) + if (p->waiting_scbs.head == NULL) return; - save_flags(flags); - cli(); - - PAUSE_SEQUENCER(p); - cur_scb = inb(SCBPTR + base); - intstat = inb(INTSTAT + base); - + pause_sequencer(p); /* * First handle SCBs that are waiting but have been assigned a slot. */ - scb = p->assigned_scbs.head; - while (scb != NULL) - { - scbq_remove_head(&(p->assigned_scbs)); - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - /* Mark this as an active command. */ - scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE; - outb(scb->position, QINFIFO + base); - scb = p->assigned_scbs.head; - } - - /* Now deal with SCBs that require paging. */ scb = p->waiting_scbs.head; - if (scb != NULL) + while (scb != NULL) { - u_char disc_scb = inb(DISCONNECTED_SCBH + base); - u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN); - int count = 0; - u_char next_scb; - - while (scb != NULL) + if (p->curqincnt >= p->qfullcount) { - /* Attempt to page this SCB in */ - if (disc_scb == SCB_LIST_NULL) - break; - - /* - * Advance disc_scb to the next one in the list. - */ - outb(disc_scb, SCBPTR + base); - next_scb = inb(SCB_NEXT + base); - - /* - * We have to be careful about when we allow an SCB to be paged out. - * There must always be at least one slot availible for a reconnecting - * target in case it references an SCB that has been paged out. Our - * heuristic is that either the disconnected list has at least two - * entries in it or there is one entry and the sequencer is activily - * working on an SCB which implies that it will either complete or - * disconnect before another reconnection can occur. - */ - if ((next_scb != SCB_LIST_NULL) || active) + p->curqincnt = inb(p->base + QINCNT) & p->qcntmask; + if (p->curqincnt >= p->qfullcount) { - u_char out_scbi; - struct aic7xxx_scb *out_scbp; - - scbq_remove_head(&(p->waiting_scbs)); - - /* - * Find the in-core SCB for the one we're paging out. - */ - out_scbi = inb(SCB_TAG + base); - out_scbp = (p->scb_array[out_scbi]); - - /* Do the page out and mark the paged in SCB as active. */ - aic7xxx_page_scb(p, out_scbp, scb); - - /* Mark this as an active command. */ - scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE; - - /* Queue the command */ - outb(scb->position, QINFIFO + base); - count++; - - /* Advance to the next disconnected SCB */ - disc_scb = next_scb; - scb = p->waiting_scbs.head; + break; } - else - scb = NULL; } - if (count) + /* + * We have some space. + */ + scbq_remove_head(&(p->waiting_scbs)); + scb->flags &= ~SCB_WAITINGQ; + + outb(scb->hscb->tag, p->base + QINFIFO); + + if ((p->flags & PAGE_ENABLED) != 0) { - /* - * Update the head of the disconnected list. + /* + * We only care about this statistic when paging + * since it's impossible to overflow the qinfifo + * in the non-paging case. */ - outb(disc_scb, DISCONNECTED_SCBH + base); - if (disc_scb != SCB_LIST_NULL) - { - outb(disc_scb, SCBPTR + base); - outb(SCB_LIST_NULL, SCB_PREV + base); - } + p->curqincnt++; } + scb = p->waiting_scbs.head; } - /* Restore old position */ - outb(cur_scb, SCBPTR + base); - /* - * Guard against unpausing the sequencer if there is an interrupt - * waiting to happen. - */ - if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) - { - UNPAUSE_SEQUENCER(p); - } + unpause_sequencer(p, FALSE); +} - restore_flags(flags); +/*+F************************************************************************* + * Function: + * aic7xxx_construct_sdtr + * + * Description: + * Constucts a synchronous data transfer message in the message + * buffer on the sequencer. + *-F*************************************************************************/ +static void +aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte, + unsigned char period, unsigned char offset) +{ + outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte); + outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte); + outb(MSG_EXT_SDTR, p->base + MSG_OUT + 2 + start_byte); + outb(period, p->base + MSG_OUT + 3 + start_byte); + outb(offset, p->base + MSG_OUT + 4 + start_byte); + outb(start_byte + 5, p->base + MSG_LEN); } /*+F************************************************************************* * Function: - * aic7xxx_isr + * aic7xxx_construct_wdtr * * Description: - * SCSI controller interrupt handler. + * Constucts a wide data transfer message in the message buffer + * on the sequencer. + *-F*************************************************************************/ +static void +aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte, + unsigned char bus_width) +{ + outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte); + outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte); + outb(MSG_EXT_WDTR, p->base + MSG_OUT + 2 + start_byte); + outb(bus_width, p->base + MSG_OUT + 3 + start_byte); + outb(start_byte + 4, p->base + MSG_LEN); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_calc_residual * - * NOTE: Since we declared this using SA_INTERRUPT, interrupts should - * be disabled all through this function unless we say otherwise. + * Description: + * Calculate the residual data not yet transferred. *-F*************************************************************************/ static void -aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) +aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - int base, intstat, actual, scb_index, run_aborted_queue = FALSE; - struct aic7xxx_host *p; - struct aic7xxx_scb *scb = NULL; - short transfer; - unsigned char ha_flags, scsi_id, bus_width; - unsigned char offset, rate, scratch, scratch_offset; - unsigned char max_offset, rej_byte; - unsigned short target_mask; - char channel; - unsigned int addr; /* must be 32 bits */ + struct aic7xxx_hwscb *hscb; Scsi_Cmnd *cmd; + int actual; - p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; + cmd = scb->cmd; + hscb = scb->hscb; /* - * Search for the host with a pending interrupt. If we can't find - * one, then we've encountered a spurious interrupt. + * Don't destroy valid residual information with + * residual coming from a check sense operation. */ - while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND)) + if (((scb->hscb->control & DISCONNECTED) == 0) && + (scb->flags & SCB_SENSE) == 0) { - if (p->next == NULL) - { - p = NULL; - } - else + /* + * We had an underflow. At this time, there's only + * one other driver that bothers to check for this, + * and cmd->underflow seems to be set rather half- + * heartedly in the higher-level SCSI code. + */ + actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count); + + actual -= (hscb->residual_data_count[2] << 16) | + (hscb->residual_data_count[1] << 8) | + hscb->residual_data_count[0]; + + if (actual < cmd->underflow) { - p = (struct aic7xxx_host *) p->next->hostdata; + printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - " + "Wanted at least %u, got %u, residual SG count %d.\n", + p->host_no, TC_OF_SCB(scb), cmd->underflow, actual, + hscb->residual_SG_segment_count); + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + aic7xxx_status(cmd) = hscb->target_status; } } - if (p == NULL) - return; - /* - * Keep track of interrupts for /proc/scsi + * Clean out the residual information in the SCB for the + * next consumer. */ - p->isr_count++; + hscb->residual_data_count[2] = 0; + hscb->residual_data_count[1] = 0; + hscb->residual_data_count[0] = 0; + hscb->residual_SG_segment_count = 0; +} - if (!(p->flags & A_SCANNED) && (p->isr_count == 1)) +/*+F************************************************************************* + * Function: + * aic7xxx_handle_device_reset + * + * Description: + * Interrupt handler for sequencer interrupts (SEQINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel) +{ + unsigned short targ_mask; + unsigned char targ_scratch; + int scratch_offset = target; + int found; + + if (channel == 'B') { - /* - * We must only have one card at this IRQ and it must have been - * added to the board data before the spurious interrupt occurred. - * It is sufficient that we check isr_count and not the spurious - * interrupt count. - */ - printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n"); - return; + scratch_offset += 8; } - - base = p->base; + targ_mask = (0x01 << scratch_offset); /* - * Handle all the interrupt sources - especially for SCSI - * interrupts, we won't get a second chance at them. + * Go back to async/narrow transfers and renegotiate. */ - intstat = inb(INTSTAT + base); + p->needsdtr |= p->needsdtr_copy & targ_mask; + p->needwdtr |= p->needwdtr_copy & targ_mask; + p->sdtr_pending &= ~targ_mask; + p->wdtr_pending &= ~targ_mask; + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + targ_scratch &= SXFR; + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); + printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, " + "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found); + aic7xxx_run_done_queue(p, /*complete*/ TRUE); +} - /* - * Indicate that we're in the interrupt handler. - */ - p->flags |= IN_ISR; +/*+F************************************************************************* + * Function: + * aic7xxx_handle_seqint + * + * Description: + * Interrupt handler for sequencer interrupts (SEQINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) +{ + struct aic7xxx_scb *scb; + unsigned short target_mask; + unsigned char target, scratch_offset; + char channel; - if (intstat & BRKADRINT) + if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0) { - int i; - unsigned char errno = inb(ERROR + base); - - printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno); - for (i = 0; i < NUMBER(hard_error); i++) - { - if (errno & hard_error[i].errno) - { - printk(KERN_ERR " %s\n", hard_error[i].errmesg); - } - } - panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no, - inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base)); + target = (inb(p->base + SELID) >> 4) & 0x0F; + } + else + { + target = (inb(p->base + SCSIID) >> 4) & 0x0F; + } + scratch_offset = target; + channel = 'A'; + if (inb(p->base + SBLKCTL) & SELBUSB) + { + channel = 'B'; + scratch_offset += 8; } + target_mask = (0x01 << scratch_offset); - if (intstat & SEQINT) + switch (intstat & SEQINT_MASK) { - /* - * Although the sequencer is paused immediately on - * a SEQINT, an interrupt for a SCSIINT condition will - * unpaused the sequencer before this point. - */ - PAUSE_SEQUENCER(p); + case NO_MATCH: + { + /* + * This could be for a normal abort request. Figure out + * which SCB we were trying to find and only give an error + * if we didn't ask for this to happen. + */ + unsigned char scb_index; + unsigned char busy_scbid; + unsigned char arg1; - scsi_id = (inb(SCSIID + base) >> 4) & 0x0F; - scratch_offset = scsi_id; - channel = 'A'; - if (inb(SBLKCTL + base) & SELBUSB) - { - channel = 'B'; - scratch_offset += 8; - } - target_mask = (0x01 << scratch_offset); + busy_scbid = aic7xxx_index_busy_target(p, target, channel, + /*unbusy*/ FALSE); + arg1 = inb(p->base + ARG_1); - switch (intstat & SEQINT_MASK) - { - case NO_MATCH: - if (p->flags & PAGE_ENABLED) + if (arg1 == SCB_LIST_NULL) { - /* SCB Page-in request */ - struct aic7xxx_scb *outscb; - u_char arg_1 = inb(ARG_1 + base); - int use_disconnected = FALSE; - - /* - * The sequencer expects this value upon return. Assume - * we will find the paged out SCB and set the value now. - * If we don't, and one of the methods used to acquire an - * SCB calls aic7xxx_done(), we will end up in our queue - * routine and unpause the sequencer without giving it the - * correct return value, which causes a hang. - */ - outb(SCB_PAGEDIN, RETURN_1 + base); - if (arg_1 == SCB_LIST_NULL) - { - /* Non-tagged command */ - int index = scsi_id; - if (channel == 'B') - { - index |= SELBUSB; - } - scb = p->pagedout_ntscbs[index]; - } - else - scb = (p->scb_array[arg_1]); + /* untagged request */ + scb_index = busy_scbid; + } + else + { + scb_index = arg1; + } - if (!(scb->state & SCB_PAGED_OUT)) + if (scb_index < p->scb_data->numscbs) + { + scb = p->scb_data->scb_array[scb_index]; + if (scb->hscb->control & ABORT_SCB) { - printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); + /* + * We expected this. Let the busfree handler take care + * of this when we the abort is finially sent. Set + * IDENTIFY_SEEN so that the busfree handler knows that + * there is an SCB to cleanup. + */ + outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS); + printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n", + p->host_no, TC_OF_SCB(scb)); break; } + } + printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting " + "target - Issuing BUS DEVICE RESET.\n", + p->host_no, target, CHAN_TO_INT(channel)); + + printk(KERN_WARNING " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n", + inb(p->base + SAVED_TCL), arg1, + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + aic7xxx_handle_device_reset(p, target, channel); + } + break; - /* - * Now to pick the SCB to page out. Either take a free SCB, an - * assigned SCB, an SCB that just completed, or the first one - * on the disconnected SCB list. - */ - if (p->scb_link->free_scbs.head != NULL) - { - outscb = p->scb_link->free_scbs.head; - scbq_remove_head(&p->scb_link->free_scbs); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->page_scbs, outscb); - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - } - else if (p->assigned_scbs.head != NULL) - { - outscb = p->assigned_scbs.head; - scbq_remove_head(&p->assigned_scbs); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->waiting_scbs, outscb); - outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ; - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - } - else if (intstat & CMDCMPLT) - { - int scb_index; + case NO_MATCH_BUSY: + { + /* + * XXX - Leave this as a panic for the time being since it + * indicates a bug in the timeout code for this to happen. + */ + unsigned char scb_index; - outb(CLRCMDINT, CLRINT + base); - scb_index = inb(QOUTFIFO + base); - if (!(inb(QOUTCNT + base) & p->qcntmask)) - { - intstat &= ~CMDCMPLT; - } - outscb = (p->scb_array[scb_index]); - if (!(outscb->state & SCB_ACTIVE)) - { - printk(KERN_WARNING "scsi%d: No command for completed SCB %d " - "during NO_MATCH interrupt\n", scb_index, p->host_no); - use_disconnected = TRUE; - } - else - { - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16); - if ((outscb->cmd->flags & WAS_SENSE) && - !(outscb->cmd->flags & ASKED_FOR_SENSE)) - { - /* - * Got sense information. - */ - outscb->cmd->flags &= ASKED_FOR_SENSE; - } - p->device_status[TARGET_INDEX(outscb->cmd)].flags - |= DEVICE_SUCCESS; - aic7xxx_done(p, outscb); - } - } - else - { - use_disconnected = TRUE; - } - if (use_disconnected) - { - u_char tag; - u_char next; - u_char disc_scb = inb(DISCONNECTED_SCBH + base); - if (disc_scb != SCB_LIST_NULL) - { - outb(disc_scb, SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); - next = inb(SCB_NEXT + base); - if (next != SCB_LIST_NULL) - { - outb(next, SCBPTR + base); - outb(SCB_LIST_NULL, SCB_PREV + base); - outb(disc_scb, SCBPTR + base); - } - outb(next, DISCONNECTED_SCBH + base); - aic7xxx_page_scb(p, outscb, scb); - } - else if (inb(QINCNT + base) & p->qcntmask) - { - /* Pull one of our queued commands as a last resort. */ - disc_scb = inb(QINFIFO + base); - outb(disc_scb, SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); - if ((outscb->control & 0x23) != TAG_ENB) - { - /* - * This is not a simple tagged command so its position - * in the queue matters. Take the command at the end of - * the queue instead. - */ - int i; - int saved_queue[AIC7XXX_MAXSCB]; - int queued = inb(QINCNT + base) & p->qcntmask; - - /* Count the command we removed already */ - saved_queue[0] = disc_scb; - queued++; - - /* Empty the input queue. */ - for (i = 1; i < queued; i++) - { - saved_queue[i] = inb(QINFIFO + base); - } - - /* Put everyone back but the last entry. */ - queued--; - for (i = 0; i < queued; i++) - { - outb(saved_queue[i], QINFIFO + base); - } - - outb(saved_queue[queued], SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); - } - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->waiting_scbs, outscb); - outscb->state |= SCB_WAITINGQ; - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - } - else - { - printk(KERN_WARNING "scsi%d: Page-in request with no candidates " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); - } - } - } - else - { - printk(KERN_WARNING "scsi%d: No active SCB for reconnecting " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(0, SCB_CONTROL + base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); - } - break; + scb_index = inb(p->base + CUR_SCBID); + scb = p->scb_data->scb_array[scb_index]; - case BAD_PHASE: - panic("scsi%d: Unknown scsi bus phase.\n", p->host_no); - break; + panic("scsi%d: Target %d, channel %c, Target busy link failure, " + "but busy SCB exists!\n", + p->host_no, target, channel); + } + break; - case SEND_REJECT: - rej_byte = inb(REJBYTE + base); - if ((rej_byte & 0xF0) == 0x20) - { - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - printk(KERN_WARNING "scsi%d: Tagged message received without identify." - "Disabling tagged commands for target %d channel %c.\n", - p->host_no, scsi_id, channel); - scb->cmd->device->tagged_supported = 0; - scb->cmd->device->tagged_queue = 0; - } - else - { - printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received " - "from target %d channel %c.\n", - p->host_no, rej_byte, scsi_id, channel); - } - break; + case SEND_REJECT: + { + unsigned char rej_byte; - case NO_IDENT: - panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY " - "message. SAVED_TCL 0x%x.\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - break; + rej_byte = inb(p->base + REJBYTE); + printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) " + "received from target, SEQ_FLAGS=0x%x\n", + p->host_no, target, CHAN_TO_INT(channel), rej_byte, + inb(p->base + SEQ_FLAGS)); + } + break; - case SDTR_MSG: - /* - * Help the sequencer to translate the negotiated - * transfer rate. Transfer is 1/4 the period - * in ns as is returned by the sync negotiation - * message. So, we must multiply by four. - */ - transfer = (inb(ARG_1 + base) << 2); - offset = inb(ACCUM + base); - scratch = inb(TARG_SCRATCH + base + scratch_offset); - /* - * The maximum offset for a wide device is 0x08; for a - * 8-bit bus device the maximum offset is 0x0F. - */ - if (scratch & WIDEXFER) - { - max_offset = 0x08; - } - else - { - max_offset = 0x0F; - } - aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset), - scsi_id, channel); - /* - * Preserve the wide transfer flag. - */ - scratch = rate | (scratch & WIDEXFER); - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - if ((scratch & 0x0F) == 0) + case NO_IDENT: + { + /* + * The reconnecting target either did not send an identify + * message, or did, but we didn't find and SCB to match and + * before it could respond to our ATN/abort, it hit a dataphase. + * The only safe thing to do is to blow it away with a bus + * reset. + */ + int found; + + printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY " + "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n", + p->host_no, target, CHAN_TO_INT(channel), + inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL)); + + found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE); + + printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; " + "%d SCBs aborted\n", p->host_no, channel, found); + } + break; + + case BAD_PHASE: + if (inb(p->base + LASTPHASE) == P_BUSFREE) + { + printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n", + p->host_no, CHAN_TO_INT(channel), target); + restart_sequencer(p); + } + else + { + printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting " + "to continue\n", p->host_no, CHAN_TO_INT(channel), target); + } + break; + + case EXTENDED_MSG: + { + unsigned char message_length; + unsigned char message_code; + unsigned char scb_index; + + message_length = inb(p->base + MSGIN_EXT_LEN); + message_code = inb(p->base + MSGIN_EXT_OPCODE); + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + + switch (message_code) { - /* - * One of two things happened. Either the device requested - * asynchronous data transfers, or it requested a synchronous - * data transfer rate that was so low that asynchronous - * transfers are faster (not to mention the controller won't - * support them). In both cases the synchronous data transfer - * rate and the offset are set to 0 indicating asynchronous - * transfers. - * - * If the device requested an asynchronous transfer, then - * accept the request. If the device is being forced to - * asynchronous data transfers and this is the first time - * we've seen the request, accept the request. If we've - * already seen the request, then attempt to force - * asynchronous data transfers by rejecting the message. - */ - if ((offset == 0) || (p->sdtr_pending & target_mask)) + case MSG_EXT_SDTR: { + unsigned char period; + unsigned char offset; + unsigned char saved_offset; + unsigned char targ_scratch; + unsigned char max_offset; + unsigned char rate; + + if (message_length != MSG_EXT_SDTR_LEN) + { + outb(SEND_REJ, p->base + RETURN_1); + break; + } + + period = inb(p->base + MSGIN_EXT_BYTES); + saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1); + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + + if (targ_scratch & WIDEXFER) + max_offset = MAX_OFFSET_16BIT; + else + max_offset = MAX_OFFSET_8BIT; + offset = MIN(saved_offset, max_offset); + + aic7xxx_scsirate(p, &rate, &period, &offset, target, channel); + /* - * Device requested asynchronous transfers or we're - * forcing asynchronous transfers for the first time. + * Preserve the WideXfer flag. */ - outb(0, RETURN_1 + base); - } - else - { + targ_scratch = rate | (targ_scratch & WIDEXFER); + + /* + * Update both the target scratch area and current SCSIRATE. + */ + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(targ_scratch, p->base + SCSIRATE); + + /* + * See if we initiated Sync Negotiation and didn't have + * have to fall down to async transfers. + */ + if ((scb->flags & SCB_MSGOUT_SDTR) != 0) + { + /* We started it. */ + if (saved_offset == offset) + { + /* + * Don't send an SDTR back to the target. + */ + outb(0, p->base + RETURN_1); + } + else + { + /* We went too low - force async. */ + outb(SEND_REJ, p->base + RETURN_1); + } + } + else + { + /* + * Send our own SDTR in reply. + * + * We want to see this message as we don't expect a target + * to send us a SDTR request first. + */ + printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no); + aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset); + outb(SEND_MSG, p->base + RETURN_1); + } /* - * The first time in forcing asynchronous transfers - * failed, so we try sending a reject message. - */ - outb(SEND_REJ, RETURN_1 + base); + * Clear the flags. + */ + p->needsdtr &= ~target_mask; + break; } - } - else - { - /* - * See if we initiated Sync Negotiation - */ - if (p->sdtr_pending & target_mask) - { - /* - * Don't send an SDTR back to the target. - */ - outb(0, RETURN_1 + base); - } - else - { - /* - * Send our own SDTR in reply. - */ - printk("aic7xxx: Sending SDTR!!\n"); - outb(SEND_SDTR, RETURN_1 + base); - } - } - /* - * Clear the flags. - */ - p->needsdtr &= ~target_mask; - p->sdtr_pending &= ~target_mask; - break; - case WDTR_MSG: - { - bus_width = inb(ARG_1 + base); - printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c " - "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr); - scratch = inb(TARG_SCRATCH + base + scratch_offset); + case MSG_EXT_WDTR: + { + unsigned char scratch, bus_width; - if (p->wdtr_pending & target_mask) - { - /* - * Don't send an WDTR back to the target, since we asked first. - */ - outb(0, RETURN_1 + base); - switch (bus_width) - { - case BUS_8_BIT: - scratch &= 0x7F; - break; - - case BUS_16_BIT: - printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit " - "transfers.\n", p->host_no, scsi_id, channel); - scratch |= 0x80; - break; - - case BUS_32_BIT: - outb(SEND_REJ, RETURN_1 + base); - printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit " - "transfers, rejecting...\n", p->host_no, scsi_id, channel); - break; - } - } - else - { - /* - * Send our own WDTR in reply. - */ - printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no); - switch (bus_width) - { - case BUS_8_BIT: - scratch &= 0x7F; - break; + if (message_length != MSG_EXT_WDTR_LEN) + { + outb(SEND_REJ, p->base + RETURN_1); + break; + } + + bus_width = inb(p->base + MSGIN_EXT_BYTES); + scratch = inb(p->base + TARG_SCRATCH + scratch_offset); - case BUS_32_BIT: - /* - * Negotiate 16 bits. + if ((scb->flags & SCB_MSGOUT_WDTR) != 0) + { + /* + * Don't send an WDTR back to the target, since we asked first. */ - bus_width = BUS_16_BIT; - /* Yes, we mean to fall thru here. */ - - case BUS_16_BIT: - printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit " - "transfers.\n", p->host_no, scsi_id, channel); - scratch |= 0x80; - break; - } - outb(bus_width | SEND_WDTR, RETURN_1 + base); - } - p->needwdtr &= ~target_mask; - p->wdtr_pending &= ~target_mask; - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - break; - } + outb(0, p->base + RETURN_1); + switch (bus_width) + { + case BUS_8_BIT: + scratch &= 0x7F; + break; + + case BUS_16_BIT: + if (aic7xxx_verbose) + { + printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 " + "bit transfers.\n", p->host_no, target, channel); + } + scratch |= WIDEXFER; + break; + + case BUS_32_BIT: + outb(SEND_REJ, p->base + RETURN_1); + /* No verbose here! We want to see this condition. */ + printk(KERN_WARNING "scsi%d: Target %d, channel %c, " + "requesting 32 bit transfers, rejecting...\n", + p->host_no, target, channel); + break; + + default: + break; + } + } + else + { + /* + * Send our own WDTR in reply. + */ + switch (bus_width) + { + case BUS_8_BIT: + scratch &= 0x7F; + break; + + case BUS_32_BIT: + case BUS_16_BIT: + if (p->bus_type == AIC_WIDE) + { + printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 " + "bit transfers.\n", p->host_no, target, channel); + bus_width = BUS_16_BIT; + scratch |= WIDEXFER; + } + else + { + bus_width = BUS_8_BIT; + scratch &= 0x7F; /* XXX - FreeBSD doesn't do this. */ + } + break; + + default: + break; + } + aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width); + outb(SEND_MSG, p->base + RETURN_1); + } + p->needwdtr &= ~target_mask; + outb(scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(scratch, p->base + SCSIRATE); + break; + } /* case MSG_EXT_WDTR */ - case REJECT_MSG: + default: + /* + * Unknown extended message - reject it. + */ + outb(SEND_REJ, p->base + RETURN_1); + break; + } /* switch (message_code) */ + } /* case EXTENDED_MSG */ + break; + + case REJECT_MSG: { /* - * What we care about here is if we had an - * outstanding SDTR or WDTR message for this - * target. If we did, this is a signal that - * the target is refusing negotiation. + * What we care about here is if we had an outstanding SDTR + * or WDTR message for this target. If we did, this is a + * signal that the target is refusing negotiation. */ + unsigned char targ_scratch; + unsigned char scb_index; - scratch = inb(TARG_SCRATCH + base + scratch_offset); + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); - if (p->wdtr_pending & target_mask) + if ((scb->flags & SCB_MSGOUT_WDTR) != 0) { - /* - * note 8bit xfers and clear flag - */ - scratch &= 0x7F; - p->needwdtr &= ~target_mask; - p->wdtr_pending &= ~target_mask; - printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE " - "negotiation; using 8 bit transfers.\n", - p->host_no, scsi_id, channel); + /* + * note 8bit xfers and clear flag + */ + targ_scratch &= 0x7F; + p->needwdtr &= ~target_mask; + printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE " + "negotiation; using 8 bit transfers.\n", + p->host_no, target, channel); } else { - if (p->sdtr_pending & target_mask) - { - /* - * note asynch xfers and clear flag - */ - scratch &= 0xF0; - p->needsdtr &= ~target_mask; - p->sdtr_pending &= ~target_mask; - printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing " - "synchronous negotiation; using asynchronous transfers.\n", - p->host_no, scsi_id, channel); - } - /* - * Otherwise, we ignore it. - */ + if ((scb->flags & SCB_MSGOUT_SDTR) != 0) + { + /* + * note asynch xfers and clear flag + */ + targ_scratch &= 0xF0; + p->needsdtr &= ~target_mask; + printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing " + "synchronous negotiation; using asynchronous transfers.\n", + p->host_no, target, channel); + } + /* + * Otherwise, we ignore it. + */ } - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - break; + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(targ_scratch, p->base + SCSIRATE); } + break; - case BAD_STATUS: - /* The sequencer will notify us when a command has an error that - * would be of interest to the kernel. This allows us to leave - * the sequencerrunning in the common case of command completes - * without error. - */ + case BAD_STATUS: + { + unsigned char scb_index; + struct aic7xxx_hwscb *hscb; + Scsi_Cmnd *cmd; + + /* The sequencer will notify us when a command has an error that + * would be of interest to the kernel. This allows us to leave + * the sequencer running in the common case of command completes + * without error. The sequencer will have DMA'd the SCB back + * up to us, so we can reference the drivers SCB array. + */ + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + hscb = scb->hscb; - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */ - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + /* + * Set the default return value to 0 indicating not to send + * sense. The sense code will change this if needed and this + * reduces code duplication. + */ + outb(0, p->base + RETURN_1); + if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); + printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " + "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no, + intstat, scb_index, scb->flags, (unsigned int) scb->cmd); } else { - cmd = scb->cmd; - scb->target_status = inb(SCB_TARGET_STATUS + base); - aic7xxx_status(cmd) = scb->target_status; - - cmd->result |= scb->target_status; + cmd = scb->cmd; + hscb->target_status = inb(p->base + SCB_TARGET_STATUS); + aic7xxx_status(cmd) = hscb->target_status; - switch (status_byte(scb->target_status)) - { - case GOOD: - printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n"); - break; + cmd->result |= hscb->target_status; - case CHECK_CONDITION: - if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE)) - { - unsigned char tcl; - unsigned int req_buf; /* must be 32 bits */ + switch (status_byte(hscb->target_status)) + { + case GOOD: + printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of " + "GOOD???\n", p->host_no, TC_OF_SCB(scb)); + break; - tcl = scb->target_channel_lun; + case CHECK_CONDITION: + if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE)) + { + unsigned int addr; /* must be 32 bits */ + /* + * XXX - How do we save the residual (if there is one). + */ + aic7xxx_calculate_residual(p, scb); + + /* + * Send a sense command to the requesting target. + * XXX - revisit this and get rid of the memcopys. + */ + memcpy((void *) scb->sense_cmd, (void *) generic_sense, + sizeof(generic_sense)); + + scb->sense_cmd[1] = (cmd->lun << 5); + scb->sense_cmd[4] = sizeof(cmd->sense_buffer); + + scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer); + scb->sg_list[0].length = sizeof(cmd->sense_buffer); + cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - /* - * Send a sense command to the requesting target. + /* + * XXX - We should allow disconnection, but can't as it + * might allow overlapped tagged commands. */ - cmd->flags |= WAS_SENSE; - memcpy((void *) scb->sense_cmd, (void *) generic_sense, - sizeof(generic_sense)); - - scb->sense_cmd[1] = (cmd->lun << 5); - scb->sense_cmd[4] = sizeof(cmd->sense_buffer); - - scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer); - scb->sg_list[0].length = sizeof(cmd->sense_buffer); - req_buf = VIRT_TO_BUS(&scb->sg_list[0]); - cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - - scb->control = scb->control & DISCENB; - scb->target_channel_lun = tcl; - addr = VIRT_TO_BUS(scb->sense_cmd); - scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); - memcpy(scb->SCSI_cmd_pointer, &addr, - sizeof(scb->SCSI_cmd_pointer)); - scb->SG_segment_count = 1; - memcpy(scb->SG_list_pointer, &req_buf, - sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sg_list[0].length; - memcpy(scb->data_pointer, &(scb->sg_list[0].address), - sizeof(scb->data_pointer)); - - aic7xxx_putscb(p, scb); + /* hscb->control &= DISCENB; */ + hscb->control = 0; + hscb->target_status = 0; + hscb->SG_segment_count = 1; + + addr = VIRT_TO_BUS(&scb->sg_list[0]); + memcpy(&hscb->SG_list_pointer, &addr, + sizeof(hscb->SG_list_pointer)); + + memcpy(&hscb->data_pointer, &(scb->sg_list[0].address), + sizeof(hscb->data_pointer)); + /* Maintain SCB_LINKED_NEXT */ + hscb->data_count &= 0xFF000000; + hscb->data_count |= scb->sg_list[0].length; + + addr = VIRT_TO_BUS(scb->sense_cmd); + memcpy(&hscb->SCSI_cmd_pointer, &addr, + sizeof(hscb->SCSI_cmd_pointer)); + hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); + + scb->sg_count = hscb->SG_segment_count; + scb->flags |= SCB_SENSE; /* - * Ensure that the target is "BUSY" so we don't get overlapping - * commands if we happen to be doing tagged I/O. + * Ensure the target is busy since this will be an + * an untagged request. */ - aic7xxx_busy_target(scsi_id, channel, base); + aic7xxx_busy_target(p, target, channel, hscb->tag); + outb(SEND_SENSE, p->base + RETURN_1); + } /* first time sense, no errors */ + else + { + if (aic7xxx_error(cmd) == 0) + { + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + } + } + break; - aic7xxx_add_waiting_scb(base, scb); - outb(SEND_SENSE, RETURN_1 + base); - } /* first time sense, no errors */ - else + case QUEUE_FULL: +#ifdef NOT_YET + if (scb->hscb->control & TAG_ENB) { - cmd->flags &= ~ASKED_FOR_SENSE; - if (aic7xxx_error(cmd) == 0) - { - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } + if (cmd->device->queue_depth > 2) + { + cmd->device->queue_depth--; /* Not correct */ + printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth " + "reduced to %d\n", p->host_no, + TC_OF_SCB(scb), cmd->device->queue_depth); + } + /* + * XXX - Requeue this unconditionally? + */ + + /* + * We'd like to be able to give the SCB some more time + * (untimeout, then timeout). + */ + break; } - break; - - case BUSY: - printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n", - p->host_no, scb->target_channel_lun); - if (!aic7xxx_error(cmd)) - { - /* The error code here used to be DID_BUS_BUSY, - * but after extensive testing, it has been determined - * that a DID_BUS_BUSY return is a waste of time. If - * the problem is something that will go away, then it - * will, if it isn't, then you don't want the endless - * looping that you get with a DID_BUS_BUSY. Better - * to be on the safe side and specify an error condition - * that will eventually lead to a reset or abort of some - * sort instead of an endless loop. - */ - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } - break; - - case QUEUE_FULL: - printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no); - scb->state |= SCB_ASSIGNEDQ; - scbq_insert_tail(&p->assigned_scbs, scb); - break; - - default: - printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n", - p->host_no, scb->target_status); - if (!aic7xxx_error(cmd)) - { - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } - break; - } /* end switch */ +#endif + printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; " + "queue depth %d, active %d\n", p->host_no, + TC_OF_SCB(scb), cmd->device->queue_depth, + p->device_status[TARGET_INDEX(cmd)].active_cmds); + + /* Else treat this as if it was a BUSY condition. */ + scb->hscb->target_status = (BUSY << 1) | + (scb->hscb->target_status & 0x01); + /* Fall through to the BUSY case. */ + + case BUSY: + printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n", + p->host_no, TC_OF_SCB(scb)); + if (!aic7xxx_error(cmd)) + { + /* + * The mid-level SCSI code should be fixed to + * retry the command at a later time instead of + * trying right away. + */ + aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8); + } + udelay(1000); /* A small pause (1ms) to help the drive */ + break; + + default: + printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target " + "status 0x%x.\n", p->host_no, + TC_OF_SCB(scb), scb->hscb->target_status); + if (!aic7xxx_error(cmd)) + { + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + } + break; + } /* end switch */ } /* end else of */ - break; + } + break; - case RESIDUAL: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); - } - else - { - cmd = scb->cmd; - /* - * Don't destroy valid residual information with - * residual coming from a check sense operation. - */ - if (!(cmd->flags & WAS_SENSE)) - { - /* - * We had an underflow. At this time, there's only - * one other driver that bothers to check for this, - * and cmd->underflow seems to be set rather half- - * heartedly in the higher-level SCSI code. - */ - actual = aic7xxx_length(cmd, scb->residual_SG_segment_count); - - actual -= (inb(SCB_RESID_DCNT2 + base) << 16) | - (inb(SCB_RESID_DCNT1 + base) << 8) | - inb(SCB_RESID_DCNT0 + base); - - if (actual < cmd->underflow) - { - printk(KERN_WARNING "scsi%d: Target %d underflow - " - "Wanted at least %u, got %u, residual SG count %d.\n", - p->host_no, cmd->target, cmd->underflow, actual, - inb(SCB_RESID_SGCNT + base)); - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - aic7xxx_status(cmd) = scb->target_status; - } - } - } - break; + case AWAITING_MSG: + { + unsigned char scb_index; + unsigned char message_offset; - case ABORT_TAG: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); - } - else - { - cmd = scb->cmd; - /* - * We didn't receive a valid tag back from the target - * on a reconnect. - */ - printk("scsi%d: Invalid tag received on target %d, channel %c, " - "lun %d - Sending ABORT_TAG.\n", p->host_no, - scsi_id, channel, cmd->lun & 0x07); - - cmd->result = (DID_RETRY_COMMAND << 16); - aic7xxx_done(p, scb); - } - break; + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; - case AWAITING_MSG: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + /* + * This SCB had a MK_MESSAGE set in its control byte informing + * the sequencer that we wanted to send a special message to + * this target. + */ + message_offset = inb(p->base + MSG_LEN); + if (scb->flags & SCB_DEVICE_RESET) { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); + outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT); + outb(1, p->base + MSG_LEN); + printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n", + p->host_no, TC_OF_SCB(scb)); } - else + else if (scb->flags & SCB_ABORT) + { + if ((scb->hscb->control & TAG_ENB) != 0) + { + outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset); + } + else + { + outb(MSG_ABORT, p->base + MSG_OUT + message_offset); + } + outb(message_offset + 1, p->base + MSG_LEN); + printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n", + p->host_no, TC_OF_SCB(scb)); + } + else if (scb->flags & SCB_MSGOUT_WDTR) { - /* - * This SCB had a zero length command, informing the sequencer - * that we wanted to send a special message to this target. - * We only do this for BUS_DEVICE_RESET messages currently. - */ - if (scb->state & SCB_DEVICE_RESET) - { -#ifdef AIC7XXX_DEBUG_ABORT - printk ("aic7xxx: (isr) sending bus device reset to target %d\n", - scsi_id); -#endif - outb(MSG_BUS_DEVICE_RESET, MSG0 + base); - outb(1, MSG_LEN + base); - } - else - { - panic("scsi%d: AWAITING_SCB for an SCB that does " - "not have a waiting message.\n", p->host_no); - } - } - break; - - case IMMEDDONE: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n", - scsi_id, scb_index, scb->state); -#endif - if (scb->state & SCB_DEVICE_RESET) + aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT); + } + else if (scb->flags & SCB_MSGOUT_SDTR) { - int found; + unsigned char target_scratch; + unsigned short ultra_enable; + int i, sxfr; /* - * Go back to async/narrow transfers and renegotiate. + * Pull the user defined setting from scratch RAM. */ - aic7xxx_unbusy_target(scsi_id, channel, base); - p->needsdtr |= (p->needsdtr_copy & target_mask); - p->needwdtr |= (p->needwdtr_copy & target_mask); - p->sdtr_pending &= ~target_mask; - p->wdtr_pending &= ~target_mask; - scratch = inb(TARG_SCRATCH + base + scratch_offset); - scratch &= SXFR; - outb(scratch, TARG_SCRATCH + base + scratch_offset); - found = aic7xxx_reset_device(p, (int) scsi_id, channel); - printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs " - "aborted.\n", p->host_no, found); - /* Indicate that we want to call aic7xxx_done_aborted_scbs() */ - run_aborted_queue = TRUE; + target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + sxfr = target_scratch & SXFR; + ultra_enable = inb(p->base + ULTRA_ENB) | + (inb(p->base + ULTRA_ENB + 1) << 8); + if (ultra_enable & target_mask) + { + sxfr |= 0x100; + } + for (i = 0; i < num_aic7xxx_syncrates; i++) + { + if (sxfr == aic7xxx_syncrates[i].rate) + break; + } + aic7xxx_construct_sdtr(p, message_offset, + aic7xxx_syncrates[i].period, + target_scratch & WIDEXFER ? + MAX_OFFSET_16BIT : MAX_OFFSET_8BIT); } - else + else { - panic("scsi%d: Immediate complete for unknown operation.\n", - p->host_no); - } - break; + panic("aic7xxx: AWAITING_MSG for an SCB that does " + "not have a waiting message."); + } + } + break; - case DATA_OVERRUN: + case DATA_OVERRUN: { - unsigned int overrun; - - scb = (p->scb_array[inb(base + SCB_TAG)]); - overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) | - (inb(base + STCNT2) << 16); - overrun =0x00FFFFFF - overrun; - printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing " - "a retry.\n", p->host_no, overrun); - aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND; - break; + unsigned char scb_index = inb(p->base + SCB_TAG); + unsigned char lastphase = inb(p->base + LASTPHASE); + unsigned int i, overrun; + + scb = (p->scb_data->scb_array[scb_index]); + overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) | + (inb(p->base + STCNT + 2) << 16); + overrun = 0x00FFFFFF - overrun; + printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected " + "in %s phase, tag %d; forcing a retry.\n", + p->host_no, TC_OF_SCB(scb), overrun, + lastphase == P_DATAIN ? "Data-In" : "Data-Out", + scb->hscb->tag); + printk(KERN_WARNING "%s seen Data Phase. Length = %d, NumSGs = %d.\n", + inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't", + aic7xxx_length(scb->cmd, 0), scb->sg_count); + for (i = 0; i < scb->sg_count; i++) + { + printk(KERN_INFO " sg[%d] - Addr 0x%x : Length %d\n", + i, scb->sg_list[i].address, scb->sg_list[i].length); + } + /* + * XXX - What do we really want to do on an overrun? The + * mid-level SCSI code should handle this, but for now, + * we'll just indicate that the command should retried. + */ + aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND; } + break; -#if AIC7XXX_NOT_YET - /* XXX Fill these in later */ - case MESG_BUFFER_BUSY: - break; - case MSGIN_PHASEMIS: - break; -#endif +/* #if AIC7XXX_NOT_YET */ + /* XXX Fill these in later */ + case MSG_BUFFER_BUSY: + printk("aic7xxx: Message buffer busy.\n"); + break; + case MSGIN_PHASEMIS: + printk("aic7xxx: Message-in phasemis.\n"); + break; +/*#endif */ + + case ABORT_CMDCMPLT: + /* This interrupt serves to pause the sequencer until we can clean + * up the QOUTFIFO allowing us to handle any abort SCBs that may + * completed yet still have an SCB in the QINFIFO or waiting for + * selection queue. By the time we get here, we should have + * already cleaned up the queues, so all we need to do is unpause + * the sequencer. + */ + break; + + default: /* unknown */ + printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n", + p->host_no, intstat, inb(p->base + SCSISIGI)); + break; + } + + /* + * Clear the sequencer interrupt and unpause the sequencer. + */ + outb(CLRSEQINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_handle_scsiint + * + * Description: + * Interrupt handler for SCSI interrupts (SCSIINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) +{ + unsigned char scb_index; + unsigned char status; + struct aic7xxx_scb *scb; + + scb_index = inb(p->base + SCB_TAG); + status = inb(p->base + SSTAT1); - default: /* unknown */ - printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n", - p->host_no, intstat, inb(SCSISIGI + base)); - break; + if (scb_index < p->scb_data->numscbs) + { + scb = p->scb_data->scb_array[scb_index]; + if ((scb->flags & SCB_ACTIVE) == 0) + { + scb = NULL; } + } + else + { + scb = NULL; + } + + if ((status & SCSIRSTI) != 0) + { + char channel; + + channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A'; + printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n", + p->host_no, channel); /* - * Clear the sequencer interrupt and unpause the sequencer. + * Go through and abort all commands for the channel, but do not + * reset the channel again. */ - outb(CLRSEQINT, CLRINT + base); - UNPAUSE_SEQUENCER(p); + aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE); + scb = NULL; } - - if (intstat & SCSIINT) + else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) ) { - int status = inb(SSTAT1 + base); - scsi_id = (inb(SCSIID + base) >> 4) & 0x0F; - channel = 'A'; - if (inb(SBLKCTL + base) & SELBUSB) + /* + * First look at what phase we were last in. If it's message-out, + * chances are pretty good that the bus free was in response to + * one of our abort requests. + */ + unsigned char lastphase = inb(p->base + LASTPHASE); + unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F; + char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A'; + int printerror = TRUE; + + outb(0, p->base + SCSISEQ); + if (lastphase == P_MESGOUT) { - channel = 'B'; + unsigned char sindex; + unsigned char message; + + sindex = inb(p->base + SINDEX); + message = inb(p->base + sindex - 1); + + if (message == MSG_ABORT) + { + printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL); + aic7xxx_run_done_queue(p, /* complete */ TRUE); + scb = NULL; + printerror = 0; + } + else if (message == MSG_ABORT_TAG) + { + printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag); + aic7xxx_run_done_queue(p, /* complete */ TRUE); + scb = NULL; + printerror = 0; + } + else if (message == MSG_BUS_DEV_RESET) + { + aic7xxx_handle_device_reset(p, target, channel); + scb = NULL; + printerror = 0; + } } + if (printerror != 0) + { + if (scb != NULL) + { + unsigned char tag; - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (status & SCSIRSTI) + if ((scb->hscb->control & TAG_ENB) != 0) + { + tag = scb->hscb->tag; + } + else + { + tag = SCB_LIST_NULL; + } + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag); + } + else + { + aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); + } + printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, " + "SEQADDR = 0x%x\n", p->host_no, lastphase, + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + } + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); + outb(CLRBUSFREE, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + restart_sequencer(p); + } + else if ((status & SELTO) != 0) + { + unsigned char scbptr; + unsigned char nextscb; + Scsi_Cmnd *cmd; + + scbptr = inb(p->base + WAITING_SCBH); + outb(scbptr, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + + scb = NULL; + if (scb_index < p->scb_data->numscbs) { - PAUSE_SEQUENCER(p); - printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n", - p->host_no, channel); - /* - * Go through and abort all commands for the channel, but do not - * reset the channel again. - */ - aic7xxx_reset_channel(p, channel, FALSE); - run_aborted_queue = TRUE; + scb = p->scb_data->scb_array[scb_index]; + if ((scb->flags & SCB_ACTIVE) == 0) + { + scb = NULL; + } } - else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + if (scb == NULL) { - printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no); + printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n", + p->host_no, scb_index); + printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x " + "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ), + inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8), + inb(p->base + SSTAT0), inb(p->base + SSTAT1)); + } + else + { + /* + * XXX - If we queued an abort tag, go clean up the disconnected list. + */ + cmd = scb->cmd; + cmd->result = (DID_TIME_OUT << 16); + + /* + * Clear an pending messages for the timed out + * target and mark the target as free. + */ + outb(0, p->base + MSG_LEN); + aic7xxx_index_busy_target(p, cmd->target, + cmd->channel ? 'B': 'A', /*unbusy*/ TRUE); + outb(0, p->base + SCB_CONTROL); + /* - * Turn off the interrupt and set status to zero, so that it - * falls through the rest of the SCSIINT code. + * Shift the waiting for selection queue forward + */ + nextscb = inb(p->base + SCB_NEXT); + outb(nextscb, p->base + WAITING_SCBH); + + /* + * Put this SCB back on the free list. */ - outb(status, CLRSINT1 + base); - UNPAUSE_SEQUENCER(p); - outb(CLRSCSIINT, CLRINT + base); + aic7xxx_add_curscb_to_free_list(p); + } + /* + * Stop the selection. + */ + outb(0, p->base + SCSISEQ); + outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + restart_sequencer(p); + } + else if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid " + "during scsiint 0x%x scb(%d)\n" + " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n", + p->host_no, status, scb_index, inb(p->base + SIMODE0), + inb(p->base + SIMODE1), inb(p->base + SSTAT0), + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + /* + * Turn off the interrupt and set status to zero, so that it + * falls through the rest of the SCSIINT code. + */ + outb(status, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); + scb = NULL; + } + else if (status & SCSIPERR) + { + /* + * Determine the bus phase and queue an appropriate message. + */ + char *phase; + Scsi_Cmnd *cmd; + unsigned char mesg_out = MSG_NOOP; + unsigned char lastphase = inb(p->base + LASTPHASE); + + cmd = scb->cmd; + switch (lastphase) + { + case P_DATAOUT: + phase = "Data-Out"; + break; + case P_DATAIN: + phase = "Data-In"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_COMMAND: + phase = "Command"; + break; + case P_MESGOUT: + phase = "Message-Out"; + break; + case P_STATUS: + phase = "Status"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_MESGIN: + phase = "Message-In"; + mesg_out = MSG_PARITY_ERROR; + break; + default: + phase = "unknown"; + break; + } + + /* + * A parity error has occurred during a data + * transfer phase. Flag it and continue. + */ + printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n", + p->host_no, TC_OF_SCB(scb), phase); + + /* + * We've set the hardware to assert ATN if we get a parity + * error on "in" phases, so all we need to do is stuff the + * message buffer with the appropriate message. "In" phases + * have set mesg_out to something other than MSG_NOP. + */ + if (mesg_out != MSG_NOOP) + { + outb(mesg_out, p->base + MSG_OUT); + outb(1, p->base + MSG_LEN); scb = NULL; } - else if (status & SCSIPERR) + else { - char *phase; - unsigned char mesg_out = MSG_NOP; - unsigned char lastphase = inb(LASTPHASE + base); + /* + * Should we allow the target to make this decision for us? + */ + cmd->result = DID_RETRY_COMMAND << 16; + } + outb(CLRSCSIPERR, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause_always */ TRUE); + } + else + { + /* + * We don't know what's going on. Turn off the + * interrupt source and try to continue. + */ + printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status); + outb(status, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); + scb = NULL; + } + if (scb != NULL) + { + aic7xxx_done(p, scb); + aic7xxx_done_cmds_complete(p); + } +} - cmd = scb->cmd; - switch (lastphase) - { - case P_DATAOUT: - phase = "Data-Out"; - break; - case P_DATAIN: - phase = "Data-In"; - mesg_out = MSG_INITIATOR_DET_ERROR; - break; - case P_COMMAND: - phase = "Command"; - break; - case P_MESGOUT: - phase = "Message-Out"; - break; - case P_STATUS: - phase = "Status"; - mesg_out = MSG_INITIATOR_DET_ERROR; - break; - case P_MESGIN: - phase = "Message-In"; - mesg_out = MSG_MSG_PARITY_ERROR; - break; - default: - phase = "unknown"; - break; - } +/*+F************************************************************************* + * Function: + * aic7xxx_isr + * + * Description: + * SCSI controller interrupt handler. + * + * NOTE: Since we declared this using SA_INTERRUPT, interrupts should + * be disabled all through this function unless we say otherwise. + *-F*************************************************************************/ +static void +aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct aic7xxx_host *p; + unsigned char intstat; + unsigned long flags; - /* - * A parity error has occurred during a data - * transfer phase. Flag it and continue. - */ - printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, " - "channel %d, lun %d.\n", p->host_no, phase, - cmd->target, cmd->channel & 0x01, cmd->lun & 0x07); + p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; - /* - * We've set the hardware to assert ATN if we get a parity - * error on "in" phases, so all we need to do is stuff the - * message buffer with the appropriate message. In phases - * have set mesg_out to something other than MSG_NOP. - */ - if (mesg_out != MSG_NOP) - { - outb(mesg_out, MSG0 + base); - outb(1, MSG_LEN + base); - cmd->result = DID_PARITY << 16; - } - else - { - /* - * Should we allow the target to make this decision for us? - */ - cmd->result = DID_RETRY_COMMAND << 16; - } - aic7xxx_done(p, scb); + /* + * Search for the host with a pending interrupt. If we can't find + * one, then we've encountered a spurious interrupt. + */ + while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND)) + { + if (p->next == NULL) + { + p = NULL; } - else if (status & SELTO) + else { - unsigned char waiting; + p = (struct aic7xxx_host *) p->next->hostdata; + } + } - cmd = scb->cmd; + if (p == NULL) + return; - cmd->result = (DID_TIME_OUT << 16); - /* - * Clear an pending messages for the timed out - * target and mark the target as free. - */ - ha_flags = inb(FLAGS + base); - outb(0, MSG_LEN + base); - aic7xxx_unbusy_target(scsi_id, channel, base); - /* - * Stop the selection. - */ - outb(0, SCSISEQ + base); - outb(0, SCB_CONTROL + base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); + /* + * Handle all the interrupt sources - especially for SCSI + * interrupts, we won't get a second chance at them. + */ + intstat = inb(p->base + INTSTAT); - /* - * Shift the waiting for selection queue forward - */ - waiting = inb(WAITING_SCBH + base); - outb(waiting, SCBPTR + base); - waiting = inb(SCB_NEXT + base); - outb(waiting, WAITING_SCBH + base); + /* + * Keep track of interrupts for /proc/scsi + */ + p->isr_count++; - RESTART_SEQUENCER(p); - aic7xxx_done(p, scb); - } - else if (!(status & BUSFREE)) + if (!(p->flags & A_SCANNED) && (p->isr_count == 1)) + { + /* + * We must only have one card at this IRQ and it must have been + * added to the board data before the spurious interrupt occurred. + * It is sufficient that we check isr_count and not the spurious + * interrupt count. + */ + printk("scsi%d: Encountered spurious interrupt.\n", p->host_no); + if (intstat) { - /* - * We don't know what's going on. Turn off the - * interrupt source and try to continue. - */ - printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status); - outb(status, CLRSINT1 + base); - UNPAUSE_SEQUENCER(p); - outb(CLRSCSIINT, CLRINT + base); + /* Try clearing all interrupts. */ + outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT); } + return; + } + + if (p->flags & IN_ISR) + { + printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n", + p->host_no); + return; } - if (run_aborted_queue) - aic7xxx_done_aborted_scbs(p); + /* + * Indicate that we're in the interrupt handler. + */ + save_flags(flags); + cli(); + p->flags |= IN_ISR; if (intstat & CMDCMPLT) { - int complete; + struct aic7xxx_scb *scb = NULL; + Scsi_Cmnd *cmd; + unsigned char qoutcnt; + unsigned char scb_index; + int i, interrupts_cleared = 0; /* * The sequencer will continue running when it * issues this interrupt. There may be >1 commands * finished, so loop until we've processed them all. */ - do { - complete = inb(QOUTFIFO + base); + qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask; - scb = (p->scb_array[complete]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n" - " QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, " - "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base), - inb(QINCNT + base), scb->state, (unsigned long) scb->cmd, - scb->position); - outb(CLRCMDINT, CLRINT + base); - continue; - } - cmd = scb->cmd; - cmd->result |= (aic7xxx_error(cmd) << 16); - if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE)) +#if 1 + if (qoutcnt >= p->qfullcount - 1) + printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, " + "qoutcnt = %d.\n", qoutcnt); +#endif + while (qoutcnt > 0) + { + for (i = 0; i < qoutcnt; i++) { - /* - * Got sense information. - */ - cmd->flags &= ASKED_FOR_SENSE; + scb_index = inb(p->base + QOUTFIFO); + scb = p->scb_data->scb_array[scb_index]; + if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, " + "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index, + inb(p->base + QOUTCNT), inb(p->base + QINCNT)); + continue; + } + else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, " + "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n", + p->host_no, scb_index, inb(p->base + QOUTCNT), + inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd); + continue; + } + cmd = scb->cmd; + if (scb->hscb->residual_SG_segment_count != 0) + { + aic7xxx_calculate_residual(p, scb); + } + if ((scb->flags & SCB_QUEUED_ABORT) != 0) + { + /* + * Have to clean up any possible entries in the + * waiting queue and the QINFIFO. + */ + int target; + char channel; + int lun; + unsigned char tag; + + tag = SCB_LIST_NULL; + target = cmd->target; + lun = cmd->lun; + channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + if (scb->hscb->control & TAG_ENB) + { + tag = scb->hscb->tag; + } + aic7xxx_reset_device(p, target, channel, lun, tag); + /* + * Run the done queue, but don't complete the commands; we + * do this once at the end of the loop. + */ + aic7xxx_run_done_queue(p, /*complete*/ FALSE); + } + cmd->result |= (aic7xxx_error(cmd) << 16); + p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS; + aic7xxx_done(p, scb); } - p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS; - /* * Clear interrupt status before checking the output queue again. * This eliminates a race condition whereby a command could @@ -3243,56 +3979,152 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) * so notification of the command being complete never made it * back up to the kernel. */ - outb(CLRCMDINT, CLRINT + base); - aic7xxx_done(p, scb); + outb(CLRCMDINT, p->base + CLRINT); + interrupts_cleared++; + qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask; + } -#ifdef AIC7XXX_PROC_STATS - /* - * XXX: we should actually know how much actually transferred - * XXX: for each command, but apparently that's too difficult. - */ - actual = aic7xxx_length(cmd, 0); - if (!(cmd->flags & WAS_SENSE) && (actual > 0)) + if (interrupts_cleared == 0) + { + outb(CLRCMDINT, p->base + CLRINT); + } + + aic7xxx_done_cmds_complete(p); + } + + if (intstat & BRKADRINT) + { + int i; + unsigned char errno = inb(p->base + ERROR); + + printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno); + for (i = 0; i < NUMBER(hard_error); i++) + { + if (errno & hard_error[i].errno) { - struct aic7xxx_xferstats *sp; - long *ptr; - int x; + printk(KERN_ERR " %s\n", hard_error[i].errmesg); + } + } + printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no, + inb(p->base + ERROR), + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL); + aic7xxx_run_done_queue(p, /*complete*/ TRUE); + } + + if (intstat & SEQINT) + { + aic7xxx_handle_seqint(p, intstat); + } + + if (intstat & SCSIINT) + { + aic7xxx_handle_scsiint(p, intstat); + } + + if (p->waiting_scbs.head != NULL) + { + aic7xxx_run_waiting_queues(p); + } + + p->flags &= ~IN_ISR; + restore_flags(flags); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_device_queue_depth + * + * Description: + * Determines the queue depth for a given device. There are two ways + * a queue depth can be obtained for a tagged queueing device. One + * way is the default queue depth which is determined by whether + * AIC7XXX_CMDS_PER_LUN is defined. If it is defined, then it is used + * as the default queue depth. Otherwise, we use either 4 or 8 as the + * default queue depth (dependent on the number of hardware SCBs). + * The other way we determine queue depth is through the use of the + * aic7xxx_tag_info array which is enabled by defining + * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized + * with queue depths for individual devices. It also allows tagged + * queueing to be [en|dis]abled for a specific adapter. + *-F*************************************************************************/ +static void +aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) +{ + int default_depth = 2; - sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07]; - sp->xfers++; + device->queue_depth = default_depth; +#ifdef AIC7XXX_TAGGED_QUEUEING + if (device->tagged_supported) + { + unsigned short target_mask; + int tag_enabled = TRUE; + + target_mask = (1 << (device->id | (device->channel << 3))); + +#ifdef AIC7XXX_CMDS_PER_LUN + default_depth = AIC7XXX_CMDS_PER_LUN; +#else + if (p->scb_data->maxhscbs <= 4) + { + default_depth = 4; /* Not many SCBs to work with. */ + } + else + { + default_depth = 8; + } +#endif + + if (!(p->discenable & target_mask)) + { + printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to " + "enable tagged queueing.\n", + p->host_no, device->id, device->channel); + } + else + { +#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE + device->queue_depth = default_depth; +#else + if (p->instance > NUMBER(aic7xxx_tag_info)) + { + device->queue_depth = default_depth; + } + else + { + unsigned char tindex; - if (cmd->request.cmd == WRITE) + tindex = device->id | (device->channel << 3); + if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == -1) { - sp->w_total++; - sp->w_total512 += (actual >> 9); - ptr = sp->w_bins; + tag_enabled = FALSE; + device->queue_depth = 2; /* Tagged queueing is disabled. */ } - else + else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0) { - sp->r_total++; - sp->r_total512 += (actual >> 9); - ptr = sp->r_bins; + device->queue_depth = default_depth; } - for (x = 9; x <= 17; x++) + else { - if (actual < (1 << x)) - { - ptr[x - 9]++; - break; - } + device->queue_depth = + aic7xxx_tag_info[p->instance].tag_commands[tindex]; } - if (x > 17) + } +#endif + if ((device->tagged_queue == 0) && tag_enabled) + { + if (aic7xxx_verbose) { - ptr[x - 9]++; + printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, " + "queue depth %d.\n", p->host_no, + device->id, device->channel, device->queue_depth); } + device->tagged_queue = 1; + device->current_tag = SCB_LIST_NULL; } -#endif /* AIC7XXX_PROC_STATS */ - - } while (inb(QOUTCNT + base) & p->qcntmask); + } } - aic7xxx_done_cmds_complete(p); - p->flags &= ~IN_ISR; - aic7xxx_run_waiting_queues(p); +#endif } /*+F************************************************************************* @@ -3307,59 +4139,18 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) * algorithm for determining the queue depth based on the maximum * SCBs for the controller. *-F*************************************************************************/ -static void aic7xxx_select_queue_depth(struct Scsi_Host *host, +static void +aic7xxx_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) { - Scsi_Device *device = scsi_devs; - int tq_depth = 2; + Scsi_Device *device; struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata; -#ifdef AIC7XXX_CMDS_PER_LUN - tq_depth = AIC7XXX_CMDS_PER_LUN; -#else - { - if (p->maxhscbs <= 4) - { - tq_depth = 4; /* Not many SCBs to work with. */ - } - else - { - tq_depth = 8; - } - } -#endif - for (device = scsi_devs; device != NULL; device = device->next) { if (device->host == host) { - device->queue_depth = 2; -#ifdef AIC7XXX_TAGGED_QUEUEING - if (device->tagged_supported) - { - unsigned short target_mask = (1 << device->id) | device->channel; - - if (!(p->discenable & target_mask)) - { - printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable " - "tagged queueing for target %d, channel %d, LUN %d.\n", - host->host_no, device->id, device->channel, device->lun); - } - else - { - device->queue_depth = tq_depth; - if (device->tagged_queue == 0) - { - printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, " - "channel %d, LUN %d, queue depth %d.\n", host->host_no, - device->id, device->channel, device->lun, - device->queue_depth); - device->tagged_queue = 1; - device->current_tag = SCB_LIST_NULL; - } - } - } -#endif + aic7xxx_device_queue_depth(p, device); } } } @@ -3386,7 +4177,7 @@ static void aic7xxx_select_queue_depth(struct Scsi_Host *host, * The fourth byte's lowest bit seems to be an enabled/disabled * flag (rest of the bits are reserved?). *-F*************************************************************************/ -static aha_type +static aha_chip_type aic7xxx_probe(int slot, int base, aha_status_type *bios) { int i; @@ -3395,7 +4186,7 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios) static struct { int n; unsigned char signature[sizeof(buf)]; - aha_type type; + aha_chip_type type; int bios_disabled; } AIC7xxx[] = { { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */ @@ -3434,7 +4225,8 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios) return (AIC7xxx[i].type); } - printk("aic7xxx: Disabled at slot %d, ignored.\n", slot); + printk("aic7xxx: " + "disabled at slot %d, ignored.\n", slot); } } @@ -3461,10 +4253,9 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios) * useful in that it gives us an 800 nsec timer. After a read from the * SEECTL_2840 register the timing flag is cleared and goes high 800 nsec * later. - * *-F*************************************************************************/ static int -read_2840_seeprom(int base, struct seeprom_config *sc) +read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc) { int i = 0, k = 0; unsigned char temp; @@ -3477,11 +4268,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc) struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; #define CLOCK_PULSE(p) \ - while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \ + while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0) \ { \ ; /* Do nothing */ \ } \ - (void) inb(SEECTL_2840 + base); + (void) inb(p->base + SEECTL_2840); /* * Read the first 32 registers of the seeprom. For the 2840, @@ -3494,8 +4285,8 @@ read_2840_seeprom(int base, struct seeprom_config *sc) /* * Send chip select for one clock cycle. */ - outb(CK_2840 | CS_2840, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(CK_2840 | CS_2840, p->base + SEECTL_2840); + CLOCK_PULSE(p); /* * Now we're ready to send the read command followed by the @@ -3504,11 +4295,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc) for (i = 0; i < seeprom_read.len; i++) { temp = CS_2840 | seeprom_read.bits[i]; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* * Send the 6 bit address (MSB first, LSB last). @@ -3518,11 +4309,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc) temp = k; temp = (temp >> i) & 1; /* Mask out all but lower bit. */ temp = CS_2840 | temp; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* @@ -3534,12 +4325,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc) for (i = 0; i <= 16; i++) { temp = CS_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840); - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* * The serial EEPROM has a checksum in the last word. Keep a @@ -3555,12 +4346,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc) /* * Reset the chip select for the next command cycle. */ - outb(0, SEECTL_2840 + base); - CLOCK_PULSE(base); - outb(CK_2840, SEECTL_2840 + base); - CLOCK_PULSE(base); - outb(0, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(0, p->base + SEECTL_2840); + CLOCK_PULSE(p); + outb(CK_2840, p->base + SEECTL_2840); + CLOCK_PULSE(p); + outb(0, p->base + SEECTL_2840); + CLOCK_PULSE(p); } #if 0 @@ -3587,6 +4378,53 @@ read_2840_seeprom(int base, struct seeprom_config *sc) #undef CLOCK_PULSE } +/*+F************************************************************************* + * Function: + * acquire_seeprom + * + * Description: + * Acquires access to the memory port on PCI controllers. + *-F*************************************************************************/ +static inline int +acquire_seeprom(struct aic7xxx_host *p) +{ + int wait; + + /* + * Request access of the memory port. When access is + * granted, SEERDY will go high. We use a 1 second + * timeout which should be near 1 second more than + * is needed. Reason: after the 7870 chip reset, there + * should be no contention. + */ + outb(SEEMS, p->base + SEECTL); + wait = 1000; /* 1000 msec = 1 second */ + while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0)) + { + wait--; + udelay(1000); /* 1 msec */ + } + if ((inb(p->base + SEECTL) & SEERDY) == 0) + { + outb(0, p->base + SEECTL); + return (0); + } + return (1); +} + +/*+F************************************************************************* + * Function: + * release_seeprom + * + * Description: + * Releases access to the memory port on PCI controllers. + *-F*************************************************************************/ +static inline void +release_seeprom(struct aic7xxx_host *p) +{ + outb(0, p->base + SEECTL); +} + /*+F************************************************************************* * Function: * read_seeprom @@ -3626,7 +4464,7 @@ read_2840_seeprom(int base, struct seeprom_config *sc) * first). The clock cycling from low to high initiates the next data * bit to be sent from the chip. * - * The 7870 interface to the 93C46 serial EEPROM is through the SEECTL + * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL * register. After successful arbitration for the memory port, the * SEECS bit of the SEECTL register is connected to the chip select. * The SEECK, SEEDO, and SEEDI are connected to the clock, data out, @@ -3636,17 +4474,14 @@ read_2840_seeprom(int base, struct seeprom_config *sc) * to this is when we first request access to the memory port. The * SEERDY goes high to signify that access has been granted and, for * this case, has no implied timing. - * *-F*************************************************************************/ static int -read_seeprom(int base, int offset, struct seeprom_config *sc, - seeprom_chip_type chip) +read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray, + unsigned int len, seeprom_chip_type chip) { int i = 0, k; - unsigned long timeout; unsigned char temp; unsigned short checksum = 0; - unsigned short *seeprom = (unsigned short *) sc; struct seeprom_cmd { unsigned char len; unsigned char bits[3]; @@ -3654,43 +4489,33 @@ read_seeprom(int base, int offset, struct seeprom_config *sc, struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; #define CLOCK_PULSE(p) \ - while ((inb(SEECTL + base) & SEERDY) == 0) \ + while ((inb(p->base + SEECTL) & SEERDY) == 0) \ { \ ; /* Do nothing */ \ } /* - * Request access of the memory port. When access is - * granted, SEERDY will go high. We use a 1 second - * timeout which should be near 1 second more than - * is needed. Reason: after the 7870 chip reset, there - * should be no contention. + * Request access of the memory port. */ - outb(SEEMS, SEECTL + base); - timeout = jiffies + 100; /* 1 second timeout */ - while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0)) - { - ; /* Do nothing! Wait for access to be granted. */ - } - if ((inb(SEECTL + base) & SEERDY) == 0) + if (acquire_seeprom(p) == 0) { - outb(0, SEECTL + base); return (0); } /* - * Read the first 32 registers of the seeprom. For the 7870, - * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers - * but only the first 32 are used by Adaptec BIOS. The loop - * will range from 0 to 31. + * Read 'len' registers of the seeprom. For the 7870, the 93C46 + * SEEPROM is a 1024-bit device with 64 16-bit registers but only + * the first 32 are used by Adaptec BIOS. Some adapters use the + * 93C56 SEEPROM which is a 2048-bit device. The loop will range + * from 0 to 'len' - 1. */ - for (k = 0; k < (sizeof(*sc) / 2); k++) + for (k = 0; k < len; k++) { /* * Send chip select for one clock cycle. */ - outb(SEEMS | SEECK | SEECS, SEECTL + base); - CLOCK_PULSE(base); + outb(SEEMS | SEECK | SEECS, p->base + SEECTL); + CLOCK_PULSE(p); /* * Now we're ready to send the read command followed by the @@ -3699,25 +4524,25 @@ read_seeprom(int base, int offset, struct seeprom_config *sc, for (i = 0; i < seeprom_read.len; i++) { temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* - * Send the 6 bit address (MSB first, LSB last). + * Send the 6 or 8 bit address (MSB first, LSB last). */ for (i = ((int) chip - 1); i >= 0; i--) { temp = k + offset; temp = (temp >> i) & 1; /* Mask out all but lower bit. */ temp = SEEMS | SEECS | (temp << 1); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* @@ -3729,56 +4554,57 @@ read_seeprom(int base, int offset, struct seeprom_config *sc, for (i = 0; i <= 16; i++) { temp = SEEMS | SEECS; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } - - /* - * The serial EEPROM has a checksum in the last word. Keep a - * running checksum for all words read except for the last - * word. We'll verify the checksum after all words have been - * read. + + /* + * The serial EEPROM should have a checksum in the last word. + * Keep a running checksum for all words read except for the + * last word. We'll verify the checksum after all words have + * been read. */ - if (k < (sizeof(*sc) / 2) - 1) + if (k < (len - 1)) { - checksum = checksum + seeprom[k]; + checksum = checksum + scarray[k]; } /* * Reset the chip select for the next command cycle. */ - outb(SEEMS, SEECTL + base); - CLOCK_PULSE(base); - outb(SEEMS | SEECK, SEECTL + base); - CLOCK_PULSE(base); - outb(SEEMS, SEECTL + base); - CLOCK_PULSE(base); + outb(SEEMS, p->base + SEECTL); + CLOCK_PULSE(p); + outb(SEEMS | SEECK, p->base + SEECTL); + CLOCK_PULSE(p); + outb(SEEMS, p->base + SEECTL); + CLOCK_PULSE(p); } /* * Release access to the memory port and the serial EEPROM. */ - outb(0, SEECTL + base); + release_seeprom(p); #if 0 - printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum); + printk("Computed checksum 0x%x, checksum read 0x%x\n", + checksum, scarray[len - 1]); printk("Serial EEPROM:"); - for (k = 0; k < (sizeof(*sc) / 2); k++) + for (k = 0; k < len; k++) { if (((k % 8) == 0) && (k != 0)) { printk("\n "); } - printk(" 0x%x", seeprom[k]); + printk(" 0x%x", scarray[k]); } printk("\n"); #endif - if (checksum != sc->checksum) + if (checksum != scarray[len - 1]) { return (0); } @@ -3789,563 +4615,451 @@ read_seeprom(int base, int offset, struct seeprom_config *sc, /*+F************************************************************************* * Function: - * detect_maxscb + * write_brdctl * * Description: - * Detects the maximum number of SCBs for the controller and returns - * the count and a mask in config (config->maxscbs, config->qcntmask). + * Writes a value to the BRDCTL register. *-F*************************************************************************/ -static void -detect_maxscb(struct aic7xxx_host_config *config) +static inline void +write_brdctl(struct aic7xxx_host *p, unsigned char value) { - unsigned char sblkctl_reg; - int base, i; - -#ifdef AIC7XXX_PAGE_ENABLE - config->flags |= PAGE_ENABLED; -#endif - base = config->base; - switch (config->type) - { - case AIC_7770: - case AIC_7771: - case AIC_284x: - /* - * Check for Rev C or E boards. Rev E boards can supposedly have - * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs. - * It's still not clear extactly what is different about the Rev E - * boards, but we think it allows 8 bit entries in the QOUTFIFO to - * support "paging" SCBs (more than 4 commands can be active at once). - * - * The Rev E boards have a read/write autoflush bit in the - * SBLKCTL register, while in the Rev C boards it is read only. - */ - sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS; - outb(sblkctl_reg, SBLKCTL + base); - if (inb(SBLKCTL + base) == sblkctl_reg) - { - /* - * We detected a Rev E board, we allow paging on this board. - */ - printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n", - board_names[config->type]); - outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base); - } - else - { - /* Do not allow paging. */ - config->flags &= ~PAGE_ENABLED; - printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n", - board_names[config->type]); - } - break; - - default: - break; - } - - /* - * Walk the SCBs to determine how many there are. - */ - i = 1; - outb(0, SCBPTR + base); - outb(0, SCBARRAY + base); - - while (i < AIC7XXX_MAXSCB) - { - outb(i, SCBPTR + base); - outb(i, SCBARRAY + base); - if (inb(SCBARRAY + base) != i) - break; - outb(0, SCBPTR + base); - if (inb(SCBARRAY + base) != 0) - break; - - outb(i, SCBPTR + base); /* Clear the control byte. */ - outb(0, SCBARRAY + base); - - config->qcntmask |= i; /* Update the count mask. */ - i++; - } - outb(i, SCBPTR + base); /* Ensure we clear the control bytes. */ - outb(0, SCBARRAY + base); - outb(0, SCBPTR + base); - outb(0, SCBARRAY + base); - - config->maxhscbs = i; - config->qcntmask |= i; - if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB)) - { - config->maxscbs = AIC7XXX_MAXSCB; - } - else - { - config->flags &= ~PAGE_ENABLED; /* Disable paging if we have 255 SCBs!. */ - config->maxscbs = config->maxhscbs; - } - - printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs); - if (config->flags & PAGE_ENABLED) - printk(", %d page-enabled SCBs.\n", config->maxscbs); - else - printk(", paging not enabled.\n"); - + unsigned char brdctl; + + brdctl = BRDCS | BRDSTB; + outb(brdctl, p->base + BRDCTL); + brdctl |= value; + outb(brdctl, p->base + BRDCTL); + brdctl &= ~BRDSTB; + outb(brdctl, p->base + BRDCTL); + brdctl &= ~BRDCS; + outb(brdctl, p->base + BRDCTL); } /*+F************************************************************************* * Function: - * aic7xxx_register + * read_brdctl * * Description: - * Register a Adaptec aic7xxx chip SCSI controller with the kernel. + * Reads the BRDCTL register. *-F*************************************************************************/ -static int -aic7xxx_register(Scsi_Host_Template *template, - struct aic7xxx_host_config *config) +static inline unsigned char +read_brdctl(struct aic7xxx_host *p) { - int i; - unsigned char sblkctl, flags = 0; - int max_targets; - int found = 1; - unsigned int sram, base; - unsigned char target_settings; - unsigned char scsi_conf, host_conf; - unsigned short ultraenable = 0; - int have_seeprom = FALSE; - struct Scsi_Host *host; - struct aic7xxx_host *p; - struct seeprom_config sc; - - base = config->base; - - /* - * Lock out other contenders for our i/o space. - */ - request_region(base, MAXREG - MINREG, "aic7xxx"); + outb(BRDRW | BRDCS, p->base + BRDCTL); + return (inb(p->base + BRDCTL)); +} - switch (config->type) +/*+F************************************************************************* + * Function: + * configure_termination + * + * Description: + * Configures the termination settings on PCI adapters that have + * SEEPROMs available. + *-F*************************************************************************/ +static void +configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1, + unsigned short adapter_control, unsigned char max_targ) +{ + unsigned char brdctl_int, brdctl_ext; + int internal50_present; + int internal68_present = 0; + int external_present = 0; + int eprom_present; + int high_on; + int low_on; + int old_verbose; + + if (acquire_seeprom(p)) { - case AIC_7770: - case AIC_7771: - /* - * Use the boot-time option for the interrupt trigger type. If not - * supplied (-1), then we use BIOS settings to determine the interrupt - * trigger type (level or edge) and use this value for pausing and - * unpausing the sequencer. - */ - switch (aic7xxx_irq_trigger) - { - case 0: config->unpause = INTEN; /* Edge */ - break; - case 1: config->unpause = IRQMS | INTEN; /* Level */ - break; - case -1: - default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN; - break; - } - config->pause = config->unpause | PAUSE; + if (adapter_control & CFAUTOTERM) + { + old_verbose = aic7xxx_verbose; + printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please " + "verify driver"); + printk(KERN_INFO " detected settings and use manual termination " + "if necessary."); + + /* Configure auto termination. */ + outb(SEECS | SEEMS, p->base + SEECTL); /* - * For some 274x boards, we must clear the CHIPRST bit and pause - * the sequencer. For some reason, this makes the driver work. - * For 284x boards, we give it a CHIPRST just like the 294x boards. + * First read the status of our cables. Set the rom bank to + * 0 since the bank setting serves as a multiplexor for the + * cable detection logic. BRDDAT5 controls the bank switch. */ - outb(config->pause | CHIPRST, HCNTRL + base); - aic7xxx_delay(1); - if (inb(HCNTRL + base) & CHIPRST) - { - printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); - } - outb(config->pause, HCNTRL + base); + write_brdctl(p, 0); /* - * Just to be on the safe side with the 274x, we will re-read the irq - * since there was some issue about resetting the board. + * Now read the state of the internal connectors. The + * bits BRDDAT6 and BRDDAT7 are 0 when cables are present + * set when cables are not present (BRDDAT6 is INT50 and + * BRDDAT7 is INT68). */ - config->irq = inb(INTDEF + base) & 0x0F; - if ((config->type == AIC_7771) && - (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED) - { - config->bios = AIC_DISABLED; - config->flags |= USE_DEFAULTS; - } - else + brdctl_int = read_brdctl(p); + internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1; + if (max_targ > 8) { - host_conf = inb(HOSTCONF + base); - config->bus_speed = host_conf & DFTHRSH; - config->busrtime = (host_conf << 2) & BOFF; + internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1; } /* - * Setup the FIFO threshold and the bus off time + * Set the rom bank to 1 and determine + * the other signals. */ - outb(config->bus_speed & DFTHRSH, BUSSPD + base); - outb(config->busrtime, BUSTIME + base); + write_brdctl(p, BRDDAT5); /* - * A reminder until this can be detected automatically. + * Now read the state of the external connectors. BRDDAT6 is + * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is + * set when the eprom is present. */ - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; - - case AIC_284x: - outb(CHIPRST, HCNTRL + base); - config->unpause = UNPAUSE_284X; - config->pause = REQ_PAUSE; /* DWG would like to be like the rest */ - aic7xxx_delay(1); - outb(config->pause, HCNTRL + base); - - config->parity = AIC_ENABLED; - config->irq = inb(INTDEF + base) & 0x0F; - host_conf = inb(HOSTCONF + base); - - printk(KERN_INFO "aic7xxx: Reading SEEPROM..."); - have_seeprom = read_2840_seeprom(base, &sc); - if (!have_seeprom) + brdctl_ext = read_brdctl(p); + external_present = (brdctl_ext & BRDDAT6) ? 0 : 1; + eprom_present = brdctl_ext & BRDDAT7; + if (aic7xxx_verbose) { - printk("aic7xxx: Unable to read SEEPROM.\n"); - } - else - { - printk("done.\n"); - config->flags |= HAVE_SEEPROM; - if (sc.bios_control & CF284XEXTEND) - config->flags |= EXTENDED_TRANSLATION; - if (!(sc.bios_control & CFBIOSEN)) + if (max_targ > 8) { - /* - * The BIOS is disabled; the values left over in scratch - * RAM are still valid. Do not use defaults as in the - * AIC-7770 case. - */ - config->bios = AIC_DISABLED; + printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, " + "Ext-68 %s)\n", + internal50_present ? "YES" : "NO", + internal68_present ? "YES" : "NO", + external_present ? "YES" : "NO"); } else { - config->parity = (sc.adapter_control & CFSPARITY) ? - AIC_ENABLED : AIC_DISABLED; - config->low_term = (sc.adapter_control & CF284XSTERM) ? - AIC_ENABLED : AIC_DISABLED; - /* - * XXX - Adaptec *does* make 284x wide controllers, but the - * documents do not say where the high byte termination - * enable bit is located. - */ + printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n", + internal50_present ? "YES" : "NO", + external_present ? "YES" : "NO"); } + printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, " + "brdctl_ext=0x%x\n", + eprom_present ? "is" : "not", brdctl_int, brdctl_ext); } - host_conf = inb(HOSTCONF + base); - config->bus_speed = host_conf & DFTHRSH; - config->busrtime = (host_conf << 2) & BOFF; - - /* - * Setup the FIFO threshold and the bus off time - */ - outb(config->bus_speed & DFTHRSH, BUSSPD + base); - outb(config->busrtime, BUSTIME + base); - - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; - - case AIC_7860: - case AIC_7861: - case AIC_7880: - case AIC_7881: - case AIC_7882: - case AIC_7883: - case AIC_7884: - /* - * Remember if Ultra was enabled in case there is no SEEPROM. - * Fall through to the rest of the AIC_78xx code. - */ - if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra) - config->flags |= ULTRA_ENABLED; - - case AIC_7850: - case AIC_7855: - case AIC_7870: - case AIC_7871: - case AIC_7872: - case AIC_7873: - case AIC_7874: /* - * Grab the SCSI ID before chip reset in case there is no SEEPROM. + * Now set the termination based on what we found. BRDDAT6 + * controls wide termination enable. */ - config->scsi_id = inb(SCSIID + base) & OID; - outb(CHIPRST, HCNTRL + base); - config->unpause = UNPAUSE_294X; - config->pause = config->unpause | PAUSE; - aic7xxx_delay(1); - outb(config->pause, HCNTRL + base); - - config->parity = AIC_ENABLED; + high_on = FALSE; + low_on = FALSE; + if ((max_targ > 8) && + ((external_present == 0) || (internal68_present == 0))) + { + high_on = TRUE; + } - printk(KERN_INFO "aic7xxx: Reading SEEPROM..."); - if ((config->type == AIC_7873) || (config->type == AIC_7883)) + if ((internal50_present + internal68_present + external_present) <= 1) { - have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), - &sc, c56_66); + low_on = TRUE; } - else + + if (internal50_present && internal68_present && external_present) { - have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), - &sc, c46); + printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n" + " Only two connectors on the adapter may be " + "used at a time!\n"); } - if (!have_seeprom) + + if (high_on == TRUE) + write_brdctl(p, BRDDAT6); + else + write_brdctl(p, 0); + + if (low_on == TRUE) + *sxfrctl1 |= STPWEN; + + if (aic7xxx_verbose) { - for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++) + if (max_targ > 8) { - if (inb(sram) != 0x00) - break; - } - if (sram == base + TARG_SCRATCH) - { - for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++) - { - if (inb(sram) != 0xFF) - break; - } - } - if ((sram != base + 0x60) && (config->scsi_id != 0)) - { - config->flags &= ~USE_DEFAULTS; - printk("\naic7xxx: Unable to read SEEPROM; " - "using leftover BIOS values.\n"); + printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", + low_on ? "ON" : "OFF", + high_on ? "ON" : "OFF"); } else { - printk("\n"); - printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default " - "settings.\n"); - config->flags |= USE_DEFAULTS; - config->flags &= ~ULTRA_ENABLED; - config->scsi_id = 7; + printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF"); } - scsi_conf = ENSPCHK | RESET_SCSI; + } + aic7xxx_verbose = old_verbose; + } + else + { + if (adapter_control & CFSTERM) + { + *sxfrctl1 |= STPWEN; + } + outb(SEEMS | SEECS, p->base + SEECTL); + /* + * Configure high byte termination. + */ + if (adapter_control & CFWSTERM) + { + write_brdctl(p, BRDDAT6); } else { - printk("done.\n"); - config->flags |= HAVE_SEEPROM; - if (!(sc.bios_control & CFBIOSEN)) - { - /* - * The BIOS is disabled; the values left over in scratch - * RAM are still valid. Do not use defaults as in the - * AIC-7770 case. - */ - config->bios = AIC_DISABLED; - scsi_conf = ENSPCHK | RESET_SCSI; - } - else - { - scsi_conf = 0; - if (sc.adapter_control & CFRESETB) - scsi_conf |= RESET_SCSI; - if (sc.adapter_control & CFSPARITY) - scsi_conf |= ENSPCHK; - if (sc.bios_control & CFEXTEND) - config->flags |= EXTENDED_TRANSLATION; - config->scsi_id = (sc.brtime_id & CFSCSIID); - config->parity = (sc.adapter_control & CFSPARITY) ? - AIC_ENABLED : AIC_DISABLED; - config->low_term = (sc.adapter_control & CFSTERM) ? - AIC_ENABLED : AIC_DISABLED; - config->high_term = (sc.adapter_control & CFWSTERM) ? - AIC_ENABLED : AIC_DISABLED; - config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8); - if (((config->type == AIC_7880) || (config->type == AIC_7881) || - (config->type == AIC_7882) || (config->type == AIC_7883) || - (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN)) - { - printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI " - "speed.\n"); - config->flags |= ULTRA_ENABLED; - } - } + write_brdctl(p, 0); + } + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", + (adapter_control & CFSTERM) ? "ON" : "OFF", + (adapter_control & CFWSTERM) ? "ON" : "OFF"); } + } + release_seeprom(p); + } +} - outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base); - config->bus_speed = DFTHRSH_100; - outb(config->bus_speed, DSPCISTATUS + base); +/*+F************************************************************************* + * Function: + * detect_maxscb + * + * Description: + * Detects the maximum number of SCBs for the controller and returns + * the count and a mask in p (p->maxscbs, p->qcntmask). + *-F*************************************************************************/ +static void +detect_maxscb(struct aic7xxx_host *p) +{ + int i; + unsigned char max_scbid = 255; - /* - * In case we are a wide card... - */ - outb(config->scsi_id, SCSICONF + base + 1); + /* + * It's possible that we've already done this for multichannel + * adapters. + */ + if (p->scb_data->maxhscbs == 0) + { + /* + * We haven't initialized the SCB settings yet. Walk the SCBs to + * determince how many there are. + */ + outb(0, p->base + FREE_SCBH); - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; + for (i = 0; i < AIC7XXX_MAXSCB; i++) + { + outb(i, p->base + SCBPTR); + outb(i, p->base + SCB_CONTROL); + if (inb(p->base + SCB_CONTROL) != i) + break; + outb(0, p->base + SCBPTR); + if (inb(p->base + SCB_CONTROL) != 0) + break; - default: - panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n"); + outb(i, p->base + SCBPTR); + outb(0, p->base + SCB_CONTROL); /* Clear the control byte. */ + outb(i + 1, p->base + SCB_NEXT); /* Set the next pointer. */ + outb(SCB_LIST_NULL, p->base + SCB_TAG); /* Make the tag invalid. */ + + /* Make the non-tagged targets not busy. */ + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3); + } + + /* Make sure the last SCB terminates the free list. */ + outb(i - 1, p->base + SCBPTR); + outb(SCB_LIST_NULL, p->base + SCB_NEXT); + + /* Ensure we clear the first (0) SCBs control byte. */ + outb(0, p->base + SCBPTR); + outb(0, p->base + SCB_CONTROL); + + p->scb_data->maxhscbs = i; } - detect_maxscb(config); + if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB)) + { + /* Determine the number of valid bits in the FIFOs. */ + outb(max_scbid, p->base + QINFIFO); + max_scbid = inb(p->base + QINFIFO); + p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1); + } + else + { + p->scb_data->maxscbs = p->scb_data->maxhscbs; + } + if (p->scb_data->maxscbs == p->scb_data->maxhscbs) + { + /* + * Disable paging if the QINFIFO doesn't allow more SCBs than + * we have in hardware. + */ + p->flags &= ~PAGE_ENABLED; + } - if (config->chip_type == AIC_777x) + /* + * Set the Queue Full Count. Some cards have more queue space than + * SCBs. + */ + switch (p->chip_class) { - if (config->pause & IRQMS) - { - printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n"); - } - else - { - printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n"); - } + case AIC_777x: + p->qfullcount = 4; + p->qcntmask = 0x07; + break; + case AIC_785x: + case AIC_786x: + p->qfullcount = 8; + p->qcntmask = 0x0f; + break; + case AIC_787x: + case AIC_788x: + if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB) + { + p->qfullcount = AIC7XXX_MAXSCB; + p->qcntmask = 0xFF; + } + else + { + p->qfullcount = 16; + p->qcntmask = 0x1F; + } + break; } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_register + * + * Description: + * Register a Adaptec aic7xxx chip SCSI controller with the kernel. + *-F*************************************************************************/ +static int +aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p) +{ + int i; + unsigned char sblkctl, flags = 0; + int max_targets; + int found = 1; + char channel_ids[] = {'A', 'B', 'C'}; + unsigned char target_settings; + unsigned char scsi_conf, sxfrctl1; + unsigned short ultraenable = 0; + struct Scsi_Host *host; + + /* + * Lock out other contenders for our i/o space. + */ + request_region(p->base, MAXREG - MINREG, "aic7xxx"); /* * Read the bus type from the SBLKCTL register. Set the FLAGS * register in the sequencer for twin and wide bus cards. */ - sblkctl = inb(SBLKCTL + base); - if (config->flags & PAGE_ENABLED) + sblkctl = inb(p->base + SBLKCTL); + if (p->flags & PAGE_ENABLED) flags = PAGESCBS; switch (sblkctl & SELBUS_MASK) { case SELNARROW: /* narrow/normal bus */ - config->scsi_id = inb(SCSICONF + base) & 0x07; - config->bus_type = AIC_SINGLE; - outb(flags | SINGLE_BUS, FLAGS + base); + p->scsi_id = inb(p->base + SCSICONF) & 0x07; + p->bus_type = AIC_SINGLE; + p->flags &= ~FLAGS_CHANNEL_B_PRIMARY; + if (p->flags & MULTI_CHANNEL) + { + printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ", + channel_ids[p->chan_num], p->scsi_id); + } + else + { + printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ", + p->scsi_id); + } + outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS); break; case SELWIDE: /* Wide bus */ - config->scsi_id = inb(SCSICONF + base + 1) & 0x0F; - config->bus_type = AIC_WIDE; - printk("aic7xxx: Enabling wide channel of %s-Wide.\n", - board_names[config->type]); - outb(flags | WIDE_BUS, FLAGS + base); + p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID; + p->bus_type = AIC_WIDE; + p->flags &= ~FLAGS_CHANNEL_B_PRIMARY; + if (p->flags & MULTI_CHANNEL) + { + printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ", + channel_ids[p->chan_num], p->scsi_id); + } + else + { + printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ", + p->scsi_id); + } + outb(flags | WIDE_BUS, p->base + SEQ_FLAGS); break; case SELBUSB: /* Twin bus */ - config->scsi_id = inb(SCSICONF + base) & 0x07; -#ifdef AIC7XXX_TWIN_SUPPORT - config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07; - config->bus_type = AIC_TWIN; - printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n", - board_names[config->type]); - outb(flags | TWIN_BUS, FLAGS + base); -#else - config->bus_type = AIC_SINGLE; - printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n", - board_names[config->type]); - outb(flags, FLAGS + base); -#endif + p->scsi_id = inb(p->base + SCSICONF) & HSCSIID; + p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID; + p->bus_type = AIC_TWIN; + printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ", + p->scsi_id, p->scsi_id_b); + outb(flags | TWIN_BUS, p->base + SEQ_FLAGS); break; default: printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please " - "mail deang@teleport.com\n", inb(SBLKCTL + base)); - outb(0, FLAGS + base); + "mail deang@teleport.com\n", inb(p->base + SBLKCTL)); + outb(0, p->base + SEQ_FLAGS); return (0); } /* - * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will - * take the card out of diagnostic mode and make the host adapter - * LED follow bus activity (will not always be on). - */ - outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base); - - /* - * The IRQ level in i/o port 4 maps directly onto the real - * IRQ number. If it's ok, register it with the kernel. - * - * NB. the Adaptec documentation says the IRQ number is only - * in the lower four bits; the ECU information shows the - * high bit being used as well. Which is correct? - * - * The PCI cards get their interrupt from PCI BIOS. - */ - if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15))) - { - printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, " - "ignoring.\n"); - return (0); - } - - /* - * Print out debugging information before re-enabling - * the card - a lot of registers on it can't be read - * when the sequencer is active. + * Detect SCB parameters and initialize the SCB array. */ - debug_config(config); + detect_maxscb(p); + printk("%d/%d SCBs, QFull %d, QMask 0x%x\n", + p->scb_data->maxhscbs, p->scb_data->maxscbs, + p->qfullcount, p->qcntmask); - /* - * Register each "host" and fill in the returned Scsi_Host - * structure as best we can. Some of the parameters aren't - * really relevant for bus types beyond ISA, and none of the - * high-level SCSI code looks at it anyway. Why are the fields - * there? Also save the pointer so that we can find the - * information when an IRQ is triggered. - */ - host = scsi_register(template, sizeof(struct aic7xxx_host)); - host->can_queue = config->maxscbs; + host = p->host; + + host->can_queue = p->scb_data->maxscbs; host->cmd_per_lun = 2; host->select_queue_depths = aic7xxx_select_queue_depth; - host->this_id = config->scsi_id; - host->io_port = config->base; + host->this_id = p->scsi_id; + host->io_port = p->base; host->n_io_port = 0xFF; - host->base = (unsigned char *)config->mbase; - host->irq = config->irq; - if (config->bus_type == AIC_WIDE) + host->base = (unsigned char *) p->mbase; + host->irq = p->irq; + if (p->bus_type == AIC_WIDE) { host->max_id = 16; } - if (config->bus_type == AIC_TWIN) + if (p->bus_type == AIC_TWIN) { host->max_channel = 1; } - p = (struct aic7xxx_host *) host->hostdata; - p->host = host; - p->host_no = (int)host->host_no; + p->host_no = host->host_no; p->isr_count = 0; - p->base = base; - p->maxscbs = config->maxscbs; - p->maxhscbs = config->maxhscbs; - p->qcntmask = config->qcntmask; - p->mbase = (char *)config->mbase; - p->type = config->type; - p->chip_type = config->chip_type; - p->flags = config->flags; - p->chan_num = config->chan_num; - p->scb_link = &(p->scb_usage); -#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS) - if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883))) - { - shared_3985_scbs = &(p->scb_usage); - p->scb_link = &(p->scb_usage); - } -#endif - p->scb_link->numscbs = 0; - p->bus_type = config->bus_type; - p->seeprom = sc; p->next = NULL; p->completeq.head = NULL; p->completeq.tail = NULL; - scbq_init(&p->scb_link->free_scbs); - scbq_init(&p->page_scbs); + scbq_init(&p->scb_data->free_scbs); scbq_init(&p->waiting_scbs); - scbq_init(&p->assigned_scbs); - - p->unpause = config->unpause; - p->pause = config->pause; - for (i = 0; i <= 15; i++) + for (i = 0; i <= NUMBER(p->device_status); i++) { p->device_status[i].commands_sent = 0; p->device_status[i].flags = 0; + p->device_status[i].active_cmds = 0; p->device_status[i].last_reset = 0; } - if (aic7xxx_boards[config->irq] == NULL) + if (aic7xxx_boards[p->irq] == NULL) { + int result; + int irq_flags = 0; + +#ifdef AIC7XXX_OLD_ISR_TYPE + irg_flags = SA_INTERRUPT; +#endif /* * Warning! This must be done before requesting the irq. It is * possible for some boards to raise an interrupt as soon as @@ -4353,17 +5067,26 @@ aic7xxx_register(Scsi_Host_Template *template, * kernel, an interrupt is triggered immediately. Therefore, we * must ensure the board data is correctly set before the request. */ - aic7xxx_boards[config->irq] = host; + aic7xxx_boards[p->irq] = host; /* - * Register IRQ with the kernel. + * Register IRQ with the kernel. Only allow sharing IRQs with + * PCI devices. */ - if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ, - "aic7xxx", NULL)) + if (p->chip_class == AIC_777x) + { + result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL)); + } + else + { + result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ, + "aic7xxx", NULL)); + } + if (result < 0) { printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n", - config->irq); - aic7xxx_boards[config->irq] = NULL; + p->irq); + aic7xxx_boards[p->irq] = NULL; return (0); } } @@ -4374,79 +5097,74 @@ aic7xxx_register(Scsi_Host_Template *template, * registered host adapter. Add this host adapter's Scsi_Host * to the beginning of the linked list of hosts at the same IRQ. */ - p->next = aic7xxx_boards[config->irq]; - aic7xxx_boards[config->irq] = host; - } - - /* - * Load the sequencer program, then re-enable the board - - * resetting the AIC-7770 disables it, leaving the lights - * on with nobody home. On the PCI bus you *may* be home, - * but then your mailing address is dynamically assigned - * so no one can find you anyway :-) - */ - printk(KERN_INFO "aic7xxx: Downloading sequencer code..."); - aic7xxx_loadseq(base); - - /* - * Set Fast Mode and Enable the board - */ - outb(FASTMODE, SEQCTL + base); - - if (p->chip_type == AIC_777x) - { - outb(ENABLE, BCTL + base); + p->next = aic7xxx_boards[p->irq]; + aic7xxx_boards[p->irq] = host; } - printk("done.\n"); - /* * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels */ if (p->bus_type == AIC_TWIN) { /* - * Select Channel B. + * The controller is gated to channel B after a chip reset; set + * bus B values first. */ - outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base); - - outb(config->scsi_id_b, SCSIID + base); - scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL); - outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if EXPERIMENTAL_FLAGS - outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); -#else - outb(ENSELTIMO, SIMODE1 + base); -#endif + outb(p->scsi_id_b, p->base + SCSIID); + scsi_conf = inb(p->base + SCSICONF + 1); + sxfrctl1 = inb(p->base + SXFRCTL1); + outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | + ENSTIMER | ACTNEGEN, p->base + SXFRCTL1); + outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1); if (p->flags & ULTRA_ENABLED) { - outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base); + outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0); } else { - outb(DFON | SPIOEN, SXFRCTL0 + base); + outb(DFON | SPIOEN, p->base + SXFRCTL0); } - /* - * Select Channel A - */ - outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base); + if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0)) + { + /* Reset SCSI bus B. */ + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Resetting channel B\n"); + + aic7xxx_reset_current_bus(p); + } + + /* Select channel A */ + outb(SELNARROW, p->base + SBLKCTL); } - outb(config->scsi_id, SCSIID + base); - scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL); - outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if EXPERIMENTAL_FLAGS - outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); -#else - outb(ENSELTIMO, SIMODE1 + base); -#endif + + outb(p->scsi_id, p->base + SCSIID); + scsi_conf = inb(p->base + SCSICONF); + sxfrctl1 = inb(p->base + SXFRCTL1); + outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | + ENSTIMER | ACTNEGEN, p->base + SXFRCTL1); + outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1); if (p->flags & ULTRA_ENABLED) { - outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base); + outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0); } else { - outb(DFON | SPIOEN, SXFRCTL0 + base); + outb(DFON | SPIOEN, p->base + SXFRCTL0); + } + + if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0)) + { + /* Reset SCSI bus A. */ + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Resetting channel A\n"); + + aic7xxx_reset_current_bus(p); + + /* + * Delay for the reset delay. + */ + aic7xxx_delay(AIC7XXX_RESET_DELAY); } /* @@ -4473,67 +5191,47 @@ aic7xxx_register(Scsi_Host_Template *template, /* * Grab the disconnection disable table and invert it for our needs */ - if (have_seeprom) + if (p->flags & USE_DEFAULTS) { - p->discenable = 0x0; + printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI " + "device parameters.\n"); + p->discenable = 0xFFFF; } else { - if (config->bios == AIC_DISABLED) - { - printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI " - "device parameters.\n"); - p->discenable = 0xFFFF; - } - else - { - p->discenable = ~((inb(DISC_DSB + base + 1) << 8) | - inb(DISC_DSB + base)); - } + p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) | + inb(p->base + DISC_DSB)); } for (i = 0; i < max_targets; i++) { - if (config->flags & USE_DEFAULTS) + if (p->flags & USE_DEFAULTS) { - target_settings = 0; /* 10 MHz */ + target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */ p->needsdtr_copy |= (0x01 << i); p->needwdtr_copy |= (0x01 << i); + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) + ultraenable |= (0x01 << i); } else { - if (have_seeprom) + target_settings = inb(p->base + TARG_SCRATCH + i); + if (target_settings & 0x0F) { - target_settings = ((sc.device_flags[i] & CFXFER) << 4); - if (sc.device_flags[i] & CFSYNCH) - { - p->needsdtr_copy |= (0x01 << i); - } - if (sc.device_flags[i] & CFWIDEB) - { - p->needwdtr_copy |= (0x01 << i); - } - if (sc.device_flags[i] & CFDISC) - { - p->discenable |= (0x01 << i); - } + p->needsdtr_copy |= (0x01 << i); + /* + * Default to asynchronous transfers (0 offset) + */ + target_settings &= 0xF0; } - else + if (target_settings & 0x80) { - target_settings = inb(TARG_SCRATCH + base + i); - if (target_settings & 0x0F) - { - p->needsdtr_copy |= (0x01 << i); - /* - * Default to asynchronous transfers (0 offset) - */ - target_settings &= 0xF0; - } - if (target_settings & 0x80) - { - p->needwdtr_copy |= (0x01 << i); - target_settings &= 0x7F; - } + p->needwdtr_copy |= (0x01 << i); + /* + * Clear the wide flag. When wide negotiation is successful, + * we'll enable it. + */ + target_settings &= 0x7F; } if (p->flags & ULTRA_ENABLED) { @@ -4544,126 +5242,471 @@ aic7xxx_register(Scsi_Host_Template *template, case 0x20: ultraenable |= (0x01 << i); break; - case 0x40: + case 0x40: /* treat 10MHz as 10MHz without Ultra enabled */ target_settings &= ~(0x70); break; default: break; } } - } - outb(target_settings, (TARG_SCRATCH + base + i)); + } + outb(target_settings, p->base + TARG_SCRATCH + i); + } + + /* + * If we are not wide, forget WDTR. This makes the driver + * work on some cards that don't leave these fields cleared + * when BIOS is not installed. + */ + if (p->bus_type != AIC_WIDE) + { + p->needwdtr_copy = 0; + } + p->needsdtr = p->needsdtr_copy; + p->needwdtr = p->needwdtr_copy; + p->orderedtag = 0; + outb(ultraenable & 0xFF, p->base + ULTRA_ENB); + outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1); + + /* + * Set the number of available hardware SCBs. + */ + outb(p->scb_data->maxhscbs, p->base + SCBCOUNT); + + /* + * 2s compliment of maximum tag value. + */ + i = p->scb_data->maxscbs; + outb(-i & 0xFF, p->base + COMP_SCBCOUNT); + + /* + * Allocate enough hardware scbs to handle the maximum number of + * concurrent transactions we can have. We have to make sure that + * the allocated memory is contiguous memory. The Linux kmalloc + * routine should only allocate contiguous memory, but note that + * this could be a problem if kmalloc() is changed. + */ + if (p->scb_data->hscbs == NULL) + { + size_t array_size; + unsigned int hscb_physaddr; + + array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb); + p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC); + if (p->scb_data->hscbs == NULL) + { + printk("aic7xxx: Unable to allocate hardware SCB array; " + "failing detection.\n"); + release_region(p->base, MAXREG - MINREG); + /* + * Ensure that we only free the IRQ when there is _not_ another + * aic7xxx adapter sharing this IRQ. The adapters are always + * added to the beginning of the list, so we can grab the next + * pointer and place it back in the board array. + */ + if (p->next == NULL) + { + free_irq(p->irq, aic7xxx_isr); + } + aic7xxx_boards[p->irq] = p->next; + return(0); + } + /* At least the control byte of each SCB needs to be 0. */ + memset(p->scb_data->hscbs, 0, array_size); + + /* Tell the sequencer where it can find the hardware SCB array. */ + hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs); + outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR); + outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1); + outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2); + outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3); + } + + /* + * QCount mask to deal with broken aic7850s that sporadically get + * garbage in the upper bits of their QCNT registers. + */ + outb(p->qcntmask, p->base + QCNTMASK); + + /* + * We don't have any waiting selections or disconnected SCBs. + */ + outb(SCB_LIST_NULL, p->base + WAITING_SCBH); + outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH); + + /* + * Message out buffer starts empty + */ + outb(0, p->base + MSG_LEN); + + /* + * Load the sequencer program, then re-enable the board - + * resetting the AIC-7770 disables it, leaving the lights + * on with nobody home. On the PCI bus you *may* be home, + * but then your mailing address is dynamically assigned + * so no one can find you anyway :-) + */ + aic7xxx_loadseq(p); + + if (p->chip_class == AIC_777x) + { + outb(ENABLE, p->base + BCTL); /* Enable the boards BUS drivers. */ + } + + /* + * Unpause the sequencer before returning and enable + * interrupts - we shouldn't get any until the first + * command is sent to us by the high-level SCSI code. + */ + unpause_sequencer(p, /* unpause_always */ TRUE); + + return (found); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_chip_reset + * + * Description: + * Perform a chip reset on the aic7xxx SCSI controller. The controller + * is paused upon return. + *-F*************************************************************************/ +static void +aic7xxx_chip_reset(struct aic7xxx_host *p) +{ + unsigned char hcntrl; + int wait; + + /* Retain the IRQ type across the chip reset. */ + hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN; + + /* + * For some 274x boards, we must clear the CHIPRST bit and pause + * the sequencer. For some reason, this makes the driver work. + */ + outb(PAUSE | CHIPRST, p->base + HCNTRL); + + /* + * In the future, we may call this function as a last resort for + * error handling. Let's be nice and not do any unecessary delays. + */ + wait = 1000; /* 1 second (1000 * 1000 usec) */ + while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)) + { + udelay(1000); /* 1 msec = 1000 usec */ + wait = wait - 1; + } + + if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0) + { + printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); + } + + outb(hcntrl | PAUSE, p->base + HCNTRL); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_alloc + * + * Description: + * Allocate and initialize a host structure. Returns NULL upon error + * and a pointer to a aic7xxx_host struct upon success. + *-F*************************************************************************/ +static struct aic7xxx_host * +aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase, + aha_chip_type chip_type, int flags, scb_data_type *scb_data) +{ + struct aic7xxx_host *p = NULL; + struct Scsi_Host *host; + + /* + * Allocate a storage area by registering us with the mid-level + * SCSI layer. + */ + host = scsi_register(sht, sizeof(struct aic7xxx_host)); + + if (host != NULL) + { + p = (struct aic7xxx_host *) host->hostdata; + memset(p, 0, sizeof(struct aic7xxx_host)); + p->host = host; + + if (scb_data != NULL) + { + /* + * We are sharing SCB data areas; use the SCB data pointer + * provided. + */ + p->scb_data = scb_data; + p->flags |= SHARED_SCBDATA; + } + else + { + /* + * We are not sharing SCB data; allocate one. + */ + p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC); + if (p->scb_data != NULL) + { + memset(p->scb_data, 0, sizeof(scb_data_type)); + scbq_init (&p->scb_data->free_scbs); + } + else + { + /* + * For some reason we don't have enough memory. Free the + * allocated memory for the aic7xxx_host struct, and return NULL. + */ + scsi_unregister(host); + p = NULL; + } + } + if (p != NULL) + { + p->host_no = host->host_no; + p->base = base; + p->mbase = mbase; + p->maddr = NULL; + p->flags = flags; + p->chip_type = chip_type; + p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN; + p->pause = p->unpause | PAUSE; + } + } + return (p); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_free + * + * Description: + * Frees and releases all resources associated with an instance of + * the driver (struct aic7xxx_host *). + *-F*************************************************************************/ +static void +aic7xxx_free (struct aic7xxx_host *p) +{ + int i; + + /* + * We should be careful in freeing the scb_data area. For those + * adapters sharing external SCB RAM(398x), there will be only one + * scb_data area allocated. The flag SHARED_SCBDATA indicates if + * one adapter is sharing anothers SCB RAM. + */ + if (!(p->flags & SHARED_SCBDATA)) + { + /* + * Free the allocated hardware SCB space. + */ + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscbs); + } + /* + * Free the driver SCBs. These were allocated on an as-need + * basis. + */ + for (i = 0; i < p->scb_data->numscbs; i++) + { + kfree(p->scb_data->scb_array[i]); + } + /* + * Free the hardware SCBs. + */ + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscbs); + } + + /* + * Free the SCB data area. + */ + kfree(p->scb_data); + } + /* + * Free the instance of the device structure. + */ + scsi_unregister(p->host); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_load_seeprom + * + * Description: + * Load the seeprom and configure adapter and target settings. + * Returns 1 if the load was successful and 0 otherwise. + *-F*************************************************************************/ +static int +load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1) +{ + int have_seeprom = 0; + int i, max_targets; + unsigned char target_settings, scsi_conf; + unsigned short scarray[128]; + struct seeprom_config *sc = (struct seeprom_config *) scarray; + + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Loading serial EEPROM..."); + } + switch (p->chip_type) + { + case AIC_7770: /* None of these adapters have seeproms. */ + case AIC_7771: + case AIC_7850: + case AIC_7855: + break; + + case AIC_284x: + have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray); + break; + + case AIC_7861: + case AIC_7870: + case AIC_7871: + case AIC_7872: + case AIC_7874: + case AIC_7881: + case AIC_7882: + case AIC_7884: + have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2), + scarray, sizeof(*sc)/2, C46); + break; + + case AIC_7860: /* Motherboard Ultra controllers might have RAID port. */ + case AIC_7880: + have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46); + if (!have_seeprom) + { + have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66); + } + break; + + case AIC_7873: /* The 3985 adapters use the 93c56 serial EEPROM. */ + case AIC_7883: + have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2), + scarray, sizeof(scarray)/2, C56_66); + break; + + default: + break; } - /* - * If we are not wide, forget WDTR. This makes the driver - * work on some cards that don't leave these fields cleared - * when BIOS is not installed. - */ - if (p->bus_type != AIC_WIDE) + if (!have_seeprom) { - p->needwdtr_copy = 0; + if (aic7xxx_verbose) + { + printk("\naic7xxx: No SEEPROM available; using defaults.\n"); + } + p->flags |= USE_DEFAULTS; } - p->needsdtr = p->needsdtr_copy; - p->needwdtr = p->needwdtr_copy; - p->orderedtag = 0; -#if 0 - printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr); - printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr); -#endif - outb(ultraenable & 0xFF, ULTRA_ENB + base); - outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1); - - /* - * Set the number of available SCBs. - */ - outb(config->maxhscbs, SCBCOUNT + base); + else + { + if (aic7xxx_verbose) + { + printk("done\n"); + } + p->flags |= HAVE_SEEPROM; - /* - * 2s compliment of maximum tag value. - */ - i = p->maxscbs; - outb(-i & 0xFF, COMP_SCBCOUNT + base); + /* + * Update the settings in sxfrctl1 to match the termination settings. + */ + *sxfrctl1 = 0; - /* - * Set the QCNT (queue count) mask to deal with broken aic7850s that - * sporatically get garbage in the upper bits of their QCNT registers. - */ - outb(config->qcntmask, QCNTMASK + base); + /* + * First process the settings that are different between the VLB + * and PCI adapter seeproms. + */ + if (p->chip_class == AIC_777x) + { + /* VLB adapter seeproms */ + if (sc->bios_control & CF284XEXTEND) + p->flags |= EXTENDED_TRANSLATION; - /* - * Clear the active flags - no targets are busy. - */ - outb(0, ACTIVE_A + base); - outb(0, ACTIVE_B + base); + if (sc->adapter_control & CF284XSTERM) + *sxfrctl1 |= STPWEN; + /* + * The 284x SEEPROM doesn't have a max targets field. We + * set it to 16 to make sure we take care of the 284x-wide + * adapters. For narrow adapters, going through the extra + * 8 target entries will not cause any harm since they will + * will not be used. + * + * XXX - We should probably break out the bus detection + * from the register function so we can use it here + * to tell us how many targets there really are. + */ + max_targets = 16; + } + else + { + /* PCI adapter seeproms */ + if (sc->bios_control & CFEXTEND) + p->flags |= EXTENDED_TRANSLATION; - /* - * We don't have any waiting selections or disconnected SCBs. - */ - outb(SCB_LIST_NULL, WAITING_SCBH + base); - outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base); + if (sc->adapter_control & CFSTERM) + *sxfrctl1 |= STPWEN; - /* - * Message out buffer starts empty - */ - outb(0, MSG_LEN + base); + /* Limit to 16 targets just in case. */ + max_targets = MIN(sc->max_targets & CFMAXTARG, 16); + } - /* - * Reset the SCSI bus. Is this necessary? - * There may be problems for a warm boot without resetting - * the SCSI bus. Either BIOS settings in scratch RAM - * will not get reinitialized, or devices may stay at - * previous negotiated settings (SDTR and WDTR) while - * the driver will think that no negotiations have been - * performed. - * - * Some devices need a long time to "settle" after a SCSI - * bus reset. - */ - if (!aic7xxx_no_reset) - { - printk("aic7xxx: Resetting the SCSI bus..."); - if (p->bus_type == AIC_TWIN) + for (i = 0; i < max_targets; i++) { - /* - * Select Channel B. - */ - outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base); + target_settings = (sc->device_flags[i] & CFXFER) << 4; + if (sc->device_flags[i] & CFSYNCH) + target_settings |= SOFS; + if (sc->device_flags[i] & CFWIDEB) + target_settings |= WIDEXFER; + if (sc->device_flags[i] & CFDISC) + p->discenable |= (0x01 << i); + outb(target_settings, p->base + TARG_SCRATCH + i); + } + outb(~(p->discenable & 0xFF), p->base + DISC_DSB); + outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1); - outb(SCSIRSTO, SCSISEQ + base); - udelay(1000); - outb(0, SCSISEQ + base); + p->scsi_id = sc->brtime_id & CFSCSIID; - /* Ensure we don't get a RSTI interrupt from this. */ - outb(CLRSCSIRSTI, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); + scsi_conf = (p->scsi_id & 0x7); + if (sc->adapter_control & CFSPARITY) + scsi_conf |= ENSPCHK; + if (sc->adapter_control & CFRESETB) + scsi_conf |= RESET_SCSI; - /* - * Select Channel A. + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) + { + /* + * We allow the operator to override ultra enable through + * the boot prompt. */ - outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base); + if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0)) + { + /* Treat us as a non-ultra card */ + p->flags &= ~ULTRA_ENABLED; + } } - outb(SCSIRSTO, SCSISEQ + base); - udelay(1000); - outb(0, SCSISEQ + base); - - /* Ensure we don't get a RSTI interrupt from this. */ - outb(CLRSCSIRSTI, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - - aic7xxx_delay(AIC7XXX_RESET_DELAY); + /* Set the host ID */ + outb(scsi_conf, p->base + SCSICONF); + /* In case we are a wide card */ + outb(p->scsi_id, p->base + SCSICONF + 1); - printk("done.\n"); + if (p->chip_class != AIC_777x) + { + /* + * Update the settings in sxfrctl1 to match the termination + * settings. + */ + *sxfrctl1 = 0; + configure_termination(p, sxfrctl1, sc->adapter_control, + (unsigned char) sc->max_targets & CFMAXTARG); + } } - - /* - * Unpause the sequencer before returning and enable - * interrupts - we shouldn't get any until the first - * command is sent to us by the high-level SCSI code. - */ - UNPAUSE_SEQUENCER(p); - return (found); + return (have_seeprom); } /*+F************************************************************************* @@ -4672,17 +5715,24 @@ aic7xxx_register(Scsi_Host_Template *template, * * Description: * Try to detect and register an Adaptec 7770 or 7870 SCSI controller. + * + * XXX - This should really be called aic7xxx_probe(). A sequence of + * probe(), attach()/detach(), and init() makes more sense than + * one do-it-all function. This may be useful when (and if) the + * mid-level SCSI code is overhauled. *-F*************************************************************************/ int aic7xxx_detect(Scsi_Host_Template *template) { - int found = 0, slot, base; - unsigned char irq = 0; + int found = 0; + aha_status_type adapter_bios; + aha_chip_class_type chip_class; + aha_chip_type chip_type; + int slot, base; + int chan_num = 0; + unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0; int i; - struct aic7xxx_host_config config; - - template->proc_dir = &proc_scsi_aic7xxx; - config.chan_num = 0; + struct aic7xxx_host *p; /* * Since we may allow sharing of IRQs, it is imperative @@ -4696,6 +5746,9 @@ aic7xxx_detect(Scsi_Host_Template *template) aic7xxx_boards[i] = NULL; } + template->proc_dir = &proc_scsi_aic7xxx; + template->name = aic7xxx_info(NULL); + /* * Initialize the spurious count to 0. */ @@ -4717,33 +5770,170 @@ aic7xxx_detect(Scsi_Host_Template *template) continue; } - config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios)); - if (config.type != AIC_NONE) + chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios)); + if (chip_type != AIC_NONE) { + + switch (chip_type) + { + case AIC_7770: + case AIC_7771: + printk("aic7xxx: <%s> at EISA %d\n", + board_names[chip_type], slot); + break; + case AIC_284x: + printk("aic7xxx: <%s> at VLB %d\n", + board_names[chip_type], slot); + break; + default: + break; + } + /* * We found a card, allow 1 spurious interrupt. */ aic7xxx_spurious_count = 1; /* - * We "find" a AIC-7770 if we locate the card - * signature and we can set it up and register - * it with the kernel without incident. + * Pause the card preserving the IRQ type. Allow the operator + * to override the IRQ trigger. */ - config.chip_type = AIC_777x; - config.base = base; - config.mbase = 0; - config.irq = irq; - config.parity = AIC_ENABLED; - config.low_term = AIC_UNKNOWN; - config.high_term = AIC_UNKNOWN; - config.flags = 0; - if (aic7xxx_extended) - config.flags |= EXTENDED_TRANSLATION; - config.bus_speed = DFTHRSH_100; - config.busrtime = BOFF; - found += aic7xxx_register(template, &config); + if (aic7xxx_irq_trigger == 1) + hcntrl = IRQMS; /* Level */ + else if (aic7xxx_irq_trigger == 0) + hcntrl = 0; /* Edge */ + else + hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */ + outb(hcntrl | PAUSE, base + HCNTRL); + + irq = inb(INTDEF + base) & 0x0F; + switch (irq) + { + case 9: + case 10: + case 11: + case 12: + case 14: + case 15: + break; + + default: + printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ " + "level, ignoring.\n"); + irq = 0; + break; + } + + if (irq != 0) + { + p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL); + if (p == NULL) + { + printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); + continue; + } + p->irq = irq & 0x0F; + p->chip_class = AIC_777x; +#ifdef AIC7XXX_PAGE_ENABLE + p->flags |= PAGE_ENABLED; +#endif + p->instance = found; + if (aic7xxx_extended) + { + p->flags |= EXTENDED_TRANSLATION; + } + aic7xxx_chip_reset(p); + + switch (p->chip_type) + { + case AIC_7770: + case AIC_7771: + { + unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL); + + /* + * Get the primary channel information. Right now we don't + * do anything with this, but someday we will be able to inform + * the mid-level SCSI code which channel is primary. + */ + if (biosctrl & CHANNEL_B_PRIMARY) + { + p->flags |= FLAGS_CHANNEL_B_PRIMARY; + } + + if ((biosctrl & BIOSMODE) == BIOSDISABLED) + { + p->flags |= USE_DEFAULTS; + } + break; + } + + case AIC_284x: + if (!load_seeprom(p, &sxfrctl1)) + { + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: SEEPROM not available.\n"); + } + break; + + default: /* Won't get here. */ + break; + } + printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ", + (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq, + (p->pause & IRQMS) ? "level sensitive" : "edge triggered"); + /* + * Check for Rev C or E boards. Rev E boards can supposedly have + * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs. + * It's still not clear extactly what is different about the Rev E + * boards, but we think it allows 8 bit entries in the QOUTFIFO to + * support "paging" SCBs (more than 4 commands can be active at once). + * + * The Rev E boards have a read/write autoflush bit in the + * SBLKCTL register, while in the Rev C boards it is read only. + */ + sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS; + outb(sblkctl, p->base + SBLKCTL); + if (inb(p->base + SBLKCTL) == sblkctl) + { + /* + * We detected a Rev E board, we allow paging on this board. + */ + printk("Revision >= E\n"); + outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL); + } + else + { + /* Do not allow paging. */ + p->flags &= ~PAGE_ENABLED; + printk("Revision <= C\n"); + } + + /* + * Set the FIFO threshold and the bus off time. + */ + hostconf = inb(p->base + HOSTCONF); + outb(hostconf & DFTHRSH, p->base + BUSSPD); + outb((hostconf << 2) & BOFF, p->base + BUSTIME); + /* + * Try to initialize the card and register it with the kernel. + */ + if (aic7xxx_register(template, p)) + { + /* + * We successfully found a board and registered it. + */ + found = found + 1; + } + else + { + /* + * Something went wrong; release and free all resources. + */ + aic7xxx_free(p); + } + } /* * Disallow spurious interrupts. */ @@ -4759,15 +5949,15 @@ aic7xxx_detect(Scsi_Host_Template *template) { struct { - unsigned short vendor_id; - unsigned short device_id; - aha_type card_type; - aha_chip_type chip_type; + unsigned short vendor_id; + unsigned short device_id; + aha_chip_type chip_type; + aha_chip_class_type chip_class; } const aic7xxx_pci_devices[] = { {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x}, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x}, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x}, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x}, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x}, @@ -4780,14 +5970,14 @@ aic7xxx_detect(Scsi_Host_Template *template) {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x} }; - int error; + int error, flags; int done = 0; unsigned int iobase, mbase; unsigned short index = 0; unsigned char pci_bus, pci_device_fn; - unsigned int csize_lattime; - unsigned int class_revid; - unsigned int devconfig; + unsigned char ultra_enb = 0; + unsigned int devconfig, class_revid; + scb_data_type *shared_scb_data = NULL; char rev_id[] = {'B', 'C', 'D'}; for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++) @@ -4804,36 +5994,33 @@ aic7xxx_detect(Scsi_Host_Template *template) } else /* Found an Adaptec PCI device. */ { - config.type = aic7xxx_pci_devices[i].card_type; - config.chip_type = aic7xxx_pci_devices[i].chip_type; - config.chan_num = 0; - config.bios = AIC_ENABLED; /* Assume bios is enabled. */ - config.flags = 0; - config.busrtime = 40; - switch (config.type) + chip_class = aic7xxx_pci_devices[i].chip_class; + chip_type = aic7xxx_pci_devices[i].chip_type; + chan_num = 0; + flags = 0; + switch (aic7xxx_pci_devices[i].chip_type) { case AIC_7850: case AIC_7855: - case AIC_7860: - case AIC_7861: - config.bios = AIC_DISABLED; - config.flags |= USE_DEFAULTS; - config.bus_speed = DFTHRSH_100; + flags |= USE_DEFAULTS; break; case AIC_7872: /* 3940 */ case AIC_7882: /* 3940-Ultra */ - config.chan_num = number_of_3940s & 0x1; /* Has 2 controllers */ + flags |= MULTI_CHANNEL; + chan_num = number_of_3940s & 0x1; /* Has 2 controllers */ number_of_3940s++; break; case AIC_7873: /* 3985 */ case AIC_7883: /* 3985-Ultra */ - config.chan_num = number_of_3985s; /* Has 3 controllers */ + chan_num = number_of_3985s; /* Has 3 controllers */ + flags |= MULTI_CHANNEL; number_of_3985s++; if (number_of_3985s == 3) { number_of_3985s = 0; + shared_scb_data = NULL; } break; @@ -4850,39 +6037,165 @@ aic7xxx_detect(Scsi_Host_Template *template) PCI_INTERRUPT_LINE, &irq); error += pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &mbase); + error += pcibios_read_config_dword(pci_bus, pci_device_fn, + DEVCONFIG, &devconfig); + error += pcibios_read_config_dword(pci_bus, pci_device_fn, + CLASS_PROGIF_REVID, &class_revid); + + printk("aic7xxx: <%s> at PCI %d\n", + board_names[chip_type], PCI_SLOT(pci_device_fn)); /* - * The first bit of PCI_BASE_ADDRESS_0 is always set, so + * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so * we mask it off. */ iobase &= PCI_BASE_ADDRESS_IO_MASK; + p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags, + shared_scb_data); + + if (p == NULL) + { + printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); + continue; + } + + /* Remember to set the channel number, irq, and chip class. */ + p->chan_num = chan_num; + p->irq = irq; + p->chip_class = chip_class; +#ifdef AIC7XXX_PAGE_ENABLE + p->flags |= PAGE_ENABLED; +#endif + p->instance = found; + /* - * Read the PCI burst size and latency timer. + * Remember how the card was setup in case there is no seeprom. */ - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - CSIZE_LATTIME, &csize_lattime); - printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d " - "PCLKS\n", (int) (csize_lattime & CACHESIZE), - (csize_lattime >> 8) & 0x000000ff); + p->scsi_id = inb(p->base + SCSIID) & OID; + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) + { + p->flags |= ULTRA_ENABLED; + ultra_enb = inb(p->base + SXFRCTL1) & FAST20; + } + sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN; - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - CLASS_PROGIF_REVID, &class_revid); - if ((class_revid & DEVREVID) < 3) + aic7xxx_chip_reset(p); + +#ifdef AIC7XXX_USE_EXT_SCBRAM + if (devconfig & RAMPSM) { - printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type], - rev_id[class_revid & DEVREVID]); + printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM " + "access.\n"); + /* + * XXX - Assume 9 bit SRAM and enable parity checking. + */ + devconfig |= EXTSCBPEN; + + /* + * XXX - Assume fast SRAM and only enable 2 cycle access if we + * are sharing the SRAM across multiple adapters (398x). + */ + if ((devconfig & MPORTMODE) == 0) + { + devconfig |= EXTSCBTIME; + } + devconfig &= ~SCBRAMSEL; + pcibios_write_config_dword(pci_bus, pci_device_fn, + DEVCONFIG, devconfig); } +#endif - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - DEVCONFIG, &devconfig); - if (error) + if ((p->flags & USE_DEFAULTS) == 0) + { + load_seeprom(p, &sxfrctl1); + } + + /* + * Take the LED out of diagnostic mode + */ + sblkctl = inb(p->base + SBLKCTL); + outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL); + + /* + * We don't know where this is set in the SEEPROM or by the + * BIOS, so we default to 100%. + */ + outb(DFTHRSH_100, p->base + DSPCISTATUS); + + if (p->flags & USE_DEFAULTS) { - panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n", - error); + int j; + /* + * Default setup; should only be used if the adapter does + * not have a SEEPROM. + */ + /* + * Check the target scratch area to see if someone set us + * up already. We are previously set up if the scratch + * area contains something other than all zeroes and ones. + */ + for (j = TARG_SCRATCH; j < 0x60; j++) + { + if (inb(p->base + j) != 0x00) /* Check for all zeroes. */ + break; + } + if (j == TARG_SCRATCH) + { + for (j = TARG_SCRATCH; j < 0x60; j++) + { + if (inb(p->base + 1) != 0xFF) /* Check for all ones. */ + break; + } + } + if ((j != 0x60) && (p->scsi_id != 0)) + { + p->flags &= ~USE_DEFAULTS; + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n"); + } + } + else + { + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: No BIOS found; using default " + "settings.\n"); + } + /* + * Assume only one connector and always turn on + * termination. + */ + sxfrctl1 = STPWEN; + p->scsi_id = 7; + } + outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI, + p->base + SCSICONF); + /* In case we are a wide card. */ + outb(p->scsi_id, p->base + SCSICONF + 1); + if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0)) + { + /* + * If there wasn't a BIOS or the board wasn't in this mode + * to begin with, turn off Ultra. + */ + p->flags &= ~ULTRA_ENABLED; + } } - printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig); + /* + * Print some additional information about the adapter. + */ + printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, " + "IO Mem 0x%x, IRQ %d", + (p->flags & USE_DEFAULTS) ? "dis" : "en", + p->base, p->mbase, p->irq); + if ((class_revid & DEVREVID) < 3) + { + printk(", Revision %c", rev_id[class_revid & DEVREVID]); + } + printk("\n"); /* * I don't think we need to bother with allowing @@ -4891,58 +6204,53 @@ aic7xxx_detect(Scsi_Host_Template *template) */ aic7xxx_spurious_count = 1; - config.base = iobase; - config.mbase = mbase; - config.irq = irq; - config.parity = AIC_ENABLED; - config.low_term = AIC_UNKNOWN; - config.high_term = AIC_UNKNOWN; if (aic7xxx_extended) - config.flags |= EXTENDED_TRANSLATION; -#ifdef AIC7XXX_SHARE_SCBs - if (devconfig & RAMPSM) -#else - if ((devconfig & RAMPSM) && (config.type != AIC_7873) && - (config.type != AIC_7883)) -#endif + p->flags |= EXTENDED_TRANSLATION; + + /* + * Put our termination setting into sxfrctl1 now that the + * generic initialization is complete. + */ + sxfrctl1 |= inb(p->base + SXFRCTL1); + outb(sxfrctl1, p->base + SXFRCTL1); + + if (aic7xxx_register(template, p) == 0) + { + aic7xxx_free(p); + } + else { + found = found + 1; + +#ifdef AIC7XXX_USE_EXT_SCBRAM /* - * External SRAM present. The probe will walk the SCBs to see - * how much SRAM we have and set the number of SCBs accordingly. - * We have to turn off SCBRAMSEL to access the external SCB - * SRAM. + * Set the shared SCB data once we've successfully probed a + * 398x adapter. * - * It seems that early versions of the aic7870 didn't use these - * bits, hence the hack for the 3940 above. I would guess that - * recent 3940s using later aic7870 or aic7880 chips do actually - * set RAMPSM. - * - * The documentation isn't clear, but it sounds like the value - * written to devconfig must not have RAMPSM set. The second - * sixteen bits of the register are R/O anyway, so it shouldn't - * affect RAMPSM either way. + * Note that we can only do this if the use of external + * SCB RAM is enabled. */ - printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM " - "access.\n"); - devconfig &= ~(RAMPSM | SCBRAMSEL); - pcibios_write_config_dword(pci_bus, pci_device_fn, - DEVCONFIG, devconfig); + if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883)) + { + if (shared_scb_data == NULL) + { + shared_scb_data = p->scb_data; + } + } +#endif } - found += aic7xxx_register(template, &config); + index++; /* * Disable spurious interrupts. */ - aic7xxx_spurious_count = 0; - - index++; + aic7xxx_spurious_count = 0; } /* Found an Adaptec PCI device. */ } } } #endif CONFIG_PCI - template->name = aic7xxx_info(NULL); return (found); } @@ -4958,45 +6266,45 @@ static void aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, struct aic7xxx_scb *scb) { - unsigned int addr; /* must be 32 bits */ unsigned short mask; + struct aic7xxx_hwscb *hscb; mask = (0x01 << TARGET_INDEX(cmd)); + hscb = scb->hscb; + /* * Setup the control byte if we need negotiation and have not * already requested it. */ -#ifdef AIC7XXX_TAGGED_QUEUEING - if (cmd->device->tagged_queue) + if (p->discenable & mask) { - cmd->tag = scb->tag; - cmd->device->current_tag = scb->tag; - scb->control |= TAG_ENB; - p->device_status[TARGET_INDEX(cmd)].commands_sent++; - if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200) - { - scb->control |= 0x02; - p->device_status[TARGET_INDEX(cmd)].commands_sent = 0; - } -#if 0 - if (p->orderedtag & mask) + hscb->control |= DISCENB; +#ifdef AIC7XXX_TAGGED_QUEUEING + if (cmd->device->tagged_queue) { - scb->control |= 0x02; - p->orderedtag = p->orderedtag & ~mask; + cmd->tag = hscb->tag; + p->device_status[TARGET_INDEX(cmd)].commands_sent++; + if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75) + { + hscb->control |= MSG_SIMPLE_Q_TAG; + } + else + { + hscb->control |= MSG_ORDERED_Q_TAG; + p->device_status[TARGET_INDEX(cmd)].commands_sent = 0; + } } -#endif - } -#endif - if (p->discenable & mask) - { - scb->control |= DISCENB; +#endif /* Tagged queueing */ } + if ((p->needwdtr & mask) && !(p->wdtr_pending & mask)) { p->wdtr_pending |= mask; - scb->control |= NEEDWDTR; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_WDTR; #if 0 - printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target); + printk("scsi%d: Sending WDTR request to target %d.\n", + p->host_no, cmd->target); #endif } else @@ -5004,19 +6312,20 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, if ((p->needsdtr & mask) && !(p->sdtr_pending & mask)) { p->sdtr_pending |= mask; - scb->control |= NEEDSDTR; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_SDTR; #if 0 - printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target); + printk("scsi%d: Sending SDTR request to target %d.\n", + p->host_no, cmd->target); #endif } } - #if 0 printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) " "mask(0x%x).\n", cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask); #endif - scb->target_channel_lun = ((cmd->target << 4) & 0xF0) | + hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) | ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07); /* @@ -5030,9 +6339,8 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, * XXX - this relies on the host data being stored in a * little-endian format. */ - addr = VIRT_TO_BUS(cmd->cmnd); - scb->SCSI_cmd_length = cmd->cmd_len; - memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); + hscb->SCSI_cmd_length = cmd->cmd_len; + hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd); if (cmd->use_sg) { @@ -5052,15 +6360,16 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address); scb->sg_list[i].length = (unsigned int) sg[i].length; } - scb->SG_segment_count = cmd->use_sg; - addr = VIRT_TO_BUS(scb->sg_list); - memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); - memcpy(scb->data_pointer, &(scb->sg_list[0].address), - sizeof(scb->data_pointer)); - scb->data_count = scb->sg_list[0].length; + hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list); + hscb->SG_segment_count = cmd->use_sg; + scb->sg_count = hscb->SG_segment_count; + + /* Copy the first SG into the data pointer area. */ + hscb->data_pointer = scb->sg_list[0].address; + hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24); #if 0 printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n", - cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count); + cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count); #endif } else @@ -5069,28 +6378,23 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n", (unsigned long) cmd->request_buffer, cmd->request_bufflen); #endif - if (cmd->request_bufflen == 0) + if (cmd->request_bufflen) { - /* - * In case the higher level SCSI code ever tries to send a zero - * length command, ensure the SCB indicates no data. The driver - * will interpret a zero length command as a Bus Device Reset. - */ - scb->SG_segment_count = 0; - memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); - memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); - scb->data_count = 0; + hscb->SG_segment_count = 1; + scb->sg_count = 1; + scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer); + scb->sg_list[0].length = cmd->request_bufflen; + hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]); + hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24); + hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer); } else { - scb->SG_segment_count = 1; - scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer); - scb->sg_list[0].length = cmd->request_bufflen; - addr = VIRT_TO_BUS(&scb->sg_list[0]); - memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sg_list[0].length; - addr = VIRT_TO_BUS(cmd->request_buffer); - memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer)); + hscb->SG_segment_count = 0; + scb->sg_count = 0; + hscb->SG_list_pointer = 0; + hscb->data_pointer = 0; + hscb->data_count = SCB_LIST_NULL << 24; } } } @@ -5108,7 +6412,6 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) long processor_flags; struct aic7xxx_host *p; struct aic7xxx_scb *scb; - u_char curscb, intstat; p = (struct aic7xxx_host *) cmd->host->hostdata; if (p->host != cmd->host) @@ -5140,34 +6443,21 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) cmd->lun & 0x07); #endif - /* - * This is a critical section, since we don't want the interrupt - * routine mucking with the host data or the card. For this reason - * it is nice to know that this function can only be called in one - * of two ways from scsi.c First, as part of a routine queue command, - * in which case, the irq for our card is disabled before this - * function is called. This doesn't help us if there is more than - * one card using more than one IRQ in our system, therefore, we - * should disable all interrupts on these grounds alone. Second, - * this can be called as part of the scsi_done routine, in which case - * we are in the aic7xxx_isr routine already and interrupts are - * disabled, therefore we should saveflags first, then disable the - * interrupts, do our work, then restore the CPU flags. If it weren't - * for the possibility of more than one card using more than one IRQ - * in our system, we wouldn't have to touch the interrupt flags at all. - */ - save_flags(processor_flags); - cli(); - + if (p->device_status[TARGET_INDEX(cmd)].active_cmds + > cmd->device->queue_depth) + { + printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n", + p->host_no, cmd->target, cmd->channel); + } scb = aic7xxx_allocate_scb(p); if (scb == NULL) { - panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n"); + panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n"); } else { scb->cmd = cmd; - aic7xxx_position(cmd) = scb->tag; + aic7xxx_position(cmd) = scb->hscb->tag; #if 0 debug_scb(scb); #endif; @@ -5179,14 +6469,14 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) aic7xxx_buildscb(p, cmd, scb); #if 0 - if (scb != (p->scb_array[scb->position])) + if (scb != (p->scb_data->scb_array[scb->hscb->tag])) { printk("aic7xxx: (queue) Address of SCB by position does not match SCB " "address.\n"); } printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n", - scb->position, (unsigned int) scb->cmd, - scb->state, (unsigned int) p->free_scb); + scb->hscb->tag, (unsigned int) scb->cmd, + scb->flags, (unsigned int) p->free_scb); #endif /* @@ -5201,70 +6491,28 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) cmd->host_scribble = NULL; memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); - if (scb->position != SCB_LIST_NULL) - { - /* We've got a valid slot, yeah! */ - if (p->flags & IN_ISR) - { - scbq_insert_tail(&p->assigned_scbs, scb); - scb->state |= SCB_ASSIGNEDQ; - } - else - { - /* - * Pause the sequencer so we can play with its registers - - * wait for it to acknowledge the pause. - * - * XXX - should the interrupts be left on while doing this? - */ - PAUSE_SEQUENCER(p); - intstat = inb(INTSTAT + p->base); - - /* - * Save the SCB pointer and put our own pointer in - this - * selects one of the four banks of SCB registers. Load - * the SCB, then write its pointer into the queue in FIFO - * and restore the saved SCB pointer. - */ - curscb = inb(SCBPTR + p->base); - outb(scb->position, SCBPTR + p->base); - aic7xxx_putscb(p, scb); - outb(curscb, SCBPTR + p->base); - outb(scb->position, QINFIFO + p->base); - scb->state |= SCB_ACTIVE; + scb->flags |= SCB_ACTIVE | SCB_WAITINGQ; - /* - * Guard against unpausing the sequencer if there is an interrupt - * waiting to happen. - */ - if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) - { - UNPAUSE_SEQUENCER(p); - } - } - } - else + save_flags(processor_flags); + cli(); + scbq_insert_tail(&p->waiting_scbs, scb); + if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0) { - scb->state |= SCB_WAITINGQ; - scbq_insert_tail(&p->waiting_scbs, scb); - if (!(p->flags & IN_ISR)) - { - aic7xxx_run_waiting_queues(p); - } + aic7xxx_run_waiting_queues(p); } + restore_flags(processor_flags); #if 0 printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n", - (long) cmd, (long) scb->cmd, scb->position); + (long) cmd, (long) scb->cmd, scb->hscb->tag); #endif; - restore_flags(processor_flags); } return (0); } /*+F************************************************************************* * Function: - * aic7xxx_abort_reset + * aic7xxx_bus_device_reset * * Description: * Abort or reset the current SCSI command(s). If the scb has not @@ -5276,204 +6524,257 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) static int aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { - struct aic7xxx_scb *scb; + struct aic7xxx_scb *scb; + struct aic7xxx_hwscb *hscb; unsigned char bus_state; - int base, result = -1; + int result = -1; char channel; - scb = (p->scb_array[aic7xxx_position(cmd)]); - base = p->base; + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); + hscb = scb->hscb; + + /* + * Ensure that the card doesn't do anything behind our back. + * Also make sure that we didn't just miss an interrupt that + * could affect this abort/reset. + */ + pause_sequencer(p); + while (inb(p->base + INTSTAT) & INT_PEND); + { + aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL); + pause_sequencer(p); + } + if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0)) + { + result = SCSI_RESET_NOT_RUNNING; + unpause_sequencer(p, /* unpause_always */ TRUE); + return(result); + } - channel = scb->target_channel_lun & SELBUSB ? 'B': 'A'; - if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS)) + + printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ", + p->host_no, TC_OF_SCB(scb), scb->flags); + bus_state = inb(p->base + LASTPHASE); + + switch (bus_state) { + case P_DATAOUT: + printk("Data-Out phase, "); + break; + case P_DATAIN: + printk("Data-In phase, "); + break; + case P_COMMAND: + printk("Command phase, "); + break; + case P_MESGOUT: + printk("Message-Out phase, "); + break; + case P_STATUS: + printk("Status phase, "); + break; + case P_MESGIN: + printk("Message-In phase, "); + break; + default: + /* + * We're not in a valid phase, so assume we're idle. + */ + printk("while idle, LASTPHASE = 0x%x, ", bus_state); + break; + } + printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n", + inb(p->base + SCSISIGI), + inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8), + inb(p->base + SSTAT0), inb(p->base + SSTAT1)); - if (scb->state & SCB_IN_PROGRESS) + channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A'; + /* + * Determine our course of action. + */ + if (scb->flags & SCB_ABORT) + { + /* + * Been down this road before; do a full bus reset. + */ + scb->flags |= SCB_RECOVERY_SCB; + unpause_sequencer(p, /* unpause_always */ TRUE); + result = -1; + } +#if 0 + else if (hscb->control & TAG_ENB) { /* - * Ensure that the card doesn't do anything - * behind our back. + * We could be starving this command; try sending and ordered tag + * command to the target we come from. */ - PAUSE_SEQUENCER(p); + scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB; + p->orderedtag = p->orderedtag | 0xFF; + result = SCSI_RESET_PENDING; + unpause_sequencer(p, /* unpause_always */ TRUE); + printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n", + p->host_no); + } +#endif + else + { + unsigned char active_scb_index, saved_scbptr; + struct aic7xxx_scb *active_scb; - printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state); - bus_state = inb(LASTPHASE + p->base); + /* + * Send an Abort Message: + * The target that is holding up the bus may not be the same as + * the one that triggered this timeout (different commands have + * different timeout lengths). Our strategy here is to queue an + * abort message to the timed out target if it is disconnected. + * Otherwise, if we have an active target we stuff the message buffer + * with an abort message and assert ATN in the hopes that the target + * will let go of the bus and go to the mesgout phase. If this + * fails, we'll get another timeout a few seconds later which will + * attempt a bus reset. + */ + saved_scbptr = inb(p->base + SCBPTR); + active_scb_index = inb(p->base + SCB_TAG); + active_scb = p->scb_data->scb_array[active_scb_index]; - switch (bus_state) + if (bus_state != P_BUSFREE) + { + if (active_scb_index >= p->scb_data->numscbs) { - case P_DATAOUT: - printk("Data-Out phase, "); - break; - case P_DATAIN: - printk("Data-In phase, "); - break; - case P_COMMAND: - printk("Command phase, "); - break; - case P_MESGOUT: - printk("Message-Out phase, "); - break; - case P_STATUS: - printk("Status phase, "); - break; - case P_MESGIN: - printk("Message-In phase, "); - break; - default: - printk("while idle, LASTPHASE = 0x%x, ", bus_state); - /* - * We're not in a valid phase, so assume we're idle. - */ - bus_state = 0; - break; + /* + * Perform a bus reset. + * + * XXX - We want to queue an abort for the timedout SCB + * instead. + */ + result = -1; + printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, " + "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags); } - printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI)); - - /* - * First, determine if we want to do a bus reset or simply a bus device - * reset. If this is the first time that a transaction has timed out - * and the SCB is not paged out, just schedule a bus device reset. - * Otherwise, we reset the bus and abort all pending I/Os on that bus. - */ - if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT))) + else { -#if 0 - if (scb->control & TAG_ENB) - { + /* Send the abort message to the active SCB. */ + outb(1, p->base + MSG_LEN); + if (active_scb->hscb->control & TAG_ENB) + { + outb(MSG_ABORT_TAG, p->base + MSG_OUT); + } + else + { + outb(MSG_ABORT, p->base + MSG_OUT); + } + outb(bus_state | ATNO, p->base + SCSISIGO); + printk(KERN_WARNING "scsi%d: abort message in message buffer\n", + p->host_no); + active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB; + if (active_scb != scb) + { /* - * We could be starving this command; try sending and ordered tag - * command to the target we come from. + * XXX - We would like to increment the timeout on scb, but + * access to that routine is denied because it is hidden + * in scsi.c. If we were able to do this, it would give + * scb a new lease on life. */ - scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG; - p->orderedtag = p->orderedtag | 0xFF; result = SCSI_RESET_PENDING; - UNPAUSE_SEQUENCER(p); - printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n"); - } -#endif - unsigned char active_scb, control; - struct aic7xxx_scb *active_scbp; + aic7xxx_error(active_scb->cmd) = DID_RESET; + } + else + { + aic7xxx_error(scb->cmd) = DID_RESET; + result = SCSI_RESET_PENDING; + } + unpause_sequencer(p, /* unpause_always */ TRUE); + } + } + else + { + unsigned char hscb_index, linked_next; + int disconnected; - /* - * Send a Bus Device Reset Message: - * The target we select to send the message to may be entirely - * different than the target pointed to by the scb that timed - * out. If the command is in the QINFIFO or the waiting for - * selection list, its not tying up the bus and isn't responsible - * for the delay so we pick off the active command which should - * be the SCB selected by SCBPTR. If its disconnected or active, - * we device reset the target scbp points to. Although it may - * be that this target is not responsible for the delay, it may - * may also be that we're timing out on a command that just takes - * too much time, so we try the bus device reset there first. - */ - active_scb = inb(SCBPTR + base); - active_scbp = (p->scb_array[inb(SCB_TAG + base)]); - control = inb(SCB_CONTROL + base); + disconnected = FALSE; + hscb_index = aic7xxx_find_scb(p, scb); + if (hscb_index == SCB_LIST_NULL) + { + disconnected = TRUE; + linked_next = (scb->hscb->data_count >> 24) & 0xFF; + } + else + { + outb(hscb_index, p->base + SCBPTR); + if (inb(p->base + SCB_CONTROL) & DISCONNECTED) + { + disconnected = TRUE; + } + linked_next = inb(p->base + SCB_LINKED_NEXT); + } + if (disconnected) + { + /* + * Simply set the ABORT_SCB control bit and preserve the + * linked next pointer. + */ + scb->hscb->control |= ABORT_SCB | MK_MESSAGE; + scb->hscb->data_count &= ~0xFF000000; + scb->hscb->data_count |= linked_next << 24; + if ((p->flags & PAGE_ENABLED) == 0) + { + scb->hscb->control &= ~DISCONNECTED; + } + scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB; + if (hscb_index != SCB_LIST_NULL) + { + unsigned char scb_control; - /* - * Test to see if scbp is disconnected - */ - outb(scb->position, SCBPTR + base); - if (inb(SCB_CONTROL + base) & DISCONNECTED) - { -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort_scb) scb %d is disconnected; " - "bus device reset message queued.\n", scb->position); -#endif - if (p->flags & PAGE_ENABLED) - { - /* Pull this SCB out of the disconnected list. */ - u_char prev = inb(SCB_PREV + base); - u_char next = inb(SCB_NEXT + base); - if (prev == SCB_LIST_NULL) - { - /* Head of list */ - outb(next, DISCONNECTED_SCBH + base); - } - else - { - outb(prev, SCBPTR + base); - outb(next, SCB_NEXT + base); - if (next != SCB_LIST_NULL) - { - outb(next, SCBPTR + base); - outb(prev, SCB_PREV + base); - } - outb(scb->position, SCBPTR + base); - } - } - scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED); - scb->control = scb->control & DISCENB; - scb->SCSI_cmd_length = 0; - scb->SG_segment_count = 0; - memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); - memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); - scb->data_count = 0; - aic7xxx_putscb(p, scb); - aic7xxx_add_waiting_scb(base, scb); - outb(active_scb, SCBPTR + base); - result = SCSI_RESET_PENDING; - UNPAUSE_SEQUENCER(p); - } - else - { - /* - * Is the active SCB really active? - */ - if ((active_scbp->state & SCB_ACTIVE) && bus_state) - { - /* - * Load the message buffer and assert attention. - */ - active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED); - outb(1, MSG_LEN + base); - outb(MSG_BUS_DEVICE_RESET, MSG0 + base); - outb(bus_state | ATNO, SCSISIGO + base); -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort_scb) asserted ATN - " - "bus device reset in message buffer.\n"); -#endif - if (active_scbp != scb) - { - /* - * XXX - We would like to increment the timeout on scb, but - * access to that routine is denied because it is hidden - * in scsi.c. If we were able to do this, it would give - * scb a new lease on life. - */ - ; - } - aic7xxx_error(scb->cmd) = DID_RESET; - /* - * Restore the active SCB and unpause the sequencer. - */ - outb(active_scb, SCBPTR + base); - if (active_scbp != scb) - { - /* - * The mid-level SCSI code requested us to reset a command - * different from the one that we actually reset. Return - * a "not running" indication and hope that the SCSI code - * will Do the Right Thing (tm). - */ - result = SCSI_RESET_NOT_RUNNING; - } - else - { - result = SCSI_RESET_PENDING; - } - UNPAUSE_SEQUENCER(p); - } - } + scb_control = inb(p->base + SCB_CONTROL); + outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL); + } + /* + * Actually requeue this SCB in case we can select the + * device before it reconnects. If the transaction we + * want to abort is not tagged, unbusy it first so that + * we don't get held back from sending the command. + */ + if ((scb->hscb->control & TAG_ENB) == 0) + { + unsigned char target; + int lun; + + target = scb->cmd->target; + lun = scb->cmd->lun; + aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL, + 0, /* requeue */ TRUE); + } + printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n", + p->host_no, TC_OF_SCB(scb)); + scbq_insert_head(&p->waiting_scbs, scb); + scb->flags |= SCB_WAITINGQ; + outb(saved_scbptr, p->base + SCBPTR); + if ((p->flags & IN_ISR) == 0) + { + /* + * Processing the waiting queue may unpause us. + */ + aic7xxx_run_waiting_queues(p); + /* + * If we are using AAP, aic7xxx_run_waiting_queues() will not + * unpause us, so ensure we are unpaused. + */ + unpause_sequencer(p, /*unpause_always*/ FALSE); + } + else + { + unpause_sequencer(p, /*unpause_always*/ TRUE); + } + result = SCSI_RESET_PENDING; + } + else + { + scb->flags |= SCB_RECOVERY_SCB; + unpause_sequencer(p, /* unpause_always */ TRUE); + result = -1; } } } - /* Make sure the sequencer is unpaused upon return. */ - if (result == -1) - { - UNPAUSE_SEQUENCER(p); - } return (result); } @@ -5491,16 +6792,48 @@ aic7xxx_abort(Scsi_Cmnd *cmd) struct aic7xxx_scb *scb = NULL; struct aic7xxx_host *p; int base, result; + unsigned long processor_flags; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = (p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); base = p->base; + save_flags(processor_flags); + cli(); + #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n", - scb->position, TCL_OF_SCB(scb)); + if (scb != NULL) + { + printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags); + } + else + { + printk("aic7xxx: Abort called with no SCB for cmd.\n"); + } #endif + if (p->flags & IN_TIMEOUT) + { + /* + * We've already started a recovery operation. + */ + if ((scb->flags & SCB_RECOVERY_SCB) == 0) + { + restore_flags(processor_flags); + return (SCSI_ABORT_PENDING); + } + else + { + /* + * This is the second time we've tried to abort the recovery + * SCB. We want the mid-level SCSI code to call the reset + * function to reset the SCSI bus. + */ + restore_flags(processor_flags); + return (SCSI_ABORT_NOT_RUNNING); + } + } if (cmd->serial_number != cmd->serial_number_at_timeout) { result = SCSI_ABORT_NOT_RUNNING; @@ -5509,14 +6842,34 @@ aic7xxx_abort(Scsi_Cmnd *cmd) { result = SCSI_ABORT_NOT_RUNNING; } - else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS))) + else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE))) { result = SCSI_ABORT_NOT_RUNNING; } else { - result = SCSI_ABORT_SNOOZE; + /* + * XXX - Check use of IN_TIMEOUT to see if we're Doing the + * Right Thing with it. + */ + p->flags |= IN_TIMEOUT; + result = aic7xxx_bus_device_reset(p, scb->cmd); + switch (result) + { + case SCSI_RESET_NOT_RUNNING: + p->flags &= ~IN_TIMEOUT; + result = SCSI_ABORT_NOT_RUNNING; + break; + case SCSI_RESET_PENDING: + result = SCSI_ABORT_PENDING; + break; + default: + p->flags &= ~IN_TIMEOUT; + result = SCSI_ABORT_SNOOZE; + break; + } } + restore_flags(processor_flags); return (result); } @@ -5536,18 +6889,27 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) { struct aic7xxx_scb *scb = NULL; struct aic7xxx_host *p; - int base, found, tindex, min_target, max_target, result = -1; + int base, found, tindex, min_target, max_target; + int result = -1; char channel = 'A'; unsigned long processor_flags; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = (p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); base = p->base; channel = cmd->channel ? 'B': 'A'; tindex = (cmd->channel << 4) | cmd->target; -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel); +#ifdef 0 /* AIC7XXX_DEBUG_ABORT */ + if (scb != NULL) + { + printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags); + } + else + { + printk("aic7xxx: Reset called with no SCB for cmd.\n"); + } #endif /* @@ -5562,34 +6924,45 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) if (scb->cmd != cmd) scb = NULL; - if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) - && (scb != NULL)) + if (p->flags & IN_TIMEOUT) { /* - * Attempt a bus device reset if commands have completed successfully - * since the last bus device reset, or it has been less than 100ms - * since the last reset. + * We've already started a recovery operation. */ - if ((p->flags & DEVICE_SUCCESS) || - ((jiffies - p->device_status[tindex].last_reset) < HZ/10)) + if ((scb->flags & SCB_RECOVERY_SCB) == 0) { - if (cmd->serial_number != cmd->serial_number_at_timeout) - { - result = SCSI_RESET_NOT_RUNNING; - } - else + restore_flags(processor_flags); + return (SCSI_RESET_PENDING); + } + } + else + { + if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) + && (scb != NULL)) + { + /* + * Attempt a bus device reset if commands have completed successfully + * since the last bus device reset, or it has been less than 100ms + * since the last reset. + */ + if ((p->flags & DEVICE_SUCCESS) || + ((jiffies - p->device_status[tindex].last_reset) < HZ/10)) { - if (scb == NULL) + if (cmd->serial_number != cmd->serial_number_at_timeout) + { + result = SCSI_RESET_NOT_RUNNING; + } + else if (scb == NULL) { result = SCSI_RESET_NOT_RUNNING; } else if (flags & SCSI_RESET_ASYNCHRONOUS) { - if (scb->state & SCB_ABORTED) + if (scb->flags & SCB_ABORTED) { result = SCSI_RESET_PENDING; } - else if (!(scb->state & SCB_IN_PROGRESS)) + else if (!(scb->flags & SCB_ACTIVE)) { result = SCSI_RESET_NOT_RUNNING; } @@ -5600,20 +6973,23 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) if ((flags & SCSI_RESET_SYNCHRONOUS) && (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)) { - scb->state |= SCB_ABORTED; + scb->flags |= SCB_ABORTED; result = SCSI_RESET_PENDING; } else { + p->flags |= IN_TIMEOUT; result = aic7xxx_bus_device_reset(p, cmd); if (result == 0) + { + p->flags &= ~IN_TIMEOUT; result = SCSI_RESET_PENDING; + } } - } + } } } } - if (result == -1) { /* @@ -5626,11 +7002,11 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) { result = SCSI_RESET_NOT_RUNNING; } - else if (!(scb->state & SCB_IN_PROGRESS)) + else if (!(scb->flags & SCB_ACTIVE)) { result = SCSI_RESET_NOT_RUNNING; } - else if ((scb->state & SCB_ABORTED) && + else if ((scb->flags & SCB_ABORTED) && (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))) { result = SCSI_RESET_PENDING; @@ -5642,8 +7018,9 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) /* * The reset channel function assumes that the sequencer is paused. */ - PAUSE_SEQUENCER(p); + pause_sequencer(p); found = aic7xxx_reset_channel(p, channel, TRUE); + p->flags = p->flags & ~IN_TIMEOUT; /* * If this is a synchronous reset and there is no SCB for this @@ -5689,8 +7066,10 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) } result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET; + p->flags &= ~IN_TIMEOUT; } } + aic7xxx_run_waiting_queues(p); restore_flags(processor_flags); return (result); } diff --git a/drivers/scsi/aic7xxx.h b/drivers/scsi/aic7xxx.h index d4de8fd83487..4f1e76c33199 100644 --- a/drivers/scsi/aic7xxx.h +++ b/drivers/scsi/aic7xxx.h @@ -18,12 +18,12 @@ * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: aic7xxx.h,v 3.2 1996/07/23 03:37:26 deang Exp $ + * $Id: aic7xxx.h,v 3.3 1997/06/12 03:37:26 deang Exp $ *-M*************************************************************************/ #ifndef _aic7xxx_h #define _aic7xxx_h -#define AIC7XXX_H_VERSION "$Revision: 3.2 $" +#define AIC7XXX_H_VERSION "$Revision: 3.3 $" /* * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields @@ -40,7 +40,7 @@ aic7xxx_info, \ NULL, \ aic7xxx_queue, \ - aic7xxx_abort, \ + NULL, \ aic7xxx_reset, \ NULL, \ aic7xxx_biosparam, \ @@ -57,7 +57,6 @@ extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); extern int aic7xxx_biosparam(Disk *, kdev_t, int[]); extern int aic7xxx_detect(Scsi_Host_Template *); extern int aic7xxx_command(Scsi_Cmnd *); -extern int aic7xxx_abort(Scsi_Cmnd *); extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int); extern const char *aic7xxx_info(struct Scsi_Host *); diff --git a/drivers/scsi/aic7xxx.seq b/drivers/scsi/aic7xxx.seq deleted file mode 100644 index 98d4b9545938..000000000000 --- a/drivers/scsi/aic7xxx.seq +++ /dev/null @@ -1,1127 +0,0 @@ -/*+M************************************************************************* - * Adaptec AIC7xxx device driver for Linux and FreeBSD. - * - * Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * - *Modifications/enhancements: - * Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved. - * - * 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. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other - * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org) - * - * This version corresponds to version 1.42 of FreeBSDs aic7xxx.seq. - * - *-M*************************************************************************/ - -VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $" - -#ifdef linux -#include "aic7xxx_reg.h" -#else -#if defined(__NetBSD__) -#include "../../../../dev/ic/aic7xxxreg.h" -#elif defined(__FreeBSD__) -#include "../../dev/aic7xxx/aic7xxx_reg.h" -#endif -#endif - -/* - * We can't just use ACCUM in the sequencer code because it - * must be treated specially by the assembler, and it currently - * looks for the symbol 'A'. This is the only register defined in - * the assembler's symbol space. - */ -A = ACCUM - -/* After starting the selection hardware, we check for reconnecting targets - * as well as for our selection to complete just in case the reselection wins - * bus arbitration. The problem with this is that we must keep track of the - * SCB that we've already pulled from the QINFIFO and started the selection - * on just in case the reselection wins so that we can retry the selection at - * a later time. This problem cannot be resolved by holding a single entry - * in scratch ram since a reconnecting target can request sense and this will - * create yet another SCB waiting for selection. The solution used here is to - * use byte 27 of the SCB as a pseudo-next pointer and to thread a list - * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets, - * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must - * add an entry to this list every time a request sense occurs. The sequencer - * will automatically consume the entries. - */ - -/* - * We assume that the kernel driver may reset us at any time, even in the - * middle of a DMA, so clear DFCNTRL too. - */ -reset: - clr DFCNTRL - clr SCSISIGO /* De-assert BSY */ -/* - * We jump to start after every bus free. - */ -start: - and FLAGS,0x0f /* clear target specific flags */ - mvi SCSISEQ,ENRSELI /* Always allow reselection */ - clr SCSIRATE /* - * We don't know the target we will - * connect to, so default to narrow - * transfers to avoid parity problems. - */ -poll_for_work: - /* - * Are we a twin channel device? - * For fairness, we check the other bus first, - * since we just finished a transaction on the - * current channel. - */ - test FLAGS,TWIN_BUS jz start2 - xor SBLKCTL,SELBUSB /* Toggle to the other bus */ - test SSTAT0,SELDI jnz reselect - xor SBLKCTL,SELBUSB /* Toggle to the original bus */ -start2: - test SSTAT0,SELDI jnz reselect - cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting - mov A, QCNTMASK - test QINCNT,A jz poll_for_work - -/* - * We have at least one queued SCB now and we don't have any - * SCBs in the list of SCBs awaiting selection. Set the SCB - * pointer from the FIFO so we see the right bank of SCB - * registers. - */ - mov SCBPTR,QINFIFO - -/* - * See if there is not already an active SCB for this target. This code - * locks out on a per target basis instead of target/lun. Although this - * is not ideal for devices that have multiple luns active at the same - * time, it is faster than looping through all SCB's looking for active - * commands. It may be beneficial to make findscb a more general procedure - * to see if the added cost of the search is negligible. This code also - * assumes that the kernel driver will clear the active flags on board - * initialization, board reset, and a target SELTO. Tagged commands - * don't set the active bits since you can queue more than one command - * at a time. We do, however, look to see if there are any non-tagged - * I/Os in progress, and requeue the command if there are. Tagged and - * non-tagged commands cannot be mixed to a single target. - */ - -test_busy: - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */ - - test ACTIVE_B,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_B,A - jmp start_scb - -/* Place the currently active SCB back on the queue for later processing */ -requeue: - mov QINFIFO, SCBPTR - jmp poll_for_work - -/* - * Pull the first entry off of the waiting for selection list - * We don't have to "test_busy" because only transactions that - * have passed that test can be in the waiting_scb list. - */ -start_waiting: - mov SCBPTR,WAITING_SCBH - jmp start_scb2 - -test_a: - test ACTIVE_A,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_A,A - -start_scb: - mov SCB_NEXT,WAITING_SCBH - mov WAITING_SCBH, SCBPTR -start_scb2: - and SINDEX,0xf7,SBLKCTL /* Clear the channel select bit */ - and A,0x08,SCB_TCL /* Get new channel bit */ - or SINDEX,A - mov SBLKCTL,SINDEX /* select channel */ - mov SCB_TCL call initialize_scsiid - -/* - * Enable selection phase as an initiator, and do automatic ATN - * after the selection. We do this now so that we can overlap the - * rest of our work to set up this target with the arbitration and - * selection bus phases. - */ -start_selection: - mvi SCSISEQ,0x58 /* ENSELO|ENAUTOATNO|ENRSELI */ - -/* - * As soon as we get a successful selection, the target should go - * into the message out phase since we have ATN asserted. Prepare - * the message to send. - * - * Messages are stored in scratch RAM starting with a length byte - * followed by the message itself. - */ - test SCB_CMDLEN,0xff jnz mk_identify /* 0 Length Command? */ - -/* - * The kernel has sent us an SCB with no command attached. This implies - * that the kernel wants to send a message of some sort to this target, - * so we interrupt the driver, allow it to fill the message buffer, and - * then go back into the arbitration loop - */ - mvi INTSTAT,AWAITING_MSG - jmp wait_for_selection - -mk_identify: - and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ - - and MSG0,0x7,SCB_TCL /* lun */ - or MSG0,A /* or in disconnect privledge */ - or MSG0,MSG_IDENTIFY - mvi MSG_LEN, 1 - - test SCB_CONTROL,0xb0 jz !message /* WDTR, SDTR or TAG?? */ -/* - * Send a tag message if TAG_ENB is set in the SCB control block. - * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. - */ - -mk_tag: - mvi DINDEX, MSG1 - test SCB_CONTROL,TAG_ENB jz mk_tag_done - and DINDIR,0x23,SCB_CONTROL - mov DINDIR,SCB_TAG - - add MSG_LEN,COMP_MSG0,DINDEX /* update message length */ - -mk_tag_done: - - test SCB_CONTROL,0x90 jz !message /* NEEDWDTR|NEEDSDTR */ - mov DINDEX call mk_dtr /* build DTR message if needed */ - -!message: -wait_for_selection: - test SSTAT0,SELDO jnz select - test SSTAT0,SELDI jz wait_for_selection - -/* - * Reselection has been initiated by a target. Make a note that we've been - * reselected, but haven't seen an IDENTIFY message from the target - * yet. - */ -reselect: - clr MSG_LEN /* Don't have anything in the mesg buffer */ - mov SELID call initialize_scsiid - or FLAGS,RESELECTED - jmp select2 - -/* - * After the selection, remove this SCB from the "waiting for selection" - * list. This is achieved by simply moving our "next" pointer into - * WAITING_SCBH. Our next pointer will be set to null the next time this - * SCB is used, so don't bother with it now. - */ -select: - mov WAITING_SCBH,SCB_NEXT - or FLAGS,SELECTED -select2: -/* - * Set CLRCHN here before the target has entered a data transfer mode - - * with synchronous SCSI, if you do it later, you blow away some - * data in the SCSI FIFO that the target has already sent to you. - */ - or SXFRCTL0,CLRCHN -/* - * Initialize SCSIRATE with the appropriate value for this target. - */ - call ndx_dtr - mov SCSIRATE,SINDIR - -/* - * Initialize Ultra mode setting. - */ - mov FUNCTION1,SCSIID - mov A,FUNCTION1 - and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ - test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */ - test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */ - test ULTRA_ENB,A jz set_sxfrctl0 - or SINDEX, ULTRAEN jmp set_sxfrctl0 -ultra_b: - test ULTRA_ENB_B,A jz set_sxfrctl0 - or SINDEX, ULTRAEN - -set_sxfrctl0: - mov SXFRCTL0,SINDEX - - mvi SCSISEQ,ENAUTOATNP /* - * ATN on parity errors - * for "in" phases - */ - mvi CLRSINT1,CLRBUSFREE - mvi CLRSINT0,0x60 /* CLRSELDI|CLRSELDO */ -/* - * Main loop for information transfer phases. If BSY is false, then - * we have a bus free condition, expected or not. Otherwise, wait - * for the target to assert REQ before checking MSG, C/D and I/O - * for the bus phase. - * - */ -ITloop: - test SSTAT1,BUSFREE jnz p_busfree - test SSTAT1,REQINIT jz ITloop - - and A,PHASE_MASK,SCSISIGI - mov LASTPHASE,A - mov SCSISIGO,A - - cmp ALLZEROS,A je p_dataout - cmp A,P_DATAIN je p_datain - cmp A,P_COMMAND je p_command - cmp A,P_MESGOUT je p_mesgout - cmp A,P_STATUS je p_status - cmp A,P_MESGIN je p_mesgin - - mvi INTSTAT,BAD_PHASE /* unknown phase - signal driver */ - jmp ITloop /* Try reading the bus again. */ - -p_dataout: - mvi DMAPARAMS,0x7d /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * DIRECTION|FIFORESET - */ - jmp data_phase_init - -/* - * If we re-enter the data phase after going through another phase, the - * STCNT may have been cleared, so restore it from the residual field. - */ -data_phase_reinit: - mov STCNT0,SCB_RESID_DCNT0 - mov STCNT1,SCB_RESID_DCNT1 - mov STCNT2,SCB_RESID_DCNT2 - jmp data_phase_loop - -p_datain: - mvi DMAPARAMS,0x79 /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * !DIRECTION|FIFORESET - */ -data_phase_init: - call assert - - test FLAGS, DPHASE jnz data_phase_reinit - call sg_scb2ram - or FLAGS, DPHASE /* We have seen a data phase */ - -data_phase_loop: -/* Guard against overruns */ - test SG_COUNT, 0xff jnz data_phase_inbounds -/* - * Turn on 'Bit Bucket' mode, set the transfer count to - * 16meg and let the target run until it changes phase. - * When the transfer completes, notify the host that we - * had an overrun. - */ - or SXFRCTL1,BITBUCKET - mvi STCNT0,0xff - mvi STCNT1,0xff - mvi STCNT2,0xff - -data_phase_inbounds: -/* If we are the last SG block, don't set wideodd. */ - cmp SG_COUNT,0x01 jne data_phase_wideodd - and DMAPARAMS, 0xbf /* Turn off WIDEODD */ -data_phase_wideodd: - mov DMAPARAMS call dma - -/* Go tell the host about any overruns */ - test SXFRCTL1,BITBUCKET jnz data_phase_overrun - -/* Exit if we had an underrun */ - test SSTAT0,SDONE jz data_phase_finish /* underrun STCNT != 0 */ - -/* - * Advance the scatter-gather pointers if needed - */ -sg_advance: - dec SG_COUNT /* one less segment to go */ - - test SG_COUNT, 0xff jz data_phase_finish /* Are we done? */ - - clr A /* add sizeof(struct scatter) */ - add SG_NEXT0,SG_SIZEOF,SG_NEXT0 - adc SG_NEXT1,A,SG_NEXT1 - -/* - * Load a struct scatter and set up the data address and length. - * If the working value of the SG count is nonzero, then - * we need to load a new set of values. - * - * This, like all DMA's, assumes a little-endian host data storage. - */ -sg_load: - clr HCNT2 - clr HCNT1 - mvi HCNT0,SG_SIZEOF - - mov HADDR0,SG_NEXT0 - mov HADDR1,SG_NEXT1 - mov HADDR2,SG_NEXT2 - mov HADDR3,SG_NEXT3 - - or DFCNTRL,0xd /* HDMAEN|DIRECTION|FIFORESET */ - -/* - * Wait for DMA from host memory to data FIFO to complete, then disable - * DMA and wait for it to acknowledge that it's off. - */ -dma_finish: - test DFSTATUS,HDONE jz dma_finish - /* Turn off DMA preserving WIDEODD */ - and DFCNTRL,WIDEODD -dma_finish2: - test DFCNTRL,HDMAENACK jnz dma_finish2 - -/* - * Copy data from FIFO into SCB data pointer and data count. In - * both FreeBSD and Linux, the scatter list entry is 8 bytes. - * - * struct ahc_dma_seg { - * physaddr addr; four bytes, little-endian order - * long len; four bytes, little endian order - * }; - */ - - mov HADDR0,DFDAT - mov HADDR1,DFDAT - mov HADDR2,DFDAT - mov HADDR3,DFDAT - mov HCNT0,DFDAT - mov HCNT1,DFDAT - mov HCNT2,DFDAT - -/* Load STCNT as well. It is a mirror of HCNT */ - mov STCNT0,HCNT0 - mov STCNT1,HCNT1 - mov STCNT2,HCNT2 - test SSTAT1,PHASEMIS jz data_phase_loop - -data_phase_finish: -/* - * After a DMA finishes, save the SG and STCNT residuals back into the SCB - * We use STCNT instead of HCNT, since it's a reflection of how many bytes - * were transferred on the SCSI (as opposed to the host) bus. - */ - mov SCB_RESID_DCNT0,STCNT0 - mov SCB_RESID_DCNT1,STCNT1 - mov SCB_RESID_DCNT2,STCNT2 - mov SCB_RESID_SGCNT, SG_COUNT - jmp ITloop - -data_phase_overrun: -/* - * Turn off BITBUCKET mode and notify the host - */ - and SXFRCTL1,0x7f /* ~BITBUCKET */ - mvi INTSTAT,DATA_OVERRUN - jmp ITloop - -/* - * Command phase. Set up the DMA registers and let 'er rip. - */ -p_command: - call assert - -/* - * Load HADDR and HCNT. - */ - mov HADDR0, SCB_CMDPTR0 - mov HADDR1, SCB_CMDPTR1 - mov HADDR2, SCB_CMDPTR2 - mov HADDR3, SCB_CMDPTR3 - mov HCNT0, SCB_CMDLEN - clr HCNT1 - clr HCNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET - jmp ITloop - -/* - * Status phase. Wait for the data byte to appear, then read it - * and store it into the SCB. - */ -p_status: - mvi SCB_TARGET_STATUS call inb_first - jmp mesgin_done - -/* - * Message out phase. If there is not an active message, but the target - * took us into this phase anyway, build a no-op message and send it. - */ -p_mesgout: - test MSG_LEN, 0xff jnz p_mesgout_start - mvi MSG_NOP call mk_mesg /* build NOP message */ - -p_mesgout_start: -/* - * Set up automatic PIO transfer from MSG0. Bit 3 in - * SXFRCTL0 (SPIOEN) is already on. - */ - mvi SINDEX,MSG0 - mov DINDEX,MSG_LEN - -/* - * When target asks for a byte, drop ATN if it's the last one in - * the message. Otherwise, keep going until the message is exhausted. - * - * Keep an eye out for a phase change, in case the target issues - * a MESSAGE REJECT. - */ -p_mesgout_loop: - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - test SSTAT0,SPIORDY jz p_mesgout_loop - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - cmp DINDEX,1 jne p_mesgout_outb /* last byte? */ - mvi CLRSINT1,CLRATNO /* drop ATN */ -p_mesgout_outb: - dec DINDEX - or CLRSINT0, CLRSPIORDY - mov SCSIDATL,SINDIR - -p_mesgout4: - test DINDEX,0xff jnz p_mesgout_loop - -/* - * If the next bus phase after ATN drops is a message out, it means - * that the target is requesting that the last message(s) be resent. - */ -p_mesgout_snoop: - test SSTAT1,BUSFREE jnz p_mesgout_done - test SSTAT1,REQINIT jz p_mesgout_snoop - - test SSTAT1,PHASEMIS jnz p_mesgout_done - - or SCSISIGO,ATNO /* turn on ATNO */ - - jmp ITloop - -p_mesgout_phasemis: - mvi CLRSINT1,CLRATNO /* Be sure to turn ATNO off */ -p_mesgout_done: - clr MSG_LEN /* no active msg */ - jmp ITloop - -/* - * Message in phase. Bytes are read using Automatic PIO mode. - */ -p_mesgin: - mvi A call inb_first /* read the 1st message byte */ - mov REJBYTE,A /* save it for the driver */ - - test A,MSG_IDENTIFY jnz mesgin_identify - cmp A,MSG_DISCONNECT je mesgin_disconnect - cmp A,MSG_SDPTRS je mesgin_sdptrs - cmp ALLZEROS,A je mesgin_complete - cmp A,MSG_RDPTRS je mesgin_rdptrs - cmp A,MSG_EXTENDED je mesgin_extended - cmp A,MSG_REJECT je mesgin_reject - -rej_mesgin: -/* - * We have no idea what this message in is, and there's no way - * to pass it up to the kernel, so we issue a message reject and - * hope for the best. Since we're now using manual PIO mode to - * read in the message, there should no longer be a race condition - * present when we assert ATN. In any case, rejection should be a - * rare occurrence - signal the driver when it happens. - */ - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,SEND_REJECT /* let driver know */ - - mvi MSG_REJECT call mk_mesg - -mesgin_done: - call inb_last /*ack & turn auto PIO back on*/ - jmp ITloop - - -mesgin_complete: -/* - * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT, - * and trigger a completion interrupt. Check status for non zero return - * and interrupt driver if needed. This allows the driver to interpret - * errors only when they occur instead of always uploading the scb. If - * the status is SCSI_CHECK, the driver will download a new scb requesting - * sense to replace the old one, modify the "waiting for selection" SCB list - * and set RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE the - * sequencer imediately jumps to main loop where it will run down the waiting - * SCB list and process the sense request. If the kernel driver does not - * wish to request sense, it need only clear RETURN_1, and the command is - * allowed to complete. We don't bother to post to the QOUTFIFO in the - * error case since it would require extra work in the kernel driver to - * ensure that the entry was removed before the command complete code tried - * processing it. - * - * First check for residuals - */ - test SCB_RESID_SGCNT,0xff jz check_status -/* - * If we have a residual count, interrupt and tell the host. Other - * alternatives are to pause the sequencer on all command completes (yuck), - * dma the resid directly to the host (slick, we may have space to do it now) - * or have the sequencer pause itself when it encounters a non-zero resid - * (unnecessary pause just to flag the command -yuck-, but takes one instruction - * and since it shouldn't happen that often is good enough for our purposes). - */ -resid: - mvi INTSTAT,RESIDUAL - -check_status: - test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */ - mvi INTSTAT,BAD_STATUS /* let driver know */ - cmp RETURN_1, SEND_SENSE jne status_ok - jmp mesgin_done - -status_ok: -/* First, mark this target as free. */ - test SCB_CONTROL,TAG_ENB jnz test_immediate /* - * Tagged commands - * don't busy the - * target. - */ - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz clear_a - xor ACTIVE_B,A - jmp test_immediate - -clear_a: - xor ACTIVE_A,A - -test_immediate: - test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */ -/* - * Pause the sequencer until the driver gets around to handling the command - * complete. This is so that any action that might require careful timing - * with the completion of this command can occur. - */ - mvi INTSTAT,IMMEDDONE - jmp start -complete: - mov QOUTFIFO,SCB_TAG - mvi INTSTAT,CMDCMPLT - jmp mesgin_done - - -/* - * Is it an extended message? We only support the synchronous and wide data - * transfer request messages, which will probably be in response to - * WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it - - * apparently this can be done after any message in byte, according - * to the SCSI-2 spec. - */ -mesgin_extended: - mvi ARG_1 call inb_next /* extended message length */ - mvi REJBYTE_EXT call inb_next /* extended message code */ - - cmp REJBYTE_EXT,MSG_SDTR je p_mesginSDTR - cmp REJBYTE_EXT,MSG_WDTR je p_mesginWDTR - jmp rej_mesgin - -p_mesginWDTR: - cmp ARG_1,2 jne rej_mesgin /* extended mesg length=2 */ - mvi ARG_1 call inb_next /* Width of bus */ - mvi INTSTAT,WDTR_MSG /* let driver know */ - test RETURN_1,0xff jz mesgin_done /* Do we need to send WDTR? */ - cmp RETURN_1,SEND_REJ je rej_mesgin /* - * Bus width was too large - * Reject it. - */ - -/* We didn't initiate the wide negotiation, so we must respond to the request */ - and RETURN_1,0x7f /* Clear the SEND_WDTR Flag */ - mvi DINDEX,MSG0 - mvi MSG0 call mk_wdtr /* build WDTR message */ - or SCSISIGO,ATNO /* turn on ATNO */ - jmp mesgin_done - -p_mesginSDTR: - cmp ARG_1,3 jne rej_mesgin /* extended mesg length=3 */ - mvi ARG_1 call inb_next /* xfer period */ - mvi A call inb_next /* REQ/ACK offset */ - mvi INTSTAT,SDTR_MSG /* call driver to convert */ - - test RETURN_1,0xff jz mesgin_done /* Do we need to mk_sdtr/rej */ - cmp RETURN_1,SEND_REJ je rej_mesgin /* - * Requested SDTR too small - * Reject it. - */ - clr ARG_1 /* Use the scratch ram rate */ - mvi DINDEX, MSG0 - mvi MSG0 call mk_sdtr - or SCSISIGO,ATNO /* turn on ATNO */ - jmp mesgin_done - -/* - * Is it a disconnect message? Set a flag in the SCB to remind us - * and await the bus going free. - */ -mesgin_disconnect: - or SCB_CONTROL,DISCONNECTED - test FLAGS, PAGESCBS jz mesgin_done -/* - * Link this SCB into the DISCONNECTED list. This list holds the - * candidates for paging out an SCB if one is needed for a new command. - * Modifying the disconnected list is a critical(pause dissabled) section. - */ - mvi SCB_PREV, SCB_LIST_NULL - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - mov SCB_NEXT, DISCONNECTED_SCBH - mov DISCONNECTED_SCBH, SCBPTR - cmp SCB_NEXT,SCB_LIST_NULL je linkdone - mov SCBPTR,SCB_NEXT - mov SCB_PREV,DISCONNECTED_SCBH - mov SCBPTR,DISCONNECTED_SCBH -linkdone: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - jmp mesgin_done - -/* - * Save data pointers message? Copy working values into the SCB, - * usually in preparation for a disconnect. - */ -mesgin_sdptrs: - call sg_ram2scb - jmp mesgin_done - -/* - * Restore pointers message? Data pointers are recopied from the - * SCB anytime we enter a data phase for the first time, so all - * we need to do is clear the DPHASE flag and let the data phase - * code do the rest. - */ -mesgin_rdptrs: - and FLAGS,0xef /* - * !DPHASE we'll reload them - * the next time through - */ - jmp mesgin_done - -/* - * Identify message? For a reconnecting target, this tells us the lun - * that the reconnection is for - find the correct SCB and switch to it, - * clearing the "disconnected" bit so we don't "find" it by accident later. - */ -mesgin_identify: - test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/ - - and A,0x07 /* lun in lower three bits */ - or SAVED_TCL,A,SELID - and SAVED_TCL,0xf7 - and A,SELBUSB,SBLKCTL /* B Channel?? */ - or SAVED_TCL,A - call inb_last /* ACK */ - -/* - * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. - * If we get one, we use the tag returned to switch to find the proper - * SCB. With SCB paging, this requires using findSCB for both tagged - * and non-tagged transactions since the SCB may exist in any slot. - * If we're not using SCB paging, we can use the tag as the direct - * index to the SCB. - */ - mvi ARG_1,SCB_LIST_NULL /* Default to no-tag */ -snoop_tag_loop: - test SSTAT1,BUSFREE jnz use_findSCB - test SSTAT1,REQINIT jz snoop_tag_loop - test SSTAT1,PHASEMIS jnz use_findSCB - mvi A call inb_first - cmp A,MSG_SIMPLE_TAG jne use_findSCB -get_tag: - mvi ARG_1 call inb_next /* tag value */ -/* - * See if the tag is in range. The tag is < SCBCOUNT if we add - * the complement of SCBCOUNT to the incoming tag and there is - * no carry. - */ - mov A,COMP_SCBCOUNT - add SINDEX,A,ARG_1 - jc abort_tag - -/* - * Ensure that the SCB the tag points to is for a SCB transaction - * to the reconnecting target. - */ - test FLAGS, PAGESCBS jz index_by_tag - call inb_last /* Ack Tag */ -use_findSCB: - mov ALLZEROS call findSCB /* Have to search */ -setup_SCB: - and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */ - or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */ - jmp ITloop -index_by_tag: - mov SCBPTR,ARG_1 - mov A,SAVED_TCL - cmp SCB_TCL,A jne abort_tag - test SCB_CONTROL,TAG_ENB jz abort_tag - call inb_last /* Ack Successful tag */ - jmp setup_SCB - -abort_tag: - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,ABORT_TAG /* let driver know */ - mvi MSG_ABORT_TAG call mk_mesg /* ABORT TAG message */ - jmp mesgin_done - -/* - * Message reject? Let the kernel driver handle this. If we have an - * outstanding WDTR or SDTR negotiation, assume that it's a response from - * the target selecting 8bit or asynchronous transfer, otherwise just ignore - * it since we have no clue what it pertains to. - */ -mesgin_reject: - mvi INTSTAT, REJECT_MSG - jmp mesgin_done - -/* - * [ ADD MORE MESSAGE HANDLING HERE ] - */ - -/* - * Bus free phase. It might be useful to interrupt the device - * driver if we aren't expecting this. For now, make sure that - * ATN isn't being asserted and look for a new command. - */ -p_busfree: - mvi CLRSINT1,CLRATNO - clr LASTPHASE - -/* - * if this is an immediate command, perform a pseudo command complete to - * notify the driver. - */ - test SCB_CMDLEN,0xff jz status_ok - jmp start - -/* - * Locking the driver out, build a one-byte message passed in SINDEX - * if there is no active message already. SINDEX is returned intact. - */ -mk_mesg: - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - test MSG_LEN,0xff jz mk_mesg1 /* Should always succeed */ - - /* - * Hmmm. For some reason the mesg buffer is in use. - * Tell the driver. It should look at SINDEX to find - * out what we wanted to use the buffer for and resolve - * the conflict. - */ - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - mvi INTSTAT,MSG_BUFFER_BUSY - -mk_mesg1: - mvi MSG_LEN,1 /* length = 1 */ - mov MSG0,SINDEX /* 1-byte message */ - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -/* - * Functions to read data in Automatic PIO mode. - * - * According to Adaptec's documentation, an ACK is not sent on input from - * the target until SCSIDATL is read from. So we wait until SCSIDATL is - * latched (the usual way), then read the data byte directly off the bus - * using SCSIBUSL. When we have pulled the ATN line, or we just want to - * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI - * spec guarantees that the target will hold the data byte on the bus until - * we send our ACK. - * - * The assumption here is that these are called in a particular sequence, - * and that REQ is already set when inb_first is called. inb_{first,next} - * use the same calling convention as inb. - */ - -inb_next: - or CLRSINT0, CLRSPIORDY - mov NONE,SCSIDATL /*dummy read from latch to ACK*/ -inb_next_wait: - test SSTAT1,PHASEMIS jnz mesgin_phasemis - test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */ -inb_first: - mov DINDEX,SINDEX - test SSTAT1,PHASEMIS jnz mesgin_phasemis - mov DINDIR,SCSIBUSL ret /*read byte directly from bus*/ -inb_last: - mov NONE,SCSIDATL ret /*dummy read from latch to ACK*/ - -mesgin_phasemis: -/* - * We expected to receive another byte, but the target changed phase - */ - mvi INTSTAT, MSGIN_PHASEMIS - jmp ITloop - -/* - * DMA data transfer. HADDR and HCNT must be loaded first, and - * SINDEX should contain the value to load DFCNTRL with - 0x3d for - * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared - * during initialization. - */ -dma: - mov DFCNTRL,SINDEX -dma1: - test SSTAT0,DMADONE jnz dma3 - test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */ - -/* - * We will be "done" DMAing when the transfer count goes to zero, or - * the target changes the phase (in light of this, it makes sense that - * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are - * doing a SCSI->Host transfer, the data FIFO should be flushed auto- - * magically on STCNT=0 or a phase change, so just wait for FIFO empty - * status. - */ -dma3: - test SINDEX,DIRECTION jnz dma5 -dma4: - test DFSTATUS,FIFOEMP jz dma4 - -/* - * Now shut the DMA enables off and make sure that the DMA enables are - * actually off first lest we get an ILLSADDR. - */ -dma5: - /* disable DMA, but maintain WIDEODD */ - and DFCNTRL,WIDEODD -dma6: - test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */ - - ret - -/* - * Common SCSI initialization for selection and reselection. Expects - * the target SCSI ID to be in the upper four bits of SINDEX, and A's - * contents are stomped on return. - */ -initialize_scsiid: - and SINDEX,0xf0 /* Get target ID */ - and A,0x0f,SCSIID - or SINDEX,A - mov SCSIID,SINDEX ret - -/* - * Assert that if we've been reselected, then we've seen an IDENTIFY - * message. - */ -assert: - test FLAGS,RESELECTED jz return /* reselected? */ - test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */ - - mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */ - -/* - * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag - * value in ARG_1. If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged - * SCB. Have the kernel print a warning message if it can't be found, and - * generate an ABORT/ABORT_TAG message to the target. SINDEX should be - * cleared on call. - */ -findSCB: - mov A,SAVED_TCL - mov SCBPTR,SINDEX /* switch to next SCB */ - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */ - test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/ - test SCB_CONTROL,TAG_ENB jnz findTaggedSCB - cmp ARG_1,SCB_LIST_NULL je foundSCB - jmp findSCB1 -findTaggedSCB: - mov A, ARG_1 /* Tag passed in ARG_1 */ - cmp SCB_TAG,A jne findSCB1 /* Found it? */ -foundSCB: - test FLAGS,PAGESCBS jz foundSCB_ret -/* Remove this SCB from the disconnection list */ - cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev - mov SAVED_LINKPTR, SCB_PREV - mov SCBPTR, SCB_NEXT - mov SCB_PREV, SAVED_LINKPTR - mov SCBPTR, SINDEX -unlink_prev: - cmp SCB_PREV,SCB_LIST_NULL je rHead/* At the head of the list */ - mov SAVED_LINKPTR, SCB_NEXT - mov SCBPTR, SCB_PREV - mov SCB_NEXT, SAVED_LINKPTR - mov SCBPTR, SINDEX - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ -rHead: - mov DISCONNECTED_SCBH,SCB_NEXT -foundSCB_ret: - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -findSCB1: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - inc SINDEX - mov A,SCBCOUNT - cmp SINDEX,A jne findSCB - - mvi INTSTAT,NO_MATCH /* not found - signal kernel */ - cmp RETURN_1,SCB_PAGEDIN je return - or SCSISIGO,ATNO /* assert ATNO */ - cmp ARG_1,SCB_LIST_NULL jne find_abort_tag - mvi MSG_ABORT call mk_mesg - jmp ITloop -find_abort_tag: - mvi MSG_ABORT_TAG call mk_mesg - jmp ITloop - -/* - * Make a working copy of the scatter-gather parameters from the SCB. - */ -sg_scb2ram: - mov HADDR0, SCB_DATAPTR0 - mov HADDR1, SCB_DATAPTR1 - mov HADDR2, SCB_DATAPTR2 - mov HADDR3, SCB_DATAPTR3 - mov HCNT0, SCB_DATACNT0 - mov HCNT1, SCB_DATACNT1 - mov HCNT2, SCB_DATACNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mov SG_COUNT,SCB_SGCOUNT - - mov SG_NEXT0, SCB_SGPTR0 - mov SG_NEXT1, SCB_SGPTR1 - mov SG_NEXT2, SCB_SGPTR2 - mov SG_NEXT3, SCB_SGPTR3 ret - -/* - * Copying RAM values back to SCB, for Save Data Pointers message, but - * only if we've actually been into a data phase to change them. This - * protects against bogus data in scratch ram and the residual counts - * since they are only initialized when we go into data_in or data_out. - */ -sg_ram2scb: - test FLAGS, DPHASE jz return - mov SCB_SGCOUNT,SG_COUNT - - mov SCB_SGPTR0,SG_NEXT0 - mov SCB_SGPTR1,SG_NEXT1 - mov SCB_SGPTR2,SG_NEXT2 - mov SCB_SGPTR3,SG_NEXT3 - - mov SCB_DATAPTR0,SHADDR0 - mov SCB_DATAPTR1,SHADDR1 - mov SCB_DATAPTR2,SHADDR2 - mov SCB_DATAPTR3,SHADDR3 - -/* - * Use the residual number since STCNT is corrupted by any message transfer - */ - mov SCB_DATACNT0,SCB_RESID_DCNT0 - mov SCB_DATACNT1,SCB_RESID_DCNT1 - mov SCB_DATACNT2,SCB_RESID_DCNT2 ret - -/* - * Add the array base TARG_SCRATCH to the target offset (the target address - * is in SCSIID), and return the result in SINDEX. The accumulator - * contains the 3->8 decoding of the target ID on return. - */ -ndx_dtr: - shr A,SCSIID,4 - test SBLKCTL,SELBUSB jz ndx_dtr_2 - or A,0x08 /* Channel B entries add 8 */ -ndx_dtr_2: - add SINDEX,TARG_SCRATCH,A ret - -/* - * If we need to negotiate transfer parameters, build the WDTR or SDTR message - * starting at the address passed in SINDEX. DINDEX is modified on return. - * The SCSI-II spec requires that Wide negotiation occur first and you can - * only negotiate one or the other at a time otherwise in the event of a message - * reject, you wouldn't be able to tell which message was the culprit. - */ -mk_dtr: - test SCB_CONTROL,NEEDWDTR jnz mk_wdtr_16bit - mvi ARG_1, MAXOFFSET /* Force an offset of 15 or 8 if WIDE */ - -mk_sdtr: - mvi DINDIR,1 /* extended message */ - mvi DINDIR,3 /* extended message length = 3 */ - mvi DINDIR,1 /* SDTR code */ - call sdtr_to_rate - mov DINDIR,RETURN_1 /* REQ/ACK transfer period */ - cmp ARG_1, MAXOFFSET je mk_sdtr_max_offset - and DINDIR,0x0f,SINDIR /* Sync Offset */ - -mk_sdtr_done: - add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ - -mk_sdtr_max_offset: -/* - * We're initiating sync negotiation, so request the max offset we can (15 or 8) - */ - /* Talking to a WIDE device? */ - test SCSIRATE, WIDEXFER jnz wmax_offset - mvi DINDIR, MAX_OFFSET_8BIT - jmp mk_sdtr_done - -wmax_offset: - mvi DINDIR, MAX_OFFSET_16BIT - jmp mk_sdtr_done - -mk_wdtr_16bit: - mvi ARG_1,BUS_16_BIT -mk_wdtr: - mvi DINDIR,1 /* extended message */ - mvi DINDIR,2 /* extended message length = 2 */ - mvi DINDIR,3 /* WDTR code */ - mov DINDIR,ARG_1 /* bus width */ - - add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ - -sdtr_to_rate: - call ndx_dtr /* index scratch space for target */ - shr A,SINDIR,0x4 - dec SINDEX /* Preserve SINDEX */ - and A,0x7 - clr RETURN_1 -sdtr_to_rate_loop: - test A,0x0f jz sdtr_to_rate_done - add RETURN_1,0x19 - dec A - jmp sdtr_to_rate_loop -sdtr_to_rate_done: - shr RETURN_1,0x2 - add RETURN_1,0x19 - test SXFRCTL0,ULTRAEN jz return - shr RETURN_1,0x1 -return: - ret diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile new file mode 100644 index 000000000000..8c0ebaf081cf --- /dev/null +++ b/drivers/scsi/aic7xxx/Makefile @@ -0,0 +1,77 @@ + +.SUFFIXES: .c .y .o + +PROG= aic7xxx_asm + +CSRCS= aic7xxx_asm.c symbol.c +GENSRCS= gram.c scan.c +GENHDRS= y.tab.h token.h +OBJS= gram.o scan.o symbol.o aic7xxx_asm.o + +SRCS= ${GENSRCS} ${CSRCS} +CLEANFILES= ${GENSRCS} ${GENHDRS} y.output ${OBJS} +LIBS = -L/usr/local/lib -ldb +NOMAN= noman + +LEX = lex +LFLAGS = -t +YACC = yacc +YFLAGS = -d + +${PROG}: $(OBJS) + ${CC} ${CFLAGS} -o ${PROG} ${OBJS} ${LIBS} + + + +y.tab.c: gram.y + ${YACC} ${YFLAGS} ${.IMPSRC} + mv y.tab.c gram.c + +gram.o: gram.c + ${CC} ${CFLAGS} -c gram.c + + +.y.o: + ${YACC} ${YFLAGS} ${.IMPSRC} + ${CC} ${CFLAGS} -c y.tab.c + rm -f y.tab.c + cp y.tab.o $@ + +.y.c: + ${YACC} ${YFLAGS} gram.y + mv y.tab.c gram.c + +.l.c: + ${LEX} ${LFLAGS} scan.l > scan.c + mv lex.yy.c $@ + +.l.o: + ${LEX} ${LFLAGS} scan.l > scan.c + ${CC} ${CFLAGS} -c scan.c +# rm -f lex.yy.c +# mv lex.yy.o $@ + +clean_most: + rm -f ${CLEANFILES} + +clean: clean_most + rm -f ${PROG} +# +# This is how FreeBSD builds the assembler: +# +# bash$ make +# Warning: Object directory not changed from original /home/deischen/Linux/aic7xxx/Mar24/fbsd +# yacc -d gram.y +# mv y.tab.c gram.c +# cc -O2 -m486 -pipe -c gram.c +# lex -t scan.l > scan.c +# cc -O2 -m486 -pipe -c scan.c +# cc -O2 -m486 -pipe -c aic7xxx_asm.c +# cc -O2 -m486 -pipe -c symbol.c +# cc -O2 -m486 -pipe -o aic7xxx_asm gram.o scan.o aic7xxx_asm.o symbol.o -ll +# bash$ cc -O2 -m486 -pipe -o aic7xxx_asm gram.o scan.o aic7xxx_asm.o symbol.o +# bash$ aic7xxx_asm +# aic7xxx_asm: No input file specifiled +# usage: aic7xxx_asm [-nostdinc] [-I-] [-I directory] [-o output_file] +# [-r register_output_file] [-l program_list_file] +# [-O option_name[|options_name2]] input_file diff --git a/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg new file mode 100644 index 000000000000..c0cc71cd66ee --- /dev/null +++ b/drivers/scsi/aic7xxx/aic7xxx.reg @@ -0,0 +1,1113 @@ +/* + * Aic7xxx register and scratch ram definitions. + * + * Copyright (c) 1994, 1995, 1996, 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: aic7xxx.reg,v 1.3 1997/04/24 16:52:16 gibbs Exp $ + */ + +/* + * This file is processed by the aic7xxx_asm utility for use in assembling + * firmware for the aic7xxx family of SCSI host adapters as well as to generate + * a C header file for use in the kernel portion of the Aic7xxx driver. + * + * All page numbers refer to the Adaptec AIC-7770 Data Book available from + * Adaptec's Technical Documents Department 1-800-934-2766 + */ + +/* + * SCSI Sequence Control (p. 3-11). + * Each bit, when set starts a specific SCSI sequence on the bus + */ +register SCSISEQ { + address 0x000 + access_mode RW + bit TEMODE 0x80 + bit ENSELO 0x40 + bit ENSELI 0x20 + bit ENRSELI 0x10 + bit ENAUTOATNO 0x08 + bit ENAUTOATNI 0x04 + bit ENAUTOATNP 0x02 + bit SCSIRSTO 0x01 +} + +/* + * SCSI Transfer Control 0 Register (pp. 3-13). + * Controls the SCSI module data path. + */ +register SXFRCTL0 { + address 0x001 + access_mode RW + bit DFON 0x80 + bit DFPEXP 0x40 + bit FAST20 0x20 + bit CLRSTCNT 0x10 + bit SPIOEN 0x08 + bit SCAMEN 0x04 + bit CLRCHN 0x02 +} + +/* + * SCSI Transfer Control 1 Register (pp. 3-14,15). + * Controls the SCSI module data path. + */ +register SXFRCTL1 { + address 0x002 + access_mode RW + bit BITBUCKET 0x80 + bit SWRAPEN 0x40 + bit ENSPCHK 0x20 + mask STIMESEL 0x18 + bit ENSTIMER 0x04 + bit ACTNEGEN 0x02 + bit STPWEN 0x01 /* Powered Termination */ +} + +/* + * SCSI Control Signal Read Register (p. 3-15). + * Reads the actual state of the SCSI bus pins + */ +register SCSISIGI { + address 0x003 + access_mode RO + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + bit ATNI 0x10 + bit SELI 0x08 + bit BSYI 0x04 + bit REQI 0x02 + bit ACKI 0x01 +/* + * Possible phases in SCSISIGI + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Control Signal Write Register (p. 3-16). + * Writing to this register modifies the control signals on the bus. Only + * those signals that are allowed in the current mode (Initiator/Target) are + * asserted. + */ +register SCSISIGO { + address 0x003 + access_mode WO + bit CDO 0x80 + bit IOO 0x40 + bit MSGO 0x20 + bit ATNO 0x10 + bit SELO 0x08 + bit BSYO 0x04 + bit REQO 0x02 + bit ACKO 0x01 +/* + * Possible phases to write into SCSISIG0 + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Rate Control (p. 3-17). + * Contents of this register determine the Synchronous SCSI data transfer + * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the + * SOFS (3:0) bits disables synchronous data transfers. Any offset value + * greater than 0 enables synchronous transfers. + */ +register SCSIRATE { + address 0x004 + access_mode RW + bit WIDEXFER 0x80 /* Wide transfer control */ + mask SXFR 0x70 /* Sync transfer rate */ + mask SOFS 0x0f /* Sync offset */ +} + +/* + * SCSI ID (p. 3-18). + * Contains the ID of the board and the current target on the + * selected channel. + */ +register SCSIID { + address 0x005 + access_mode RW + mask TID 0xf0 /* Target ID mask */ + mask OID 0x0f /* Our ID mask */ +} + +/* + * SCSI Latched Data (p. 3-19). + * Read/Write latches used to transfer data on the SCSI bus during + * Automatic or Manual PIO mode. SCSIDATH can be used for the + * upper byte of a 16bit wide asynchronouse data phase transfer. + */ +register SCSIDATL { + address 0x006 + access_mode RW +} + +register SCSIDATH { + address 0x007 + access_mode RW +} + +/* + * SCSI Transfer Count (pp. 3-19,20) + * These registers count down the number of bytes transferred + * across the SCSI bus. The counter is decremented only once + * the data has been safely transferred. SDONE in SSTAT0 is + * set when STCNT goes to 0 + */ +register STCNT { + address 0x008 + size 3 + access_mode RW +} + +/* + * Clear SCSI Interrupt 0 (p. 3-20) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0. + */ +register CLRSINT0 { + address 0x00b + access_mode WO + bit CLRSELDO 0x40 + bit CLRSELDI 0x20 + bit CLRSELINGO 0x10 + bit CLRSWRAP 0x08 + bit CLRSPIORDY 0x02 +} + +/* + * SCSI Status 0 (p. 3-21) + * Contains one set of SCSI Interrupt codes + * These are most likely of interest to the sequencer + */ +register SSTAT0 { + address 0x00b + access_mode RO + bit TARGET 0x80 /* Board acting as target */ + bit SELDO 0x40 /* Selection Done */ + bit SELDI 0x20 /* Board has been selected */ + bit SELINGO 0x10 /* Selection In Progress */ + bit SWRAP 0x08 /* 24bit counter wrap */ + bit SDONE 0x04 /* STCNT = 0x000000 */ + bit SPIORDY 0x02 /* SCSI PIO Ready */ + bit DMADONE 0x01 /* DMA transfer completed */ +} + +/* + * Clear SCSI Interrupt 1 (p. 3-23) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1. + */ +register CLRSINT1 { + address 0x00c + access_mode WO + bit CLRSELTIMEO 0x80 + bit CLRATNO 0x40 + bit CLRSCSIRSTI 0x20 + bit CLRBUSFREE 0x08 + bit CLRSCSIPERR 0x04 + bit CLRPHASECHG 0x02 + bit CLRREQINIT 0x01 +} + +/* + * SCSI Status 1 (p. 3-24) + */ +register SSTAT1 { + address 0x00c + access_mode RO + bit SELTO 0x80 + bit ATNTARG 0x40 + bit SCSIRSTI 0x20 + bit PHASEMIS 0x10 + bit BUSFREE 0x08 + bit SCSIPERR 0x04 + bit PHASECHG 0x02 + bit REQINIT 0x01 +} + +/* + * SCSI Status 2 (pp. 3-25,26) + */ +register SSTAT2 { + address 0x00d + access_mode RO + bit OVERRUN 0x80 + mask SFCNT 0x1f +} + +/* + * SCSI Status 3 (p. 3-26) + */ +register SSTAT3 { + address 0x00e + access_mode RO + mask SCSICNT 0xf0 + mask OFFCNT 0x0f +} + +/* + * SCSI Test Control (p. 3-27) + */ +register SCSITEST { + address 0x00f + access_mode RW + bit RQAKCNT 0x04 + bit CNTRTEST 0x02 + bit CMODE 0x01 +} + +/* + * SCSI Interrupt Mode 1 (p. 3-28) + * Setting any bit will enable the corresponding function + * in SIMODE0 to interrupt via the IRQ pin. + */ +register SIMODE0 { + address 0x010 + access_mode RW + bit ENSELDO 0x40 + bit ENSELDI 0x20 + bit ENSELINGO 0x10 + bit ENSWRAP 0x08 + bit ENSDONE 0x04 + bit ENSPIORDY 0x02 + bit ENDMADONE 0x01 +} + +/* + * SCSI Interrupt Mode 1 (pp. 3-28,29) + * Setting any bit will enable the corresponding function + * in SIMODE1 to interrupt via the IRQ pin. + */ +register SIMODE1 { + address 0x011 + access_mode RW + bit ENSELTIMO 0x80 + bit ENATNTARG 0x40 + bit ENSCSIRST 0x20 + bit ENPHASEMIS 0x10 + bit ENBUSFREE 0x08 + bit ENSCSIPERR 0x04 + bit ENPHASECHG 0x02 + bit ENREQINIT 0x01 +} + +/* + * SCSI Data Bus (High) (p. 3-29) + * This register reads data on the SCSI Data bus directly. + */ +register SCSIBUSL { + address 0x012 + access_mode RO +} + +register SCSIBUSH { + address 0x013 + access_mode RO +} + +/* + * SCSI/Host Address (p. 3-30) + * These registers hold the host address for the byte about to be + * transferred on the SCSI bus. They are counted up in the same + * manner as STCNT is counted down. SHADDR should always be used + * to determine the address of the last byte transferred since HADDR + * can be skewed by write ahead. + */ +register SHADDR { + address 0x014 + size 4 + access_mode RO +} + +/* + * Selection Timeout Timer (p. 3-30) + */ +register SELTIMER { + address 0x018 + access_mode RW + bit STAGE6 0x20 + bit STAGE5 0x10 + bit STAGE4 0x08 + bit STAGE3 0x04 + bit STAGE2 0x02 + bit STAGE1 0x01 +} + +/* + * Selection/Reselection ID (p. 3-31) + * Upper four bits are the device id. The ONEBIT is set when the re/selecting + * device did not set its own ID. + */ +register SELID { + address 0x019 + access_mode RW + mask SELID_MASK 0xf0 + bit ONEBIT 0x08 +} + +/* + * SCSI Block Control (p. 3-32) + * Controls Bus type and channel selection. In a twin channel configuration + * addresses 0x00-0x1e are gated to the appropriate channel based on this + * register. SELWIDE allows for the coexistence of 8bit and 16bit devices + * on a wide bus. + */ +register SBLKCTL { + address 0x01f + access_mode RW + bit DIAGLEDEN 0x80 /* Aic78X0 only */ + bit DIAGLEDON 0x40 /* Aic78X0 only */ + bit AUTOFLUSHDIS 0x20 + bit SELBUSB 0x08 + bit SELWIDE 0x02 +} + +/* + * Sequencer Control (p. 3-33) + * Error detection mode and speed configuration + */ +register SEQCTL { + address 0x060 + access_mode RW + bit PERRORDIS 0x80 + bit PAUSEDIS 0x40 + bit FAILDIS 0x20 + bit FASTMODE 0x10 + bit BRKADRINTEN 0x08 + bit STEP 0x04 + bit SEQRESET 0x02 + bit LOADRAM 0x01 +} + +/* + * Sequencer RAM Data (p. 3-34) + * Single byte window into the Scratch Ram area starting at the address + * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write + * four bytes in sucessesion. The SEQADDRs will increment after the most + * significant byte is written + */ +register SEQRAM { + address 0x061 + access_mode RW +} + +/* + * Sequencer Address Registers (p. 3-35) + * Only the first bit of SEQADDR1 holds addressing information + */ +register SEQADDR0 { + address 0x062 + access_mode RW +} + +register SEQADDR1 { + address 0x063 + access_mode RW + mask SEQADDR1_MASK 0x01 +} + +/* + * Accumulator + * We cheat by passing arguments in the Accumulator up to the kernel driver + */ +register ACCUM { + address 0x064 + access_mode RW + accumulator +} + +register SINDEX { + address 0x065 + access_mode RW + sindex +} + +register DINDEX { + address 0x066 + access_mode RW +} + +register ALLONES { + address 0x069 + access_mode RO + allones +} + +register ALLZEROS { + address 0x06a + access_mode RO + allzeros +} + +register NONE { + address 0x06a + access_mode WO + none +} + +register FLAGS { + address 0x06b + access_mode RO + bit ZERO 0x02 + bit CARRY 0x01 +} + +register SINDIR { + address 0x06c + access_mode RO +} + +register DINDIR { + address 0x06d + access_mode WO +} + +register FUNCTION1 { + address 0x06e + access_mode RW +} + +register STACK { + address 0x06f + access_mode RO +} + +/* + * Board Control (p. 3-43) + */ +register BCTL { + address 0x084 + access_mode RW + bit ACE 0x08 + bit ENABLE 0x01 +} + +/* + * On the aic78X0 chips, Board Control is replaced by the DSCommand + * register (p. 4-64) + */ +register DSCOMMAND { + address 0x084 + access_mode RW + bit CACHETHEN 0x80 /* Cache Threshold enable */ + bit DPARCKEN 0x40 /* Data Parity Check Enable */ + bit MPARCKEN 0x20 /* Memory Parity Check Enable */ + bit EXTREQLCK 0x10 /* External Request Lock */ +} + +/* + * Bus On/Off Time (p. 3-44) + */ +register BUSTIME { + address 0x085 + access_mode RW + mask BOFF 0xf0 + mask BON 0x0f +} + +/* + * Bus Speed (p. 3-45) + */ +register BUSSPD { + address 0x086 + access_mode RW + mask DFTHRSH 0xc0 + mask STBOFF 0x38 + mask STBON 0x07 + mask DFTHRSH_100 0xc0 +} + +/* + * Host Control (p. 3-47) R/W + * Overall host control of the device. + */ +register HCNTRL { + address 0x087 + access_mode RW + bit POWRDN 0x40 + bit SWINT 0x10 + bit IRQMS 0x08 + bit PAUSE 0x04 + bit INTEN 0x02 + bit CHIPRST 0x01 + bit CHIPRSTACK 0x01 +} + +/* + * Host Address (p. 3-48) + * This register contains the address of the byte about + * to be transferred across the host bus. + */ +register HADDR { + address 0x088 + size 4 + access_mode RW +} + +register HCNT { + address 0x08c + size 3 + access_mode RW +} + +/* + * SCB Pointer (p. 3-49) + * Gate one of the four SCBs into the SCBARRAY window. + */ +register SCBPTR { + address 0x090 + access_mode RW +} + +/* + * Interrupt Status (p. 3-50) + * Status for system interrupts + */ +register INTSTAT { + address 0x091 + access_mode RW + bit BRKADRINT 0x08 + bit SCSIINT 0x04 + bit CMDCMPLT 0x02 + bit SEQINT 0x01 + mask BAD_PHASE SEQINT /* unknown scsi bus phase */ + mask SEND_REJECT 0x10|SEQINT /* sending a message reject */ + mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/ + mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */ + mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */ + mask NO_MATCH_BUSY 0x50|SEQINT /* Couldn't find BUSY SCB */ + mask REJECT_MSG 0x60|SEQINT /* Reject message received */ + mask BAD_STATUS 0x70|SEQINT /* Bad status from target */ + mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */ + mask ABORT_CMDCMPLT 0x91 /* + * Command tagged for abort + * completed successfully. + */ + mask AWAITING_MSG 0xa0|SEQINT /* + * Kernel requested to specify + * a message to this target + * (command was null), so tell + * it that it can fill the + * message buffer. + */ + mask MSG_BUFFER_BUSY 0xc0|SEQINT /* + * Sequencer wants to use the + * message buffer, but it + * already contains a message + */ + mask MSGIN_PHASEMIS 0xd0|SEQINT /* + * Target changed phase on us + * when we were expecting + * another msgin byte. + */ + mask DATA_OVERRUN 0xe0|SEQINT /* + * Target attempted to write + * beyond the bounds of its + * command. + */ + + mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */ + mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT) +} + +/* + * Hard Error (p. 3-53) + * Reporting of catastrophic errors. You usually cannot recover from + * these without a full board reset. + */ +register ERROR { + address 0x092 + access_mode RO + bit PARERR 0x08 + bit ILLOPCODE 0x04 + bit ILLSADDR 0x02 + bit ILLHADDR 0x01 +} + +/* + * Clear Interrupt Status (p. 3-52) + */ +register CLRINT { + address 0x092 + access_mode WO + bit CLRBRKADRINT 0x08 + bit CLRSCSIINT 0x04 + bit CLRCMDINT 0x02 + bit CLRSEQINT 0x01 +} + +register DFCNTRL { + address 0x093 + access_mode RW + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 +} + +register DFSTATUS { + address 0x094 + access_mode RO + bit DWORDEMP 0x20 + bit MREQPEND 0x10 + bit HDONE 0x08 + bit DFTHRESH 0x04 + bit FIFOFULL 0x02 + bit FIFOEMP 0x01 +} + +register DFDAT { + address 0x099 + access_mode RW +} + +/* + * SCB Auto Increment (p. 3-59) + * Byte offset into the SCB Array and an optional bit to allow auto + * incrementing of the address during download and upload operations + */ +register SCBCNT { + address 0x09a + access_mode RW + bit SCBAUTO 0x80 + mask SCBCNT_MASK 0x1f +} + +/* + * Queue In FIFO (p. 3-60) + * Input queue for queued SCBs (commands that the seqencer has yet to start) + */ +register QINFIFO { + address 0x09b + access_mode RW +} + +/* + * Queue In Count (p. 3-60) + * Number of queued SCBs + */ +register QINCNT { + address 0x09c + access_mode RO +} + +/* + * Queue Out FIFO (p. 3-61) + * Queue of SCBs that have completed and await the host + */ +register QOUTFIFO { + address 0x09d + access_mode WO +} + +/* + * Queue Out Count (p. 3-61) + * Number of queued SCBs in the Out FIFO + */ +register QOUTCNT { + address 0x09e + access_mode RO +} + +/* + * SCB Definition (p. 5-4) + */ +scb { + address 0x0a0 + SCB_CONTROL { + size 1 + bit MK_MESSAGE 0x80 + bit DISCENB 0x40 + bit TAG_ENB 0x20 + bit MUST_DMAUP_SCB 0x10 + bit ABORT_SCB 0x08 + bit DISCONNECTED 0x04 + mask SCB_TAG_TYPE 0x03 + } + SCB_TCL { + size 1 + bit SELBUSB 0x08 + mask TID 0xf0 + mask LID 0x07 + } + SCB_TARGET_STATUS { + size 1 + } + SCB_SGCOUNT { + size 1 + } + SCB_SGPTR { + size 4 + } + SCB_RESID_SGCNT { + size 1 + } + SCB_RESID_DCNT { + size 3 + } + SCB_DATAPTR { + size 4 + } + SCB_DATACNT { + size 3 + } + SCB_LINKED_NEXT { + size 1 + } + SCB_CMDPTR { + size 4 + } + SCB_CMDLEN { + size 1 + } + SCB_TAG { + size 1 + } + SCB_NEXT { + size 1 + } + SCB_PREV { + size 1 + } + SCB_BUSYTARGETS { + size 4 + } +} + +const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */ + +/* --------------------- AHA-2840-only definitions -------------------- */ + +register SEECTL_2840 { + address 0x0c0 + access_mode RW + bit CS_2840 0x04 + bit CK_2840 0x02 + bit DO_2840 0x01 +} + +register STATUS_2840 { + address 0x0c1 + access_mode RW + bit EEPROM_TF 0x80 + mask BIOS_SEL 0x60 + mask ADSEL 0x1e + bit DI_2840 0x01 +} + +/* --------------------- AIC-7870-only definitions -------------------- */ + +register DSPCISTATUS { + address 0x086 +} + +register BRDCTL { + address 0x01d + bit BRDDAT7 0x80 + bit BRDDAT6 0x40 + bit BRDDAT5 0x20 + bit BRDSTB 0x10 + bit BRDCS 0x08 + bit BRDRW 0x04 + bit BRDCTL1 0x02 + bit BRDCTL0 0x01 +} + +/* + * Serial EEPROM Control (p. 4-92 in 7870 Databook) + * Controls the reading and writing of an external serial 1-bit + * EEPROM Device. In order to access the serial EEPROM, you must + * first set the SEEMS bit that generates a request to the memory + * port for access to the serial EEPROM device. When the memory + * port is not busy servicing another request, it reconfigures + * to allow access to the serial EEPROM. When this happens, SEERDY + * gets set high to verify that the memory port access has been + * granted. + * + * After successful arbitration for the memory port, the SEECS bit of + * the SEECTL register is connected to the chip select. The SEECK, + * SEEDO, and SEEDI are connected to the clock, data out, and data in + * lines respectively. The SEERDY bit of SEECTL is useful in that it + * gives us an 800 nsec timer. After a write to the SEECTL register, + * the SEERDY goes high 800 nsec later. The one exception to this is + * when we first request access to the memory port. The SEERDY goes + * high to signify that access has been granted and, for this case, has + * no implied timing. + * + * See 93cx6.c for detailed information on the protocol necessary to + * read the serial EEPROM. + */ +register SEECTL { + address 0x01e + bit EXTARBACK 0x80 + bit EXTARBREQ 0x40 + bit SEEMS 0x20 + bit SEERDY 0x10 + bit SEECS 0x08 + bit SEECK 0x04 + bit SEEDO 0x02 + bit SEEDI 0x01 +} +/* ---------------------- Scratch RAM Offsets ------------------------- */ +/* These offsets are either to values that are initialized by the board's + * BIOS or are specified by the sequencer code. + * + * The host adapter card (at least the BIOS) uses 20-2f for SCSI + * device information, 32-33 and 5a-5f as well. As it turns out, the + * BIOS trashes 20-2f, writing the synchronous negotiation results + * on top of the BIOS values, so we re-use those for our per-target + * scratchspace (actually a value that can be copied directly into + * SCSIRATE). The kernel driver will enable synchronous negotiation + * for all targets that have a value other than 0 in the lower four + * bits of the target scratch space. This should work regardless of + * whether the bios has been installed. + */ + +scratch_ram { + address 0x020 + + /* + * 1 byte per target starting at this address for configuration values + */ + TARG_SCRATCH { + size 16 + } + ULTRA_ENB { + size 2 + } + /* + * Bit vector of targets that have disconnection disabled. + */ + DISC_DSB { + size 2 + } + /* + * Length of pending message + */ + MSG_LEN { + size 1 + } + /* We reserve 8bytes to store outgoing messages */ + MSG_OUT { + size 8 + } + /* Parameters for DMA Logic */ + DMAPARAMS { + size 1 + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 + } + /* + * Number of SCBs supported by + * this card. + */ + SCBCOUNT { + size 1 + } + /* + * Two's complement of SCBCOUNT + */ + COMP_SCBCOUNT { + size 1 + } + /* + * Mask of bits to test against + * when looking at the Queue Count + * registers. Works around a bug + * on aic7850 chips. + */ + QCNTMASK { + size 1 + } + SEQ_FLAGS { + size 1 + bit RESELECTED 0x80 + bit IDENTIFY_SEEN 0x40 + bit TAGGED_SCB 0x20 + bit DPHASE 0x10 + bit PAGESCBS 0x04 + bit WIDE_BUS 0x02 + bit TWIN_BUS 0x01 + } + /* + * Temporary storage for the + * target/channel/lun of a + * reconnecting target + */ + SAVED_TCL { + size 1 + } + SG_COUNT { + size 1 + } + /* working value of SG pointer */ + SG_NEXT { + size 4 + } + /* + * head of list of SCBs awaiting + * selection + */ + WAITING_SCBH { + size 1 + } + SAVED_LINKPTR { + size 1 + } + SAVED_SCBPTR { + size 1 + } + /* + * The sequencer will stick the frist byte of any rejected message here + * so we can see what is getting thrown away. + */ + REJBYTE { + size 1 + } + /* + * The last bus phase as seen by the sequencer. + */ + LASTPHASE { + size 1 + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI + mask P_BUSFREE 0x01 + } + MSGIN_EXT_LEN { + size 1 + } + MSGIN_EXT_OPCODE { + size 1 + } + /* + * location 3, stores the last + * byte of an extended message if + * it passes the two bytes of space + * we allow now. This byte isn't + * used for anything, it just makes + * the code shorter for tossing + * extra bytes. + */ + MSGIN_EXT_BYTES { + size 3 + } + /* + * head of list of SCBs that are + * disconnected. Used for SCB + * paging. + */ + DISCONNECTED_SCBH { + size 1 + } + /* + * head of list of SCBs that are + * not in use. Used for SCB paging. + */ + FREE_SCBH { + size 1 + } + HSCB_ADDR { + size 4 + } + CUR_SCBID { + size 1 + } + ARG_1 { + size 1 + mask SEND_MSG 0x80 + mask SEND_SENSE 0x40 + mask SEND_REJ 0x20 + alias RETURN_1 + } + /* + * These are reserved registers in the card's scratch ram. Some of + * the values are specified in the AHA2742 technical reference manual + * and are initialized by the BIOS at boot time. + */ + SCSICONF { + address 0x05a + size 1 + bit RESET_SCSI 0x40 + } + HOSTCONF { + address 0x05d + size 1 + } + HA_274_BIOSCTRL { + address 0x05f + size 1 + mask BIOSMODE 0x30 + mask BIOSDISABLED 0x30 + bit CHANNEL_B_PRIMARY 0x08 + } +} + +const SCB_LIST_NULL 0xff + + +/* WDTR Message values */ +const BUS_8_BIT 0x00 +const BUS_16_BIT 0x01 +const BUS_32_BIT 0x02 +const MAX_OFFSET_8BIT 0x0f +const MAX_OFFSET_16BIT 0x08 diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq new file mode 100644 index 000000000000..faa9e1b0f416 --- /dev/null +++ b/drivers/scsi/aic7xxx/aic7xxx.seq @@ -0,0 +1,1150 @@ +/*+M*********************************************************************** + *Adaptec 274x/284x/294x device driver for Linux and FreeBSD. + * + *Copyright (c) 1994 John Aycock + * The University of Calgary Department of Computer Science. + * All rights reserved. + * + *FreeBSD, Twin, Wide, 2 command per target support, tagged queuing, + *SCB paging and other optimizations: + *Copyright (c) 1994, 1995, 1996, 1997 Justin Gibbs. All rights reserved. + * + *Redistribution and use in source and binary forms, with or without + *modification, are permitted provided that the following conditions + *are met: + *1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer. + *2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + *3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of Calgary + * Department of Computer Science and its contributors. + *4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + *THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + *ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + *ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + *OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + *HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + *LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + *OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + *SUCH DAMAGE. + * + * $Id: aic7xxx.seq,v 1.73 1997/04/24 16:52:18 gibbs Exp $ + * + *-M************************************************************************/ + +#include +#include + +/* + * A few words on the waiting SCB list: + * After starting the selection hardware, we check for reconnecting targets + * as well as for our selection to complete just in case the reselection wins + * bus arbitration. The problem with this is that we must keep track of the + * SCB that we've already pulled from the QINFIFO and started the selection + * on just in case the reselection wins so that we can retry the selection at + * a later time. This problem cannot be resolved by holding a single entry + * in scratch ram since a reconnecting target can request sense and this will + * create yet another SCB waiting for selection. The solution used here is to + * use byte 27 of the SCB as a psuedo-next pointer and to thread a list + * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, + * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to + * this list everytime a request sense occurs or after completing a non-tagged + * command for which a second SCB has been queued. The sequencer will + * automatically consume the entries. + */ + +/* + * We assume that the kernel driver may reset us at any time, even in the + * middle of a DMA, so clear DFCNTRL too. + */ +reset: + clr SCSISIGO; /* De-assert BSY */ + /* Always allow reselection */ + mvi SCSISEQ, ENRSELI|ENAUTOATNP; + call clear_target_state; +poll_for_work: + test SSTAT0,SELDO jnz select; + test SSTAT0,SELDI jnz reselect; + test SCSISEQ, ENSELO jnz poll_for_work; +.if ( TWIN_CHANNEL ) + /* + * Twin channel devices cannot handle things like SELTO + * interrupts on the "background" channel. So, if we + * are selecting, keep polling the current channel util + * either a selection or reselection occurs. + */ + xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + test SSTAT0,SELDO jnz select; + test SSTAT0,SELDI jnz reselect; + test SCSISEQ, ENSELO jnz poll_for_work; + xor SBLKCTL,SELBUSB; /* Toggle back */ +.endif + cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; +test_queue: + /* Has the driver posted any work for us? */ + mov A, QCNTMASK; + test QINCNT,A jz poll_for_work; + +/* + * We have at least one queued SCB now and we don't have any + * SCBs in the list of SCBs awaiting selection. If we have + * any SCBs available for use, pull the tag from the QINFIFO + * and get to work on it. + */ +.if ( SCB_PAGING ) + mov ALLZEROS call get_free_or_disc_scb; + cmp SINDEX, SCB_LIST_NULL je poll_for_work; +.endif +dequeue_scb: + mov CUR_SCBID,QINFIFO; +.if !( SCB_PAGING ) + /* In the non-paging case, the SCBID == hardware SCB index */ + mov SCBPTR, CUR_SCBID; +.endif +dma_queued_scb: +/* + * DMA the SCB from host ram into the current SCB location. + */ + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov CUR_SCBID call dma_scb; + +/* + * See if there is not already an active SCB for this target. This code + * locks out on a per target basis instead of target/lun. Although this + * is not ideal for devices that have multiple luns active at the same + * time, it is faster than looping through all SCB's looking for active + * commands. We also don't have enough spare SCB space for us to store the + * SCBID of the currently busy transaction for each target/lun making it + * impossible to link up the SCBs. + */ +test_busy: + test SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb; + mvi SEQCTL, PAUSEDIS|FASTMODE; + mov SAVED_SCBPTR, SCBPTR; + mov SCB_TCL call index_untagged_scb; + mov ARG_1, SINDIR; /* + * ARG_1 should + * now have the SCB ID of + * any active, non-tagged, + * command for this target. + */ + cmp ARG_1, SCB_LIST_NULL je make_busy; +.if ( SCB_PAGING ) + /* + * Put this SCB back onto the free list. It + * may be necessary to satisfy the search for + * the active SCB. + */ + mov SCBPTR, SAVED_SCBPTR; + call add_scb_to_free_list; + /* Find the active SCB */ + mov ALLZEROS call findSCB; + /* + * If we couldn't find it, tell the kernel. This should + * never happen. + */ + cmp SINDEX, SCB_LIST_NULL jne paged_busy_link; + mvi INTSTAT, NO_MATCH_BUSY; +paged_busy_link: + /* Link us in */ + mov SCB_LINKED_NEXT, CUR_SCBID; + /* Put it back on the disconnected list */ + call add_scb_to_disc_list; + mvi SEQCTL, FASTMODE; + jmp poll_for_work; +.else +simple_busy_link: + mov SCBPTR, ARG_1; + mov SCB_LINKED_NEXT, CUR_SCBID; + mvi SEQCTL, FASTMODE; + jmp poll_for_work; +.endif +make_busy: + mov DINDIR, CUR_SCBID; + mov SCBPTR, SAVED_SCBPTR; + mvi SEQCTL, FASTMODE; + +start_scb: + /* + * Place us on the waiting list in case our selection + * doesn't win during bus arbitration. + */ + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; +start_waiting: + /* + * Pull the first entry off of the waiting SCB list + * We don't have to "test_busy" because only transactions that + * have passed that test can be in the WAITING_SCB list. + */ + mov SCBPTR, WAITING_SCBH; + call start_selection; + jmp poll_for_work; + +start_selection: +.if ( TWIN_CHANNEL ) + and SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */ + and A,SELBUSB,SCB_TCL; /* Get new channel bit */ + or SINDEX,A; + mov SBLKCTL,SINDEX; /* select channel */ +.endif +initialize_scsiid: + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID, OID; /* Clear old target */ + or SCSIID, A; + mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; +/* + * Reselection has been initiated by a target. Make a note that we've been + * reselected, but haven't seen an IDENTIFY message from the target yet. + */ +reselect: + clr MSG_LEN; /* Don't have anything in the mesg buffer */ + mvi CLRSINT0, CLRSELDI; + /* XXX test for and handle ONE BIT condition */ + and SAVED_TCL, SELID_MASK, SELID; + or SEQ_FLAGS,RESELECTED; + jmp select2; + +/* + * After the selection, remove this SCB from the "waiting SCB" + * list. This is achieved by simply moving our "next" pointer into + * WAITING_SCBH. Our next pointer will be set to null the next time this + * SCB is used, so don't bother with it now. + */ +select: + /* Turn off the selection hardware */ + mvi SCSISEQ, ENRSELI|ENAUTOATNP; /* + * ATN on parity errors + * for "in" phases + */ + mvi CLRSINT0, CLRSELDO; + mov SCBPTR, WAITING_SCBH; + mov WAITING_SCBH,SCB_NEXT; + mov SAVED_TCL, SCB_TCL; +/* + * As soon as we get a successful selection, the target should go + * into the message out phase since we have ATN asserted. Prepare + * the message to send. + * + * Messages are stored in scratch RAM starting with a length byte + * followed by the message itself. + */ + +mk_identify: + and MSG_OUT,0x7,SCB_TCL; /* lun */ + and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ + or MSG_OUT,A; /* or in disconnect privledge */ + or MSG_OUT,MSG_IDENTIFYFLAG; + mvi MSG_LEN, 1; + +/* + * Send a tag message if TAG_ENB is set in the SCB control block. + * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. + */ +mk_tag: + test SCB_CONTROL,TAG_ENB jz mk_message; + and MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL; + mov MSG_OUT[2],SCB_TAG; + add MSG_LEN,2; /* update message length */ + +/* + * Interrupt the driver, and allow it to tweak the message buffer + * if it asks. + */ +mk_message: + test SCB_CONTROL,MK_MESSAGE jz select2; + mvi INTSTAT,AWAITING_MSG; + +select2: + mvi CLRSINT1,CLRBUSFREE; + or SIMODE1, ENBUSFREE; /* + * We aren't expecting a + * bus free, so interrupt + * the kernel driver if it + * happens. + */ +/* + * Initialize Ultra mode setting and clear the SCSI channel. + */ + or SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN; +.if ( ULTRA ) +ultra: + mvi SINDEX, ULTRA_ENB+1; + test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ + dec SINDEX; +ultra_2: + mov FUNCTION1,SAVED_TCL; + mov A,FUNCTION1; + test SINDIR, A jz ndx_dtr; + or SXFRCTL0, FAST20; +.endif + +/* + * Initialize SCSIRATE with the appropriate value for this target. + * The SCSIRATE settings for each target are stored in an array + * based at TARG_SCRATCH. + */ +ndx_dtr: + shr A,4,SAVED_TCL; + test SBLKCTL,SELBUSB jz ndx_dtr_2; + or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */ + or A,0x08; /* Channel B entries add 8 */ +ndx_dtr_2: + add SINDEX,TARG_SCRATCH,A; + mov SCSIRATE,SINDIR; + + +/* + * Main loop for information transfer phases. If BSY is false, then + * we have a bus free condition, expected or not. Otherwise, wait + * for the target to assert REQ before checking MSG, C/D and I/O + * for the bus phase. + * + */ +ITloop: + test SSTAT1,REQINIT jz ITloop; + test SSTAT1, SCSIPERR jnz ITloop; + + and A,PHASE_MASK,SCSISIGI; + mov LASTPHASE,A; + mov SCSISIGO,A; + + cmp ALLZEROS,A je p_dataout; + cmp A,P_DATAIN je p_datain; + cmp A,P_COMMAND je p_command; + cmp A,P_MESGOUT je p_mesgout; + cmp A,P_STATUS je p_status; + cmp A,P_MESGIN je p_mesgin; + + mvi INTSTAT,BAD_PHASE; /* unknown phase - signal driver */ + jmp ITloop; /* Try reading the bus again. */ + +await_busfree: + and SIMODE1, ~ENBUSFREE; + call clear_target_state; + mov NONE, SCSIDATL; /* Ack the last byte */ + test SSTAT1,REQINIT|BUSFREE jz .; + test SSTAT1, BUSFREE jnz poll_for_work; + mvi INTSTAT, BAD_PHASE; + +clear_target_state: + clr DFCNTRL; + clr SCSIRATE; /* + * We don't know the target we will + * connect to, so default to narrow + * transfers to avoid parity problems. + */ + and SXFRCTL0, ~FAST20; + mvi LASTPHASE, P_BUSFREE; + /* clear target specific flags */ + and SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret; + +p_dataout: + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET; + jmp data_phase_init; + +/* + * If we re-enter the data phase after going through another phase, the + * STCNT may have been cleared, so restore it from the residual field. + */ +data_phase_reinit: + mvi DINDEX, STCNT; + mvi SCB_RESID_DCNT call bcopy_3; + jmp data_phase_loop; + +p_datain: + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; +data_phase_init: + call assert; /* + * Ensure entering a data + * phase is okay - seen identify, etc. + */ + + test SEQ_FLAGS, DPHASE jnz data_phase_reinit; + + /* + * Initialize the DMA address and counter from the SCB. + * Also set SG_COUNT and SG_NEXT in memory since we cannot + * modify the values in the SCB itself until we see a + * save data pointers message. + */ + mvi DINDEX, HADDR; + mvi SCB_DATAPTR call bcopy_7; + + call set_stcnt_from_hcnt; + + mov SG_COUNT,SCB_SGCOUNT; + + mvi DINDEX, SG_NEXT; + mvi SCB_SGPTR call bcopy_4; + +data_phase_loop: +/* Guard against overruns */ + test SG_COUNT, 0xff jnz data_phase_inbounds; +/* + * Turn on 'Bit Bucket' mode, set the transfer count to + * 16meg and let the target run until it changes phase. + * When the transfer completes, notify the host that we + * had an overrun. + */ + or SXFRCTL1,BITBUCKET; + mvi HCNT[0], 0xff; + mvi HCNT[1], 0xff; + mvi HCNT[2], 0xff; + call set_stcnt_from_hcnt; + +data_phase_inbounds: +/* If we are the last SG block, ensure wideodd is off. */ + cmp SG_COUNT,0x01 jne data_phase_wideodd; + and DMAPARAMS, ~WIDEODD; +data_phase_wideodd: + mov DMAPARAMS call dma; + +/* Go tell the host about any overruns */ + test SXFRCTL1,BITBUCKET jnz data_phase_overrun; + +/* Exit if we had an underrun. dma clears SINDEX in this case. */ + test SINDEX,0xff jz data_phase_finish; + +/* + * Advance the scatter-gather pointers if needed + */ +sg_advance: + dec SG_COUNT; /* one less segment to go */ + + test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ + + clr A; /* add sizeof(struct scatter) */ + add SG_NEXT[0],SG_SIZEOF; + adc SG_NEXT[1],A; + +/* + * Load a struct scatter and set up the data address and length. + * If the working value of the SG count is nonzero, then + * we need to load a new set of values. + * + * This, like all DMA's, assumes little-endian host data storage. + */ +sg_load: + mvi DINDEX, HADDR; + mvi SG_NEXT call bcopy_4; + + mvi HCNT[0],SG_SIZEOF; + clr HCNT[1]; + clr HCNT[2]; + + or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + + call dma_finish; + +/* + * Copy data from FIFO into SCB data pointer and data count. This assumes + * that the SG segments are of the form: + * + * struct ahc_dma_seg { + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order + * }; + */ + mvi HADDR call dfdat_in_7; + +/* Load STCNT as well. It is a mirror of HCNT */ + call set_stcnt_from_hcnt; + test SSTAT1,PHASEMIS jz data_phase_loop; + +data_phase_finish: +/* + * After a DMA finishes, save the SG and STCNT residuals back into the SCB + * We use STCNT instead of HCNT, since it's a reflection of how many bytes + * were transferred on the SCSI (as opposed to the host) bus. + */ + mov SCB_RESID_DCNT[0],STCNT[0]; + mov SCB_RESID_DCNT[1],STCNT[1]; + mov SCB_RESID_DCNT[2],STCNT[2]; + mov SCB_RESID_SGCNT, SG_COUNT; + + /* We have seen a data phase */ + or SEQ_FLAGS, DPHASE; + + jmp ITloop; + +data_phase_overrun: +/* + * Turn off BITBUCKET mode and notify the host + */ + and SXFRCTL1, ~BITBUCKET; + mvi INTSTAT,DATA_OVERRUN; + jmp ITloop; + +/* + * Command phase. Set up the DMA registers and let 'er rip. + */ +p_command: + call assert; + +/* + * Load HADDR and HCNT. + */ + mvi DINDEX, HADDR; + mvi SCB_CMDPTR call bcopy_5; + clr HCNT[1]; + clr HCNT[2]; + + call set_stcnt_from_hcnt; + + mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; + jmp ITloop; + +/* + * Status phase. Wait for the data byte to appear, then read it + * and store it into the SCB. + */ +p_status: + call assert; + + mov SCB_TARGET_STATUS, SCSIDATL; + jmp ITloop; + +/* + * Message out phase. If there is not an active message, but the target + * took us into this phase anyway, build a no-op message and send it. + */ +p_mesgout: + test MSG_LEN, 0xff jnz p_mesgout_start; + mvi MSG_NOOP call mk_mesg; /* build NOP message */ +p_mesgout_start: +/* + * Set up automatic PIO transfer from MSG_OUT. Bit 3 in + * SXFRCTL0 (SPIOEN) is already on. + */ + mvi SINDEX,MSG_OUT; + mov DINDEX,MSG_LEN; + +/* + * When target asks for a byte, drop ATN if it's the last one in + * the message. Otherwise, keep going until the message is exhausted. + * ATN must be dropped *at least* 90ns before we ack the last byte, so + * the code is aranged to execute two instructions before the byte is + * transferred to give a good margin of safety + * + * Keep an eye out for a phase change, in case the target issues + * a MESSAGE REJECT. + */ +p_mesgout_loop: + test SSTAT1, REQINIT jz p_mesgout_loop; + test SSTAT1, SCSIPERR jnz p_mesgout_loop; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; +p_mesgout_testretry: + test DINDEX,0xff jnz p_mesgout_dropatn; + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ + jmp p_mesgout_start; +/* + * If the next bus phase after ATN drops is a message out, it means + * that the target is requesting that the last message(s) be resent. + */ +p_mesgout_dropatn: + cmp DINDEX,1 jne p_mesgout_outb; /* last byte? */ + mvi CLRSINT1,CLRATNO; /* drop ATN */ +p_mesgout_outb: + dec DINDEX; + mov SCSIDATL,SINDIR; + jmp p_mesgout_loop; + +p_mesgout_done: + mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */ + clr MSG_LEN; /* no active msg */ + jmp ITloop; + +/* + * Message in phase. Bytes are read using Automatic PIO mode. + */ +p_mesgin: + mvi ACCUM call inb_first; /* read the 1st message byte */ + mov REJBYTE,A; /* save it for the driver */ + + test A,MSG_IDENTIFYFLAG jnz mesgin_identify; + cmp A,MSG_DISCONNECT je mesgin_disconnect; + cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs; + cmp ALLZEROS,A je mesgin_complete; + cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs; + cmp A,MSG_EXTENDED je mesgin_extended; + cmp A,MSG_MESSAGE_REJECT je mesgin_reject; + cmp A,MSG_NOOP je mesgin_done; + +rej_mesgin: +/* + * We have no idea what this message in is, so we issue a message reject + * and hope for the best. In any case, rejection should be a rare + * occurrence - signal the driver when it happens. + */ + mvi INTSTAT,SEND_REJECT; /* let driver know */ + + mvi MSG_MESSAGE_REJECT call mk_mesg; + +mesgin_done: + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ + jmp ITloop; + + +mesgin_complete: +/* + * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, + * and trigger a completion interrupt. Before doing so, check to see if there + * is a residual or the status byte is something other than NO_ERROR (0). In + * either of these conditions, we upload the SCB back to the host so it can + * process this information. In the case of a non zero status byte, we + * additionally interrupt the kernel driver synchronously, allowing it to + * decide if sense should be retrieved. If the kernel driver wishes to request + * sense, it will fill the kernel SCB with a request sense command and set + * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload + * the SCB, and process it as the next command by adding it to the waiting list. + * If the kernel driver does not wish to request sense, it need only clear + * RETURN_1, and the command is allowed to complete normally. We don't bother + * to post to the QOUTFIFO in the error cases since it would require extra + * work in the kernel driver to ensure that the entry was removed before the + * command complete code tried processing it. + */ + +/* + * First check for residuals + */ + test SCB_RESID_SGCNT,0xff jnz upload_scb; + test SCB_TARGET_STATUS,0xff jz status_ok; /* Good Status? */ +upload_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +check_status: + test SCB_TARGET_STATUS,0xff jz status_ok; /* Just a residual? */ + mvi INTSTAT,BAD_STATUS; /* let driver know */ + cmp RETURN_1, SEND_SENSE jne status_ok; + /* This SCB becomes the next to execute as it will retrieve sense */ + mov SCB_LINKED_NEXT, SCB_TAG; + jmp dma_next_scb; + +status_ok: +/* First, mark this target as free. */ + test SCB_CONTROL,TAG_ENB jnz complete; /* + * Tagged commands + * don't busy the + * target. + */ + mov SAVED_SCBPTR, SCBPTR; + mov SAVED_LINKPTR, SCB_LINKED_NEXT; + mov SCB_TCL call index_untagged_scb; + mov DINDIR, SAVED_LINKPTR; + mov SCBPTR, SAVED_SCBPTR; + +complete: + /* Post the SCB and issue an interrupt */ + mov QOUTFIFO,SCB_TAG; + mvi INTSTAT,CMDCMPLT; + test SCB_CONTROL, ABORT_SCB jz dma_next_scb; + mvi INTSTAT, ABORT_CMDCMPLT; + +dma_next_scb: + cmp SCB_LINKED_NEXT, SCB_LIST_NULL je add_to_free_list; +.if !( SCB_PAGING ) + /* Only DMA on top of ourselves if we are the SCB to download */ + mov A, SCB_LINKED_NEXT; + cmp SCB_TAG, A je dma_next_scb2; + call add_scb_to_free_list; + mov SCBPTR, A; + jmp add_to_waiting_list; +.endif +dma_next_scb2: + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov SCB_LINKED_NEXT call dma_scb; +add_to_waiting_list: + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; + /* + * Prepare our selection hardware before the busfree so we have a + * high probability of winning arbitration. + */ + call start_selection; + jmp await_busfree; +add_to_free_list: + call add_scb_to_free_list; + jmp await_busfree; + +/* + * Is it an extended message? Copy the message to our message buffer and + * notify the host. The host will tell us whether to reject this message, + * respond to it with the message that the host placed in our message buffer, + * or simply to do nothing. + */ +mesgin_extended: + mvi MSGIN_EXT_LEN call inb_next; + mov A, MSGIN_EXT_LEN; +mesgin_extended_loop: + mov DINDEX call inb_next; + dec A; + cmp DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test; + dec DINDEX; /* dump by repeatedly filling the last byte */ +mesgin_extended_loop_test: + test A, 0xFF jnz mesgin_extended_loop; +mesgin_extended_intr: + mvi INTSTAT,EXTENDED_MSG; /* let driver know */ + cmp RETURN_1,SEND_REJ je rej_mesgin; + cmp RETURN_1,SEND_MSG jne mesgin_done; +/* The kernel has setup a message to be sent */ + or SCSISIGO,ATNO,LASTPHASE; /* turn on ATNO */ + jmp mesgin_done; + +/* + * Is it a disconnect message? Set a flag in the SCB to remind us + * and await the bus going free. + */ +mesgin_disconnect: + or SCB_CONTROL,DISCONNECTED; +.if ( SCB_PAGING ) + call add_scb_to_disc_list; +.endif + jmp await_busfree; + +/* + * Save data pointers message: + * Copying RAM values back to SCB, for Save Data Pointers message, but + * only if we've actually been into a data phase to change them. This + * protects against bogus data in scratch ram and the residual counts + * since they are only initialized when we go into data_in or data_out. + */ +mesgin_sdptrs: + test SEQ_FLAGS, DPHASE jz mesgin_done; + mov SCB_SGCOUNT,SG_COUNT; + + /* The SCB SGPTR becomes the next one we'll download */ + mvi DINDEX, SCB_SGPTR; + mvi SG_NEXT call bcopy_4; + + /* The SCB DATAPTR0 becomes the current SHADDR */ + mvi DINDEX, SCB_DATAPTR; + mvi SHADDR call bcopy_4; + +/* + * Use the residual number since STCNT is corrupted by any message transfer. + */ + mvi SCB_RESID_DCNT call bcopy_3; + + jmp mesgin_done; + +/* + * Restore pointers message? Data pointers are recopied from the + * SCB anytime we enter a data phase for the first time, so all + * we need to do is clear the DPHASE flag and let the data phase + * code do the rest. + */ +mesgin_rdptrs: + and SEQ_FLAGS, ~DPHASE; /* + * We'll reload them + * the next time through + * the dataphase. + */ + jmp mesgin_done; + +/* + * Identify message? For a reconnecting target, this tells us the lun + * that the reconnection is for - find the correct SCB and switch to it, + * clearing the "disconnected" bit so we don't "find" it by accident later. + */ +mesgin_identify: + test A,0x78 jnz rej_mesgin; /*!DiscPriv|!LUNTAR|!Reserved*/ + and A,0x07; /* lun in lower three bits */ + or SAVED_TCL,A; /* SAVED_TCL should be complete now */ + mov SAVED_TCL call index_untagged_scb; + mov ARG_1, SINDIR; +.if ( SCB_PAGING ) + cmp ARG_1,SCB_LIST_NULL jne use_findSCB; +.else + cmp ARG_1,SCB_LIST_NULL je snoop_tag; + /* Directly index the SCB */ + mov SCBPTR,ARG_1; + test SCB_CONTROL,DISCONNECTED jz not_found; + jmp setup_SCB; +.endif +/* + * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. + * If we get one, we use the tag returned to find the proper + * SCB. With SCB paging, this requires using findSCB for both tagged + * and non-tagged transactions since the SCB may exist in any slot. + * If we're not using SCB paging, we can use the tag as the direct + * index to the SCB. + */ +snoop_tag: + mov NONE,SCSIDATL; /* ACK Identify MSG */ +snoop_tag_loop: + test SSTAT1,REQINIT jz snoop_tag_loop; + test SSTAT1, SCSIPERR jnz snoop_tag_loop; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGIN jne not_found; + cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found; +get_tag: + or SEQ_FLAGS, TAGGED_SCB; + mvi ARG_1 call inb_next; /* tag value */ +/* + * See if the tag is in range. The tag is < SCBCOUNT if we add + * the complement of SCBCOUNT to the incomming tag and there is + * no carry. + */ + mov A,COMP_SCBCOUNT; + add SINDEX,A,ARG_1; + jc not_found; + +.if ! ( SCB_PAGING ) +index_by_tag: + mov SCBPTR,ARG_1; + mov A, SAVED_TCL; + cmp SCB_TCL,A jne not_found; + test SCB_CONTROL,TAG_ENB jz not_found; + test SCB_CONTROL,DISCONNECTED jz not_found; +.else +/* + * Ensure that the SCB the tag points to is for an SCB transaction + * to the reconnecting target. + */ +use_findSCB: + mov ALLZEROS call findSCB; /* Have to search */ + cmp SINDEX, SCB_LIST_NULL je not_found; +.endif +setup_SCB: + and SCB_CONTROL,~DISCONNECTED; + or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */ + jmp mesgin_done; + +not_found: + mvi INTSTAT, NO_MATCH; + mvi MSG_BUS_DEV_RESET call mk_mesg; + jmp mesgin_done; + +/* + * Message reject? Let the kernel driver handle this. If we have an + * outstanding WDTR or SDTR negotiation, assume that it's a response from + * the target selecting 8bit or asynchronous transfer, otherwise just ignore + * it since we have no clue what it pertains to. + */ +mesgin_reject: + mvi INTSTAT, REJECT_MSG; + jmp mesgin_done; + +/* + * [ ADD MORE MESSAGE HANDLING HERE ] + */ + +/* + * Locking the driver out, build a one-byte message passed in SINDEX + * if there is no active message already. SINDEX is returned intact. + */ +mk_mesg: + mvi SEQCTL, PAUSEDIS|FASTMODE; + test MSG_LEN,0xff jz mk_mesg1; /* Should always succeed */ + + /* + * Hmmm. For some reason the mesg buffer is in use. + * Tell the driver. It should look at SINDEX to find + * out what we wanted to use the buffer for and resolve + * the conflict. + */ + mvi SEQCTL,FASTMODE; + mvi INTSTAT,MSG_BUFFER_BUSY; + +mk_mesg1: + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */ + mvi MSG_LEN,1; /* length = 1 */ + mov MSG_OUT,SINDEX; /* 1-byte message */ + mvi SEQCTL,FASTMODE ret; + +/* + * Functions to read data in Automatic PIO mode. + * + * According to Adaptec's documentation, an ACK is not sent on input from + * the target until SCSIDATL is read from. So we wait until SCSIDATL is + * latched (the usual way), then read the data byte directly off the bus + * using SCSIBUSL. When we have pulled the ATN line, or we just want to + * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI + * spec guarantees that the target will hold the data byte on the bus until + * we send our ACK. + * + * The assumption here is that these are called in a particular sequence, + * and that REQ is already set when inb_first is called. inb_{first,next} + * use the same calling convention as inb. + */ + +inb_next: + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ +inb_next_wait: + /* + * If there is a parity error, wait for the kernel to + * see the interrupt and prepare our message response + * before continuing. + */ + test SSTAT1, REQINIT jz inb_next_wait; + test SSTAT1, SCSIPERR jnz inb_next_wait; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; +inb_first: + mov DINDEX,SINDEX; + mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ +inb_last: + mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ + +mesgin_phasemis: +/* + * We expected to receive another byte, but the target changed phase + */ + mvi INTSTAT, MSGIN_PHASEMIS; + jmp ITloop; + +/* + * DMA data transfer. HADDR and HCNT must be loaded first, and + * SINDEX should contain the value to load DFCNTRL with - 0x3d for + * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared + * during initialization. + */ +dma: + mov DFCNTRL,SINDEX; +dma_loop: + test SSTAT0,DMADONE jnz dma_dmadone; + test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ +dma_phasemis: + test SSTAT0,SDONE jnz dma_checkfifo; + mov SINDEX,ALLZEROS; /* Notify caller of phasemiss */ + +/* + * We will be "done" DMAing when the transfer count goes to zero, or + * the target changes the phase (in light of this, it makes sense that + * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are + * doing a SCSI->Host transfer, the data FIFO should be flushed auto- + * magically on STCNT=0 or a phase change, so just wait for FIFO empty + * status. + */ +dma_checkfifo: + test DFCNTRL,DIRECTION jnz dma_fifoempty; +dma_fifoflush: + test DFSTATUS,FIFOEMP jz dma_fifoflush; + +dma_fifoempty: + /* Don't clobber an inprogress host data transfer */ + test DFSTATUS, MREQPEND jnz dma_fifoempty; +/* + * Now shut the DMA enables off and make sure that the DMA enables are + * actually off first lest we get an ILLSADDR. + */ +dma_dmadone: + and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); +dma_halt: + test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; +return: + ret; + +/* + * Assert that if we've been reselected, then we've seen an IDENTIFY + * message. + */ +assert: + test SEQ_FLAGS,RESELECTED jz return; /* reselected? */ + test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */ + + mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */ + +.if ( SCB_PAGING ) +/* + * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL) + * or by the SCBIDn ARG_1. The search begins at the SCB index passed in + * via SINDEX. If the SCB cannot be found, SINDEX will be SCB_LIST_NULL, + * otherwise, SCBPTR is set to the proper SCB. + */ +findSCB: + mov SCBPTR,SINDEX; /* switch to next SCB */ + mov A, ARG_1; /* Tag passed in ARG_1 */ + cmp SCB_TAG,A jne findSCB_loop; + test SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/ +findSCB_loop: + inc SINDEX; + mov A,SCBCOUNT; + cmp SINDEX,A jne findSCB; +/* + * We didn't find it. If we're paging, pull an SCB and DMA down the + * one we want. If we aren't paging or the SCB we dma down has the + * abort flag set, return not found. + */ + mov ALLZEROS call get_free_or_disc_scb; + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov ARG_1 call dma_scb; + test SCB_RESID_SGCNT, 0xff jz . + 2; + or SCB_CONTROL, MUST_DMAUP_SCB; + test SCB_CONTROL, ABORT_SCB jz return; +find_error: + mvi SINDEX, SCB_LIST_NULL ret; +foundSCB: + test SCB_CONTROL, ABORT_SCB jnz find_error; +rem_scb_from_disc_list: +/* Remove this SCB from the disconnection list */ + cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev; + mov SAVED_LINKPTR, SCB_PREV; + mov SCBPTR, SCB_NEXT; + mov SCB_PREV, SAVED_LINKPTR; + mov SCBPTR, SINDEX; +unlink_prev: + cmp SCB_PREV,SCB_LIST_NULL je rHead;/* At the head of the list */ + mov SAVED_LINKPTR, SCB_NEXT; + mov SCBPTR, SCB_PREV; + mov SCB_NEXT, SAVED_LINKPTR; + mov SCBPTR, SINDEX ret; +rHead: + mov DISCONNECTED_SCBH,SCB_NEXT ret; +.else + ret; +.endif + +set_stcnt_from_hcnt: + mov STCNT[0], HCNT[0]; + mov STCNT[1], HCNT[1]; + mov STCNT[2], HCNT[2] ret; + +bcopy_7: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; +bcopy_5: + mov DINDIR, SINDIR; +bcopy_4: + mov DINDIR, SINDIR; +bcopy_3: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; + mov DINDIR, SINDIR ret; + +dma_scb: + /* + * SCB index is in SINDEX. Determine the physical address in + * the host where this SCB is located and load HADDR with it. + */ + shr DINDEX, 3, SINDEX; + shl A, 5, SINDEX; + add HADDR[0], A, HSCB_ADDR[0]; + mov A, DINDEX; + adc HADDR[1], A, HSCB_ADDR[1]; + clr A; + adc HADDR[2], A, HSCB_ADDR[2]; + adc HADDR[3], A, HSCB_ADDR[3]; + /* Setup Count */ + mvi HCNT[0], 28; + clr HCNT[1]; + clr HCNT[2]; + mov DFCNTRL, DMAPARAMS; + test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; + /* Fill it with the SCB data */ +copy_scb_tofifo: + mvi SINDEX, SCB_CONTROL; + add A, 28, SINDEX; +copy_scb_tofifo_loop: + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + cmp SINDEX, A jne copy_scb_tofifo_loop; + or DFCNTRL, HDMAEN|FIFOFLUSH; +dma_scb_fromhost: + call dma_finish; + /* If we were putting the SCB, we are done */ + test DMAPARAMS, DIRECTION jz return; + mvi SCB_CONTROL call dfdat_in_7; + call dfdat_in_7_continued; + call dfdat_in_7_continued; + jmp dfdat_in_7_continued; +dfdat_in_7: + mov DINDEX,SINDEX; +dfdat_in_7_continued: + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT ret; + +/* + * Wait for DMA from host memory to data FIFO to complete, then disable + * DMA and wait for it to acknowledge that it's off. + */ +dma_finish: + test DFSTATUS,HDONE jz dma_finish; + /* Turn off DMA */ + and DFCNTRL, ~HDMAEN; + test DFCNTRL, HDMAEN jnz .; + ret; + +index_untagged_scb: + mov DINDEX, SINDEX; + shr DINDEX, 4; + and DINDEX, 0x03; /* Bottom two bits of tid */ + add DINDEX, SCB_BUSYTARGETS; + shr A, 6, SINDEX; /* Target ID divided by 4 */ + test SINDEX, SELBUSB jz index_untagged_scb2; + add A, 2; /* Add 2 positions */ +index_untagged_scb2: + mov SCBPTR, A; /* + * Select the SCB with this + * target's information. + */ + mov SINDEX, DINDEX ret; + +add_scb_to_free_list: + mov SCB_NEXT, FREE_SCBH; + mvi SCB_TAG, SCB_LIST_NULL; + mov FREE_SCBH, SCBPTR ret; + +.if ( SCB_PAGING ) +get_free_or_disc_scb: + cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; + cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; +return_error: + mvi SINDEX, SCB_LIST_NULL ret; +dequeue_disc_scb: + mov SCBPTR, DISCONNECTED_SCBH; +/* + * If we have a residual, then we are in the middle of some I/O + * and we have to send this SCB back up to the kernel so that the + * saved data pointers and residual information isn't lost. + */ + test SCB_CONTROL, MUST_DMAUP_SCB jz . + 3; + and SCB_CONTROL, ~MUST_DMAUP_SCB; + jmp dma_up_scb; + test SCB_RESID_SGCNT,0xff jnz dma_up_scb; + cmp SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb; +dma_up_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +unlink_disc_scb: + /* jmp instead of call since we want to return anyway */ + mov SCBPTR jmp rem_scb_from_disc_list; +dequeue_free_scb: + mov SCBPTR, FREE_SCBH; + mov FREE_SCBH, SCB_NEXT ret; + +add_scb_to_disc_list: +/* + * Link this SCB into the DISCONNECTED list. This list holds the + * candidates for paging out an SCB if one is needed for a new command. + * Modifying the disconnected list is a critical(pause dissabled) section. + */ + mvi SCB_PREV, SCB_LIST_NULL; + mov SCB_NEXT, DISCONNECTED_SCBH; + mov DISCONNECTED_SCBH, SCBPTR; + cmp SCB_NEXT,SCB_LIST_NULL je return; + mov SCBPTR,SCB_NEXT; + mov SCB_PREV,DISCONNECTED_SCBH; + mov SCBPTR,DISCONNECTED_SCBH ret; +.endif diff --git a/drivers/scsi/aic7xxx/aic7xxx_asm.c b/drivers/scsi/aic7xxx/aic7xxx_asm.c new file mode 100644 index 000000000000..7dc26c00a100 --- /dev/null +++ b/drivers/scsi/aic7xxx/aic7xxx_asm.c @@ -0,0 +1,482 @@ +/* + * Aic7xxx SCSI host adapter firmware asssembler + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: aic7xxx_asm.c,v 1.16 1997/03/18 19:18:39 gibbs Exp $ + */ +#include +#include + +#include +#include +#include +#include +#include + +#include "aic7xxx_asm.h" +#include "symbol.h" +#include "sequencer.h" + +static void usage __P((void)); +static void back_patch __P((void)); +static void output_code __P((FILE *ofile)); +static void output_listing __P((FILE *listfile, char *ifilename, + char *options)); +static struct patch *next_patch __P((struct patch *cur_patch, int options, + int instrptr)); + +struct path_list search_path; +int includes_search_curdir; +char *appname; +FILE *ofile; +char *ofilename; + +static STAILQ_HEAD(,instruction) seq_program; +static STAILQ_HEAD(, patch) patch_list; +symlist_t patch_options; + +#if DEBUG +extern int yy_flex_debug; +extern int yydebug; +#endif +extern FILE *yyin; +extern int yyparse __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int ch; + int retval; + char *inputfilename; + char *regfilename; + FILE *regfile; + char *listfilename; + FILE *listfile; + char *options; + + SLIST_INIT(&search_path); + STAILQ_INIT(&seq_program); + STAILQ_INIT(&patch_list); + SLIST_INIT(&patch_options); + includes_search_curdir = 1; + appname = *argv; + regfile = NULL; + listfile = NULL; + options = NULL; +#if DEBUG + yy_flex_debug = 0; +#endif + while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != EOF) { + switch(ch) { + case 'd': +#if DEBUG + if (strcmp(optarg, "s") == 0) + yy_flex_debug = 1; + else if (strcmp(optarg, "p") == 0) + yydebug = 1; +#else + stop("-d: Assembler not built with debugging " + "information", EX_SOFTWARE); +#endif + break; + case 'l': + /* Create a program listing */ + if ((listfile = fopen(optarg, "w")) == NULL) { + perror(optarg); + stop(NULL, EX_CANTCREAT); + } + listfilename = optarg; + break; + case 'n': + /* Don't complain about the -nostdinc directrive */ + if (strcmp(optarg, "ostdinc")) { + fprintf(stderr, "%s: Unknown option -%c%s\n", + appname, ch, optarg); + usage(); + /* NOTREACHED */ + } + break; + case 'o': + if ((ofile = fopen(optarg, "w")) == NULL) { + perror(optarg); + stop(NULL, EX_CANTCREAT); + } + ofilename = optarg; + break; + case 'O': + /* Patches to include in the listing */ + options = optarg; + break; + case 'r': + if ((regfile = fopen(optarg, "w")) == NULL) { + perror(optarg); + stop(NULL, EX_CANTCREAT); + } + regfilename = optarg; + break; + case 'I': + { + path_entry_t include_dir; + + if (strcmp(optarg, "-") == 0) { + if (includes_search_curdir == 0) { + fprintf(stderr, "%s: Warning - '-I-' " + "specified multiple " + "times\n", appname); + } + includes_search_curdir = 0; + for (include_dir = search_path.slh_first; + include_dir != NULL; + include_dir = include_dir->links.sle_next) + /* + * All entries before a '-I-' only + * apply to includes specified with + * quotes instead of "<>". + */ + include_dir->quoted_includes_only = 1; + } else { + include_dir = + (path_entry_t)malloc(sizeof(*include_dir)); + if (include_dir == NULL) { + perror(optarg); + stop(NULL, EX_OSERR); + } + include_dir->directory = strdup(optarg); + if (include_dir->directory == NULL) { + perror(optarg); + stop(NULL, EX_OSERR); + } + include_dir->quoted_includes_only = 0; + SLIST_INSERT_HEAD(&search_path, include_dir, + links); + } + break; + } + case '?': + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (argc != 1) { + fprintf(stderr, "%s: No input file specifiled\n", appname); + usage(); + /* NOTREACHED */ + } + + symtable_open(); + inputfilename = *argv; + include_file(*argv, SOURCE_FILE); + retval = yyparse(); + if (retval == 0) { + back_patch(); + if (ofile != NULL) + output_code(ofile); + if (regfile != NULL) + symtable_dump(regfile); + if (listfile != NULL) + output_listing(listfile, inputfilename, options); + } + + stop(NULL, 0); + /* NOTREACHED */ + return (0); +} + +static void +usage() +{ + + (void)fprintf(stderr, +"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file] + [-r register_output_file] [-l program_list_file] + [-O option_name[|options_name2]] input_file\n", + appname); + exit(EX_USAGE); +} + +static void +back_patch() +{ + struct instruction *cur_instr; + + for(cur_instr = seq_program.stqh_first; + cur_instr != NULL; + cur_instr = cur_instr->links.stqe_next) { + if (cur_instr->patch_label != NULL) { + struct ins_format3 *f3_instr; + u_int address; + + if (cur_instr->patch_label->type != LABEL) { + char buf[255]; + + snprintf(buf, sizeof(buf), + "Undefined label %s", + cur_instr->patch_label->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } + f3_instr = &cur_instr->format.format3; + address = ((f3_instr->opcode_addr & ADDR_HIGH_BIT) << 8) + | f3_instr->address; + address += cur_instr->patch_label->info.linfo->address; + f3_instr->opcode_addr &= ~ADDR_HIGH_BIT; + f3_instr->opcode_addr |= (address >> 8) & ADDR_HIGH_BIT; + f3_instr->address = address & 0xFF; + } + } +} + +static void +output_code(ofile) + FILE *ofile; +{ + struct instruction *cur_instr; + patch_t *cur_patch; + symbol_node_t *cur_node; + int instrcount; + + instrcount = 0; + fprintf(ofile, +"/* + * DO NOT EDIT - This file is automatically generated. + */\n"); + + fprintf(ofile, "static u_int8_t seqprog[] = {\n"); + for(cur_instr = seq_program.stqh_first; + cur_instr != NULL; + cur_instr = cur_instr->links.stqe_next) { + fprintf(ofile, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n", + cur_instr->format.bytes[0], + cur_instr->format.bytes[1], + cur_instr->format.bytes[2], + cur_instr->format.bytes[3]); + instrcount++; + } + fprintf(ofile, "};\n"); + + /* + * Output the patch list, option definitions first. + */ + for(cur_node = patch_options.slh_first; + cur_node != NULL; + cur_node = cur_node->links.sle_next) { + fprintf(ofile, "#define\t%-16s\t0x%x\n", cur_node->symbol->name, + cur_node->symbol->info.condinfo->value); + } + + fprintf(ofile, +"struct patch { + int options; + int negative; + int begin; + int end; +} patches[] = {\n"); + + for(cur_patch = patch_list.stqh_first; + cur_patch != NULL; + cur_patch = cur_patch->links.stqe_next) + + fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x },\n", + cur_patch->options, cur_patch->negative, cur_patch->begin, + cur_patch->end); + + fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x }\n};\n", + 0, 0, 0, 0); + + fprintf(stderr, "%s: %d instructions used\n", appname, instrcount); +} + +void +output_listing(listfile, ifilename, patches) + FILE *listfile; + char *ifilename; + char *patches; +{ + FILE *ifile; + int line; + struct instruction *cur_instr; + int instrcount; + int instrptr; + char buf[1024]; + patch_t *cur_patch; + char *option_spec; + int options; + + instrcount = 0; + instrptr = 0; + line = 1; + options = 1; /* All code outside of patch blocks */ + if ((ifile = fopen(ifilename, "r")) == NULL) { + perror(ifilename); + stop(NULL, EX_DATAERR); + } + + /* + * Determine which options to apply to this listing. + */ + while ((option_spec = strsep(&patches, "|")) != NULL) { + symbol_t *symbol; + + symbol = symtable_get(option_spec); + if (symbol->type != CONDITIONAL) { + stop("Invalid option specified in patch list for " + "program listing", EX_USAGE); + /* NOTREACHED */ + } + options |= symbol->info.condinfo->value; + } + + cur_patch = patch_list.stqh_first; + for(cur_instr = seq_program.stqh_first; + cur_instr != NULL; + cur_instr = cur_instr->links.stqe_next,instrcount++) { + + cur_patch = next_patch(cur_patch, options, instrcount); + if (cur_patch + && cur_patch->begin <= instrcount + && cur_patch->end > instrcount) + /* Don't count this instruction as it is in a patch + * that was removed. + */ + continue; + + while (line < cur_instr->srcline) { + fgets(buf, sizeof(buf), ifile); + fprintf(listfile, "\t\t%s", buf); + line++; + } + fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr, + cur_instr->format.bytes[0], + cur_instr->format.bytes[1], + cur_instr->format.bytes[2], + cur_instr->format.bytes[3]); + fgets(buf, sizeof(buf), ifile); + fprintf(listfile, "\t%s", buf); + line++; + instrptr++; + } + /* Dump the remainder of the file */ + while(fgets(buf, sizeof(buf), ifile) != NULL) + fprintf(listfile, "\t\t%s", buf); + + fclose(ifile); +} + +static struct patch * +next_patch(cur_patch, options, instrptr) + struct patch *cur_patch; + int options; + int instrptr; +{ + while(cur_patch != NULL) { + if (((cur_patch->options & options) != 0 + && cur_patch->negative == FALSE) + || ((cur_patch->options & options) == 0 + && cur_patch->negative == TRUE) + || (instrptr >= cur_patch->end)) { + /* + * Either we want to keep this section of code, + * or we have consumed this patch. Skip to the + * next patch. + */ + cur_patch = cur_patch->links.stqe_next; + } else + /* Found an okay patch */ + break; + } + return (cur_patch); +} + +/* + * Print out error information if appropriate, and clean up before + * terminating the program. + */ +void +stop(string, err_code) + const char *string; + int err_code; +{ + if (string != NULL) { + fprintf(stderr, "%s: ", appname); + if (yyfilename != NULL) { + fprintf(stderr, "Stopped at file %s, line %d - ", + yyfilename, yylineno); + } + fprintf(stderr, "%s\n", string); + } + + if (ofile != NULL) { + fclose(ofile); + if (err_code != 0) { + fprintf(stderr, "%s: Removing %s due to error\n", + appname, ofilename); + unlink(ofilename); + } + } + + symlist_free(&patch_options); + symtable_close(); + + exit(err_code); +} + +struct instruction * +seq_alloc() +{ + struct instruction *new_instr; + + new_instr = (struct instruction *)malloc(sizeof(struct instruction)); + if (new_instr == NULL) + stop("Unable to malloc instruction object", EX_SOFTWARE); + memset(new_instr, 0, sizeof(*new_instr)); + STAILQ_INSERT_TAIL(&seq_program, new_instr, links); + new_instr->srcline = yylineno; + return new_instr; +} + +patch_t * +patch_alloc() +{ + patch_t *new_patch; + + new_patch = (patch_t *)malloc(sizeof(patch_t)); + if (new_patch == NULL) + stop("Unable to malloc patch object", EX_SOFTWARE); + memset(new_patch, 0, sizeof(*new_patch)); + STAILQ_INSERT_TAIL(&patch_list, new_patch, links); + return new_patch; +} diff --git a/drivers/scsi/aic7xxx/aic7xxx_asm.h b/drivers/scsi/aic7xxx/aic7xxx_asm.h new file mode 100644 index 000000000000..1c9424b11d0f --- /dev/null +++ b/drivers/scsi/aic7xxx/aic7xxx_asm.h @@ -0,0 +1,69 @@ +/* + * Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: aic7xxx_asm.h,v 1.2 1997/03/16 07:28:30 gibbs Exp $ + */ + +#include "bsd_q.h" +#include + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +typedef struct path_entry { + char *directory; + int quoted_includes_only; + SLIST_ENTRY(path_entry) links; +} *path_entry_t; + +typedef enum { + QUOTED_INCLUDE, + BRACKETED_INCLUDE, + SOURCE_FILE +} include_type; + +SLIST_HEAD(path_list, path_entry); + +extern struct path_list search_path; +extern struct symlist patch_options; +extern int includes_search_curdir; /* False if we've seen -I- */ +extern char *appname; +extern int yylineno; +extern char *yyfilename; + +void stop __P((const char *errstring, int err_code)); +void include_file __P((char *file_name, include_type type)); +struct instruction *seq_alloc __P((void)); +struct patch *patch_alloc __P((void)); diff --git a/drivers/scsi/aic7xxx/bsd_q.h b/drivers/scsi/aic7xxx/bsd_q.h new file mode 100644 index 000000000000..5187335629fb --- /dev/null +++ b/drivers/scsi/aic7xxx/bsd_q.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.3 (Berkeley) 12/13/93 + */ + +/* + * Define the BSD singly linked queues which Linux doesn't + * define (yet). + */ + +#include + +#if !defined(SLIST_HEAD) && !defined(__BSD_Q__) + +#define __BSD_Q__ + +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_INIT(head) { \ + (head)->slh_first = NULL; \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} + +#define SLIST_INSERT_HEAD(head, elm, field) { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE_HEAD(head, field) { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} + +#define SLIST_REMOVE(head, elm, type, field) { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while( curelm->field.sle_next != (elm) ) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} + +/* + * Singly-linked Tail queue definitions. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} + +#define STAILQ_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} + +#define STAILQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) { \ + if (((elm)->field.stqe_next = (tqelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (tqelm)->field.stqe_next = (elm); \ +} + +#define STAILQ_REMOVE_HEAD(head, field) { \ + if (((head)->stqh_first = \ + (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} + +#define STAILQ_REMOVE(head, elm, type, field) { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD(head, field); \ + } \ + else { \ + struct type *curelm = (head)->stqh_first; \ + while( curelm->field.stqe_next != (elm) ) \ + curelm = curelm->field.stqe_next; \ + if((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} + +#endif diff --git a/drivers/scsi/aic7xxx/gram.y b/drivers/scsi/aic7xxx/gram.y new file mode 100644 index 000000000000..146e8a752088 --- /dev/null +++ b/drivers/scsi/aic7xxx/gram.y @@ -0,0 +1,1305 @@ +%{ +/* + * Parser for the Aic7xxx SCSI Host adapter sequencer assembler. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: gram.y,v 1.1 1997/03/16 07:08:16 gibbs Exp $ + */ + +#include +#include +#include +#include + +#include + +#include "bsd_q.h" + +#include "aic7xxx_asm.h" +#include "symbol.h" +#include "sequencer.h" + +int yylineno; +char *yyfilename; +static symbol_t *cur_symbol; +static symtype cur_symtype; +static symbol_t *accumulator; +static symbol_ref_t allones; +static symbol_ref_t allzeros; +static symbol_ref_t none; +static symbol_ref_t sindex; +static int instruction_ptr; +static int sram_or_scb_offset; +static patch_t *cur_patch; + +static void process_bitmask __P((int mask_type, symbol_t *sym, int mask)); +static void initialize_symbol __P((symbol_t *symbol)); +static void process_register __P((symbol_t **p_symbol)); +static void format_1_instr __P((int opcode, symbol_ref_t *dest, + expression_t *immed, symbol_ref_t *src, + int ret)); +static void format_2_instr __P((int opcode, symbol_ref_t *dest, + expression_t *places, symbol_ref_t *src, + int ret)); +static void format_3_instr __P((int opcode, symbol_ref_t *src, + expression_t *immed, symbol_ref_t *address)); +static void test_readable_symbol __P((symbol_t *symbol)); +static void test_writable_symbol __P((symbol_t *symbol)); +static void type_check __P((symbol_t *symbol, expression_t *expression, + int and_op)); +static void make_expression __P((expression_t *immed, int value)); +static void add_conditional __P((symbol_t *symbol)); + +#define YYDEBUG 1 +#define SRAM_SYMNAME "SRAM_BASE" +#define SCB_SYMNAME "SCB_BASE" +%} + +%union { + int value; + char *str; + symbol_t *sym; + symbol_ref_t sym_ref; + expression_t expression; +} + +%token T_REGISTER + +%token T_CONST + +%token T_SCB + +%token T_SRAM + +%token T_ALIAS + +%token T_SIZE + +%token T_ADDRESS + +%token T_ACCESS_MODE + +%token T_MODE + +%token T_BIT + +%token T_MASK + +%token T_NUMBER + +%token T_PATH + +%token T_EOF T_INCLUDE + +%token T_SHR T_SHL T_ROR T_ROL + +%token T_MVI T_MOV T_CLR + +%token T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL + +%token T_ADD T_ADC + +%token T_INC T_DEC + +%token T_STC T_CLC + +%token T_CMP T_XOR + +%token T_TEST T_AND + +%token T_OR + +%token T_RET + +%token T_NOP + +%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX + +%token T_A + +%token T_SYMBOL + +%token T_NL + +%token T_IF T_ELSE T_ENDIF + +%type reg_symbol address destination source opt_source + +%type expression immediate immediate_or_a + +%type ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne + +%left '|' +%left '&' +%left '+' '-' +%right '~' +%nonassoc UMINUS +%% + +program: + include +| program include +| register +| program register +| constant +| program constant +| scratch_ram +| program scratch_ram +| scb +| program scb +| label +| program label +| conditional +| program conditional +| code +| program code +; + +include: + T_INCLUDE '<' T_PATH '>' + { include_file($3, BRACKETED_INCLUDE); } +| T_INCLUDE '"' T_PATH '"' + { include_file($3, QUOTED_INCLUDE); } +; + +register: + T_REGISTER { cur_symtype = REGISTER; } reg_definition +; + +reg_definition: + T_SYMBOL '{' + { + if ($1->type != UNINITIALIZED) { + stop("Register multiply defined", EX_DATAERR); + /* NOTREACHED */ + } + cur_symbol = $1; + cur_symbol->type = cur_symtype; + initialize_symbol(cur_symbol); + } + reg_attribute_list + '}' + { + /* + * Default to allowing everything in for registers + * with no bit or mask definitions. + */ + if (cur_symbol->info.rinfo->valid_bitmask == 0) + cur_symbol->info.rinfo->valid_bitmask = 0xFF; + + if (cur_symbol->info.rinfo->size == 0) + cur_symbol->info.rinfo->size = 1; + + /* + * This might be useful for registers too. + */ + if (cur_symbol->type != REGISTER) { + if (cur_symbol->info.rinfo->address == 0) + cur_symbol->info.rinfo->address = + sram_or_scb_offset; + sram_or_scb_offset += + cur_symbol->info.rinfo->size; + } + cur_symbol = NULL; + } +; + +reg_attribute_list: + reg_attribute +| reg_attribute_list reg_attribute +; + +reg_attribute: + reg_address +| size +| access_mode +| bit_defn +| mask_defn +| alias +| accumulator +| allones +| allzeros +| none +| sindex +; + +reg_address: + T_ADDRESS T_NUMBER + { + cur_symbol->info.rinfo->address = $2; + } +; + +size: + T_SIZE T_NUMBER + { + cur_symbol->info.rinfo->size = $2; + } +; + +access_mode: + T_ACCESS_MODE T_MODE + { + cur_symbol->info.rinfo->mode = $2; + } +; + +bit_defn: + T_BIT T_SYMBOL T_NUMBER + { + process_bitmask(BIT, $2, $3); + } +; + +mask_defn: + T_MASK T_SYMBOL expression + { + process_bitmask(MASK, $2, $3.value); + } +; + +alias: + T_ALIAS T_SYMBOL + { + if ($2->type != UNINITIALIZED) { + stop("Re-definition of register alias", + EX_DATAERR); + /* NOTREACHED */ + } + $2->type = ALIAS; + initialize_symbol($2); + $2->info.ainfo->parent = cur_symbol; + } +; + +accumulator: + T_ACCUM + { + if (accumulator != NULL) { + stop("Only one accumulator definition allowed", + EX_DATAERR); + /* NOTREACHED */ + } + accumulator = cur_symbol; + } +; + +allones: + T_ALLONES + { + if (allones.symbol != NULL) { + stop("Only one definition of allones allowed", + EX_DATAERR); + /* NOTREACHED */ + } + allones.symbol = cur_symbol; + } +; + +allzeros: + T_ALLZEROS + { + if (allzeros.symbol != NULL) { + stop("Only one definition of allzeros allowed", + EX_DATAERR); + /* NOTREACHED */ + } + allzeros.symbol = cur_symbol; + } +; + +none: + T_NONE + { + if (none.symbol != NULL) { + stop("Only one definition of none allowed", + EX_DATAERR); + /* NOTREACHED */ + } + none.symbol = cur_symbol; + } +; + +sindex: + T_SINDEX + { + if (sindex.symbol != NULL) { + stop("Only one definition of sindex allowed", + EX_DATAERR); + /* NOTREACHED */ + } + sindex.symbol = cur_symbol; + } +; + +expression: + expression '|' expression + { + $$.value = $1.value | $3.value; + symlist_merge(&$$.referenced_syms, + &$1.referenced_syms, + &$3.referenced_syms); + } +| expression '&' expression + { + $$.value = $1.value & $3.value; + symlist_merge(&$$.referenced_syms, + &$1.referenced_syms, + &$3.referenced_syms); + } +| expression '+' expression + { + $$.value = $1.value + $3.value; + symlist_merge(&$$.referenced_syms, + &$1.referenced_syms, + &$3.referenced_syms); + } +| expression '-' expression + { + $$.value = $1.value - $3.value; + symlist_merge(&($$.referenced_syms), + &($1.referenced_syms), + &($3.referenced_syms)); + } +| '(' expression ')' + { + $$ = $2; + } +| '~' expression + { + $$ = $2; + $$.value = (~$$.value) & 0xFF; + } +| '-' expression %prec UMINUS + { + $$ = $2; + $$.value = -$$.value; + } +| T_NUMBER + { + $$.value = $1; + SLIST_INIT(&$$.referenced_syms); + } +| T_SYMBOL + { + symbol_t *symbol; + + symbol = $1; + switch (symbol->type) { + case ALIAS: + symbol = $1->info.ainfo->parent; + case REGISTER: + case SCBLOC: + case SRAMLOC: + $$.value = symbol->info.rinfo->address; + break; + case MASK: + case BIT: + $$.value = symbol->info.minfo->mask; + break; + case CONST: + $$.value = symbol->info.cinfo->value; + break; + case UNINITIALIZED: + default: + { + char buf[255]; + + snprintf(buf, sizeof(buf), + "Undefined symbol %s referenced", + symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + break; + } + } + SLIST_INIT(&$$.referenced_syms); + symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD); + } +; + +constant: + T_CONST T_SYMBOL T_NUMBER + { + if ($2->type != UNINITIALIZED) { + stop("Re-definition of constant variable", + EX_DATAERR); + /* NOTREACHED */ + } + $2->type = CONST; + initialize_symbol($2); + $2->info.cinfo->value = $3; + $2->info.cinfo->define = $1; + } +; + +scratch_ram: + T_SRAM '{' + { + cur_symbol = symtable_get(SRAM_SYMNAME); + cur_symtype = SRAMLOC; + if (cur_symbol->type != UNINITIALIZED) { + stop("Only one SRAM definition allowed", + EX_DATAERR); + /* NOTREACHED */ + } + cur_symbol->type = SRAMLOC; + initialize_symbol(cur_symbol); + } + reg_address + { + sram_or_scb_offset = cur_symbol->info.rinfo->address; + } + scb_or_sram_reg_list + '}' + { + cur_symbol = NULL; + } +; + +scb: + T_SCB '{' + { + cur_symbol = symtable_get(SCB_SYMNAME); + cur_symtype = SCBLOC; + if (cur_symbol->type != UNINITIALIZED) { + stop("Only one SRAM definition allowed", + EX_SOFTWARE); + /* NOTREACHED */ + } + cur_symbol->type = SCBLOC; + initialize_symbol(cur_symbol); + } + reg_address + { + sram_or_scb_offset = cur_symbol->info.rinfo->address; + } + scb_or_sram_reg_list + '}' + { + cur_symbol = NULL; + } +; + +scb_or_sram_reg_list: + reg_definition +| scb_or_sram_reg_list reg_definition +; + +reg_symbol: + T_SYMBOL + { + process_register(&$1); + $$.symbol = $1; + $$.offset = 0; + } +| T_SYMBOL '[' T_NUMBER ']' + { + process_register(&$1); + if (($3 + 1) > $1->info.rinfo->size) { + stop("Accessing offset beyond range of register", + EX_DATAERR); + /* NOTREACHED */ + } + $$.symbol = $1; + $$.offset = $3; + } +| T_A + { + if (accumulator == NULL) { + stop("No accumulator has been defined", EX_DATAERR); + /* NOTREACHED */ + } + $$.symbol = accumulator; + $$.offset = 0; + } +; + +destination: + reg_symbol + { + test_writable_symbol($1.symbol); + $$ = $1; + } +; + +immediate: + expression + { $$ = $1; } +; + +immediate_or_a: + expression + { + $$ = $1; + } +| T_A + { + SLIST_INIT(&$$.referenced_syms); + $$.value = 0; + } +; + +source: + reg_symbol + { + test_readable_symbol($1.symbol); + $$ = $1; + } +; + +opt_source: + { + $$.symbol = NULL; + $$.offset = 0; + } +| ',' source + { $$ = $2; } +; + +ret: + { $$ = 0; } +| T_RET + { $$ = 1; } +; + +label: + T_SYMBOL ':' + { + if ($1->type != UNINITIALIZED) { + stop("Program label multiply defined", EX_DATAERR); + /* NOTREACHED */ + } + $1->type = LABEL; + initialize_symbol($1); + $1->info.linfo->address = instruction_ptr; + } +; + +address: + T_SYMBOL + { + $$.symbol = $1; + $$.offset = 0; + } +| T_SYMBOL '+' T_NUMBER + { + $$.symbol = $1; + $$.offset = $3; + } +| T_SYMBOL '-' T_NUMBER + { + $$.symbol = $1; + $$.offset = -$3; + } +| '.' + { + $$.symbol = NULL; + $$.offset = 0; + } +| '.' '+' T_NUMBER + { + $$.symbol = NULL; + $$.offset = $3; + } +| '.' '-' T_NUMBER + { + $$.symbol = NULL; + $$.offset = -$3; + } +; + +conditional: + T_IF + { + if (cur_patch != NULL) { + stop("Nested .if directive", EX_DATAERR); + /* NOTREACHED */ + } + cur_patch = patch_alloc(); + cur_patch->begin = instruction_ptr; + } + option_list +; + +conditional: + T_ELSE + { + patch_t *next_patch; + + if (cur_patch == NULL) { + stop(".else outsize of .if", EX_DATAERR); + /* NOTREACHED */ + } + cur_patch->end = instruction_ptr; + next_patch = patch_alloc(); + next_patch->options = cur_patch->options; + next_patch->negative = cur_patch->negative ? FALSE : TRUE; + cur_patch = next_patch; + cur_patch->begin = instruction_ptr; + } +; + +conditional: + T_ENDIF + { + if (cur_patch == NULL) { + stop(".endif outsize of .if", EX_DATAERR); + /* NOTREACHED */ + } + cur_patch->end = instruction_ptr; + cur_patch = NULL; + } +; + +option_list: + '(' option_symbol_list ')' +| '!' option_list + { + cur_patch->negative = cur_patch->negative ? FALSE : TRUE; + } +; + +option_symbol_list: + T_SYMBOL + { + add_conditional($1); + } +| option_list '|' T_SYMBOL + { + add_conditional($3); + } +; + +f1_opcode: + T_AND { $$ = AIC_OP_AND; } +| T_XOR { $$ = AIC_OP_XOR; } +| T_ADD { $$ = AIC_OP_ADD; } +| T_ADC { $$ = AIC_OP_ADC; } +; + +code: + f1_opcode destination ',' immediate_or_a opt_source ret ';' + { + format_1_instr($1, &$2, &$4, &$5, $6); + } +; + +code: + T_OR reg_symbol ',' immediate_or_a opt_source ret ';' + { + format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6); + } +; + +code: + T_INC destination opt_source ret ';' + { + expression_t immed; + + make_expression(&immed, 1); + format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4); + } +; + +code: + T_DEC destination opt_source ret ';' + { + expression_t immed; + + make_expression(&immed, -1); + format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4); + } +; + +code: + T_CLC ret ';' + { + expression_t immed; + + make_expression(&immed, -1); + format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2); + } +| T_CLC T_MVI destination ',' immediate_or_a ret ';' + { + format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6); + } +; + +code: + T_STC ret ';' + { + expression_t immed; + + make_expression(&immed, 1); + format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2); + } +| T_STC destination ret ';' + { + expression_t immed; + + make_expression(&immed, 1); + format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3); + } +; + +code: + T_MOV destination ',' source ret ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &$2, &immed, &$4, $5); + } +; + +code: + T_MVI destination ',' immediate_or_a ret ';' + { + format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5); + } +; + +code: + T_CLR destination ret ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3); + } +; + +code: + T_NOP ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, FALSE); + } +; + +code: + T_RET ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE); + } +; + + /* + * This grammer differs from the one in the aic7xxx + * reference manual since the grammer listed there is + * ambiguous and causes a shift/reduce conflict. + * It also seems more logical as the "immediate" + * argument is listed as the second arg like the + * other formats. + */ + +f2_opcode: + T_SHL { $$ = AIC_OP_SHL; } +| T_SHR { $$ = AIC_OP_SHR; } +| T_ROL { $$ = AIC_OP_ROL; } +| T_ROR { $$ = AIC_OP_ROR; } +; + +code: + f2_opcode destination ',' expression opt_source ret ';' + { + format_2_instr($1, &$2, &$4, &$5, $6); + } +; + +jmp_jc_jnc_call: + T_JMP { $$ = AIC_OP_JMP; } +| T_JC { $$ = AIC_OP_JC; } +| T_JNC { $$ = AIC_OP_JNC; } +| T_CALL { $$ = AIC_OP_CALL; } +; + +jz_jnz: + T_JZ { $$ = AIC_OP_JZ; } +| T_JNZ { $$ = AIC_OP_JNZ; } +; + +je_jne: + T_JE { $$ = AIC_OP_JE; } +| T_JNE { $$ = AIC_OP_JNE; } +; + +code: + jmp_jc_jnc_call address ';' + { + expression_t immed; + + make_expression(&immed, 0); + format_3_instr($1, &sindex, &immed, &$2); + } +; + +code: + T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';' + { + format_3_instr($5, &$2, &$4, &$6); + } +; + +code: + T_TEST source ',' immediate_or_a jz_jnz address ';' + { + format_3_instr($5, &$2, &$4, &$6); + } +; + +code: + T_CMP source ',' immediate_or_a je_jne address ';' + { + format_3_instr($5, &$2, &$4, &$6); + } +; + +code: + T_MOV source jmp_jc_jnc_call address ';' + { + expression_t immed; + + make_expression(&immed, 0); + format_3_instr($3, &$2, &immed, &$4); + } +; + +code: + T_MVI immediate jmp_jc_jnc_call address ';' + { + format_3_instr($3, &allzeros, &$2, &$4); + } +; + +%% + +static void +process_bitmask(mask_type, sym, mask) + int mask_type; + symbol_t *sym; + int mask; +{ + /* + * Add the current register to its + * symbol list, if it already exists, + * warn if we are setting it to a + * different value, or in the bit to + * the "allowed bits" of this register. + */ + if (sym->type == UNINITIALIZED) { + sym->type = mask_type; + initialize_symbol(sym); + if (mask_type == BIT) { + if (mask == 0) { + stop("Bitmask with no bits set", EX_DATAERR); + /* NOTREACHED */ + } + if ((mask & ~(0x01 << (ffs(mask) - 1))) != 0) { + stop("Bitmask with more than one bit set", + EX_DATAERR); + /* NOTREACHED */ + } + } + sym->info.minfo->mask = mask; + } else if (sym->type != mask_type) { + stop("Bit definition mirrors a definition of the same " + " name, but a different type", EX_DATAERR); + /* NOTREACHED */ + } else if (mask != sym->info.minfo->mask) { + stop("Bitmask redefined with a conflicting value", EX_DATAERR); + /* NOTREACHED */ + } + /* Fail if this symbol is already listed */ + if (symlist_search(&(sym->info.minfo->symrefs), + cur_symbol->name) != NULL) { + stop("Bitmask defined multiple times for register", EX_DATAERR); + /* NOTREACHED */ + } + symlist_add(&(sym->info.minfo->symrefs), cur_symbol, + SYMLIST_INSERT_HEAD); + cur_symbol->info.rinfo->valid_bitmask |= mask; + cur_symbol->info.rinfo->typecheck_masks = TRUE; +} + +static void +initialize_symbol(symbol) + symbol_t *symbol; +{ + switch (symbol->type) { + case UNINITIALIZED: + stop("Call to initialize_symbol with type field unset", + EX_SOFTWARE); + /* NOTREACHED */ + break; + case REGISTER: + case SRAMLOC: + case SCBLOC: + symbol->info.rinfo = + (struct reg_info *)malloc(sizeof(struct reg_info)); + if (symbol->info.rinfo == NULL) { + stop("Can't create register info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.rinfo, 0, + sizeof(struct reg_info)); + break; + case ALIAS: + symbol->info.ainfo = + (struct alias_info *)malloc(sizeof(struct alias_info)); + if (symbol->info.ainfo == NULL) { + stop("Can't create alias info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.ainfo, 0, + sizeof(struct alias_info)); + break; + case MASK: + case BIT: + symbol->info.minfo = + (struct mask_info *)malloc(sizeof(struct mask_info)); + if (symbol->info.minfo == NULL) { + stop("Can't create bitmask info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.minfo, 0, sizeof(struct mask_info)); + SLIST_INIT(&(symbol->info.minfo->symrefs)); + break; + case CONST: + symbol->info.cinfo = + (struct const_info *)malloc(sizeof(struct const_info)); + if (symbol->info.cinfo == NULL) { + stop("Can't create alias info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.cinfo, 0, + sizeof(struct const_info)); + break; + case LABEL: + symbol->info.linfo = + (struct label_info *)malloc(sizeof(struct label_info)); + if (symbol->info.linfo == NULL) { + stop("Can't create label info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.linfo, 0, + sizeof(struct label_info)); + break; + case CONDITIONAL: + symbol->info.condinfo = + (struct cond_info *)malloc(sizeof(struct cond_info)); + if (symbol->info.condinfo == NULL) { + stop("Can't create conditional info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.condinfo, 0, + sizeof(struct cond_info)); + break; + default: + stop("Call to initialize_symbol with invalid symbol type", + EX_SOFTWARE); + /* NOTREACHED */ + break; + } +} + +static void +process_register(p_symbol) + symbol_t **p_symbol; +{ + char buf[255]; + symbol_t *symbol = *p_symbol; + + if (symbol->type == UNINITIALIZED) { + snprintf(buf, sizeof(buf), "Undefined register %s", + symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } else if (symbol->type == ALIAS) { + *p_symbol = symbol->info.ainfo->parent; + } else if ((symbol->type != REGISTER) + && (symbol->type != SCBLOC) + && (symbol->type != SRAMLOC)) { + snprintf(buf, sizeof(buf), + "Specified symbol %s is not a register", + symbol->name); + stop(buf, EX_DATAERR); + } +} + +static void +format_1_instr(opcode, dest, immed, src, ret) + int opcode; + symbol_ref_t *dest; + expression_t *immed; + symbol_ref_t *src; + int ret; +{ + struct instruction *instr; + struct ins_format1 *f1_instr; + + if (src->symbol == NULL) + src = dest; + + /* Test register permissions */ + test_writable_symbol(dest->symbol); + test_readable_symbol(src->symbol); + + /* Ensure that immediate makes sense for this destination */ + type_check(dest->symbol, immed, opcode); + + /* Allocate sequencer space for the instruction and fill it out */ + instr = seq_alloc(); + f1_instr = &instr->format.format1; + f1_instr->opcode_ret = (opcode << 1) | (ret ? RETURN_BIT : 0); + f1_instr->destination = dest->symbol->info.rinfo->address + + dest->offset; + f1_instr->source = src->symbol->info.rinfo->address + + src->offset; + f1_instr->immediate = immed->value; + symlist_free(&immed->referenced_syms); + instruction_ptr++; +} + +static void +format_2_instr(opcode, dest, places, src, ret) + int opcode; + symbol_ref_t *dest; + expression_t *places; + symbol_ref_t *src; + int ret; +{ + struct instruction *instr; + struct ins_format2 *f2_instr; + u_int8_t shift_control; + + if (src->symbol == NULL) + src = dest; + + /* Test register permissions */ + test_writable_symbol(dest->symbol); + test_readable_symbol(src->symbol); + + /* Allocate sequencer space for the instruction and fill it out */ + instr = seq_alloc(); + f2_instr = &instr->format.format2; + f2_instr->opcode_ret = (AIC_OP_ROL << 1) | (ret ? RETURN_BIT : 0); + f2_instr->destination = dest->symbol->info.rinfo->address + + dest->offset; + f2_instr->source = src->symbol->info.rinfo->address + + src->offset; + if (places->value > 8 || places->value <= 0) { + stop("illegal shift value", EX_DATAERR); + /* NOTREACHED */ + } + switch (opcode) { + case AIC_OP_SHL: + if (places->value == 8) + shift_control = 0xf0; + else + shift_control = (places->value << 4) | places->value; + break; + case AIC_OP_SHR: + if (places->value == 8) { + shift_control = 0xf8; + } else { + shift_control = (places->value << 4) + | (8 - places->value) + | 0x08; + } + break; + case AIC_OP_ROL: + shift_control = places->value & 0x7; + break; + case AIC_OP_ROR: + shift_control = (8 - places->value) | 0x08; + break; + default: + shift_control = 0; /* Quiet Compiler */ + stop("Invalid shift operation specified", EX_SOFTWARE); + /* NOTREACHED */ + break; + }; + f2_instr->shift_control = shift_control; + symlist_free(&places->referenced_syms); + instruction_ptr++; +} + +static void +format_3_instr(opcode, src, immed, address) + int opcode; + symbol_ref_t *src; + expression_t *immed; + symbol_ref_t *address; +{ + struct instruction *instr; + struct ins_format3 *f3_instr; + int addr; + + /* Test register permissions */ + test_readable_symbol(src->symbol); + + /* Ensure that immediate makes sense for this source */ + type_check(src->symbol, immed, opcode); + + /* Allocate sequencer space for the instruction and fill it out */ + instr = seq_alloc(); + f3_instr = &instr->format.format3; + if (address->symbol == NULL) { + /* 'dot' referrence. Use the current instruction pointer */ + addr = instruction_ptr + address->offset; + } else if (address->symbol->type == UNINITIALIZED) { + /* forward reference */ + addr = address->offset; + instr->patch_label = address->symbol; + } else + addr = address->symbol->info.linfo->address + address->offset; + f3_instr->opcode_addr = (opcode << 1) + | ((addr >> 8) & 0x01); + f3_instr->address = addr & 0xff; + f3_instr->source = src->symbol->info.rinfo->address + + src->offset; + f3_instr->immediate = immed->value; + symlist_free(&immed->referenced_syms); + instruction_ptr++; +} + +static void +test_readable_symbol(symbol) + symbol_t *symbol; +{ + if (symbol->info.rinfo->mode == WO) { + stop("Write Only register specified as source", + EX_DATAERR); + /* NOTREACHED */ + } +} + +static void +test_writable_symbol(symbol) + symbol_t *symbol; +{ + if (symbol->info.rinfo->mode == RO) { + stop("Read Only register specified as destination", + EX_DATAERR); + /* NOTREACHED */ + } +} + +static void +type_check(symbol, expression, opcode) + symbol_t *symbol; + expression_t *expression; + int opcode; +{ + symbol_node_t *node; + int and_op; + char buf[255]; + + and_op = FALSE; + if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ) + and_op = TRUE; + /* + * Make sure that we aren't attempting to write something + * that hasn't been defined. If this is an and operation, + * this is a mask, so "undefined" bits are okay. + */ + if (and_op == FALSE + && (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) { + snprintf(buf, sizeof(buf), + "Invalid bit(s) 0x%x in immediate written to %s", + expression->value & ~symbol->info.rinfo->valid_bitmask, + symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } + + /* + * Now make sure that all of the symbols referenced by the + * expression are defined for this register. + */ + if(symbol->info.rinfo->typecheck_masks != FALSE) { + for(node = expression->referenced_syms.slh_first; + node != NULL; + node = node->links.sle_next) { + if ((node->symbol->type == MASK + || node->symbol->type == BIT) + && symlist_search(&node->symbol->info.minfo->symrefs, + symbol->name) == NULL) { + snprintf(buf, sizeof(buf), + "Invalid bit or mask %s " + "for register %s", + node->symbol->name, symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } + } + } +} + +static void +make_expression(immed, value) + expression_t *immed; + int value; +{ + SLIST_INIT(&immed->referenced_syms); + immed->value = value & 0xff; +} + +static void +add_conditional(symbol) + symbol_t *symbol; +{ + static int numoptions = 1; + + if (symbol->type == UNINITIALIZED) { + symbol->type = CONDITIONAL; + initialize_symbol(symbol); + symbol->info.condinfo->value = 0x01 << numoptions++; + symlist_add(&patch_options, symbol, SYMLIST_INSERT_HEAD); + } else if (symbol->type != CONDITIONAL) { + stop("Conditional symbol mirrors other symbol", + EX_DATAERR); + /* NOTREACHED */ + } + cur_patch->options |= symbol->info.condinfo->value; +} + +void +yyerror(string) + const char *string; +{ + stop(string, EX_DATAERR); +} diff --git a/drivers/scsi/aic7xxx/scan.l b/drivers/scsi/aic7xxx/scan.l new file mode 100644 index 000000000000..640d3833a5fb --- /dev/null +++ b/drivers/scsi/aic7xxx/scan.l @@ -0,0 +1,248 @@ +%{ +/* + * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: scan.l,v 1.2 1997/03/16 17:31:59 bde Exp $ + */ + +#include + +#include +#include +#include +#include +#include + +#include "bsd_q.h" +#include "aic7xxx_asm.h" +#include "symbol.h" +#include "y.tab.h" +%} + +PATH [-/A-Za-z0-9_.]*[./][-/A-Za-z0-9_.]* +WORD [A-Za-z_][-A-Za-z_0-9]* +SPACE [ \t]+ + +%x COMMENT + +%% +\n { ++yylineno; } +"/*" { BEGIN COMMENT; /* Enter comment eating state */ } +"/*" { fprintf(stderr, "Warning! Comment within comment."); } +\n { ++yylineno; } +[^*/\n]* ; +"*"+[^*/\n]* ; +"/"+[^*/\n]* ; +"*"+"/" { BEGIN INITIAL; } + +{SPACE} ; + + /* Register/SCB/SRAM definition keywords */ +register { return T_REGISTER; } +const { yylval.value = FALSE; return T_CONST; } +address { return T_ADDRESS; } +access_mode { return T_ACCESS_MODE; } +RW|RO|WO { + if (strcmp(yytext, "RW") == 0) + yylval.value = RW; + else if (strcmp(yytext, "RO") == 0) + yylval.value = RO; + else + yylval.value = WO; + return T_MODE; + } +bit { return T_BIT; } +mask { return T_MASK; } +alias { return T_ALIAS; } +size { return T_SIZE; } +scb { return T_SCB; } +scratch_ram { return T_SRAM; } +accumulator { return T_ACCUM; } +allones { return T_ALLONES; } +allzeros { return T_ALLZEROS; } +none { return T_NONE; } +sindex { return T_SINDEX; } +A { return T_A; } + + /* Opcodes */ +shl { return T_SHL; } +shr { return T_SHR; } +ror { return T_ROR; } +rol { return T_ROL; } +mvi { return T_MVI; } +mov { return T_MOV; } +clr { return T_CLR; } +jmp { return T_JMP; } +jc { return T_JC; } +jnc { return T_JNC; } +je { return T_JE; } +jne { return T_JNE; } +jz { return T_JZ; } +jnz { return T_JNZ; } +call { return T_CALL; } +add { return T_ADD; } +adc { return T_ADC; } +inc { return T_INC; } +dec { return T_DEC; } +stc { return T_STC; } +clc { return T_CLC; } +cmp { return T_CMP; } +xor { return T_XOR; } +test { return T_TEST;} +and { return T_AND; } +or { return T_OR; } +ret { return T_RET; } +nop { return T_NOP; } +.if { return T_IF; } +.else { return T_ELSE; } +.endif { return T_ENDIF; } + + /* Allowed Symbols */ +[-+,:()~|&."{};<>[\]!] { return yytext[0]; } + + /* Number processing */ +0[0-7]* { + yylval.value = strtol(yytext, NULL, 8); + return T_NUMBER; + } + +0[xX][0-9a-fA-F]+ { + yylval.value = strtoul(yytext + 2, NULL, 16); + return T_NUMBER; + } + +[1-9][0-9]* { + yylval.value = strtol(yytext, NULL, 10); + return T_NUMBER; + } + + /* Include Files */ +#include { return T_INCLUDE; } + + /* For parsing C include files with #define foo */ +#define { yylval.value = TRUE; return T_CONST; } + /* Throw away macros */ +#define[^\n]*[()]+[^\n]* ; +{PATH} { yylval.str = strdup(yytext); return T_PATH; } + +{WORD} { yylval.sym = symtable_get(yytext); return T_SYMBOL; } + +. { + char buf[255]; + + snprintf(buf, sizeof(buf), "Invalid character " + "'%c'", yytext[0]); + stop(buf, EX_DATAERR); + } +%% + +typedef struct include { + YY_BUFFER_STATE buffer; + int lineno; + char *filename; + SLIST_ENTRY(include) links; +}include_t; + +SLIST_HEAD(, include) include_stack; + +void +include_file(file_name, type) + char *file_name; + include_type type; +{ + FILE *newfile; + include_t *include; + + newfile = NULL; + /* Try the current directory first */ + if (includes_search_curdir != 0 || type == SOURCE_FILE) + newfile = fopen(file_name, "r"); + + if (newfile == NULL && type != SOURCE_FILE) { + path_entry_t include_dir; + for (include_dir = search_path.slh_first; + include_dir != NULL; + include_dir = include_dir->links.sle_next) { + char fullname[PATH_MAX]; + + if ((include_dir->quoted_includes_only == TRUE) + && (type != QUOTED_INCLUDE)) + continue; + + snprintf(fullname, sizeof(fullname), + "%s/%s", include_dir->directory, file_name); + + if ((newfile = fopen(fullname, "r")) != NULL) + break; + } + } + + if (newfile == NULL) { + perror(file_name); + stop("Unable to open input file", EX_SOFTWARE); + /* NOTREACHED */ + } + include = (include_t *)malloc(sizeof(include_t)); + if (include == NULL) { + stop("Unable to allocate include stack entry", EX_SOFTWARE); + /* NOTREACHED */ + } + if (type != SOURCE_FILE) { + include->buffer = YY_CURRENT_BUFFER; + include->lineno = yylineno; + include->filename = yyfilename; + SLIST_INSERT_HEAD(&include_stack, include, links); + } + yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE)); + yylineno = 1; + yyfilename = strdup(file_name); +} + +int +yywrap() +{ + include_t *include; + + yy_delete_buffer(YY_CURRENT_BUFFER); + (void)fclose(yyin); + if (yyfilename != NULL) + free(yyfilename); + yyfilename = NULL; + include = include_stack.slh_first; + if (include != NULL) { + yy_switch_to_buffer(include->buffer); + yylineno = include->lineno; + yyfilename = include->filename; + SLIST_REMOVE_HEAD(&include_stack, links); + free(include); + return (0); + } + return (1); +} diff --git a/drivers/scsi/aic7xxx/scsi_message.h b/drivers/scsi/aic7xxx/scsi_message.h new file mode 100644 index 000000000000..267a01591636 --- /dev/null +++ b/drivers/scsi/aic7xxx/scsi_message.h @@ -0,0 +1,41 @@ +/* + * SCSI messages definitions. + */ + +/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */ +#define MSG_CMDCOMPLETE 0x00 /* M/M */ +#define MSG_EXTENDED 0x01 /* O/O */ +#define MSG_SAVEDATAPOINTER 0x02 /* O/O */ +#define MSG_RESTOREPOINTERS 0x03 /* O/O */ +#define MSG_DISCONNECT 0x04 /* O/O */ +#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */ +#define MSG_ABORT 0x06 /* O/M */ +#define MSG_MESSAGE_REJECT 0x07 /* M/M */ +#define MSG_NOOP 0x08 /* M/M */ +#define MSG_PARITY_ERROR 0x09 /* M/M */ +#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */ +#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */ +#define MSG_BUS_DEV_RESET 0x0c /* O/M */ +#define MSG_ABORT_TAG 0x0d /* O/O */ +#define MSG_CLEAR_QUEUE 0x0e /* O/O */ +#define MSG_INIT_RECOVERY 0x0f /* O/O */ +#define MSG_REL_RECOVERY 0x10 /* O/O */ +#define MSG_TERM_IO_PROC 0x11 /* O/O */ + +/* Messages (2 byte) */ +#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */ +#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */ +#define MSG_ORDERED_Q_TAG 0x22 /* O/O */ +#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */ + +/* Identify message */ /* M/M */ +#define MSG_IDENTIFYFLAG 0x80 +#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun)) +#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG) + +/* Extended messages (opcode and length) */ +#define MSG_EXT_SDTR 0x01 +#define MSG_EXT_SDTR_LEN 0x03 + +#define MSG_EXT_WDTR 0x03 +#define MSG_EXT_WDTR_LEN 0x02 diff --git a/drivers/scsi/aic7xxx/sequencer.h b/drivers/scsi/aic7xxx/sequencer.h new file mode 100644 index 000000000000..2f9407524849 --- /dev/null +++ b/drivers/scsi/aic7xxx/sequencer.h @@ -0,0 +1,90 @@ +/* + * Instruction formats for the sequencer program downloaded to + * Aic7xxx SCSI host adapters + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: sequencer.h,v 1.1 1997/03/16 07:08:18 gibbs Exp $ + */ +#include "bsd_q.h" + +struct ins_format1 { + u_int8_t immediate; + u_int8_t source; + u_int8_t destination; + u_int8_t opcode_ret; +}; + +struct ins_format2 { + u_int8_t shift_control; + u_int8_t source; + u_int8_t destination; + u_int8_t opcode_ret; +#define RETURN_BIT 0x01 +}; + +struct ins_format3 { + u_int8_t immediate; + u_int8_t source; + u_int8_t address; + u_int8_t opcode_addr; +#define ADDR_HIGH_BIT 0x01 +}; + +struct instruction { + union { + struct ins_format1 format1; + struct ins_format2 format2; + struct ins_format3 format3; + u_int8_t bytes[4]; + } format; + u_int srcline; + struct symbol *patch_label; + STAILQ_ENTRY(instruction) links; +}; + +#define AIC_OP_OR 0x0 +#define AIC_OP_AND 0x1 +#define AIC_OP_XOR 0x2 +#define AIC_OP_ADD 0x3 +#define AIC_OP_ADC 0x4 +#define AIC_OP_ROL 0x5 + +#define AIC_OP_JMP 0x8 +#define AIC_OP_JC 0x9 +#define AIC_OP_JNC 0xa +#define AIC_OP_CALL 0xb +#define AIC_OP_JNE 0xc +#define AIC_OP_JNZ 0xd +#define AIC_OP_JE 0xe +#define AIC_OP_JZ 0xf + +/* Pseudo Ops */ +#define AIC_OP_SHL 0x10 +#define AIC_OP_SHR 0x20 +#define AIC_OP_ROR 0x30 diff --git a/drivers/scsi/aic7xxx/symbol.c b/drivers/scsi/aic7xxx/symbol.c new file mode 100644 index 000000000000..5842932e56a3 --- /dev/null +++ b/drivers/scsi/aic7xxx/symbol.c @@ -0,0 +1,451 @@ +/* + * Aic7xxx SCSI host adapter firmware asssembler symbol table implementation + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: symbol.c,v 1.1 1997/03/16 07:08:18 gibbs Exp $ + */ + + +#include + +#include +#include +#include +#include +#include +#include + +#include "symbol.h" +#include "aic7xxx_asm.h" + +static DB *symtable; + +symbol_t * +symbol_create(name) + char *name; +{ + symbol_t *new_symbol; + + new_symbol = (symbol_t *)malloc(sizeof(symbol_t)); + if (new_symbol == NULL) { + perror("Unable to create new symbol"); + exit(EX_SOFTWARE); + } + memset(new_symbol, 0, sizeof(*new_symbol)); + new_symbol->name = strdup(name); + new_symbol->type = UNINITIALIZED; + return (new_symbol); +} + +void +symbol_delete(symbol) + symbol_t *symbol; +{ + if (symtable != NULL) { + DBT key; + + key.data = symbol->name; + key.size = strlen(symbol->name); + symtable->del(symtable, &key, /*flags*/0); + } + switch(symbol->type) { + case SCBLOC: + case SRAMLOC: + case REGISTER: + if (symbol->info.rinfo != NULL) + free(symbol->info.rinfo); + break; + case ALIAS: + if (symbol->info.ainfo != NULL) + free(symbol->info.ainfo); + break; + case MASK: + case BIT: + if (symbol->info.minfo != NULL) { + symlist_free(&symbol->info.minfo->symrefs); + free(symbol->info.minfo); + } + break; + case CONST: + if (symbol->info.cinfo != NULL) + free(symbol->info.cinfo); + break; + case LABEL: + if (symbol->info.linfo != NULL) + free(symbol->info.linfo); + break; + case UNINITIALIZED: + default: + break; + } + free(symbol->name); + free(symbol); +} + +void +symtable_open() +{ + symtable = dbopen(/*filename*/NULL, + O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH, + /*openinfo*/NULL); + + if (symtable == NULL) { + perror("Symbol table creation failed"); + exit(EX_SOFTWARE); + /* NOTREACHED */ + } +} + +void +symtable_close() +{ + if (symtable != NULL) { + DBT key; + DBT data; + + while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) { + symbol_t *cursym; + + cursym = *(symbol_t **)data.data; + symbol_delete(cursym); + } + symtable->close(symtable); + } +} + +/* + * The semantics of get is to return an uninitialized symbol entry + * if a lookup fails. + */ +symbol_t * +symtable_get(name) + char *name; +{ + DBT key; + DBT data; + int retval; + + key.data = (void *)name; + key.size = strlen(name); + + if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) { + if (retval == -1) { + perror("Symbol table get operation failed"); + exit(EX_SOFTWARE); + /* NOTREACHED */ + } else if (retval == 1) { + /* Symbol wasn't found, so create a new one */ + symbol_t *new_symbol; + + new_symbol = symbol_create(name); + data.data = &new_symbol; + data.size = sizeof(new_symbol); + if (symtable->put(symtable, &key, &data, + /*flags*/0) !=0) { + perror("Symtable put failed"); + exit(EX_SOFTWARE); + } + return (new_symbol); + } else { + perror("Unexpected return value from db get routine"); + exit(EX_SOFTWARE); + /* NOTREACHED */ + } + } + return (*(symbol_t **)data.data); +} + +symbol_node_t * +symlist_search(symlist, symname) + symlist_t *symlist; + char *symname; +{ + symbol_node_t *curnode; + + curnode = symlist->slh_first; + while(curnode != NULL) { + if (strcmp(symname, curnode->symbol->name) == 0) + break; + curnode = curnode->links.sle_next; + } + return (curnode); +} + +void +symlist_add(symlist, symbol, how) + symlist_t *symlist; + symbol_t *symbol; + int how; +{ + symbol_node_t *newnode; + + newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t)); + if (newnode == NULL) { + stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE); + /* NOTREACHED */ + } + newnode->symbol = symbol; + if (how == SYMLIST_SORT) { + symbol_node_t *curnode; + int mask; + + mask = FALSE; + switch(symbol->type) { + case REGISTER: + case SCBLOC: + case SRAMLOC: + break; + case BIT: + case MASK: + mask = TRUE; + break; + default: + stop("symlist_add: Invalid symbol type for sorting", + EX_SOFTWARE); + /* NOTREACHED */ + } + + curnode = symlist->slh_first; + if (curnode == NULL + || (mask && (curnode->symbol->info.minfo->mask > + newnode->symbol->info.minfo->mask)) + || (!mask && (curnode->symbol->info.rinfo->address > + newnode->symbol->info.rinfo->address))) { + SLIST_INSERT_HEAD(symlist, newnode, links); + return; + } + + while (1) { + if (curnode->links.sle_next == NULL) { + SLIST_INSERT_AFTER(curnode, newnode, + links); + break; + } else { + symbol_t *cursymbol; + + cursymbol = curnode->links.sle_next->symbol; + if ((mask && (cursymbol->info.minfo->mask > + symbol->info.minfo->mask)) + || (!mask &&(cursymbol->info.rinfo->address > + symbol->info.rinfo->address))){ + SLIST_INSERT_AFTER(curnode, newnode, + links); + break; + } + } + curnode = curnode->links.sle_next; + } + } else { + SLIST_INSERT_HEAD(symlist, newnode, links); + } +} + +void +symlist_free(symlist) + symlist_t *symlist; +{ + symbol_node_t *node1, *node2; + + node1 = symlist->slh_first; + while (node1 != NULL) { + node2 = node1->links.sle_next; + free(node1); + node1 = node2; + } + SLIST_INIT(symlist); +} + +void +symlist_merge(symlist_dest, symlist_src1, symlist_src2) + symlist_t *symlist_dest; + symlist_t *symlist_src1; + symlist_t *symlist_src2; +{ + symbol_node_t *node; + + *symlist_dest = *symlist_src1; + while((node = symlist_src2->slh_first) != NULL) { + SLIST_REMOVE_HEAD(symlist_src2, links); + SLIST_INSERT_HEAD(symlist_dest, node, links); + } + + /* These are now empty */ + SLIST_INIT(symlist_src1); + SLIST_INIT(symlist_src2); +} + +void +symtable_dump(ofile) + FILE *ofile; +{ + /* + * Sort the registers by address with a simple insertion sort. + * Put bitmasks next to the first register that defines them. + * Put constants at the end. + */ + symlist_t registers; + symlist_t masks; + symlist_t constants; + symlist_t aliases; + + SLIST_INIT(®isters); + SLIST_INIT(&masks); + SLIST_INIT(&constants); + SLIST_INIT(&aliases); + + if (symtable != NULL) { + DBT key; + DBT data; + int flag = R_FIRST; + + while (symtable->seq(symtable, &key, &data, flag) == 0) { + symbol_t *cursym; + + cursym = *(symbol_t **)data.data; + switch(cursym->type) { + case REGISTER: + case SCBLOC: + case SRAMLOC: + symlist_add(®isters, cursym, SYMLIST_SORT); + break; + case MASK: + case BIT: + symlist_add(&masks, cursym, SYMLIST_SORT); + break; + case CONST: + if (cursym->info.cinfo->define == FALSE) { + symlist_add(&constants, cursym, + SYMLIST_INSERT_HEAD); + } + break; + case ALIAS: + symlist_add(&aliases, cursym, + SYMLIST_INSERT_HEAD); + default: + break; + } + flag = R_NEXT; + } + + /* Put in the masks and bits */ + while (masks.slh_first != NULL) { + symbol_node_t *curnode; + symbol_node_t *regnode; + char *regname; + + curnode = masks.slh_first; + SLIST_REMOVE_HEAD(&masks, links); + + regnode = + curnode->symbol->info.minfo->symrefs.slh_first; + regname = regnode->symbol->name; + regnode = symlist_search(®isters, regname); + SLIST_INSERT_AFTER(regnode, curnode, links); + } + + /* Add the aliases */ + while (aliases.slh_first != NULL) { + symbol_node_t *curnode; + symbol_node_t *regnode; + char *regname; + + curnode = aliases.slh_first; + SLIST_REMOVE_HEAD(&aliases, links); + + regname = curnode->symbol->info.ainfo->parent->name; + regnode = symlist_search(®isters, regname); + SLIST_INSERT_AFTER(regnode, curnode, links); + } + + /* Output what we have */ + fprintf(ofile, +"/* + * DO NOT EDIT - This file is automatically generated. + */\n"); + while (registers.slh_first != NULL) { + symbol_node_t *curnode; + u_int8_t value; + char *tab_str; + char *tab_str2; + + curnode = registers.slh_first; + SLIST_REMOVE_HEAD(®isters, links); + switch(curnode->symbol->type) { + case REGISTER: + case SCBLOC: + case SRAMLOC: + fprintf(ofile, "\n"); + value = curnode->symbol->info.rinfo->address; + tab_str = "\t"; + tab_str2 = "\t\t"; + break; + case ALIAS: + { + symbol_t *parent; + + parent = curnode->symbol->info.ainfo->parent; + value = parent->info.rinfo->address; + tab_str = "\t"; + tab_str2 = "\t\t"; + break; + } + case MASK: + case BIT: + value = curnode->symbol->info.minfo->mask; + tab_str = "\t\t"; + tab_str2 = "\t"; + break; + default: + value = 0; /* Quiet compiler */ + tab_str = NULL; + tab_str2 = NULL; + stop("symtable_dump: Invalid symbol type " + "encountered", EX_SOFTWARE); + break; + } + fprintf(ofile, "#define%s%-16s%s0x%02x\n", + tab_str, curnode->symbol->name, tab_str2, + value); + free(curnode); + } + fprintf(ofile, "\n\n"); + + while (constants.slh_first != NULL) { + symbol_node_t *curnode; + + curnode = constants.slh_first; + SLIST_REMOVE_HEAD(&constants, links); + fprintf(ofile, "#define\t%-8s\t0x%02x\n", + curnode->symbol->name, + curnode->symbol->info.cinfo->value); + free(curnode); + } + } +} + diff --git a/drivers/scsi/aic7xxx/symbol.h b/drivers/scsi/aic7xxx/symbol.h new file mode 100644 index 000000000000..59aa65763a0c --- /dev/null +++ b/drivers/scsi/aic7xxx/symbol.h @@ -0,0 +1,148 @@ +/* + * Aic7xxx SCSI host adapter firmware asssembler symbol table definitions + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: symbol.h,v 1.1 1997/03/16 07:08:19 gibbs Exp $ + */ +#if defined(linux) +#include "bsd_q.h" +#else +#include +#endif + + +typedef enum { + UNINITIALIZED, + REGISTER, + ALIAS, + SCBLOC, + SRAMLOC, + MASK, + BIT, + CONST, + LABEL, + CONDITIONAL +}symtype; + +typedef enum { + RO = 0x01, + WO = 0x02, + RW = 0x03 +}amode_t; + +struct reg_info { + u_int8_t address; + int size; + amode_t mode; + u_int8_t valid_bitmask; + int typecheck_masks; +}; + +typedef SLIST_HEAD(symlist, symbol_node) symlist_t; + +struct mask_info { + symlist_t symrefs; + u_int8_t mask; +}; + +struct const_info { + u_int8_t value; + int define; +}; + +struct alias_info { + struct symbol *parent; +}; + +struct label_info { + int address; +}; + +struct cond_info { + int value; +}; + +typedef struct expression_info { + symlist_t referenced_syms; + int value; +} expression_t; + +typedef struct symbol { + char *name; + symtype type; + union { + struct reg_info *rinfo; + struct mask_info *minfo; + struct const_info *cinfo; + struct alias_info *ainfo; + struct label_info *linfo; + struct cond_info *condinfo; + }info; +} symbol_t; + +typedef struct symbol_ref { + symbol_t *symbol; + int offset; +} symbol_ref_t; + +typedef struct symbol_node { + SLIST_ENTRY(symbol_node) links; + symbol_t *symbol; +}symbol_node_t; + +typedef struct patch { + STAILQ_ENTRY(patch) links; + int negative; + int begin; + int end; + int options; +} patch_t; + +void symbol_delete __P((symbol_t *symbol)); + +void symtable_open __P((void)); + +void symtable_close __P((void)); + +symbol_t * + symtable_get __P((char *name)); + +symbol_node_t * + symlist_search __P((symlist_t *symlist, char *symname)); + +void + symlist_add __P((symlist_t *symlist, symbol_t *symbol, int how)); +#define SYMLIST_INSERT_HEAD 0x00 +#define SYMLIST_SORT 0x01 + +void symlist_free __P((symlist_t *symlist)); + +void symlist_merge __P((symlist_t *symlist_dest, symlist_t *symlist_src1, + symlist_t *symlist_src2)); +void symtable_dump __P((FILE *ofile)); diff --git a/drivers/scsi/aic7xxx_asm.c b/drivers/scsi/aic7xxx_asm.c deleted file mode 100644 index 544edf0fa161..000000000000 --- a/drivers/scsi/aic7xxx_asm.c +++ /dev/null @@ -1,734 +0,0 @@ -/*+M************************************************************************* - * Adaptec AIC7xxx sequencer code assembler. - * - * Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * - * 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. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Comments are started by `#' and continue to the end of the line; lines - * may be of the form: - *