From 9f60221d74e5ae904c2595519bfd2d15a86b912a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:19:24 -0500 Subject: [PATCH] Linux 2.2.12pre1 Fix o Fix the TCP memory leak (Dave Miller) o Fix the 6Mb kernel bug on PPC (Paul Mackerras) o Fix shared IRQ on ISI serial (Alan Cox) | Still not SMP safe it seems o Sb1000 compiles built in (Alan Cox) o Sb1000 works (Alan Cox) o Fix warning in eth16i driver o Sis900 driver update (Jim Huang SIS) o PAS16 takes module arguments (Bob Tracey) o 53C875 model 37 detection (Pekka Pietikanen) o Autofs deadlock with nfs o Require RAWIO cap for kcore (Matthew Kirkwood) o munlock isnt priviledged (Matthew Kirkwood) o RLIM_INFINITY handling (Kanoj Sarcar) Add o Enhanced Raid (Ingo Molnar) o Computone Serial Driver (Computone) o Minor documentation update o IP virtual server masquerade (Wensong Zhang and others) o Iomega Buz capture driver (Rainer Johanni, Dave Perks, et al) o Large file array support (Stephen Tweedie) o FPU emulation enhancements for Alpha (Richard Henderson) o Universal Serial Bus (Assorted, backport by Paul Mackerras) o Detect AMD Athlon and report (Alan Cox) o Honour Bios EBDA (??) o PPC updates o Celtic language NLS (Rhys Jones) o IPH5526 FC Driver (Vineet M Abraham) --- CREDITS | 23 +- Documentation/00-INDEX | 30 +- Documentation/Configure.help | 130 +- Documentation/computone.txt | 343 ++ Documentation/locks.txt | 17 +- Documentation/m68k/kernel-options.txt | 53 +- Documentation/modules.txt | 2 +- Documentation/mtrr.txt | 4 +- Documentation/networking/00-INDEX | 8 +- Documentation/networking/CREDITS.ipvs | 26 + Documentation/networking/ChangeLog.ipvs | 292 ++ Documentation/networking/README.ipvs | 93 + Documentation/networking/baycom.txt | 6 +- Documentation/networking/cs89x0.txt | 18 +- Documentation/networking/multicast.txt | 14 +- Documentation/sound/MAD16 | 19 + Documentation/video4linux/README.buz | 212 + MAINTAINERS | 5 +- Makefile | 6 +- arch/alpha/kernel/process.c | 1 - arch/alpha/math-emu/Makefile | 16 +- arch/alpha/math-emu/cmptxx.c | 23 - arch/alpha/math-emu/div128.c | 125 - arch/alpha/math-emu/fp-emul.c | 468 +-- arch/alpha/math-emu/fxtod.c | 11 - arch/alpha/math-emu/fxtos.c | 11 - arch/alpha/math-emu/sfp-machine.h | 204 +- arch/i386/config.in | 2 +- arch/i386/defconfig | 17 +- arch/i386/kernel/init_task.c | 1 - arch/i386/kernel/setup.c | 76 +- arch/i386/mm/init.c | 5 +- arch/m68k/kernel/process.c | 4 - arch/ppc/boot/Makefile | 4 + arch/ppc/boot/misc.c | 76 +- arch/ppc/chrpboot/mknote.c | 43 + arch/ppc/defconfig | 2 + arch/ppc/kernel/chrp_setup.c | 28 +- arch/ppc/kernel/i8259.c | 48 +- arch/ppc/kernel/irq.c | 19 +- arch/ppc/kernel/misc.S | 12 +- arch/ppc/kernel/ppc_htab.c | 74 +- arch/ppc/kernel/ppc_ksyms.c | 2 + arch/ppc/kernel/prep_pci.c | 163 +- arch/ppc/kernel/prep_setup.c | 13 +- arch/ppc/kernel/process.c | 1 - arch/ppc/kernel/prom.c | 14 +- arch/ppc/kernel/smp.c | 44 +- arch/ppc/pmac_defconfig | 2 + arch/sparc/kernel/init_task.c | 1 - arch/sparc64/kernel/init_task.c | 1 - arch/sparc64/kernel/ioctl32.c | 25 +- arch/sparc64/math-emu/op-2.h | 4 +- arch/sparc64/math-emu/op-common.h | 6 +- drivers/block/Makefile | 34 +- drivers/block/cpqarray.h | 1 - drivers/block/floppy.c | 2 +- drivers/block/genhd.c | 4 + drivers/block/hsm.c | 840 ++++ drivers/block/linear.c | 278 +- drivers/block/linear.h | 16 - drivers/block/ll_rw_blk.c | 31 +- drivers/block/md.c | 4435 ++++++++++++++++----- drivers/block/raid0.c | 494 +-- drivers/block/raid1.c | 1249 +++--- drivers/block/raid5.c | 1317 ++++--- drivers/block/translucent.c | 136 + drivers/block/xor.c | 1480 +++++++ drivers/char/Config.in | 4 + drivers/char/Makefile | 32 + drivers/char/README.computone | 195 + drivers/char/buz.c | 3481 +++++++++++++++++ drivers/char/buz.h | 319 ++ drivers/char/chipsets.h | 41 + drivers/char/i2c.c | 16 + drivers/char/ip2.c | 73 + drivers/char/ip2/Makefile | 12 + drivers/char/ip2/fip_firm.h | 2149 +++++++++++ drivers/char/ip2/i2cmd.c | 264 ++ drivers/char/ip2/i2cmd.h | 660 ++++ drivers/char/ip2/i2ellis.c | 1470 +++++++ drivers/char/ip2/i2ellis.h | 609 +++ drivers/char/ip2/i2hw.h | 648 ++++ drivers/char/ip2/i2lib.c | 2255 +++++++++++ drivers/char/ip2/i2lib.h | 350 ++ drivers/char/ip2/i2os.h | 145 + drivers/char/ip2/i2pack.h | 364 ++ drivers/char/ip2/ip2.h | 109 + drivers/char/ip2/ip2ioctl.h | 35 + drivers/char/ip2/ip2trace.h | 43 + drivers/char/ip2/ip2types.h | 54 + drivers/char/ip2main.c | 3236 ++++++++++++++++ drivers/char/isicom.c | 7 +- drivers/char/mem.c | 20 +- drivers/char/nvram.c | 6 +- drivers/char/pcwd.c | 3 + drivers/char/saa7111.c | 421 ++ drivers/char/saa7185.c | 379 ++ drivers/char/tty_io.c | 3 + drivers/char/zr36057.h | 168 + drivers/char/zr36060.h | 35 + drivers/macintosh/adb.c | 15 +- drivers/macintosh/mac_keyb.c | 21 +- drivers/net/Config.in | 5 + drivers/net/Makefile | 11 +- drivers/net/Space.c | 35 +- drivers/net/eth16i.c | 2 +- drivers/net/fc/Makefile | 28 + drivers/net/fc/iph5526.c | 4703 +++++++++++++++++++++++ drivers/net/fc/iph5526_ip.h | 25 + drivers/net/fc/iph5526_novram.c | 278 ++ drivers/net/fc/iph5526_scsi.h | 31 + drivers/net/fc/tach.h | 475 +++ drivers/net/fc/tach_structs.h | 428 +++ drivers/net/net_init.c | 126 +- drivers/net/rtl8139.c | 5 +- drivers/net/sb1000.c | 2 +- drivers/net/sis900.c | 464 ++- drivers/pci/oldproc.c | 2 + drivers/scsi/hosts.c | 7 + drivers/scsi/ini9100u.c | 27 +- drivers/scsi/inia100.c | 22 +- drivers/scsi/mac53c94.c | 2 + drivers/scsi/mesh.c | 18 +- drivers/scsi/pas16.c | 36 +- drivers/scsi/sym53c8xx_defs.h | 3 + drivers/scsi/wd33c93.h | 1 + drivers/sound/mad16.c | 114 +- drivers/usb/CREDITS | 7 +- drivers/usb/Config.in | 44 +- drivers/usb/Makefile | 167 +- drivers/usb/README.ohci | 61 +- drivers/usb/acm.c | 295 ++ drivers/usb/audio.c | 30 +- drivers/usb/cpia.c | 1277 ++++++ drivers/usb/cpia.h | 139 + drivers/usb/ezusb.c | 472 +++ drivers/usb/ezusb.h | 67 + drivers/usb/hub.c | 156 +- drivers/usb/hub.h | 9 +- drivers/usb/inits.h | 9 +- drivers/usb/keyboard.c | 40 +- drivers/usb/keymap.c | 4 +- drivers/usb/maps/fixup.map | 3 +- drivers/usb/maps/mac.map | 350 ++ drivers/usb/mkmap.adb | 88 + drivers/usb/mouse.c | 112 +- drivers/usb/ohci-debug.c | 95 +- drivers/usb/ohci-hcd.c | 205 +- drivers/usb/ohci-hcd.h | 1 - drivers/usb/ohci.c | 2046 +++++++--- drivers/usb/ohci.h | 127 +- drivers/usb/printer.c | 420 ++ drivers/usb/proc_usb.c | 487 +++ drivers/usb/procusb | 41 + drivers/usb/stopusb | 4 + drivers/usb/uhci-debug.c | 35 +- drivers/usb/uhci.c | 832 +++- drivers/usb/uhci.h | 35 +- drivers/usb/usb-core.c | 103 + drivers/usb/usb-debug.c | 55 +- drivers/usb/usb.c | 618 ++- drivers/usb/usb.h | 192 +- drivers/usb/usb_scsi.c | 1453 +++++++ drivers/usb/usb_scsi.h | 147 + drivers/usb/usb_scsi_debug.c | 116 + drivers/usb/usb_scsi_dt.c | 4 + drivers/video/fbcon.c | 2 +- fs/Makefile | 2 +- fs/autofs/root.c | 13 +- fs/block_dev.c | 35 +- fs/buffer.c | 141 +- fs/exec.c | 6 +- fs/fcntl.c | 74 +- fs/file.c | 224 ++ fs/ioctl.c | 4 +- fs/nls/Config.in | 1 + fs/nls/Makefile | 8 + fs/nls/nls_base.c | 3 + fs/nls/nls_iso8859-14.c | 275 ++ fs/open.c | 43 +- fs/proc/array.c | 11 + fs/select.c | 28 +- include/asm-alpha/md.h | 13 - include/asm-alpha/resource.h | 2 +- include/asm-i386/md.h | 13 - include/asm-i386/resource.h | 2 +- include/asm-m68k/md.h | 13 - include/asm-m68k/resource.h | 2 +- include/asm-mips/resource.h | 2 +- include/asm-ppc/keyboard.h | 14 +- include/asm-ppc/md.h | 13 - include/asm-ppc/resource.h | 2 +- include/asm-sparc/md.h | 13 - include/asm-sparc/resource.h | 2 +- include/asm-sparc64/md.h | 91 - include/asm-sparc64/resource.h | 2 +- include/linux/blkdev.h | 5 +- include/linux/fcdevice.h | 40 + include/linux/fs.h | 65 +- include/linux/if_fc.h | 50 + include/linux/ip_masq.h | 30 +- include/linux/major.h | 1 - include/linux/md.h | 300 -- include/linux/mm.h | 9 +- include/linux/netdevice.h | 4 + include/linux/pci.h | 7 + include/linux/poll.h | 12 +- include/linux/proc_fs.h | 2 + include/linux/raid/hsm.h | 65 + include/linux/raid/hsm_p.h | 237 ++ include/linux/raid/linear.h | 32 + include/linux/raid/md.h | 95 + include/linux/raid/md_compatible.h | 387 ++ include/linux/raid/md_k.h | 338 ++ include/linux/raid/md_p.h | 161 + include/linux/raid/md_u.h | 114 + include/linux/raid/raid0.h | 33 + include/linux/raid/raid1.h | 64 + include/linux/{ => raid}/raid5.h | 21 +- include/linux/raid/translucent.h | 23 + include/linux/raid/xor.h | 12 + include/linux/raid0.h | 27 - include/linux/raid1.h | 49 - include/linux/sched.h | 65 +- include/linux/sysctl.h | 8 +- include/linux/tty.h | 1 + include/linux/video_decoder.h | 37 + include/linux/video_encoder.h | 21 + include/net/ip_masq.h | 27 +- include/net/ip_vs.h | 154 + init/main.c | 9 +- ipc/shm.c | 5 +- kernel/exit.c | 21 +- kernel/fork.c | 126 +- mm/mlock.c | 22 +- mm/mmap.c | 8 +- mm/mremap.c | 8 +- net/802/Makefile | 4 + net/802/fc.c | 96 + net/ipv4/Config.in | 8 + net/ipv4/Makefile | 36 + net/ipv4/arp.c | 16 + net/ipv4/ip_input.c | 9 + net/ipv4/ip_masq.c | 497 ++- net/ipv4/ip_masq_autofw.c | 2 +- net/ipv4/ip_masq_mfw.c | 1 + net/ipv4/ip_masq_portfw.c | 7 +- net/ipv4/ip_masq_user.c | 2 +- net/ipv4/ip_vs.c | 1296 +++++++ net/ipv4/ip_vs_pcc.c | 240 ++ net/ipv4/ip_vs_rr.c | 138 + net/ipv4/ip_vs_wlc.c | 167 + net/ipv4/ip_vs_wrr.c | 196 + net/ipv4/tcp_ipv4.c | 5 +- net/ipv6/tcp_ipv6.c | 6 +- net/netsyms.c | 9 + 257 files changed, 49309 insertions(+), 5177 deletions(-) create mode 100644 Documentation/computone.txt create mode 100644 Documentation/networking/CREDITS.ipvs create mode 100644 Documentation/networking/ChangeLog.ipvs create mode 100644 Documentation/networking/README.ipvs create mode 100644 Documentation/video4linux/README.buz delete mode 100644 arch/alpha/math-emu/cmptxx.c delete mode 100644 arch/alpha/math-emu/div128.c delete mode 100644 arch/alpha/math-emu/fxtod.c delete mode 100644 arch/alpha/math-emu/fxtos.c create mode 100644 arch/ppc/chrpboot/mknote.c create mode 100644 drivers/block/hsm.c delete mode 100644 drivers/block/linear.h create mode 100644 drivers/block/translucent.c create mode 100644 drivers/block/xor.c create mode 100644 drivers/char/README.computone create mode 100644 drivers/char/buz.c create mode 100644 drivers/char/buz.h create mode 100644 drivers/char/chipsets.h create mode 100644 drivers/char/ip2.c create mode 100644 drivers/char/ip2/Makefile create mode 100644 drivers/char/ip2/fip_firm.h create mode 100644 drivers/char/ip2/i2cmd.c create mode 100644 drivers/char/ip2/i2cmd.h create mode 100644 drivers/char/ip2/i2ellis.c create mode 100644 drivers/char/ip2/i2ellis.h create mode 100644 drivers/char/ip2/i2hw.h create mode 100644 drivers/char/ip2/i2lib.c create mode 100644 drivers/char/ip2/i2lib.h create mode 100644 drivers/char/ip2/i2os.h create mode 100644 drivers/char/ip2/i2pack.h create mode 100644 drivers/char/ip2/ip2.h create mode 100644 drivers/char/ip2/ip2ioctl.h create mode 100644 drivers/char/ip2/ip2trace.h create mode 100644 drivers/char/ip2/ip2types.h create mode 100644 drivers/char/ip2main.c create mode 100644 drivers/char/saa7111.c create mode 100644 drivers/char/saa7185.c create mode 100644 drivers/char/zr36057.h create mode 100644 drivers/char/zr36060.h create mode 100644 drivers/net/fc/Makefile create mode 100644 drivers/net/fc/iph5526.c create mode 100644 drivers/net/fc/iph5526_ip.h create mode 100644 drivers/net/fc/iph5526_novram.c create mode 100644 drivers/net/fc/iph5526_scsi.h create mode 100644 drivers/net/fc/tach.h create mode 100644 drivers/net/fc/tach_structs.h create mode 100644 drivers/usb/acm.c create mode 100644 drivers/usb/cpia.c create mode 100644 drivers/usb/cpia.h create mode 100644 drivers/usb/ezusb.c create mode 100644 drivers/usb/ezusb.h create mode 100644 drivers/usb/maps/mac.map create mode 100644 drivers/usb/mkmap.adb create mode 100644 drivers/usb/printer.c create mode 100644 drivers/usb/proc_usb.c create mode 100644 drivers/usb/procusb create mode 100644 drivers/usb/usb-core.c create mode 100644 drivers/usb/usb_scsi.c create mode 100644 drivers/usb/usb_scsi.h create mode 100644 drivers/usb/usb_scsi_debug.c create mode 100644 drivers/usb/usb_scsi_dt.c create mode 100644 fs/file.c create mode 100644 fs/nls/nls_iso8859-14.c delete mode 100644 include/asm-alpha/md.h delete mode 100644 include/asm-i386/md.h delete mode 100644 include/asm-m68k/md.h delete mode 100644 include/asm-ppc/md.h delete mode 100644 include/asm-sparc/md.h delete mode 100644 include/asm-sparc64/md.h create mode 100644 include/linux/fcdevice.h create mode 100644 include/linux/if_fc.h delete mode 100644 include/linux/md.h create mode 100644 include/linux/raid/hsm.h create mode 100644 include/linux/raid/hsm_p.h create mode 100644 include/linux/raid/linear.h create mode 100644 include/linux/raid/md.h create mode 100644 include/linux/raid/md_compatible.h create mode 100644 include/linux/raid/md_k.h create mode 100644 include/linux/raid/md_p.h create mode 100644 include/linux/raid/md_u.h create mode 100644 include/linux/raid/raid0.h create mode 100644 include/linux/raid/raid1.h rename include/linux/{ => raid}/raid5.h (86%) create mode 100644 include/linux/raid/translucent.h create mode 100644 include/linux/raid/xor.h delete mode 100644 include/linux/raid0.h delete mode 100644 include/linux/raid1.h create mode 100644 include/linux/video_decoder.h create mode 100644 include/linux/video_encoder.h create mode 100644 include/net/ip_vs.h create mode 100644 net/802/fc.c create mode 100644 net/ipv4/ip_vs.c create mode 100644 net/ipv4/ip_vs_pcc.c create mode 100644 net/ipv4/ip_vs_rr.c create mode 100644 net/ipv4/ip_vs_wlc.c create mode 100644 net/ipv4/ip_vs_wrr.c diff --git a/CREDITS b/CREDITS index 6ed94397717d..126812378c55 100644 --- a/CREDITS +++ b/CREDITS @@ -689,14 +689,11 @@ S: Woodbridge, Connecticut 06525 S: USA N: Paul Gortmaker -E: gpg109@rsphy1.anu.edu.au -W: http://rsphy1.anu.edu.au/~gpg109 -D: Real Time Clock driver author. -D: 8390 net driver hacker (ne2000, wd8013, smc-ultra, 3c503, etc.) -D: Ethernet-HOWTO and BootPrompt-HOWTO author. -D: Added many new CONFIG options (modules, ramdisk, generic-serial, etc.) -D: Implemented 1st "official" kernel thread (moved user bdflush to kflushd) -D: Various other random hacks, patches and utilities. +E: p_gortmaker@yahoo.com +D: Author of RTC driver & several net drivers, Ethernet & BootPrompt Howto. +D: Made support for modules, ramdisk, generic-serial, etc. optional. +D: Transformed old user space bdflush into 1st kernel thread - kflushd. +D: Many other patches, documentation files, mini kernels, utilities, ... N: John E. Gotts E: jgotts@engin.umich.edu @@ -978,6 +975,16 @@ S: 201 Howell Street, Apartment 1C S: Chapel Hill, North Carolina 27514-4818 S: USA +N: Dave Jones +E: dave@powertweak.com +W: http://linux.powertweak.com +D: Centaur/IDT Winchip/Winchip 2 tweaks +D: Misc clean ups and other random hacking. +S: 40, Heol Edward Lewis, +S: Gelligaer, Hengoed, +S: Mid Glamorgan, CF82 8EJ, +S: Wales, United Kingdom + N: Bernhard Kaindl E: bkaindl@netway.at E: edv@bartelt.via.at diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 39516f871160..ea256f2f1747 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -25,10 +25,14 @@ SMP.txt - notes, and "To Fix" list for multi-processor Linux. (see smp.tex) VGA-softcursor.txt - how to change your VGA cursor from a blinking underscore. +arm/ + - directory with info about Linux on the ARM architecture. binfmt_misc.txt - info on the kernel support for extra binary formats. cdrom/ - directory with information on the CD-ROM drivers that Linux has. +cpqarray.txt + - info on using Compaq's SMART2 Intelligent Disk Array Controllers. devices.tex - TeX source listing of all the nodes in /dev/ with major minor #'s devices.txt @@ -39,12 +43,16 @@ digiepca.txt - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. exception.txt - how Linux v2.2 handles exceptions without verify_area etc. +fb/ + - directory with info on the frame buffer graphics abstraction layer. filesystems/ - directory with info on the various filesystems that Linux supports. ftape.txt - notes about the floppy tape device driver hayes-esp.txt - info on using the Hayes ESP serial driver. +i386/ + - directory with info about Linux on the intel ix86 architecture. ide.txt - important info for users of ATA devices (IDE/EIDE disks and CD-ROMS) initrd.txt @@ -57,10 +65,18 @@ java.txt - info on the in-kernel binary support for Java(tm) joystick.txt - info on using joystick devices (and driver) with Linux. +joystick-api.txt + - API specification for applications that will be using the joystick. +joystick-parport.txt + - info on how to hook joysticks/gamepads to the parallel port. kbuild/ - directory with info about the kernel build process +kernel-docs.txt + - listing of various WWW + books that document kernel internals. +kernel-parameters.txt + - summary listing of command line / boot prompt args for the kernel. kmod.txt - - - info on the kernel module loader/unloader (kerneld replacement) + - info on the kernel module loader/unloader (kerneld replacement). locks.txt - info on file locking implementations, flock() vs. fcntl(), etc. logo.gif @@ -79,6 +95,8 @@ md.txt - info on boot arguments for the multiple devices driver memory.txt - info on typical Linux memory problems. +mkdev.ida + - script to make /dev entries for Intelligent Disk Array Controllers. modules.txt - short guide on how to make kernel parts into loadable modules mtrr.txt @@ -101,16 +119,22 @@ pcwd-watchdog.txt - info and sample code for using with the PC Watchdog reset card. powerpc/ - directory with info on using Linux with the PowerPC. +proc.txt + - detailed info on Linux's /proc filesystem. ramdisk.txt - short guide on how to set up and use the RAM disk. riscom8.txt - notes on using the RISCom/8 multi-port serial driver. rtc.txt - notes on how to use the Real Time Clock (aka CMOS clock) driver. +scsi-generic.txt + - info on the sg driver for generic (non-disk/CD/tape) SCSI devices. scsi.txt - short blurb on using SCSI support as a module. serial-console.txt - how to set up Linux with a serial line console as the default. +sgi-visws.txt + - short blurb on the SGI Visual Workstations. smart-config.txt - description of the Smart Config makefile feature. smp.tex @@ -127,6 +151,8 @@ stallion.txt - info on using the Stallion multiport serial driver. svga.txt - short guide on selecting video modes at boot via VGA BIOS. +sx.txt + - info on the Specialix SX/SI multiport serial driver. sysctl/ - directory with info on the /proc/sys/* files sysrq.txt @@ -135,6 +161,8 @@ transname.txt - how to use name translation to ease use of diskless systems. unicode.txt - info on the Unicode character/font mapping used in Linux. +video4linux/ + - directory with info regarding video/TV/radio cards and linux. watchdog.txt - how to auto-reboot Linux if it has "fallen and can't get up". ;-) xterm-linux.xpm diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 999e9a3621d2..2666406d9626 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -688,6 +688,17 @@ CONFIG_BLK_DEV_XD It's pretty unlikely that you have one of these: say N. +Mylex DAC960/DAC1100 PCI RAID Controller support +CONFIG_BLK_DEV_DAC960 + This driver adds support for the Mylex DAC960, AcceleRAID, and + eXtremeRAID PCI RAID controllers. See README.DAC960 for further + information about this driver. + + If you want to compile the driver as 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 DAC960.o. + Parallel port IDE device support CONFIG_PARIDE There are many external CD-ROM and disk devices that connect through @@ -945,6 +956,13 @@ CONFIG_BLK_DEV_MD If unsure, say N. +Autodetect RAID partitions +CONFIG_AUTODETECT_RAID + This feature lets the kernel detect RAID partitions on bootup. + An autodetect RAID partition is a normal partition with partition + type 0xfd. Use this if you want to boot RAID devices, or want to + run them automatically. + Linear (append) mode CONFIG_MD_LINEAR If you say Y here, then your multiple devices driver will be able to @@ -1024,6 +1042,21 @@ CONFIG_MD_RAID5 If unsure, say Y. +Translucent Block Device Support (EXPERIMENTAL) +CONFIG_MD_TRANSLUCENT + DO NOT USE THIS STUFF YET! + + currently there is only a placeholder there as the implementation + is not yet usable. + +Logical Volume Manager support (EXPERIMENTAL) +CONFIG_MD_LVM + DO NOT USE THIS STUFF YET! + + i have released this so people can comment on the architecture, + but user-space tools are still unusable so there is nothing much + you can do with this. + Boot support (linear, striped) CONFIG_MD_BOOT To boot with an initial linear or striped md device you have to @@ -2632,6 +2665,76 @@ CONFIG_IP_MASQUERADE_MFW The module will be called ip_masq_markfw.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +IP: masquerading virtual server support +CONFIG_IP_MASQUERADE_VS + IP Virtual Server support will let you build a virtual server + based on cluster of two or more real servers. This option must + be enabled for at least one of the clustered computers that will + take care of intercepting incomming connections to the virtual IP + and scheduling them to real servers. + Three request dispatching techniques are implemented, they are + virtual server via NAT, virtual server via tunneling and virtual + server via direct routing. The round-robin scheduling, the weighted + round-robin secheduling, or the weighted least-connection scheduling + algorithm can be used to choose which server the connection is + directed to, thus load balancing can be achieved among the servers. + For more information and its administration program, please visit + the following URL: + http://proxy.iinchina.net/~wensong/ippfvs/ + If you want this, say Y. + +IP masquerading VS table size (the Nth power of 2) +CONFIG_IP_MASQUERADE_VS_TAB_BITS + Using a big IP masquerading hash table for virtual server will greatly + reduce conflicts in the masquerading hash table when there are + thousands of active connections. + Note the table size must be power of 2. The table size will be the + value of 2 to the your input number power. For example, the default + number is 12, so the table size is 4096. Don't input the number too + small, otherwise you will lose performance on it. + You can adapt the table size yourself, according to your virtual + server application. It is good to set the table size larger than + the number of connections per second multiplying average lasting time + of connection in the table. For example, your virtual server gets + 20 connections per second, the connection lasts for 200 seconds in + average in the masquerading table, the table size should be larger + than 20x200, it is good to set the table size 4096 (2**12). + +IPVS: round-robin scheduling +CONFIG_IP_MASQUERADE_VS_RR + The robin-robin scheduling algorithm simply directs network + connections to different real servers in a round-robin manner. + If you want to compile it in kernel, say Y. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + +IPVS: weighted round-robin scheduling +CONFIG_IP_MASQUERADE_VS_WRR + The weighted robin-robin scheduling algorithm directs network + connections to different real servers based on server weights + in a round-robin manner. Servers with higher weights receive + new connections first than those with less weights, and servers + with higher weights get more connections than those with less + weights and servers with equal weights get equal connections. + If you want to compile it in kernel, say Y. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + +IPVS: weighted least-connection scheduling +CONFIG_IP_MASQUERADE_VS_WLC + The weighted least-connection scheduling algorithm directs network + connections to the server with the least number of alive connections + dividing the server weight. + If you want to compile it in kernel, say Y. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + +IPVS: persistent client connection scheduling +CONFIG_IP_MASQUERADE_VS_PCC + The persistent client connection feature means that after a client + establishs a connection to the selected server, all connections + from the same client will be directed to the same server in a + specified period. + If you want to compile it in kernel, say Y. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + IP: always defragment (required for masquerading) CONFIG_IP_ALWAYS_DEFRAG If you say Y here, then all incoming fragments (parts of IP packets @@ -2827,8 +2930,7 @@ CONFIG_IPV6 IPv6, see http://playground.sun.com/pub/ipng/html/ipng-main.html (to browse the WWW, you need to have access to a machine on the Internet that has a program like lynx or netscape); for specific information - about IPv6 under Linux read the HOWTO at http://www.terra.net/ipv6/ - and the file net/ipv6/README in the kernel source. + about IPv6 under Linux read the file net/ipv6/README in the kernel source. If you want to use IPv6, please upgrade to the newest net-tools as given in Documentation/Changes. You will still be able to do regular @@ -3248,6 +3350,13 @@ CONFIG_SCC_DELAY ### Don't know what's going on here. ### # +YAM driver for AX.25 +CONFIG_YAM + Support for the YAM modem on serial port. If you want to compile this + as 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. + BAYCOM picpar and par96 driver for AX.25 CONFIG_BAYCOM_PAR This is a driver for Baycom style simple amateur radio modems that @@ -7254,11 +7363,12 @@ CONFIG_NTFS_RW If unsure, say N. -System V and Coherent filesystem support +System V, Version 7 and Coherent filesystem support CONFIG_SYSV_FS SCO, Xenix and Coherent are commercial Unix systems for Intel - machines. Saying Y here would allow you to read to and write from - their floppies and hard disk partitions. + machines, and Version 7 was used on the DEC PDP-11. Saying Y here + would allow you to read to and write from their floppies and hard + disk partitions. If you have floppies or hard disk partitions like that, it is likely that they contain binaries from those other Unix systems; in order @@ -7943,6 +8053,16 @@ CONFIG_NLS_ISO8859_10 letters that were missing in Latin 4 to cover the entire Nordic area. +nls iso8859-14 +CONFIG_NLS_ISO8859_14 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 8 character + set, which adds the last accented vowels for Welsh (and Manx Gaelic) + that were missing in Latin 1. http://linux.speech.cymru.org/ + has further information. + nls iso8859-15 CONFIG_NLS_ISO8859_15 If you want to display filenames with native language characters diff --git a/Documentation/computone.txt b/Documentation/computone.txt new file mode 100644 index 000000000000..c1577ec21098 --- /dev/null +++ b/Documentation/computone.txt @@ -0,0 +1,343 @@ + +Computone Intelliport II/Plus Multiport Serial Driver +----------------------------------------------------- + +Release Notes For Linux Kernel 2.2 and higher. +These notes are for the drivers which have already been integrated into the +kernel and have been tested on Linux kernels 2.0, 2.2, and 2.3. + +Version: 1.2.4 +Date: 08/04/99 +Author: Andrew Manison +Testing: larryg@computone.com +Support: support@computone.com +Fixes and Updates: Doug McNash +Proc Filesystem and Kernel Integration: Mike Warfield + + +This file assumes that you are using the Computone drivers which are +integrated into the kernel sources. For updating the drivers or installing +drivers into kernels which do not already have Computone drivers, please +refer to the instructions in the README.computone file in the driver patch. + + +1. INTRODUCTION + +This driver supports the entire family of Intelliport II/Plus controllers +with the exception of the MicroChannel controllers. It does not support +products previous to the Intelliport II. + +This driver was developed on the v2.0.x Linux tree and has been tested up +to v2.2.9; it will probably not work with earlier v1.X kernels,. + + +2. QUICK INSTALLATION + +Hardware - If you have an ISA card, find a free interrupt and io port. + List those in use with `cat /proc/interrupts` and + `cat /proc/ioports`. Set the card dip switches to a free + address. You may need to configure your BIOS to reserve an + irq for an ISA card. PCI and EISA parameters are set + automagically. Insert card into computer with the power off + before or after drivers installation. + + Note the hardware address from the Computone ISA cards installed into + the system. These are required for editing ip2.h or editing + /etc/config.modules, or for specification on the modprobe + command line. + +Software - + +Module installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (m) module for CONFIG_COMPUTONE under character + devices. CONFIG_PCI and CONFIG_MODULES also may need to be set. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h if needed + or + edit /etc/conf.modules (or /etc/modules.conf) if needed (module). + or both to match this setting. +f) Run "make dep" +g) Run "make modules" +h) Run "make modules_install" +i) Run "/sbin/depmod -a" +j) install driver using `modprobe ip2 ` (options listed below) +k) run ip2mkdev (either the script below or the binary version) + + +Kernel installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (y) kernel for CONFIG_COMPUTONE under character + devices. CONFIG_PCI may need to be set if you have PCI bus. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h +f) Run "make dep" +g) Run "make zImage" or whatever target you prefer. +h) mv /usr/src/linux/arch/i386/boot/zImage to /boot. +i) Add new config for this kernel into /etc/lilo.conf, run "lilo" + or copy to a floppy disk and boot from that floppy disk. +j) Reboot using this kernel +k) run ip2mkdev (either the script below or the binary version) + + +3. INSTALLATION + +Previously, the driver sources were packaged with a set of patch files +to update the character drivers' makefile and configuration file, and other +kernel source files. A build script (ip2build) was included which applies +the patches if needed, and build any utilities needed. +What you recieve may be a single patch file in conventional kernel +patch format build script. That form can also be applied by +running patch -p1 < ThePatchFile. Otherwise run ip2build. + +The driver can be installed as a module (recommended) or built into the +kernel. This is selected as for other drivers through the `make config` +command from the root of the Linux source tree. If the driver is built +into the kernel you will need to edit the file ip2.h to match the boards +you are installing. See that file for instructions. If the driver is +installed as a module the configuration can also be specified on the +modprobe command line as follows: + + modprobe ip2 irq=irq1,irq2,irq3,irq4 io=addr1,addr2,addr3,addr4 + +where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11, +12,15) and addr1-4 are the base addresses for up to four controllers. If +the irqs are not specified the driver uses the default in ip2/ip2.h (which +selects polled mode). If no base addresses are specified the defaults in +ip2.h are used. If you are autoloading the driver module with kerneld or +kmod the base addresses and interrupt number must also be set in ip2/ip2.h +and recompile or just insert and options line in /etc/modules.conf or both. +The options line is equivalent to the command line and takes precidence over +what is in ip2.h. + +/etc/modules.conf sample: + options ip2 io=1,0x328 irq=1,10 + alias char-major-71 ip2 + alias char-major-72 ip2 + alias char-major-73 ip2 + +equivelant ip2.h: +static ip2config_t ip2config = +{ + {1,10,0,0}, + { + 0x0001, // Board 0, ttyF0 - ttyF63 /* PCI card */ + 0x0328, // Board 1, ttyF64 - ttyF127 /* ISA card */ + 0x0000, // Board 2, ttyF128 - ttyF191 /* empty */ + 0x0000 // Board 3, ttyF192 - ttyF255 /* empty */ + } +}; + + +Note: Both io and irq should be updated to reflect YOUR system. An "io" + address of "1/2" indicates a PCI/EISA card in the board table. The + PCI or EISA irq will be assigned automatically. + +Specifying an invalid or in-use irq will default the driver into +running in polled mode for that card. If all irq entries are 0 then +all cards will operate in polled mode. + +If you select the driver as part of the kernel run : + + make depend + make zlilo (or whatever you do to create a bootable kernel) + +If you selected a module run : + + make modules && make modules_install + +The utility ip2mkdev (see 5 and 7 below) creates all the device nodes +required by the driver. For a device to be created it must be configured +in the driver and the board must be installed. Only devices corresponding +to real IntelliPort II ports are created. With multiple boards and expansion +boxes this will leave gaps in the sequence of device names. ip2mkdev uses +Linux tty naming conventions: ttyF0 - ttyF255 for normal devices, and +cuf0 - cuf255 for callout devices. + + +4. USING THE DRIVERS + +As noted above, the driver implements the ports in accordance with Linux +conventions, and the devices should be interchangeable with the standard +serial devices. (This is a key point for problem reporting: please make +sure that what you are trying do works on the ttySx/cuax ports first; then +tell us what went wrong with the ip2 ports!) + +Higher speeds can be obtained using the setserial utility which remaps +38,400 bps (extb) to 57,600 bps, 115,200 bps, or a custom speed. +Intelliport II installations using the PowerPort expansion module can +use the custom speed setting to select the highest speeds: 153,600 bps, +230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for +custom baud rate configuration is fixed at 921,600 for cards/expantion +modules with ST654's and 115200 for those with Cirrus CD1400's. This +corresponds to the maximum bit rates those chips are capable. +For example if the baud base is 921600 and the baud divisor is 18 then +the custom rate is 921600/18 = 51200 bps. See the setserial man page for +complete details. Of course if stty accepts the higher rates now you can +use that as well as the standard ioctls(). + + +5. ip2mkdev and assorted utilities... + +Several utilities, including the source for a binary ip2mkdev utility are +available under .../drivers/char/ip2. These can be build by changing to +that directory and typing "make" after the kernel has be built. If you do +not wish to compile the binary utilities, the shell script below can be +cut out and run as "ip2mkdev" to create the necessary device files. To +use the ip2mkdev script, you must have procfs enabled and the proc file +system mounted on /proc. + +6. NOTES + +This is a release version of the driver, but it is impossible to test it +in all configurations of Linux. If there is any anomalous behaviour that +does not match the standard serial port's behaviour please let us know. + + +7. ip2mkdev shell script + +===== Cut Here ===== +#!/bin/sh - + +# ip2mkdev +# +# Make or remove devices as needed for Computone Intelliport drivers +# +# First rule! If the dev file exists and you need it, don't mess +# with it. That prevents us from screwing up open ttys, ownership +# and permissions on a running system! +# +# This script will NOT remove devices that no longer exist because +# their board or interface box has been removed. If you want to get +# rid of them, you can manually do an "rm -f /dev/ttyF* /dev/cuaf*" +# before running this script, which will then recreate all the valid +# devices +# +# =mhw= +# Michael H. Warfield +# mhw@wittsend.com +# +if test ! -f /proc/tty/drivers +then + echo "\ +Unable to check driver status. +Make sure proc file system is mounted." + + exit 255 +fi + +if test ! -f /proc/tty/driver/ip2 +then + echo "\ +Unable to locate ip2 proc file. +Attempting to load driver" + + if insmod ip2 + then + if test ! -f /proc/tty/driver/ip2 + then + echo "\ +Unable to locate ip2 proc file after loading driver. +Driver initialization failure or driver version error. +" + exit 255 + fi + else + echo "Unable to load ip2 driver." + exit 255 + fi +fi + +# Ok... So we got the driver loaded and we can locate the procfs files. +# Next we need our major numbers. + +TTYMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/tty/!d' -e 's/.*tty.[ ]*\([0-9]*\)[ ]*.*/\1/' < /proc/tty/drivers` +CUAMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/cu/!d' -e 's/.*cu.[ ]*\([0-9]*\)[ ]*.*/\1/' < /proc/tty/drivers` +BRDMAJOR=`sed -e '/^Driver: /!d' -e 's/.*IMajor=\([0-9]*\)[ ]*.*/\1/' < /proc/tty/driver/ip2` + +echo "\ +TTYMAJOR = $TTYMAJOR +CUAMAJOR = $CUAMAJOR +BRDMAJOR = $BRDMAJOR +" + +# Ok... Now we should know our major numbers, if appropriate... +# Now we need our boards and start the device loops. + +grep '^Board [0-9]:' /proc/tty/driver/ip2 | while read token number type alltherest +do + # The test for blank "type" will catch the stats lead-in lines + # if they exist in the file + if test "$type" = "vacant" -o "$type" = "Vacant" -o "$type" = "" + then + continue + fi + + BOARDNO=`expr "$number" : '\([0-9]\):'` + PORTS=`expr "$alltherest" : '.*ports=\([0-9]*\)' | tr ',' ' '` + MINORS=`expr "$alltherest" : '.*minors=\([0-9,]*\)' | tr ',' ' '` + + if test "$BOARDNO" = "" -o "$PORTS" = "" + then +# This may be a bug. We should at least get this much information + echo "Unable to process board line" + continue + fi + + if test "$MINORS" = "" + then +# Silently skip this one. This board seems to have no boxes + continue + fi + + echo "board $BOARDNO: $type ports = $PORTS; port numbers = $MINORS" + + if test "$BRDMAJOR" != "" + then + BRDMINOR=`expr $BOARDNO \* 4` + STSMINOR=`expr $BRDMINOR + 1` + if test ! -c /dev/ip2ipl$BOARDNO ; then + mknod /dev/ip2ipl$BOARDNO c $BRDMAJOR $BRDMINOR + fi + if test ! -c /dev/ip2stat$BOARDNO ; then + mknod /dev/ip2stat$BOARDNO c $BRDMAJOR $STSMINOR + fi + fi + + if test "$TTYMAJOR" != "" + then + PORTNO=$BOARDBASE + + for PORTNO in $MINORS + do + if test ! -c /dev/ttyF$PORTNO ; then + # We got the harware but no device - make it + mknod /dev/ttyF$PORTNO c $TTYMAJOR $PORTNO + fi + done + fi + + if test "$CUAMAJOR" != "" + then + PORTNO=$BOARDBASE + + for PORTNO in $MINORS + do + if test ! -c /dev/cuf$PORTNO ; then + # We got the harware but no device - make it + mknod /dev/cuf$PORTNO c $CUAMAJOR $PORTNO + fi + done + fi +done + +exit 0 +===== Cut Here ===== diff --git a/Documentation/locks.txt b/Documentation/locks.txt index d2a797e0b181..5fe26385af2e 100644 --- a/Documentation/locks.txt +++ b/Documentation/locks.txt @@ -13,12 +13,11 @@ The old flock(2) emulation in the kernel was swapped for proper BSD compatible flock(2) support in the 1.3.x series of kernels. With the -release of the 2.1.x kernel series, support for the old emulation has -been totally removed, so that we don't need to carry this baggage -forever. +release of the 2.1.x kernel series, support for the old emulation was +totally removed, so that we don't need to carry this baggage forever. -This should not cause problems for anybody, since everybody using a -2.1.x kernel should have updated their C library to a suitable version +This should not cause problems for anybody, since everybody using a 2.1.x +or 2.2.x kernel should have updated their C library to a suitable version anyway (see the file "linux/Documentation/Changes".) 1.2 Allow Mixed Locks Again @@ -31,9 +30,9 @@ installations use fcntl() instead of flock(). This is true of Slackware 3.0 for example. This gave rise to some other subtle problems if sendmail was configured to rebuild the alias file. Sendmail tried to lock the aliases.dir file with fcntl() at the same time as the GDBM routines tried to lock this -file with flock(). With pre 1.3.96 kernels this could result in deadlocks that, -over time, or under a very heavy mail load, would eventually cause the kernel -to lock solid with deadlocked processes. +file with flock(). With kernels before 1.3.96 this could result in deadlocks +that, over time or under a very heavy mail load, would eventually cause the +kernel to lock solid with deadlocked processes. 1.2.2 The Solution @@ -60,7 +59,7 @@ of which was the ability to freeze an NFS server by asking it to read a file for which a mandatory lock existed. From this release of the kernel, mandatory locking can be turned on and off -on a per-filesystem basis, using the mount options 'mand' and 'nomand'. +on a per-file-system basis, using the mount options 'mand' and 'nomand'. The default is to disallow mandatory locking. The intention is that mandatory locking only be enabled on a local filesystem as the specific need arises. diff --git a/Documentation/m68k/kernel-options.txt b/Documentation/m68k/kernel-options.txt index 5938cb8cf90f..bcc29cef6864 100644 --- a/Documentation/m68k/kernel-options.txt +++ b/Documentation/m68k/kernel-options.txt @@ -1,5 +1,3 @@ - - Command Line Options for Linux/m68k =================================== @@ -14,7 +12,7 @@ Update: jds@kom.auc.dk (Jes Sorensen) and faq@linux-m68k.org (Chris Lawrence) Often I've been asked which command line options the Linux/m68k kernel understands, or how the exact syntax for the ... option is, or ... about the option ... . I hope, this document supplies all the -answers... +answers. Note that some options might be outdated, their descriptions being incomplete or missing. Please update the information and send in the @@ -110,20 +108,19 @@ the kernel command line. [Strange and maybe uninteresting stuff ON] This unusual translation of device names has some strange -consequences: If, for example, you have a symbolic link from /dev/fd +consequences: if, for example, you have a symbolic link from /dev/fd to /dev/fd0D720 as an abbreviation for floppy driver #0 in DD format, you cannot use this name for specifying the root device, because the kernel cannot see this symlink before mounting the root FS and it isn't in the table above. If you use it, the root device will not be -set at all, without an error message. Another example: You cannot use a +set at all, without an error message. Another example: you cannot use a partition on e.g. the sixth SCSI disk as the root filesystem, if you want to specify it by name. This is, because only the devices up to -/dev/sde are in the table above, but not /dev/sdf. Although, you can -use the sixth SCSI disk for the root FS, but you have to specify the -device by number... (see below). Or, even more strange, you can use the -fact that there is no range checking of the partition number, and your -knowledge that each disk uses 16 minors, and write "root=/dev/sde17" -(for /dev/sdf1). +/dev/sde are in the table above, but not /dev/sdf. You can use the sixth +SCSI disk for the root filesystem, but you have to specify the device by +number (see below). Even more strange, you can use the fact that there is +no range checking of the partition number, and your knowledge that each +disk uses 16 minors, and write "root=/dev/sde17" (for /dev/sdf1). [Strange and maybe uninteresting stuff OFF] @@ -181,7 +178,7 @@ at least 8 can also be set by the "debug" command line option (see Devices possible for Amiga: - "ser": built-in serial port; parameters: 9600bps, 8N1 - - "mem": Save the messages to a reserved area in chip mem. After + - "mem": Save the messages to a reserved area in chip memory. After rebooting, they can be read under AmigaOS with the tool 'dmesg'. @@ -205,7 +202,7 @@ Devices possible for Atari: Syntax: ramdisk= This option instructs the kernel to set up a ramdisk of the given -size in KBytes. Do not use this option if the ramdisk contents are +size in kilobytes. Do not use this option if the ramdisk contents are passed by bootstrap! In this case, the size is selected automatically and should not be overwritten. @@ -234,10 +231,10 @@ Syntax: ether=[[,[,[,]]]], drivers/net/Space.c in the Linux source. Most prominent are eth0, ... eth3, sl0, ... sl3, ppp0, ..., ppp3, dummy, and lo. - The non-ethernet drivers (sl, ppp, dummy, lo) obviously ignore the -settings by this options. Also, the existing ethernet drivers for + The non-Ethernet drivers (sl, ppp, dummy, lo) obviously ignore the +settings by this options. Also, the existing Ethernet drivers for Linux/m68k (ariadne, a2065, hydra) don't use them because Zorro boards -are really Plug-'n-Play, so the "ether=" option is useless altogether +are really Plug-'n'-Play, so the "ether=" option is useless altogether for Linux/m68k. @@ -288,8 +285,8 @@ driver (Amiga and Atari): is the number of buffers you want to use (minimum 4, default 4), is the size of each buffer in kilobytes (minimum 4, default 32) and says how much percent of error will be tolerated when setting a frequency -(maximum 10, default 0). For example with 3% you can play 8000Hz -AU-Files on the Falcon with its hardware frequency of 8195Hz and thus +(maximum 10, default 0). For example, with 3% you can play 8000 Hz Sun +audio files on the Falcon with its hardware frequency of 8195 Hz and thus don't need to expand the sound. @@ -482,7 +479,7 @@ the physical linelength differs from the visible length. With ProMST, xres_virtual must be set to 2048. For ET4000, xres_virtual depends on the initialisation of the video-card. If you're missing a corresponding yres_virtual: the external part is legacy, -therefore we don't support hardware-dependend functions like hardware-scroll, +therefore we don't support hardware-dependent functions like hardware scroll, panning or blanking. 4.1.7) eclock: @@ -653,7 +650,7 @@ present hardware.) "ov_midi", ... These options are meant for switching on an OverScan video extension. The difference to the bare option is that the switch-on is done after video initialization, and somehow synchronized -to the HBLANK. A speciality is that ov_ikbd and ov_midi are switched +to the HBLANK. A specialty is that ov_ikbd and ov_midi are switched off before rebooting, so that OverScan is disabled and TOS boots correctly. @@ -737,8 +734,8 @@ VGA modes: - vga70 : 640x400, 31 kHz, 70 Hz Please notice that the ECS and VGA modes require either an ECS or AGA -chipset, and that these modes are limited to 2-bit color for the ECS -chipset and 8-bit color for the AGA chipset. +chip set, and that these modes are limited to 2-bit color for the ECS +chip set and 8-bit color for the AGA chip set. 5.1.2) depth ------------ @@ -842,11 +839,11 @@ wd33c93.h. Syntax: clock:x x = clock input in MHz for WD33c93 chip. Normal values would be from -8 through 20. The default value depends on your hostadapter(s), +8 through 20. The default value depends on your host adapter(s), default for the A3000 internal controller is 14, for the A2091 it's 8 -and for the GVP hostadapters it's either 8 or 14, depending on the -hostadapter and the SCSI-clock jumper present on some GVP -hostadapters. +and for the GVP host adapters it's either 8 or 14, depending on the +host adapter and the SCSI-clock jumper present on some GVP +host adapters. 5.3.6) next ----------- @@ -875,8 +872,8 @@ Syntax: gvp11= The earlier versions of the GVP driver did not handle DMA address-mask settings correctly which made it necessary for some people to use this option, in order to get their GVP controller -running under Linux. These problems have hopefully been solved and the -use of this option is now highly unrecommended! +running under Linux. These problems should now be solved and +further use of this option is highly discouraged! Incorrect use can lead to unpredictable behavior, so please only use this option if you *know* what you are doing and have a reason to do diff --git a/Documentation/modules.txt b/Documentation/modules.txt index ca9c434f716e..cf8b422140e4 100644 --- a/Documentation/modules.txt +++ b/Documentation/modules.txt @@ -59,7 +59,7 @@ Here is a sample of the available modules included in the kernel sources: Most low-level SCSI drivers: (i.e. aha1542, in2000) All SCSI high-level drivers: disk, tape, cdrom, generic. - Most ethernet drivers: (too many to list, please see the file + Most Ethernet drivers: (too many to list, please see the file ./Documentation/networking/net-modules.txt) Most CDROM drivers: diff --git a/Documentation/mtrr.txt b/Documentation/mtrr.txt index af58d63d7854..368d4dd2f382 100644 --- a/Documentation/mtrr.txt +++ b/Documentation/mtrr.txt @@ -40,7 +40,7 @@ reg00: base=0x00000000 ( 0MB), size= 128MB: write-back, count=1 reg01: base=0x08000000 ( 128MB), size= 64MB: write-back, count=1 reg02: base=0xf8000000 (3968MB), size= 4MB: write-combining, count=1 -This is for videoram at base address 0xf8000000 and size 4 MBytes. To +This is for video RAM at base address 0xf8000000 and size 4 megabytes. To find out your base address, you need to look at the output of your X server, which tells you where the linear framebuffer address is. A typical line that you may get is: @@ -56,7 +56,7 @@ know?), the following line will tell you: (--) S3: videoram: 4096k -That's 4 MBytes, which is 0x400000 bytes (in hexadecimal). +That's 4 megabytes, which is 0x400000 bytes (in hexadecimal). A patch is being written for XFree86 which will make this automatic: in other words the X server will manipulate /proc/mtrr using the ioctl() interface, so users won't have to do anything. If you use a diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index e3981efff11a..be39d93e40b8 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -13,9 +13,9 @@ PLIP.txt alias.txt - info on using alias network devices arcnet-hardware.txt - - tons of info on arcnet, hubs, arcnet card jumper settings, etc. + - tons of info on ARCnet, hubs, jumper settings for ARCnet cards, etc. arcnet.txt - - info on the using the arcnet driver itself. + - info on the using the ARCnet driver itself. ax25.txt - info on using AX.25 and NET/ROM code for Linux baycom.txt @@ -69,13 +69,13 @@ shaper.txt smc9.txt - the driver for SMC's 9000 series of Ethernet cards soundmodem.txt - - Linux driver for soundcards as AX.25 modems + - Linux driver for sound cards as AX.25 modems tcp.txt - short blurb on how TCP output takes place. tulip.txt - info on using DEC 21040/21041/21140 based PCI Ethernet cards. vortex.txt - - info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) e'net cards. + - info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) Ethernet cards. wan-router.txt - Wan router documentation wanpipe.txt diff --git a/Documentation/networking/CREDITS.ipvs b/Documentation/networking/CREDITS.ipvs new file mode 100644 index 000000000000..95d869e00f92 --- /dev/null +++ b/Documentation/networking/CREDITS.ipvs @@ -0,0 +1,26 @@ +The contributors of Linux Virtual Server project are listed +as follows in alphabetical order. + + Mike Douglas + Virtual Server Logo. + + Matthew Kellett + Added the loadable load-balancing module to VS-0.5 patch + for kernel 2.0. + + Peter Kese + Suggested the idea of the local-node feature and provided a + local-node prototype patch for VS via tunneling. + Port the VS patch to kernel 2.2 and rewrite of the code + The persistent client connection feature. + + Joseph Mack + Gaving a talk about Linux Virtual Server in the LinuxExpo'99. + + Rob Thomas + Wrote the the "Greased Turkey" document about how to setup a + load-sharing server. (a little bit stale, though) + + Wensong Zhang + Chief author and developer. + diff --git a/Documentation/networking/ChangeLog.ipvs b/Documentation/networking/ChangeLog.ipvs new file mode 100644 index 000000000000..55d5eff9b156 --- /dev/null +++ b/Documentation/networking/ChangeLog.ipvs @@ -0,0 +1,292 @@ +ChangeLog of Virtual Server patch for Linux 2.2 + +Virtual Server patch for Linux 2.2 - Version 0.7 - July 9, 1999 + +Changes: +- Added a separate masq hash table for IPVS. + +- Added slow timers to expire masq entries. + Slow timers are checked in one second by default. Most overhead + of cascading timers is avoided. + + With this new hash table and slow timers, the system can hold + huge number of masq entries, but make sure that you have + enough free memory. One masq entry costs 128 bytes memory + effectively (Thank Alan Cox), if your box holds 1 million masq + entries (it means that your box can receive 2000 connections per + second if masq expire time is 500 seconds in average.), make sure + that you have 128M free memory. And, thank Alan for suggesting + the early random drop algorithm for masq entries that prevents + the system from running out of memory, I will design and implement + this feature in the near future. + +- Fixed the unlocking bug in the ip_vs_del_dest(). + Thank Ted Pavlic for reporting it. + +---------------------------------------------------------------------- + +Virtual Server patch for Linux 2.2 - Version 0.6 - July 1, 1999 + +Changes: +- Fixed the overflow bug in the ip_vs_procinfo(). + Thank Ted Pavlic for reporting it. + +- Added the functionality to change weight and forwarding + (dispatching) method of existing real server. + This is useful for load-informed scheduling. + +- Added the functionality to change scheduler of virtual service + on the fly. + +- Reorganized some code and changed names of some functions. + This make the code more readable. + +---------------------------------------------------------------------- + +Virtual Server patch for Linux 2.2 - Version 0.5 - June 22, 1999 + +Changes: +- Fixed the bug that LocalNode doesn't work in vs-0.4-2.2.9. + Thank Changwon Kim for + reporting the bug and pointing me the checksum update + problem in the code. + +- some code of VS in the ip_fw_demasquerade was reorganized + so that the packets for VS-Tunneling, VS-DRouting and LocalNode + skip the checksum update. This make the code right and efficient + + +---------------------------------------------------------------------- + +Virtual Server patch for Linux 2.2 - Version 0.4 - June 1, 1999 + +Most of the code was rewritten. The locking and refcnt was changed +The violation of "no floats in kernel mode" rule in the weighted +least-connection scheduling was fixed. This patch is more efficient, +and should be more stable. + + +---------------------------------------------------------------------- + +Virtual Server patch for Linux 2.2 - Version 0.1~0.3 - May 1999 + +Peter Kese ported the VS patch to kernel 2.2, +rewrote the code and loadable scheduling modules. + +========================================================================== + +ChangeLog of Virtual Server patch for Linux 2.0 +---------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.9 - May 1, 1999 + +Differences with virtual server patch version 0.8: + +- Add Virtual Server via Direct Routing + This approach was first implemented in IBM's NetDispatcher. All real + servers have their loopback alias interface configured with the virtual + IP address, the load balancer and the real servers must have one of + their interfaces physically linked by a HUB/Switch. When the packets + destined for the virtual IP address arrives, the load balnacer directly + route them to the real servers, the real servers processing the requests + and return the reply packets directly to the clients. Compared to the + virtual server via IP tunneling approach, this approach doesn't have + tunneling overhead(In fact, this overhead is minimal in most situations), + but requires that one of the load balancer's interfaces and the real + servers' interfaces must be in physical segment. + +- Add more satistics information + The active connection counter and the total connection counter of + each real server were added for all the scheduling algorithms. + +- Add resetting(zeroing) counters + The total connection counters of all real servers can be reset to zero. + +- Change some statements in the masq_expire function and the + ip_fw_demasquerade function, so that ip_masq_free_ports won't become + abnormal number after the masquerading entries for virtual server + are released. + +- Fix the bug of "double unlock on device queue" + Remove the unnecessary function call of skb_device_unlock(skb) in the + ip_pfvs_encapsule function, which sometimes cause "kernel: double + unlock on device queue" waring in the virtual server via tunneling. + +- Many functions of virtual server patch was splitted into the + linux/net/ipv4/ip_masq_pfvs.c. + +- Upgrade ippfvsadm 1.0.2 to ippfvsadm 1.0.3 + Zeroing counters is supported in the new version. The ippfvsadm 1.0.3 + can be used for all kernel with different virtual server options + without rebuilding the program. + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.8 - March 6, 1999 + +Differences with virtual server patch version 0.7: + +- Add virtual FTP server support + The original ippfvs via IP tunneling could not be used to + build a virtual FTP server, because the real servers could + not establish data connections to clients. The code was + added to parse the port number in the ftp control data + and create the corresponding masquerading entry for the + coming data connection. + Although the original ippfvs via NAT could be used to build + a virtual server, the data connection was established in + this way. + Real Server port:20 ----> ippfvs: allocate a free masq port + -----> the client port + It is not elegent but time-consuming. Now it was changed + as follows: + Real Server port:20 ----> ippfvs port: 20 + ----> the client port + +- Change the port checking order in the ip_fw_demasquerade() + If the size of masquerade hash table is well chosen, checking + a masquerading entry in the hash table will just require one + hit. It is much efficient than checking port for virtual + services, and there are at least 3 incoming packets for each + connection, which require port checking. So, it is efficient + to check the masquerading hash table first and then check + port for virtual services. + +- Remove a useless statement in the ip_masq_new_pfvs() + The useless statement in the ip_masq_new_pfvs function is + ip_masq_free_ports[masq_proto_num(proto)]++; + which may disturb system. + +- Change the header printing of the ip_pfvs_procinfo() + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.7 - Febuary 10, 1999 + +Differences with virtual server patch version 0.6: + +- Fix a bug in detect the finish of connection for tunneling + or NATing to the local node. + Since the server reply the client directly in tunneling or + NATing to the local node, the load balancer (LinuxDirector) + can only detect a FIN segment. It is mistake that the masq + entry is removed only if both-side FIN segments are detected, + and then the masq entry expires in 15 minutes. For the + situation above, the code was changed to set the masq entry + expire in TCP_FIN_TIMEOUT (2min) when an incoming FIN segment + is detecting. +- Add the patch version printing in the ip_pfvs_procinfo() + It would be easy for users and hackers to know which + virtual server patch version they are running. Thank + Peter Kese for the suggestion. + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.6 - Febuary 2, 1999 + +Differences with virtual server patch version 0.5: + +- Add the local node feature in virtual server. + If the local node feature is enabled, the load balancer can + not only redirect the packets of the specified port to the + other servers (remote nodes) to process it, but also can process + the packets locally (local node). Which node is chosen depends on + the scheduling algorithms. + This local node feature can be used to build a virtual server of + a few nodes, for example, 2, 3 or more sites, in which it is a + resource waste if the load balancer is only used to redirect + packets. It is wise to direct some packets to the local node to + process. This feature can also be used to build distributed + identical servers, in which one is too busy to handle requests + locally, then it can seamlessly forward requests to other servers + to process them. + This feature can be applied to both virtual server via NAT and + virtual server via IP tunneling. + Thank Peter Kese for idea of "Two node Virtual + Server" and his single line patch for virtual server via IP + tunneling. +- Remove a useless function call ip_send_check in the virtual + server via IP tunneling code. + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.5 - November 25, 1998 + +Differences with virtual server patch version 0.4: + +- Add the feature of virtual server via IP tunneling. + If the ippfvs is enabled using IP tunneling, the load balancer + chooses a real server from a cluster based on a scheduling algorithm, + encapsules the packet and forwards it to the chosen server. All real + servers are configured with "ifconfig tunl0 up". + When the chosen server receives the encapsuled packet, it decapsules + the packet, processes the request and returns the reply packets + directly to the client without passing the load balancer. This can + greatly increase the scalability of virtual server. +- Fix a bug in the ip_portfw_del() for the weighted RR scheduling. + The bug in version 0.4 is when the weighted round-robin scheduling + is used, deleting the last rule for a virtual server will report + "setsockopt failed: Invalid argument" warning, in fact the last + rule is deleted but the gen_scheduling_seq() works on a null list + and causes that warning. +- Add and modify some description for virtual server options in + the Linux kernel configuration help texts. + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.4 - November 12, 1998 + +Differences with virtual server patch version 0.3: + +- Fix a memory access error bug. + The set_serverpointer_null() function is added to scan all the existing + ip masquerading records for its server pointer which points to the + server specified and set it null. It is useful when administrators + delete a real server or all real servers, those pointers pointing to + the server must be set null. Otherwise, decreasing the connection + counter of the server may cause memory access error when the connection + terminates or timeout. + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.3 - November 10, 1998 + +Differences with virtual server patch version 0.2: + +- Change the simple round-robin scheduling to the weighted round-robin + scheduling. Simple is a special instance of the weighted round-robin + scheduling when the weights of the servers are the same. +- The scheduling algorithm, originally called the weighted round-robin + scheduling in version 0.2, actually is the weighted least-connection + scheduling. So the concept is clarified here. +- Add the least-connection scheduling algorithm. Although it is a + special instance of the weighted least-connection scheduling algorithm, + it is used to avoid dividing the weight in looking up servers when + the weights of the servers are the same, so the overhead of scheduling + can be minimized in this case. +- Change the type of the server load variables, curr_load and least_load, + from integer to float in the weighted least-connection scheduling. + It can make a better load-balancing when the weights specified are high. +- Merge the original two patches into one. Users have to specify which + scheduling algorithm is used, the weighted round-robin scheduling, + the least-connection scheduling, or the weighted least-connection + scheduling, before rebuild the kernel. +- Change the ip_pfvs_proc function to make the output of the port + forwarding & virtual server table more beautiful. + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.2 - May 28, 1998 + +Differences with virtual server patch version 0.1: + +- Add the weighted round-robin scheduling patch. + +-------------------------------------------------------------------- + +Virtual Server Patch for Linux - Version 0.1 - May 26, 1998 + +- Implement the infrastructure of virtual server. +- Implement the simple round-robin scheduling algorithm. + +-------------------------------------------------------------------- diff --git a/Documentation/networking/README.ipvs b/Documentation/networking/README.ipvs new file mode 100644 index 000000000000..a7a626b53ae5 --- /dev/null +++ b/Documentation/networking/README.ipvs @@ -0,0 +1,93 @@ +README of Virtual Server Patch for Linux 2.2.10 +-------------------------------------------------------------------- + +Virtual Server Patch for Linux 2.2.10 - Version 0.7 - July 9, 1999 + +Copyright (c) 1998,1999 by Wensong Zhang, Peter Kese. +This is free software. See below for details. + +The ipvs is IP Virtual Server support in Linux kernel, which can be used +to build a high-performance and highly available server. Check out the +Linux Virtual Server Project homepage on the World Wide Web: + http://proxy.iinchina.net/~wensong/ippfvs/ +for the most recent information and original sources about ipvs. + +We now call the Linux box running ipvs LinuxDirector. Thank +Robert Thomas for this name, I love it. :-) + +This patch (Version 0.7) is for the Linux kernel 2.2.10. See the ChangeLog +for how the code has been improved and what new features it has now. + +To rebuild a Linux kernel with virtual server support, first get a clean +copy of the Linux kernel source of the right version and apply the patch +to the kernel. The commands can be as follows: + cd /usr/src/linux + cat /ipvs-0.7-2.2.10.patch | patch -p1 +Then make sure the following kernel compile options at least are selected +via "make menuconfig" or "make xconfig". + +Kernel Compile Options: + +Code maturity level options --- + [*] Prompt for development and/or incomplete code/drivers +Networking options --- + [*] Network firewalls + .... + [*] IP: firewalling + [*] IP: always defragment (required for masquerading) + .... + [*] IP: masquerading + .... + [*] IP: masquerading virtual server support + (12) IP masquerading table size (the Nth power of 2) + < > IPVS: round-robin scheduling + < > IPVS: weighted round-robin scheduling + < > IPVS: weighted least-connection scheduling + < > IPVS: persistent client connection scheduling +Note that you can compile scheduling algorithms in kernel or as modules. + +Finally, rebuild the kernel. Once you have your kernel properly built, +update your system kernel and reboot. + +Note that there are three request dispatching techniques existing together +in the LinuxDirector, and there are also three scheduling algorithms +implemented. Both the VS via IP Tunneling and the VS via Direct Routing +can greatly increase the scalability of virtual server. If the VS-Tunneling +is selected, it requires that all the servers must be configured with + ifconfig tunl0 netmask 255.255.255.255 +If the VS-DRouting is chosen, it requires that all servers must be configured +with the following command: + ifconfig lo:0 netmask 255.255.255.255 +The localnode feature can make that the LinuxDiretor can not only redirect +packets to other servers, but also process packets locally. + +Thanks must go to other contributors, check the CREDITS file to know +who they are. + +There is a mailing list for virtual server. You are welcome to talk about +building the virtual server kernel, using the virtual server and making +the virtual server better there. :-) To subscribe, send a message to + majordomo@iinchina.net +with the body of "subscribe linux-virtualserver". + + +Wensong Zhang + + +-------------------------------------------------------------------- + +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. + +-------------------------------------------------------------------- diff --git a/Documentation/networking/baycom.txt b/Documentation/networking/baycom.txt index 8d5bc3566781..71e0c76dfde7 100644 --- a/Documentation/networking/baycom.txt +++ b/Documentation/networking/baycom.txt @@ -31,7 +31,7 @@ baycom_par: Its devices are called bcp0 through bcp3. baycom_epp: - This driver supports the epp modem. + This driver supports the EPP modem. Its devices are called bce0 through bce3. This driver is work-in-progress. @@ -60,10 +60,10 @@ picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem an additional power supply. Furthermore, it incorporates a carrier detect circuitry. -epp: This is a high speed modem adaptor that connects to an enhanced parallel port. +EPP: This is a high-speed modem adaptor that connects to an enhanced parallel port. Its target audience is users working over a high speed hub (76.8kbit/s). -eppfpga: This is a redesign of the epp adaptor. +eppfpga: This is a redesign of the EPP adaptor. diff --git a/Documentation/networking/cs89x0.txt b/Documentation/networking/cs89x0.txt index 839db8eceb5b..1da37d5ce00b 100644 --- a/Documentation/networking/cs89x0.txt +++ b/Documentation/networking/cs89x0.txt @@ -203,7 +203,7 @@ configuration options are available on the command line: * io=### - specify IO address (200h-360h) * irq=## - specify interrupt level * mmode=##### - specify memory base address -* dma=# - specify dma channel +* dma=# - specify DMA channel * media=rj45 - specify media type or media=2 or media=aui @@ -412,33 +412,33 @@ its ability to communicate across the ISA bus based on the system resources assigned during hardware configuration. The following tests are performed: * IO Register Read/Write Test - The IO Register Read/Write test insures that the CS8900/20 can be + The IO Register Read/Write test ensures that the CS8900/20 can be accessed in IO mode, and that the IO base address is correct. * Shared Memory Test - The Shared Memory test insures the CS8900/20 can be accessed in memory + The Shared Memory test ensures the CS8900/20 can be accessed in memory mode and that the range of memory addresses assigned does not conflict with other devices in the system. * Interrupt Test - The Interrupt test insures there are no conflicts with the assigned IRQ + The Interrupt test ensures there are no conflicts with the assigned IRQ signal. * EEPROM Test - The EEPROM test insures the EEPROM can be read. + The EEPROM test ensures the EEPROM can be read. * Chip RAM Test - The Chip RAM test insures the 4K of memory internal to the CS8900/20 is + The Chip RAM test ensures the 4 K of memory internal to the CS8900/20 is working properly. * Internal Loop-back Test - The Internal Loop Back test insures the adapter's transmitter and + The Internal Loop Back test ensures the adapter's transmitter and receiver are operating properly. If this test fails, make sure the adapter's cable is connected to the network (check for LED activity for example). * Boot PROM Test - The Boot PROM test insures the Boot PROM is present, and can be read. + The Boot PROM test ensures the Boot PROM is present, and can be read. Failure indicates the Boot PROM was not successfully read due to a hardware problem or due to a conflicts on the Boot PROM address assignment. (Test only applies if the adapter is configured to use the @@ -564,7 +564,7 @@ Crystal's CS89XX Technical Application Support can be reached at: Telephone :(800) 888-5016 (from inside U.S. and Canada) :(512) 442-7555 (from outside the U.S. and Canada) Fax :(512) 912-3871 -Email :ethernet@crystal.cirrus.com +E-mail :ethernet@crystal.cirrus.com WWW :http://www.crystal.com diff --git a/Documentation/networking/multicast.txt b/Documentation/networking/multicast.txt index 2bd6fd9ba7a7..d426f36dbadc 100644 --- a/Documentation/networking/multicast.txt +++ b/Documentation/networking/multicast.txt @@ -1,11 +1,13 @@ -Behaviour of cards under Multicast. This is how they currently -behave not what the hardware can do - i.e. the lance driver doesn't -use its filter, even though the code for loading it is in the DEC -lance based driver. +Behaviour of Cards Under Multicast +================================== -The following multicast requirements are needed +This is how they currently behave, not what the hardware can do--for example, +the Lance driver doesn't use its filter, even though the code for loading +it is in the DEC Lance-based driver. + +The following are requirements for multicasting ----------------------------------------------- -Appletalk Multicast hardware filtering not important but +AppleTalk Multicast hardware filtering not important but avoid cards only doing promisc IP-Multicast Multicast hardware filters really help IP-MRoute AllMulti hardware filters are of no help diff --git a/Documentation/sound/MAD16 b/Documentation/sound/MAD16 index 6f5f651673c0..8dda947f049d 100644 --- a/Documentation/sound/MAD16 +++ b/Documentation/sound/MAD16 @@ -32,3 +32,22 @@ options sb mad16=1 options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is + +------------------------------------------------------------------------ +The mad16 module in addition supports the following options: + +option: meaning: default: +joystick=0,1 disabled, enabled disabled +cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled + 0x06,0x08,0x0a Mitsumi, Panasonic, + Secondary IDE, Primary IDE +cdport=0x340,0x320, 0x340 + 0x330,0x360 +cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled +cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE +cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic +opl4=0,1 OPL3, OPL4 OPL3 + +for more details see linux/drivers/sound/mad16.c + +Rui Sousa diff --git a/Documentation/video4linux/README.buz b/Documentation/video4linux/README.buz new file mode 100644 index 000000000000..b9eb9cd74f3f --- /dev/null +++ b/Documentation/video4linux/README.buz @@ -0,0 +1,212 @@ +Iomega Buz Driver for Linux +=========================== + +by Rainer Johanni + +Compiling and Loading the Driver +================================ + +You must run a 2.2.x kernel in order to use this driver. + +To compile the driver, just type make. + +Besides the files in this directory, the driver needs the +'videodev' and the 'i2c' module from the Linux kernel. +In order to get these modules available, enable module support +for VIDEODEV and BTTV (which implies i2c) in your kernel +configuration. You find these devices in the menu +"Character Devices" in your Kernel Configuration. + +Before you load the driver you must have a video device +at major device node 81. If you don't have it yet, do the +following (as root!): + +cd /dev +mknod video0 c 81 0 +ln -s video0 video + +Edit the 'update' script if you want to give the driver +special options and then type (as root) + +./update + +to insert all the necessary modules into the kernel. + +If you want to make full use of the Video for Linux uncompressed +grabbing facilities, you must either + +- obtain and install the "big_physarea patch" for your kernel and + set aside the necessary memory during boot time. + There seem to be several versions of this patch against + various kernel versions floating around in the net, + you may obtain one e.g. from: + http://www.polyware.nl/~middelin/patch/bigphysarea-2.2.1.tar.gz + You also have to compile your driber AFTER installing that patch + in order to get it working + + or + +- start your kernel with the mem=xxx option, where xxx is your + real memory minus the memory needed for the buffers. + For doing this add an entry in lilo.conf (if you use lilo): + append "mem=xxxM" + or add a line in your linux.par file (if you use loadlin): + mem=xxxM + +The second method is by far easier, however it is dangerous +if more than one driver at a time has the idea to use the memory +leftover by setting the mem=xxx parameter below the actual +memory size. + +Read also below how to use this memory! + + + +Driver Options +============== + +You are able to customize the behavior of the driver by giving +it some options at start time. + +default_input, default_norm +--------------------------- + +As soon as the driver is loaded, the Buz samples video signals +from one of its input ports and displays it on its output. +The driver uses the Composite Input and the video norm PAL for this. +If you want to change this default behavior, set default_input=1 +(for S-VHS input) or default_norm=1 for NTSC. + +v4l_nbufs, v4l_bufsize +---------------------- + +In order to make to make full use of the Video for Linux picture +grabbing facilities of the driver (which are needed by many +Video for Linux applications), the driver needs a set of +physically contiguous buffers for grabbing. These parameters +determine how many buffers of which size the driver will +allocate at open (the open will fail if it is unable to do so!). + +These values do not affect the MJPEG grabbing facilities of the driver, +they are needed for uncompressed image grabbing only!!! + +v4l_nbufs is the number of buffers to allocate, a value of 2 (the default) +should be sufficient in allmost all cases. Only special applications +(streaming captures) will need more buffers and then mostly the +MJPEG capturing features of the Buz will be more apropriate. +So leave this parameter at it's default unless you know what you do. + +The things for v4l_bufsize are more complicated: +v4l_bufsize is set by default to 128 [KB] which is the maximum +amount of physically contiguous memory Linux is able to allocate +without kernel changes. This is sufficient for grabbing 24 bit color images +up to sizes of approx. 240x180 pixels (240*180*3 = 129600, 128 KB = 131072). + +In order to be able to capture bigger images you have either to +- obtain and install the "big_physarea patch" and set aside + the necessary memory during boot time or +- start your kernel with the mem=xxx option, where xxx is your + real memory minus the memory needed for the buffers. +In that case, usefull settings for v4l_bufsize are +- 1296 [Kb] for grabbing 24 bit images of max size 768*576 +- 1728 [Kb] for 32bit images of same size (4*768*576 = 1728 Kb!) +You may reduce these numbers accordingly if you know you are only +grabbing 720 pixels wide images or NTSC images (max height 480). + +In some cases it may happen that Linux isn't even able to obtain +the default 128 KB buffers. If you don't need uncompressed image +grabbing at all, set v4l_bufsize to an arbitrary small value (e.g. 4) +in order to be able to open the video device. + +vidmem +------ + +The video mem address of the video card. +The driver has a little database for some videocards +to determine it from there. If your video card is not in there +you have either to give it to the driver as a parameter +or set in in a VIDIOCSFBUF ioctl + +The videocard database is contained in the file "videocards.h" +Gernot Ziegler wants to keep an actual version of that file. +If your card is not contained in that file, look at +http://www.lysator.liu.se/~gz/buz/ for an actual version of +"videocards.h". + +triton, natoma +-------------- + +The driver tries to detect if you have a triton or natome chipset +in order to take special messures for these chipsets. +If this detection fails but you are sure you have such a chipset, +set the corresponding variable to 1. +This is a very special option and may go away in the future. + + + +Programming interface +===================== + +This driver should be fully compliant to Video for Linux, so all +tools working with Video for Linux should work with (hopefully) +no problems. + +A description of the Video for Linux programming interace can be found at: +http://roadrunner.swansea.linux.org.uk/v4lapi.shtml + +Besides the Video for Linux interface, the driver has a "proprietary" +interface for accessing the Buz's MJPEG capture and playback facilities. + +The ioctls for that interface are as follows: + +BUZIOC_G_PARAMS +BUZIOC_S_PARAMS + +Get and set the parameters of the buz. The user should allways +do a BUZIOC_G_PARAMS (with a struct buz_params) to obtain the default +settings, change what he likes and then make a BUZIOC_S_PARAMS call. +A typical application should at least set the members +input, norm and decimation of the struct buz_params. +For a full description of all members see "buz.h" + +BUZIOC_REQBUFS + +Before being able to capture/playback, the user has to request +the buffers he is wanting to use. Fill the structure +buz_requestbuffers with the size (recommended: 256*1024) and +the number (recommended 32 up to 256). There are no such restrictions +as for the Video for Linux buffers, you should LEAVE SUFFICIENT +MEMORY for your system however, else strange things will happen .... +On return, the buz_requestbuffers structure contains number and +size of the actually allocated buffers. +You should use these numbers for doing a mmap of the buffers +into the user space. +The BUZIOC_REQBUFS ioctl also makes it happen, that the next mmap +maps the MJPEG buffer instead of the V4L buffers. + +BUZIOC_QBUF_CAPT +BUZIOC_QBUF_PLAY + +Queue a buffer for capture or playback. The first call also starts +streaming capture. When streaming capture is going on, you may +only queue further buffers or issue syncs until streaming +capture is switched off again with a argument of -1 to +a BUZIOC_QBUF_CAPT/BUZIOC_QBUF_PLAY ioctl. + +BUZIOC_SYNC + +Issue this ioctl when all buffers are queued. This ioctl will +block until the first buffer becomes free for saving its +data to disk (after BUZIOC_QBUF_CAPT) or for reuse (after BUZIOC_QBUF_PLAY). + +BUZIOC_G_STATUS + +Get the status of the input lines (video source connected/norm). +This ioctl may be subject to change. + + + + + +See the examples directory delivered with this driver +for actual coding examples! diff --git a/MAINTAINERS b/MAINTAINERS index 643f0c0bc23d..d9b201db0985 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -89,10 +89,9 @@ S: Maintained 8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] P: Paul Gortmaker -M: gpg109@rsphy1.anu.edu.au +M: p_gortmaker@yahoo.com L: linux-net@vger.rutgers.edu S: Maintained -W: http://rsphy1.anu.edu.au/~gpg109/ne2000.html AD1816 SOUND DRIVER P: Thorsten Knabe @@ -660,7 +659,7 @@ S: Maintained REAL TIME CLOCK DRIVER P: Paul Gortmaker -M: gpg109@rsphy1.anu.edu.au +M: p_gortmaker@yahoo.com L: linux-kernel@vger.rutgers.edu S: Maintained diff --git a/Makefile b/Makefile index f3acf3d26480..a67314ae3400 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 11 +SUBLEVEL = 12 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -158,6 +158,10 @@ ifeq ($(CONFIG_FC4),y) DRIVERS := $(DRIVERS) drivers/fc4/fc4.a endif +ifeq ($(CONFIG_NET_FC),y) +DRIVERS := $(DRIVERS) drivers/net/fc/fc.a +endif + ifdef CONFIG_PPC DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a endif diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 9dc9f1cc5a2d..a89d4c9e3d9d 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -55,7 +55,6 @@ unsigned long init_user_stack[1024] = { STACK_MAGIC, }; static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/alpha/math-emu/Makefile b/arch/alpha/math-emu/Makefile index b1812b9923be..e007669c415d 100644 --- a/arch/alpha/math-emu/Makefile +++ b/arch/alpha/math-emu/Makefile @@ -2,19 +2,11 @@ # Makefile for math-emulator files... # -O_TARGET := math-emu.o -O_OBJS := fp-emul.o faddd.o fadds.o fdivd.o fdivs.o fdtoi.o \ - fdtos.o fdtox.o fmuld.o fmuls.o fsmuld.o fsqrtd.o \ - fsqrts.o fstod.o fstoi.o fstox.o fsubd.o fsubs.o \ - cmptxx.o fxtos.o fxtod.o udivmodti4.o div128.o - -LINKS := double.h faddd.c fadds.c fdivd.c fdivs.c fdtoi.c \ - fdtos.c fdtox.c fmuld.c fmuls.c fsmuld.c fsqrtd.c \ - fsqrts.c fstod.c fstoi.c fstox.c fsubd.c fsubs.c \ - op-common.h op-1.h op-2.h op-4.h single.h soft-fp.h \ - udivmodti4.c - +O_TARGET := math-emu.o +O_OBJS := fp-emul.o udivmodti4.o +LINKS := double.h op-common.h op-1.h op-2.h op-4.h single.h \ + soft-fp.h udivmodti4.c ifeq ($(CONFIG_MATHEMU),m) M_OBJS := $(O_TARGET) diff --git a/arch/alpha/math-emu/cmptxx.c b/arch/alpha/math-emu/cmptxx.c deleted file mode 100644 index eae156f0a26a..000000000000 --- a/arch/alpha/math-emu/cmptxx.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int CMPTXX(void *rc, void *rb, void *ra, int type) -{ - FP_DECL_D(A); FP_DECL_D(B); - long ret; - - __FP_UNPACK_D(A, ra); - __FP_UNPACK_D(B, rb); - FP_CMP_D(ret, A, B, 3); - if(ret == type) { - *(unsigned long *)rc = 0x4000000000000000; - } - else if((type == CMPTXX_LE) && - ((ret == CMPTXX_LT) || (ret == CMPTXX_EQ))) { - *(unsigned long *)rc = 0x4000000000000000; - } - else { - *(unsigned long *)rc = 0; - } - return 0; -} diff --git a/arch/alpha/math-emu/div128.c b/arch/alpha/math-emu/div128.c deleted file mode 100644 index 39829cfd1a87..000000000000 --- a/arch/alpha/math-emu/div128.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright stuff - - Use of this program, for any purpose, is granted the author, - Ian Kaplan, as long as this copyright notice is included in - the source code or any source code derived from this program. - The user assumes all responsibility for using this code. - - Ian Kaplan, October 1996 - -*/ - -#define HI 0 -#define LO 1 - -void set128(unsigned long *n, unsigned long hi, unsigned long lo) -{ - n[HI] = hi; - n[LO] = lo; -} - -int eq128(unsigned long *n1, unsigned long *n2) -{ - return((n1[HI] == n2[HI]) && (n1[LO] == n2[LO])); -} - -int gt128(unsigned long *n1, unsigned long *n2) -{ - return((n1[HI] > n2[HI]) || - ((n1[HI] == n2[HI]) && (n1[LO] > n2[LO]))); -} - -int lt128(unsigned long *n1, unsigned long *n2) -{ - return((n1[HI] < n2[HI]) || - ((n1[HI] == n2[HI]) && (n1[LO] < n2[LO]))); -} - - -void copy128(unsigned long *dest, unsigned long *src) -{ - dest[HI] = src[HI]; - dest[LO] = src[LO]; -} - -/* Shift the given bit into the octaword from the right - * (i.e. left-shift-1, or in low bit). If "bit" is zero, - * then this is a simple left shift. - */ -void shiftin128(unsigned long *n, unsigned long bit) -{ - n[HI] <<= 1; - if(n[LO] & 0x8000000000000000) { - n[HI] |= 1; - } - n[LO] = (n[LO] << 1) | bit; -} - -void sub128(unsigned long *n1, unsigned long *n2, unsigned long *result) -{ - if(n1[LO] < n2[LO]) { - result[LO] = n1[LO] - n2[LO]; - result[HI] = n1[HI] - n2[HI] - 1; - } - else { - result[LO] = n1[LO] - n2[LO]; - result[HI] = n1[HI] - n2[HI]; - } -} - - -void udiv128(unsigned long *dividend, - unsigned long *divisor, - unsigned long *quotient, - unsigned long *remainder ) -{ - unsigned long zero[2]; - unsigned long t[2], num_bits; - unsigned long q, bit; - unsigned long rem[2]; - int i; - - set128(remainder, 0, 0); - set128(quotient, 0, 0); - set128(zero, 0, 0); - - if (eq128(divisor, zero)) { - return; - } - - if(gt128(divisor, dividend)) { - copy128(remainder, dividend); - return; - } - - if (eq128(divisor, dividend)) { - set128(quotient, 0, 1); - return; - } - - num_bits = 128; - - while(1) { - bit = (dividend[HI] & 0x8000000000000000) >> 63; - copy128(rem, remainder); - shiftin128(rem, bit); - if(lt128(rem, divisor)) break; - copy128(remainder, rem); - shiftin128(dividend, 0); - num_bits--; - } - - for (i = 0; i < num_bits; i++) { - bit = (dividend[HI] & 0x8000000000000000) >> 63; - shiftin128(remainder, bit); - sub128(remainder, divisor, t); - q = !((t[HI] & 0x8000000000000000) >> 63); - shiftin128(dividend, 0); - shiftin128(quotient, q); - if (q) { - copy128(remainder, t); - } - } -} /* unsigned_divide128 */ - diff --git a/arch/alpha/math-emu/fp-emul.c b/arch/alpha/math-emu/fp-emul.c index 2ded8192a2fc..9ab3b710b2bc 100644 --- a/arch/alpha/math-emu/fp-emul.c +++ b/arch/alpha/math-emu/fp-emul.c @@ -6,26 +6,10 @@ #include #include "soft-fp.h" - -extern int CMPTXX(void *, void *, void *, int); -extern int FXTOS(void *, void *); -extern int FXTOD(void *, void *); -extern int FDTOS(void *, void *); -extern int FSTOD(void *, void *); -extern int FDIVS(void *, void *, void *); -extern int FDIVD(void *, void *, void *); -extern int FMULS(void *, void *, void *); -extern int FMULD(void *, void *, void *); -extern int FSUBS(void *, void *, void *); -extern int FSUBD(void *, void *, void *); -extern int FADDS(void *, void *, void *); -extern int FADDD(void *, void *, void *); -extern int FDTOX(void *, void *); -extern int FSQRTS(void *, void *); -extern int FSQRTD(void *, void *); +#include "double.h" +#include "single.h" #define OPC_PAL 0x00 - #define OPC_INTA 0x10 #define OPC_INTL 0x11 #define OPC_INTS 0x12 @@ -34,42 +18,26 @@ extern int FSQRTD(void *, void *); #define OPC_FLTV 0x15 #define OPC_FLTI 0x16 #define OPC_FLTL 0x17 - #define OPC_MISC 0x18 - #define OPC_JSR 0x1a -#define OP_FUN(OP,FUN) ((OP << 26) | (FUN << 5)) - -/* - * "Base" function codes for the FLTI-class instructions. - * Note that in most cases these actually correspond to the "chopped" - * form of the instruction. Not to worry---we extract the qualifier - * bits separately and deal with them separately. Notice that base - * function code 0x2c is used for both CVTTS and CVTST. The other bits - * in the function code are used to distinguish the two. - */ -#define FLTI_FUNC_ADDS OP_FUN(OPC_FLTI, 0x000) -#define FLTI_FUNC_ADDT OP_FUN(OPC_FLTI, 0x020) -#define FLTI_FUNC_CMPTEQ OP_FUN(OPC_FLTI, 0x025) -#define FLTI_FUNC_CMPTLT OP_FUN(OPC_FLTI, 0x026) -#define FLTI_FUNC_CMPTLE OP_FUN(OPC_FLTI, 0x027) -#define FLTI_FUNC_CMPTUN OP_FUN(OPC_FLTI, 0x024) -#define FLTI_FUNC_CVTTS_or_CVTST OP_FUN(OPC_FLTI, 0x02c) -#define FLTI_FUNC_CVTTQ OP_FUN(OPC_FLTI, 0x02f) -#define FLTI_FUNC_CVTQS OP_FUN(OPC_FLTI, 0x03c) -#define FLTI_FUNC_CVTQT OP_FUN(OPC_FLTI, 0x03e) -#define FLTI_FUNC_DIVS OP_FUN(OPC_FLTI, 0x003) -#define FLTI_FUNC_DIVT OP_FUN(OPC_FLTI, 0x023) -#define FLTI_FUNC_MULS OP_FUN(OPC_FLTI, 0x002) -#define FLTI_FUNC_MULT OP_FUN(OPC_FLTI, 0x022) -#define FLTI_FUNC_SUBS OP_FUN(OPC_FLTI, 0x001) -#define FLTI_FUNC_SUBT OP_FUN(OPC_FLTI, 0x021) - -#define FLTC_FUNC_SQRTS OP_FUN(OPC_FLTC, 0x00B) -#define FLTC_FUNC_SQRTT OP_FUN(OPC_FLTC, 0x02B) - -#define FLTL_FUNC_CVTQL OP_FUN(OPC_FLTL, 0x030) +#define FOP_SRC_S 0 +#define FOP_SRC_T 2 +#define FOP_SRC_Q 3 + +#define FOP_FNC_ADDx 0 +#define FOP_FNC_CVTQL 0 +#define FOP_FNC_SUBx 1 +#define FOP_FNC_MULx 2 +#define FOP_FNC_DIVx 3 +#define FOP_FNC_CMPxUN 4 +#define FOP_FNC_CMPxEQ 5 +#define FOP_FNC_CMPxLT 6 +#define FOP_FNC_CMPxLE 7 +#define FOP_FNC_SQRTx 11 +#define FOP_FNC_CVTxS 12 +#define FOP_FNC_CVTxT 14 +#define FOP_FNC_CVTxQ 15 #define MISC_TRAPB 0x0000 #define MISC_EXCB 0x0400 @@ -115,6 +83,67 @@ void cleanup_module(void) #endif /* MODULE */ +/* For 128-bit division. */ + +__complex__ unsigned long +udiv128(unsigned long divisor_f0, unsigned long divisor_f1, + unsigned long dividend_f0, unsigned long dividend_f1) +{ + _FP_FRAC_DECL_2(quo); + _FP_FRAC_DECL_2(rem); + _FP_FRAC_DECL_2(tmp); + unsigned long i, num_bits, bit; + __complex__ unsigned long ret; + + _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2); + _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2); + + if (_FP_FRAC_ZEROP_2(divisor)) + goto out; + + if (_FP_FRAC_GT_2(divisor, dividend)) { + _FP_FRAC_COPY_2(rem, dividend); + goto out; + } + + if (_FP_FRAC_EQ_2(divisor, dividend)) { + __FP_FRAC_SET_2(quo, 0, 1); + goto out; + } + + num_bits = 128; + while (1) { + bit = _FP_FRAC_NEGP_2(dividend); + _FP_FRAC_COPY_2(tmp, rem); + _FP_FRAC_SLL_2(tmp, 1); + _FP_FRAC_LOW_2(tmp) |= bit; + if (! _FP_FRAC_GE_2(tmp, divisor)) + break; + _FP_FRAC_COPY_2(rem, tmp); + _FP_FRAC_SLL_2(dividend, 1); + num_bits--; + } + + for (i = 0; i < num_bits; i++) { + bit = _FP_FRAC_NEGP_2(dividend); + _FP_FRAC_SLL_2(rem, 1); + _FP_FRAC_LOW_2(rem) |= bit; + _FP_FRAC_SUB_2(tmp, rem, divisor); + bit = _FP_FRAC_NEGP_2(tmp); + _FP_FRAC_SLL_2(dividend, 1); + _FP_FRAC_SLL_2(quo, 1); + if (!bit) { + _FP_FRAC_LOW_2(quo) |= 1; + _FP_FRAC_COPY_2(rem, tmp); + } + } + +out: + __real__ ret = quo_f1; + __imag__ ret = rem_f1; + return ret; +} + /* * Emulate the floating point instruction at address PC. Returns 0 if * emulation fails. Notice that the kernel does not and cannot use FP @@ -125,195 +154,185 @@ void cleanup_module(void) long alpha_fp_emul (unsigned long pc) { - unsigned long op_fun, fa, fb, fc, func, mode; + FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); + FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); + + unsigned long fa, fb, fc, func, mode, src; unsigned long fpcw = current->tss.flags; - unsigned long va, vb, vc, res, fpcr; + unsigned long res, cmptype, va, vb, vc, fpcr; __u32 insn; MOD_INC_USE_COUNT; get_user(insn, (__u32*)pc); - fc = (insn >> 0) & 0x1f; /* destination register */ - fb = (insn >> 16) & 0x1f; - fa = (insn >> 21) & 0x1f; - func = (insn >> 5) & 0x7ff; - mode = (insn >> 11) & 0x3; - op_fun = insn & OP_FUN(0x3f, 0x3f); + fc = (insn >> 0) & 0x1f; /* destination register */ + fb = (insn >> 16) & 0x1f; + fa = (insn >> 21) & 0x1f; + func = (insn >> 5) & 0xf; + src = (insn >> 9) & 0x3; + mode = (insn >> 11) & 0x3; fpcr = rdfpcr(); - /* - * Try the operation in software. First, obtain the rounding - * mode and set it in the task struct - */ - current->tss.flags &= ~IEEE_CURRENT_RM_MASK; if (mode == 3) { - /* dynamic---get rounding mode from fpcr: */ - current->tss.flags |= - (((fpcr&FPCR_DYN_MASK)>>FPCR_DYN_SHIFT)<tss.flags |= (mode << IEEE_CURRENT_RM_SHIFT); - } - - /* JRP - What is this test supposed to check for? */ - if ((IEEE_TRAP_ENABLE_MASK & 0x80 /* was 0xc0 */)) { - extern int something_is_wrong (void); - something_is_wrong(); + /* Dynamic -- get rounding mode from fpcr. */ + mode = (fpcr >> FPCR_DYN_SHIFT) & 3; } - switch (op_fun) { - case FLTI_FUNC_CMPTEQ: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_EQ); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CMPTLT: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_LT); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CMPTLE: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_LE); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CMPTUN: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_UN); - alpha_write_fp_reg(fc, vc); - break; - - case FLTL_FUNC_CVTQL: - /* - * Notice: We can get here only due to an integer - * overflow. Such overflows are reported as invalid - * ops. We return the result the hw would have - * computed. - */ - vb = alpha_read_fp_reg(fb); - vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ - (vb & 0x3fffffff) << 29); /* rest of the integer */ - res = EFLAG_INVALID; - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CVTQS: - vb = alpha_read_fp_reg(fb); - res = FXTOS(&vc, &vb); - alpha_write_fp_reg_s(fc, vc); - break; - - case FLTI_FUNC_CVTQT: - vb = alpha_read_fp_reg(fb); - res = FXTOD(&vc, &vb); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CVTTS_or_CVTST: - if (func == 0x6ac) { - /* - * 0x2ac is also CVTST, but if the /S - * qualifier isn't set, we wouldn't be here in - * the first place... - */ - vb = alpha_read_fp_reg_s(fb); - res = FSTOD(&vc, &vb); - alpha_write_fp_reg(fc, vc); - } else { - vb = alpha_read_fp_reg(fb); - res = FDTOS(&vc, &vb); - alpha_write_fp_reg_s(fc, vc); - } - break; - - case FLTI_FUNC_DIVS: - va = alpha_read_fp_reg_s(fa); - vb = alpha_read_fp_reg_s(fb); - res = FDIVS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; + res = 0; - case FLTI_FUNC_DIVT: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = FDIVD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_MULS: + switch (src) { + case FOP_SRC_S: va = alpha_read_fp_reg_s(fa); vb = alpha_read_fp_reg_s(fb); - res = FMULS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; - - case FLTI_FUNC_MULT: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = FMULD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; + + __FP_UNPACK_S(SA, &va); + __FP_UNPACK_S(SB, &vb); + + switch (func) { + case FOP_FNC_SUBx: + if (SB_c != FP_CLS_NAN) + SB_s ^= 1; + /* FALLTHRU */ + case FOP_FNC_ADDx: + FP_ADD_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_MULx: + FP_MUL_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_DIVx: + if (SB_c == FP_CLS_ZERO && SA_c != FP_CLS_ZERO) { + res |= EFLAG_DIVZERO; + if (__FPU_TRAP_P(EFLAG_DIVZERO)) + goto done; + } + FP_DIV_S(SR, SA, SB); + goto pack_s; - case FLTI_FUNC_SUBS: - va = alpha_read_fp_reg_s(fa); - vb = alpha_read_fp_reg_s(fb); - res = FSUBS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; + case FOP_FNC_SQRTx: + FP_SQRT_S(SR, SA); + goto pack_s; + } + goto bad_insn; - case FLTI_FUNC_SUBT: + case FOP_SRC_T: va = alpha_read_fp_reg(fa); vb = alpha_read_fp_reg(fb); - res = FSUBD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; + + __FP_UNPACK_D(DA, &va); + __FP_UNPACK_D(DB, &vb); + + switch (func) { + case FOP_FNC_SUBx: + if (DB_c != FP_CLS_NAN) + DB_s ^= 1; + /* FALLTHRU */ + case FOP_FNC_ADDx: + FP_ADD_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_MULx: + FP_MUL_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_DIVx: + if (DB_c == FP_CLS_ZERO && DA_c != FP_CLS_ZERO) { + res |= EFLAG_DIVZERO; + if (__FPU_TRAP_P(EFLAG_DIVZERO)) + goto done; + } + FP_DIV_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_CMPxUN: + cmptype = CMPTXX_UN; + goto compare; + case FOP_FNC_CMPxEQ: + cmptype = CMPTXX_EQ; + goto compare; + case FOP_FNC_CMPxLT: + cmptype = CMPTXX_LT; + goto compare; + case FOP_FNC_CMPxLE: + cmptype = CMPTXX_LE; + goto compare; + compare: + FP_CMP_D(res, DA, DB, 3); + vc = 0; + if (res == cmptype + || (cmptype == CMPTXX_LE + && (res == CMPTXX_LT || res == CMPTXX_EQ))) { + vc = 0x4000000000000000; + } + goto done_d; + + case FOP_FNC_SQRTx: + FP_SQRT_D(DR, DA); + goto pack_d; + + case FOP_FNC_CVTxS: + /* It is irritating that DEC encoded CVTST with + SRC == T_floating. It is also interesting that + the bit used to tell the two apart is /U... */ + if (insn & 0x2000) { + FP_CONV(S,D,1,1,SR,DA); + goto pack_s; + } else { + /* CVTST need do nothing else but copy the + bits and repack. */ + DR_c = DA_c; + DR_s = DA_s; + DR_e = DA_e; + DR_r = DA_r; + DR_f = DA_f; + goto pack_d; + } - case FLTI_FUNC_ADDS: - va = alpha_read_fp_reg_s(fa); - vb = alpha_read_fp_reg_s(fb); - res = FADDS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; + case FOP_FNC_CVTxQ: + FP_TO_INT_D(vc, DA, 64, 1); + res = _FTOI_RESULT(DA); + goto done_d; + } + goto bad_insn; - case FLTI_FUNC_ADDT: - va = alpha_read_fp_reg(fa); + case FOP_SRC_Q: vb = alpha_read_fp_reg(fb); - res = FADDD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; - case FLTI_FUNC_CVTTQ: - vb = alpha_read_fp_reg(fb); - res = FDTOX(&vc, &vb); - alpha_write_fp_reg(fc, vc); - break; + switch (func) { + case FOP_FNC_CVTQL: + /* Notice: We can get here only due to an integer + overflow. Such overflows are reported as invalid + ops. We return the result the hw would have + computed. */ + vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ + (vb & 0x3fffffff) << 29); /* rest of the int */ + res = EFLAG_INVALID; + goto done_d; + + case FOP_FNC_CVTxS: + FP_FROM_INT_S(SR, ((long)vb), 64, long); + goto pack_s; + + case FOP_FNC_CVTxT: + FP_FROM_INT_D(DR, ((long)vb), 64, long); + goto pack_d; + } + goto bad_insn; + } + goto bad_insn; - case FLTC_FUNC_SQRTS: - vb = alpha_read_fp_reg_s(fb); - res = FSQRTS(&vc, &vb); - alpha_write_fp_reg_s(fc, vc); - break; +pack_s: + res |= __FP_PACK_S(&vc, SR); + alpha_write_fp_reg_s(fc, vc); + goto done; - case FLTC_FUNC_SQRTT: - vb = alpha_read_fp_reg(fb); - res = FSQRTD(&vc, &vb); - alpha_write_fp_reg(fc, vc); - break; - - default: - printk("alpha_fp_emul: unexpected function code %#lx at %#lx\n", - func & 0x3f, pc); - MOD_DEC_USE_COUNT; - return 0; - } +pack_d: + res |= __FP_PACK_D(&vc, DR); +done_d: + alpha_write_fp_reg(fc, vc); + goto done; /* * Take the appropriate action for each possible @@ -327,9 +346,11 @@ alpha_fp_emul (unsigned long pc) * In addition, properly track the exception state in software * as described in the Alpha Architectre Handbook section 4.7.7.3. */ +done: if (res) { /* Record exceptions in software control word. */ - current->tss.flags = fpcw |= (res << IEEE_STATUS_TO_EXCSUM_SHIFT); + current->tss.flags + = fpcw |= (res << IEEE_STATUS_TO_EXCSUM_SHIFT); /* Update hardware control register */ fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); @@ -343,16 +364,19 @@ alpha_fp_emul (unsigned long pc) } } - /* We used to write the destination register here, but - * DEC FORTRAN requires that the result *always* be - * written... so we do the write immediately after - * the operations above. - */ + /* We used to write the destination register here, but DEC FORTRAN + requires that the result *always* be written... so we do the write + immediately after the operations above. */ MOD_DEC_USE_COUNT; return 1; -} +bad_insn: + printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n", + insn, pc); + MOD_DEC_USE_COUNT; + return 0; +} long alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) diff --git a/arch/alpha/math-emu/fxtod.c b/arch/alpha/math-emu/fxtod.c deleted file mode 100644 index b0063235c3f2..000000000000 --- a/arch/alpha/math-emu/fxtod.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FXTOD(void *rd, void *rs2) -{ - FP_DECL_D(R); - long a = *(long *)rs2; - - FP_FROM_INT_D(R, a, 64, long); - return __FP_PACK_D(rd, R); -} diff --git a/arch/alpha/math-emu/fxtos.c b/arch/alpha/math-emu/fxtos.c deleted file mode 100644 index 2d9bc46393ea..000000000000 --- a/arch/alpha/math-emu/fxtos.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FXTOS(void *rd, void *rs2) -{ - FP_DECL_S(R); - long a = *(long *)rs2; - - FP_FROM_INT_S(R, a, 64, long); - return __FP_PACK_S(rd, R); -} diff --git a/arch/alpha/math-emu/sfp-machine.h b/arch/alpha/math-emu/sfp-machine.h index 14650cbf4335..9557f5aa6dda 100644 --- a/arch/alpha/math-emu/sfp-machine.h +++ b/arch/alpha/math-emu/sfp-machine.h @@ -38,13 +38,13 @@ #define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0 /* On some architectures float-to-int conversions return a result - * code. On others (e.g. Sparc) they return 0 - */ -#define _FTOI_RESULT A_r + code. On others (e.g. Sparc) they return 0. */ +#define _FTOI_RESULT(X) X##_r -#define _FP_KEEPNANFRACP 1 +#define _FP_KEEPNANFRACP 1 -/* Alpha Architecture Manual Section 4.7.10.4: Propagating NaN Values, +/* + * Alpha Architecture Manual Section 4.7.10.4: Propagating NaN Values, * summary: * * The first of the following rules that is applicable governs the @@ -66,29 +66,25 @@ #define _FP_CHOOSENAN(fs, wc, R, X, Y) \ do { \ R##_r |= (X##_r | Y##_r); \ - if(_FP_IS_NAN(fs, Y)) { \ + if (_FP_IS_NAN(fs, Y)) { \ R##_s = Y##_s; \ R##_c = FP_CLS_NAN; \ - if(_FP_IS_QNAN(fs, Y)) { /* Rule 1 */ \ + if (_FP_IS_QNAN(fs, Y)) { /* Rule 1 */ \ _FP_FRAC_COPY_##wc(R,Y); \ - } \ - else { /* Rule 2 */ \ + } else { /* Rule 2 */ \ _FP_FRAC_SET_##wc(R,Y##_f | _FP_QNANBIT_##fs); \ R##_r = EFLAG_INVALID; \ } \ - } \ - else if(_FP_IS_NAN(fs, X)) { \ + } else if (_FP_IS_NAN(fs, X)) { \ R##_s = X##_s; \ R##_c = FP_CLS_NAN; \ - if(_FP_IS_QNAN(fs, X)) { /* Rule 3 */ \ + if (_FP_IS_QNAN(fs, X)) { /* Rule 3 */ \ _FP_FRAC_COPY_##wc(R,X); \ - } \ - else { /* Rule 4 */ \ + } else { /* Rule 4 */ \ _FP_FRAC_SET_##wc(R,X##_f | _FP_QNANBIT_##fs); \ R##_r |= EFLAG_INVALID; \ } \ - } \ - else { /* Rule 5 */ \ + } else { /* Rule 5 */ \ R##_s = 1; \ R##_c = FP_CLS_NAN; \ _FP_FRAC_SET_##wc(R,_FP_QNANBIT_##fs | EFLAG_MASK); \ @@ -98,18 +94,16 @@ /* Rules 3 and 4 don't apply to functions of only one argument */ #define _FP_CHOOSENAN_1(fs, wc, R, X) \ do { \ - if(_FP_IS_NAN(fs, X)) { \ + if (_FP_IS_NAN(fs, X)) { \ R##_s = X##_s; \ R##_c = FP_CLS_NAN; \ - if(_FP_IS_QNAN(fs, X)) { /* Rule 1 */ \ + if (_FP_IS_QNAN(fs, X)) { /* Rule 1 */ \ _FP_FRAC_COPY_##wc(R,X); \ - } \ - else { /* Rule 2 */ \ + } else { /* Rule 2 */ \ _FP_FRAC_SET_##wc(R,X##_f | _FP_QNANBIT_##fs); \ R##_r |= EFLAG_INVALID; \ } \ - } \ - else { /* Rule 5 */ \ + } else { /* Rule 5 */ \ R##_s = 1; \ R##_c = FP_CLS_NAN; \ _FP_FRAC_SET_##wc(R,_FP_QNANBIT_##fs | EFLAG_MASK); \ @@ -119,45 +113,44 @@ #define _FP_CHOOSENAN_SQRT _FP_CHOOSENAN_1 -#define __FP_UNPACK_DENORM(fs, wc, X) \ - { \ +#define __FP_UNPACK_DENORM(fs, wc, X) \ + do { \ _FP_I_TYPE _shift; \ X##_r |= EFLAG_DENORM; \ - if(_FP_DENORM_TO_ZERO) { \ + if (_FP_DENORM_TO_ZERO) { \ /* Crunching a nonzero denorm to zero necessarily makes */ \ /* the result inexact */ \ X##_r |= EFLAG_INEXACT; \ _FP_FRAC_SET_##wc(X, 0); \ X##_c = FP_CLS_ZERO; \ - } \ - else { \ + } else { \ _FP_FRAC_CLZ_##wc(_shift, X); \ _shift -= _FP_FRACXBITS_##fs; \ _FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS)); \ X##_e -= _FP_EXPBIAS_##fs - 1 + _shift; \ X##_c = FP_CLS_NORMAL; \ } \ - } + } while (0) -#define __FP_UNPACK_RAW_1(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f = _flo->bits.frac; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ +#define __FP_UNPACK_RAW_1(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f = _flo->bits.frac; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ } while (0) -#define __FP_UNPACK_RAW_2(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f0 = _flo->bits.frac0; \ - X##_f1 = _flo->bits.frac1; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ +#define __FP_UNPACK_RAW_2(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f0 = _flo->bits.frac0; \ + X##_f1 = _flo->bits.frac1; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ } while (0) #define __FP_UNPACK_S(X,val) \ @@ -178,25 +171,25 @@ _FP_UNPACK_CANONICAL(Q,2,X); \ } while (0) -#define __FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ } while (0) -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ } while (0) @@ -223,7 +216,7 @@ do { \ _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs); \ else if (!_FP_FRAC_ZEROP_##wc(X)) { \ _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \ - R_r |= EFLAG_INEXACT; \ + R##_r |= EFLAG_INEXACT; \ } \ else \ _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ @@ -237,7 +230,7 @@ do { \ _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs); \ else if (!_FP_FRAC_ZEROP_##wc(Y)) { \ _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc); \ - R_r |= EFLAG_INEXACT; \ + R##_r |= EFLAG_INEXACT; \ } \ else \ _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc); \ @@ -412,8 +405,8 @@ do { \ #define _FP_DIV(fs, wc, R, X, Y) \ do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ + /* Propagate any flags that may have been set during unpacking */ \ + R##_r |= (X##_r | Y##_r); \ R##_s = X##_s ^ Y##_s; \ switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ { \ @@ -512,17 +505,14 @@ do { \ }) -/* We only actually write to the destination register - * if exceptions signalled (if any) will not trap. - */ -#define __FPU_TEM ((current->tss.flags)& IEEE_TRAP_ENABLE_MASK) - -#define __FPU_TRAP_P(bits) \ - ((__FPU_TEM & (bits)) != 0) +/* We only actually write to the destination register if exceptions + signalled (if any) will not trap. */ +#define __FPU_TEM (current->tss.flags & IEEE_TRAP_ENABLE_MASK) +#define __FPU_TRAP_P(bits) ((__FPU_TEM & (bits)) != 0) #define __FP_PACK_S(val,X) \ ({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ + if (!__exc || !__FPU_TRAP_P(__exc)) \ __FP_PACK_RAW_1(S,val,X); \ __exc; \ }) @@ -542,9 +532,7 @@ do { \ }) /* Obtain the current rounding mode. */ -#define FP_ROUNDMODE \ - (((current->tss.flags)&IEEE_CURRENT_RM_MASK)>>IEEE_CURRENT_RM_SHIFT) - +#define FP_ROUNDMODE mode #define FP_RND_NEAREST (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT) #define FP_RND_ZERO (FPCR_DYN_CHOPPED >> FPCR_DYN_SHIFT) #define FP_RND_PINF (FPCR_DYN_PLUS >> FPCR_DYN_SHIFT) @@ -552,48 +540,32 @@ do { \ #define add_ssaaaa(sh, sl, ah, al, bh, bl) \ - __asm__ ("addq %4,%5,%1; cmpult %1,%4,$28; addq %2,%3,%0; addq %0,$28,%0" \ - : "=r" ((UDItype)(sh)), \ - "=r" ((UDItype)(sl)) \ - : "r" ((UDItype)(ah)), \ - "r" ((UDItype)(bh)), \ - "r" ((UDItype)(al)), \ - "r" ((UDItype)(bl))) - + ((sl) = (al) + (bl), (sh) = (ah) + (bh) + ((sl) < (al))) + #define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - __asm__ ("subq %4,%5,%1; cmpult %4,%5,$28; subq %2,%3,%0; subq %0,$28,%0"\ - : "=r" ((UDItype)(sh)), \ - "=&r" ((UDItype)(sl)) \ - : "r" ((UDItype)(ah)), \ - "r" ((UDItype)(bh)), \ - "r" ((UDItype)(al)), \ - "r" ((UDItype)(bl))) - -#define umul_ppmm(wh, wl, u, v) \ - do { \ - __asm__ ("mulq %2,%3,%1; umulh %2,%3,%0" \ - : "=r" ((UDItype)(wh)), \ - "=&r" ((UDItype)(wl)) \ - : "r" ((UDItype)(u)), \ - "r" ((UDItype)(v))); \ - } while (0) + ((sl) = (al) - (bl), (sh) = (ah) - (bh) - ((al) < (bl))) +#define umul_ppmm(wh, wl, u, v) \ + __asm__ ("mulq %2,%3,%1; umulh %2,%3,%0" \ + : "=r" ((UDItype)(wh)), \ + "=&r" ((UDItype)(wl)) \ + : "r" ((UDItype)(u)), \ + "r" ((UDItype)(v))) -#define udiv_qrnnd(q, r, n1, n0, d) \ - do { \ - unsigned long __n[2]; \ - unsigned long __d[2]; \ - unsigned long __q[2]; \ - unsigned long __r[2]; \ - __n[0]=n1; __n[1]=n0; __d[0]=0; __d[1]=d; \ - udiv128(__n, __d, __q, __r); \ - q=__q[1]; r=__r[1]; \ +extern __complex__ unsigned long udiv128(unsigned long, unsigned long, + unsigned long, unsigned long); + +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + __complex__ unsigned long x_; \ + x_ = udiv128((n0), (n1), 0, (d)); \ + (q) = __real__ x_; \ + (r) = __imag__ x_; \ } while (0) #define UDIV_NEEDS_NORMALIZATION 1 -#define abort() \ - return 0 +#define abort() goto bad_insn #ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN -1 @@ -609,13 +581,7 @@ do { \ #define EFLAG_DENORM IEEE_TRAP_ENABLE_DNO #define EFLAG_MASK IEEE_TRAP_ENABLE_MASK -#ifdef FP_TEST_XXX -#define _FP_DENORM_TO_ZERO \ - (tss_flags&IEEE_MAP_DMZ) -#else -#define _FP_DENORM_TO_ZERO \ - ((current->tss.flags)&IEEE_MAP_DMZ) -#endif +#define _FP_DENORM_TO_ZERO ((current->tss.flags) & IEEE_MAP_DMZ) /* Comparison operations */ #define CMPTXX_EQ 0 diff --git a/arch/i386/config.in b/arch/i386/config.in index c4e882510e7d..e083337bda74 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -172,7 +172,7 @@ endmenu source drivers/char/Config.in -# source drivers/usb/Config.in +source drivers/usb/Config.in source fs/Config.in diff --git a/arch/i386/defconfig b/arch/i386/defconfig index af261ce0e2ad..453b36aa4b03 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -93,11 +93,20 @@ CONFIG_IDEDMA_AUTO=y # # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_MD=y +CONFIG_AUTODETECT_RAID=y +CONFIG_MD_LINEAR=y +CONFIG_MD_STRIPED=y +CONFIG_MD_MIRRORING=y +CONFIG_MD_RAID5=y +CONFIG_MD_TRANSLUCENT=y +CONFIG_MD_LVM=y +CONFIG_MD_BOOT=y # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_XD is not set CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_DEV_HD is not set # @@ -230,6 +239,7 @@ CONFIG_EEXPRESS_PRO100=y # CONFIG_TR is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_SEALEVEL_4021 is not set # CONFIG_RCPCI is not set # CONFIG_WAN_DRIVERS is not set # CONFIG_LAPBETHER is not set @@ -240,6 +250,11 @@ CONFIG_EEXPRESS_PRO100=y # # CONFIG_HAMRADIO is not set +# +# IrDA subsystem support +# +# CONFIG_IRDA is not set + # # ISDN subsystem # diff --git a/arch/i386/kernel/init_task.c b/arch/i386/kernel/init_task.c index d3b395732c54..931f8da17566 100644 --- a/arch/i386/kernel/init_task.c +++ b/arch/i386/kernel/init_task.c @@ -7,7 +7,6 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 202de42d7563..f5a6e69c335d 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -14,6 +14,9 @@ * Bart Hartgers , May 199. * * Intel Mobile Pentium II detection fix. Sean Gilley, June 1999. + * + * IDT Winchip tweaks, misc clean ups. + * Dave Jones , August 1999 */ /* @@ -480,6 +483,19 @@ __initfunc(static int amd_model(struct cpuinfo_x86 *c)) break; } break; + case 6: /* An Athlon. We can trust the BIOS probably */ + { + + u32 ecx, edx, dummy; + cpuid(0x80000005, &dummy, &dummy, &ecx, &edx); + printk("L1 I Cache: %dK L1 D Cache: %dK\n", + ecx>>24, edx>>24); + cpuid(0x80000006, &dummy, &dummy, &ecx, &edx); + printk("L2 Cache: %dK\n", ecx>>16); + c->x86_cache_size = ecx>>16; + break; + } + } return r; } @@ -690,8 +706,9 @@ static struct cpu_model_info cpu_models[] __initdata = { NULL, NULL, NULL, NULL }}, { X86_VENDOR_INTEL, 6, { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", - NULL, "Pentium II (Deschutes)", "Mobile Pentium II", NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + NULL, "Pentium II (Deschutes)", "Mobile Pentium II", + "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, + NULL, NULL, NULL, NULL }}, { X86_VENDOR_AMD, 4, { NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", @@ -701,6 +718,11 @@ static struct cpu_model_info cpu_models[] __initdata = { "K5", "K5", NULL, NULL, "K6", "K6", "K6-2", "K6-3", NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_AMD, 6, + { "Athlon", "Athlon", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, { X86_VENDOR_UMC, 4, { NULL, "U5D", "U5S", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, @@ -810,7 +832,6 @@ __initfunc(void identify_cpu(struct cpuinfo_x86 *c)) } } } - } if (p) { @@ -860,22 +881,25 @@ __initfunc(void print_cpu_info(struct cpuinfo_x86 *c)) printk("%s", c->x86_model_id); if (c->x86_mask || c->cpuid_level>=0) - printk(" stepping %02x", c->x86_mask); + printk(" stepping %02x\n", c->x86_mask); - if(c->x86_vendor == X86_VENDOR_CENTAUR) - { + if(c->x86_vendor == X86_VENDOR_CENTAUR) { u32 hv,lv; rdmsr(0x107, lv, hv); - printk("\nCentaur FSR was 0x%X ",lv); - lv|=(1<<8); - lv|=(1<<7); + printk("Centaur FSR was 0x%X ",lv); + lv|=(1<<1 | 1<<2 | 1<<7); /* lv|=(1<<6); - may help too if the board can cope */ - printk("now 0x%X", lv); + printk("now 0x%X\n", lv); wrmsr(0x107, lv, hv); /* Emulate MTRRs using Centaur's MCR. */ - c->x86_capability |= X86_FEATURE_MTRR; + c->x86_capability |= X86_FEATURE_MTRR; + + /* Set 3DNow! on Winchip 2 and above. */ + if (c->x86_model >=8) + c->x86_capability |= X86_FEATURE_AMD3D; + + c->x86_capability |=X86_FEATURE_CX8; } - printk("\n"); } /* @@ -910,7 +934,7 @@ int get_cpuinfo(char * buffer) c->x86 + '0', c->x86_model, c->x86_model_id[0] ? c->x86_model_id : "unknown"); - + if (c->x86_mask || c->cpuid_level >= 0) p += sprintf(p, "stepping\t: %d\n", c->x86_mask); else @@ -926,14 +950,20 @@ int get_cpuinfo(char * buffer) p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size); /* Modify the capabilities according to chip type */ - if (c->x86_vendor == X86_VENDOR_CYRIX) { + switch (c->x86_vendor) { + + case X86_VENDOR_CYRIX: x86_cap_flags[24] = "cxmmx"; - } else if (c->x86_vendor == X86_VENDOR_AMD) { - x86_cap_flags[16] = "fcmov"; - x86_cap_flags[31] = "3dnow"; + break; + + case X86_VENDOR_AMD: if (c->x86 == 5 && c->x86_model == 6) x86_cap_flags[10] = "sep"; - } else if (c->x86_vendor == X86_VENDOR_INTEL) { + x86_cap_flags[16] = "fcmov"; + x86_cap_flags[31] = "3dnow"; + break; + + case X86_VENDOR_INTEL: x86_cap_flags[6] = "pae"; x86_cap_flags[9] = "apic"; x86_cap_flags[14] = "mca"; @@ -941,6 +971,16 @@ int get_cpuinfo(char * buffer) x86_cap_flags[17] = "pse36"; x86_cap_flags[18] = "psn"; x86_cap_flags[24] = "osfxsr"; + break; + + case X86_VENDOR_CENTAUR: + if (c->x86_model >=8) /* Only Winchip2 and above */ + x86_cap_flags[31] = "3dnow"; + break; + + default: + /* Unknown CPU manufacturer. Transmeta ? :-) */ + break; } sep_bug = c->x86_vendor == X86_VENDOR_INTEL && diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 2948069e30b1..2f0206b734f3 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -392,6 +392,7 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) int datapages = 0; int initpages = 0; unsigned long tmp; + unsigned long endbase; end_mem &= PAGE_MASK; high_memory = (void *) end_mem; @@ -419,8 +420,10 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) * IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000. * They seem to have done something stupid with the floppy * controller as well.. + * The amount of available base memory is in WORD 40:13. */ - while (start_low_mem < 0x9f000+PAGE_OFFSET) { + endbase = PAGE_OFFSET + ((*(unsigned short *)__va(0x413) * 1024) & PAGE_MASK); + while (start_low_mem < endbase) { clear_bit(PG_reserved, &mem_map[MAP_NR(start_low_mem)].flags); start_low_mem += PAGE_SIZE; } diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 97f3bd15134d..ee45219cbf71 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -40,7 +40,6 @@ */ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; @@ -87,9 +86,6 @@ void machine_halt(void) void machine_power_off(void) { -#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) - apm_set_power_state(APM_STATE_OFF); -#endif } void show_regs(struct pt_regs * regs) diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index 58c9c3c82b12..493b362d6e63 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -46,6 +46,10 @@ CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc +ifeq ($(CONFIG_SMP),y) +CFLAGS += -D__SMP__ +endif + OBJECTS += vreset.o kbd.o of1275.o ifeq ($(CONFIG_SERIAL_CONSOLE),y) OBJECTS += ns16550.o diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c index 2cd57093842f..75aaffb68742 100644 --- a/arch/ppc/boot/misc.c +++ b/arch/ppc/boot/misc.c @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.64.2.2 1999/05/29 19:09:29 cort Exp $ + * $Id: misc.c,v 1.64.2.4 1999/07/22 03:28:03 cort Exp $ * * Adapted for PowerPC by Gary Thomas * @@ -311,6 +311,33 @@ void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) unsigned char sanity[0x2000]; +/* + * This routine is used to control the second processor on the + * Motorola dual processor platforms. + */ +void +park_cpus() +{ +#ifdef __SMP__ + volatile void (*go)(RESIDUAL *, int, int, char *, int); + unsigned int i; + volatile unsigned long *smp_iar = &(hold_residual->VitalProductData.SmpIar); + + /* Wait for indication to continue. If the kernel + was not compiled with SMP support then the second + processor will spin forever here makeing the kernel + multiprocessor safe. */ + while (*smp_iar == 0) { + for (i=0; i < 512; i++); + } + + (unsigned long)go = hold_residual->VitalProductData.SmpIar; + go(hold_residual, 0, 0, cmd_line, sizeof(cmd_preset)); +#else + while(1); +#endif +} + unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual, void *OFW_interface) @@ -328,6 +355,7 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, int res, size; unsigned char board_type; unsigned char base_mod; + int start_multi = 0; lines = 25; cols = 80; @@ -368,6 +396,14 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, keyb_present = 0; /* no keyboard */ } } + + /* If this is a multiprocessor system then + * park the other processor so that the + * kernel knows where to find them. + */ + if (residual->MaxNumCpus > 1) { + start_multi = 1; + } } memcpy(hold_residual,residual,sizeof(RESIDUAL)); } else { @@ -408,7 +444,15 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, residual = hold_residual; /* Turn MMU back off */ _put_MSR(orig_MSR & ~0x0030); - } + } + + if (start_multi) { + hold_residual->VitalProductData.SmpIar = 0; + hold_residual->Cpus[1].CpuState = CPU_GOOD_FW; + residual->VitalProductData.SmpIar = (unsigned long)park_cpus; + residual->Cpus[1].CpuState = CPU_GOOD; + hold_residual->VitalProductData.Reserved5 = 0xdeadbeef; + } /* assume the chunk below 8M is free */ end_avail = (char *)0x00800000; @@ -465,22 +509,23 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, puts(" "); puthex((unsigned long)zimage_size+(unsigned long)zimage_start); puts("\n"); - } - /* relocate initrd */ - if ( initrd_start ) - { + /* relocate initrd */ + if ( initrd_start ) + { + puts("initrd at: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + avail_ram = (char *)PAGE_ALIGN( + (unsigned long)zimage_size+(unsigned long)zimage_start); + memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); + initrd_start = (unsigned long)avail_ram; + initrd_end = initrd_start + INITRD_SIZE; + puts("relocated to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + } + } else if ( initrd_start ) { puts("initrd at: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); -#ifdef OMIT - avail_ram = (char *)PAGE_ALIGN( - (unsigned long)zimage_size+(unsigned long)zimage_start); - memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); - initrd_start = (unsigned long)avail_ram; - initrd_end = initrd_start + INITRD_SIZE; - puts("relocated to: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); -#endif } avail_ram = (char *)0x00400000; @@ -528,6 +573,7 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, gunzip(0, 0x400000, zimage_start, &zimage_size); puts("done.\n"); + puts("Now booting the kernel\n"); return (unsigned long)hold_residual; } diff --git a/arch/ppc/chrpboot/mknote.c b/arch/ppc/chrpboot/mknote.c new file mode 100644 index 000000000000..120cc1d89739 --- /dev/null +++ b/arch/ppc/chrpboot/mknote.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) Cort Dougan 1999. + * + * 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. + * + * Generate a note section as per the CHRP specification. + * + */ + +#include + +#define PL(x) printf("%c%c%c%c", ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, (x)&0xff ); + +int main(void) +{ +/* header */ + /* namesz */ + PL(strlen("PowerPC")+1); + /* descrsz */ + PL(6*4); + /* type */ + PL(0x1275); + /* name */ + printf("PowerPC"); printf("%c", 0); + +/* descriptor */ + /* real-mode */ + PL(0xffffffff); + /* real-base */ + PL(0x00c00000); + /* real-size */ + PL(0xffffffff); + /* virt-base */ + PL(0xffffffff); + /* virt-size */ + PL(0xffffffff); + /* load-base */ + PL(0x4000); + return 0; +} diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index 9324eb6690d0..d8039844132a 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -245,6 +245,7 @@ CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set # CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set # # Ethernet (10 or 100Mbit) @@ -257,6 +258,7 @@ CONFIG_BMAC=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set +# CONFIG_SIS900 is not set # CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 134c2c4dbd69..9aed25d1c7a7 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -294,16 +294,19 @@ __initfunc(void { struct property *p; device = find_devices("rtas"); - for ( p = device->properties; - p && strncmp(p->name, "rtas-event-scan-rate", 20); - p = p->next ) - /* nothing */ ; - if ( p && *(unsigned long *)p->value ) + if ( device ) { - rtas_event_scan_rate = (HZ/(*(unsigned long *)p->value)*30)-1; - rtas_event_scan_ct = 1; - printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", - *(unsigned long *)p->value, rtas_event_scan_rate ); + for ( p = device->properties; + p && strncmp(p->name, "rtas-event-scan-rate", 20); + p = p->next ) + /* nothing */ ; + if ( p && *(unsigned long *)p->value ) + { + rtas_event_scan_rate = (HZ/(*(unsigned long *)p->value)*30)-1; + rtas_event_scan_ct = 1; + printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", + *(unsigned long *)p->value, rtas_event_scan_rate ); + } } } } @@ -312,11 +315,10 @@ void chrp_event_scan(void) { unsigned char log[1024]; + unsigned long ret = 0; if ( rtas_event_scan_rate && (rtas_event_scan_ct-- <= 0) ) - { - call_rtas( "event-scan", 4, 1, NULL, 0x0, 1, __pa(log), 1024 ); - rtas_event_scan_ct = rtas_event_scan_rate; - } + call_rtas( "event-scan", 4, 1, &ret, 0xffffffff, 0, + __pa(log), 1024 ); } void diff --git a/arch/ppc/kernel/i8259.c b/arch/ppc/kernel/i8259.c index 4ec6d3e118f8..057c0593be92 100644 --- a/arch/ppc/kernel/i8259.c +++ b/arch/ppc/kernel/i8259.c @@ -13,33 +13,32 @@ unsigned char cached_8259[2] = { 0xff, 0xff }; int i8259_irq(int cpu) { int irq; + unsigned char irr; /* * Perform an interrupt acknowledge cycle on controller 1 - */ - outb(0x0C, 0x20); - irq = inb(0x20) & 7; - if (irq == 2) - { - /* + */ + irr = inb(0x20) & ~cached_21; + if (!irr) return -1; + irq = 0; + while ((irq < 7) && !(irr&0x01)) + { + irq++; + irr >>= 1; + } + if (irq == 2) + { + /* * Interrupt is cascaded so perform interrupt * acknowledge on controller 2 */ - outb(0x0C, 0xA0); - irq = (inb(0xA0) & 7) + 8; - } - else if (irq==7) - { - /* - * This may be a spurious interrupt - * - * Read the interrupt status register. If the most - * significant bit is not set then there is no valid - * interrupt - */ - outb(0x0b, 0x20); - if(~inb(0x20)&0x80) - return -1; + irr = inb(0xA0) & ~cached_A1; + irq = 8; + while ((irq < 15) && !(irr&0x01)) + { + irq++; + irr >>= 1; + } } return irq; } @@ -53,13 +52,13 @@ static void i8259_mask_and_ack_irq(unsigned int irq_nr) cached_A1 |= 1 << (irq_nr-8); inb(0xA1); /* DUMMY */ outb(cached_A1,0xA1); - outb(0x20,0xA0); /* Non-specific EOI */ - outb(0x20,0x20); /* Non-specific EOI to cascade */ + outb(0x62,0x20); /* Specific EOI to cascade */ + outb(0x60|(irq_nr-8),0xA0); /* Specific EOI */ } else { cached_21 |= 1 << irq_nr; inb(0x21); /* DUMMY */ outb(cached_21,0x21); - outb(0x20,0x20); /* Non-specific EOI */ + outb(0x60|irq_nr,0x20); /* Specific EOI */ } } @@ -82,7 +81,6 @@ static void i8259_mask_irq(unsigned int irq_nr) static void i8259_unmask_irq(unsigned int irq_nr) { - if ( irq_nr >= i8259_pic.irq_offset ) irq_nr -= i8259_pic.irq_offset; if ( irq_nr < 8 ) diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index bd175ba50197..d0da0db398cb 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -1,5 +1,5 @@ /* - * $Id: irq.c,v 1.105.2.2 1999/06/17 01:16:10 paulus Exp $ + * $Id: irq.c,v 1.105.2.3 1999/07/22 01:49:41 cort Exp $ * * arch/ppc/kernel/irq.c * @@ -139,13 +139,16 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) /* Free */ for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { - /* Found it - now free it */ - save_flags(flags); - cli(); - *p = action->next; - restore_flags(flags); - irq_kfree(action); - return 0; + if (action->dev_id == dev_id) + { + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + restore_flags(flags); + irq_kfree(action); + return 0; + } } return -ENOENT; } diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 4c6486232dba..82c01667d36f 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -378,6 +378,7 @@ _GLOBAL(_outsl_ns) * * ashrdi3: XXXYYY/ZZZAAA -> SSSXXX/YYYZZZ * ashldi3: XXXYYY/ZZZAAA -> YYYZZZ/AAA000 + * lshrdi3: XXXYYY/ZZZAAA -> 000XXX/YYYZZZ */ _GLOBAL(__ashrdi3) li r6,32 @@ -387,7 +388,7 @@ _GLOBAL(__ashrdi3) or r4,r4,r7 /* YYYZZZ */ sraw r3,r3,r5 /* SSSXXX */ blr - + _GLOBAL(__ashldi3) li r6,32 sub r6,r6,r5 @@ -397,6 +398,15 @@ _GLOBAL(__ashldi3) or r3,r3,r7 /* YYYZZZ */ blr +_GLOBAL(__lshrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + srw r3,r3,r5 /* 000XXX */ + blr + _GLOBAL(abs) cmpi 0,r3,0 bge 10f diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index 8cf1cf998c44..b8dd53eccf15 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -1,5 +1,5 @@ /* - * $Id: ppc_htab.c,v 1.26 1998/12/10 00:24:23 cort Exp $ + * $Id: ppc_htab.c,v 1.26.2.2 1999/08/10 01:55:03 paulus Exp $ * * PowerPC hash table management proc entry. Will show information * about the current hash table and will allow changes to it. @@ -532,7 +532,21 @@ int proc_dol2crvec(ctl_table *table, int write, struct file *filp, int vleft, first=1, len, left, val; #define TMPBUFLEN 256 char buf[TMPBUFLEN], *p; - + static const char *sizestrings[4] = { + "unknown size", "256KB", "512KB", "1MB" + }; + static const char *clockstrings[8] = { + "clock disabled", "+1 clock", "+1.5 clock", "reserved(3)", + "+2 clock", "+2.5 clock", "+3 clock", "reserved(7)" + }; + static const char *typestrings[4] = { + "flow-through burst SRAM", "reserved SRAM", + "pipelined burst SRAM", "pipelined late-write SRAM" + }; + static const char *holdstrings[4] = { + "0.5", "1.0", "(reserved2)", "(reserved3)" + }; + if ( (_get_PVR() >> 16) != 8) return -EFAULT; if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { @@ -585,55 +599,13 @@ int proc_dol2crvec(ctl_table *table, int write, struct file *filp, p += sprintf(p, " %s", (val&0x80000000)?"enabled":"disabled"); p += sprintf(p,",%sparity",(val&0x40000000)?"":"no "); - - switch( (val >> 28) & 0x3 ) - { - case 1: p += sprintf(p,",256Kb"); - break; - case 2: p += sprintf(p,",512Kb"); - break; - case 3: p += sprintf(p,",1M"); - break; - default: p += sprintf(p,",unknown size"); - break; - } - - - switch( (val >> 25) & 0x7 ) - { - case 0: p += sprintf(p,",clock disabled"); - break; - case 1: p += sprintf(p,",+1 clock"); - break; - case 2: p += sprintf(p,",+1.5 clock"); - break; - case 7: - case 3: p += sprintf(p,",reserved clock"); - break; - case 4: p += sprintf(p,",+2 clock"); - break; - case 5: p += sprintf(p,",+2.5 clock"); - break; - case 6: p += sprintf(p,",+3 clock"); - break; - } - - switch( (val >> 23) & 0x2 ) - { - case 0: p += sprintf(p,",flow-through burst SRAM"); - break; - case 1: p += sprintf(p,",reserved SRAM"); - break; - case 2: p += sprintf(p,",pipelined burst SRAM"); - break; - case 3: p += sprintf(p,",pipelined late-write SRAM"); - break; - } - - p += sprintf(p,"%s",(val>>22)?"":",data only"); - p += sprintf(p,"%s",(val>>20)?",ZZ enabled":""); - p += sprintf(p,",%s",(val>>19)?"write-through":"copy-back"); - p += sprintf(p,",%sns hold",(val>>16)?"1.0":"0.5"); + p += sprintf(p, ",%s", sizestrings[(val >> 28) & 3]); + p += sprintf(p, ",%s", clockstrings[(val >> 25) & 7]); + p += sprintf(p, ",%s", typestrings[(val >> 23) & 0x2]); + p += sprintf(p,"%s",(val>>22)&1?"":",data only"); + p += sprintf(p,"%s",(val>>20)&1?",ZZ enabled":""); + p += sprintf(p,",%s",(val>>19)&1?"write-through":"copy-back"); + p += sprintf(p,",%sns hold", holdstrings[(val>>16)&3]); p += sprintf(p,"\n"); diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 21d250266ae9..9c7dc8d4eb70 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -47,6 +47,7 @@ extern void do_lost_interrupts(unsigned long); extern int do_signal(sigset_t *, struct pt_regs *); asmlinkage long long __ashrdi3(long long, int); +asmlinkage long long __lshrdi3(long long, int); asmlinkage int abs(int); EXPORT_SYMBOL(clear_page); @@ -214,6 +215,7 @@ EXPORT_SYMBOL(abs); EXPORT_SYMBOL(device_is_compatible); EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memmove); diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index 63a1e9ddf78c..6d248d2b64c0 100644 --- a/arch/ppc/kernel/prep_pci.c +++ b/arch/ppc/kernel/prep_pci.c @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.35 1999/05/10 23:31:03 cort Exp $ + * $Id: prep_pci.c,v 1.35.2.1 1999/07/22 01:49:42 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -34,6 +34,9 @@ unsigned char *Motherboard_map_name; /* How is the 82378 PIRQ mapping setup? */ unsigned char *Motherboard_routes; +void (*Motherboard_non0)(struct pci_dev *); + +void Mesquite_Map_Non0(struct pci_dev *); /* Used for Motorola to store system config register */ static unsigned long *ProcInfo; @@ -314,7 +317,7 @@ static char Genesis2_pci_IRQ_map[23] __prepdata = 0, /* Slot 10 - Ethernet */ 0, /* Slot 11 - Universe PCI - VME Bridge */ 3, /* Slot 12 - unused */ - 0, /* Slot 13 - unused */ + 5, /* Slot 13 - VME */ 2, /* Slot 14 - SCSI */ 0, /* Slot 15 - graphics on 3600 */ 9, /* Slot 16 - PMC */ @@ -682,8 +685,11 @@ static u_char mvme2600_openpic_initsenses[] __initdata = { #define MOT_RAVEN_PRESENT 0x1 #define MOT_HAWK_PRESENT 0x2 -int prep_keybd_present = 1; +/* Keyboard present flag */ +int prep_kbd_present = 1; /* Keyboard present by default */ + int MotMPIC = 0; +int mot_multi = 0; __initfunc(int raven_init(void)) { @@ -740,13 +746,18 @@ __initfunc(int raven_init(void)) */ ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); + /* Indicate to system if this is a multiprocessor board */ + if (!(*ProcInfo & MOT_PROC2_BIT)) { + mot_multi = 1; + } + /* This is a hack. If this is a 2300 or 2400 mot board then there is * no keyboard controller and we have to indicate that. */ base_mod = inb(MOTOROLA_BASETYPE_REG); if ((MotMPIC == MOT_HAWK_PRESENT) || (base_mod == 0xF9) || (base_mod == 0xFA) || (base_mod == 0xE1)) - prep_keybd_present = 0; + prep_kbd_present = 0; return 1; } @@ -759,33 +770,34 @@ struct mot_info { const char *name; unsigned char *map; unsigned char *routes; + void (*map_non0_bus)(struct pci_dev *); /* For boards with more than bus 0 devices. */ } mot_info[] = { - {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes}, - {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes}, - {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes}, - {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes}, - {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes}, - {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes}, - {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x000, 0x00, 0x00, "", NULL, NULL} + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes, NULL}, + {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes, NULL}, + {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes, NULL}, + {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes, NULL}, + {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes, NULL}, + {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes, NULL}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Mesquite_Map_Non0}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x000, 0x00, 0x00, "", NULL, NULL, NULL} }; __initfunc(unsigned long prep_route_pci_interrupts(void)) @@ -846,6 +858,7 @@ __initfunc(unsigned long prep_route_pci_interrupts(void)) Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; Motherboard_map = mot_info[mot_entry].map; Motherboard_routes = mot_info[mot_entry].routes; + Motherboard_non0 = mot_info[mot_entry].map_non0_bus; if (!(mot_info[entry].cpu_type & 0x100)) { /* AJF adjust level/edge control according to routes */ @@ -976,6 +989,93 @@ __initfunc(unsigned long prep_route_pci_interrupts(void)) return 0; } +static unsigned int pci_localpirqs[4] = +{ + 24, + 25, + 26, + 27 +}; + +static unsigned int pci_remotepirqs[4] = +{ + 28, + 29, + 30, + 31 +}; + +static unsigned int pci_remotedev = 0xc0; + +void +Mesquite_Map_Non0(struct pci_dev *pdev) +{ + struct pci_bus *pbus; /* Parent Bus Structure Pointer */ + unsigned int devnum; /* Accumulated Device Number */ + unsigned int irq; /* IRQ Value */ + + /* + ** Device Interrupt Line register initialization. + ** The IRQ line number will be generated after + ** taking into account all the PCI-2-PCI bridge + ** devices between the device and the Host Bridge. + */ + devnum = PCI_SLOT(pdev->devfn); + pbus = pdev->bus; + + while ((pbus->parent)->primary != (pbus->parent)->secondary) + { + devnum += PCI_SLOT((pbus->self)->devfn); + + pbus = pbus->parent; + } + + devnum &= 0x03; + + /* + ** By default, get the PCI local domain IRQ value. + */ + irq = pci_localpirqs[devnum]; + + /* + ** Determine if the device is located in the + ** remote domain or not. We must find the + ** domain's bridge device located on bus 0. + */ + pbus = pdev->bus; + + while (pbus->primary != 0) + pbus = pbus->parent; + + /* + ** Check the device/function of domain's bridge + ** device against the remote device/function. + ** If the same, then the device is located in + ** the remote domain. Thus, get the PCI remote + ** domain IRQ value. + */ + if ((pbus->self)->devfn == pci_remotedev) + irq = pci_remotepirqs[devnum]; + + /* + ** Validate the IRQ number. + */ + if (irq <= 255) + { + /* + ** Set the device's Interrupt Line register + ** to the IRQ number and save it in the + ** device's structure. + */ + + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, (u8)irq); + + pdev->irq = irq; + + } + return; +} + __initfunc( void prep_pcibios_fixup(void)) @@ -1000,6 +1100,9 @@ prep_pcibios_fixup(void)) if (dev->bus->number == 0) { dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, dev->irq); + } else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); } } return; diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index dc3ef3d00b8c..5860716c0bb4 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -84,7 +84,6 @@ extern void pckbd_init_hw(void); extern unsigned char pckbd_sysrq_xlate[128]; extern void prep_setup_pci_ptrs(void); -extern void chrp_do_IRQ(struct pt_regs *regs, int cpu, int isfake); extern char saved_command_line[256]; int _prep_type; @@ -704,15 +703,27 @@ prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)) } #endif +unsigned long *MotSave_SmpIar; +unsigned char *MotSave_CpusState[2]; + __initfunc(void prep_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7)) { + RESIDUAL *old_res = (RESIDUAL *)(r3 + KERNELBASE); + /* make a copy of residual data */ if ( r3 ) { memcpy((void *)res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL)); + + /* These need to be saved for the Motorola Prep + * MVME4600 and Dual MTX boards. + */ + MotSave_SmpIar = &old_res->VitalProductData.SmpIar; + MotSave_CpusState[0] = &old_res->Cpus[0].CpuState; + MotSave_CpusState[1] = &old_res->Cpus[1].CpuState; } isa_io_base = PREP_ISA_IO_BASE; diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index 823d5e0a53f3..1bf375c2910d 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -47,7 +47,6 @@ extern unsigned long _get_SP(void); struct task_struct *last_task_used_math = NULL; static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index c82c00b714a5..582428b80b35 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -1,5 +1,5 @@ /* - * $Id: prom.c,v 1.54.2.3 1999/07/02 19:58:27 cort Exp $ + * $Id: prom.c,v 1.54.2.5 1999/07/21 20:28:18 cort Exp $ * * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. @@ -261,6 +261,7 @@ prom_print(const char *msg) * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. */ +unsigned long promi = 0; __init void prom_init(int r3, int r4, prom_entry pp) @@ -1296,8 +1297,6 @@ print_properties(struct device_node *np) } #endif -spinlock_t rtas_lock = SPIN_LOCK_UNLOCKED; - /* this can be called after setup -- Cort */ __openfirmware int @@ -1328,12 +1327,11 @@ call_rtas(const char *service, int nargs, int nret, for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); - - s = _disable_interrupts(); - spin_lock(&rtas_lock); + + save_flags(s); + cli(); enter_rtas((void *)__pa(&u)); - spin_unlock(&rtas_lock); - _enable_interrupts(s); + restore_flags(s); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index 9e1c3803c73e..504bff082923 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -1,5 +1,5 @@ /* - * $Id: smp.c,v 1.49.2.3 1999/06/24 17:12:55 cort Exp $ + * $Id: smp.c,v 1.49.2.5 1999/07/22 01:49:45 cort Exp $ * * Smp support for ppc. * @@ -57,6 +57,10 @@ int start_secondary(void *); extern int cpu_idle(void *unused); u_int openpic_read(volatile u_int *addr); +extern int mot_multi; +extern unsigned long *MotSave_SmpIar; +extern unsigned char *MotSave_CpusState[2]; + /* register for interrupting the secondary processor on the powersurge */ #define PSURGE_INTR ((volatile unsigned *)0xf80000c0) @@ -171,7 +175,7 @@ spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; void smp_message_pass(int target, int msg, unsigned long data, int wait) { int i; - if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) + if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_prep)) ) return; spin_lock(&mesg_pass_lock); @@ -197,8 +201,8 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) break; } - if ( _machine == _MACH_Pmac ) - { + switch (_machine) { + case _MACH_Pmac: /* interrupt secondary processor */ out_be32(PSURGE_INTR, ~0); out_be32(PSURGE_INTR, 0); @@ -208,10 +212,10 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) */ /* interrupt primary */ /**(volatile unsigned long *)(0xf3019000);*/ - } + break; - if ( _machine == _MACH_chrp ) - { + case _MACH_chrp: + case _MACH_prep: /* * There has to be some way of doing this better - * perhaps a sent-to-all or send-to-all-but-self @@ -234,6 +238,7 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) openpic_cause_IPI(target, 0, 1U << target); break; } + break; } spin_unlock(&mesg_pass_lock); @@ -273,23 +278,27 @@ void __init smp_boot_cpus(void) */ cacheflush_time = 5 * 1024; - if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) - { - printk("SMP not supported on this machine.\n"); - return; - } - switch ( _machine ) { case _MACH_Pmac: /* assume powersurge board - 2 processors -- Cort */ - cpu_nr = 2; + cpu_nr = 2; break; case _MACH_chrp: cpu_nr = ((openpic_read(&OpenPIC->Global.Feature_Reporting0) & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT)+1; break; + case _MACH_prep: + /* assume 2 for now == fix later -- Johnnie */ + if ( mot_multi ) + { + cpu_nr = 2; + break; + } + default: + printk("SMP not supported on this machine.\n"); + return; } /* @@ -343,6 +352,11 @@ void __init smp_boot_cpus(void) __pa(__secondary_start_chrp), i); #endif break; + case _MACH_prep: + *MotSave_SmpIar = (unsigned long)__secondary_start_psurge - KERNELBASE; + *MotSave_CpusState[1] = CPU_GOOD; + printk("CPU1 reset, waiting\n"); + break; } /* @@ -399,12 +413,12 @@ void __init smp_callin(void) { smp_store_cpu_info(current->processor); set_dec(decrementer_count); - #if 0 current->mm->mmap->vm_page_prot = PAGE_SHARED; current->mm->mmap->vm_start = PAGE_OFFSET; current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; #endif + init_idle(); cpu_callin_map[current->processor] = 1; while(!smp_commenced) barrier(); diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig index 9324eb6690d0..d8039844132a 100644 --- a/arch/ppc/pmac_defconfig +++ b/arch/ppc/pmac_defconfig @@ -245,6 +245,7 @@ CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set # CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set # # Ethernet (10 or 100Mbit) @@ -257,6 +258,7 @@ CONFIG_BMAC=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set +# CONFIG_SIS900 is not set # CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y diff --git a/arch/sparc/kernel/init_task.c b/arch/sparc/kernel/init_task.c index 156ed433715c..996e54b98031 100644 --- a/arch/sparc/kernel/init_task.c +++ b/arch/sparc/kernel/init_task.c @@ -6,7 +6,6 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/sparc64/kernel/init_task.c b/arch/sparc64/kernel/init_task.c index 86b6c3dd60fc..d0fc09346eca 100644 --- a/arch/sparc64/kernel/init_task.c +++ b/arch/sparc64/kernel/init_task.c @@ -6,7 +6,6 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 5caa558b9498..e89ade288a4e 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -1992,11 +1992,24 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) case BLKRASET: /* 0x09 */ - case REGISTER_DEV: - case REGISTER_DEV_NEW: - case START_MD: - case STOP_MD: - + case RAID_VERSION: + case GET_ARRAY_INFO: + case GET_DISK_INFO: + case CLEAR_ARRAY: + case ADD_NEW_DISK: + case HOT_REMOVE_DISK: + case SET_ARRAY_INFO: + case SET_DISK_INFO: + case WRITE_RAID_INFO: + case UNPROTECT_ARRAY: + case PROTECT_ARRAY: + case HOT_ADD_DISK: + case RUN_ARRAY: + case START_ARRAY: + case STOP_ARRAY: + case STOP_ARRAY_RO: + case RESTART_ARRAY_RW: + /* Big K */ case PIO_FONT: case GIO_FONT: diff --git a/arch/sparc64/math-emu/op-2.h b/arch/sparc64/math-emu/op-2.h index 8ac63188ca24..bbfc60320c9e 100644 --- a/arch/sparc64/math-emu/op-2.h +++ b/arch/sparc64/math-emu/op-2.h @@ -90,9 +90,9 @@ #define _FP_FRAC_OVERP_2(fs,X) (X##_f1 & _FP_OVERFLOW_##fs) #define _FP_FRAC_EQ_2(X, Y) (X##_f1 == Y##_f1 && X##_f0 == Y##_f0) #define _FP_FRAC_GT_2(X, Y) \ - (X##_f1 > Y##_f1 || X##_f1 == Y##_f1 && X##_f0 > Y##_f0) + (X##_f1 > Y##_f1 || (X##_f1 == Y##_f1 && X##_f0 > Y##_f0)) #define _FP_FRAC_GE_2(X, Y) \ - (X##_f1 > Y##_f1 || X##_f1 == Y##_f1 && X##_f0 >= Y##_f0) + (X##_f1 > Y##_f1 || (X##_f1 == Y##_f1 && X##_f0 >= Y##_f0)) #define _FP_ZEROFRAC_2 0, 0 #define _FP_MINFRAC_2 0, 1 diff --git a/arch/sparc64/math-emu/op-common.h b/arch/sparc64/math-emu/op-common.h index 0a6052960847..8b1a4777a4f4 100644 --- a/arch/sparc64/math-emu/op-common.h +++ b/arch/sparc64/math-emu/op-common.h @@ -23,15 +23,15 @@ do { \ if (_FP_FRAC_ZEROP_##wc(X)) \ X##_c = FP_CLS_ZERO; \ else \ - /* a denormalized number */ \ - __FP_UNPACK_DENORM(fs, wc, X) \ + /* A denormalized number. */ \ + __FP_UNPACK_DENORM(fs, wc, X); \ break; \ \ case _FP_EXPMAX_##fs: \ if (_FP_FRAC_ZEROP_##wc(X)) \ X##_c = FP_CLS_INF; \ else \ - /* we don't differentiate between signaling and quiet nans */ \ + /* We don't differentiate between signaling and quiet nans. */ \ X##_c = FP_CLS_NAN; \ break; \ } \ diff --git a/drivers/block/Makefile b/drivers/block/Makefile index d050b53ac9e4..9e2b1b078acf 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -234,14 +234,6 @@ else endif endif -ifeq ($(CONFIG_BLK_CPQ_DA),y) -L_OBJS += cpqarray.o -else - ifeq ($(CONFIG_BLK_CPQ_DA),m) - M_OBJS += cpqarray.o - endif -endif - ifeq ($(CONFIG_BLK_DEV_DAC960),y) LX_OBJS += DAC960.o else @@ -250,6 +242,14 @@ else endif endif +ifeq ($(CONFIG_BLK_CPQ_DA),y) +L_OBJS += cpqarray.o +else + ifeq ($(CONFIG_BLK_CPQ_DA),m) + M_OBJS += cpqarray.o + endif +endif + ifeq ($(CONFIG_BLK_DEV_MD),y) LX_OBJS += md.o @@ -278,13 +278,31 @@ else endif ifeq ($(CONFIG_MD_RAID5),y) +LX_OBJS += xor.o L_OBJS += raid5.o else ifeq ($(CONFIG_MD_RAID5),m) + LX_OBJS += xor.o M_OBJS += raid5.o endif endif +ifeq ($(CONFIG_MD_TRANSLUCENT),y) +L_OBJS += translucent.o +else + ifeq ($(CONFIG_MD_TRANSLUCENT),m) + M_OBJS += translucent.o + endif +endif + +ifeq ($(CONFIG_MD_HSM),y) +L_OBJS += hsm.o +else + ifeq ($(CONFIG_MD_HSM),m) + M_OBJS += hsm.o + endif +endif + endif ifeq ($(CONFIG_BLK_DEV_NBD),y) diff --git a/drivers/block/cpqarray.h b/drivers/block/cpqarray.h index fa51d3dee402..31e9786f3fa2 100644 --- a/drivers/block/cpqarray.h +++ b/drivers/block/cpqarray.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #endif diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index a9615798772f..5cd038abe7c7 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3989,7 +3989,7 @@ __initfunc(static void floppy_set_flags(int *ints,int param, int param2)) else default_drive_params[i].params.flags &= ~param2; } - DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param); + DPRINT("%s flag 0x%x\n", param ? "Setting" : "Clearing", param2); } __initfunc(static void daring(int *ints,int param, int param2)) diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index c0cb2f8e91cd..aff62db8b1a7 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1466,6 +1467,9 @@ __initfunc(void device_setup(void)) #endif rd_load(); #endif +#ifdef CONFIG_BLK_DEV_MD + autodetect_raid(); +#endif #ifdef CONFIG_MD_BOOT md_setup_drive(); #endif diff --git a/drivers/block/hsm.c b/drivers/block/hsm.c new file mode 100644 index 000000000000..6307a502a6fc --- /dev/null +++ b/drivers/block/hsm.c @@ -0,0 +1,840 @@ +/* + hsm.c : HSM RAID driver for Linux + Copyright (C) 1998 Ingo Molnar + + HSM mode management functions. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include +#include + +#include +#include + +#define MAJOR_NR MD_MAJOR +#define MD_DRIVER +#define MD_PERSONALITY + + +#define DEBUG_HSM 1 + +#if DEBUG_HSM +#define dprintk(x,y...) printk(x,##y) +#else +#define dprintk(x,y...) do { } while (0) +#endif + +void print_bh(struct buffer_head *bh) +{ + dprintk("bh %p: %lx %lx %x %x %lx %p %lx %p %x %p %x %lx\n", bh, + bh->b_blocknr, bh->b_size, bh->b_dev, bh->b_rdev, + bh->b_rsector, bh->b_this_page, bh->b_state, + bh->b_next_free, bh->b_count, bh->b_data, + bh->b_list, bh->b_flushtime + ); +} + +static int check_bg (pv_t *pv, pv_block_group_t * bg) +{ + int i, free = 0; + + dprintk("checking bg ...\n"); + + for (i = 0; i < pv->pv_sb->pv_bg_size-1; i++) { + if (pv_pptr_free(bg->blocks + i)) { + free++; + if (test_bit(i, bg->used_bitmap)) { + printk("hm, bit %d set?\n", i); + } + } else { + if (!test_bit(i, bg->used_bitmap)) { + printk("hm, bit %d not set?\n", i); + } + } + } + dprintk("%d free blocks in bg ...\n", free); + return free; +} + +static void get_bg (pv_t *pv, pv_bg_desc_t *desc, int nr) +{ + unsigned int bg_pos = nr * pv->pv_sb->pv_bg_size + 2; + struct buffer_head *bh; + + dprintk("... getting BG at %u ...\n", bg_pos); + + bh = bread (pv->dev, bg_pos, HSM_BLOCKSIZE); + if (!bh) { + MD_BUG(); + return; + } + desc->bg = (pv_block_group_t *) bh->b_data; + desc->free_blocks = check_bg(pv, desc->bg); +} + +static int find_free_block (lv_t *lv, pv_t *pv, pv_bg_desc_t *desc, int nr, + unsigned int lblock, lv_lptr_t * index) +{ + int i; + + for (i = 0; i < pv->pv_sb->pv_bg_size-1; i++) { + pv_pptr_t * bptr = desc->bg->blocks + i; + if (pv_pptr_free(bptr)) { + unsigned int bg_pos = nr * pv->pv_sb->pv_bg_size + 2; + + if (test_bit(i, desc->bg->used_bitmap)) { + MD_BUG(); + continue; + } + bptr->u.used.owner.log_id = lv->log_id; + bptr->u.used.owner.log_index = lblock; + index->data.phys_nr = pv->phys_nr; + index->data.phys_block = bg_pos + i + 1; + set_bit(i, desc->bg->used_bitmap); + desc->free_blocks--; + dprintk(".....free blocks left in bg %p: %d\n", + desc->bg, desc->free_blocks); + return 0; + } + } + return -ENOSPC; +} + +static int __get_free_block (lv_t *lv, pv_t *pv, + unsigned int lblock, lv_lptr_t * index) +{ + int i; + + dprintk("trying to get free block for lblock %d ...\n", lblock); + + for (i = 0; i < pv->pv_sb->pv_block_groups; i++) { + pv_bg_desc_t *desc = pv->bg_array + i; + + dprintk("looking at desc #%d (%p)...\n", i, desc->bg); + if (!desc->bg) + get_bg(pv, desc, i); + + if (desc->bg && desc->free_blocks) + return find_free_block(lv, pv, desc, i, + lblock, index); + } + dprintk("hsm: pv %s full!\n", partition_name(pv->dev)); + return -ENOSPC; +} + +static int get_free_block (lv_t *lv, unsigned int lblock, lv_lptr_t * index) +{ + int err; + + if (!lv->free_indices) + return -ENOSPC; + + /* fix me */ + err = __get_free_block(lv, lv->vg->pv_array + 0, lblock, index); + + if (err || !index->data.phys_block) { + MD_BUG(); + return -ENOSPC; + } + + lv->free_indices--; + + return 0; +} + +/* + * fix me: wordsize assumptions ... + */ +#define INDEX_BITS 8 +#define INDEX_DEPTH (32/INDEX_BITS) +#define INDEX_MASK ((1< [.", index->data.phys_nr, + index->data.phys_block, index->cpu_addr); + + tmp = index_child(index); + for (i = 0; i < HSM_LPTRS_PER_BLOCK; i++) { + if (index_block(lv, tmp)) + dprintk("(%d->%d)", i, index_block(lv, tmp)); + tmp++; + } + dprintk(".]\n"); +} + +static int read_index_group (lv_t *lv, lv_lptr_t *index) +{ + lv_lptr_t *index_group, *tmp; + struct buffer_head *bh; + int i; + + dprintk("reading index group <%s:%d>\n", + partition_name(index_dev(lv, index)), index_block(lv, index)); + + bh = bread(index_dev(lv, index), index_block(lv, index), HSM_BLOCKSIZE); + if (!bh) { + MD_BUG(); + return -EIO; + } + if (!buffer_uptodate(bh)) + MD_BUG(); + + index_group = (lv_lptr_t *) bh->b_data; + tmp = index_group; + for (i = 0; i < HSM_LPTRS_PER_BLOCK; i++) { + if (index_block(lv, tmp)) { + dprintk("index group has BLOCK %d, non-present.\n", i); + tmp->cpu_addr = 0; + } + tmp++; + } + index->cpu_addr = ptr_to_cpuaddr(index_group); + + dprintk("have read index group %p at block %d.\n", + index_group, index_block(lv, index)); + print_index_list(lv, index); + + return 0; +} + +static int alloc_index_group (lv_t *lv, unsigned int lblock, lv_lptr_t * index) +{ + struct buffer_head *bh; + lv_lptr_t * index_group; + + if (get_free_block(lv, lblock, index)) + return -ENOSPC; + + dprintk("creating block for index group <%s:%d>\n", + partition_name(index_dev(lv, index)), index_block(lv, index)); + + bh = getblk(index_dev(lv, index), + index_block(lv, index), HSM_BLOCKSIZE); + + index_group = (lv_lptr_t *) bh->b_data; + md_clear_page(index_group); + mark_buffer_uptodate(bh, 1); + + index->cpu_addr = ptr_to_cpuaddr(index_group); + + dprintk("allocated index group %p at block %d.\n", + index_group, index_block(lv, index)); + return 0; +} + +static lv_lptr_t * alloc_fixed_index (lv_t *lv, unsigned int lblock) +{ + lv_lptr_t * index = index_child(&lv->root_index); + int idx, l; + + for (l = INDEX_DEPTH-1; l >= 0; l--) { + idx = (lblock >> (INDEX_BITS*l)) & INDEX_MASK; + index += idx; + if (!l) + break; + if (!index_present(index)) { + dprintk("no group, level %u, pos %u\n", l, idx); + if (alloc_index_group(lv, lblock, index)) + return NULL; + } + index = index_child(index); + } + if (!index_block(lv,index)) { + dprintk("no data, pos %u\n", idx); + if (get_free_block(lv, lblock, index)) + return NULL; + return index; + } + MD_BUG(); + return index; +} + +static lv_lptr_t * find_index (lv_t *lv, unsigned int lblock) +{ + lv_lptr_t * index = index_child(&lv->root_index); + int idx, l; + + for (l = INDEX_DEPTH-1; l >= 0; l--) { + idx = (lblock >> (INDEX_BITS*l)) & INDEX_MASK; + index += idx; + if (!l) + break; + if (index_free(index)) + return NULL; + if (!index_present(index)) + read_index_group(lv, index); + if (!index_present(index)) { + MD_BUG(); + return NULL; + } + index = index_child(index); + } + if (!index_block(lv,index)) + return NULL; + return index; +} + +static int read_root_index(lv_t *lv) +{ + int err; + lv_lptr_t *index = &lv->root_index; + + if (!index_block(lv, index)) { + printk("LV has no root index yet, creating.\n"); + + err = alloc_index_group (lv, 0, index); + if (err) { + printk("could not create index group, err:%d\n", err); + return err; + } + lv->vg->vg_sb->lv_array[lv->log_id].lv_root_idx = + lv->root_index.data; + } else { + printk("LV already has a root index.\n"); + printk("... at <%s:%d>.\n", + partition_name(index_dev(lv, index)), + index_block(lv, index)); + + read_index_group(lv, index); + } + return 0; +} + +static int init_pv(pv_t *pv) +{ + struct buffer_head *bh; + pv_sb_t *pv_sb; + + bh = bread (pv->dev, 0, HSM_BLOCKSIZE); + if (!bh) { + MD_BUG(); + return -1; + } + + pv_sb = (pv_sb_t *) bh->b_data; + pv->pv_sb = pv_sb; + + if (pv_sb->pv_magic != HSM_PV_SB_MAGIC) { + printk("%s is not a PV, has magic %x instead of %x!\n", + partition_name(pv->dev), pv_sb->pv_magic, + HSM_PV_SB_MAGIC); + return -1; + } + printk("%s detected as a valid PV (#%d).\n", partition_name(pv->dev), + pv->phys_nr); + printk("... created under HSM version %d.%d.%d, at %x.\n", + pv_sb->pv_major, pv_sb->pv_minor, pv_sb->pv_patch, pv_sb->pv_ctime); + printk("... total # of blocks: %d (%d left unallocated).\n", + pv_sb->pv_total_size, pv_sb->pv_blocks_left); + + printk("... block size: %d bytes.\n", pv_sb->pv_block_size); + printk("... block descriptor size: %d bytes.\n", pv_sb->pv_pptr_size); + printk("... block group size: %d blocks.\n", pv_sb->pv_bg_size); + printk("... # of block groups: %d.\n", pv_sb->pv_block_groups); + + if (pv_sb->pv_block_groups*sizeof(pv_bg_desc_t) > PAGE_SIZE) { + MD_BUG(); + return 1; + } + pv->bg_array = (pv_bg_desc_t *)__get_free_page(GFP_KERNEL); + if (!pv->bg_array) { + MD_BUG(); + return 1; + } + memset(pv->bg_array, 0, PAGE_SIZE); + + return 0; +} + +static int free_pv(pv_t *pv) +{ + struct buffer_head *bh; + + dprintk("freeing PV %d ...\n", pv->phys_nr); + + if (pv->bg_array) { + int i; + + dprintk(".... freeing BGs ...\n"); + for (i = 0; i < pv->pv_sb->pv_block_groups; i++) { + unsigned int bg_pos = i * pv->pv_sb->pv_bg_size + 2; + pv_bg_desc_t *desc = pv->bg_array + i; + + if (desc->bg) { + dprintk(".... freeing BG %d ...\n", i); + bh = getblk (pv->dev, bg_pos, HSM_BLOCKSIZE); + mark_buffer_dirty(bh, 1); + brelse(bh); + brelse(bh); + } + } + free_page((unsigned long)pv->bg_array); + } else + MD_BUG(); + + bh = getblk (pv->dev, 0, HSM_BLOCKSIZE); + if (!bh) { + MD_BUG(); + return -1; + } + mark_buffer_dirty(bh, 1); + brelse(bh); + brelse(bh); + + return 0; +} + +struct semaphore hsm_sem = MUTEX; + +#define HSM_SECTORS (HSM_BLOCKSIZE/512) + +static int hsm_map (mddev_t *mddev, kdev_t dev, kdev_t *rdev, + unsigned long *rsector, unsigned long bsectors) +{ + lv_t *lv = kdev_to_lv(dev); + lv_lptr_t *index; + unsigned int lblock = *rsector / HSM_SECTORS; + unsigned int offset = *rsector % HSM_SECTORS; + int err = -EIO; + + if (!lv) { + printk("HSM: md%d not a Logical Volume!\n", mdidx(mddev)); + goto out; + } + if (offset + bsectors > HSM_SECTORS) { + MD_BUG(); + goto out; + } + down(&hsm_sem); + index = find_index(lv, lblock); + if (!index) { + printk("no block %u yet ... allocating\n", lblock); + index = alloc_fixed_index(lv, lblock); + } + + err = 0; + + printk(" %u <%s : %ld(%ld)> -> ", lblock, + partition_name(*rdev), *rsector, bsectors); + + *rdev = index_dev(lv, index); + *rsector = index_block(lv, index) * HSM_SECTORS + offset; + + printk(" <%s : %ld> %u\n", + partition_name(*rdev), *rsector, index_block(lv, index)); + + up(&hsm_sem); +out: + return err; +} + +static void free_index (lv_t *lv, lv_lptr_t * index) +{ + struct buffer_head *bh; + + printk("tryin to get cached block for index group <%s:%d>\n", + partition_name(index_dev(lv, index)), index_block(lv, index)); + + bh = getblk(index_dev(lv, index), index_block(lv, index),HSM_BLOCKSIZE); + + printk("....FREEING "); + print_index_list(lv, index); + + if (bh) { + if (!buffer_uptodate(bh)) + MD_BUG(); + if ((lv_lptr_t *)bh->b_data != index_child(index)) { + printk("huh? b_data is %p, index content is %p.\n", + bh->b_data, index_child(index)); + } else + printk("good, b_data == index content == %p.\n", + index_child(index)); + printk("b_count == %d, writing.\n", bh->b_count); + mark_buffer_dirty(bh, 1); + brelse(bh); + brelse(bh); + printk("done.\n"); + } else { + printk("FAILED!\n"); + } + print_index_list(lv, index); + index_child(index) = NULL; +} + +static void free_index_group (lv_t *lv, int level, lv_lptr_t * index_0) +{ + char dots [3*8]; + lv_lptr_t * index; + int i, nr_dots; + + nr_dots = (INDEX_DEPTH-level)*3; + memcpy(dots,"...............",nr_dots); + dots[nr_dots] = 0; + + dprintk("%s level %d index group block:\n", dots, level); + + + index = index_0; + for (i = 0; i < HSM_LPTRS_PER_BLOCK; i++) { + if (index->data.phys_block) { + dprintk("%s block <%u,%u,%x>\n", dots, + index->data.phys_nr, + index->data.phys_block, + index->cpu_addr); + if (level && index_present(index)) { + dprintk("%s==> deeper one level\n", dots); + free_index_group(lv, level-1, + index_child(index)); + dprintk("%s freeing index group block %p ...", + dots, index_child(index)); + free_index(lv, index); + } + } + index++; + } + dprintk("%s DONE: level %d index group block.\n", dots, level); +} + +static void free_lv_indextree (lv_t *lv) +{ + dprintk("freeing LV %d ...\n", lv->log_id); + dprintk("..root index: %p\n", index_child(&lv->root_index)); + dprintk("..INDEX TREE:\n"); + free_index_group(lv, INDEX_DEPTH-1, index_child(&lv->root_index)); + dprintk("..freeing root index %p ...", index_child(&lv->root_index)); + dprintk("root block <%u,%u,%x>\n", lv->root_index.data.phys_nr, + lv->root_index.data.phys_block, lv->root_index.cpu_addr); + free_index(lv, &lv->root_index); + dprintk("..INDEX TREE done.\n"); + fsync_dev(lv->vg->pv_array[0].dev); /* fix me */ + lv->vg->vg_sb->lv_array[lv->log_id].lv_free_indices = lv->free_indices; +} + +static void print_index_group (lv_t *lv, int level, lv_lptr_t * index_0) +{ + char dots [3*5]; + lv_lptr_t * index; + int i, nr_dots; + + nr_dots = (INDEX_DEPTH-level)*3; + memcpy(dots,"...............",nr_dots); + dots[nr_dots] = 0; + + dprintk("%s level %d index group block:\n", dots, level); + + + for (i = 0; i < HSM_LPTRS_PER_BLOCK; i++) { + index = index_0 + i; + if (index->data.phys_block) { + dprintk("%s block <%u,%u,%x>\n", dots, + index->data.phys_nr, + index->data.phys_block, + index->cpu_addr); + if (level && index_present(index)) { + dprintk("%s==> deeper one level\n", dots); + print_index_group(lv, level-1, + index_child(index)); + } + } + } + dprintk("%s DONE: level %d index group block.\n", dots, level); +} + +static void print_lv (lv_t *lv) +{ + dprintk("printing LV %d ...\n", lv->log_id); + dprintk("..root index: %p\n", index_child(&lv->root_index)); + dprintk("..INDEX TREE:\n"); + print_index_group(lv, INDEX_DEPTH-1, index_child(&lv->root_index)); + dprintk("..INDEX TREE done.\n"); +} + +static int map_lv (lv_t *lv) +{ + kdev_t dev = lv->dev; + unsigned int nr = MINOR(dev); + mddev_t *mddev = lv->vg->mddev; + + if (MAJOR(dev) != MD_MAJOR) { + MD_BUG(); + return -1; + } + if (kdev_to_mddev(dev)) { + MD_BUG(); + return -1; + } + md_hd_struct[nr].start_sect = 0; + md_hd_struct[nr].nr_sects = md_size[mdidx(mddev)] << 1; + md_size[nr] = md_size[mdidx(mddev)]; + add_mddev_mapping(mddev, dev, lv); + + return 0; +} + +static int unmap_lv (lv_t *lv) +{ + kdev_t dev = lv->dev; + unsigned int nr = MINOR(dev); + + if (MAJOR(dev) != MD_MAJOR) { + MD_BUG(); + return -1; + } + md_hd_struct[nr].start_sect = 0; + md_hd_struct[nr].nr_sects = 0; + md_size[nr] = 0; + del_mddev_mapping(lv->vg->mddev, dev); + + return 0; +} + +static int init_vg (vg_t *vg) +{ + int i; + lv_t *lv; + kdev_t dev; + vg_sb_t *vg_sb; + struct buffer_head *bh; + lv_descriptor_t *lv_desc; + + /* + * fix me: read all PVs and compare the SB + */ + dev = vg->pv_array[0].dev; + bh = bread (dev, 1, HSM_BLOCKSIZE); + if (!bh) { + MD_BUG(); + return -1; + } + + vg_sb = (vg_sb_t *) bh->b_data; + vg->vg_sb = vg_sb; + + if (vg_sb->vg_magic != HSM_VG_SB_MAGIC) { + printk("%s is not a valid VG, has magic %x instead of %x!\n", + partition_name(dev), vg_sb->vg_magic, + HSM_VG_SB_MAGIC); + return -1; + } + + vg->nr_lv = 0; + for (i = 0; i < HSM_MAX_LVS_PER_VG; i++) { + unsigned int id; + lv_desc = vg->vg_sb->lv_array + i; + + id = lv_desc->lv_id; + if (!id) { + printk("... LV desc %d empty\n", i); + continue; + } + if (id >= HSM_MAX_LVS_PER_VG) { + MD_BUG(); + continue; + } + + lv = vg->lv_array + id; + if (lv->vg) { + MD_BUG(); + continue; + } + lv->log_id = id; + lv->vg = vg; + lv->max_indices = lv_desc->lv_max_indices; + lv->free_indices = lv_desc->lv_free_indices; + lv->root_index.data = lv_desc->lv_root_idx; + lv->dev = MKDEV(MD_MAJOR, lv_desc->md_id); + + vg->nr_lv++; + + map_lv(lv); + if (read_root_index(lv)) { + vg->nr_lv--; + unmap_lv(lv); + memset(lv, 0, sizeof(*lv)); + } + } + if (vg->nr_lv != vg_sb->nr_lvs) + MD_BUG(); + + return 0; +} + +static int hsm_run (mddev_t *mddev) +{ + int i; + vg_t *vg; + mdk_rdev_t *rdev; + + MOD_INC_USE_COUNT; + + vg = kmalloc (sizeof (*vg), GFP_KERNEL); + if (!vg) + goto out; + memset(vg, 0, sizeof(*vg)); + mddev->private = vg; + vg->mddev = mddev; + + if (md_check_ordering(mddev)) { + printk("hsm: disks are not ordered, aborting!\n"); + goto out; + } + + set_blocksize (mddev_to_kdev(mddev), HSM_BLOCKSIZE); + + vg->nr_pv = mddev->nb_dev; + ITERATE_RDEV_ORDERED(mddev,rdev,i) { + pv_t *pv = vg->pv_array + i; + + pv->dev = rdev->dev; + fsync_dev (pv->dev); + set_blocksize (pv->dev, HSM_BLOCKSIZE); + pv->phys_nr = i; + if (init_pv(pv)) + goto out; + } + + init_vg(vg); + + return 0; + +out: + if (vg) { + kfree(vg); + mddev->private = NULL; + } + MOD_DEC_USE_COUNT; + + return 1; +} + +static int hsm_stop (mddev_t *mddev) +{ + lv_t *lv; + vg_t *vg; + int i; + + vg = mddev_to_vg(mddev); + + for (i = 0; i < HSM_MAX_LVS_PER_VG; i++) { + lv = vg->lv_array + i; + if (!lv->log_id) + continue; + print_lv(lv); + free_lv_indextree(lv); + unmap_lv(lv); + } + for (i = 0; i < vg->nr_pv; i++) + free_pv(vg->pv_array + i); + + kfree(vg); + + MOD_DEC_USE_COUNT; + + return 0; +} + + +static int hsm_status (char *page, mddev_t *mddev) +{ + int sz = 0, i; + lv_t *lv; + vg_t *vg; + + vg = mddev_to_vg(mddev); + + for (i = 0; i < HSM_MAX_LVS_PER_VG; i++) { + lv = vg->lv_array + i; + if (!lv->log_id) + continue; + sz += sprintf(page+sz, " ", lv->log_id, + lv->max_indices - lv->free_indices, lv->max_indices); + } + return sz; +} + + +static mdk_personality_t hsm_personality= +{ + "hsm", + hsm_map, + NULL, + NULL, + hsm_run, + hsm_stop, + hsm_status, + NULL, + 0, + NULL, + NULL, + NULL, + NULL +}; + +#ifndef MODULE + +md__initfunc(void hsm_init (void)) +{ + register_md_personality (HSM, &hsm_personality); +} + +#else + +int init_module (void) +{ + return (register_md_personality (HSM, &hsm_personality)); +} + +void cleanup_module (void) +{ + unregister_md_personality (HSM); +} + +#endif + +/* + * This Linus-trick catches bugs via the linker. + */ + +extern void __BUG__in__hsm_dot_c_1(void); +extern void __BUG__in__hsm_dot_c_2(void); +extern void __BUG__in__hsm_dot_c_3(void); +extern void __BUG__in__hsm_dot_c_4(void); +extern void __BUG__in__hsm_dot_c_5(void); +extern void __BUG__in__hsm_dot_c_6(void); +extern void __BUG__in__hsm_dot_c_7(void); + +void bugcatcher (void) +{ + if (sizeof(pv_block_group_t) != HSM_BLOCKSIZE) + __BUG__in__hsm_dot_c_1(); + if (sizeof(lv_index_block_t) != HSM_BLOCKSIZE) + __BUG__in__hsm_dot_c_2(); + + if (sizeof(pv_sb_t) != HSM_BLOCKSIZE) + __BUG__in__hsm_dot_c_4(); + if (sizeof(lv_sb_t) != HSM_BLOCKSIZE) + __BUG__in__hsm_dot_c_3(); + if (sizeof(vg_sb_t) != HSM_BLOCKSIZE) + __BUG__in__hsm_dot_c_6(); + + if (sizeof(lv_lptr_t) != 16) + __BUG__in__hsm_dot_c_5(); + if (sizeof(pv_pptr_t) != 16) + __BUG__in__hsm_dot_c_6(); +} + diff --git a/drivers/block/linear.c b/drivers/block/linear.c index b6f72fd6a038..e60ef217780d 100644 --- a/drivers/block/linear.c +++ b/drivers/block/linear.c @@ -1,4 +1,3 @@ - /* linear.c : Multiple Devices driver for Linux Copyright (C) 1994-96 Marc ZYNGIER @@ -19,186 +18,207 @@ #include -#include +#include #include -#include -#include "linear.h" +#include #define MAJOR_NR MD_MAJOR #define MD_DRIVER #define MD_PERSONALITY -static int linear_run (int minor, struct md_dev *mddev) +static int linear_run (mddev_t *mddev) { - int cur=0, i, size, dev0_size, nb_zone; - struct linear_data *data; - - MOD_INC_USE_COUNT; - - mddev->private=kmalloc (sizeof (struct linear_data), GFP_KERNEL); - data=(struct linear_data *) mddev->private; - - /* - Find out the smallest device. This was previously done - at registry time, but since it violates modularity, - I moved it here... Any comment ? ;-) - */ - - data->smallest=mddev->devices; - for (i=1; inb_dev; i++) - if (data->smallest->size > mddev->devices[i].size) - data->smallest=mddev->devices+i; + linear_conf_t *conf; + struct linear_hash *table; + mdk_rdev_t *rdev; + int size, i, j, nb_zone; + unsigned int curr_offset; + + MOD_INC_USE_COUNT; + + conf = kmalloc (sizeof (*conf), GFP_KERNEL); + if (!conf) + goto out; + mddev->private = conf; + + if (md_check_ordering(mddev)) { + printk("linear: disks are not ordered, aborting!\n"); + goto out; + } + /* + * Find the smallest device. + */ + + conf->smallest = NULL; + curr_offset = 0; + ITERATE_RDEV_ORDERED(mddev,rdev,j) { + dev_info_t *disk = conf->disks + j; + + disk->dev = rdev->dev; + disk->size = rdev->size; + disk->offset = curr_offset; + + curr_offset += disk->size; + + if (!conf->smallest || (disk->size < conf->smallest->size)) + conf->smallest = disk; + } + + nb_zone = conf->nr_zones = + md_size[mdidx(mddev)] / conf->smallest->size + + ((md_size[mdidx(mddev)] % conf->smallest->size) ? 1 : 0); - nb_zone=data->nr_zones= - md_size[minor]/data->smallest->size + - (md_size[minor]%data->smallest->size ? 1 : 0); - - data->hash_table=kmalloc (sizeof (struct linear_hash)*nb_zone, GFP_KERNEL); - - size=mddev->devices[cur].size; - - i=0; - while (curnb_dev) - { - data->hash_table[i].dev0=mddev->devices+cur; - - if (size>=data->smallest->size) /* If we completely fill the slot */ - { - data->hash_table[i++].dev1=NULL; - size-=data->smallest->size; - - if (!size) - { - if (++cur==mddev->nb_dev) continue; - size=mddev->devices[cur].size; - } - - continue; - } - - if (++cur==mddev->nb_dev) /* Last dev, set dev1 as NULL */ - { - data->hash_table[i].dev1=NULL; - continue; - } - - dev0_size=size; /* Here, we use a 2nd dev to fill the slot */ - size=mddev->devices[cur].size; - data->hash_table[i++].dev1=mddev->devices+cur; - size-=(data->smallest->size - dev0_size); - } - - return 0; + conf->hash_table = kmalloc (sizeof (struct linear_hash) * nb_zone, + GFP_KERNEL); + if (!conf->hash_table) + goto out; + + /* + * Here we generate the linear hash table + */ + table = conf->hash_table; + i = 0; + size = 0; + for (j = 0; j < mddev->nb_dev; j++) { + dev_info_t *disk = conf->disks + j; + + if (size < 0) { + table->dev1 = disk; + table++; + } + size += disk->size; + + while (size) { + table->dev0 = disk; + size -= conf->smallest->size; + if (size < 0) + break; + table->dev1 = NULL; + table++; + } + } + table->dev1 = NULL; + + return 0; + +out: + if (conf) + kfree(conf); + MOD_DEC_USE_COUNT; + return 1; } -static int linear_stop (int minor, struct md_dev *mddev) +static int linear_stop (mddev_t *mddev) { - struct linear_data *data=(struct linear_data *) mddev->private; + linear_conf_t *conf = mddev_to_conf(mddev); - kfree (data->hash_table); - kfree (data); + kfree(conf->hash_table); + kfree(conf); - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; - return 0; + return 0; } -static int linear_map (struct md_dev *mddev, kdev_t *rdev, +static int linear_map (mddev_t *mddev, kdev_t dev, kdev_t *rdev, unsigned long *rsector, unsigned long size) { - struct linear_data *data=(struct linear_data *) mddev->private; - struct linear_hash *hash; - struct real_dev *tmp_dev; - long block; + linear_conf_t *conf = mddev_to_conf(mddev); + struct linear_hash *hash; + dev_info_t *tmp_dev; + long block; - block=*rsector >> 1; - hash=data->hash_table+(block/data->smallest->size); + block = *rsector >> 1; + hash = conf->hash_table + (block / conf->smallest->size); - if (block >= (hash->dev0->size + hash->dev0->offset)) - { - if (!hash->dev1) - { - printk ("linear_map : hash->dev1==NULL for block %ld\n", block); - return (-1); - } - - tmp_dev=hash->dev1; - } - else - tmp_dev=hash->dev0; + if (block >= (hash->dev0->size + hash->dev0->offset)) + { + if (!hash->dev1) + { + printk ("linear_map : hash->dev1==NULL for block %ld\n", + block); + return -1; + } + tmp_dev = hash->dev1; + } else + tmp_dev = hash->dev0; - if (block >= (tmp_dev->size + tmp_dev->offset) || block < tmp_dev->offset) - printk ("Block %ld out of bounds on dev %s size %d offset %d\n", - block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset); + if (block >= (tmp_dev->size + tmp_dev->offset) + || block < tmp_dev->offset) + printk ("Block %ld out of bounds on dev %s size %d offset %d\n", + block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset); - *rdev=tmp_dev->dev; - *rsector=(block-(tmp_dev->offset)) << 1; + *rdev = tmp_dev->dev; + *rsector = (block - tmp_dev->offset) << 1; - return (0); + return 0; } -static int linear_status (char *page, int minor, struct md_dev *mddev) +static int linear_status (char *page, mddev_t *mddev) { - int sz=0; + int sz=0; #undef MD_DEBUG #ifdef MD_DEBUG - int j; - struct linear_data *data=(struct linear_data *) mddev->private; + int j; + linear_conf_t *conf = mddev_to_conf(mddev); - sz+=sprintf (page+sz, " "); - for (j=0; jnr_zones; j++) - { - sz+=sprintf (page+sz, "[%s", - partition_name (data->hash_table[j].dev0->dev)); - - if (data->hash_table[j].dev1) - sz+=sprintf (page+sz, "/%s] ", - partition_name(data->hash_table[j].dev1->dev)); - else - sz+=sprintf (page+sz, "] "); - } - - sz+=sprintf (page+sz, "\n"); + sz += sprintf(page+sz, " "); + for (j = 0; j < conf->nr_zones; j++) + { + sz += sprintf(page+sz, "[%s", + partition_name(conf->hash_table[j].dev0->dev)); + + if (conf->hash_table[j].dev1) + sz += sprintf(page+sz, "/%s] ", + partition_name(conf->hash_table[j].dev1->dev)); + else + sz += sprintf(page+sz, "] "); + } + sz += sprintf(page+sz, "\n"); #endif - sz+=sprintf (page+sz, " %dk rounding", 1<param.chunk_size/1024); + return sz; } -static struct md_personality linear_personality= +static mdk_personality_t linear_personality= { - "linear", - linear_map, - NULL, - NULL, - linear_run, - linear_stop, - linear_status, - NULL, /* no ioctls */ - 0 + "linear", + linear_map, + NULL, + NULL, + linear_run, + linear_stop, + linear_status, + NULL, + 0, + NULL, + NULL, + NULL, + NULL }; - #ifndef MODULE -__initfunc(void linear_init (void)) +md__initfunc(void linear_init (void)) { - register_md_personality (LINEAR, &linear_personality); + register_md_personality (LINEAR, &linear_personality); } #else int init_module (void) { - return (register_md_personality (LINEAR, &linear_personality)); + return (register_md_personality (LINEAR, &linear_personality)); } void cleanup_module (void) { - unregister_md_personality (LINEAR); + unregister_md_personality (LINEAR); } #endif + diff --git a/drivers/block/linear.h b/drivers/block/linear.h deleted file mode 100644 index 1146d8329fae..000000000000 --- a/drivers/block/linear.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _LINEAR_H -#define _LINEAR_H - -struct linear_hash -{ - struct real_dev *dev0, *dev1; -}; - -struct linear_data -{ - struct linear_hash *hash_table; /* Dynamically allocated */ - struct real_dev *smallest; - int nr_zones; -}; - -#endif diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index f07c528e3b98..9e8d84413471 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -50,6 +51,11 @@ DECLARE_TASK_QUEUE(tq_disk); */ spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; +/* + * per-major idle-IO detection + */ +unsigned long io_events[MAX_BLKDEV] = {0, }; + /* * used to wait on when there are no free requests */ @@ -424,6 +430,8 @@ void make_request(int major, int rw, struct buffer_head * bh) /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */ lock_buffer(bh); + if (!buffer_lowprio(bh)) + io_events[major]++; if (blk_size[major]) { unsigned long maxsector = (blk_size[major][MINOR(bh->b_rdev)] << 1) + 1; @@ -522,7 +530,7 @@ void make_request(int major, int rw, struct buffer_head * bh) * entry may be busy being processed and we thus can't change it. */ if (req == blk_dev[major].current_request) - req = req->next; + req = req->next; if (!req) break; /* fall through */ @@ -681,11 +689,12 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[]) bh[i]->b_rsector=bh[i]->b_blocknr*(bh[i]->b_size >> 9); #ifdef CONFIG_BLK_DEV_MD if (major==MD_MAJOR && - md_map (MINOR(bh[i]->b_dev), &bh[i]->b_rdev, - &bh[i]->b_rsector, bh[i]->b_size >> 9)) { - printk (KERN_ERR + /* changed v to allow LVM to remap */ + md_map (bh[i]->b_rdev, &bh[i]->b_rdev, + &bh[i]->b_rsector, bh[i]->b_size >> 9)) { + printk (KERN_ERR "Bad md_map in ll_rw_block\n"); - goto sorry; + goto sorry; } #endif } @@ -700,8 +709,10 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[]) if (bh[i]) { set_bit(BH_Req, &bh[i]->b_state); #ifdef CONFIG_BLK_DEV_MD - if (MAJOR(bh[i]->b_dev) == MD_MAJOR) { - md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]); + /* changed v to allow LVM to remap */ + if (MAJOR(bh[i]->b_rdev) == MD_MAJOR) { + /* changed for LVM to remap v */ + md_make_request(bh[i], rw); continue; } #endif @@ -784,10 +795,10 @@ __initfunc(int blk_dev_init(void)) for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) { dev->request_fn = NULL; - dev->queue = NULL; + dev->queue = NULL; dev->current_request = NULL; dev->plug.rq_status = RQ_INACTIVE; - dev->plug.cmd = -1; + dev->plug.cmd = -1; dev->plug.next = NULL; dev->plug_tq.sync = 0; dev->plug_tq.routine = &unplug_device; @@ -868,7 +879,7 @@ __initfunc(int blk_dev_init(void)) sbpcd_init(); #endif CONFIG_SBPCD #ifdef CONFIG_AZTCD - aztcd_init(); + aztcd_init(); #endif CONFIG_AZTCD #ifdef CONFIG_CDU535 sony535_init(); diff --git a/drivers/block/md.c b/drivers/block/md.c index 77090708e6d1..ca11dd495ee5 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -1,21 +1,17 @@ - /* md.c : Multiple Devices driver for Linux - Copyright (C) 1994-96 Marc ZYNGIER - or - + Copyright (C) 1998 Ingo Molnar - A lot of inspiration came from hd.c ... + completely rewritten, based on the MD driver code from Marc Zyngier - kerneld support by Boris Tobotras - boot support for linear and striped mode by Harald Hoyer + Changes: - RAID-1/RAID-5 extensions by: - Ingo Molnar, Miguel de Icaza, Gadi Oxman + - RAID-1/RAID-5 extensions by Miguel de Icaza, Gadi Oxman, Ingo Molnar + - boot support for linear and striped mode by Harald Hoyer + - kerneld support by Boris Tobotras + - kmod support by: Cyrus Durgin + - RAID0 bugfixes: Mark Anthony Lisher - Changes for kmod by: - Cyrus Durgin - 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) @@ -26,807 +22,2972 @@ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * Current RAID-1,4,5 parallel reconstruction speed limit is 1024 KB/sec, so - * the extra system load does not show up that much. Increase it if your - * system can take more. - */ -#define SPEED_LIMIT 1024 - #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include + #ifdef CONFIG_KMOD #include #endif -#include -#include #define __KERNEL_SYSCALLS__ #include +#include + +extern asmlinkage int sys_sched_yield(void); +extern asmlinkage int sys_setsid(void); + +extern unsigned long io_events[MAX_BLKDEV]; + #define MAJOR_NR MD_MAJOR #define MD_DRIVER #include -#include -#include -#include #ifdef CONFIG_MD_BOOT -extern kdev_t name_to_kdev_t(char *line) __init; +extern kdev_t name_to_kdev_t(char *line) md__init; #endif -static struct hd_struct md_hd_struct[MAX_MD_DEV]; -static int md_blocksizes[MAX_MD_DEV]; -int md_maxreadahead[MAX_MD_DEV]; -#if SUPPORT_RECONSTRUCTION -static struct md_thread *md_sync_thread = NULL; -#endif /* SUPPORT_RECONSTRUCTION */ +static mdk_personality_t *pers[MAX_PERSONALITY] = {NULL, }; + +/* + * these have to be allocated separately because external + * subsystems want to have a pre-defined structure + */ +struct hd_struct md_hd_struct[MAX_MD_DEVS]; +static int md_blocksizes[MAX_MD_DEVS]; +static int md_maxreadahead[MAX_MD_DEVS]; +static mdk_thread_t *md_recovery_thread = NULL; -int md_size[MAX_MD_DEV]={0, }; +int md_size[MAX_MD_DEVS] = {0, }; static void md_geninit (struct gendisk *); static struct gendisk md_gendisk= { - MD_MAJOR, - "md", - 0, - 1, - MAX_MD_DEV, - md_geninit, - md_hd_struct, - md_size, - MAX_MD_DEV, - NULL, - NULL + MD_MAJOR, + "md", + 0, + 1, + MAX_MD_DEVS, + md_geninit, + md_hd_struct, + md_size, + MAX_MD_DEVS, + NULL, + NULL }; -static struct md_personality *pers[MAX_PERSONALITY]={NULL, }; -struct md_dev md_dev[MAX_MD_DEV]; - -int md_thread(void * arg); +/* + * Current RAID-1,4,5 parallel reconstruction 'guaranteed speed limit' + * is 100 KB/sec, so the extra system load does not show up that much. + * Increase it if you want to have more _guaranteed_ speed. Note that + * the RAID driver will use the maximum available bandwith if the IO + * subsystem is idle. + * + * you can change it via /proc/sys/dev/speed-limit + */ -static struct gendisk *find_gendisk (kdev_t dev) -{ - struct gendisk *tmp=gendisk_head; +static int sysctl_speed_limit = 100; - while (tmp != NULL) - { - if (tmp->major==MAJOR(dev)) - return (tmp); - - tmp=tmp->next; - } +static struct ctl_table_header *md_table_header; - return (NULL); -} +static ctl_table md_table[] = { + {DEV_MD_SPEED_LIMIT, "speed-limit", + &sysctl_speed_limit, sizeof(int), 0644, NULL, &proc_dointvec}, + {0} +}; -char *partition_name (kdev_t dev) -{ - static char name[40]; /* This should be long - enough for a device name ! */ - struct gendisk *hd = find_gendisk (dev); +static ctl_table md_dir_table[] = { + {DEV_MD, "md", NULL, 0, 0555, md_table}, + {0} +}; - if (!hd) - { - sprintf (name, "[dev %s]", kdevname(dev)); - return (name); - } +static ctl_table md_root_table[] = { + {CTL_DEV, "dev", NULL, 0, 0555, md_dir_table}, + {0} +}; - return disk_name (hd, MINOR(dev), name); /* routine in genhd.c */ +static void md_register_sysctl(void) +{ + md_table_header = register_sysctl_table(md_root_table, 1); } -static int legacy_raid_sb (int minor, int pnum) +void md_unregister_sysctl(void) { - int i, factor; + unregister_sysctl_table(md_table_header); +} - factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); +/* + * The mapping between kdev and mddev is not necessary a simple + * one! Eg. HSM uses several sub-devices to implement Logical + * Volumes. All these sub-devices map to the same mddev. + */ +dev_mapping_t mddev_map [MAX_MD_DEVS] = { {NULL, 0}, }; - /***** - * do size and offset calculations. - */ - for (i=0; i> PERSONALITY_SHIFT) - md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * md_dev[minor].nb_dev; - return 0; +void add_mddev_mapping (mddev_t * mddev, kdev_t dev, void *data) +{ + unsigned int minor = MINOR(dev); + + if (MAJOR(dev) != MD_MAJOR) { + MD_BUG(); + return; + } + if (mddev_map[minor].mddev != NULL) { + MD_BUG(); + return; + } + mddev_map[minor].mddev = mddev; + mddev_map[minor].data = data; } -static void free_sb (struct md_dev *mddev) +void del_mddev_mapping (mddev_t * mddev, kdev_t dev) { - int i; - struct real_dev *realdev; + unsigned int minor = MINOR(dev); - if (mddev->sb) { - free_page((unsigned long) mddev->sb); - mddev->sb = NULL; + if (MAJOR(dev) != MD_MAJOR) { + MD_BUG(); + return; } - for (i = 0; i nb_dev; i++) { - realdev = mddev->devices + i; - if (realdev->sb) { - free_page((unsigned long) realdev->sb); - realdev->sb = NULL; - } + if (mddev_map[minor].mddev != mddev) { + MD_BUG(); + return; } + mddev_map[minor].mddev = NULL; + mddev_map[minor].data = NULL; } /* - * Check one RAID superblock for generic plausibility + * Enables to iterate over all existing md arrays */ +static MD_LIST_HEAD(all_mddevs); -#define BAD_MAGIC KERN_ERR \ -"md: %s: invalid raid superblock magic (%x) on block %u\n" - -#define OUT_OF_MEM KERN_ALERT \ -"md: out of memory.\n" +static mddev_t * alloc_mddev (kdev_t dev) +{ + mddev_t * mddev; -#define NO_DEVICE KERN_ERR \ -"md: disabled device %s\n" + if (MAJOR(dev) != MD_MAJOR) { + MD_BUG(); + return 0; + } + mddev = (mddev_t *) kmalloc(sizeof(*mddev), GFP_KERNEL); + if (!mddev) + return NULL; + + memset(mddev, 0, sizeof(*mddev)); + + mddev->__minor = MINOR(dev); + mddev->reconfig_sem = MUTEX; + mddev->recovery_sem = MUTEX; + mddev->resync_sem = MUTEX; + MD_INIT_LIST_HEAD(&mddev->disks); + /* + * The 'base' mddev is the one with data NULL. + * personalities can create additional mddevs + * if necessary. + */ + add_mddev_mapping(mddev, dev, 0); + md_list_add(&mddev->all_mddevs, &all_mddevs); -#define SUCCESS 0 -#define FAILURE -1 + return mddev; +} -static int analyze_one_sb (struct real_dev * rdev) +static void free_mddev (mddev_t *mddev) { - int ret = FAILURE; - struct buffer_head *bh; - kdev_t dev = rdev->dev; - md_superblock_t *sb; + if (!mddev) { + MD_BUG(); + return; + } /* - * Read the superblock, it's at the end of the disk + * Make sure nobody else is using this mddev + * (careful, we rely on the global kernel lock here) */ - rdev->sb_offset = MD_NEW_SIZE_BLOCKS (blk_size[MAJOR(dev)][MINOR(dev)]); - set_blocksize (dev, MD_SB_BYTES); - bh = bread (dev, rdev->sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + while (md_atomic_read(&mddev->resync_sem.count) != 1) + schedule(); + while (md_atomic_read(&mddev->recovery_sem.count) != 1) + schedule(); + + del_mddev_mapping(mddev, MKDEV(MD_MAJOR, mdidx(mddev))); + md_list_del(&mddev->all_mddevs); + MD_INIT_LIST_HEAD(&mddev->all_mddevs); + kfree(mddev); +} - if (bh) { - sb = (md_superblock_t *) bh->b_data; - if (sb->md_magic != MD_SB_MAGIC) { - printk (BAD_MAGIC, kdevname(dev), - sb->md_magic, rdev->sb_offset); - goto abort; - } - rdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL); - if (!rdev->sb) { - printk (OUT_OF_MEM); - goto abort; - } - memcpy (rdev->sb, bh->b_data, MD_SB_BYTES); - rdev->size = sb->size; - } else - printk (NO_DEVICE,kdevname(rdev->dev)); - ret = SUCCESS; -abort: - if (bh) - brelse (bh); - return ret; +struct gendisk * find_gendisk (kdev_t dev) +{ + struct gendisk *tmp = gendisk_head; + + while (tmp != NULL) { + if (tmp->major == MAJOR(dev)) + return (tmp); + tmp = tmp->next; + } + return (NULL); } -#undef SUCCESS -#undef FAILURE +mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr) +{ + mdk_rdev_t * rdev; + struct md_list_head *tmp; -#undef BAD_MAGIC -#undef OUT_OF_MEM -#undef NO_DEVICE + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->desc_nr == nr) + return rdev; + } + return NULL; +} -/* - * Check a full RAID array for plausibility - */ +mdk_rdev_t * find_rdev(mddev_t * mddev, kdev_t dev) +{ + struct md_list_head *tmp; + mdk_rdev_t *rdev; -#define INCONSISTENT KERN_ERR \ -"md: superblock inconsistency -- run ckraid\n" + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->dev == dev) + return rdev; + } + return NULL; +} -#define OUT_OF_DATE KERN_ERR \ -"md: superblock update time inconsistenty -- using the most recent one\n" +static MD_LIST_HEAD(device_names); -#define OLD_VERSION KERN_ALERT \ -"md: %s: unsupported raid array version %d.%d.%d\n" +char * partition_name (kdev_t dev) +{ + struct gendisk *hd; + static char nomem [] = ""; + dev_name_t *dname; + struct md_list_head *tmp = device_names.next; + + while (tmp != &device_names) { + dname = md_list_entry(tmp, dev_name_t, list); + if (dname->dev == dev) + return dname->name; + tmp = tmp->next; + } -#define NOT_CLEAN KERN_ERR \ -"md: %s: raid array is not clean -- run ckraid\n" + dname = (dev_name_t *) kmalloc(sizeof(*dname), GFP_KERNEL); -#define NOT_CLEAN_IGNORE KERN_ERR \ -"md: %s: raid array is not clean -- reconstructing parity\n" + if (!dname) + return nomem; + /* + * ok, add this new device name to the list + */ + hd = find_gendisk (dev); -#define UNKNOWN_LEVEL KERN_ERR \ -"md: %s: unsupported raid level %d\n" + if (!hd) + sprintf (dname->name, "[dev %s]", kdevname(dev)); + else + disk_name (hd, MINOR(dev), dname->name); -static int analyze_sbs (int minor, int pnum) + dname->dev = dev; + md_list_add(&dname->list, &device_names); + + return dname->name; +} + +static unsigned int calc_dev_sboffset (kdev_t dev, mddev_t *mddev, + int persistent) { - struct md_dev *mddev = md_dev + minor; - int i, N = mddev->nb_dev, out_of_date = 0; - struct real_dev * disks = mddev->devices; - md_superblock_t *sb, *freshest = NULL; + unsigned int size = 0; - /* - * RAID-0 and linear don't use a RAID superblock - */ - if (pnum == RAID0 >> PERSONALITY_SHIFT || - pnum == LINEAR >> PERSONALITY_SHIFT) - return legacy_raid_sb (minor, pnum); + if (blk_size[MAJOR(dev)]) + size = blk_size[MAJOR(dev)][MINOR(dev)]; + if (persistent) + size = MD_NEW_SIZE_BLOCKS(size); + return size; +} - /* - * Verify the RAID superblock on each real device - */ - for (i = 0; i < N; i++) - if (analyze_one_sb(disks+i)) - goto abort; +static unsigned int calc_dev_size (kdev_t dev, mddev_t *mddev, int persistent) +{ + unsigned int size; + + size = calc_dev_sboffset(dev, mddev, persistent); + if (!mddev->sb) { + MD_BUG(); + return size; + } + if (mddev->sb->chunk_size) + size &= ~(mddev->sb->chunk_size/1024 - 1); + return size; +} + +/* + * We check wether all devices are numbered from 0 to nb_dev-1. The + * order is guaranteed even after device name changes. + * + * Some personalities (raid0, linear) use this. Personalities that + * provide data have to be able to deal with loss of individual + * disks, so they do their checking themselves. + */ +int md_check_ordering (mddev_t *mddev) +{ + int i, c; + mdk_rdev_t *rdev; + struct md_list_head *tmp; /* - * The superblock constant part has to be the same - * for all disks in the array. + * First, all devices must be fully functional */ - sb = NULL; - for (i = 0; i < N; i++) { - if (!disks[i].sb) - continue; - if (!sb) { - sb = disks[i].sb; - continue; - } - if (memcmp(sb, - disks[i].sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) { - printk (INCONSISTENT); + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->faulty) { + printk("md: md%d's device %s faulty, aborting.\n", + mdidx(mddev), partition_name(rdev->dev)); goto abort; } } - /* - * OK, we have all disks and the array is ready to run. Let's - * find the freshest superblock, that one will be the superblock - * that represents the whole array. - */ - if ((sb = mddev->sb = (md_superblock_t *) __get_free_page (GFP_KERNEL)) == NULL) + c = 0; + ITERATE_RDEV(mddev,rdev,tmp) { + c++; + } + if (c != mddev->nb_dev) { + MD_BUG(); goto abort; - freshest = NULL; - for (i = 0; i < N; i++) { - if (!disks[i].sb) - continue; - if (!freshest) { - freshest = disks[i].sb; - continue; - } - /* - * Find the newest superblock version - */ - if (disks[i].sb->utime != freshest->utime) { - out_of_date = 1; - if (disks[i].sb->utime > freshest->utime) - freshest = disks[i].sb; - } } - if (out_of_date) - printk(OUT_OF_DATE); - memcpy (sb, freshest, sizeof(*freshest)); - - /* - * Check if we can support this RAID array - */ - if (sb->major_version != MD_MAJOR_VERSION || - sb->minor_version > MD_MINOR_VERSION) { - - printk (OLD_VERSION, kdevname(MKDEV(MD_MAJOR, minor)), - sb->major_version, sb->minor_version, - sb->patch_version); + if (mddev->nb_dev != mddev->sb->raid_disks) { + printk("md: md%d, array needs %d disks, has %d, aborting.\n", + mdidx(mddev), mddev->sb->raid_disks, mddev->nb_dev); goto abort; } - /* - * We need to add this as a superblock option. + * Now the numbering check */ -#if SUPPORT_RECONSTRUCTION - if (sb->state != (1 << MD_SB_CLEAN)) { - if (sb->level == 1) { - printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor))); + for (i = 0; i < mddev->nb_dev; i++) { + c = 0; + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->desc_nr == i) + c++; + } + if (c == 0) { + printk("md: md%d, missing disk #%d, aborting.\n", + mdidx(mddev), i); goto abort; - } else - printk (NOT_CLEAN_IGNORE, kdevname(MKDEV(MD_MAJOR, minor))); - } -#else - if (sb->state != (1 << MD_SB_CLEAN)) { - printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor))); - goto abort; - } -#endif /* SUPPORT_RECONSTRUCTION */ - - switch (sb->level) { - case 1: - md_size[minor] = sb->size; - md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD; - break; - case 4: - case 5: - md_size[minor] = sb->size * (sb->raid_disks - 1); - md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * (sb->raid_disks - 1); - break; - default: - printk (UNKNOWN_LEVEL, kdevname(MKDEV(MD_MAJOR, minor)), - sb->level); + } + if (c > 1) { + printk("md: md%d, too many disks #%d, aborting.\n", + mdidx(mddev), i); goto abort; + } } return 0; abort: - free_sb(mddev); return 1; } -#undef INCONSISTENT -#undef OUT_OF_DATE -#undef OLD_VERSION -#undef NOT_CLEAN -#undef OLD_LEVEL - -int md_update_sb(int minor) +static unsigned int zoned_raid_size (mddev_t *mddev) { - struct md_dev *mddev = md_dev + minor; - struct buffer_head *bh; - md_superblock_t *sb = mddev->sb; - struct real_dev *realdev; - kdev_t dev; - int i; - u32 sb_offset; + unsigned int mask; + mdk_rdev_t * rdev; + struct md_list_head *tmp; - sb->utime = CURRENT_TIME; - for (i = 0; i < mddev->nb_dev; i++) { - realdev = mddev->devices + i; - if (!realdev->sb) - continue; - dev = realdev->dev; - sb_offset = realdev->sb_offset; - set_blocksize(dev, MD_SB_BYTES); - printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset); - bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); - if (bh) { - sb = (md_superblock_t *) bh->b_data; - memcpy(sb, mddev->sb, MD_SB_BYTES); - memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - ll_rw_block(WRITE, 1, &bh); - wait_on_buffer(bh); - bforget(bh); - fsync_dev(dev); - invalidate_buffers(dev); - } else - printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev)); + if (!mddev->sb) { + MD_BUG(); + return -EINVAL; + } + /* + * do size and offset calculations. + */ + mask = ~(mddev->sb->chunk_size/1024 - 1); +printk("mask %08x\n", mask); + + ITERATE_RDEV(mddev,rdev,tmp) { +printk(" rdev->size: %d\n", rdev->size); + rdev->size &= mask; +printk(" masked rdev->size: %d\n", rdev->size); + md_size[mdidx(mddev)] += rdev->size; +printk(" new md_size: %d\n", md_size[mdidx(mddev)]); } return 0; } -static int do_md_run (int minor, int repart) +static void remove_descriptor (mdp_disk_t *disk, mdp_super_t *sb) { - int pnum, i, min, factor, err; + if (disk_active(disk)) { + sb->working_disks--; + } else { + if (disk_spare(disk)) { + sb->spare_disks--; + sb->working_disks--; + } else { + sb->failed_disks--; + } + } + sb->nr_disks--; + disk->major = 0; + disk->minor = 0; + mark_disk_removed(disk); +} - if (!md_dev[minor].nb_dev) - return -EINVAL; - - if (md_dev[minor].pers) - return -EBUSY; +#define BAD_MAGIC KERN_ERR \ +"md: invalid raid superblock magic on %s\n" - md_dev[minor].repartition=repart; - - if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT)) - >= MAX_PERSONALITY) - return -EINVAL; - - /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */ - if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){ - for (i = 0; i < md_dev [minor].nb_dev; i++) - if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR) - return -EINVAL; - } - if (!pers[pnum]) - { -#ifdef CONFIG_KMOD - char module_name[80]; - sprintf (module_name, "md-personality-%d", pnum); - request_module (module_name); - if (!pers[pnum]) -#endif - return -EINVAL; - } - - factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); - - for (i=0; irun (minor, md_dev+minor))) - { - md_dev[minor].pers=NULL; - free_sb(md_dev + minor); - return (err); - } - - if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT) - { - md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN); - md_update_sb(minor); - } - - /* FIXME : We assume here we have blocks - that are twice as large as sectors. - THIS MAY NOT BE TRUE !!! */ - md_hd_struct[minor].start_sect=0; - md_hd_struct[minor].nr_sects=md_size[minor]<<1; - - read_ahead[MD_MAJOR] = 128; - return (0); -} +#define BAD_CSUM KERN_WARNING \ +"md: invalid superblock checksum on %s\n" -static int do_md_stop (int minor, struct inode *inode) +static int alloc_array_sb (mddev_t * mddev) { - int i; - - if (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); - return -EBUSY; - } - - if (md_dev[minor].pers) { - /* - * It is safe to call stop here, it only frees private - * data. Also, it tells us if a device is unstoppable - * (eg. resyncing is in progress) - */ - if (md_dev[minor].pers->stop (minor, md_dev+minor)) - return -EBUSY; - /* - * The device won't exist anymore -> flush it now - */ - fsync_dev (inode->i_rdev); - invalidate_buffers (inode->i_rdev); - if (md_dev[minor].sb) { - md_dev[minor].sb->state |= 1 << MD_SB_CLEAN; - md_update_sb(minor); - } + if (mddev->sb) { + MD_BUG(); + return 0; } - - /* Remove locks. */ - if (md_dev[minor].sb) - free_sb(md_dev + minor); - for (i=0; isb = (mdp_super_t *) __get_free_page (GFP_KERNEL); + if (!mddev->sb) + return -ENOMEM; + md_clear_page((unsigned long)mddev->sb); + return 0; } -static int do_md_add (int minor, kdev_t dev) +static int alloc_disk_sb (mdk_rdev_t * rdev) { - int i; - int hot_add=0; - struct real_dev *realdev; + if (rdev->sb) + MD_BUG(); - if (md_dev[minor].nb_dev==MAX_REAL) + rdev->sb = (mdp_super_t *) __get_free_page(GFP_KERNEL); + if (!rdev->sb) { + printk (OUT_OF_MEM); return -EINVAL; + } + md_clear_page((unsigned long)rdev->sb); - if (!fs_may_mount (dev)) - return -EBUSY; + return 0; +} - if (blk_size[MAJOR(dev)] == NULL || blk_size[MAJOR(dev)][MINOR(dev)] == 0) { - printk("md_add(): zero device size, huh, bailing out.\n"); - return -EINVAL; +static void free_disk_sb (mdk_rdev_t * rdev) +{ + if (rdev->sb) { + free_page((unsigned long) rdev->sb); + rdev->sb = NULL; + rdev->sb_offset = 0; + rdev->size = 0; + } else { + if (!rdev->faulty) + MD_BUG(); } +} - if (md_dev[minor].pers) { - /* - * The array is already running, hot-add the drive, or - * bail out: - */ - if (!md_dev[minor].pers->hot_add_disk) - return -EBUSY; - else - hot_add=1; +static void mark_rdev_faulty (mdk_rdev_t * rdev) +{ + unsigned long flags; + + if (!rdev) { + MD_BUG(); + return; } + save_flags(flags); + cli(); + free_disk_sb(rdev); + rdev->faulty = 1; + restore_flags(flags); +} + +static int read_disk_sb (mdk_rdev_t * rdev) +{ + int ret = -EINVAL; + struct buffer_head *bh = NULL; + kdev_t dev = rdev->dev; + mdp_super_t *sb; + u32 sb_offset; + if (!rdev->sb) { + MD_BUG(); + goto abort; + } + /* - * Careful. We cannot increase nb_dev for a running array. + * Calculate the position of the superblock, + * it's at the end of the disk */ - i=md_dev[minor].nb_dev; - realdev = &md_dev[minor].devices[i]; - realdev->dev=dev; - - /* Lock the device by inserting a dummy inode. This doesn't - smell very good, but I need to be consistent with the - mount stuff, specially with fs_may_mount. If someone have - a better idea, please help ! */ - - realdev->inode=get_empty_inode (); - realdev->inode->i_dev=dev; /* don't care about other fields */ - insert_inode_hash (realdev->inode); - - /* Sizes are now rounded at run time */ - -/* md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/ - - realdev->size=blk_size[MAJOR(dev)][MINOR(dev)]; + sb_offset = calc_dev_sboffset(rdev->dev, rdev->mddev, 1); + rdev->sb_offset = sb_offset; + printk("(read) %s's sb offset: %d", partition_name(dev), + sb_offset); + fsync_dev(dev); + set_blocksize (dev, MD_SB_BYTES); + bh = bread (dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); - if (hot_add) { + if (bh) { + sb = (mdp_super_t *) bh->b_data; + memcpy (rdev->sb, sb, MD_SB_BYTES); + } else { + printk (NO_SB,partition_name(rdev->dev)); + goto abort; + } + printk(" [events: %08lx]\n", (unsigned long)get_unaligned(&rdev->sb->events)); + ret = 0; +abort: + if (bh) + brelse (bh); + return ret; +} + +static unsigned int calc_sb_csum (mdp_super_t * sb) +{ + unsigned int disk_csum, csum; + + disk_csum = sb->sb_csum; + sb->sb_csum = 0; + csum = csum_partial((void *)sb, MD_SB_BYTES, 0); + sb->sb_csum = disk_csum; + return csum; +} + +/* + * Check one RAID superblock for generic plausibility + */ + +static int check_disk_sb (mdk_rdev_t * rdev) +{ + mdp_super_t *sb; + int ret = -EINVAL; + + sb = rdev->sb; + if (!sb) { + MD_BUG(); + goto abort; + } + + if (sb->md_magic != MD_SB_MAGIC) { + printk (BAD_MAGIC, partition_name(rdev->dev)); + goto abort; + } + + if (sb->md_minor >= MAX_MD_DEVS) { + printk (BAD_MINOR, partition_name(rdev->dev), + sb->md_minor); + goto abort; + } + + if (calc_sb_csum(sb) != sb->sb_csum) + printk(BAD_CSUM, partition_name(rdev->dev)); + ret = 0; +abort: + return ret; +} + +static kdev_t dev_unit(kdev_t dev) +{ + unsigned int mask; + struct gendisk *hd = find_gendisk(dev); + + if (!hd) + return 0; + mask = ~((1 << hd->minor_shift) - 1); + + return MKDEV(MAJOR(dev), MINOR(dev) & mask); +} + +static mdk_rdev_t * match_dev_unit(mddev_t *mddev, kdev_t dev) +{ + struct md_list_head *tmp; + mdk_rdev_t *rdev; + + ITERATE_RDEV(mddev,rdev,tmp) + if (dev_unit(rdev->dev) == dev_unit(dev)) + return rdev; + + return NULL; +} + +static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) +{ + struct md_list_head *tmp; + mdk_rdev_t *rdev; + + ITERATE_RDEV(mddev1,rdev,tmp) + if (match_dev_unit(mddev2, rdev->dev)) + return 1; + + return 0; +} + +static MD_LIST_HEAD(all_raid_disks); +static MD_LIST_HEAD(pending_raid_disks); + +static void bind_rdev_to_array (mdk_rdev_t * rdev, mddev_t * mddev) +{ + mdk_rdev_t *same_pdev; + + if (rdev->mddev) { + MD_BUG(); + return; + } + same_pdev = match_dev_unit(mddev, rdev->dev); + if (same_pdev) + printk( KERN_WARNING +"md%d: WARNING: %s appears to be on the same physical disk as %s. True\n" +" protection against single-disk failure might be compromised.\n", + mdidx(mddev), partition_name(rdev->dev), + partition_name(same_pdev->dev)); + + md_list_add(&rdev->same_set, &mddev->disks); + rdev->mddev = mddev; + mddev->nb_dev++; + printk("bind<%s,%d>\n", partition_name(rdev->dev), mddev->nb_dev); +} + +static void unbind_rdev_from_array (mdk_rdev_t * rdev) +{ + if (!rdev->mddev) { + MD_BUG(); + return; + } + md_list_del(&rdev->same_set); + MD_INIT_LIST_HEAD(&rdev->same_set); + rdev->mddev->nb_dev--; + printk("unbind<%s,%d>\n", partition_name(rdev->dev), + rdev->mddev->nb_dev); + rdev->mddev = NULL; +} + +/* + * prevent the device from being mounted, repartitioned or + * otherwise reused by a RAID array (or any other kernel + * subsystem), by opening the device. [simply getting an + * inode is not enough, the SCSI module usage code needs + * an explicit open() on the device] + */ +static void lock_rdev (mdk_rdev_t *rdev) +{ + int err = 0; + + /* + * First insert a dummy inode. + */ + rdev->inode = get_empty_inode(); + /* + * we dont care about any other fields + */ + rdev->inode->i_dev = rdev->inode->i_rdev = rdev->dev; + insert_inode_hash(rdev->inode); + + memset(&rdev->filp, 0, sizeof(rdev->filp)); + rdev->filp.f_mode = 3; /* read write */ + err = blkdev_open(rdev->inode, &rdev->filp); + if (err) + printk("blkdev_open() failed: %d\n", err); +} + +static void unlock_rdev (mdk_rdev_t *rdev) +{ + blkdev_release(rdev->inode); +} + +static void export_rdev (mdk_rdev_t * rdev) +{ + printk("export_rdev(%s)\n",partition_name(rdev->dev)); + if (rdev->mddev) + MD_BUG(); + unlock_rdev(rdev); + free_disk_sb(rdev); + md_list_del(&rdev->all); + MD_INIT_LIST_HEAD(&rdev->all); + if (rdev->pending.next != &rdev->pending) { + printk("(%s was pending)\n",partition_name(rdev->dev)); + md_list_del(&rdev->pending); + MD_INIT_LIST_HEAD(&rdev->pending); + } + rdev->dev = 0; + rdev->faulty = 0; + kfree(rdev); +} + +static void kick_rdev_from_array (mdk_rdev_t * rdev) +{ + unbind_rdev_from_array(rdev); + export_rdev(rdev); +} + +static void export_array (mddev_t *mddev) +{ + struct md_list_head *tmp; + mdk_rdev_t *rdev; + mdp_super_t *sb = mddev->sb; + + if (mddev->sb) { + mddev->sb = NULL; + free_page((unsigned long) sb); + } + + ITERATE_RDEV(mddev,rdev,tmp) { + if (!rdev->mddev) { + MD_BUG(); + continue; + } + kick_rdev_from_array(rdev); + } + if (mddev->nb_dev) + MD_BUG(); +} + +#undef BAD_CSUM +#undef BAD_MAGIC +#undef OUT_OF_MEM +#undef NO_SB + +static void print_desc(mdp_disk_t *desc) +{ + printk(" DISK\n", desc->number, + partition_name(MKDEV(desc->major,desc->minor)), + desc->major,desc->minor,desc->raid_disk,desc->state); +} + +static void print_sb(mdp_super_t *sb) +{ + int i; + + printk(" SB: (V:%d.%d.%d) ID:<%08x.%08x.%08x.%08x> CT:%08x\n", + sb->major_version, sb->minor_version, sb->patch_version, + sb->set_uuid0, sb->set_uuid1, sb->set_uuid2, sb->set_uuid3, + sb->ctime); + printk(" L%d S%08d ND:%d RD:%d md%d LO:%d CS:%d\n", sb->level, + sb->size, sb->nr_disks, sb->raid_disks, sb->md_minor, + sb->layout, sb->chunk_size); + printk(" UT:%08x ST:%d AD:%d WD:%d FD:%d SD:%d CSUM:%08x E:%08lx\n", + sb->utime, sb->state, sb->active_disks, sb->working_disks, + sb->failed_disks, sb->spare_disks, + sb->sb_csum, (unsigned long)get_unaligned(&sb->events)); + + for (i = 0; i < MD_SB_DISKS; i++) { + mdp_disk_t *desc; + + desc = sb->disks + i; + printk(" D %2d: ", i); + print_desc(desc); + } + printk(" THIS: "); + print_desc(&sb->this_disk); + +} + +static void print_rdev(mdk_rdev_t *rdev) +{ + printk(" rdev %s: O:%s, SZ:%08d F:%d DN:%d ", + partition_name(rdev->dev), partition_name(rdev->old_dev), + rdev->size, rdev->faulty, rdev->desc_nr); + if (rdev->sb) { + printk("rdev superblock:\n"); + print_sb(rdev->sb); + } else + printk("no rdev superblock!\n"); +} + +void md_print_devices (void) +{ + struct md_list_head *tmp, *tmp2; + mdk_rdev_t *rdev; + mddev_t *mddev; + + printk("\n"); + printk(" **********************************\n"); + printk(" * *\n"); + printk(" **********************************\n"); + ITERATE_MDDEV(mddev,tmp) { + printk("md%d: ", mdidx(mddev)); + + ITERATE_RDEV(mddev,rdev,tmp2) + printk("<%s>", partition_name(rdev->dev)); + + if (mddev->sb) { + printk(" array superblock:\n"); + print_sb(mddev->sb); + } else + printk(" no array superblock.\n"); + + ITERATE_RDEV(mddev,rdev,tmp2) + print_rdev(rdev); + } + printk(" **********************************\n"); + printk("\n"); +} + +static int sb_equal ( mdp_super_t *sb1, mdp_super_t *sb2) +{ + int ret; + mdp_super_t *tmp1, *tmp2; + + tmp1 = kmalloc(sizeof(*tmp1),GFP_KERNEL); + tmp2 = kmalloc(sizeof(*tmp2),GFP_KERNEL); + + if (!tmp1 || !tmp2) { + ret = 0; + goto abort; + } + + *tmp1 = *sb1; + *tmp2 = *sb2; + + /* + * nr_disks is not constant + */ + tmp1->nr_disks = 0; + tmp2->nr_disks = 0; + + if (memcmp(tmp1, tmp2, MD_SB_GENERIC_CONSTANT_WORDS * 4)) + ret = 0; + else + ret = 1; + +abort: + if (tmp1) + kfree(tmp1); + if (tmp2) + kfree(tmp2); + + return ret; +} + +static int uuid_equal(mdk_rdev_t *rdev1, mdk_rdev_t *rdev2) +{ + if ( (rdev1->sb->set_uuid0 == rdev2->sb->set_uuid0) && + (rdev1->sb->set_uuid1 == rdev2->sb->set_uuid1) && + (rdev1->sb->set_uuid2 == rdev2->sb->set_uuid2) && + (rdev1->sb->set_uuid3 == rdev2->sb->set_uuid3)) + + return 1; + + return 0; +} + +static mdk_rdev_t * find_rdev_all (kdev_t dev) +{ + struct md_list_head *tmp; + mdk_rdev_t *rdev; + + tmp = all_raid_disks.next; + while (tmp != &all_raid_disks) { + rdev = md_list_entry(tmp, mdk_rdev_t, all); + if (rdev->dev == dev) + return rdev; + tmp = tmp->next; + } + return NULL; +} + +#define GETBLK_FAILED KERN_ERR \ +"md: getblk failed for device %s\n" + +static int write_disk_sb(mdk_rdev_t * rdev) +{ + struct buffer_head *bh; + kdev_t dev; + u32 sb_offset, size; + mdp_super_t *sb; + + if (!rdev->sb) { + MD_BUG(); + return -1; + } + if (rdev->faulty) { + MD_BUG(); + return -1; + } + if (rdev->sb->md_magic != MD_SB_MAGIC) { + MD_BUG(); + return -1; + } + + dev = rdev->dev; + sb_offset = calc_dev_sboffset(dev, rdev->mddev, 1); + if (rdev->sb_offset != sb_offset) { + printk("%s's sb offset has changed from %d to %d, skipping\n", partition_name(dev), rdev->sb_offset, sb_offset); + goto skip; + } + /* + * If the disk went offline meanwhile and it's just a spare, then + * it's size has changed to zero silently, and the MD code does + * not yet know that it's faulty. + */ + size = calc_dev_size(dev, rdev->mddev, 1); + if (size != rdev->size) { + printk("%s's size has changed from %d to %d since import, skipping\n", partition_name(dev), rdev->size, size); + goto skip; + } + + printk("(write) %s's sb offset: %d\n", partition_name(dev), sb_offset); + fsync_dev(dev); + set_blocksize(dev, MD_SB_BYTES); + bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + if (!bh) { + printk(GETBLK_FAILED, partition_name(dev)); + return 1; + } + memset(bh->b_data,0,bh->b_size); + sb = (mdp_super_t *) bh->b_data; + memcpy(sb, rdev->sb, MD_SB_BYTES); + + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + brelse(bh); + fsync_dev(dev); +skip: + return 0; +} +#undef GETBLK_FAILED KERN_ERR + +static void set_this_disk(mddev_t *mddev, mdk_rdev_t *rdev) +{ + int i, ok = 0; + mdp_disk_t *desc; + + for (i = 0; i < MD_SB_DISKS; i++) { + desc = mddev->sb->disks + i; +#if 0 + if (disk_faulty(desc)) { + if (MKDEV(desc->major,desc->minor) == rdev->dev) + ok = 1; + continue; + } +#endif + if (MKDEV(desc->major,desc->minor) == rdev->dev) { + rdev->sb->this_disk = *desc; + rdev->desc_nr = desc->number; + ok = 1; + break; + } + } + + if (!ok) { + MD_BUG(); + } +} + +static int sync_sbs(mddev_t * mddev) +{ + mdk_rdev_t *rdev; + mdp_super_t *sb; + struct md_list_head *tmp; + + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->faulty) + continue; + sb = rdev->sb; + *sb = *mddev->sb; + set_this_disk(mddev, rdev); + sb->sb_csum = calc_sb_csum(sb); + } + return 0; +} + +int md_update_sb(mddev_t * mddev) +{ + int first, err, count = 100; + struct md_list_head *tmp; + mdk_rdev_t *rdev; + __u64 ev; + +repeat: + mddev->sb->utime = CURRENT_TIME; + ev = get_unaligned(&mddev->sb->events); + ++ev; + put_unaligned(ev,&mddev->sb->events); + if (ev == (__u64)0) { + /* + * oops, this 64-bit counter should never wrap. + * Either we are in around ~1 trillion A.C., assuming + * 1 reboot per second, or we have a bug: + */ + MD_BUG(); + --ev; + put_unaligned(ev,&mddev->sb->events); + } + sync_sbs(mddev); + + /* + * do not write anything to disk if using + * nonpersistent superblocks + */ + if (mddev->sb->not_persistent) + return 0; + + printk(KERN_INFO "md: updating md%d RAID superblock on device\n", + mdidx(mddev)); + + first = 1; + err = 0; + ITERATE_RDEV(mddev,rdev,tmp) { + if (!first) { + first = 0; + printk(", "); + } + if (rdev->faulty) + printk("(skipping faulty "); + printk("%s ", partition_name(rdev->dev)); + if (!rdev->faulty) { + printk("[events: %08lx]", + (unsigned long)get_unaligned(&rdev->sb->events)); + err += write_disk_sb(rdev); + } else + printk(")\n"); + } + printk(".\n"); + if (err) { + printk("errors occured during superblock update, repeating\n"); + if (--count) + goto repeat; + printk("excessive errors occured during superblock update, exiting\n"); + } + return 0; +} + +/* + * Import a device. If 'on_disk', then sanity check the superblock + * + * mark the device faulty if: + * + * - the device is nonexistent (zero size) + * - the device has no valid superblock + * + * a faulty rdev _never_ has rdev->sb set. + */ +static int md_import_device (kdev_t newdev, int on_disk) +{ + int err; + mdk_rdev_t *rdev; + unsigned int size; + + if (find_rdev_all(newdev)) + return -EEXIST; + + rdev = (mdk_rdev_t *) kmalloc(sizeof(*rdev), GFP_KERNEL); + if (!rdev) { + printk("could not alloc mem for %s!\n", partition_name(newdev)); + return -ENOMEM; + } + memset(rdev, 0, sizeof(*rdev)); + + if (!fs_may_mount(newdev)) { + printk("md: can not import %s, has active inodes!\n", + partition_name(newdev)); + err = -EBUSY; + goto abort_free; + } + + if ((err = alloc_disk_sb(rdev))) + goto abort_free; + + rdev->dev = newdev; + lock_rdev(rdev); + rdev->desc_nr = -1; + rdev->faulty = 0; + + size = 0; + if (blk_size[MAJOR(newdev)]) + size = blk_size[MAJOR(newdev)][MINOR(newdev)]; + if (!size) { + printk("md: %s has zero size, marking faulty!\n", + partition_name(newdev)); + err = -EINVAL; + goto abort_free; + } + + if (on_disk) { + if ((err = read_disk_sb(rdev))) { + printk("md: could not read %s's sb, not importing!\n", + partition_name(newdev)); + goto abort_free; + } + if ((err = check_disk_sb(rdev))) { + printk("md: %s has invalid sb, not importing!\n", + partition_name(newdev)); + goto abort_free; + } + + rdev->old_dev = MKDEV(rdev->sb->this_disk.major, + rdev->sb->this_disk.minor); + rdev->desc_nr = rdev->sb->this_disk.number; + } + md_list_add(&rdev->all, &all_raid_disks); + MD_INIT_LIST_HEAD(&rdev->pending); + + if (rdev->faulty && rdev->sb) + free_disk_sb(rdev); + return 0; + +abort_free: + if (rdev->sb) + free_disk_sb(rdev); + kfree(rdev); + return err; +} + +/* + * Check a full RAID array for plausibility + */ + +#define INCONSISTENT KERN_ERR \ +"md: fatal superblock inconsistency in %s -- removing from array\n" + +#define OUT_OF_DATE KERN_ERR \ +"md: superblock update time inconsistency -- using the most recent one\n" + +#define OLD_VERSION KERN_ALERT \ +"md: md%d: unsupported raid array version %d.%d.%d\n" + +#define NOT_CLEAN_IGNORE KERN_ERR \ +"md: md%d: raid array is not clean -- starting background reconstruction\n" + +#define UNKNOWN_LEVEL KERN_ERR \ +"md: md%d: unsupported raid level %d\n" + +static int analyze_sbs (mddev_t * mddev) +{ + int out_of_date = 0, i; + struct md_list_head *tmp, *tmp2; + mdk_rdev_t *rdev, *rdev2, *freshest; + mdp_super_t *sb; + + /* + * Verify the RAID superblock on each real device + */ + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->faulty) { + MD_BUG(); + goto abort; + } + if (!rdev->sb) { + MD_BUG(); + goto abort; + } + if (check_disk_sb(rdev)) + goto abort; + } + + /* + * The superblock constant part has to be the same + * for all disks in the array. + */ + sb = NULL; + + ITERATE_RDEV(mddev,rdev,tmp) { + if (!sb) { + sb = rdev->sb; + continue; + } + if (!sb_equal(sb, rdev->sb)) { + printk (INCONSISTENT, partition_name(rdev->dev)); + kick_rdev_from_array(rdev); + continue; + } + } + + /* + * OK, we have all disks and the array is ready to run. Let's + * find the freshest superblock, that one will be the superblock + * that represents the whole array. + */ + if (!mddev->sb) + if (alloc_array_sb(mddev)) + goto abort; + sb = mddev->sb; + freshest = NULL; + + ITERATE_RDEV(mddev,rdev,tmp) { + __u64 ev1, ev2; + /* + * if the checksum is invalid, use the superblock + * only as a last resort. (decrease it's age by + * one event) + */ + if (calc_sb_csum(rdev->sb) != rdev->sb->sb_csum) { + __u64 ev = get_unaligned(&rdev->sb->events); + if (ev != (__u64)0) { + --ev; + put_unaligned(ev,&rdev->sb->events); + } + } + + printk("%s's event counter: %08lx\n", partition_name(rdev->dev), + (unsigned long)get_unaligned(&rdev->sb->events)); + if (!freshest) { + freshest = rdev; + continue; + } + /* + * Find the newest superblock version + */ + ev1 = get_unaligned(&rdev->sb->events); + ev2 = get_unaligned(&freshest->sb->events); + if (ev1 != ev2) { + out_of_date = 1; + if (ev1 > ev2) + freshest = rdev; + } + } + if (out_of_date) { + printk(OUT_OF_DATE); + printk("freshest: %s\n", partition_name(freshest->dev)); + } + memcpy (sb, freshest->sb, sizeof(*sb)); + + /* + * at this point we have picked the 'best' superblock + * from all available superblocks. + * now we validate this superblock and kick out possibly + * failed disks. + */ + ITERATE_RDEV(mddev,rdev,tmp) { + /* + * Kick all non-fresh devices faulty + */ + __u64 ev1, ev2; + ev1 = get_unaligned(&rdev->sb->events); + ev2 = get_unaligned(&sb->events); + ++ev1; + if (ev1 < ev2) { + printk("md: kicking non-fresh %s from array!\n", + partition_name(rdev->dev)); + kick_rdev_from_array(rdev); + continue; + } + } + + /* + * Fix up changed device names ... but only if this disk has a + * recent update time. Use faulty checksum ones too. + */ + ITERATE_RDEV(mddev,rdev,tmp) { + __u64 ev1, ev2, ev3; + if (rdev->faulty) { /* REMOVEME */ + MD_BUG(); + goto abort; + } + ev1 = get_unaligned(&rdev->sb->events); + ev2 = get_unaligned(&sb->events); + ev3 = ev2; + --ev3; + if ((rdev->dev != rdev->old_dev) && + ((ev1 == ev2) || (ev1 == ev3))) { + mdp_disk_t *desc; + + printk("md: device name has changed from %s to %s since last import!\n", partition_name(rdev->old_dev), partition_name(rdev->dev)); + if (rdev->desc_nr == -1) { + MD_BUG(); + goto abort; + } + desc = &sb->disks[rdev->desc_nr]; + if (rdev->old_dev != MKDEV(desc->major, desc->minor)) { + MD_BUG(); + goto abort; + } + desc->major = MAJOR(rdev->dev); + desc->minor = MINOR(rdev->dev); + desc = &rdev->sb->this_disk; + desc->major = MAJOR(rdev->dev); + desc->minor = MINOR(rdev->dev); + } + } + + /* + * Remove unavailable and faulty devices ... + * + * note that if an array becomes completely unrunnable due to + * missing devices, we do not write the superblock back, so the + * administrator has a chance to fix things up. The removal thus + * only happens if it's nonfatal to the contents of the array. + */ + for (i = 0; i < MD_SB_DISKS; i++) { + int found; + mdp_disk_t *desc; + kdev_t dev; + + desc = sb->disks + i; + dev = MKDEV(desc->major, desc->minor); + + /* + * We kick faulty devices/descriptors immediately. + */ + if (disk_faulty(desc)) { + found = 0; + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->desc_nr != desc->number) + continue; + printk("md%d: kicking faulty %s!\n", + mdidx(mddev),partition_name(rdev->dev)); + kick_rdev_from_array(rdev); + found = 1; + break; + } + if (!found) { + if (dev == MKDEV(0,0)) + continue; + printk("md%d: removing former faulty %s!\n", + mdidx(mddev), partition_name(dev)); + } + remove_descriptor(desc, sb); + continue; + } + + if (dev == MKDEV(0,0)) + continue; + /* + * Is this device present in the rdev ring? + */ + found = 0; + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->desc_nr == desc->number) { + found = 1; + break; + } + } + if (found) + continue; + + printk("md%d: former device %s is unavailable, removing from array!\n", mdidx(mddev), partition_name(dev)); + remove_descriptor(desc, sb); + } + + /* + * Double check wether all devices mentioned in the + * superblock are in the rdev ring. + */ + for (i = 0; i < MD_SB_DISKS; i++) { + mdp_disk_t *desc; + kdev_t dev; + + desc = sb->disks + i; + dev = MKDEV(desc->major, desc->minor); + + if (dev == MKDEV(0,0)) + continue; + + if (disk_faulty(desc)) { + MD_BUG(); + goto abort; + } + + rdev = find_rdev(mddev, dev); + if (!rdev) { + MD_BUG(); + goto abort; + } + } + + /* + * Do a final reality check. + */ + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->desc_nr == -1) { + MD_BUG(); + goto abort; + } + /* + * is the desc_nr unique? + */ + ITERATE_RDEV(mddev,rdev2,tmp2) { + if ((rdev2 != rdev) && + (rdev2->desc_nr == rdev->desc_nr)) { + MD_BUG(); + goto abort; + } + } + /* + * is the device unique? + */ + ITERATE_RDEV(mddev,rdev2,tmp2) { + if ((rdev2 != rdev) && + (rdev2->dev == rdev->dev)) { + MD_BUG(); + goto abort; + } + } + } + + /* + * Check if we can support this RAID array + */ + if (sb->major_version != MD_MAJOR_VERSION || + sb->minor_version > MD_MINOR_VERSION) { + + printk (OLD_VERSION, mdidx(mddev), sb->major_version, + sb->minor_version, sb->patch_version); + goto abort; + } + + if ((sb->state != (1 << MD_SB_CLEAN)) && ((sb->level == 1) || + (sb->level == 4) || (sb->level == 5))) + printk (NOT_CLEAN_IGNORE, mdidx(mddev)); + + return 0; +abort: + return 1; +} + +#undef INCONSISTENT +#undef OUT_OF_DATE +#undef OLD_VERSION +#undef OLD_LEVEL + +static int device_size_calculation (mddev_t * mddev) +{ + int data_disks = 0, persistent; + unsigned int readahead; + mdp_super_t *sb = mddev->sb; + struct md_list_head *tmp; + mdk_rdev_t *rdev; + + /* + * Do device size calculation. Bail out if too small. + * (we have to do this after having validated chunk_size, + * because device size has to be modulo chunk_size) + */ + persistent = !mddev->sb->not_persistent; + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->faulty) + continue; + if (rdev->size) { + MD_BUG(); + continue; + } + rdev->size = calc_dev_size(rdev->dev, mddev, persistent); + if (rdev->size < sb->chunk_size / 1024) { + printk (KERN_WARNING + "Dev %s smaller than chunk_size: %dk < %dk\n", + partition_name(rdev->dev), + rdev->size, sb->chunk_size / 1024); + return -EINVAL; + } + } + + switch (sb->level) { + case -3: + data_disks = 1; + break; + case -2: + data_disks = 1; + break; + case -1: + zoned_raid_size(mddev); + data_disks = 1; + break; + case 0: + zoned_raid_size(mddev); + data_disks = sb->raid_disks; + break; + case 1: + data_disks = 1; + break; + case 4: + case 5: + data_disks = sb->raid_disks-1; + break; + default: + printk (UNKNOWN_LEVEL, mdidx(mddev), sb->level); + goto abort; + } + if (!md_size[mdidx(mddev)]) + md_size[mdidx(mddev)] = sb->size * data_disks; + + readahead = MD_READAHEAD; + if ((sb->level == 0) || (sb->level == 4) || (sb->level == 5)) + readahead = mddev->sb->chunk_size * 4 * data_disks; + if (readahead < data_disks * MAX_SECTORS*512*2) + readahead = data_disks * MAX_SECTORS*512*2; + else { + if (sb->level == -3) + readahead = 0; + } + md_maxreadahead[mdidx(mddev)] = readahead; + + printk(KERN_INFO "md%d: max total readahead window set to %dk\n", + mdidx(mddev), readahead/1024); + + printk(KERN_INFO + "md%d: %d data-disks, max readahead per data-disk: %dk\n", + mdidx(mddev), data_disks, readahead/data_disks/1024); + return 0; +abort: + return 1; +} + + +#define TOO_BIG_CHUNKSIZE KERN_ERR \ +"too big chunk_size: %d > %d\n" + +#define TOO_SMALL_CHUNKSIZE KERN_ERR \ +"too small chunk_size: %d < %ld\n" + +#define BAD_CHUNKSIZE KERN_ERR \ +"no chunksize specified, see 'man raidtab'\n" + +static int do_md_run (mddev_t * mddev) +{ + int pnum, err; + int chunk_size; + struct md_list_head *tmp; + mdk_rdev_t *rdev; + + + if (!mddev->nb_dev) { + MD_BUG(); + return -EINVAL; + } + + if (mddev->pers) + return -EBUSY; + + /* + * Resize disks to align partitions size on a given + * chunk size. + */ + md_size[mdidx(mddev)] = 0; + + /* + * Analyze all RAID superblock(s) + */ + if (analyze_sbs(mddev)) { + MD_BUG(); + return -EINVAL; + } + + chunk_size = mddev->sb->chunk_size; + pnum = level_to_pers(mddev->sb->level); + + mddev->param.chunk_size = chunk_size; + mddev->param.personality = pnum; + + if (chunk_size > MAX_CHUNK_SIZE) { + printk(TOO_BIG_CHUNKSIZE, chunk_size, MAX_CHUNK_SIZE); + return -EINVAL; + } + /* + * chunk-size has to be a power of 2 and multiples of PAGE_SIZE + */ + if ( (1 << ffz(~chunk_size)) != chunk_size) { + MD_BUG(); + return -EINVAL; + } + if (chunk_size < PAGE_SIZE) { + printk(TOO_SMALL_CHUNKSIZE, chunk_size, PAGE_SIZE); + return -EINVAL; + } + + if (pnum >= MAX_PERSONALITY) { + MD_BUG(); + return -EINVAL; + } + + if ((pnum != RAID1) && (pnum != LINEAR) && !chunk_size) { + /* + * 'default chunksize' in the old md code used to + * be PAGE_SIZE, baaad. + * we abort here to be on the safe side. We dont + * want to continue the bad practice. + */ + printk(BAD_CHUNKSIZE); + return -EINVAL; + } + + if (!pers[pnum]) + { +#ifdef CONFIG_KMOD + char module_name[80]; + sprintf (module_name, "md-personality-%d", pnum); + request_module (module_name); + if (!pers[pnum]) +#endif + return -EINVAL; + } + + if (device_size_calculation(mddev)) + return -EINVAL; + + /* + * Drop all container device buffers, from now on + * the only valid external interface is through the md + * device. + */ + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->faulty) + continue; + fsync_dev(rdev->dev); + invalidate_buffers(rdev->dev); + } + + mddev->pers = pers[pnum]; + + err = mddev->pers->run(mddev); + if (err) { + printk("pers->run() failed ...\n"); + mddev->pers = NULL; + return -EINVAL; + } + + mddev->sb->state &= ~(1 << MD_SB_CLEAN); + md_update_sb(mddev); + + /* + * md_size has units of 1K blocks, which are + * twice as large as sectors. + */ + md_hd_struct[mdidx(mddev)].start_sect = 0; + md_hd_struct[mdidx(mddev)].nr_sects = md_size[mdidx(mddev)] << 1; + + read_ahead[MD_MAJOR] = 1024; + return (0); +} + +#undef TOO_BIG_CHUNKSIZE +#undef BAD_CHUNKSIZE + +#define OUT(x) do { err = (x); goto out; } while (0) + +static int restart_array (mddev_t *mddev) +{ + int err = 0; + + /* + * Complain if it has no devices + */ + if (!mddev->nb_dev) + OUT(-ENXIO); + + if (mddev->pers) { + if (!mddev->ro) + OUT(-EBUSY); + + mddev->ro = 0; + set_device_ro(mddev_to_kdev(mddev), 0); + + printk (KERN_INFO + "md%d switched to read-write mode.\n", mdidx(mddev)); + /* + * Kick recovery or resync if necessary + */ + md_recover_arrays(); + if (mddev->pers->restart_resync) + mddev->pers->restart_resync(mddev); + } else + err = -EINVAL; + +out: + return err; +} + +#define STILL_MOUNTED KERN_WARNING \ +"md: md%d still mounted.\n" + +static int do_md_stop (mddev_t * mddev, int ro) +{ + int err = 0, resync_interrupted = 0; + kdev_t dev = mddev_to_kdev(mddev); + + if (!ro && !fs_may_mount (dev)) { + printk (STILL_MOUNTED, mdidx(mddev)); + OUT(-EBUSY); + } + + /* + * complain if it's already stopped + */ + if (!mddev->nb_dev) + OUT(-ENXIO); + + if (mddev->pers) { + /* + * It is safe to call stop here, it only frees private + * data. Also, it tells us if a device is unstoppable + * (eg. resyncing is in progress) + */ + if (mddev->pers->stop_resync) + if (mddev->pers->stop_resync(mddev)) + resync_interrupted = 1; + + if (mddev->recovery_running) + md_interrupt_thread(md_recovery_thread); + + /* + * This synchronizes with signal delivery to the + * resync or reconstruction thread. It also nicely + * hangs the process if some reconstruction has not + * finished. + */ + down(&mddev->recovery_sem); + up(&mddev->recovery_sem); + + /* + * sync and invalidate buffers because we cannot kill the + * main thread with valid IO transfers still around. + * the kernel lock protects us from new requests being + * added after invalidate_buffers(). + */ + fsync_dev (mddev_to_kdev(mddev)); + fsync_dev (dev); + invalidate_buffers (dev); + + if (ro) { + if (mddev->ro) + OUT(-ENXIO); + mddev->ro = 1; + } else { + if (mddev->ro) + set_device_ro(dev, 0); + if (mddev->pers->stop(mddev)) { + if (mddev->ro) + set_device_ro(dev, 1); + OUT(-EBUSY); + } + if (mddev->ro) + mddev->ro = 0; + } + if (mddev->sb) { + /* + * mark it clean only if there was no resync + * interrupted. + */ + if (!mddev->recovery_running && !resync_interrupted) { + printk("marking sb clean...\n"); + mddev->sb->state |= 1 << MD_SB_CLEAN; + } + md_update_sb(mddev); + } + if (ro) + set_device_ro(dev, 1); + } + + /* + * Free resources if final stop + */ + if (!ro) { + export_array(mddev); + md_size[mdidx(mddev)] = 0; + md_hd_struct[mdidx(mddev)].nr_sects = 0; + free_mddev(mddev); + + printk (KERN_INFO "md%d stopped.\n", mdidx(mddev)); + } else + printk (KERN_INFO + "md%d switched to read-only mode.\n", mdidx(mddev)); +out: + return err; +} + +#undef OUT + +/* + * We have to safely support old arrays too. + */ +int detect_old_array (mdp_super_t *sb) +{ + if (sb->major_version > 0) + return 0; + if (sb->minor_version >= 90) + return 0; + + return -EINVAL; +} + + +static void autorun_array (mddev_t *mddev) +{ + mdk_rdev_t *rdev; + struct md_list_head *tmp; + int err; + + if (mddev->disks.prev == &mddev->disks) { + MD_BUG(); + return; + } + + printk("running: "); + + ITERATE_RDEV(mddev,rdev,tmp) { + printk("<%s>", partition_name(rdev->dev)); + } + printk("\nnow!\n"); + + err = do_md_run (mddev); + if (err) { + printk("do_md_run() returned %d\n", err); /* - * Check the superblock for consistency. - * The personality itself has to check whether it's getting - * added with the proper flags. The personality has to be - * checked too. ;) + * prevent the writeback of an unrunnable array */ - if (analyze_one_sb (realdev)) + mddev->sb_dirty = 0; + do_md_stop (mddev, 0); + } +} + +/* + * lets try to run arrays based on all disks that have arrived + * until now. (those are in the ->pending list) + * + * the method: pick the first pending disk, collect all disks with + * the same UUID, remove all from the pending list and put them into + * the 'same_array' list. Then order this list based on superblock + * update time (freshest comes first), kick out 'old' disks and + * compare superblocks. If everything's fine then run it. + */ +static void autorun_devices (void) +{ + struct md_list_head candidates; + struct md_list_head *tmp; + mdk_rdev_t *rdev0, *rdev; + mddev_t *mddev; + kdev_t md_kdev; + + + printk("autorun ...\n"); + while (pending_raid_disks.next != &pending_raid_disks) { + rdev0 = md_list_entry(pending_raid_disks.next, + mdk_rdev_t, pending); + + printk("considering %s ...\n", partition_name(rdev0->dev)); + MD_INIT_LIST_HEAD(&candidates); + ITERATE_RDEV_PENDING(rdev,tmp) { + if (uuid_equal(rdev0, rdev)) { + if (!sb_equal(rdev0->sb, rdev->sb)) { + printk("%s has same UUID as %s, but superblocks differ ...\n", partition_name(rdev->dev), partition_name(rdev0->dev)); + continue; + } + printk(" adding %s ...\n", partition_name(rdev->dev)); + md_list_del(&rdev->pending); + md_list_add(&rdev->pending, &candidates); + } + } + /* + * now we have a set of devices, with all of them having + * mostly sane superblocks. It's time to allocate the + * mddev. + */ + md_kdev = MKDEV(MD_MAJOR, rdev0->sb->md_minor); + mddev = kdev_to_mddev(md_kdev); + if (mddev) { + printk("md%d already running, cannot run %s\n", + mdidx(mddev), partition_name(rdev0->dev)); + ITERATE_RDEV_GENERIC(candidates,pending,rdev,tmp) + export_rdev(rdev); + continue; + } + mddev = alloc_mddev(md_kdev); + printk("created md%d\n", mdidx(mddev)); + ITERATE_RDEV_GENERIC(candidates,pending,rdev,tmp) { + bind_rdev_to_array(rdev, mddev); + md_list_del(&rdev->pending); + MD_INIT_LIST_HEAD(&rdev->pending); + } + autorun_array(mddev); + } + printk("... autorun DONE.\n"); +} + +/* + * import RAID devices based on one partition + * if possible, the array gets run as well. + */ + +#define BAD_VERSION KERN_ERR \ +"md: %s has RAID superblock version 0.%d, autodetect needs v0.90 or higher\n" + +#define OUT_OF_MEM KERN_ALERT \ +"md: out of memory.\n" + +#define NO_DEVICE KERN_ERR \ +"md: disabled device %s\n" + +#define AUTOADD_FAILED KERN_ERR \ +"md: auto-adding devices to md%d FAILED (error %d).\n" + +#define AUTOADD_FAILED_USED KERN_ERR \ +"md: cannot auto-add device %s to md%d, already used.\n" + +#define AUTORUN_FAILED KERN_ERR \ +"md: auto-running md%d FAILED (error %d).\n" + +#define MDDEV_BUSY KERN_ERR \ +"md: cannot auto-add to md%d, already running.\n" + +#define AUTOADDING KERN_INFO \ +"md: auto-adding devices to md%d, based on %s's superblock.\n" + +#define AUTORUNNING KERN_INFO \ +"md: auto-running md%d.\n" + +static int autostart_array (kdev_t startdev) +{ + int err = -EINVAL, i; + mdp_super_t *sb = NULL; + mdk_rdev_t *start_rdev = NULL, *rdev; + + if (md_import_device(startdev, 1)) { + printk("could not import %s!\n", partition_name(startdev)); + goto abort; + } + + start_rdev = find_rdev_all(startdev); + if (!start_rdev) { + MD_BUG(); + goto abort; + } + if (start_rdev->faulty) { + printk("can not autostart based on faulty %s!\n", + partition_name(startdev)); + goto abort; + } + md_list_add(&start_rdev->pending, &pending_raid_disks); + + sb = start_rdev->sb; + + err = detect_old_array(sb); + if (err) { + printk("array version is too old to be autostarted, use raidtools 0.90 mkraid --upgrade\nto upgrade the array without data loss!\n"); + goto abort; + } + + for (i = 0; i < MD_SB_DISKS; i++) { + mdp_disk_t *desc; + kdev_t dev; + + desc = sb->disks + i; + dev = MKDEV(desc->major, desc->minor); + + if (dev == MKDEV(0,0)) + continue; + if (dev == startdev) + continue; + if (md_import_device(dev, 1)) { + printk("could not import %s, trying to run array nevertheless.\n", partition_name(dev)); + continue; + } + rdev = find_rdev_all(dev); + if (!rdev) { + MD_BUG(); + goto abort; + } + md_list_add(&rdev->pending, &pending_raid_disks); + } + + /* + * possibly return codes + */ + autorun_devices(); + return 0; + +abort: + if (start_rdev) + export_rdev(start_rdev); + return err; +} + +#undef BAD_VERSION +#undef OUT_OF_MEM +#undef NO_DEVICE +#undef AUTOADD_FAILED_USED +#undef AUTOADD_FAILED +#undef AUTORUN_FAILED +#undef AUTOADDING +#undef AUTORUNNING + +struct { + int set; + int noautodetect; + +} raid_setup_args md__initdata = { 0, 0 }; + +/* + * Searches all registered partitions for autorun RAID arrays + * at boot time. + */ +md__initfunc(void autodetect_raid(void)) +{ +#ifdef CONFIG_AUTODETECT_RAID + struct gendisk *disk; + mdk_rdev_t *rdev; + int i; + + if (raid_setup_args.noautodetect) { + printk(KERN_INFO "skipping autodetection of RAID arrays\n"); + return; + } + printk(KERN_INFO "autodetecting RAID arrays\n"); + + for (disk = gendisk_head ; disk ; disk = disk->next) { + for (i = 0; i < disk->max_p*disk->max_nr; i++) { + kdev_t dev = MKDEV(disk->major,i); + + if (disk->part[i].type == LINUX_OLD_RAID_PARTITION) { + printk(KERN_ALERT +"md: %s's partition type has to be changed from type 0x86 to type 0xfd\n" +" to maintain interoperability with other OSs! Autodetection support for\n" +" type 0x86 will be deleted after some migration timeout. Sorry.\n", + partition_name(dev)); + disk->part[i].type = LINUX_RAID_PARTITION; + } + if (disk->part[i].type != LINUX_RAID_PARTITION) + continue; + + if (md_import_device(dev,1)) { + printk(KERN_ALERT "could not import %s!\n", + partition_name(dev)); + continue; + } + /* + * Sanity checks: + */ + rdev = find_rdev_all(dev); + if (!rdev) { + MD_BUG(); + continue; + } + if (rdev->faulty) { + MD_BUG(); + continue; + } + md_list_add(&rdev->pending, &pending_raid_disks); + } + } + + autorun_devices(); +#endif +} + +static int get_version (void * arg) +{ + mdu_version_t ver; + + ver.major = MD_MAJOR_VERSION; + ver.minor = MD_MINOR_VERSION; + ver.patchlevel = MD_PATCHLEVEL_VERSION; + + if (md_copy_to_user(arg, &ver, sizeof(ver))) + return -EFAULT; + + return 0; +} + +#define SET_FROM_SB(x) info.x = mddev->sb->x +static int get_array_info (mddev_t * mddev, void * arg) +{ + mdu_array_info_t info; + + if (!mddev->sb) + return -EINVAL; + + SET_FROM_SB(major_version); + SET_FROM_SB(minor_version); + SET_FROM_SB(patch_version); + SET_FROM_SB(ctime); + SET_FROM_SB(level); + SET_FROM_SB(size); + SET_FROM_SB(nr_disks); + SET_FROM_SB(raid_disks); + SET_FROM_SB(md_minor); + SET_FROM_SB(not_persistent); + + SET_FROM_SB(utime); + SET_FROM_SB(state); + SET_FROM_SB(active_disks); + SET_FROM_SB(working_disks); + SET_FROM_SB(failed_disks); + SET_FROM_SB(spare_disks); + + SET_FROM_SB(layout); + SET_FROM_SB(chunk_size); + + if (md_copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} +#undef SET_FROM_SB + +#define SET_FROM_SB(x) info.x = mddev->sb->disks[nr].x +static int get_disk_info (mddev_t * mddev, void * arg) +{ + mdu_disk_info_t info; + unsigned int nr; + + if (!mddev->sb) + return -EINVAL; + + if (md_copy_from_user(&info, arg, sizeof(info))) + return -EFAULT; + + nr = info.number; + if (nr >= mddev->sb->nr_disks) + return -EINVAL; + + SET_FROM_SB(major); + SET_FROM_SB(minor); + SET_FROM_SB(raid_disk); + SET_FROM_SB(state); + + if (md_copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} +#undef SET_FROM_SB + +#define SET_SB(x) mddev->sb->disks[nr].x = info.x + +static int add_new_disk (mddev_t * mddev, void * arg) +{ + int err, size, persistent; + mdu_disk_info_t info; + mdk_rdev_t *rdev; + unsigned int nr; + kdev_t dev; + + if (!mddev->sb) + return -EINVAL; + + if (md_copy_from_user(&info, arg, sizeof(info))) + return -EFAULT; + + nr = info.number; + if (nr >= mddev->sb->nr_disks) + return -EINVAL; + + dev = MKDEV(info.major,info.minor); + + if (find_rdev_all(dev)) { + printk("device %s already used in a RAID array!\n", + partition_name(dev)); + return -EBUSY; + } + + SET_SB(number); + SET_SB(major); + SET_SB(minor); + SET_SB(raid_disk); + SET_SB(state); + + if ((info.state & (1<old_dev = dev; + rdev->desc_nr = info.number; + + bind_rdev_to_array(rdev, mddev); + + persistent = !mddev->sb->not_persistent; + if (!persistent) + printk("nonpersistent superblock ...\n"); + if (!mddev->sb->chunk_size) + printk("no chunksize?\n"); + + size = calc_dev_size(dev, mddev, persistent); + rdev->sb_offset = calc_dev_sboffset(dev, mddev, persistent); + + if (!mddev->sb->size || (mddev->sb->size > size)) + mddev->sb->size = size; + } + + /* + * sync all other superblocks with the main superblock + */ + sync_sbs(mddev); + + return 0; +} +#undef SET_SB + +static int hot_remove_disk (mddev_t * mddev, kdev_t dev) +{ + int err; + mdk_rdev_t *rdev; + mdp_disk_t *disk; + + if (!mddev->pers) + return -ENODEV; + + printk("trying to remove %s from md%d ... \n", + partition_name(dev), mdidx(mddev)); + + if (!mddev->pers->diskop) { + printk("md%d: personality does not support diskops!\n", + mdidx(mddev)); + return -EINVAL; + } + + rdev = find_rdev(mddev, dev); + if (!rdev) + return -ENXIO; + + if (rdev->desc_nr == -1) { + MD_BUG(); + return -EINVAL; + } + disk = &mddev->sb->disks[rdev->desc_nr]; + if (disk_active(disk)) + goto busy; + if (disk_removed(disk)) { + MD_BUG(); + return -EINVAL; + } + + err = mddev->pers->diskop(mddev, &disk, DISKOP_HOT_REMOVE_DISK); + if (err == -EBUSY) + goto busy; + if (err) { + MD_BUG(); + return -EINVAL; + } + + remove_descriptor(disk, mddev->sb); + kick_rdev_from_array(rdev); + mddev->sb_dirty = 1; + md_update_sb(mddev); + + return 0; +busy: + printk("cannot remove active disk %s from md%d ... \n", + partition_name(dev), mdidx(mddev)); + return -EBUSY; +} + +static int hot_add_disk (mddev_t * mddev, kdev_t dev) +{ + int i, err, persistent; + unsigned int size; + mdk_rdev_t *rdev; + mdp_disk_t *disk; + + if (!mddev->pers) + return -ENODEV; + + printk("trying to hot-add %s to md%d ... \n", + partition_name(dev), mdidx(mddev)); + + if (!mddev->pers->diskop) { + printk("md%d: personality does not support diskops!\n", + mdidx(mddev)); + return -EINVAL; + } + + persistent = !mddev->sb->not_persistent; + size = calc_dev_size(dev, mddev, persistent); + + if (size < mddev->sb->size) { + printk("md%d: disk size %d blocks < array size %d\n", + mdidx(mddev), size, mddev->sb->size); + return -ENOSPC; + } + + rdev = find_rdev(mddev, dev); + if (rdev) + return -EBUSY; + + err = md_import_device (dev, 0); + if (err) { + printk("md: error, md_import_device() returned %d\n", err); + return -EINVAL; + } + rdev = find_rdev_all(dev); + if (!rdev) { + MD_BUG(); + return -EINVAL; + } + if (rdev->faulty) { + printk("md: can not hot-add faulty %s disk to md%d!\n", + partition_name(dev), mdidx(mddev)); + err = -EINVAL; + goto abort_export; + } + bind_rdev_to_array(rdev, mddev); + + /* + * The rest should better be atomic, we can have disk failures + * noticed in interrupt contexts ... + */ + cli(); + rdev->old_dev = dev; + rdev->size = size; + rdev->sb_offset = calc_dev_sboffset(dev, mddev, persistent); + + disk = mddev->sb->disks + mddev->sb->raid_disks; + for (i = mddev->sb->raid_disks; i < MD_SB_DISKS; i++) { + disk = mddev->sb->disks + i; + + if (!disk->major && !disk->minor) + break; + if (disk_removed(disk)) + break; + } + if (i == MD_SB_DISKS) { + sti(); + printk("md%d: can not hot-add to full array!\n", mdidx(mddev)); + err = -EBUSY; + goto abort_unbind_export; + } + + if (disk_removed(disk)) { /* - * hot_add has to bump up nb_dev itself + * reuse slot */ - if (md_dev[minor].pers->hot_add_disk (&md_dev[minor], dev)) { - /* - * FIXME: here we should free up the inode and stuff - */ - printk ("FIXME\n"); - return -EINVAL; + if (disk->number != i) { + sti(); + MD_BUG(); + err = -EINVAL; + goto abort_unbind_export; } - } else - md_dev[minor].nb_dev++; + } else { + disk->number = i; + } - printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor); - return (0); + disk->raid_disk = disk->number; + disk->major = MAJOR(dev); + disk->minor = MINOR(dev); + + if (mddev->pers->diskop(mddev, &disk, DISKOP_HOT_ADD_DISK)) { + sti(); + MD_BUG(); + err = -EINVAL; + goto abort_unbind_export; + } + + mark_disk_spare(disk); + mddev->sb->nr_disks++; + mddev->sb->spare_disks++; + mddev->sb->working_disks++; + + mddev->sb_dirty = 1; + + sti(); + md_update_sb(mddev); + + /* + * Kick recovery, maybe this spare has to be added to the + * array immediately. + */ + md_recover_arrays(); + + return 0; + +abort_unbind_export: + unbind_rdev_from_array(rdev); + +abort_export: + export_rdev(rdev); + return err; +} + +#define SET_SB(x) mddev->sb->x = info.x +static int set_array_info (mddev_t * mddev, void * arg) +{ + mdu_array_info_t info; + + if (mddev->sb) { + printk("array md%d already has a superblock!\n", + mdidx(mddev)); + return -EBUSY; + } + + if (md_copy_from_user(&info, arg, sizeof(info))) + return -EFAULT; + + if (alloc_array_sb(mddev)) + return -ENOMEM; + + mddev->sb->major_version = MD_MAJOR_VERSION; + mddev->sb->minor_version = MD_MINOR_VERSION; + mddev->sb->patch_version = MD_PATCHLEVEL_VERSION; + mddev->sb->ctime = CURRENT_TIME; + + SET_SB(level); + SET_SB(size); + SET_SB(nr_disks); + SET_SB(raid_disks); + SET_SB(md_minor); + SET_SB(not_persistent); + + SET_SB(state); + SET_SB(active_disks); + SET_SB(working_disks); + SET_SB(failed_disks); + SET_SB(spare_disks); + + SET_SB(layout); + SET_SB(chunk_size); + + mddev->sb->md_magic = MD_SB_MAGIC; + + /* + * Generate a 128 bit UUID + */ + get_random_bytes(&mddev->sb->set_uuid0, 4); + get_random_bytes(&mddev->sb->set_uuid1, 4); + get_random_bytes(&mddev->sb->set_uuid2, 4); + get_random_bytes(&mddev->sb->set_uuid3, 4); + + return 0; +} +#undef SET_SB + +static int set_disk_info (mddev_t * mddev, void * arg) +{ + printk("not yet"); + return -EINVAL; +} + +static int clear_array (mddev_t * mddev) +{ + printk("not yet"); + return -EINVAL; +} + +static int write_raid_info (mddev_t * mddev) +{ + printk("not yet"); + return -EINVAL; +} + +static int protect_array (mddev_t * mddev) +{ + printk("not yet"); + return -EINVAL; +} + +static int unprotect_array (mddev_t * mddev) +{ + printk("not yet"); + return -EINVAL; } static int md_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int minor, err; - struct hd_geometry *loc = (struct hd_geometry *) arg; + unsigned int minor; + int err = 0; + struct hd_geometry *loc = (struct hd_geometry *) arg; + mddev_t *mddev = NULL; + kdev_t dev; - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; + if (!md_capable_admin()) + return -EACCES; - if (((minor=MINOR(inode->i_rdev)) & 0x80) && - (minor & 0x7f) < MAX_PERSONALITY && - pers[minor & 0x7f] && - pers[minor & 0x7f]->ioctl) - return (pers[minor & 0x7f]->ioctl (inode, file, cmd, arg)); - - if (minor >= MAX_MD_DEV) - return -EINVAL; + dev = inode->i_rdev; + minor = MINOR(dev); + if (minor >= MAX_MD_DEVS) + return -EINVAL; - switch (cmd) - { - case REGISTER_DEV: - return do_md_add (minor, to_kdev_t ((dev_t) arg)); + /* + * Commands dealing with the RAID driver but not any + * particular array: + */ + switch (cmd) + { + case RAID_VERSION: + err = get_version((void *)arg); + goto done; + + case PRINT_RAID_DEBUG: + err = 0; + md_print_devices(); + goto done_unlock; + + case BLKGETSIZE: /* Return device size */ + if (!arg) { + err = -EINVAL; + goto abort; + } + err = md_put_user(md_hd_struct[minor].nr_sects, + (long *) arg); + goto done; - case START_MD: - return do_md_run (minor, (int) arg); + case BLKFLSBUF: + fsync_dev(dev); + invalidate_buffers(dev); + goto done; - case STOP_MD: - return do_md_stop (minor, inode); - - case BLKGETSIZE: /* Return device size */ - if (!arg) return -EINVAL; - err = put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg); - if (err) - return err; - break; - - case BLKFLSBUF: - fsync_dev (inode->i_rdev); - invalidate_buffers (inode->i_rdev); - break; - - case BLKRASET: - if (arg > 0xff) - return -EINVAL; - read_ahead[MAJOR(inode->i_rdev)] = arg; - return 0; - - case BLKRAGET: - if (!arg) return -EINVAL; - err = put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg); - if (err) - return err; - break; - - /* We have a problem here : there is no easy way to give a CHS - virtual geometry. We currently pretend that we have a 2 heads - 4 sectors (with a BIG number of cylinders...). This drives dosfs - just mad... ;-) */ - - case HDIO_GETGEO: - if (!loc) return -EINVAL; - err = put_user (2, (char *) &loc->heads); - if (err) - return err; - err = put_user (4, (char *) &loc->sectors); - if (err) - return err; - err = put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders); - if (err) - return err; - err = put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect, - (long *) &loc->start); - if (err) - return err; - break; - - RO_IOCTLS(inode->i_rdev,arg); + case BLKRASET: + if (arg > 0xff) { + err = -EINVAL; + goto abort; + } + read_ahead[MAJOR(dev)] = arg; + goto done; - default: - return -EINVAL; - } + case BLKRAGET: + if (!arg) { + err = -EINVAL; + goto abort; + } + err = md_put_user (read_ahead[ + MAJOR(dev)], (long *) arg); + goto done; + default: + } + + /* + * Commands creating/starting a new array: + */ + + mddev = kdev_to_mddev(dev); + + switch (cmd) + { + case SET_ARRAY_INFO: + case START_ARRAY: + if (mddev) { + printk("array md%d already exists!\n", + mdidx(mddev)); + err = -EEXIST; + goto abort; + } + default: + } + + switch (cmd) + { + case SET_ARRAY_INFO: + mddev = alloc_mddev(dev); + if (!mddev) { + err = -ENOMEM; + goto abort; + } + /* + * alloc_mddev() should possibly self-lock. + */ + err = lock_mddev(mddev); + if (err) { + printk("ioctl, reason %d, cmd %d\n",err, cmd); + goto abort; + } + err = set_array_info(mddev, (void *)arg); + goto done_unlock; + + case START_ARRAY: + /* + * possibly make it lock the array ... + */ + err = autostart_array((kdev_t)arg); + if (err) { + printk("autostart %s failed!\n", + partition_name((kdev_t)arg)); + } + goto done; + + default: + } + + /* + * Commands querying/configuring an existing array: + */ + + if (!mddev) { + err = -ENODEV; + goto abort; + } + err = lock_mddev(mddev); + if (err) { + printk("ioctl lock interrupted, reason %d, cmd %d\n",err, cmd); + goto abort; + } + + /* + * Commands even a read-only array can execute: + */ + switch (cmd) + { + case GET_ARRAY_INFO: + err = get_array_info(mddev, (void *)arg); + goto done_unlock; + + case GET_DISK_INFO: + err = get_disk_info(mddev, (void *)arg); + goto done_unlock; + + case RESTART_ARRAY_RW: + err = restart_array(mddev); + goto done_unlock; + + case STOP_ARRAY: + err = do_md_stop (mddev, 0); + goto done_unlock; + + case STOP_ARRAY_RO: + err = do_md_stop (mddev, 1); + goto done_unlock; + + /* + * We have a problem here : there is no easy way to give a CHS + * virtual geometry. We currently pretend that we have a 2 heads + * 4 sectors (with a BIG number of cylinders...). This drives + * dosfs just mad... ;-) + */ + case HDIO_GETGEO: + if (!loc) { + err = -EINVAL; + goto abort_unlock; + } + err = md_put_user (2, (char *) &loc->heads); + if (err) + goto abort_unlock; + err = md_put_user (4, (char *) &loc->sectors); + if (err) + goto abort_unlock; + err = md_put_user (md_hd_struct[mdidx(mddev)].nr_sects/8, + (short *) &loc->cylinders); + if (err) + goto abort_unlock; + err = md_put_user (md_hd_struct[minor].start_sect, + (long *) &loc->start); + goto done_unlock; + } + + /* + * The remaining ioctls are changing the state of the + * superblock, so we do not allow read-only arrays + * here: + */ + if (mddev->ro) { + err = -EROFS; + goto abort_unlock; + } + + switch (cmd) + { + case CLEAR_ARRAY: + err = clear_array(mddev); + goto done_unlock; + + case ADD_NEW_DISK: + err = add_new_disk(mddev, (void *)arg); + goto done_unlock; + + case HOT_REMOVE_DISK: + err = hot_remove_disk(mddev, (kdev_t)arg); + goto done_unlock; + + case HOT_ADD_DISK: + err = hot_add_disk(mddev, (kdev_t)arg); + goto done_unlock; + + case SET_DISK_INFO: + err = set_disk_info(mddev, (void *)arg); + goto done_unlock; + + case WRITE_RAID_INFO: + err = write_raid_info(mddev); + goto done_unlock; + + case UNPROTECT_ARRAY: + err = unprotect_array(mddev); + goto done_unlock; + + case PROTECT_ARRAY: + err = protect_array(mddev); + goto done_unlock; + + case RUN_ARRAY: + { + mdu_param_t param; + + err = md_copy_from_user(¶m, (mdu_param_t *)arg, + sizeof(param)); + if (err) + goto abort_unlock; + + err = do_md_run (mddev); + /* + * we have to clean up the mess if + * the array cannot be run for some + * reason ... + */ + if (err) { + mddev->sb_dirty = 0; + do_md_stop (mddev, 0); + } + goto done_unlock; + } + + default: + printk(KERN_WARNING "%s(pid %d) used obsolete MD ioctl, upgrade your software to use new ictls.\n", current->comm, current->pid); + err = -EINVAL; + goto abort_unlock; + } + +done_unlock: +abort_unlock: + if (mddev) + unlock_mddev(mddev); + else + printk("huh11?\n"); - return (0); + return err; +done: + if (err) + printk("huh12?\n"); +abort: + return err; } + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0) + static int md_open (struct inode *inode, struct file *file) { - int minor=MINOR(inode->i_rdev); + /* + * Always succeed + */ + return (0); +} - md_dev[minor].busy++; - return (0); /* Always succeed */ +static void md_release (struct inode *inode, struct file *file) +{ + sync_dev(inode->i_rdev); } -static int md_release (struct inode *inode, struct file *file) +static int md_read (struct inode *inode, struct file *file, + char *buf, int count) +{ + mddev_t *mddev = kdev_to_mddev(MD_FILE_TO_INODE(file)->i_rdev); + + if (!mddev || !mddev->pers) + return -ENXIO; + + return block_read (inode, file, buf, count); +} + +static int md_write (struct inode *inode, struct file *file, + const char *buf, int count) +{ + mddev_t *mddev = kdev_to_mddev(MD_FILE_TO_INODE(file)->i_rdev); + + if (!mddev || !mddev->pers) + return -ENXIO; + + return block_write (inode, file, buf, count); +} + +static struct file_operations md_fops= { - int minor=MINOR(inode->i_rdev); + NULL, + md_read, + md_write, + NULL, + NULL, + md_ioctl, + NULL, + md_open, + md_release, + block_fsync +}; + +#else - sync_dev (inode->i_rdev); - md_dev[minor].busy--; - return 0; +static int md_open (struct inode *inode, struct file *file) +{ + /* + * Always succeed + */ + return (0); } +static int md_release (struct inode *inode, struct file *file) +{ + sync_dev(inode->i_rdev); + return 0; +} static ssize_t md_read (struct file *file, char *buf, size_t count, loff_t *ppos) { - int minor=MINOR(file->f_dentry->d_inode->i_rdev); + mddev_t *mddev = kdev_to_mddev(MD_FILE_TO_INODE(file)->i_rdev); - if (!md_dev[minor].pers) /* Check if device is being run */ - return -ENXIO; + if (!mddev || !mddev->pers) + return -ENXIO; - return block_read(file, buf, count, ppos); + return block_read(file, buf, count, ppos); } static ssize_t md_write (struct file *file, const char *buf, size_t count, loff_t *ppos) { - int minor=MINOR(file->f_dentry->d_inode->i_rdev); + mddev_t *mddev = kdev_to_mddev(MD_FILE_TO_INODE(file)->i_rdev); - if (!md_dev[minor].pers) /* Check if device is being run */ - return -ENXIO; + if (!mddev || !mddev->pers) + return -ENXIO; - return block_write(file, buf, count, ppos); + return block_write(file, buf, count, ppos); } static struct file_operations md_fops= { - NULL, - md_read, - md_write, - NULL, - NULL, - md_ioctl, - NULL, - md_open, - NULL, - md_release, - block_fsync + NULL, + md_read, + md_write, + NULL, + NULL, + md_ioctl, + NULL, + md_open, + NULL, + md_release, + block_fsync }; -int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size) +#endif + +int md_map (kdev_t dev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) { - if ((unsigned int) minor >= MAX_MD_DEV) - { - printk ("Bad md device %d\n", minor); - return (-1); - } - - if (!md_dev[minor].pers) - { - printk ("Oops ! md%d not running, giving up !\n", minor); - return (-1); - } + int err; + mddev_t *mddev = kdev_to_mddev(dev); + + if (!mddev || !mddev->pers) { + err = -ENXIO; + goto out; + } - return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size)); + err = mddev->pers->map(mddev, dev, rdev, rsector, size); +out: + return err; } -int md_make_request (int minor, int rw, struct buffer_head * bh) +int md_make_request (struct buffer_head * bh, int rw) { - if (md_dev [minor].pers->make_request) { - if (buffer_locked(bh)) - return 0; + int err; + mddev_t *mddev = kdev_to_mddev(bh->b_dev); + + if (!mddev || !mddev->pers) { + err = -ENXIO; + goto out; + } + + if (mddev->pers->make_request) { + if (buffer_locked(bh)) { + err = 0; + goto out; + } set_bit(BH_Lock, &bh->b_state); if (rw == WRITE || rw == WRITEA) { if (!buffer_dirty(bh)) { - bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); - return 0; + bh->b_end_io(bh, buffer_uptodate(bh)); + err = 0; + goto out; } } if (rw == READ || rw == READA) { if (buffer_uptodate(bh)) { - bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); - return 0; + bh->b_end_io(bh, buffer_uptodate(bh)); + err = 0; + goto out; } } - return (md_dev[minor].pers->make_request(md_dev+minor, rw, bh)); + err = mddev->pers->make_request(mddev, rw, bh); } else { make_request (MAJOR(bh->b_rdev), rw, bh); - return 0; + err = 0; } +out: + return err; } static void do_md_request (void) { - printk ("Got md request, not good..."); - return; + printk(KERN_ALERT "Got md request, not good..."); + return; +} + +int md_thread(void * arg) +{ + mdk_thread_t *thread = arg; + + md_lock_kernel(); + exit_mm(current); + exit_files(current); + exit_fs(current); + + /* + * Detach thread + */ + sys_setsid(); + sprintf(current->comm, thread->name); + md_init_signals(); + md_flush_signals(); + thread->tsk = current; + + /* + * md_thread is a 'system-thread', it's priority should be very + * high. We avoid resource deadlocks individually in each + * raid personality. (RAID5 does preallocation) We also use RR and + * the very same RT priority as kswapd, thus we will never get + * into a priority inversion deadlock. + * + * we definitely have to have equal or higher priority than + * bdflush, otherwise bdflush will deadlock if there are too + * many dirty RAID5 blocks. + */ + current->policy = SCHED_OTHER; + current->priority = 40; + + up(thread->sem); + + for (;;) { + cli(); + if (!test_bit(THREAD_WAKEUP, &thread->flags)) { + if (!thread->run) + break; + interruptible_sleep_on(&thread->wqueue); + } + sti(); + clear_bit(THREAD_WAKEUP, &thread->flags); + if (thread->run) { + thread->run(thread->data); + run_task_queue(&tq_disk); + } + if (md_signal_pending(current)) { + printk("%8s(%d) flushing signals.\n", current->comm, + current->pid); + md_flush_signals(); + } + } + sti(); + up(thread->sem); + return 0; } -void md_wakeup_thread(struct md_thread *thread) +void md_wakeup_thread(mdk_thread_t *thread) { set_bit(THREAD_WAKEUP, &thread->flags); wake_up(&thread->wqueue); } -struct md_thread *md_register_thread (void (*run) (void *), void *data) +mdk_thread_t *md_register_thread (void (*run) (void *), + void *data, const char *name) { - struct md_thread *thread = (struct md_thread *) - kmalloc(sizeof(struct md_thread), GFP_KERNEL); + mdk_thread_t *thread; int ret; struct semaphore sem = MUTEX_LOCKED; - if (!thread) return NULL; + thread = (mdk_thread_t *) kmalloc + (sizeof(mdk_thread_t), GFP_KERNEL); + if (!thread) + return NULL; - memset(thread, 0, sizeof(struct md_thread)); + memset(thread, 0, sizeof(mdk_thread_t)); init_waitqueue(&thread->wqueue); thread->sem = &sem; thread->run = run; thread->data = data; + thread->name = name; ret = kernel_thread(md_thread, thread, 0); if (ret < 0) { kfree(thread); @@ -836,270 +2997,405 @@ struct md_thread *md_register_thread (void (*run) (void *), void *data) return thread; } -void md_unregister_thread (struct md_thread *thread) +void md_interrupt_thread (mdk_thread_t *thread) +{ + if (!thread->tsk) { + MD_BUG(); + return; + } + printk("interrupting MD-thread pid %d\n", thread->tsk->pid); + send_sig(SIGKILL, thread->tsk, 1); +} + +void md_unregister_thread (mdk_thread_t *thread) { struct semaphore sem = MUTEX_LOCKED; thread->sem = &sem; thread->run = NULL; - if (thread->tsk) - printk("Killing md_thread %d %p %s\n", - thread->tsk->pid, thread->tsk, thread->tsk->comm); - else - printk("Aiee. md_thread has 0 tsk\n"); - send_sig(SIGKILL, thread->tsk, 1); - printk("downing on %p\n", &sem); + thread->name = NULL; + if (!thread->tsk) { + MD_BUG(); + return; + } + md_interrupt_thread(thread); down(&sem); } -#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) - -int md_thread(void * arg) +void md_recover_arrays (void) { - struct md_thread *thread = arg; - - lock_kernel(); - exit_mm(current); - exit_files(current); - exit_fs(current); - - current->session = 1; - current->pgrp = 1; - sprintf(current->comm, "md_thread"); - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - thread->tsk = current; - up(thread->sem); - - for (;;) { - cli(); - if (!test_bit(THREAD_WAKEUP, &thread->flags)) { - do { - spin_lock(¤t->sigmask_lock); - flush_signals(current); - spin_unlock(¤t->sigmask_lock); - interruptible_sleep_on(&thread->wqueue); - cli(); - if (test_bit(THREAD_WAKEUP, &thread->flags)) - break; - if (!thread->run) { - sti(); - up(thread->sem); - return 0; - } - } while (signal_pending(current)); - } - sti(); - clear_bit(THREAD_WAKEUP, &thread->flags); - if (thread->run) { - thread->run(thread->data); - run_task_queue(&tq_disk); - } + if (!md_recovery_thread) { + MD_BUG(); + return; } + md_wakeup_thread(md_recovery_thread); } -EXPORT_SYMBOL(md_size); -EXPORT_SYMBOL(md_maxreadahead); -EXPORT_SYMBOL(register_md_personality); -EXPORT_SYMBOL(unregister_md_personality); -EXPORT_SYMBOL(partition_name); -EXPORT_SYMBOL(md_dev); -EXPORT_SYMBOL(md_error); -EXPORT_SYMBOL(md_register_thread); -EXPORT_SYMBOL(md_unregister_thread); -EXPORT_SYMBOL(md_update_sb); -EXPORT_SYMBOL(md_map); -EXPORT_SYMBOL(md_wakeup_thread); -EXPORT_SYMBOL(md_do_sync); -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry proc_md = { - PROC_MD, 6, "mdstat", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations, -}; +int md_error (kdev_t dev, kdev_t rdev) +{ + mddev_t *mddev = kdev_to_mddev(dev); + mdk_rdev_t * rrdev; + int rc; + + if (!mddev) { + MD_BUG(); + return 0; + } + rrdev = find_rdev(mddev, rdev); + mark_rdev_faulty(rrdev); + /* + * if recovery was running, stop it now. + */ + if (mddev->pers->stop_resync) + mddev->pers->stop_resync(mddev); + if (mddev->pers->error_handler) { + rc = mddev->pers->error_handler(mddev, rdev); + md_recover_arrays(); + return rc; + } +#if 0 + /* + * Drop all buffers in the failed array. + * _not_. This is called from IRQ handlers ... + */ + invalidate_buffers(rdev); #endif + return 0; +} -static void md_geninit (struct gendisk *gdisk) +static int status_unused (char * page) { - int i; - - for(i=0;isame_set.next && !rdev->same_set.prev) { + /* + * The device is not yet used by any array. + */ + i++; + sz += sprintf(page + sz, "%s ", + partition_name(rdev->dev)); + } + } + if (!i) + sz += sprintf(page + sz, ""); + + sz += sprintf(page + sz, "\n"); + return sz; } -int md_error (kdev_t mddev, kdev_t rdev) + +static int status_resync (char * page, mddev_t * mddev) { - unsigned int minor = MINOR (mddev); - int rc; + int sz = 0; + unsigned int blocksize, max_blocks, resync, res, dt, tt, et; + + resync = mddev->curr_resync; + blocksize = blksize_size[MD_MAJOR][mdidx(mddev)]; + max_blocks = blk_size[MD_MAJOR][mdidx(mddev)] / (blocksize >> 10); + + /* + * Should not happen. + */ + if (!max_blocks) { + MD_BUG(); + return 0; + } + res = resync*100/max_blocks; + if (!mddev->recovery_running) + /* + * true resync + */ + sz += sprintf(page + sz, " resync=%u%%", res); + else + /* + * recovery ... + */ + sz += sprintf(page + sz, " recovery=%u%%", res); + + /* + * We do not want to overflow, so the order of operands and + * the * 100 / 100 trick are important. We do a +1 to be + * safe against division by zero. We only estimate anyway. + * + * dt: time until now + * tt: total time + * et: estimated finish time + */ + dt = ((jiffies - mddev->resync_start) / HZ); + tt = (dt * (max_blocks / (resync/100+1)))/100; + if (tt > dt) + et = tt - dt; + else + /* + * ignore rounding effects near finish time + */ + et = 0; + + sz += sprintf(page + sz, " finish=%u.%umin", et / 60, (et % 60)/6); - if (MAJOR(mddev) != MD_MAJOR || minor > MAX_MD_DEV) - panic ("md_error gets unknown device\n"); - if (!md_dev [minor].pers) - panic ("md_error gets an error for an unknown device\n"); - if (md_dev [minor].pers->error_handler) { - rc = md_dev [minor].pers->error_handler (md_dev+minor, rdev); -#if SUPPORT_RECONSTRUCTION - md_wakeup_thread(md_sync_thread); -#endif /* SUPPORT_RECONSTRUCTION */ - return rc; - } - return 0; + return sz; } int get_md_status (char *page) { - int sz=0, i, j, size; + int sz = 0, j, size; + struct md_list_head *tmp, *tmp2; + mdk_rdev_t *rdev; + mddev_t *mddev; - sz+=sprintf( page+sz, "Personalities : "); - for (i=0; iname); + sz += sprintf(page + sz, "Personalities : "); + for (j = 0; j < MAX_PERSONALITY; j++) + if (pers[j]) + sz += sprintf(page+sz, "[%s] ", pers[j]->name); - page[sz-1]='\n'; + sz += sprintf(page+sz, "\n"); - sz+=sprintf (page+sz, "read_ahead "); - if (read_ahead[MD_MAJOR]==INT_MAX) - sz+=sprintf (page+sz, "not set\n"); - else - sz+=sprintf (page+sz, "%d sectors\n", read_ahead[MD_MAJOR]); - - for (i=0; iname); + sz += sprintf(page+sz, "read_ahead "); + if (read_ahead[MD_MAJOR] == INT_MAX) + sz += sprintf(page+sz, "not set\n"); + else + sz += sprintf(page+sz, "%d sectors\n", read_ahead[MD_MAJOR]); + + ITERATE_MDDEV(mddev,tmp) { + sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev), + mddev->pers ? "" : "in"); + if (mddev->pers) { + if (mddev->ro) + sz += sprintf(page + sz, " (read-only)"); + sz += sprintf(page + sz, " %s", mddev->pers->name); + } - size=0; - for (j=0; jdev), rdev->desc_nr); + if (rdev->faulty) { + sz += sprintf(page + sz, "(F)"); + continue; + } + size += rdev->size; + } - if (md_dev[i].nb_dev) { - if (md_dev[i].pers) - sz+=sprintf (page+sz, " %d blocks", md_size[i]); - else - sz+=sprintf (page+sz, " %d blocks", size); - } + if (mddev->nb_dev) { + if (mddev->pers) + sz += sprintf(page + sz, " %d blocks", + md_size[mdidx(mddev)]); + else + sz += sprintf(page + sz, " %d blocks", size); + } - if (!md_dev[i].pers) - { - sz+=sprintf (page+sz, "\n"); - continue; - } + if (!mddev->pers) { + sz += sprintf(page+sz, "\n"); + continue; + } - if (md_dev[i].pers->max_invalid_dev) - sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i)); + sz += mddev->pers->status (page+sz, mddev); - sz+=md_dev[i].pers->status (page+sz, i, md_dev+i); - sz+=sprintf (page+sz, "\n"); - } + if (mddev->curr_resync) + sz += status_resync (page+sz, mddev); + else { + if (md_atomic_read(&mddev->resync_sem.count) != 1) + sz += sprintf(page + sz, " resync=DELAYED"); + } + sz += sprintf(page + sz, "\n"); + } + sz += status_unused (page + sz); - return (sz); + return (sz); } -int register_md_personality (int p_num, struct md_personality *p) +int register_md_personality (int pnum, mdk_personality_t *p) { - int i=(p_num >> PERSONALITY_SHIFT); - - if (i >= MAX_PERSONALITY) - return -EINVAL; + if (pnum >= MAX_PERSONALITY) + return -EINVAL; - if (pers[i]) - return -EBUSY; + if (pers[pnum]) + return -EBUSY; - pers[i]=p; - printk ("%s personality registered\n", p->name); - return 0; + pers[pnum] = p; + printk(KERN_INFO "%s personality registered\n", p->name); + return 0; } -int unregister_md_personality (int p_num) +int unregister_md_personality (int pnum) { - int i=(p_num >> PERSONALITY_SHIFT); - - if (i >= MAX_PERSONALITY) - return -EINVAL; + if (pnum >= MAX_PERSONALITY) + return -EINVAL; - printk ("%s personality unregistered\n", pers[i]->name); - pers[i]=NULL; - return 0; + printk(KERN_INFO "%s personality unregistered\n", pers[pnum]->name); + pers[pnum] = NULL; + return 0; } -static md_descriptor_t *get_spare(struct md_dev *mddev) +static mdp_disk_t *get_spare(mddev_t *mddev) { - int i; - md_superblock_t *sb = mddev->sb; - md_descriptor_t *descriptor; - struct real_dev *realdev; - - for (i = 0; i < mddev->nb_dev; i++) { - realdev = &mddev->devices[i]; - if (!realdev->sb) + mdp_super_t *sb = mddev->sb; + mdp_disk_t *disk; + mdk_rdev_t *rdev; + struct md_list_head *tmp; + + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->faulty) + continue; + if (!rdev->sb) { + MD_BUG(); continue; - descriptor = &sb->disks[realdev->sb->descriptor.number]; - if (descriptor->state & (1 << MD_FAULTY_DEVICE)) + } + disk = &sb->disks[rdev->desc_nr]; + if (disk_faulty(disk)) { + MD_BUG(); continue; - if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) + } + if (disk_active(disk)) continue; - return descriptor; + return disk; } return NULL; } +static int is_mddev_idle (mddev_t *mddev) +{ + mdk_rdev_t * rdev; + struct md_list_head *tmp; + int idle; + unsigned long curr_events; + + idle = 1; + ITERATE_RDEV(mddev,rdev,tmp) { + curr_events = io_events[MAJOR(rdev->dev)]; + + if (curr_events != rdev->last_events) { +// printk("!I(%d)", curr_events-rdev->last_events); + rdev->last_events = curr_events; + idle = 0; + } + } + return idle; +} + /* * parallel resyncing thread. - * - * FIXME: - make it abort with a dirty array on mdstop, now it just blocks - * - fix read error handing */ -int md_do_sync(struct md_dev *mddev) +/* + * Determine correct block size for this device. + */ +unsigned int device_bsize (kdev_t dev) +{ + unsigned int i, correct_size; + + correct_size = BLOCK_SIZE; + if (blksize_size[MAJOR(dev)]) { + i = blksize_size[MAJOR(dev)][MINOR(dev)]; + if (i) + correct_size = i; + } + + return correct_size; +} + +static struct wait_queue *resync_wait = (struct wait_queue *)NULL; + +#define RA_ORDER (1) +#define RA_PAGE_SIZE (PAGE_SIZE*(1<resync_sem); + if (err) + goto out_nolock; + +recheck: + serialize = 0; + ITERATE_MDDEV(mddev2,tmp) { + if (mddev2 == mddev) + continue; + if (mddev2->curr_resync && match_mddev_units(mddev,mddev2)) { + printk(KERN_INFO "md: serializing resync, md%d has overlapping physical units with md%d!\n", mdidx(mddev), mdidx(mddev2)); + serialize = 1; + break; + } + } + if (serialize) { + interruptible_sleep_on(&resync_wait); + if (md_signal_pending(current)) { + md_flush_signals(); + err = -EINTR; + goto out; + } + goto recheck; + } - blocksize = blksize_size[major][minor]; + mddev->curr_resync = 1; + + blocksize = device_bsize(read_disk); max_blocks = blk_size[major][minor] / (blocksize >> 10); - printk("... resync log\n"); - printk(" .... mddev->nb_dev: %d\n", mddev->nb_dev); - printk(" .... raid array: %s\n", kdevname(read_disk)); - printk(" .... max_blocks: %d blocksize: %d\n", max_blocks, blocksize); - printk("md: syncing RAID array %s\n", kdevname(read_disk)); + printk(KERN_INFO "md: syncing RAID array md%d\n", mdidx(mddev)); + printk(KERN_INFO "md: minimum _guaranteed_ reconstruction speed: %d KB/sec.\n", + sysctl_speed_limit); + printk(KERN_INFO "md: using maximum available idle IO bandwith for reconstruction.\n"); + + /* + * Resync has low priority. + */ + current->priority = 1; + + is_mddev_idle(mddev); /* this also initializes IO event counters */ + starttime = jiffies; + mddev->resync_start = starttime; - mddev->busy++; + /* + * Tune reconstruction: + */ + window = md_maxreadahead[mdidx(mddev)]/1024; + nr_blocks = window / (blocksize >> 10); + if (!nr_blocks || (nr_blocks > MAX_NR_BLOCKS)) + nr_blocks = MAX_NR_BLOCKS; + printk(KERN_INFO "md: using %dk window.\n",window); - starttime=jiffies; - for (j = 0; j < max_blocks; j++) { + for (j = 0; j < max_blocks; j += nr_blocks) { + if (j) + mddev->curr_resync = j; /* * B careful. When some1 mounts a non-'blocksize' filesystem * then we get the blocksize changed right under us. Go deal * with it transparently, recalculate 'blocksize', 'j' and * 'max_blocks': */ - curr_bsize = blksize_size[major][minor]; + curr_bsize = device_bsize(read_disk); if (curr_bsize != blocksize) { - diff_blocksize: + printk(KERN_INFO "md%d: blocksize changed\n", + mdidx(mddev)); +retry_read: if (curr_bsize > blocksize) /* * this is safe, rounds downwards. @@ -1109,114 +3405,384 @@ int md_do_sync(struct md_dev *mddev) j *= blocksize/curr_bsize; blocksize = curr_bsize; + nr_blocks = window / (blocksize >> 10); + if (!nr_blocks || (nr_blocks > MAX_NR_BLOCKS)) + nr_blocks = MAX_NR_BLOCKS; max_blocks = blk_size[major][minor] / (blocksize >> 10); - } - if ((bh = breada (read_disk, j, blocksize, j * blocksize, - max_blocks * blocksize)) != NULL) { - mark_buffer_dirty(bh, 1); - brelse(bh); - } else { + printk("nr_blocks changed to %d (blocksize %d, j %d, max_blocks %d)\n", + nr_blocks, blocksize, j, max_blocks); /* - * FIXME: Ugly, but set_blocksize() isnt safe ... + * We will retry the current block-group */ - curr_bsize = blksize_size[major][minor]; - if (curr_bsize != blocksize) - goto diff_blocksize; + } - /* - * It's a real read problem. FIXME, handle this - * a better way. - */ - printk ( KERN_ALERT - "read error, stopping reconstruction.\n"); - mddev->busy--; - return 1; + /* + * Cleanup routines expect this + */ + for (k = 0; k < nr_blocks; k++) + bh[k] = NULL; + + chunk = nr_blocks; + if (chunk > max_blocks-j) + chunk = max_blocks-j; + + /* + * request buffer heads ... + */ + for (i = 0; i < chunk; i++) { + bh[i] = getblk (read_disk, j+i, blocksize); + if (!bh[i]) + goto read_error; + if (!buffer_dirty(bh[i])) + mark_buffer_lowprio(bh[i]); } /* - * Let's sleep some if we are faster than our speed limit: + * read buffer heads ... */ - while (blocksize*j/(jiffies-starttime+1)*HZ/1024 > SPEED_LIMIT) - { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + ll_rw_block (READ, chunk, bh); + run_task_queue(&tq_disk); + + /* + * verify that all of them are OK ... + */ + for (i = 0; i < chunk; i++) { + ii = chunk-i-1; + wait_on_buffer(bh[ii]); + if (!buffer_uptodate(bh[ii])) + goto read_error; + } + +retry_write: + for (i = 0; i < chunk; i++) + mark_buffer_dirty_lowprio(bh[i]); + + ll_rw_block(WRITE, chunk, bh); + run_task_queue(&tq_disk); + + for (i = 0; i < chunk; i++) { + ii = chunk-i-1; + wait_on_buffer(bh[ii]); + + if (spare && disk_faulty(spare)) { + for (k = 0; k < chunk; k++) + brelse(bh[k]); + printk(" \n "); + err = -EIO; + goto out; + } + + if (!buffer_uptodate(bh[ii])) { + curr_bsize = device_bsize(read_disk); + if (curr_bsize != blocksize) { + printk(KERN_INFO + "md%d: blocksize changed during write\n", + mdidx(mddev)); + for (k = 0; k < chunk; k++) + if (bh[k]) { + if (buffer_lowprio(bh[k])) + mark_buffer_clean(bh[k]); + brelse(bh[k]); + } + goto retry_read; + } + printk(" BAD WRITE %8d>\n", j); + /* + * Ouch, write error, retry or bail out. + */ + if (max_write_errors) { + max_write_errors--; + printk ( KERN_WARNING "md%d: write error while reconstructing, at block %u(%d).\n", mdidx(mddev), j, blocksize); + goto retry_write; + } + printk ( KERN_ALERT + "too many write errors, stopping reconstruction.\n"); + for (k = 0; k < chunk; k++) + if (bh[k]) { + if (buffer_lowprio(bh[k])) + mark_buffer_clean(bh[k]); + brelse(bh[k]); + } + err = -EIO; + goto out; + } } /* - * FIXME: put this status bar thing into /proc + * This is the normal 'everything went OK' case + * do a 'free-behind' logic, we sure dont need + * this buffer if it was the only user. */ - if (!(j%(max_blocks/100))) { - if (!(percent%10)) - printk (" %03d%% done.\n",percent); - else - printk ("."); - percent++; + for (i = 0; i < chunk; i++) + cache_drop_behind(bh[i]); + + + if (md_signal_pending(current)) { + /* + * got a signal, exit. + */ + mddev->curr_resync = 0; + printk("md_do_sync() got signal ... exiting\n"); + md_flush_signals(); + err = -EINTR; + goto out; } + + /* + * this loop exits only if either when we are slower than + * the 'hard' speed limit, or the system was IO-idle for + * a jiffy. + * the system might be non-idle CPU-wise, but we only care + * about not overloading the IO subsystem. (things like an + * e2fsck being done on the RAID array should execute fast) + */ +repeat: + if (md_need_resched(current)) + schedule(); + + if ((blocksize/1024)*j/((jiffies-starttime)/HZ + 1) + 1 + > sysctl_speed_limit) { + current->priority = 1; + + if (!is_mddev_idle(mddev)) { + current->state = TASK_INTERRUPTIBLE; + md_schedule_timeout(HZ/2); + if (!md_signal_pending(current)) + goto repeat; + } + } else + current->priority = 40; } fsync_dev(read_disk); - printk("md: %s: sync done.\n", kdevname(read_disk)); - mddev->busy--; - return 0; + printk(KERN_INFO "md: md%d: sync done.\n",mdidx(mddev)); + err = 0; + /* + * this also signals 'finished resyncing' to md_stop + */ +out: + up(&mddev->resync_sem); +out_nolock: + free_pages((unsigned long)bh, RA_ORDER); + mddev->curr_resync = 0; + wake_up(&resync_wait); + return err; + +read_error: + /* + * set_blocksize() might change the blocksize. This + * should not happen often, but it happens when eg. + * someone mounts a filesystem that has non-1k + * blocksize. set_blocksize() doesnt touch our + * buffer, but to avoid aliasing problems we change + * our internal blocksize too and retry the read. + */ + curr_bsize = device_bsize(read_disk); + if (curr_bsize != blocksize) { + printk(KERN_INFO "md%d: blocksize changed during read\n", + mdidx(mddev)); + for (k = 0; k < chunk; k++) + if (bh[k]) { + if (buffer_lowprio(bh[k])) + mark_buffer_clean(bh[k]); + brelse(bh[k]); + } + goto retry_read; + } + + /* + * It's a real read problem. We retry and bail out + * only if it's excessive. + */ + if (max_read_errors) { + max_read_errors--; + printk ( KERN_WARNING "md%d: read error while reconstructing, at block %u(%d).\n", mdidx(mddev), j, blocksize); + for (k = 0; k < chunk; k++) + if (bh[k]) { + if (buffer_lowprio(bh[k])) + mark_buffer_clean(bh[k]); + brelse(bh[k]); + } + goto retry_read; + } + printk ( KERN_ALERT "too many read errors, stopping reconstruction.\n"); + for (k = 0; k < chunk; k++) + if (bh[k]) { + if (buffer_lowprio(bh[k])) + mark_buffer_clean(bh[k]); + brelse(bh[k]); + } + err = -EIO; + goto out; } +#undef MAX_NR_BLOCKS + /* - * This is a kernel thread which: syncs a spare disk with the active array + * This is a kernel thread which syncs a spare disk with the active array * * the amount of foolproofing might seem to be a tad excessive, but an * early (not so error-safe) version of raid1syncd synced the first 0.5 gigs * of my root partition with the first 0.5 gigs of my /home partition ... so * i'm a bit nervous ;) */ -void mdsyncd (void *data) +void md_do_recovery (void *data) { - int i; - struct md_dev *mddev; - md_superblock_t *sb; - md_descriptor_t *spare; + int err; + mddev_t *mddev; + mdp_super_t *sb; + mdp_disk_t *spare; unsigned long flags; + struct md_list_head *tmp; - for (i = 0, mddev = md_dev; i < MAX_MD_DEV; i++, mddev++) { + printk(KERN_INFO "md: recovery thread got woken up ...\n"); +restart: + ITERATE_MDDEV(mddev,tmp) { if ((sb = mddev->sb) == NULL) continue; + if (mddev->recovery_running) + continue; if (sb->active_disks == sb->raid_disks) continue; - if (!sb->spare_disks) + if (!sb->spare_disks) { + printk(KERN_ERR "md%d: no spare disk to reconstruct array! -- continuing in degraded mode\n", mdidx(mddev)); continue; + } + /* + * now here we get the spare and resync it. + */ if ((spare = get_spare(mddev)) == NULL) continue; - if (!mddev->pers->mark_spare) - continue; - if (mddev->pers->mark_spare(mddev, spare, SPARE_WRITE)) + printk(KERN_INFO "md%d: resyncing spare disk %s to replace failed disk\n", mdidx(mddev), partition_name(MKDEV(spare->major,spare->minor))); + if (!mddev->pers->diskop) continue; - if (md_do_sync(mddev) || (spare->state & (1 << MD_FAULTY_DEVICE))) { - mddev->pers->mark_spare(mddev, spare, SPARE_INACTIVE); + if (mddev->pers->diskop(mddev, &spare, DISKOP_SPARE_WRITE)) continue; + down(&mddev->recovery_sem); + mddev->recovery_running = 1; + err = md_do_sync(mddev, spare); + if (err == -EIO) { + printk(KERN_INFO "md%d: spare disk %s failed, skipping to next spare.\n", mdidx(mddev), partition_name(MKDEV(spare->major,spare->minor))); + if (!disk_faulty(spare)) { + mddev->pers->diskop(mddev,&spare,DISKOP_SPARE_INACTIVE); + mark_disk_faulty(spare); + mark_disk_nonsync(spare); + mark_disk_inactive(spare); + sb->spare_disks--; + sb->working_disks--; + sb->failed_disks++; + } + } else + if (disk_faulty(spare)) + mddev->pers->diskop(mddev, &spare, + DISKOP_SPARE_INACTIVE); + if (err == -EINTR) { + /* + * Recovery got interrupted ... + * signal back that we have finished using the array. + */ + mddev->pers->diskop(mddev, &spare, + DISKOP_SPARE_INACTIVE); + up(&mddev->recovery_sem); + /* + * we keep 'recovery_running == 1', so we will not + * start a reconstruction next time around ... + * the stop code will set it to 0 explicitly. + */ + goto restart; + } else { + mddev->recovery_running = 0; + up(&mddev->recovery_sem); } save_flags(flags); cli(); - mddev->pers->mark_spare(mddev, spare, SPARE_ACTIVE); - spare->state |= (1 << MD_SYNC_DEVICE); - spare->state |= (1 << MD_ACTIVE_DEVICE); - sb->spare_disks--; - sb->active_disks++; - mddev->sb_dirty = 1; - md_update_sb(mddev - md_dev); + if (!disk_faulty(spare)) { + /* + * the SPARE_ACTIVE diskop possibly changes the + * pointer too + */ + mddev->pers->diskop(mddev, &spare, DISKOP_SPARE_ACTIVE); + mark_disk_sync(spare); + mark_disk_active(spare); + sb->active_disks++; + sb->spare_disks--; + } restore_flags(flags); + mddev->sb_dirty = 1; + md_update_sb(mddev); + goto restart; } + printk(KERN_INFO "md: recovery thread finished ...\n"); } +int md_notify_reboot(struct notifier_block *this, + unsigned long code, void *x) +{ + struct md_list_head *tmp; + mddev_t *mddev; + + if ((code == MD_SYS_DOWN) || (code == MD_SYS_HALT) + || (code == MD_SYS_POWER_OFF)) { + + printk(KERN_INFO "stopping all md devices.\n"); + + ITERATE_MDDEV(mddev,tmp) + do_md_stop (mddev, 1); + /* + * certain more exotic SCSI devices are known to be + * volatile wrt too early system reboots. While the + * right place to handle this issue is the given + * driver, we do want to have a safe RAID driver ... + */ + md_mdelay(1000*1); + } + return NOTIFY_DONE; +} + +struct notifier_block md_notifier = { + md_notify_reboot, + NULL, + 0 +}; + +md__initfunc(void raid_setup(char *str, int *ints)) +{ + char tmpline[100]; + int len, pos, nr, i; + + len = strlen(str) + 1; + nr = 0; + pos = 0; + + for (i = 0; i < len; i++) { + char c = str[i]; + + if (c == ',' || !c) { + tmpline[pos] = 0; + if (!strcmp(tmpline,"noautodetect")) + raid_setup_args.noautodetect = 1; + nr++; + pos = 0; + continue; + } + tmpline[pos] = c; + pos++; + } + raid_setup_args.set = 1; + return; +} + #ifdef CONFIG_MD_BOOT struct { int set; int ints[100]; char str[100]; -} md_setup_args __initdata = { +} md_setup_args md__initdata = { 0,{0},{0} }; /* called from init/main.c */ -__initfunc(void md_setup(char *str,int *ints)) +md__initfunc(void md_setup(char *str,int *ints)) { int i; for(i=0;i<=ints[0];i++) { @@ -1228,21 +3794,24 @@ __initfunc(void md_setup(char *str,int *ints)) return; } -__initfunc(void do_md_setup(char *str,int *ints)) +md__initfunc(void do_md_setup(char *str,int *ints)) { - int minor, pers, factor, fault; +#if 0 + int minor, pers, chunk_size, fault; kdev_t dev; int i=1; + printk("i plan to phase this out --mingo\n"); + if(ints[0] < 4) { - printk ("md: Too few Arguments (%d).\n", ints[0]); + printk (KERN_WARNING "md: Too few Arguments (%d).\n", ints[0]); return; } minor=ints[i++]; - if (minor >= MAX_MD_DEV) { - printk ("md: Minor device number too high.\n"); + if ((unsigned int)minor >= MAX_MD_DEVS) { + printk (KERN_WARNING "md: Minor device number too high.\n"); return; } @@ -1252,18 +3821,20 @@ __initfunc(void do_md_setup(char *str,int *ints)) case -1: #ifdef CONFIG_MD_LINEAR pers = LINEAR; - printk ("md: Setting up md%d as linear device.\n",minor); + printk (KERN_INFO "md: Setting up md%d as linear device.\n", + minor); #else - printk ("md: Linear mode not configured." + printk (KERN_WARNING "md: Linear mode not configured." "Recompile the kernel with linear mode enabled!\n"); #endif break; case 0: pers = STRIPED; #ifdef CONFIG_MD_STRIPED - printk ("md: Setting up md%d as a striped device.\n",minor); + printk (KERN_INFO "md: Setting up md%d as a striped device.\n", + minor); #else - printk ("md: Striped mode not configured." + printk (KERN_WARNING "md: Striped mode not configured." "Recompile the kernel with striped mode enabled!\n"); #endif break; @@ -1278,79 +3849,145 @@ __initfunc(void do_md_setup(char *str,int *ints)) break; */ default: - printk ("md: Unknown or not supported raid level %d.\n", ints[--i]); + printk (KERN_WARNING "md: Unknown or not supported raid level %d.\n", ints[--i]); return; } - if(pers) { + if (pers) { - factor=ints[i++]; /* Chunksize */ - fault =ints[i++]; /* Faultlevel */ + chunk_size = ints[i++]; /* Chunksize */ + fault = ints[i++]; /* Faultlevel */ - pers=pers | factor | (fault << FAULT_SHIFT); + pers = pers | chunk_size | (fault << FAULT_SHIFT); - while( str && (dev = name_to_kdev_t(str))) { - do_md_add (minor, dev); - if((str = strchr (str, ',')) != NULL) - str++; - } + while( str && (dev = name_to_kdev_t(str))) { + do_md_add (minor, dev); + if((str = strchr (str, ',')) != NULL) + str++; + } - do_md_run (minor, pers); - printk ("md: Loading md%d.\n",minor); + do_md_run (minor, pers); + printk (KERN_INFO "md: Loading md%d.\n",minor); } - +#endif } #endif +void hsm_init (void); +void translucent_init (void); void linear_init (void); void raid0_init (void); void raid1_init (void); void raid5_init (void); -__initfunc(int md_init (void)) +md__initfunc(int md_init (void)) { - printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n", - MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION, - MAX_MD_DEV, MAX_REAL); + static char * name = "mdrecoveryd"; + + printk (KERN_INFO "md driver %d.%d.%d MAX_MD_DEVS=%d, MAX_REAL=%d\n", + MD_MAJOR_VERSION, MD_MINOR_VERSION, + MD_PATCHLEVEL_VERSION, MAX_MD_DEVS, MAX_REAL); + + if (register_blkdev (MD_MAJOR, "md", &md_fops)) + { + printk (KERN_ALERT "Unable to get major %d for md\n", MD_MAJOR); + return (-1); + } - if (register_blkdev (MD_MAJOR, "md", &md_fops)) - { - printk ("Unable to get major %d for md\n", MD_MAJOR); - return (-1); - } + blk_dev[MD_MAJOR].request_fn = DEVICE_REQUEST; + blk_dev[MD_MAJOR].current_request = NULL; + read_ahead[MD_MAJOR] = INT_MAX; + md_gendisk.next = gendisk_head; - blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST; - blk_dev[MD_MAJOR].current_request=NULL; - read_ahead[MD_MAJOR]=INT_MAX; - memset(md_dev, 0, MAX_MD_DEV * sizeof (struct md_dev)); - md_gendisk.next=gendisk_head; + gendisk_head = &md_gendisk; - gendisk_head=&md_gendisk; + md_recovery_thread = md_register_thread(md_do_recovery, NULL, name); + if (!md_recovery_thread) + printk(KERN_ALERT "bug: couldn't allocate md_recovery_thread\n"); -#if SUPPORT_RECONSTRUCTION - if ((md_sync_thread = md_register_thread(mdsyncd, NULL)) == NULL) - printk("md: bug: md_sync_thread == NULL\n"); -#endif /* SUPPORT_RECONSTRUCTION */ + md_register_reboot_notifier(&md_notifier); + md_register_sysctl(); +#ifdef CONFIG_MD_HSM + hsm_init (); +#endif +#ifdef CONFIG_MD_TRANSLUCENT + translucent_init (); +#endif #ifdef CONFIG_MD_LINEAR - linear_init (); + linear_init (); #endif #ifdef CONFIG_MD_STRIPED - raid0_init (); + raid0_init (); #endif #ifdef CONFIG_MD_MIRRORING - raid1_init (); + raid1_init (); #endif #ifdef CONFIG_MD_RAID5 - raid5_init (); + raid5_init (); +#endif +#if defined(CONFIG_MD_RAID5) || defined(CONFIG_MD_RAID5_MODULE) + /* + * pick a XOR routine, runtime. + */ + calibrate_xor_block(); #endif - return (0); + + return (0); } #ifdef CONFIG_MD_BOOT -__initfunc(void md_setup_drive(void)) +md__initfunc(void md_setup_drive(void)) { if(md_setup_args.set) do_md_setup(md_setup_args.str, md_setup_args.ints); } #endif + +MD_EXPORT_SYMBOL(md_size); +MD_EXPORT_SYMBOL(register_md_personality); +MD_EXPORT_SYMBOL(unregister_md_personality); +MD_EXPORT_SYMBOL(partition_name); +MD_EXPORT_SYMBOL(md_error); +MD_EXPORT_SYMBOL(md_recover_arrays); +MD_EXPORT_SYMBOL(md_register_thread); +MD_EXPORT_SYMBOL(md_unregister_thread); +MD_EXPORT_SYMBOL(md_update_sb); +MD_EXPORT_SYMBOL(md_map); +MD_EXPORT_SYMBOL(md_wakeup_thread); +MD_EXPORT_SYMBOL(md_do_sync); +MD_EXPORT_SYMBOL(md_print_devices); +MD_EXPORT_SYMBOL(find_rdev_nr); +MD_EXPORT_SYMBOL(md_check_ordering); +MD_EXPORT_SYMBOL(md_interrupt_thread); +MD_EXPORT_SYMBOL(mddev_map); + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry proc_md = { + PROC_MD, 6, "mdstat", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations, +}; +#endif + +static void md_geninit (struct gendisk *gdisk) +{ + int i; + + for(i = 0; i < MAX_MD_DEVS; i++) { + md_blocksizes[i] = 1024; + md_maxreadahead[i] = MD_READAHEAD; + md_gendisk.part[i].start_sect = -1; /* avoid partition check */ + md_gendisk.part[i].nr_sects = 0; + } + + printk("md.c: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); + + blksize_size[MD_MAJOR] = md_blocksizes; + md_set_global_readahead(md_maxreadahead); + +#ifdef CONFIG_PROC_FS + proc_register(&proc_root, &proc_md); +#endif +} + diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c index 2e95d34f89b8..5272d7353e61 100644 --- a/drivers/block/raid0.c +++ b/drivers/block/raid0.c @@ -1,4 +1,3 @@ - /* raid0.c : Multiple Devices driver for Linux Copyright (C) 1994-96 Marc ZYNGIER @@ -18,146 +17,201 @@ */ #include -#include -#include -#include +#include #define MAJOR_NR MD_MAJOR #define MD_DRIVER #define MD_PERSONALITY -static int create_strip_zones (int minor, struct md_dev *mddev) +static int create_strip_zones (mddev_t *mddev) { - int i, j, c=0; - int current_offset=0; - struct real_dev *smallest_by_zone; - struct raid0_data *data=(struct raid0_data *) mddev->private; - - data->nr_strip_zones=1; - - for (i=1; inb_dev; i++) - { - for (j=0; jdevices[i].size==mddev->devices[j].size) - { - c=1; - break; - } - - if (!c) - data->nr_strip_zones++; - - c=0; - } - - if ((data->strip_zone=vmalloc(sizeof(struct strip_zone)*data->nr_strip_zones)) == NULL) - return 1; - - data->smallest=NULL; - - for (i=0; inr_strip_zones; i++) - { - data->strip_zone[i].dev_offset=current_offset; - smallest_by_zone=NULL; - c=0; - - for (j=0; jnb_dev; j++) - if (mddev->devices[j].size>current_offset) - { - data->strip_zone[i].dev[c++]=mddev->devices+j; - if (!smallest_by_zone || - smallest_by_zone->size > mddev->devices[j].size) - smallest_by_zone=mddev->devices+j; - } - - data->strip_zone[i].nb_dev=c; - data->strip_zone[i].size=(smallest_by_zone->size-current_offset)*c; - - if (!data->smallest || - data->smallest->size > data->strip_zone[i].size) - data->smallest=data->strip_zone+i; - - data->strip_zone[i].zone_offset=i ? (data->strip_zone[i-1].zone_offset+ - data->strip_zone[i-1].size) : 0; - current_offset=smallest_by_zone->size; - } - return 0; + int i, c, j, j1, j2; + int current_offset, curr_zone_offset; + raid0_conf_t *conf = mddev_to_conf(mddev); + mdk_rdev_t *smallest, *rdev1, *rdev2, *rdev; + + /* + * The number of 'same size groups' + */ + conf->nr_strip_zones = 0; + + ITERATE_RDEV_ORDERED(mddev,rdev1,j1) { + printk("raid0: looking at %s\n", partition_name(rdev1->dev)); + c = 0; + ITERATE_RDEV_ORDERED(mddev,rdev2,j2) { + printk("raid0: comparing %s(%d) with %s(%d)\n", partition_name(rdev1->dev), rdev1->size, partition_name(rdev2->dev), rdev2->size); + if (rdev2 == rdev1) { + printk("raid0: END\n"); + break; + } + if (rdev2->size == rdev1->size) + { + /* + * Not unique, dont count it as a new + * group + */ + printk("raid0: EQUAL\n"); + c = 1; + break; + } + printk("raid0: NOT EQUAL\n"); + } + if (!c) { + printk("raid0: ==> UNIQUE\n"); + conf->nr_strip_zones++; + printk("raid0: %d zones\n", conf->nr_strip_zones); + } + } + printk("raid0: FINAL %d zones\n", conf->nr_strip_zones); + + conf->strip_zone = vmalloc(sizeof(struct strip_zone)* + conf->nr_strip_zones); + if (!conf->strip_zone) + return 1; + + + conf->smallest = NULL; + current_offset = 0; + curr_zone_offset = 0; + + for (i = 0; i < conf->nr_strip_zones; i++) + { + struct strip_zone *zone = conf->strip_zone + i; + + printk("zone %d\n", i); + zone->dev_offset = current_offset; + smallest = NULL; + c = 0; + + ITERATE_RDEV_ORDERED(mddev,rdev,j) { + + printk(" checking %s ...", partition_name(rdev->dev)); + if (rdev->size > current_offset) + { + printk(" contained as device %d\n", c); + zone->dev[c] = rdev; + c++; + if (!smallest || (rdev->size size)) { + smallest = rdev; + printk(" (%d) is smallest!.\n", rdev->size); + } + } else + printk(" nope.\n"); + } + + zone->nb_dev = c; + zone->size = (smallest->size - current_offset) * c; + printk(" zone->nb_dev: %d, size: %d\n",zone->nb_dev,zone->size); + + if (!conf->smallest || (zone->size < conf->smallest->size)) + conf->smallest = zone; + + zone->zone_offset = curr_zone_offset; + curr_zone_offset += zone->size; + + current_offset = smallest->size; + printk("current zone offset: %d\n", current_offset); + } + printk("done.\n"); + return 0; } -static int raid0_run (int minor, struct md_dev *mddev) +static int raid0_run (mddev_t *mddev) { - int cur=0, i=0, size, zone0_size, nb_zone; - struct raid0_data *data; - - MOD_INC_USE_COUNT; - - if ((mddev->private=vmalloc (sizeof (struct raid0_data))) == NULL) return 1; - data=(struct raid0_data *) mddev->private; - - if (create_strip_zones (minor, mddev)) - { - vfree(data); - return 1; - } - - nb_zone=data->nr_zones= - md_size[minor]/data->smallest->size + - (md_size[minor]%data->smallest->size ? 1 : 0); - - printk ("raid0 : Allocating %ld bytes for hash.\n",(long)sizeof(struct raid0_hash)*nb_zone); - if ((data->hash_table=vmalloc (sizeof (struct raid0_hash)*nb_zone)) == NULL) - { - vfree(data->strip_zone); - vfree(data); - return 1; - } - size=data->strip_zone[cur].size; - - i=0; - while (curnr_strip_zones) - { - data->hash_table[i].zone0=data->strip_zone+cur; - - if (size>=data->smallest->size)/* If we completely fill the slot */ - { - data->hash_table[i++].zone1=NULL; - size-=data->smallest->size; - - if (!size) - { - if (++cur==data->nr_strip_zones) continue; - size=data->strip_zone[cur].size; - } - - continue; - } - - if (++cur==data->nr_strip_zones) /* Last dev, set unit1 as NULL */ - { - data->hash_table[i].zone1=NULL; - continue; - } - - zone0_size=size; /* Here, we use a 2nd dev to fill the slot */ - size=data->strip_zone[cur].size; - data->hash_table[i++].zone1=data->strip_zone+cur; - size-=(data->smallest->size - zone0_size); - } - - return (0); + int cur=0, i=0, size, zone0_size, nb_zone; + raid0_conf_t *conf; + + MOD_INC_USE_COUNT; + + conf = vmalloc(sizeof (raid0_conf_t)); + if (!conf) + goto out; + mddev->private = (void *)conf; + + if (md_check_ordering(mddev)) { + printk("raid0: disks are not ordered, aborting!\n"); + goto out_free_conf; + } + + if (create_strip_zones (mddev)) + goto out_free_conf; + + printk("raid0 : md_size is %d blocks.\n", md_size[mdidx(mddev)]); + printk("raid0 : conf->smallest->size is %d blocks.\n", conf->smallest->size); + nb_zone = md_size[mdidx(mddev)]/conf->smallest->size + + (md_size[mdidx(mddev)] % conf->smallest->size ? 1 : 0); + printk("raid0 : nb_zone is %d.\n", nb_zone); + conf->nr_zones = nb_zone; + + printk("raid0 : Allocating %d bytes for hash.\n", + sizeof(struct raid0_hash)*nb_zone); + + conf->hash_table = vmalloc (sizeof (struct raid0_hash)*nb_zone); + if (!conf->hash_table) + goto out_free_zone_conf; + size = conf->strip_zone[cur].size; + + i = 0; + while (cur < conf->nr_strip_zones) { + conf->hash_table[i].zone0 = conf->strip_zone + cur; + + /* + * If we completely fill the slot + */ + if (size >= conf->smallest->size) { + conf->hash_table[i++].zone1 = NULL; + size -= conf->smallest->size; + + if (!size) { + if (++cur == conf->nr_strip_zones) + continue; + size = conf->strip_zone[cur].size; + } + continue; + } + if (++cur == conf->nr_strip_zones) { + /* + * Last dev, set unit1 as NULL + */ + conf->hash_table[i].zone1=NULL; + continue; + } + + /* + * Here we use a 2nd dev to fill the slot + */ + zone0_size = size; + size = conf->strip_zone[cur].size; + conf->hash_table[i++].zone1 = conf->strip_zone + cur; + size -= (conf->smallest->size - zone0_size); + } + return 0; + +out_free_zone_conf: + vfree(conf->strip_zone); + conf->strip_zone = NULL; + +out_free_conf: + vfree(conf); + mddev->private = NULL; +out: + MOD_DEC_USE_COUNT; + return 1; } - -static int raid0_stop (int minor, struct md_dev *mddev) +static int raid0_stop (mddev_t *mddev) { - struct raid0_data *data=(struct raid0_data *) mddev->private; + raid0_conf_t *conf = mddev_to_conf(mddev); - vfree (data->hash_table); - vfree (data->strip_zone); - vfree (data); + vfree (conf->hash_table); + conf->hash_table = NULL; + vfree (conf->strip_zone); + conf->strip_zone = NULL; + vfree (conf); + mddev->private = NULL; - MOD_DEC_USE_COUNT; - return 0; + MOD_DEC_USE_COUNT; + return 0; } /* @@ -167,129 +221,135 @@ static int raid0_stop (int minor, struct md_dev *mddev) * Of course, those facts may not be valid anymore (and surely won't...) * Hey guys, there's some work out there ;-) */ -static int raid0_map (struct md_dev *mddev, kdev_t *rdev, +static int raid0_map (mddev_t *mddev, kdev_t dev, kdev_t *rdev, unsigned long *rsector, unsigned long size) { - struct raid0_data *data=(struct raid0_data *) mddev->private; - static struct raid0_hash *hash; - struct strip_zone *zone; - struct real_dev *tmp_dev; - int blk_in_chunk, factor, chunk, chunk_size; - long block, rblock; - - factor=FACTOR(mddev); - chunk_size=(1UL << FACTOR_SHIFT(factor)); - block=*rsector >> 1; - hash=data->hash_table+(block/data->smallest->size); - - /* Sanity check */ - if ((chunk_size*2)<(*rsector % (chunk_size*2))+size) - { - printk ("raid0_convert : can't convert block across chunks or bigger than %dk %ld %ld\n", chunk_size, *rsector, size); - return (-1); - } - - if (block >= (hash->zone0->size + - hash->zone0->zone_offset)) - { - if (!hash->zone1) - { - printk ("raid0_convert : hash->zone1==NULL for block %ld\n", block); - return (-1); - } - - zone=hash->zone1; - } - else - zone=hash->zone0; + raid0_conf_t *conf = mddev_to_conf(mddev); + struct raid0_hash *hash; + struct strip_zone *zone; + mdk_rdev_t *tmp_dev; + int blk_in_chunk, chunksize_bits, chunk, chunk_size; + long block, rblock; + + chunk_size = mddev->param.chunk_size >> 10; + chunksize_bits = ffz(~chunk_size); + block = *rsector >> 1; + hash = conf->hash_table + block / conf->smallest->size; + + /* Sanity check */ + if ((chunk_size * 2) < (*rsector % (chunk_size * 2)) + size) + goto bad_map; + + if (!hash) + goto bad_hash; + + if (!hash->zone0) + goto bad_zone0; + + if (block >= (hash->zone0->size + hash->zone0->zone_offset)) { + if (!hash->zone1) + goto bad_zone1; + zone = hash->zone1; + } else + zone = hash->zone0; - blk_in_chunk=block & (chunk_size -1); - chunk=(block - zone->zone_offset) / (zone->nb_dev<dev[(block >> FACTOR_SHIFT(factor)) % zone->nb_dev]; - rblock=(chunk << FACTOR_SHIFT(factor)) + blk_in_chunk + zone->dev_offset; + blk_in_chunk = block & (chunk_size -1); + chunk = (block - zone->zone_offset) / (zone->nb_dev << chunksize_bits); + tmp_dev = zone->dev[(block >> chunksize_bits) % zone->nb_dev]; + rblock = (chunk << chunksize_bits) + blk_in_chunk + zone->dev_offset; - *rdev=tmp_dev->dev; - *rsector=rblock<<1; - - return (0); + *rdev = tmp_dev->dev; + *rsector = rblock << 1; + + return 0; + +bad_map: + printk ("raid0_map bug: can't convert block across chunks or bigger than %dk %ld %ld\n", chunk_size, *rsector, size); + return -1; +bad_hash: + printk("raid0_map bug: hash==NULL for block %ld\n", block); + return -1; +bad_zone0: + printk ("raid0_map bug: hash->zone0==NULL for block %ld\n", block); + return -1; +bad_zone1: + printk ("raid0_map bug: hash->zone1==NULL for block %ld\n", block); + return -1; } -static int raid0_status (char *page, int minor, struct md_dev *mddev) +static int raid0_status (char *page, mddev_t *mddev) { - int sz=0; + int sz = 0; #undef MD_DEBUG #ifdef MD_DEBUG - int j, k; - struct raid0_data *data=(struct raid0_data *) mddev->private; + int j, k; + raid0_conf_t *conf = mddev_to_conf(mddev); - sz+=sprintf (page+sz, " "); - for (j=0; jnr_zones; j++) - { - sz+=sprintf (page+sz, "[z%d", - data->hash_table[j].zone0-data->strip_zone); - if (data->hash_table[j].zone1) - sz+=sprintf (page+sz, "/z%d] ", - data->hash_table[j].zone1-data->strip_zone); - else - sz+=sprintf (page+sz, "] "); - } + sz += sprintf(page + sz, " "); + for (j = 0; j < conf->nr_zones; j++) { + sz += sprintf(page + sz, "[z%d", + conf->hash_table[j].zone0 - conf->strip_zone); + if (conf->hash_table[j].zone1) + sz += sprintf(page+sz, "/z%d] ", + conf->hash_table[j].zone1 - conf->strip_zone); + else + sz += sprintf(page+sz, "] "); + } - sz+=sprintf (page+sz, "\n"); + sz += sprintf(page + sz, "\n"); - for (j=0; jnr_strip_zones; j++) - { - sz+=sprintf (page+sz, " z%d=[", j); - for (k=0; kstrip_zone[j].nb_dev; k++) - sz+=sprintf (page+sz, "%s/", - partition_name(data->strip_zone[j].dev[k]->dev)); - sz--; - sz+=sprintf (page+sz, "] zo=%d do=%d s=%d\n", - data->strip_zone[j].zone_offset, - data->strip_zone[j].dev_offset, - data->strip_zone[j].size); - } + for (j = 0; j < conf->nr_strip_zones; j++) { + sz += sprintf(page + sz, " z%d=[", j); + for (k = 0; k < conf->strip_zone[j].nb_dev; k++) + sz += sprintf (page+sz, "%s/", partition_name( + conf->strip_zone[j].dev[k]->dev)); + sz--; + sz += sprintf (page+sz, "] zo=%d do=%d s=%d\n", + conf->strip_zone[j].zone_offset, + conf->strip_zone[j].dev_offset, + conf->strip_zone[j].size); + } #endif - sz+=sprintf (page+sz, " %dk chunks", 1<param.chunk_size/1024); + return sz; } - -static struct md_personality raid0_personality= +static mdk_personality_t raid0_personality= { - "raid0", - raid0_map, - NULL, /* no special make_request */ - NULL, /* no special end_request */ - raid0_run, - raid0_stop, - raid0_status, - NULL, /* no ioctls */ - 0, - NULL, /* no error_handler */ - NULL, /* hot_add_disk */ - NULL, /* hot_remove_disk */ - NULL /* mark_spare */ + "raid0", + raid0_map, + NULL, /* no special make_request */ + NULL, /* no special end_request */ + raid0_run, + raid0_stop, + raid0_status, + NULL, /* no ioctls */ + 0, + NULL, /* no error_handler */ + NULL, /* no diskop */ + NULL, /* no stop resync */ + NULL /* no restart resync */ }; - #ifndef MODULE void raid0_init (void) { - register_md_personality (RAID0, &raid0_personality); + register_md_personality (RAID0, &raid0_personality); } #else int init_module (void) { - return (register_md_personality (RAID0, &raid0_personality)); + return (register_md_personality (RAID0, &raid0_personality)); } void cleanup_module (void) { - unregister_md_personality (RAID0); + unregister_md_personality (RAID0); } #endif + diff --git a/drivers/block/raid1.c b/drivers/block/raid1.c index 890584dcdd68..a7caea3b5282 100644 --- a/drivers/block/raid1.c +++ b/drivers/block/raid1.c @@ -1,6 +1,6 @@ -/************************************************************************ +/* * raid1.c : Multiple Devices driver for Linux - * Copyright (C) 1996 Ingo Molnar, Miguel de Icaza, Gadi Oxman + * Copyright (C) 1996, 1997, 1998 Ingo Molnar, Miguel de Icaza, Gadi Oxman * * RAID-1 management functions. * @@ -15,50 +15,52 @@ */ #include -#include #include -#include -#include -#include +#include #include #define MAJOR_NR MD_MAJOR #define MD_DRIVER #define MD_PERSONALITY -/* - * The following can be used to debug the driver - */ -/*#define RAID1_DEBUG*/ -#ifdef RAID1_DEBUG -#define PRINTK(x) do { printk x; } while (0); -#else -#define PRINTK(x) do { ; } while (0); -#endif +#define MAX_LINEAR_SECTORS 128 #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) -static struct md_personality raid1_personality; -static struct md_thread *raid1_thread = NULL; +static mdk_personality_t raid1_personality; struct buffer_head *raid1_retry_list = NULL; -static int __raid1_map (struct md_dev *mddev, kdev_t *rdev, +static void * raid1_kmalloc (int size) +{ + void * ptr; + /* + * now we are rather fault tolerant than nice, but + * there are a couple of places in the RAID code where we + * simply can not afford to fail an allocation because + * there is no failure return path (eg. make_request()) + */ + while (!(ptr = kmalloc (sizeof (raid1_conf_t), GFP_KERNEL))) + printk ("raid1: out of memory, retrying...\n"); + + memset(ptr, 0, size); + return ptr; +} + +static int __raid1_map (mddev_t *mddev, kdev_t *rdev, unsigned long *rsector, unsigned long size) { - struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; - int i, n = raid_conf->raid_disks; + raid1_conf_t *conf = mddev_to_conf(mddev); + int i, disks = MD_SB_DISKS; /* * Later we do read balancing on the read side * now we use the first available disk. */ - PRINTK(("raid1_map().\n")); - - for (i=0; imirrors[i].operational) { - *rdev = raid_conf->mirrors[i].dev; + for (i = 0; i < disks; i++) { + if (conf->mirrors[i].operational) { + *rdev = conf->mirrors[i].dev; return (0); } } @@ -67,29 +69,29 @@ static int __raid1_map (struct md_dev *mddev, kdev_t *rdev, return (-1); } -static int raid1_map (struct md_dev *mddev, kdev_t *rdev, +static int raid1_map (mddev_t *mddev, kdev_t dev, kdev_t *rdev, unsigned long *rsector, unsigned long size) { return 0; } -void raid1_reschedule_retry (struct buffer_head *bh) +static void raid1_reschedule_retry (struct buffer_head *bh) { struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id); - - PRINTK(("raid1_reschedule_retry().\n")); + mddev_t *mddev = r1_bh->mddev; + raid1_conf_t *conf = mddev_to_conf(mddev); r1_bh->next_retry = raid1_retry_list; raid1_retry_list = bh; - md_wakeup_thread(raid1_thread); + md_wakeup_thread(conf->thread); } /* - * raid1_end_buffer_io() is called when we have finished servicing a mirrored + * raid1_end_bh_io() is called when we have finished servicing a mirrored * operation and are ready to return a success/failure code to the buffer * cache layer. */ -static inline void raid1_end_buffer_io(struct raid1_bh *r1_bh, int uptodate) +static void raid1_end_bh_io (struct raid1_bh *r1_bh, int uptodate) { struct buffer_head *bh = r1_bh->master_bh; @@ -97,8 +99,6 @@ static inline void raid1_end_buffer_io(struct raid1_bh *r1_bh, int uptodate) kfree(r1_bh); } -int raid1_one_error=0; - void raid1_end_request (struct buffer_head *bh, int uptodate) { struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id); @@ -106,12 +106,7 @@ void raid1_end_request (struct buffer_head *bh, int uptodate) save_flags(flags); cli(); - PRINTK(("raid1_end_request().\n")); - if (raid1_one_error) { - raid1_one_error=0; - uptodate=0; - } /* * this branch is our 'one mirror IO has finished' event handler: */ @@ -136,15 +131,11 @@ void raid1_end_request (struct buffer_head *bh, int uptodate) */ if ( (r1_bh->cmd == READ) || (r1_bh->cmd == READA) ) { - - PRINTK(("raid1_end_request(), read branch.\n")); - /* * we have only one buffer_head on the read side */ if (uptodate) { - PRINTK(("raid1_end_request(), read branch, uptodate.\n")); - raid1_end_buffer_io(r1_bh, uptodate); + raid1_end_bh_io(r1_bh, uptodate); restore_flags(flags); return; } @@ -152,67 +143,56 @@ void raid1_end_request (struct buffer_head *bh, int uptodate) * oops, read error: */ printk(KERN_ERR "raid1: %s: rescheduling block %lu\n", - kdevname(bh->b_dev), bh->b_blocknr); - raid1_reschedule_retry (bh); + partition_name(bh->b_dev), bh->b_blocknr); + raid1_reschedule_retry(bh); restore_flags(flags); return; } /* - * WRITE or WRITEA. - */ - PRINTK(("raid1_end_request(), write branch.\n")); - - /* + * WRITE: + * * Let's see if all mirrored write operations have finished - * already [we have irqs off, so we can decrease]: + * already. */ - if (!--r1_bh->remaining) { - struct md_dev *mddev = r1_bh->mddev; - struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; - int i, n = raid_conf->raid_disks; - - PRINTK(("raid1_end_request(), remaining == 0.\n")); + if (atomic_dec_and_test(&r1_bh->remaining)) { + int i, disks = MD_SB_DISKS; - for ( i=0; imirror_bh[i]) kfree(r1_bh->mirror_bh[i]); + for ( i = 0; i < disks; i++) + if (r1_bh->mirror_bh[i]) + kfree(r1_bh->mirror_bh[i]); - raid1_end_buffer_io(r1_bh, test_bit(BH_Uptodate, &r1_bh->state)); + raid1_end_bh_io(r1_bh, test_bit(BH_Uptodate, &r1_bh->state)); } - else PRINTK(("raid1_end_request(), remaining == %u.\n", r1_bh->remaining)); restore_flags(flags); } -/* This routine checks if the undelying device is an md device and in that - * case it maps the blocks before putting the request on the queue +/* + * This routine checks if the undelying device is an md device + * and in that case it maps the blocks before putting the + * request on the queue */ -static inline void -map_and_make_request (int rw, struct buffer_head *bh) +static void map_and_make_request (int rw, struct buffer_head *bh) { if (MAJOR (bh->b_rdev) == MD_MAJOR) - md_map (MINOR (bh->b_rdev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9); + md_map (bh->b_rdev, &bh->b_rdev, + &bh->b_rsector, bh->b_size >> 9); clear_bit(BH_Lock, &bh->b_state); make_request (MAJOR (bh->b_rdev), rw, bh); } -static int -raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) +static int raid1_make_request (mddev_t *mddev, int rw, + struct buffer_head * bh) { - - struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + raid1_conf_t *conf = mddev_to_conf(mddev); struct buffer_head *mirror_bh[MD_SB_DISKS], *bh_req; struct raid1_bh * r1_bh; - int n = raid_conf->raid_disks, i, sum_bhs = 0, switch_disks = 0, sectors; + int disks = MD_SB_DISKS; + int i, sum_bhs = 0, switch_disks = 0, sectors, lowprio = 0; struct mirror_info *mirror; - PRINTK(("raid1_make_request().\n")); - - while (!( /* FIXME: now we are rather fault tolerant than nice */ - r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL) - ) ) - printk ("raid1_make_request(#1): out of memory\n"); - memset (r1_bh, 0, sizeof (struct raid1_bh)); + r1_bh = raid1_kmalloc (sizeof (struct raid1_bh)); /* * make_request() can abort the operation when READA or WRITEA are being @@ -223,43 +203,65 @@ raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) if (rw == READA) rw = READ; if (rw == WRITEA) rw = WRITE; - if (rw == WRITE || rw == WRITEA) - mark_buffer_clean(bh); /* Too early ? */ + if (rw == WRITE) { + /* + * Too early ? + */ + mark_buffer_clean(bh); + /* + * not too early. we _first_ clean the bh, then we start + * the IO, then when the IO has finished, we unlock the + * bh and mark it uptodate. This way we do not miss the + * case when the bh got dirty again during the IO. + */ + } + + /* + * special flag for 'lowprio' reconstruction requests ... + */ + if (buffer_lowprio(bh)) + lowprio = 1; /* - * i think the read and write branch should be separated completely, since we want - * to do read balancing on the read side for example. Comments? :) --mingo + * i think the read and write branch should be separated completely, + * since we want to do read balancing on the read side for example. + * Comments? :) --mingo */ r1_bh->master_bh=bh; r1_bh->mddev=mddev; r1_bh->cmd = rw; - if (rw==READ || rw==READA) { - int last_used = raid_conf->last_used; - PRINTK(("raid1_make_request(), read branch.\n")); - mirror = raid_conf->mirrors + last_used; + if (rw==READ) { + int last_used = conf->last_used; + + /* + * read balancing logic: + */ + mirror = conf->mirrors + last_used; bh->b_rdev = mirror->dev; sectors = bh->b_size >> 9; - if (bh->b_blocknr * sectors == raid_conf->next_sect) { - raid_conf->sect_count += sectors; - if (raid_conf->sect_count >= mirror->sect_limit) + + if (bh->b_blocknr * sectors == conf->next_sect) { + conf->sect_count += sectors; + if (conf->sect_count >= mirror->sect_limit) switch_disks = 1; } else switch_disks = 1; - raid_conf->next_sect = (bh->b_blocknr + 1) * sectors; - if (switch_disks) { - PRINTK(("read-balancing: switching %d -> %d (%d sectors)\n", last_used, mirror->next, raid_conf->sect_count)); - raid_conf->sect_count = 0; - last_used = raid_conf->last_used = mirror->next; + conf->next_sect = (bh->b_blocknr + 1) * sectors; + /* + * Do not switch disks if full resync is in progress ... + */ + if (switch_disks && !conf->resync_mirrors) { + conf->sect_count = 0; + last_used = conf->last_used = mirror->next; /* - * Do not switch to write-only disks ... resyncing - * is in progress + * Do not switch to write-only disks ... + * reconstruction is in progress */ - while (raid_conf->mirrors[last_used].write_only) - raid_conf->last_used = raid_conf->mirrors[last_used].next; + while (conf->mirrors[last_used].write_only) + conf->last_used = conf->mirrors[last_used].next; } - PRINTK (("raid1 read queue: %d %d\n", MAJOR (bh->b_rdev), MINOR (bh->b_rdev))); bh_req = &r1_bh->bh_req; memcpy(bh_req, bh, sizeof(*bh)); bh_req->b_end_io = raid1_end_request; @@ -269,13 +271,12 @@ raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) } /* - * WRITE or WRITEA. + * WRITE: */ - PRINTK(("raid1_make_request(n=%d), write branch.\n",n)); - for (i = 0; i < n; i++) { + for (i = 0; i < disks; i++) { - if (!raid_conf->mirrors [i].operational) { + if (!conf->mirrors[i].operational) { /* * the r1_bh->mirror_bh[i] pointer remains NULL */ @@ -283,85 +284,91 @@ raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) continue; } - /* - * We should use a private pool (size depending on NR_REQUEST), - * to avoid writes filling up the memory with bhs - * - * Such pools are much faster than kmalloc anyways (so we waste almost - * nothing by not using the master bh when writing and win alot of cleanness) - * - * but for now we are cool enough. --mingo - * - * It's safe to sleep here, buffer heads cannot be used in a shared - * manner in the write branch. Look how we lock the buffer at the beginning - * of this function to grok the difference ;) - */ - while (!( /* FIXME: now we are rather fault tolerant than nice */ - mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL) - ) ) - printk ("raid1_make_request(#2): out of memory\n"); - memset (mirror_bh[i], 0, sizeof (struct buffer_head)); - - /* - * prepare mirrored bh (fields ordered for max mem throughput): - */ - mirror_bh [i]->b_blocknr = bh->b_blocknr; - mirror_bh [i]->b_dev = bh->b_dev; - mirror_bh [i]->b_rdev = raid_conf->mirrors [i].dev; - mirror_bh [i]->b_rsector = bh->b_rsector; - mirror_bh [i]->b_state = (1<b_count = 1; - mirror_bh [i]->b_size = bh->b_size; - mirror_bh [i]->b_data = bh->b_data; - mirror_bh [i]->b_list = BUF_LOCKED; - mirror_bh [i]->b_end_io = raid1_end_request; - mirror_bh [i]->b_dev_id = r1_bh; - - r1_bh->mirror_bh[i] = mirror_bh[i]; - sum_bhs++; + /* + * special case for reconstruction ... + */ + if (lowprio && (i == conf->last_used)) { + mirror_bh[i] = NULL; + continue; + } + + /* + * We should use a private pool (size depending on NR_REQUEST), + * to avoid writes filling up the memory with bhs + * + * Such pools are much faster than kmalloc anyways (so we waste + * almost nothing by not using the master bh when writing and + * win alot of cleanness) but for now we are cool enough. --mingo + * + * It's safe to sleep here, buffer heads cannot be used in a shared + * manner in the write branch. Look how we lock the buffer at the + * beginning of this function to grok the difference ;) + */ + mirror_bh[i] = raid1_kmalloc(sizeof(struct buffer_head)); + /* + * prepare mirrored bh (fields ordered for max mem throughput): + */ + mirror_bh[i]->b_blocknr = bh->b_blocknr; + mirror_bh[i]->b_dev = bh->b_dev; + mirror_bh[i]->b_rdev = conf->mirrors[i].dev; + mirror_bh[i]->b_rsector = bh->b_rsector; + mirror_bh[i]->b_state = (1<b_state |= (1<b_count = 1; + mirror_bh[i]->b_size = bh->b_size; + mirror_bh[i]->b_data = bh->b_data; + mirror_bh[i]->b_list = BUF_LOCKED; + mirror_bh[i]->b_end_io = raid1_end_request; + mirror_bh[i]->b_dev_id = r1_bh; + + r1_bh->mirror_bh[i] = mirror_bh[i]; + sum_bhs++; } - r1_bh->remaining = sum_bhs; - - PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs)); + md_atomic_set(&r1_bh->remaining, sum_bhs); /* - * We have to be a bit careful about the semaphore above, thats why we - * start the requests separately. Since kmalloc() could fail, sleep and - * make_request() can sleep too, this is the safer solution. Imagine, - * end_request decreasing the semaphore before we could have set it up ... - * We could play tricks with the semaphore (presetting it and correcting - * at the end if sum_bhs is not 'n' but we have to do end_request by hand - * if all requests finish until we had a chance to set up the semaphore - * correctly ... lots of races). + * We have to be a bit careful about the semaphore above, thats + * why we start the requests separately. Since kmalloc() could + * fail, sleep and make_request() can sleep too, this is the + * safer solution. Imagine, end_request decreasing the semaphore + * before we could have set it up ... We could play tricks with + * the semaphore (presetting it and correcting at the end if + * sum_bhs is not 'n' but we have to do end_request by hand if + * all requests finish until we had a chance to set up the + * semaphore correctly ... lots of races). */ - for (i = 0; i < n; i++) - if (mirror_bh [i] != NULL) - map_and_make_request (rw, mirror_bh [i]); + for (i = 0; i < disks; i++) + if (mirror_bh[i]) + map_and_make_request(rw, mirror_bh[i]); return (0); } -static int raid1_status (char *page, int minor, struct md_dev *mddev) +static int raid1_status (char *page, mddev_t *mddev) { - struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + raid1_conf_t *conf = mddev_to_conf(mddev); int sz = 0, i; - sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); - for (i = 0; i < raid_conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_"); + sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, + conf->working_disks); + for (i = 0; i < conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", + conf->mirrors[i].operational ? "U" : "_"); sz += sprintf (page+sz, "]"); return sz; } -static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index) +static void unlink_disk (raid1_conf_t *conf, int target) { - int disks = raid_conf->raid_disks; - int j; + int disks = MD_SB_DISKS; + int i; - for (j = 0; j < disks; j++) - if (raid_conf->mirrors [j].next == failed_index) - raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next; + for (i = 0; i < disks; i++) + if (conf->mirrors[i].next == target) + conf->mirrors[i].next = conf->mirrors[target].next; } #define LAST_DISK KERN_ALERT \ @@ -380,48 +387,53 @@ static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index) #define ALREADY_SYNCING KERN_INFO \ "raid1: syncing already in progress.\n" -static int raid1_error (struct md_dev *mddev, kdev_t dev) +static void mark_disk_bad (mddev_t *mddev, int failed) { - struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; - struct mirror_info *mirror; - md_superblock_t *sb = mddev->sb; - int disks = raid_conf->raid_disks; - int i; + raid1_conf_t *conf = mddev_to_conf(mddev); + struct mirror_info *mirror = conf->mirrors+failed; + mdp_super_t *sb = mddev->sb; + + mirror->operational = 0; + unlink_disk(conf, failed); + mark_disk_faulty(sb->disks+mirror->number); + mark_disk_nonsync(sb->disks+mirror->number); + mark_disk_inactive(sb->disks+mirror->number); + sb->active_disks--; + sb->working_disks--; + sb->failed_disks++; + mddev->sb_dirty = 1; + md_wakeup_thread(conf->thread); + conf->working_disks--; + printk (DISK_FAILED, partition_name (mirror->dev), + conf->working_disks); +} - PRINTK(("raid1_error called\n")); +static int raid1_error (mddev_t *mddev, kdev_t dev) +{ + raid1_conf_t *conf = mddev_to_conf(mddev); + struct mirror_info * mirrors = conf->mirrors; + int disks = MD_SB_DISKS; + int i; - if (raid_conf->working_disks == 1) { + if (conf->working_disks == 1) { /* * Uh oh, we can do nothing if this is our last disk, but * first check if this is a queued request for a device * which has just failed. */ - for (i = 0, mirror = raid_conf->mirrors; i < disks; - i++, mirror++) - if (mirror->dev == dev && !mirror->operational) + for (i = 0; i < disks; i++) { + if (mirrors[i].dev==dev && !mirrors[i].operational) return 0; + } printk (LAST_DISK); } else { - /* Mark disk as unusable */ - for (i = 0, mirror = raid_conf->mirrors; i < disks; - i++, mirror++) { - if (mirror->dev == dev && mirror->operational){ - mirror->operational = 0; - raid1_fix_links (raid_conf, i); - sb->disks[mirror->number].state |= - (1 << MD_FAULTY_DEVICE); - sb->disks[mirror->number].state &= - ~(1 << MD_SYNC_DEVICE); - sb->disks[mirror->number].state &= - ~(1 << MD_ACTIVE_DEVICE); - sb->active_disks--; - sb->working_disks--; - sb->failed_disks++; - mddev->sb_dirty = 1; - md_wakeup_thread(raid1_thread); - raid_conf->working_disks--; - printk (DISK_FAILED, kdevname (dev), - raid_conf->working_disks); + /* + * Mark disk as unusable + */ + for (i = 0; i < disks; i++) { + if (mirrors[i].dev==dev && mirrors[i].operational) { + mark_disk_bad (mddev, i); + break; } } } @@ -434,219 +446,396 @@ static int raid1_error (struct md_dev *mddev, kdev_t dev) #undef START_SYNCING /* - * This is the personality-specific hot-addition routine + * Insert the spare disk into the drive-ring */ +static void link_disk(raid1_conf_t *conf, struct mirror_info *mirror) +{ + int j, next; + int disks = MD_SB_DISKS; + struct mirror_info *p = conf->mirrors; -#define NO_SUPERBLOCK KERN_ERR \ -"raid1: cannot hot-add disk to the array with no RAID superblock\n" + for (j = 0; j < disks; j++, p++) + if (p->operational && !p->write_only) { + next = p->next; + p->next = mirror->raid_disk; + mirror->next = next; + return; + } -#define WRONG_LEVEL KERN_ERR \ -"raid1: hot-add: level of disk is not RAID-1\n" + printk("raid1: bug: no read-operational devices\n"); +} + +static void print_raid1_conf (raid1_conf_t *conf) +{ + int i; + struct mirror_info *tmp; -#define HOT_ADD_SUCCEEDED KERN_INFO \ -"raid1: device %s hot-added\n" + printk("RAID1 conf printout:\n"); + if (!conf) { + printk("(conf==NULL)\n"); + return; + } + printk(" --- wd:%d rd:%d nd:%d\n", conf->working_disks, + conf->raid_disks, conf->nr_disks); + + for (i = 0; i < MD_SB_DISKS; i++) { + tmp = conf->mirrors + i; + printk(" disk %d, s:%d, o:%d, n:%d rd:%d us:%d dev:%s\n", + i, tmp->spare,tmp->operational, + tmp->number,tmp->raid_disk,tmp->used_slot, + partition_name(tmp->dev)); + } +} -static int raid1_hot_add_disk (struct md_dev *mddev, kdev_t dev) +static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state) { + int err = 0; + int i, failed_disk=-1, spare_disk=-1, removed_disk=-1, added_disk=-1; + raid1_conf_t *conf = mddev->private; + struct mirror_info *tmp, *sdisk, *fdisk, *rdisk, *adisk; unsigned long flags; - struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; - struct mirror_info *mirror; - md_superblock_t *sb = mddev->sb; - struct real_dev * realdev; - int n; + mdp_super_t *sb = mddev->sb; + mdp_disk_t *failed_desc, *spare_desc, *added_desc; + + save_flags(flags); + cli(); + print_raid1_conf(conf); /* - * The device has its superblock already read and it was found - * to be consistent for generic RAID usage. Now we check whether - * it's usable for RAID-1 hot addition. + * find the disk ... */ + switch (state) { - n = mddev->nb_dev++; - realdev = &mddev->devices[n]; - if (!realdev->sb) { - printk (NO_SUPERBLOCK); - return -EINVAL; - } - if (realdev->sb->level != 1) { - printk (WRONG_LEVEL); - return -EINVAL; + case DISKOP_SPARE_ACTIVE: + + /* + * Find the failed disk within the RAID1 configuration ... + * (this can only be in the first conf->working_disks part) + */ + for (i = 0; i < conf->raid_disks; i++) { + tmp = conf->mirrors + i; + if ((!tmp->operational && !tmp->spare) || + !tmp->used_slot) { + failed_disk = i; + break; + } + } + /* + * When we activate a spare disk we _must_ have a disk in + * the lower (active) part of the array to replace. + */ + if ((failed_disk == -1) || (failed_disk >= conf->raid_disks)) { + MD_BUG(); + err = 1; + goto abort; + } + /* fall through */ + + case DISKOP_SPARE_WRITE: + case DISKOP_SPARE_INACTIVE: + + /* + * Find the spare disk ... (can only be in the 'high' + * area of the array) + */ + for (i = conf->raid_disks; i < MD_SB_DISKS; i++) { + tmp = conf->mirrors + i; + if (tmp->spare && tmp->number == (*d)->number) { + spare_disk = i; + break; + } + } + if (spare_disk == -1) { + MD_BUG(); + err = 1; + goto abort; + } + break; + + case DISKOP_HOT_REMOVE_DISK: + + for (i = 0; i < MD_SB_DISKS; i++) { + tmp = conf->mirrors + i; + if (tmp->used_slot && (tmp->number == (*d)->number)) { + if (tmp->operational) { + err = -EBUSY; + goto abort; + } + removed_disk = i; + break; + } + } + if (removed_disk == -1) { + MD_BUG(); + err = 1; + goto abort; + } + break; + + case DISKOP_HOT_ADD_DISK: + + for (i = conf->raid_disks; i < MD_SB_DISKS; i++) { + tmp = conf->mirrors + i; + if (!tmp->used_slot) { + added_disk = i; + break; + } + } + if (added_disk == -1) { + MD_BUG(); + err = 1; + goto abort; + } + break; } - /* FIXME: are there other things left we could sanity-check? */ + switch (state) { /* - * We have to disable interrupts, as our RAID-1 state is used - * from irq handlers as well. + * Switch the spare disk to write-only mode: */ - save_flags(flags); - cli(); + case DISKOP_SPARE_WRITE: + sdisk = conf->mirrors + spare_disk; + sdisk->operational = 1; + sdisk->write_only = 1; + break; + /* + * Deactivate a spare disk: + */ + case DISKOP_SPARE_INACTIVE: + sdisk = conf->mirrors + spare_disk; + sdisk->operational = 0; + sdisk->write_only = 0; + break; + /* + * Activate (mark read-write) the (now sync) spare disk, + * which means we switch it's 'raid position' (->raid_disk) + * with the failed disk. (only the first 'conf->nr_disks' + * slots are used for 'real' disks and we must preserve this + * property) + */ + case DISKOP_SPARE_ACTIVE: - raid_conf->raid_disks++; - mirror = raid_conf->mirrors+n; + sdisk = conf->mirrors + spare_disk; + fdisk = conf->mirrors + failed_disk; - mirror->number=n; - mirror->raid_disk=n; - mirror->dev=dev; - mirror->next=0; /* FIXME */ - mirror->sect_limit=128; + spare_desc = &sb->disks[sdisk->number]; + failed_desc = &sb->disks[fdisk->number]; - mirror->operational=0; - mirror->spare=1; - mirror->write_only=0; + if (spare_desc != *d) { + MD_BUG(); + err = 1; + goto abort; + } - sb->disks[n].state |= (1 << MD_FAULTY_DEVICE); - sb->disks[n].state &= ~(1 << MD_SYNC_DEVICE); - sb->disks[n].state &= ~(1 << MD_ACTIVE_DEVICE); - sb->nr_disks++; - sb->spare_disks++; + if (spare_desc->raid_disk != sdisk->raid_disk) { + MD_BUG(); + err = 1; + goto abort; + } + + if (sdisk->raid_disk != spare_disk) { + MD_BUG(); + err = 1; + goto abort; + } - restore_flags(flags); + if (failed_desc->raid_disk != fdisk->raid_disk) { + MD_BUG(); + err = 1; + goto abort; + } - md_update_sb(MINOR(dev)); + if (fdisk->raid_disk != failed_disk) { + MD_BUG(); + err = 1; + goto abort; + } - printk (HOT_ADD_SUCCEEDED, kdevname(realdev->dev)); + /* + * do the switch finally + */ + xchg_values(*spare_desc, *failed_desc); + xchg_values(*fdisk, *sdisk); - return 0; -} + /* + * (careful, 'failed' and 'spare' are switched from now on) + * + * we want to preserve linear numbering and we want to + * give the proper raid_disk number to the now activated + * disk. (this means we switch back these values) + */ + + xchg_values(spare_desc->raid_disk, failed_desc->raid_disk); + xchg_values(sdisk->raid_disk, fdisk->raid_disk); + xchg_values(spare_desc->number, failed_desc->number); + xchg_values(sdisk->number, fdisk->number); -#undef NO_SUPERBLOCK -#undef WRONG_LEVEL -#undef HOT_ADD_SUCCEEDED + *d = failed_desc; -/* - * Insert the spare disk into the drive-ring - */ -static void add_ring(struct raid1_data *raid_conf, struct mirror_info *mirror) -{ - int j, next; - struct mirror_info *p = raid_conf->mirrors; + if (sdisk->dev == MKDEV(0,0)) + sdisk->used_slot = 0; + /* + * this really activates the spare. + */ + fdisk->spare = 0; + fdisk->write_only = 0; + link_disk(conf, fdisk); - for (j = 0; j < raid_conf->raid_disks; j++, p++) - if (p->operational && !p->write_only) { - next = p->next; - p->next = mirror->raid_disk; - mirror->next = next; - return; + /* + * if we activate a spare, we definitely replace a + * non-operational disk slot in the 'low' area of + * the disk array. + */ + + conf->working_disks++; + + break; + + case DISKOP_HOT_REMOVE_DISK: + rdisk = conf->mirrors + removed_disk; + + if (rdisk->spare && (removed_disk < conf->raid_disks)) { + MD_BUG(); + err = 1; + goto abort; + } + rdisk->dev = MKDEV(0,0); + rdisk->used_slot = 0; + conf->nr_disks--; + break; + + case DISKOP_HOT_ADD_DISK: + adisk = conf->mirrors + added_disk; + added_desc = *d; + + if (added_disk != added_desc->number) { + MD_BUG(); + err = 1; + goto abort; } - printk("raid1: bug: no read-operational devices\n"); -} -static int raid1_mark_spare(struct md_dev *mddev, md_descriptor_t *spare, - int state) -{ - int i = 0, failed_disk = -1; - struct raid1_data *raid_conf = mddev->private; - struct mirror_info *mirror = raid_conf->mirrors; - md_descriptor_t *descriptor; - unsigned long flags; + adisk->number = added_desc->number; + adisk->raid_disk = added_desc->raid_disk; + adisk->dev = MKDEV(added_desc->major,added_desc->minor); - for (i = 0; i < MD_SB_DISKS; i++, mirror++) { - if (mirror->spare && mirror->number == spare->number) - goto found; - } - return 1; -found: - for (i = 0, mirror = raid_conf->mirrors; i < raid_conf->raid_disks; - i++, mirror++) - if (!mirror->operational) - failed_disk = i; + adisk->operational = 0; + adisk->write_only = 0; + adisk->spare = 1; + adisk->used_slot = 1; + conf->nr_disks++; - save_flags(flags); - cli(); - switch (state) { - case SPARE_WRITE: - mirror->operational = 1; - mirror->write_only = 1; - raid_conf->raid_disks = MAX(raid_conf->raid_disks, - mirror->raid_disk + 1); - break; - case SPARE_INACTIVE: - mirror->operational = 0; - mirror->write_only = 0; - break; - case SPARE_ACTIVE: - mirror->spare = 0; - mirror->write_only = 0; - raid_conf->working_disks++; - add_ring(raid_conf, mirror); - - if (failed_disk != -1) { - descriptor = &mddev->sb->disks[raid_conf->mirrors[failed_disk].number]; - i = spare->raid_disk; - spare->raid_disk = descriptor->raid_disk; - descriptor->raid_disk = i; - } - break; - default: - printk("raid1_mark_spare: bug: state == %d\n", state); - restore_flags(flags); - return 1; + break; + + default: + MD_BUG(); + err = 1; + goto abort; } +abort: restore_flags(flags); - return 0; + print_raid1_conf(conf); + return err; } + +#define IO_ERROR KERN_ALERT \ +"raid1: %s: unrecoverable I/O read error for block %lu\n" + +#define REDIRECT_SECTOR KERN_ERR \ +"raid1: %s: redirecting sector %lu to another mirror\n" + /* * This is a kernel thread which: * * 1. Retries failed read operations on working mirrors. * 2. Updates the raid superblock when problems encounter. */ -void raid1d (void *data) +static void raid1d (void *data) { struct buffer_head *bh; kdev_t dev; unsigned long flags; - struct raid1_bh * r1_bh; - struct md_dev *mddev; + struct raid1_bh *r1_bh; + mddev_t *mddev; - PRINTK(("raid1d() active\n")); - save_flags(flags); - cli(); while (raid1_retry_list) { + save_flags(flags); + cli(); bh = raid1_retry_list; r1_bh = (struct raid1_bh *)(bh->b_dev_id); raid1_retry_list = r1_bh->next_retry; restore_flags(flags); - mddev = md_dev + MINOR(bh->b_dev); + mddev = kdev_to_mddev(bh->b_dev); if (mddev->sb_dirty) { - printk("dirty sb detected, updating.\n"); + printk(KERN_INFO "dirty sb detected, updating.\n"); mddev->sb_dirty = 0; - md_update_sb(MINOR(bh->b_dev)); + md_update_sb(mddev); } dev = bh->b_rdev; - __raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9); + __raid1_map (mddev, &bh->b_rdev, &bh->b_rsector, + bh->b_size >> 9); if (bh->b_rdev == dev) { - printk (KERN_ALERT - "raid1: %s: unrecoverable I/O read error for block %lu\n", - kdevname(bh->b_dev), bh->b_blocknr); - raid1_end_buffer_io(r1_bh, 0); + printk (IO_ERROR, partition_name(bh->b_dev), bh->b_blocknr); + raid1_end_bh_io(r1_bh, 0); } else { - printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n", - kdevname(bh->b_dev), bh->b_blocknr); + printk (REDIRECT_SECTOR, + partition_name(bh->b_dev), bh->b_blocknr); map_and_make_request (r1_bh->cmd, bh); } - cli(); } - restore_flags(flags); } +#undef IO_ERROR +#undef REDIRECT_SECTOR + +/* + * Private kernel thread to reconstruct mirrors after an unclean + * shutdown. + */ +static void raid1syncd (void *data) +{ + raid1_conf_t *conf = data; + mddev_t *mddev = conf->mddev; + + if (!conf->resync_mirrors) + return; + if (conf->resync_mirrors == 2) + return; + down(&mddev->recovery_sem); + if (md_do_sync(mddev, NULL)) { + up(&mddev->recovery_sem); + return; + } + /* + * Only if everything went Ok. + */ + conf->resync_mirrors = 0; + up(&mddev->recovery_sem); +} + /* * This will catch the scenario in which one of the mirrors was * mounted as a normal device rather than as a part of a raid set. + * + * check_consistency is very personality-dependent, eg. RAID5 cannot + * do this check, it uses another method. */ -static int __check_consistency (struct md_dev *mddev, int row) +static int __check_consistency (mddev_t *mddev, int row) { - struct raid1_data *raid_conf = mddev->private; + raid1_conf_t *conf = mddev_to_conf(mddev); + int disks = MD_SB_DISKS; kdev_t dev; struct buffer_head *bh = NULL; int i, rc = 0; char *buffer = NULL; - for (i = 0; i < raid_conf->raid_disks; i++) { - if (!raid_conf->mirrors[i].operational) + for (i = 0; i < disks; i++) { + printk("(checking disk %d)\n",i); + if (!conf->mirrors[i].operational) continue; - dev = raid_conf->mirrors[i].dev; + printk("(really checking disk %d)\n",i); + dev = conf->mirrors[i].dev; set_blocksize(dev, 4096); if ((bh = bread(dev, row / 4, 4096)) == NULL) break; @@ -675,163 +864,342 @@ static int __check_consistency (struct md_dev *mddev, int row) return rc; } -static int check_consistency (struct md_dev *mddev) +static int check_consistency (mddev_t *mddev) { - int size = mddev->sb->size; - int row; + if (__check_consistency(mddev, 0)) +/* + * we do not do this currently, as it's perfectly possible to + * have an inconsistent array when it's freshly created. Only + * newly written data has to be consistent. + */ + return 0; - for (row = 0; row < size; row += size / 8) - if (__check_consistency(mddev, row)) - return 1; return 0; } -static int raid1_run (int minor, struct md_dev *mddev) +#define INVALID_LEVEL KERN_WARNING \ +"raid1: md%d: raid level not set to mirroring (%d)\n" + +#define NO_SB KERN_ERR \ +"raid1: disabled mirror %s (couldn't access raid superblock)\n" + +#define ERRORS KERN_ERR \ +"raid1: disabled mirror %s (errors detected)\n" + +#define NOT_IN_SYNC KERN_ERR \ +"raid1: disabled mirror %s (not in sync)\n" + +#define INCONSISTENT KERN_ERR \ +"raid1: disabled mirror %s (inconsistent descriptor)\n" + +#define ALREADY_RUNNING KERN_ERR \ +"raid1: disabled mirror %s (mirror %d already operational)\n" + +#define OPERATIONAL KERN_INFO \ +"raid1: device %s operational as mirror %d\n" + +#define MEM_ERROR KERN_ERR \ +"raid1: couldn't allocate memory for md%d\n" + +#define SPARE KERN_INFO \ +"raid1: spare disk %s\n" + +#define NONE_OPERATIONAL KERN_ERR \ +"raid1: no operational mirrors for md%d\n" + +#define RUNNING_CKRAID KERN_ERR \ +"raid1: detected mirror differences -- running resync\n" + +#define ARRAY_IS_ACTIVE KERN_INFO \ +"raid1: raid set md%d active with %d out of %d mirrors\n" + +#define THREAD_ERROR KERN_ERR \ +"raid1: couldn't allocate thread for md%d\n" + +#define START_RESYNC KERN_WARNING \ +"raid1: raid set md%d not clean; reconstructing mirrors\n" + +static int raid1_run (mddev_t *mddev) { - struct raid1_data *raid_conf; - int i, j, raid_disk; - md_superblock_t *sb = mddev->sb; - md_descriptor_t *descriptor; - struct real_dev *realdev; + raid1_conf_t *conf; + int i, j, disk_idx; + struct mirror_info *disk; + mdp_super_t *sb = mddev->sb; + mdp_disk_t *descriptor; + mdk_rdev_t *rdev; + struct md_list_head *tmp; + int start_recovery = 0; MOD_INC_USE_COUNT; if (sb->level != 1) { - printk("raid1: %s: raid level not set to mirroring (%d)\n", - kdevname(MKDEV(MD_MAJOR, minor)), sb->level); - MOD_DEC_USE_COUNT; - return -EIO; + printk(INVALID_LEVEL, mdidx(mddev), sb->level); + goto out; } - /**** - * copy the now verified devices into our private RAID1 bookkeeping - * area. [whatever we allocate in raid1_run(), should be freed in - * raid1_stop()] + /* + * copy the already verified devices into our private RAID1 + * bookkeeping area. [whatever we allocate in raid1_run(), + * should be freed in raid1_stop()] */ - while (!( /* FIXME: now we are rather fault tolerant than nice */ - mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL) - ) ) - printk ("raid1_run(): out of memory\n"); - raid_conf = mddev->private; - memset(raid_conf, 0, sizeof(*raid_conf)); - - PRINTK(("raid1_run(%d) called.\n", minor)); + conf = raid1_kmalloc(sizeof(raid1_conf_t)); + mddev->private = conf; + if (!conf) { + printk(MEM_ERROR, mdidx(mddev)); + goto out; + } - for (i = 0; i < mddev->nb_dev; i++) { - realdev = &mddev->devices[i]; - if (!realdev->sb) { - printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->faulty) { + printk(ERRORS, partition_name(rdev->dev)); + } else { + if (!rdev->sb) { + MD_BUG(); + continue; + } + } + if (rdev->desc_nr == -1) { + MD_BUG(); continue; } - - /* - * This is important -- we are using the descriptor on - * the disk only to get a pointer to the descriptor on - * the main superblock, which might be more recent. - */ - descriptor = &sb->disks[realdev->sb->descriptor.number]; - if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { - printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev)); + descriptor = &sb->disks[rdev->desc_nr]; + disk_idx = descriptor->raid_disk; + disk = conf->mirrors + disk_idx; + + if (disk_faulty(descriptor)) { + disk->number = descriptor->number; + disk->raid_disk = disk_idx; + disk->dev = rdev->dev; + disk->sect_limit = MAX_LINEAR_SECTORS; + disk->operational = 0; + disk->write_only = 0; + disk->spare = 0; + disk->used_slot = 1; continue; } - if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { - if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { - printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev)); + if (disk_active(descriptor)) { + if (!disk_sync(descriptor)) { + printk(NOT_IN_SYNC, + partition_name(rdev->dev)); continue; } - raid_disk = descriptor->raid_disk; - if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { - printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + if ((descriptor->number > MD_SB_DISKS) || + (disk_idx > sb->raid_disks)) { + + printk(INCONSISTENT, + partition_name(rdev->dev)); continue; } - if (raid_conf->mirrors[raid_disk].operational) { - printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk); + if (disk->operational) { + printk(ALREADY_RUNNING, + partition_name(rdev->dev), + disk_idx); continue; } - printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk); - raid_conf->mirrors[raid_disk].number = descriptor->number; - raid_conf->mirrors[raid_disk].raid_disk = raid_disk; - raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev; - raid_conf->mirrors[raid_disk].operational = 1; - raid_conf->mirrors[raid_disk].sect_limit = 128; - raid_conf->working_disks++; + printk(OPERATIONAL, partition_name(rdev->dev), + disk_idx); + disk->number = descriptor->number; + disk->raid_disk = disk_idx; + disk->dev = rdev->dev; + disk->sect_limit = MAX_LINEAR_SECTORS; + disk->operational = 1; + disk->write_only = 0; + disk->spare = 0; + disk->used_slot = 1; + conf->working_disks++; } else { /* * Must be a spare disk .. */ - printk(KERN_INFO "raid1: spare disk %s\n", kdevname(realdev->dev)); - raid_disk = descriptor->raid_disk; - raid_conf->mirrors[raid_disk].number = descriptor->number; - raid_conf->mirrors[raid_disk].raid_disk = raid_disk; - raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev; - raid_conf->mirrors[raid_disk].sect_limit = 128; - - raid_conf->mirrors[raid_disk].operational = 0; - raid_conf->mirrors[raid_disk].write_only = 0; - raid_conf->mirrors[raid_disk].spare = 1; + printk(SPARE, partition_name(rdev->dev)); + disk->number = descriptor->number; + disk->raid_disk = disk_idx; + disk->dev = rdev->dev; + disk->sect_limit = MAX_LINEAR_SECTORS; + disk->operational = 0; + disk->write_only = 0; + disk->spare = 1; + disk->used_slot = 1; } } - if (!raid_conf->working_disks) { - printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); - kfree(raid_conf); - mddev->private = NULL; - MOD_DEC_USE_COUNT; - return -EIO; + if (!conf->working_disks) { + printk(NONE_OPERATIONAL, mdidx(mddev)); + goto out_free_conf; } - raid_conf->raid_disks = sb->raid_disks; - raid_conf->mddev = mddev; + conf->raid_disks = sb->raid_disks; + conf->nr_disks = sb->nr_disks; + conf->mddev = mddev; - for (j = 0; !raid_conf->mirrors[j].operational; j++); - raid_conf->last_used = j; - for (i = raid_conf->raid_disks - 1; i >= 0; i--) { - if (raid_conf->mirrors[i].operational) { - PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j)); - raid_conf->mirrors[i].next = j; + for (i = 0; i < MD_SB_DISKS; i++) { + + descriptor = sb->disks+i; + disk_idx = descriptor->raid_disk; + disk = conf->mirrors + disk_idx; + + if (disk_faulty(descriptor) && (disk_idx < conf->raid_disks) && + !disk->used_slot) { + + disk->number = descriptor->number; + disk->raid_disk = disk_idx; + disk->dev = MKDEV(0,0); + + disk->operational = 0; + disk->write_only = 0; + disk->spare = 0; + disk->used_slot = 1; + } + } + + /* + * find the first working one and use it as a starting point + * to read balancing. + */ + for (j = 0; !conf->mirrors[j].operational; j++) + /* nothing */; + conf->last_used = j; + + /* + * initialize the 'working disks' list. + */ + for (i = conf->raid_disks - 1; i >= 0; i--) { + if (conf->mirrors[i].operational) { + conf->mirrors[i].next = j; j = i; } } - if (check_consistency(mddev)) { - printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n"); - sb->state |= 1 << MD_SB_ERRORS; - kfree(raid_conf); - mddev->private = NULL; - MOD_DEC_USE_COUNT; - return -EIO; + if (conf->working_disks != sb->raid_disks) { + printk(KERN_ALERT "raid1: md%d, not all disks are operational -- trying to recover array\n", mdidx(mddev)); + start_recovery = 1; + } + + if (!start_recovery && (sb->state & (1 << MD_SB_CLEAN))) { + /* + * we do sanity checks even if the device says + * it's clean ... + */ + if (check_consistency(mddev)) { + printk(RUNNING_CKRAID); + sb->state &= ~(1 << MD_SB_CLEAN); + } } + { + const char * name = "raid1d"; + + conf->thread = md_register_thread(raid1d, conf, name); + if (!conf->thread) { + printk(THREAD_ERROR, mdidx(mddev)); + goto out_free_conf; + } + } + + if (!start_recovery && !(sb->state & (1 << MD_SB_CLEAN))) { + const char * name = "raid1syncd"; + + conf->resync_thread = md_register_thread(raid1syncd, conf,name); + if (!conf->resync_thread) { + printk(THREAD_ERROR, mdidx(mddev)); + goto out_free_conf; + } + + printk(START_RESYNC, mdidx(mddev)); + conf->resync_mirrors = 1; + md_wakeup_thread(conf->resync_thread); + } + /* * Regenerate the "device is in sync with the raid set" bit for * each device. */ - for (i = 0; i < sb->nr_disks ; i++) { - sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (i = 0; i < MD_SB_DISKS; i++) { + mark_disk_nonsync(sb->disks+i); for (j = 0; j < sb->raid_disks; j++) { - if (!raid_conf->mirrors[j].operational) + if (!conf->mirrors[j].operational) continue; - if (sb->disks[i].number == raid_conf->mirrors[j].number) - sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + if (sb->disks[i].number == conf->mirrors[j].number) + mark_disk_sync(sb->disks+i); } } - sb->active_disks = raid_conf->working_disks; + sb->active_disks = conf->working_disks; - printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks); - /* Ok, everything is just fine now */ - return (0); + if (start_recovery) + md_recover_arrays(); + + + printk(ARRAY_IS_ACTIVE, mdidx(mddev), sb->active_disks, sb->raid_disks); + /* + * Ok, everything is just fine now + */ + return 0; + +out_free_conf: + kfree(conf); + mddev->private = NULL; +out: + MOD_DEC_USE_COUNT; + return -EIO; +} + +#undef INVALID_LEVEL +#undef NO_SB +#undef ERRORS +#undef NOT_IN_SYNC +#undef INCONSISTENT +#undef ALREADY_RUNNING +#undef OPERATIONAL +#undef SPARE +#undef NONE_OPERATIONAL +#undef RUNNING_CKRAID +#undef ARRAY_IS_ACTIVE + +static int raid1_stop_resync (mddev_t *mddev) +{ + raid1_conf_t *conf = mddev_to_conf(mddev); + + if (conf->resync_thread) { + if (conf->resync_mirrors) { + conf->resync_mirrors = 2; + md_interrupt_thread(conf->resync_thread); + printk(KERN_INFO "raid1: mirror resync was not fully finished, restarting next time.\n"); + return 1; + } + return 0; + } + return 0; +} + +static int raid1_restart_resync (mddev_t *mddev) +{ + raid1_conf_t *conf = mddev_to_conf(mddev); + + if (conf->resync_mirrors) { + if (!conf->resync_thread) { + MD_BUG(); + return 0; + } + conf->resync_mirrors = 1; + md_wakeup_thread(conf->resync_thread); + return 1; + } + return 0; } -static int raid1_stop (int minor, struct md_dev *mddev) +static int raid1_stop (mddev_t *mddev) { - struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + raid1_conf_t *conf = mddev_to_conf(mddev); - kfree (raid_conf); + md_unregister_thread(conf->thread); + if (conf->resync_thread) + md_unregister_thread(conf->resync_thread); + kfree(conf); mddev->private = NULL; MOD_DEC_USE_COUNT; return 0; } -static struct md_personality raid1_personality= +static mdk_personality_t raid1_personality= { "raid1", raid1_map, @@ -843,15 +1211,13 @@ static struct md_personality raid1_personality= NULL, /* no ioctls */ 0, raid1_error, - raid1_hot_add_disk, - /* raid1_hot_remove_drive */ NULL, - raid1_mark_spare + raid1_diskop, + raid1_stop_resync, + raid1_restart_resync }; int raid1_init (void) { - if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL) - return -EBUSY; return register_md_personality (RAID1, &raid1_personality); } @@ -863,7 +1229,6 @@ int init_module (void) void cleanup_module (void) { - md_unregister_thread (raid1_thread); unregister_md_personality (RAID1); } #endif diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c index 66713a84b955..4db59b8e77c4 100644 --- a/drivers/block/raid5.c +++ b/drivers/block/raid5.c @@ -1,4 +1,4 @@ -/***************************************************************************** +/* * raid5.c : Multiple Devices driver for Linux * Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman * @@ -14,16 +14,15 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include -#include -#include +#include #include #include -#include -static struct md_personality raid5_personality; +static mdk_personality_t raid5_personality; /* * Stripe cache @@ -33,7 +32,7 @@ static struct md_personality raid5_personality; #define HASH_PAGES_ORDER 0 #define NR_HASH (HASH_PAGES * PAGE_SIZE / sizeof(struct stripe_head *)) #define HASH_MASK (NR_HASH - 1) -#define stripe_hash(raid_conf, sect, size) ((raid_conf)->stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK]) +#define stripe_hash(conf, sect, size) ((conf)->stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK]) /* * The following can be used to debug the driver @@ -46,6 +45,8 @@ static struct md_personality raid5_personality; #define PRINTK(x) do { ; } while (0) #endif +static void print_raid5_conf (raid5_conf_t *conf); + static inline int stripe_locked(struct stripe_head *sh) { return test_bit(STRIPE_LOCKED, &sh->state); @@ -61,32 +62,32 @@ static inline int stripe_error(struct stripe_head *sh) */ static inline void lock_stripe(struct stripe_head *sh) { - struct raid5_data *raid_conf = sh->raid_conf; - if (!test_and_set_bit(STRIPE_LOCKED, &sh->state)) { + raid5_conf_t *conf = sh->raid_conf; + if (!md_test_and_set_bit(STRIPE_LOCKED, &sh->state)) { PRINTK(("locking stripe %lu\n", sh->sector)); - raid_conf->nr_locked_stripes++; + conf->nr_locked_stripes++; } } static inline void unlock_stripe(struct stripe_head *sh) { - struct raid5_data *raid_conf = sh->raid_conf; - if (test_and_clear_bit(STRIPE_LOCKED, &sh->state)) { + raid5_conf_t *conf = sh->raid_conf; + if (md_test_and_clear_bit(STRIPE_LOCKED, &sh->state)) { PRINTK(("unlocking stripe %lu\n", sh->sector)); - raid_conf->nr_locked_stripes--; + conf->nr_locked_stripes--; wake_up(&sh->wait); } } static inline void finish_stripe(struct stripe_head *sh) { - struct raid5_data *raid_conf = sh->raid_conf; + raid5_conf_t *conf = sh->raid_conf; unlock_stripe(sh); sh->cmd = STRIPE_NONE; sh->phase = PHASE_COMPLETE; - raid_conf->nr_pending_stripes--; - raid_conf->nr_cached_stripes++; - wake_up(&raid_conf->wait_for_stripe); + conf->nr_pending_stripes--; + conf->nr_cached_stripes++; + wake_up(&conf->wait_for_stripe); } void __wait_on_stripe(struct stripe_head *sh) @@ -114,7 +115,7 @@ static inline void wait_on_stripe(struct stripe_head *sh) __wait_on_stripe(sh); } -static inline void remove_hash(struct raid5_data *raid_conf, struct stripe_head *sh) +static inline void remove_hash(raid5_conf_t *conf, struct stripe_head *sh) { PRINTK(("remove_hash(), stripe %lu\n", sh->sector)); @@ -123,21 +124,22 @@ static inline void remove_hash(struct raid5_data *raid_conf, struct stripe_head sh->hash_next->hash_pprev = sh->hash_pprev; *sh->hash_pprev = sh->hash_next; sh->hash_pprev = NULL; - raid_conf->nr_hashed_stripes--; + conf->nr_hashed_stripes--; } } -static inline void insert_hash(struct raid5_data *raid_conf, struct stripe_head *sh) +static inline void insert_hash(raid5_conf_t *conf, struct stripe_head *sh) { - struct stripe_head **shp = &stripe_hash(raid_conf, sh->sector, sh->size); + struct stripe_head **shp = &stripe_hash(conf, sh->sector, sh->size); - PRINTK(("insert_hash(), stripe %lu, nr_hashed_stripes %d\n", sh->sector, raid_conf->nr_hashed_stripes)); + PRINTK(("insert_hash(), stripe %lu, nr_hashed_stripes %d\n", + sh->sector, conf->nr_hashed_stripes)); if ((sh->hash_next = *shp) != NULL) (*shp)->hash_pprev = &sh->hash_next; *shp = sh; sh->hash_pprev = shp; - raid_conf->nr_hashed_stripes++; + conf->nr_hashed_stripes++; } static struct buffer_head *get_free_buffer(struct stripe_head *sh, int b_size) @@ -145,13 +147,15 @@ static struct buffer_head *get_free_buffer(struct stripe_head *sh, int b_size) struct buffer_head *bh; unsigned long flags; - save_flags(flags); - cli(); - if ((bh = sh->buffer_pool) == NULL) - return NULL; + md_spin_lock_irqsave(&sh->stripe_lock, flags); + bh = sh->buffer_pool; + if (!bh) + goto out_unlock; sh->buffer_pool = bh->b_next; bh->b_size = b_size; - restore_flags(flags); +out_unlock: + md_spin_unlock_irqrestore(&sh->stripe_lock, flags); + return bh; } @@ -160,12 +164,14 @@ static struct buffer_head *get_free_bh(struct stripe_head *sh) struct buffer_head *bh; unsigned long flags; - save_flags(flags); - cli(); - if ((bh = sh->bh_pool) == NULL) - return NULL; + md_spin_lock_irqsave(&sh->stripe_lock, flags); + bh = sh->bh_pool; + if (!bh) + goto out_unlock; sh->bh_pool = bh->b_next; - restore_flags(flags); +out_unlock: + md_spin_unlock_irqrestore(&sh->stripe_lock, flags); + return bh; } @@ -173,54 +179,52 @@ static void put_free_buffer(struct stripe_head *sh, struct buffer_head *bh) { unsigned long flags; - save_flags(flags); - cli(); + md_spin_lock_irqsave(&sh->stripe_lock, flags); bh->b_next = sh->buffer_pool; sh->buffer_pool = bh; - restore_flags(flags); + md_spin_unlock_irqrestore(&sh->stripe_lock, flags); } static void put_free_bh(struct stripe_head *sh, struct buffer_head *bh) { unsigned long flags; - save_flags(flags); - cli(); + md_spin_lock_irqsave(&sh->stripe_lock, flags); bh->b_next = sh->bh_pool; sh->bh_pool = bh; - restore_flags(flags); + md_spin_unlock_irqrestore(&sh->stripe_lock, flags); } -static struct stripe_head *get_free_stripe(struct raid5_data *raid_conf) +static struct stripe_head *get_free_stripe(raid5_conf_t *conf) { struct stripe_head *sh; unsigned long flags; save_flags(flags); cli(); - if ((sh = raid_conf->free_sh_list) == NULL) { + if ((sh = conf->free_sh_list) == NULL) { restore_flags(flags); return NULL; } - raid_conf->free_sh_list = sh->free_next; - raid_conf->nr_free_sh--; - if (!raid_conf->nr_free_sh && raid_conf->free_sh_list) + conf->free_sh_list = sh->free_next; + conf->nr_free_sh--; + if (!conf->nr_free_sh && conf->free_sh_list) printk ("raid5: bug: free_sh_list != NULL, nr_free_sh == 0\n"); restore_flags(flags); - if (sh->hash_pprev || sh->nr_pending || sh->count) + if (sh->hash_pprev || md_atomic_read(&sh->nr_pending) || sh->count) printk("get_free_stripe(): bug\n"); return sh; } -static void put_free_stripe(struct raid5_data *raid_conf, struct stripe_head *sh) +static void put_free_stripe(raid5_conf_t *conf, struct stripe_head *sh) { unsigned long flags; save_flags(flags); cli(); - sh->free_next = raid_conf->free_sh_list; - raid_conf->free_sh_list = sh; - raid_conf->nr_free_sh++; + sh->free_next = conf->free_sh_list; + conf->free_sh_list = sh; + conf->nr_free_sh++; restore_flags(flags); } @@ -324,8 +328,8 @@ static void raid5_update_old_bh(struct stripe_head *sh, int i) static void kfree_stripe(struct stripe_head *sh) { - struct raid5_data *raid_conf = sh->raid_conf; - int disks = raid_conf->raid_disks, j; + raid5_conf_t *conf = sh->raid_conf; + int disks = conf->raid_disks, j; PRINTK(("kfree_stripe called, stripe %lu\n", sh->sector)); if (sh->phase != PHASE_COMPLETE || stripe_locked(sh) || sh->count) { @@ -338,19 +342,19 @@ static void kfree_stripe(struct stripe_head *sh) if (sh->bh_new[j] || sh->bh_copy[j]) printk("raid5: bug: sector %lu, new %p, copy %p\n", sh->sector, sh->bh_new[j], sh->bh_copy[j]); } - remove_hash(raid_conf, sh); - put_free_stripe(raid_conf, sh); + remove_hash(conf, sh); + put_free_stripe(conf, sh); } -static int shrink_stripe_cache(struct raid5_data *raid_conf, int nr) +static int shrink_stripe_cache(raid5_conf_t *conf, int nr) { struct stripe_head *sh; int i, count = 0; - PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, raid_conf->nr_hashed_stripes, raid_conf->clock)); + PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, conf->nr_hashed_stripes, conf->clock)); for (i = 0; i < NR_HASH; i++) { repeat: - sh = raid_conf->stripe_hashtbl[(i + raid_conf->clock) & HASH_MASK]; + sh = conf->stripe_hashtbl[(i + conf->clock) & HASH_MASK]; for (; sh; sh = sh->hash_next) { if (sh->phase != PHASE_COMPLETE) continue; @@ -360,30 +364,30 @@ repeat: continue; kfree_stripe(sh); if (++count == nr) { - PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes)); - raid_conf->clock = (i + raid_conf->clock) & HASH_MASK; + PRINTK(("shrink completed, nr_hashed_stripes %d\n", conf->nr_hashed_stripes)); + conf->clock = (i + conf->clock) & HASH_MASK; return nr; } goto repeat; } } - PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes)); + PRINTK(("shrink completed, nr_hashed_stripes %d\n", conf->nr_hashed_stripes)); return count; } -static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +static struct stripe_head *find_stripe(raid5_conf_t *conf, unsigned long sector, int size) { struct stripe_head *sh; - if (raid_conf->buffer_size != size) { - PRINTK(("switching size, %d --> %d\n", raid_conf->buffer_size, size)); - shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes); - raid_conf->buffer_size = size; + if (conf->buffer_size != size) { + PRINTK(("switching size, %d --> %d\n", conf->buffer_size, size)); + shrink_stripe_cache(conf, conf->max_nr_stripes); + conf->buffer_size = size; } PRINTK(("find_stripe, sector %lu\n", sector)); - for (sh = stripe_hash(raid_conf, sector, size); sh; sh = sh->hash_next) - if (sh->sector == sector && sh->raid_conf == raid_conf) { + for (sh = stripe_hash(conf, sector, size); sh; sh = sh->hash_next) + if (sh->sector == sector && sh->raid_conf == conf) { if (sh->size == size) { PRINTK(("found stripe %lu\n", sector)); return sh; @@ -397,7 +401,7 @@ static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned lo return NULL; } -static int grow_stripes(struct raid5_data *raid_conf, int num, int priority) +static int grow_stripes(raid5_conf_t *conf, int num, int priority) { struct stripe_head *sh; @@ -405,62 +409,64 @@ static int grow_stripes(struct raid5_data *raid_conf, int num, int priority) if ((sh = kmalloc(sizeof(struct stripe_head), priority)) == NULL) return 1; memset(sh, 0, sizeof(*sh)); - if (grow_buffers(sh, 2 * raid_conf->raid_disks, PAGE_SIZE, priority)) { - shrink_buffers(sh, 2 * raid_conf->raid_disks); + sh->stripe_lock = MD_SPIN_LOCK_UNLOCKED; + + if (grow_buffers(sh, 2 * conf->raid_disks, PAGE_SIZE, priority)) { + shrink_buffers(sh, 2 * conf->raid_disks); kfree(sh); return 1; } - if (grow_bh(sh, raid_conf->raid_disks, priority)) { - shrink_buffers(sh, 2 * raid_conf->raid_disks); - shrink_bh(sh, raid_conf->raid_disks); + if (grow_bh(sh, conf->raid_disks, priority)) { + shrink_buffers(sh, 2 * conf->raid_disks); + shrink_bh(sh, conf->raid_disks); kfree(sh); return 1; } - put_free_stripe(raid_conf, sh); - raid_conf->nr_stripes++; + put_free_stripe(conf, sh); + conf->nr_stripes++; } return 0; } -static void shrink_stripes(struct raid5_data *raid_conf, int num) +static void shrink_stripes(raid5_conf_t *conf, int num) { struct stripe_head *sh; while (num--) { - sh = get_free_stripe(raid_conf); + sh = get_free_stripe(conf); if (!sh) break; - shrink_buffers(sh, raid_conf->raid_disks * 2); - shrink_bh(sh, raid_conf->raid_disks); + shrink_buffers(sh, conf->raid_disks * 2); + shrink_bh(sh, conf->raid_disks); kfree(sh); - raid_conf->nr_stripes--; + conf->nr_stripes--; } } -static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +static struct stripe_head *kmalloc_stripe(raid5_conf_t *conf, unsigned long sector, int size) { struct stripe_head *sh = NULL, *tmp; struct buffer_head *buffer_pool, *bh_pool; PRINTK(("kmalloc_stripe called\n")); - while ((sh = get_free_stripe(raid_conf)) == NULL) { - shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes / 8); - if ((sh = get_free_stripe(raid_conf)) != NULL) + while ((sh = get_free_stripe(conf)) == NULL) { + shrink_stripe_cache(conf, conf->max_nr_stripes / 8); + if ((sh = get_free_stripe(conf)) != NULL) break; - if (!raid_conf->nr_pending_stripes) + if (!conf->nr_pending_stripes) printk("raid5: bug: nr_free_sh == 0, nr_pending_stripes == 0\n"); - md_wakeup_thread(raid_conf->thread); + md_wakeup_thread(conf->thread); PRINTK(("waiting for some stripes to complete\n")); - sleep_on(&raid_conf->wait_for_stripe); + sleep_on(&conf->wait_for_stripe); } /* * The above might have slept, so perhaps another process * already created the stripe for us.. */ - if ((tmp = find_stripe(raid_conf, sector, size)) != NULL) { - put_free_stripe(raid_conf, sh); + if ((tmp = find_stripe(conf, sector, size)) != NULL) { + put_free_stripe(conf, sh); wait_on_stripe(tmp); return tmp; } @@ -472,25 +478,25 @@ static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned sh->bh_pool = bh_pool; sh->phase = PHASE_COMPLETE; sh->cmd = STRIPE_NONE; - sh->raid_conf = raid_conf; + sh->raid_conf = conf; sh->sector = sector; sh->size = size; - raid_conf->nr_cached_stripes++; - insert_hash(raid_conf, sh); + conf->nr_cached_stripes++; + insert_hash(conf, sh); } else printk("raid5: bug: kmalloc_stripe() == NULL\n"); return sh; } -static struct stripe_head *get_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +static struct stripe_head *get_stripe(raid5_conf_t *conf, unsigned long sector, int size) { struct stripe_head *sh; PRINTK(("get_stripe, sector %lu\n", sector)); - sh = find_stripe(raid_conf, sector, size); + sh = find_stripe(conf, sector, size); if (sh) wait_on_stripe(sh); else - sh = kmalloc_stripe(raid_conf, sector, size); + sh = kmalloc_stripe(conf, sector, size); return sh; } @@ -523,7 +529,7 @@ static inline void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptod bh->b_end_io(bh, uptodate); if (!uptodate) printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for " - "block %lu\n", kdevname(bh->b_dev), bh->b_blocknr); + "block %lu\n", partition_name(bh->b_dev), bh->b_blocknr); } static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate) @@ -537,36 +543,35 @@ static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptod static void raid5_end_request (struct buffer_head * bh, int uptodate) { struct stripe_head *sh = bh->b_dev_id; - struct raid5_data *raid_conf = sh->raid_conf; - int disks = raid_conf->raid_disks, i; + raid5_conf_t *conf = sh->raid_conf; + int disks = conf->raid_disks, i; unsigned long flags; PRINTK(("end_request %lu, nr_pending %d\n", sh->sector, sh->nr_pending)); - save_flags(flags); - cli(); + md_spin_lock_irqsave(&sh->stripe_lock, flags); raid5_mark_buffer_uptodate(bh, uptodate); - --sh->nr_pending; - if (!sh->nr_pending) { - md_wakeup_thread(raid_conf->thread); - atomic_inc(&raid_conf->nr_handle); + if (atomic_dec_and_test(&sh->nr_pending)) { + md_wakeup_thread(conf->thread); + atomic_inc(&conf->nr_handle); } - if (!uptodate) + if (!uptodate) { md_error(bh->b_dev, bh->b_rdev); - if (raid_conf->failed_disks) { + } + if (conf->failed_disks) { for (i = 0; i < disks; i++) { - if (raid_conf->disks[i].operational) + if (conf->disks[i].operational) continue; if (bh != sh->bh_old[i] && bh != sh->bh_req[i] && bh != sh->bh_copy[i]) continue; - if (bh->b_rdev != raid_conf->disks[i].dev) + if (bh->b_rdev != conf->disks[i].dev) continue; set_bit(STRIPE_ERROR, &sh->state); } } - restore_flags(flags); + md_spin_unlock_irqrestore(&sh->stripe_lock, flags); } -static int raid5_map (struct md_dev *mddev, kdev_t *rdev, +static int raid5_map (mddev_t *mddev, kdev_t dev, kdev_t *rdev, unsigned long *rsector, unsigned long size) { /* No complex mapping used: the core of the work is done in the @@ -577,11 +582,10 @@ static int raid5_map (struct md_dev *mddev, kdev_t *rdev, static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i) { - struct raid5_data *raid_conf = sh->raid_conf; - struct md_dev *mddev = raid_conf->mddev; - int minor = (int) (mddev - md_dev); + raid5_conf_t *conf = sh->raid_conf; + mddev_t *mddev = conf->mddev; char *b_data; - kdev_t dev = MKDEV(MD_MAJOR, minor); + kdev_t dev = mddev_to_kdev(mddev); int block = sh->sector / (sh->size >> 9); b_data = ((volatile struct buffer_head *) bh)->b_data; @@ -589,7 +593,7 @@ static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, i init_buffer(bh, dev, block, raid5_end_request, sh); ((volatile struct buffer_head *) bh)->b_data = b_data; - bh->b_rdev = raid_conf->disks[i].dev; + bh->b_rdev = conf->disks[i].dev; bh->b_rsector = sh->sector; bh->b_state = (1 << BH_Req); @@ -597,33 +601,62 @@ static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, i bh->b_list = BUF_LOCKED; } -static int raid5_error (struct md_dev *mddev, kdev_t dev) +static int raid5_error (mddev_t *mddev, kdev_t dev) { - struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; - md_superblock_t *sb = mddev->sb; + raid5_conf_t *conf = (raid5_conf_t *) mddev->private; + mdp_super_t *sb = mddev->sb; struct disk_info *disk; int i; PRINTK(("raid5_error called\n")); - raid_conf->resync_parity = 0; - for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++) + conf->resync_parity = 0; + for (i = 0, disk = conf->disks; i < conf->raid_disks; i++, disk++) { if (disk->dev == dev && disk->operational) { disk->operational = 0; - sb->disks[disk->number].state |= (1 << MD_FAULTY_DEVICE); - sb->disks[disk->number].state &= ~(1 << MD_SYNC_DEVICE); - sb->disks[disk->number].state &= ~(1 << MD_ACTIVE_DEVICE); + mark_disk_faulty(sb->disks+disk->number); + mark_disk_nonsync(sb->disks+disk->number); + mark_disk_inactive(sb->disks+disk->number); sb->active_disks--; sb->working_disks--; sb->failed_disks++; mddev->sb_dirty = 1; - raid_conf->working_disks--; - raid_conf->failed_disks++; - md_wakeup_thread(raid_conf->thread); + conf->working_disks--; + conf->failed_disks++; + md_wakeup_thread(conf->thread); printk (KERN_ALERT - "RAID5: Disk failure on %s, disabling device." - "Operation continuing on %d devices\n", - kdevname (dev), raid_conf->working_disks); + "raid5: Disk failure on %s, disabling device." + " Operation continuing on %d devices\n", + partition_name (dev), conf->working_disks); + return -EIO; + } + } + /* + * handle errors in spares (during reconstruction) + */ + if (conf->spare) { + disk = conf->spare; + if (disk->dev == dev) { + printk (KERN_ALERT + "raid5: Disk failure on spare %s\n", + partition_name (dev)); + if (!conf->spare->operational) { + MD_BUG(); + return -EIO; + } + disk->operational = 0; + disk->write_only = 0; + conf->spare = NULL; + mark_disk_faulty(sb->disks+disk->number); + mark_disk_nonsync(sb->disks+disk->number); + mark_disk_inactive(sb->disks+disk->number); + sb->spare_disks--; + sb->working_disks--; + sb->failed_disks++; + + return -EIO; } + } + MD_BUG(); return 0; } @@ -634,12 +667,12 @@ static int raid5_error (struct md_dev *mddev, kdev_t dev) static inline unsigned long raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_disks, unsigned int * dd_idx, unsigned int * pd_idx, - struct raid5_data *raid_conf) + raid5_conf_t *conf) { unsigned int stripe; int chunk_number, chunk_offset; unsigned long new_sector; - int sectors_per_chunk = raid_conf->chunk_size >> 9; + int sectors_per_chunk = conf->chunk_size >> 9; /* First compute the information on this sector */ @@ -662,9 +695,9 @@ raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_d /* * Select the parity disk based on the user selected algorithm. */ - if (raid_conf->level == 4) + if (conf->level == 4) *pd_idx = data_disks; - else switch (raid_conf->algorithm) { + else switch (conf->algorithm) { case ALGORITHM_LEFT_ASYMMETRIC: *pd_idx = data_disks - stripe % raid_disks; if (*dd_idx >= *pd_idx) @@ -684,7 +717,7 @@ raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_d *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks; break; default: - printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm); + printk ("raid5: unsupported algorithm %d\n", conf->algorithm); } /* @@ -705,16 +738,16 @@ raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_d static unsigned long compute_blocknr(struct stripe_head *sh, int i) { - struct raid5_data *raid_conf = sh->raid_conf; - int raid_disks = raid_conf->raid_disks, data_disks = raid_disks - 1; + raid5_conf_t *conf = sh->raid_conf; + int raid_disks = conf->raid_disks, data_disks = raid_disks - 1; unsigned long new_sector = sh->sector, check; - int sectors_per_chunk = raid_conf->chunk_size >> 9; + int sectors_per_chunk = conf->chunk_size >> 9; unsigned long stripe = new_sector / sectors_per_chunk; int chunk_offset = new_sector % sectors_per_chunk; int chunk_number, dummy1, dummy2, dd_idx = i; unsigned long r_sector, blocknr; - switch (raid_conf->algorithm) { + switch (conf->algorithm) { case ALGORITHM_LEFT_ASYMMETRIC: case ALGORITHM_RIGHT_ASYMMETRIC: if (i > sh->pd_idx) @@ -727,14 +760,14 @@ static unsigned long compute_blocknr(struct stripe_head *sh, int i) i -= (sh->pd_idx + 1); break; default: - printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm); + printk ("raid5: unsupported algorithm %d\n", conf->algorithm); } chunk_number = stripe * data_disks + i; r_sector = chunk_number * sectors_per_chunk + chunk_offset; blocknr = r_sector / (sh->size >> 9); - check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, raid_conf); + check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, conf); if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) { printk("compute_blocknr: map not correct\n"); return 0; @@ -742,36 +775,11 @@ static unsigned long compute_blocknr(struct stripe_head *sh, int i) return blocknr; } -#ifdef HAVE_ARCH_XORBLOCK -static void xor_block(struct buffer_head *dest, struct buffer_head *source) -{ - __xor_block((char *) dest->b_data, (char *) source->b_data, dest->b_size); -} -#else -static void xor_block(struct buffer_head *dest, struct buffer_head *source) -{ - long lines = dest->b_size / (sizeof (long)) / 8, i; - long *destp = (long *) dest->b_data, *sourcep = (long *) source->b_data; - - for (i = lines; i > 0; i--) { - *(destp + 0) ^= *(sourcep + 0); - *(destp + 1) ^= *(sourcep + 1); - *(destp + 2) ^= *(sourcep + 2); - *(destp + 3) ^= *(sourcep + 3); - *(destp + 4) ^= *(sourcep + 4); - *(destp + 5) ^= *(sourcep + 5); - *(destp + 6) ^= *(sourcep + 6); - *(destp + 7) ^= *(sourcep + 7); - destp += 8; - sourcep += 8; - } -} -#endif - static void compute_block(struct stripe_head *sh, int dd_idx) { - struct raid5_data *raid_conf = sh->raid_conf; - int i, disks = raid_conf->raid_disks; + raid5_conf_t *conf = sh->raid_conf; + int i, count, disks = conf->raid_disks; + struct buffer_head *bh_ptr[MAX_XOR_BLOCKS]; PRINTK(("compute_block, stripe %lu, idx %d\n", sh->sector, dd_idx)); @@ -780,69 +788,100 @@ static void compute_block(struct stripe_head *sh, int dd_idx) raid5_build_block(sh, sh->bh_old[dd_idx], dd_idx); memset(sh->bh_old[dd_idx]->b_data, 0, sh->size); + bh_ptr[0] = sh->bh_old[dd_idx]; + count = 1; for (i = 0; i < disks; i++) { if (i == dd_idx) continue; if (sh->bh_old[i]) { - xor_block(sh->bh_old[dd_idx], sh->bh_old[i]); - continue; - } else + bh_ptr[count++] = sh->bh_old[i]; + } else { printk("compute_block() %d, stripe %lu, %d not present\n", dd_idx, sh->sector, i); + } + if (count == MAX_XOR_BLOCKS) { + xor_block(count, &bh_ptr[0]); + count = 1; + } + } + if(count != 1) { + xor_block(count, &bh_ptr[0]); } raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1); } static void compute_parity(struct stripe_head *sh, int method) { - struct raid5_data *raid_conf = sh->raid_conf; - int i, pd_idx = sh->pd_idx, disks = raid_conf->raid_disks; + raid5_conf_t *conf = sh->raid_conf; + int i, pd_idx = sh->pd_idx, disks = conf->raid_disks, lowprio, count; + struct buffer_head *bh_ptr[MAX_XOR_BLOCKS]; PRINTK(("compute_parity, stripe %lu, method %d\n", sh->sector, method)); + lowprio = 1; for (i = 0; i < disks; i++) { if (i == pd_idx || !sh->bh_new[i]) continue; if (!sh->bh_copy[i]) sh->bh_copy[i] = raid5_kmalloc_buffer(sh, sh->size); raid5_build_block(sh, sh->bh_copy[i], i); + if (!buffer_lowprio(sh->bh_new[i])) + lowprio = 0; + else + mark_buffer_lowprio(sh->bh_copy[i]); mark_buffer_clean(sh->bh_new[i]); memcpy(sh->bh_copy[i]->b_data, sh->bh_new[i]->b_data, sh->size); } if (sh->bh_copy[pd_idx] == NULL) sh->bh_copy[pd_idx] = raid5_kmalloc_buffer(sh, sh->size); raid5_build_block(sh, sh->bh_copy[pd_idx], sh->pd_idx); + if (lowprio) + mark_buffer_lowprio(sh->bh_copy[pd_idx]); if (method == RECONSTRUCT_WRITE) { memset(sh->bh_copy[pd_idx]->b_data, 0, sh->size); + bh_ptr[0] = sh->bh_copy[pd_idx]; + count = 1; for (i = 0; i < disks; i++) { if (i == sh->pd_idx) continue; if (sh->bh_new[i]) { - xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]); - continue; + bh_ptr[count++] = sh->bh_copy[i]; + } else if (sh->bh_old[i]) { + bh_ptr[count++] = sh->bh_old[i]; } - if (sh->bh_old[i]) { - xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]); - continue; + if (count == MAX_XOR_BLOCKS) { + xor_block(count, &bh_ptr[0]); + count = 1; } } + if (count != 1) { + xor_block(count, &bh_ptr[0]); + } } else if (method == READ_MODIFY_WRITE) { memcpy(sh->bh_copy[pd_idx]->b_data, sh->bh_old[pd_idx]->b_data, sh->size); + bh_ptr[0] = sh->bh_copy[pd_idx]; + count = 1; for (i = 0; i < disks; i++) { if (i == sh->pd_idx) continue; if (sh->bh_new[i] && sh->bh_old[i]) { - xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]); - xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]); - continue; + bh_ptr[count++] = sh->bh_copy[i]; + bh_ptr[count++] = sh->bh_old[i]; + } + if (count >= (MAX_XOR_BLOCKS - 1)) { + xor_block(count, &bh_ptr[0]); + count = 1; } } + if (count != 1) { + xor_block(count, &bh_ptr[0]); + } } raid5_mark_buffer_uptodate(sh->bh_copy[pd_idx], 1); } static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int dd_idx, int rw) { - struct raid5_data *raid_conf = sh->raid_conf; + raid5_conf_t *conf = sh->raid_conf; struct buffer_head *bh_req; if (sh->bh_new[dd_idx]) { @@ -860,19 +899,22 @@ static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int d if (sh->phase == PHASE_COMPLETE && sh->cmd == STRIPE_NONE) { sh->phase = PHASE_BEGIN; sh->cmd = (rw == READ) ? STRIPE_READ : STRIPE_WRITE; - raid_conf->nr_pending_stripes++; - atomic_inc(&raid_conf->nr_handle); + conf->nr_pending_stripes++; + atomic_inc(&conf->nr_handle); } sh->bh_new[dd_idx] = bh; sh->bh_req[dd_idx] = bh_req; sh->cmd_new[dd_idx] = rw; sh->new[dd_idx] = 1; + + if (buffer_lowprio(bh)) + mark_buffer_lowprio(bh_req); } static void complete_stripe(struct stripe_head *sh) { - struct raid5_data *raid_conf = sh->raid_conf; - int disks = raid_conf->raid_disks; + raid5_conf_t *conf = sh->raid_conf; + int disks = conf->raid_disks; int i, new = 0; PRINTK(("complete_stripe %lu\n", sh->sector)); @@ -909,6 +951,22 @@ static void complete_stripe(struct stripe_head *sh) } } + +static int is_stripe_lowprio(struct stripe_head *sh, int disks) +{ + int i, lowprio = 1; + + for (i = 0; i < disks; i++) { + if (sh->bh_new[i]) + if (!buffer_lowprio(sh->bh_new[i])) + lowprio = 0; + if (sh->bh_old[i]) + if (!buffer_lowprio(sh->bh_old[i])) + lowprio = 0; + } + return lowprio; +} + /* * handle_stripe() is our main logic routine. Note that: * @@ -919,28 +977,27 @@ static void complete_stripe(struct stripe_head *sh) * 2. We should be careful to set sh->nr_pending whenever we sleep, * to prevent re-entry of handle_stripe() for the same sh. * - * 3. raid_conf->failed_disks and disk->operational can be changed + * 3. conf->failed_disks and disk->operational can be changed * from an interrupt. This complicates things a bit, but it allows * us to stop issuing requests for a failed drive as soon as possible. */ static void handle_stripe(struct stripe_head *sh) { - struct raid5_data *raid_conf = sh->raid_conf; - struct md_dev *mddev = raid_conf->mddev; - int minor = (int) (mddev - md_dev); + raid5_conf_t *conf = sh->raid_conf; + mddev_t *mddev = conf->mddev; struct buffer_head *bh; - int disks = raid_conf->raid_disks; - int i, nr = 0, nr_read = 0, nr_write = 0; + int disks = conf->raid_disks; + int i, nr = 0, nr_read = 0, nr_write = 0, lowprio; int nr_cache = 0, nr_cache_other = 0, nr_cache_overwrite = 0, parity = 0; int nr_failed_other = 0, nr_failed_overwrite = 0, parity_failed = 0; int reading = 0, nr_writing = 0; int method1 = INT_MAX, method2 = INT_MAX; int block; unsigned long flags; - int operational[MD_SB_DISKS], failed_disks = raid_conf->failed_disks; + int operational[MD_SB_DISKS], failed_disks = conf->failed_disks; PRINTK(("handle_stripe(), stripe %lu\n", sh->sector)); - if (sh->nr_pending) { + if (md_atomic_read(&sh->nr_pending)) { printk("handle_stripe(), stripe %lu, io still pending\n", sh->sector); return; } @@ -949,9 +1006,9 @@ static void handle_stripe(struct stripe_head *sh) return; } - atomic_dec(&raid_conf->nr_handle); + atomic_dec(&conf->nr_handle); - if (test_and_clear_bit(STRIPE_ERROR, &sh->state)) { + if (md_test_and_clear_bit(STRIPE_ERROR, &sh->state)) { printk("raid5: restarting stripe %lu\n", sh->sector); sh->phase = PHASE_BEGIN; } @@ -969,11 +1026,11 @@ static void handle_stripe(struct stripe_head *sh) save_flags(flags); cli(); for (i = 0; i < disks; i++) { - operational[i] = raid_conf->disks[i].operational; - if (i == sh->pd_idx && raid_conf->resync_parity) + operational[i] = conf->disks[i].operational; + if (i == sh->pd_idx && conf->resync_parity) operational[i] = 0; } - failed_disks = raid_conf->failed_disks; + failed_disks = conf->failed_disks; restore_flags(flags); if (failed_disks > 1) { @@ -1017,7 +1074,7 @@ static void handle_stripe(struct stripe_head *sh) } if (nr_write && nr_read) - printk("raid5: bug, nr_write == %d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd); + printk("raid5: bug, nr_write ==`%d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd); if (nr_write) { /* @@ -1030,7 +1087,7 @@ static void handle_stripe(struct stripe_head *sh) if (sh->bh_new[i]) continue; block = (int) compute_blocknr(sh, i); - bh = find_buffer(MKDEV(MD_MAJOR, minor), block, sh->size); + bh = find_buffer(mddev_to_kdev(mddev), block, sh->size); if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) { PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block)); add_stripe_bh(sh, bh, i, WRITE); @@ -1064,21 +1121,22 @@ static void handle_stripe(struct stripe_head *sh) if (!method1 || !method2) { lock_stripe(sh); - sh->nr_pending++; + lowprio = is_stripe_lowprio(sh, disks); + atomic_inc(&sh->nr_pending); sh->phase = PHASE_WRITE; compute_parity(sh, method1 <= method2 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE); for (i = 0; i < disks; i++) { - if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity) + if (!operational[i] && !conf->spare && !conf->resync_parity) continue; if (i == sh->pd_idx || sh->bh_new[i]) nr_writing++; } - sh->nr_pending = nr_writing; - PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, sh->nr_pending)); + md_atomic_set(&sh->nr_pending, nr_writing); + PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, md_atomic_read(&sh->nr_pending))); for (i = 0; i < disks; i++) { - if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity) + if (!operational[i] && !conf->spare && !conf->resync_parity) continue; bh = sh->bh_copy[i]; if (i != sh->pd_idx && ((bh == NULL) ^ (sh->bh_new[i] == NULL))) @@ -1089,18 +1147,30 @@ static void handle_stripe(struct stripe_head *sh) bh->b_state |= (1<b_state); - if (!operational[i] && !raid_conf->resync_parity) { - bh->b_rdev = raid_conf->spare->dev; - make_request(MAJOR(raid_conf->spare->dev), WRITE, bh); - } else - make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh); + if (!operational[i] && !conf->resync_parity) { + bh->b_rdev = conf->spare->dev; + make_request(MAJOR(conf->spare->dev), WRITE, bh); + } else { +#if 0 + make_request(MAJOR(conf->disks[i].dev), WRITE, bh); +#else + if (!lowprio || (i==sh->pd_idx)) + make_request(MAJOR(conf->disks[i].dev), WRITE, bh); + else { + mark_buffer_clean(bh); + raid5_end_request(bh,1); + sh->new[i] = 0; + } +#endif + } } } return; } lock_stripe(sh); - sh->nr_pending++; + lowprio = is_stripe_lowprio(sh, disks); + atomic_inc(&sh->nr_pending); if (method1 < method2) { sh->write_method = RECONSTRUCT_WRITE; for (i = 0; i < disks; i++) { @@ -1110,6 +1180,8 @@ static void handle_stripe(struct stripe_head *sh) continue; sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size); raid5_build_block(sh, sh->bh_old[i], i); + if (lowprio) + mark_buffer_lowprio(sh->bh_old[i]); reading++; } } else { @@ -1121,19 +1193,21 @@ static void handle_stripe(struct stripe_head *sh) continue; sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size); raid5_build_block(sh, sh->bh_old[i], i); + if (lowprio) + mark_buffer_lowprio(sh->bh_old[i]); reading++; } } sh->phase = PHASE_READ_OLD; - sh->nr_pending = reading; - PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending)); + md_atomic_set(&sh->nr_pending, reading); + PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, md_atomic_read(&sh->nr_pending))); for (i = 0; i < disks; i++) { if (!sh->bh_old[i]) continue; if (buffer_uptodate(sh->bh_old[i])) continue; clear_bit(BH_Lock, &sh->bh_old[i]->b_state); - make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + make_request(MAJOR(conf->disks[i].dev), READ, sh->bh_old[i]); } } else { /* @@ -1141,7 +1215,8 @@ static void handle_stripe(struct stripe_head *sh) */ method1 = nr_read - nr_cache_overwrite; lock_stripe(sh); - sh->nr_pending++; + lowprio = is_stripe_lowprio(sh,disks); + atomic_inc(&sh->nr_pending); PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1)); if (!method1 || (method1 == 1 && nr_cache == disks - 1)) { @@ -1149,18 +1224,22 @@ static void handle_stripe(struct stripe_head *sh) for (i = 0; i < disks; i++) { if (!sh->bh_new[i]) continue; - if (!sh->bh_old[i]) + if (!sh->bh_old[i]) { compute_block(sh, i); + if (lowprio) + mark_buffer_lowprio + (sh->bh_old[i]); + } memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); } - sh->nr_pending--; + atomic_dec(&sh->nr_pending); complete_stripe(sh); return; } if (nr_failed_overwrite) { sh->phase = PHASE_READ_OLD; - sh->nr_pending = (disks - 1) - nr_cache; - PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending)); + md_atomic_set(&sh->nr_pending, (disks - 1) - nr_cache); + PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, md_atomic_read(&sh->nr_pending))); for (i = 0; i < disks; i++) { if (sh->bh_old[i]) continue; @@ -1168,13 +1247,16 @@ static void handle_stripe(struct stripe_head *sh) continue; sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size); raid5_build_block(sh, sh->bh_old[i], i); + if (lowprio) + mark_buffer_lowprio(sh->bh_old[i]); clear_bit(BH_Lock, &sh->bh_old[i]->b_state); - make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + make_request(MAJOR(conf->disks[i].dev), READ, sh->bh_old[i]); } } else { sh->phase = PHASE_READ; - sh->nr_pending = nr_read - nr_cache_overwrite; - PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending)); + md_atomic_set(&sh->nr_pending, + nr_read - nr_cache_overwrite); + PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, md_atomic_read(&sh->nr_pending))); for (i = 0; i < disks; i++) { if (!sh->bh_new[i]) continue; @@ -1182,16 +1264,16 @@ static void handle_stripe(struct stripe_head *sh) memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); continue; } - make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_req[i]); + make_request(MAJOR(conf->disks[i].dev), READ, sh->bh_req[i]); } } } } -static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) +static int raid5_make_request (mddev_t *mddev, int rw, struct buffer_head * bh) { - struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; - const unsigned int raid_disks = raid_conf->raid_disks; + raid5_conf_t *conf = (raid5_conf_t *) mddev->private; + const unsigned int raid_disks = conf->raid_disks; const unsigned int data_disks = raid_disks - 1; unsigned int dd_idx, pd_idx; unsigned long new_sector; @@ -1202,15 +1284,15 @@ static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head if (rw == WRITEA) rw = WRITE; new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks, - &dd_idx, &pd_idx, raid_conf); + &dd_idx, &pd_idx, conf); PRINTK(("raid5_make_request, sector %lu\n", new_sector)); repeat: - sh = get_stripe(raid_conf, new_sector, bh->b_size); + sh = get_stripe(conf, new_sector, bh->b_size); if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) { PRINTK(("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd)); lock_stripe(sh); - if (!sh->nr_pending) + if (!md_atomic_read(&sh->nr_pending)) handle_stripe(sh); goto repeat; } @@ -1221,24 +1303,24 @@ repeat: printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector); printk("raid5: bh %p, bh_new %p\n", bh, sh->bh_new[dd_idx]); lock_stripe(sh); - md_wakeup_thread(raid_conf->thread); + md_wakeup_thread(conf->thread); wait_on_stripe(sh); goto repeat; } add_stripe_bh(sh, bh, dd_idx, rw); - md_wakeup_thread(raid_conf->thread); + md_wakeup_thread(conf->thread); return 0; } static void unplug_devices(struct stripe_head *sh) { #if 0 - struct raid5_data *raid_conf = sh->raid_conf; + raid5_conf_t *conf = sh->raid_conf; int i; - for (i = 0; i < raid_conf->raid_disks; i++) - unplug_device(blk_dev + MAJOR(raid_conf->disks[i].dev)); + for (i = 0; i < conf->raid_disks; i++) + unplug_device(blk_dev + MAJOR(conf->disks[i].dev)); #endif } @@ -1252,8 +1334,8 @@ static void unplug_devices(struct stripe_head *sh) static void raid5d (void *data) { struct stripe_head *sh; - struct raid5_data *raid_conf = data; - struct md_dev *mddev = raid_conf->mddev; + raid5_conf_t *conf = data; + mddev_t *mddev = conf->mddev; int i, handled = 0, unplug = 0; unsigned long flags; @@ -1261,47 +1343,47 @@ static void raid5d (void *data) if (mddev->sb_dirty) { mddev->sb_dirty = 0; - md_update_sb((int) (mddev - md_dev)); + md_update_sb(mddev); } for (i = 0; i < NR_HASH; i++) { repeat: - sh = raid_conf->stripe_hashtbl[i]; + sh = conf->stripe_hashtbl[i]; for (; sh; sh = sh->hash_next) { - if (sh->raid_conf != raid_conf) + if (sh->raid_conf != conf) continue; if (sh->phase == PHASE_COMPLETE) continue; - if (sh->nr_pending) + if (md_atomic_read(&sh->nr_pending)) continue; - if (sh->sector == raid_conf->next_sector) { - raid_conf->sector_count += (sh->size >> 9); - if (raid_conf->sector_count >= 128) + if (sh->sector == conf->next_sector) { + conf->sector_count += (sh->size >> 9); + if (conf->sector_count >= 128) unplug = 1; } else unplug = 1; if (unplug) { - PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count)); + PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, conf->sector_count)); unplug_devices(sh); unplug = 0; - raid_conf->sector_count = 0; + conf->sector_count = 0; } - raid_conf->next_sector = sh->sector + (sh->size >> 9); + conf->next_sector = sh->sector + (sh->size >> 9); handled++; handle_stripe(sh); goto repeat; } } - if (raid_conf) { - PRINTK(("%d stripes handled, nr_handle %d\n", handled, atomic_read(&raid_conf->nr_handle))); + if (conf) { + PRINTK(("%d stripes handled, nr_handle %d\n", handled, md_atomic_read(&conf->nr_handle))); save_flags(flags); cli(); - if (!atomic_read(&raid_conf->nr_handle)) - clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags); + if (!md_atomic_read(&conf->nr_handle)) + clear_bit(THREAD_WAKEUP, &conf->thread->flags); + restore_flags(flags); } PRINTK(("--- raid5d inactive\n")); } -#if SUPPORT_RECONSTRUCTION /* * Private kernel thread for parity reconstruction after an unclean * shutdown. Reconstruction on spare drives in case of a failed drive @@ -1309,44 +1391,64 @@ repeat: */ static void raid5syncd (void *data) { - struct raid5_data *raid_conf = data; - struct md_dev *mddev = raid_conf->mddev; + raid5_conf_t *conf = data; + mddev_t *mddev = conf->mddev; - if (!raid_conf->resync_parity) + if (!conf->resync_parity) + return; + if (conf->resync_parity == 2) return; - md_do_sync(mddev); - raid_conf->resync_parity = 0; + down(&mddev->recovery_sem); + if (md_do_sync(mddev,NULL)) { + up(&mddev->recovery_sem); + printk("raid5: resync aborted!\n"); + return; + } + conf->resync_parity = 0; + up(&mddev->recovery_sem); + printk("raid5: resync finished.\n"); } -#endif /* SUPPORT_RECONSTRUCTION */ -static int __check_consistency (struct md_dev *mddev, int row) +static int __check_consistency (mddev_t *mddev, int row) { - struct raid5_data *raid_conf = mddev->private; + raid5_conf_t *conf = mddev->private; kdev_t dev; struct buffer_head *bh[MD_SB_DISKS], tmp; - int i, rc = 0, nr = 0; + int i, rc = 0, nr = 0, count; + struct buffer_head *bh_ptr[MAX_XOR_BLOCKS]; - if (raid_conf->working_disks != raid_conf->raid_disks) + if (conf->working_disks != conf->raid_disks) return 0; tmp.b_size = 4096; if ((tmp.b_data = (char *) get_free_page(GFP_KERNEL)) == NULL) return 0; + md_clear_page((unsigned long)tmp.b_data); memset(bh, 0, MD_SB_DISKS * sizeof(struct buffer_head *)); - for (i = 0; i < raid_conf->raid_disks; i++) { - dev = raid_conf->disks[i].dev; + for (i = 0; i < conf->raid_disks; i++) { + dev = conf->disks[i].dev; set_blocksize(dev, 4096); if ((bh[i] = bread(dev, row / 4, 4096)) == NULL) break; nr++; } - if (nr == raid_conf->raid_disks) { - for (i = 1; i < nr; i++) - xor_block(&tmp, bh[i]); + if (nr == conf->raid_disks) { + bh_ptr[0] = &tmp; + count = 1; + for (i = 1; i < nr; i++) { + bh_ptr[count++] = bh[i]; + if (count == MAX_XOR_BLOCKS) { + xor_block(count, &bh_ptr[0]); + count = 1; + } + } + if (count != 1) { + xor_block(count, &bh_ptr[0]); + } if (memcmp(tmp.b_data, bh[0]->b_data, 4096)) rc = 1; } - for (i = 0; i < raid_conf->raid_disks; i++) { - dev = raid_conf->disks[i].dev; + for (i = 0; i < conf->raid_disks; i++) { + dev = conf->disks[i].dev; if (bh[i]) { bforget(bh[i]); bh[i] = NULL; @@ -1358,285 +1460,607 @@ static int __check_consistency (struct md_dev *mddev, int row) return rc; } -static int check_consistency (struct md_dev *mddev) +static int check_consistency (mddev_t *mddev) { - int size = mddev->sb->size; - int row; + if (__check_consistency(mddev, 0)) +/* + * We are not checking this currently, as it's legitimate to have + * an inconsistent array, at creation time. + */ + return 0; - for (row = 0; row < size; row += size / 8) - if (__check_consistency(mddev, row)) - return 1; return 0; } -static int raid5_run (int minor, struct md_dev *mddev) +static int raid5_run (mddev_t *mddev) { - struct raid5_data *raid_conf; + raid5_conf_t *conf; int i, j, raid_disk, memory; - md_superblock_t *sb = mddev->sb; - md_descriptor_t *descriptor; - struct real_dev *realdev; + mdp_super_t *sb = mddev->sb; + mdp_disk_t *desc; + mdk_rdev_t *rdev; + struct disk_info *disk; + struct md_list_head *tmp; + int start_recovery = 0; MOD_INC_USE_COUNT; if (sb->level != 5 && sb->level != 4) { - printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + printk("raid5: md%d: raid level not set to 4/5 (%d)\n", mdidx(mddev), sb->level); MOD_DEC_USE_COUNT; return -EIO; } - mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL); - if ((raid_conf = mddev->private) == NULL) + mddev->private = kmalloc (sizeof (raid5_conf_t), GFP_KERNEL); + if ((conf = mddev->private) == NULL) goto abort; - memset (raid_conf, 0, sizeof (*raid_conf)); - raid_conf->mddev = mddev; + memset (conf, 0, sizeof (*conf)); + conf->mddev = mddev; - if ((raid_conf->stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER)) == NULL) + if ((conf->stripe_hashtbl = (struct stripe_head **) md__get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER)) == NULL) goto abort; - memset(raid_conf->stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE); + memset(conf->stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE); - init_waitqueue(&raid_conf->wait_for_stripe); - PRINTK(("raid5_run(%d) called.\n", minor)); - - for (i = 0; i < mddev->nb_dev; i++) { - realdev = &mddev->devices[i]; - if (!realdev->sb) { - printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); - continue; - } + init_waitqueue(&conf->wait_for_stripe); + PRINTK(("raid5_run(md%d) called.\n", mdidx(mddev))); + ITERATE_RDEV(mddev,rdev,tmp) { /* * This is important -- we are using the descriptor on * the disk only to get a pointer to the descriptor on * the main superblock, which might be more recent. */ - descriptor = &sb->disks[realdev->sb->descriptor.number]; - if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { - printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev)); + desc = sb->disks + rdev->desc_nr; + raid_disk = desc->raid_disk; + disk = conf->disks + raid_disk; + + if (disk_faulty(desc)) { + printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", partition_name(rdev->dev)); + if (!rdev->faulty) { + MD_BUG(); + goto abort; + } + disk->number = desc->number; + disk->raid_disk = raid_disk; + disk->dev = rdev->dev; + + disk->operational = 0; + disk->write_only = 0; + disk->spare = 0; + disk->used_slot = 1; continue; } - if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { - if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { - printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev)); - continue; + if (disk_active(desc)) { + if (!disk_sync(desc)) { + printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", partition_name(rdev->dev)); + MD_BUG(); + goto abort; } - raid_disk = descriptor->raid_disk; - if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { - printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + if (raid_disk > sb->raid_disks) { + printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", partition_name(rdev->dev)); continue; } - if (raid_conf->disks[raid_disk].operational) { - printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk); + if (disk->operational) { + printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", partition_name(rdev->dev), raid_disk); continue; } - printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk); + printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", partition_name(rdev->dev), raid_disk); - raid_conf->disks[raid_disk].number = descriptor->number; - raid_conf->disks[raid_disk].raid_disk = raid_disk; - raid_conf->disks[raid_disk].dev = mddev->devices[i].dev; - raid_conf->disks[raid_disk].operational = 1; + disk->number = desc->number; + disk->raid_disk = raid_disk; + disk->dev = rdev->dev; + disk->operational = 1; + disk->used_slot = 1; - raid_conf->working_disks++; + conf->working_disks++; } else { /* * Must be a spare disk .. */ - printk(KERN_INFO "raid5: spare disk %s\n", kdevname(realdev->dev)); - raid_disk = descriptor->raid_disk; - raid_conf->disks[raid_disk].number = descriptor->number; - raid_conf->disks[raid_disk].raid_disk = raid_disk; - raid_conf->disks[raid_disk].dev = mddev->devices [i].dev; - - raid_conf->disks[raid_disk].operational = 0; - raid_conf->disks[raid_disk].write_only = 0; - raid_conf->disks[raid_disk].spare = 1; + printk(KERN_INFO "raid5: spare disk %s\n", partition_name(rdev->dev)); + disk->number = desc->number; + disk->raid_disk = raid_disk; + disk->dev = rdev->dev; + + disk->operational = 0; + disk->write_only = 0; + disk->spare = 1; + disk->used_slot = 1; } } - raid_conf->raid_disks = sb->raid_disks; - raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks; - raid_conf->mddev = mddev; - raid_conf->chunk_size = sb->chunk_size; - raid_conf->level = sb->level; - raid_conf->algorithm = sb->parity_algorithm; - raid_conf->max_nr_stripes = NR_STRIPES; - if (raid_conf->working_disks != sb->raid_disks && sb->state != (1 << MD_SB_CLEAN)) { - printk(KERN_ALERT "raid5: raid set %s not clean and not all disks are operational -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor))); - goto abort; + for (i = 0; i < MD_SB_DISKS; i++) { + desc = sb->disks + i; + raid_disk = desc->raid_disk; + disk = conf->disks + raid_disk; + + if (disk_faulty(desc) && (raid_disk < sb->raid_disks) && + !conf->disks[raid_disk].used_slot) { + + disk->number = desc->number; + disk->raid_disk = raid_disk; + disk->dev = MKDEV(0,0); + + disk->operational = 0; + disk->write_only = 0; + disk->spare = 0; + disk->used_slot = 1; + } } - if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) { - printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor))); + + conf->raid_disks = sb->raid_disks; + /* + * 0 for a fully functional array, 1 for a degraded array. + */ + conf->failed_disks = conf->raid_disks - conf->working_disks; + conf->mddev = mddev; + conf->chunk_size = sb->chunk_size; + conf->level = sb->level; + conf->algorithm = sb->layout; + conf->max_nr_stripes = NR_STRIPES; + +#if 0 + for (i = 0; i < conf->raid_disks; i++) { + if (!conf->disks[i].used_slot) { + MD_BUG(); + goto abort; + } + } +#endif + if (!conf->chunk_size || conf->chunk_size % 4) { + printk(KERN_ERR "raid5: invalid chunk size %d for md%d\n", conf->chunk_size, mdidx(mddev)); goto abort; } - if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) { - printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor))); + if (conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) { + printk(KERN_ERR "raid5: unsupported parity algorithm %d for md%d\n", conf->algorithm, mdidx(mddev)); goto abort; } - if (raid_conf->failed_disks > 1) { - printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks); + if (conf->failed_disks > 1) { + printk(KERN_ERR "raid5: not enough operational devices for md%d (%d/%d failed)\n", mdidx(mddev), conf->failed_disks, conf->raid_disks); goto abort; } - if ((sb->state & (1 << MD_SB_CLEAN)) && check_consistency(mddev)) { - printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n"); - sb->state |= 1 << MD_SB_ERRORS; - goto abort; + if (conf->working_disks != sb->raid_disks) { + printk(KERN_ALERT "raid5: md%d, not all disks are operational -- trying to recover array\n", mdidx(mddev)); + start_recovery = 1; } - if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) { - printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); - goto abort; + if (!start_recovery && (sb->state & (1 << MD_SB_CLEAN)) && + check_consistency(mddev)) { + printk(KERN_ERR "raid5: detected raid-5 superblock xor inconsistency -- running resync\n"); + sb->state &= ~(1 << MD_SB_CLEAN); } -#if SUPPORT_RECONSTRUCTION - if ((raid_conf->resync_thread = md_register_thread(raid5syncd, raid_conf)) == NULL) { - printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); - goto abort; + { + const char * name = "raid5d"; + + conf->thread = md_register_thread(raid5d, conf, name); + if (!conf->thread) { + printk(KERN_ERR "raid5: couldn't allocate thread for md%d\n", mdidx(mddev)); + goto abort; + } } -#endif /* SUPPORT_RECONSTRUCTION */ - memory = raid_conf->max_nr_stripes * (sizeof(struct stripe_head) + - raid_conf->raid_disks * (sizeof(struct buffer_head) + + memory = conf->max_nr_stripes * (sizeof(struct stripe_head) + + conf->raid_disks * (sizeof(struct buffer_head) + 2 * (sizeof(struct buffer_head) + PAGE_SIZE))) / 1024; - if (grow_stripes(raid_conf, raid_conf->max_nr_stripes, GFP_KERNEL)) { + if (grow_stripes(conf, conf->max_nr_stripes, GFP_KERNEL)) { printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory); - shrink_stripes(raid_conf, raid_conf->max_nr_stripes); + shrink_stripes(conf, conf->max_nr_stripes); goto abort; } else - printk(KERN_INFO "raid5: allocated %dkB for %s\n", memory, kdevname(MKDEV(MD_MAJOR, minor))); + printk(KERN_INFO "raid5: allocated %dkB for md%d\n", memory, mdidx(mddev)); /* * Regenerate the "device is in sync with the raid set" bit for * each device. */ - for (i = 0; i < sb->nr_disks ; i++) { - sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (i = 0; i < MD_SB_DISKS ; i++) { + mark_disk_nonsync(sb->disks + i); for (j = 0; j < sb->raid_disks; j++) { - if (!raid_conf->disks[j].operational) + if (!conf->disks[j].operational) continue; - if (sb->disks[i].number == raid_conf->disks[j].number) - sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + if (sb->disks[i].number == conf->disks[j].number) + mark_disk_sync(sb->disks + i); } } - sb->active_disks = raid_conf->working_disks; + sb->active_disks = conf->working_disks; if (sb->active_disks == sb->raid_disks) - printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + printk("raid5: raid level %d set md%d active with %d out of %d devices, algorithm %d\n", conf->level, mdidx(mddev), sb->active_disks, sb->raid_disks, conf->algorithm); else - printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + printk(KERN_ALERT "raid5: raid level %d set md%d active with %d out of %d devices, algorithm %d\n", conf->level, mdidx(mddev), sb->active_disks, sb->raid_disks, conf->algorithm); + + if (!start_recovery && ((sb->state & (1 << MD_SB_CLEAN))==0)) { + const char * name = "raid5syncd"; - if ((sb->state & (1 << MD_SB_CLEAN)) == 0) { - printk("raid5: raid set %s not clean; re-constructing parity\n", kdevname(MKDEV(MD_MAJOR, minor))); - raid_conf->resync_parity = 1; -#if SUPPORT_RECONSTRUCTION - md_wakeup_thread(raid_conf->resync_thread); -#endif /* SUPPORT_RECONSTRUCTION */ + conf->resync_thread = md_register_thread(raid5syncd, conf,name); + if (!conf->resync_thread) { + printk(KERN_ERR "raid5: couldn't allocate thread for md%d\n", mdidx(mddev)); + goto abort; + } + + printk("raid5: raid set md%d not clean; reconstructing parity\n", mdidx(mddev)); + conf->resync_parity = 1; + md_wakeup_thread(conf->resync_thread); } + print_raid5_conf(conf); + if (start_recovery) + md_recover_arrays(); + print_raid5_conf(conf); + /* Ok, everything is just fine now */ return (0); abort: - if (raid_conf) { - if (raid_conf->stripe_hashtbl) - free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER); - kfree(raid_conf); + if (conf) { + print_raid5_conf(conf); + if (conf->stripe_hashtbl) + free_pages((unsigned long) conf->stripe_hashtbl, + HASH_PAGES_ORDER); + kfree(conf); } mddev->private = NULL; - printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + printk(KERN_ALERT "raid5: failed to run raid set md%d\n", mdidx(mddev)); MOD_DEC_USE_COUNT; return -EIO; } -static int raid5_stop (int minor, struct md_dev *mddev) +static int raid5_stop_resync (mddev_t *mddev) { - struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; - - shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes); - shrink_stripes(raid_conf, raid_conf->max_nr_stripes); - md_unregister_thread(raid_conf->thread); -#if SUPPORT_RECONSTRUCTION - md_unregister_thread(raid_conf->resync_thread); -#endif /* SUPPORT_RECONSTRUCTION */ - free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER); - kfree(raid_conf); + raid5_conf_t *conf = mddev_to_conf(mddev); + mdk_thread_t *thread = conf->resync_thread; + + if (thread) { + if (conf->resync_parity) { + conf->resync_parity = 2; + md_interrupt_thread(thread); + printk(KERN_INFO "raid5: parity resync was not fully finished, restarting next time.\n"); + return 1; + } + return 0; + } + return 0; +} + +static int raid5_restart_resync (mddev_t *mddev) +{ + raid5_conf_t *conf = mddev_to_conf(mddev); + + if (conf->resync_parity) { + if (!conf->resync_thread) { + MD_BUG(); + return 0; + } + printk("raid5: waking up raid5resync.\n"); + conf->resync_parity = 1; + md_wakeup_thread(conf->resync_thread); + return 1; + } else + printk("raid5: no restart-resync needed.\n"); + return 0; +} + + +static int raid5_stop (mddev_t *mddev) +{ + raid5_conf_t *conf = (raid5_conf_t *) mddev->private; + + shrink_stripe_cache(conf, conf->max_nr_stripes); + shrink_stripes(conf, conf->max_nr_stripes); + md_unregister_thread(conf->thread); + if (conf->resync_thread) + md_unregister_thread(conf->resync_thread); + free_pages((unsigned long) conf->stripe_hashtbl, HASH_PAGES_ORDER); + kfree(conf); mddev->private = NULL; MOD_DEC_USE_COUNT; return 0; } -static int raid5_status (char *page, int minor, struct md_dev *mddev) +static int raid5_status (char *page, mddev_t *mddev) { - struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; - md_superblock_t *sb = mddev->sb; + raid5_conf_t *conf = (raid5_conf_t *) mddev->private; + mdp_super_t *sb = mddev->sb; int sz = 0, i; - sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm); - sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); - for (i = 0; i < raid_conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_"); + sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->layout); + sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, conf->working_disks); + for (i = 0; i < conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", conf->disks[i].operational ? "U" : "_"); sz += sprintf (page+sz, "]"); return sz; } -static int raid5_mark_spare(struct md_dev *mddev, md_descriptor_t *spare, int state) +static void print_raid5_conf (raid5_conf_t *conf) { - int i = 0, failed_disk = -1; - struct raid5_data *raid_conf = mddev->private; - struct disk_info *disk = raid_conf->disks; + int i; + struct disk_info *tmp; + + printk("RAID5 conf printout:\n"); + if (!conf) { + printk("(conf==NULL)\n"); + return; + } + printk(" --- rd:%d wd:%d fd:%d\n", conf->raid_disks, + conf->working_disks, conf->failed_disks); + + for (i = 0; i < MD_SB_DISKS; i++) { + tmp = conf->disks + i; + printk(" disk %d, s:%d, o:%d, n:%d rd:%d us:%d dev:%s\n", + i, tmp->spare,tmp->operational, + tmp->number,tmp->raid_disk,tmp->used_slot, + partition_name(tmp->dev)); + } +} + +static int raid5_diskop(mddev_t *mddev, mdp_disk_t **d, int state) +{ + int err = 0; + int i, failed_disk=-1, spare_disk=-1, removed_disk=-1, added_disk=-1; + raid5_conf_t *conf = mddev->private; + struct disk_info *tmp, *sdisk, *fdisk, *rdisk, *adisk; unsigned long flags; - md_superblock_t *sb = mddev->sb; - md_descriptor_t *descriptor; - - for (i = 0; i < MD_SB_DISKS; i++, disk++) { - if (disk->spare && disk->number == spare->number) - goto found; - } - return 1; -found: - for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++) - if (!disk->operational) - failed_disk = i; - if (failed_disk == -1) - return 1; + mdp_super_t *sb = mddev->sb; + mdp_disk_t *failed_desc, *spare_desc, *added_desc; + save_flags(flags); cli(); + + print_raid5_conf(conf); + /* + * find the disk ... + */ switch (state) { - case SPARE_WRITE: - disk->operational = 1; - disk->write_only = 1; - raid_conf->spare = disk; - break; - case SPARE_INACTIVE: - disk->operational = 0; - disk->write_only = 0; - raid_conf->spare = NULL; - break; - case SPARE_ACTIVE: - disk->spare = 0; - disk->write_only = 0; - descriptor = &sb->disks[raid_conf->disks[failed_disk].number]; - i = spare->raid_disk; - disk->raid_disk = spare->raid_disk = descriptor->raid_disk; - if (disk->raid_disk != failed_disk) - printk("raid5: disk->raid_disk != failed_disk"); - descriptor->raid_disk = i; - - raid_conf->spare = NULL; - raid_conf->working_disks++; - raid_conf->failed_disks--; - raid_conf->disks[failed_disk] = *disk; - break; - default: - printk("raid5_mark_spare: bug: state == %d\n", state); - restore_flags(flags); - return 1; + case DISKOP_SPARE_ACTIVE: + + /* + * Find the failed disk within the RAID5 configuration ... + * (this can only be in the first conf->raid_disks part) + */ + for (i = 0; i < conf->raid_disks; i++) { + tmp = conf->disks + i; + if ((!tmp->operational && !tmp->spare) || + !tmp->used_slot) { + failed_disk = i; + break; + } + } + /* + * When we activate a spare disk we _must_ have a disk in + * the lower (active) part of the array to replace. + */ + if ((failed_disk == -1) || (failed_disk >= conf->raid_disks)) { + MD_BUG(); + err = 1; + goto abort; + } + /* fall through */ + + case DISKOP_SPARE_WRITE: + case DISKOP_SPARE_INACTIVE: + + /* + * Find the spare disk ... (can only be in the 'high' + * area of the array) + */ + for (i = conf->raid_disks; i < MD_SB_DISKS; i++) { + tmp = conf->disks + i; + if (tmp->spare && tmp->number == (*d)->number) { + spare_disk = i; + break; + } + } + if (spare_disk == -1) { + MD_BUG(); + err = 1; + goto abort; + } + break; + + case DISKOP_HOT_REMOVE_DISK: + + for (i = 0; i < MD_SB_DISKS; i++) { + tmp = conf->disks + i; + if (tmp->used_slot && (tmp->number == (*d)->number)) { + if (tmp->operational) { + err = -EBUSY; + goto abort; + } + removed_disk = i; + break; + } + } + if (removed_disk == -1) { + MD_BUG(); + err = 1; + goto abort; + } + break; + + case DISKOP_HOT_ADD_DISK: + + for (i = conf->raid_disks; i < MD_SB_DISKS; i++) { + tmp = conf->disks + i; + if (!tmp->used_slot) { + added_disk = i; + break; + } + } + if (added_disk == -1) { + MD_BUG(); + err = 1; + goto abort; + } + break; } + + switch (state) { + /* + * Switch the spare disk to write-only mode: + */ + case DISKOP_SPARE_WRITE: + if (conf->spare) { + MD_BUG(); + err = 1; + goto abort; + } + sdisk = conf->disks + spare_disk; + sdisk->operational = 1; + sdisk->write_only = 1; + conf->spare = sdisk; + break; + /* + * Deactivate a spare disk: + */ + case DISKOP_SPARE_INACTIVE: + sdisk = conf->disks + spare_disk; + sdisk->operational = 0; + sdisk->write_only = 0; + /* + * Was the spare being resynced? + */ + if (conf->spare == sdisk) + conf->spare = NULL; + break; + /* + * Activate (mark read-write) the (now sync) spare disk, + * which means we switch it's 'raid position' (->raid_disk) + * with the failed disk. (only the first 'conf->raid_disks' + * slots are used for 'real' disks and we must preserve this + * property) + */ + case DISKOP_SPARE_ACTIVE: + if (!conf->spare) { + MD_BUG(); + err = 1; + goto abort; + } + sdisk = conf->disks + spare_disk; + fdisk = conf->disks + failed_disk; + + spare_desc = &sb->disks[sdisk->number]; + failed_desc = &sb->disks[fdisk->number]; + + if (spare_desc != *d) { + MD_BUG(); + err = 1; + goto abort; + } + + if (spare_desc->raid_disk != sdisk->raid_disk) { + MD_BUG(); + err = 1; + goto abort; + } + + if (sdisk->raid_disk != spare_disk) { + MD_BUG(); + err = 1; + goto abort; + } + + if (failed_desc->raid_disk != fdisk->raid_disk) { + MD_BUG(); + err = 1; + goto abort; + } + + if (fdisk->raid_disk != failed_disk) { + MD_BUG(); + err = 1; + goto abort; + } + + /* + * do the switch finally + */ + xchg_values(*spare_desc, *failed_desc); + xchg_values(*fdisk, *sdisk); + + /* + * (careful, 'failed' and 'spare' are switched from now on) + * + * we want to preserve linear numbering and we want to + * give the proper raid_disk number to the now activated + * disk. (this means we switch back these values) + */ + + xchg_values(spare_desc->raid_disk, failed_desc->raid_disk); + xchg_values(sdisk->raid_disk, fdisk->raid_disk); + xchg_values(spare_desc->number, failed_desc->number); + xchg_values(sdisk->number, fdisk->number); + + *d = failed_desc; + + if (sdisk->dev == MKDEV(0,0)) + sdisk->used_slot = 0; + + /* + * this really activates the spare. + */ + fdisk->spare = 0; + fdisk->write_only = 0; + + /* + * if we activate a spare, we definitely replace a + * non-operational disk slot in the 'low' area of + * the disk array. + */ + conf->failed_disks--; + conf->working_disks++; + conf->spare = NULL; + + break; + + case DISKOP_HOT_REMOVE_DISK: + rdisk = conf->disks + removed_disk; + + if (rdisk->spare && (removed_disk < conf->raid_disks)) { + MD_BUG(); + err = 1; + goto abort; + } + rdisk->dev = MKDEV(0,0); + rdisk->used_slot = 0; + + break; + + case DISKOP_HOT_ADD_DISK: + adisk = conf->disks + added_disk; + added_desc = *d; + + if (added_disk != added_desc->number) { + MD_BUG(); + err = 1; + goto abort; + } + + adisk->number = added_desc->number; + adisk->raid_disk = added_desc->raid_disk; + adisk->dev = MKDEV(added_desc->major,added_desc->minor); + + adisk->operational = 0; + adisk->write_only = 0; + adisk->spare = 1; + adisk->used_slot = 1; + + + break; + + default: + MD_BUG(); + err = 1; + goto abort; + } +abort: restore_flags(flags); - return 0; + print_raid5_conf(conf); + return err; } -static struct md_personality raid5_personality= +static mdk_personality_t raid5_personality= { "raid5", raid5_map, @@ -1648,14 +2072,19 @@ static struct md_personality raid5_personality= NULL, /* no ioctls */ 0, raid5_error, - /* raid5_hot_add_disk, */ NULL, - /* raid1_hot_remove_drive */ NULL, - raid5_mark_spare + raid5_diskop, + raid5_stop_resync, + raid5_restart_resync }; int raid5_init (void) { - return register_md_personality (RAID5, &raid5_personality); + int err; + + err = register_md_personality (RAID5, &raid5_personality); + if (err) + return err; + return 0; } #ifdef MODULE diff --git a/drivers/block/translucent.c b/drivers/block/translucent.c new file mode 100644 index 000000000000..49d2d88e7c1e --- /dev/null +++ b/drivers/block/translucent.c @@ -0,0 +1,136 @@ +/* + translucent.c : Translucent RAID driver for Linux + Copyright (C) 1998 Ingo Molnar + + Translucent mode management functions. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include +#include + +#include + +#define MAJOR_NR MD_MAJOR +#define MD_DRIVER +#define MD_PERSONALITY + +static int translucent_run (mddev_t *mddev) +{ + translucent_conf_t *conf; + mdk_rdev_t *rdev; + int i; + + MOD_INC_USE_COUNT; + + conf = kmalloc (sizeof (*conf), GFP_KERNEL); + if (!conf) + goto out; + mddev->private = conf; + + if (mddev->nb_dev != 2) { + printk("translucent: this mode needs 2 disks, aborting!\n"); + goto out; + } + + if (md_check_ordering(mddev)) { + printk("translucent: disks are not ordered, aborting!\n"); + goto out; + } + + ITERATE_RDEV_ORDERED(mddev,rdev,i) { + dev_info_t *disk = conf->disks + i; + + disk->dev = rdev->dev; + disk->size = rdev->size; + } + + return 0; + +out: + if (conf) + kfree(conf); + + MOD_DEC_USE_COUNT; + return 1; +} + +static int translucent_stop (mddev_t *mddev) +{ + translucent_conf_t *conf = mddev_to_conf(mddev); + + kfree(conf); + + MOD_DEC_USE_COUNT; + + return 0; +} + + +static int translucent_map (mddev_t *mddev, kdev_t dev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) +{ + translucent_conf_t *conf = mddev_to_conf(mddev); + + *rdev = conf->disks[0].dev; + + return 0; +} + +static int translucent_status (char *page, mddev_t *mddev) +{ + int sz = 0; + + sz += sprintf(page+sz, " %d%% full", 10); + return sz; +} + + +static mdk_personality_t translucent_personality= +{ + "translucent", + translucent_map, + NULL, + NULL, + translucent_run, + translucent_stop, + translucent_status, + NULL, + 0, + NULL, + NULL, + NULL, + NULL +}; + +#ifndef MODULE + +md__initfunc(void translucent_init (void)) +{ + register_md_personality (TRANSLUCENT, &translucent_personality); +} + +#else + +int init_module (void) +{ + return (register_md_personality (TRANSLUCENT, &translucent_personality)); +} + +void cleanup_module (void) +{ + unregister_md_personality (TRANSLUCENT); +} + +#endif + diff --git a/drivers/block/xor.c b/drivers/block/xor.c new file mode 100644 index 000000000000..d52f79ed5299 --- /dev/null +++ b/drivers/block/xor.c @@ -0,0 +1,1480 @@ +/* + * xor.c : Multiple Devices driver for Linux + * + * Copyright (C) 1996, 1997, 1998, 1999 Ingo Molnar, Matti Aarnio, Jakub Jelinek + * + * + * optimized RAID-5 checksumming functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#ifdef __sparc_v9__ +#include +#include +#include +#endif + +/* + * we use the 'XOR function template' to register multiple xor + * functions runtime. The kernel measures their speed upon bootup + * and decides which one to use. (compile-time registration is + * not enough as certain CPU features like MMX can only be detected + * runtime) + * + * this architecture makes it pretty easy to add new routines + * that are faster on certain CPUs, without killing other CPU's + * 'native' routine. Although the current routines are belived + * to be the physically fastest ones on all CPUs tested, but + * feel free to prove me wrong and add yet another routine =B-) + * --mingo + */ + +#define MAX_XOR_BLOCKS 5 + +#define XOR_ARGS (unsigned int count, struct buffer_head **bh_ptr) + +typedef void (*xor_block_t) XOR_ARGS; +xor_block_t xor_block = NULL; + +#ifndef __sparc_v9__ + +struct xor_block_template; + +struct xor_block_template { + char * name; + xor_block_t xor_block; + int speed; + struct xor_block_template * next; +}; + +struct xor_block_template * xor_functions = NULL; + +#define XORBLOCK_TEMPLATE(x) \ +static void xor_block_##x XOR_ARGS; \ +static struct xor_block_template t_xor_block_##x = \ + { #x, xor_block_##x, 0, NULL }; \ +static void xor_block_##x XOR_ARGS + +#ifdef __i386__ + +#ifdef CONFIG_X86_XMM +/* + * Cache avoiding checksumming functions utilizing KNI instructions + * Copyright (C) 1999 Zach Brown (with obvious credit due Ingo) + */ + +XORBLOCK_TEMPLATE(pIII_kni) +{ + char xmm_save[16*4]; + int cr0; + int lines = (bh_ptr[0]->b_size>>8); + + __asm__ __volatile__ ( + "movl %%cr0,%0 ;\n\t" + "clts ;\n\t" + "movups %%xmm0,(%1) ;\n\t" + "movups %%xmm1,0x10(%1) ;\n\t" + "movups %%xmm2,0x20(%1) ;\n\t" + "movups %%xmm3,0x30(%1) ;\n\t" + : "=r" (cr0) + : "r" (xmm_save) + : "memory" ); + +#define OFFS(x) "8*("#x"*2)" +#define PF0(x) \ + " prefetcht0 "OFFS(x)"(%1) ;\n" +#define LD(x,y) \ + " movaps "OFFS(x)"(%1), %%xmm"#y" ;\n" +#define ST(x,y) \ + " movaps %%xmm"#y", "OFFS(x)"(%1) ;\n" +#define PF1(x) \ + " prefetchnta "OFFS(x)"(%2) ;\n" +#define PF2(x) \ + " prefetchnta "OFFS(x)"(%3) ;\n" +#define PF3(x) \ + " prefetchnta "OFFS(x)"(%4) ;\n" +#define PF4(x) \ + " prefetchnta "OFFS(x)"(%5) ;\n" +#define PF5(x) \ + " prefetchnta "OFFS(x)"(%6) ;\n" +#define XO1(x,y) \ + " xorps "OFFS(x)"(%2), %%xmm"#y" ;\n" +#define XO2(x,y) \ + " xorps "OFFS(x)"(%3), %%xmm"#y" ;\n" +#define XO3(x,y) \ + " xorps "OFFS(x)"(%4), %%xmm"#y" ;\n" +#define XO4(x,y) \ + " xorps "OFFS(x)"(%5), %%xmm"#y" ;\n" +#define XO5(x,y) \ + " xorps "OFFS(x)"(%6), %%xmm"#y" ;\n" + + switch(count) { + case 2: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + PF1(i) \ + PF1(i+2) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF0(i+4) \ + PF0(i+6) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $256, %1 ;\n" + " addl $256, %2 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data) + : "memory" ); + break; + case 3: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + PF1(i) \ + PF1(i+2) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF2(i) \ + PF2(i+2) \ + PF0(i+4) \ + PF0(i+6) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $256, %1 ;\n" + " addl $256, %2 ;\n" + " addl $256, %3 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data) + : "memory" ); + break; + case 4: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + PF1(i) \ + PF1(i+2) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF2(i) \ + PF2(i+2) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + PF3(i) \ + PF3(i+2) \ + PF0(i+4) \ + PF0(i+6) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + XO3(i,0) \ + XO3(i+1,1) \ + XO3(i+2,2) \ + XO3(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $256, %1 ;\n" + " addl $256, %2 ;\n" + " addl $256, %3 ;\n" + " addl $256, %4 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data), + "r" (bh_ptr[3]->b_data) + : "memory" ); + break; + case 5: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + PF1(i) \ + PF1(i+2) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF2(i) \ + PF2(i+2) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + PF3(i) \ + PF3(i+2) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + PF4(i) \ + PF4(i+2) \ + PF0(i+4) \ + PF0(i+6) \ + XO3(i,0) \ + XO3(i+1,1) \ + XO3(i+2,2) \ + XO3(i+3,3) \ + XO4(i,0) \ + XO4(i+1,1) \ + XO4(i+2,2) \ + XO4(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $256, %1 ;\n" + " addl $256, %2 ;\n" + " addl $256, %3 ;\n" + " addl $256, %4 ;\n" + " addl $256, %5 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data), + "r" (bh_ptr[3]->b_data), + "r" (bh_ptr[4]->b_data) + : "memory"); + break; + } + + __asm__ __volatile__ ( + "sfence ;\n\t" + "movups (%1),%%xmm0 ;\n\t" + "movups 0x10(%1),%%xmm1 ;\n\t" + "movups 0x20(%1),%%xmm2 ;\n\t" + "movups 0x30(%1),%%xmm3 ;\n\t" + "movl %0,%%cr0 ;\n\t" + : + : "r" (cr0), "r" (xmm_save) + : "memory" ); +} + +#undef OFFS +#undef LD +#undef ST +#undef PF0 +#undef PF1 +#undef PF2 +#undef PF3 +#undef PF4 +#undef PF5 +#undef XO1 +#undef XO2 +#undef XO3 +#undef XO4 +#undef XO5 +#undef BLOCK + +#endif /* CONFIG_X86_XMM */ + +/* + * high-speed RAID5 checksumming functions utilizing MMX instructions + * Copyright (C) 1998 Ingo Molnar + */ +XORBLOCK_TEMPLATE(pII_mmx) +{ + char fpu_save[108]; + int lines = (bh_ptr[0]->b_size>>7); + + if (!(current->flags & PF_USEDFPU)) + __asm__ __volatile__ ( " clts;\n"); + + __asm__ __volatile__ ( " fsave %0; fwait\n"::"m"(fpu_save[0]) ); + +#define LD(x,y) \ + " movq 8*("#x")(%1), %%mm"#y" ;\n" +#define ST(x,y) \ + " movq %%mm"#y", 8*("#x")(%1) ;\n" +#define XO1(x,y) \ + " pxor 8*("#x")(%2), %%mm"#y" ;\n" +#define XO2(x,y) \ + " pxor 8*("#x")(%3), %%mm"#y" ;\n" +#define XO3(x,y) \ + " pxor 8*("#x")(%4), %%mm"#y" ;\n" +#define XO4(x,y) \ + " pxor 8*("#x")(%5), %%mm"#y" ;\n" + + switch(count) { + case 2: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + ST(i,0) \ + XO1(i+1,1) \ + ST(i+1,1) \ + XO1(i+2,2) \ + ST(i+2,2) \ + XO1(i+3,3) \ + ST(i+3,3) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $128, %1 ;\n" + " addl $128, %2 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data) + : "memory"); + break; + case 3: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + ST(i,0) \ + XO2(i+1,1) \ + ST(i+1,1) \ + XO2(i+2,2) \ + ST(i+2,2) \ + XO2(i+3,3) \ + ST(i+3,3) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $128, %1 ;\n" + " addl $128, %2 ;\n" + " addl $128, %3 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data) + : "memory"); + break; + case 4: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + XO3(i,0) \ + ST(i,0) \ + XO3(i+1,1) \ + ST(i+1,1) \ + XO3(i+2,2) \ + ST(i+2,2) \ + XO3(i+3,3) \ + ST(i+3,3) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $128, %1 ;\n" + " addl $128, %2 ;\n" + " addl $128, %3 ;\n" + " addl $128, %4 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data), + "r" (bh_ptr[3]->b_data) + : "memory"); + break; + case 5: + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + XO3(i,0) \ + XO3(i+1,1) \ + XO3(i+2,2) \ + XO3(i+3,3) \ + XO4(i,0) \ + ST(i,0) \ + XO4(i+1,1) \ + ST(i+1,1) \ + XO4(i+2,2) \ + ST(i+2,2) \ + XO4(i+3,3) \ + ST(i+3,3) + + " .align 32,0x90 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addl $128, %1 ;\n" + " addl $128, %2 ;\n" + " addl $128, %3 ;\n" + " addl $128, %4 ;\n" + " addl $128, %5 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data), + "r" (bh_ptr[3]->b_data), + "r" (bh_ptr[4]->b_data) + : "memory"); + break; + } + + __asm__ __volatile__ ( " frstor %0;\n"::"m"(fpu_save[0]) ); + + if (!(current->flags & PF_USEDFPU)) + stts(); +} + +#undef LD +#undef XO1 +#undef XO2 +#undef XO3 +#undef XO4 +#undef ST +#undef BLOCK + +XORBLOCK_TEMPLATE(p5_mmx) +{ + char fpu_save[108]; + int lines = (bh_ptr[0]->b_size>>6); + + if (!(current->flags & PF_USEDFPU)) + __asm__ __volatile__ ( " clts;\n"); + + __asm__ __volatile__ ( " fsave %0; fwait\n"::"m"(fpu_save[0]) ); + + switch(count) { + case 2: + __asm__ __volatile__ ( + + " .align 32,0x90 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " movq 16(%1), %%mm2 ;\n" + " movq %%mm0, (%1) ;\n" + " pxor 8(%2), %%mm1 ;\n" + " movq 24(%1), %%mm3 ;\n" + " movq %%mm1, 8(%1) ;\n" + " pxor 16(%2), %%mm2 ;\n" + " movq 32(%1), %%mm4 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 24(%2), %%mm3 ;\n" + " movq 40(%1), %%mm5 ;\n" + " movq %%mm3, 24(%1) ;\n" + " pxor 32(%2), %%mm4 ;\n" + " movq 48(%1), %%mm6 ;\n" + " movq %%mm4, 32(%1) ;\n" + " pxor 40(%2), %%mm5 ;\n" + " movq 56(%1), %%mm7 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 48(%2), %%mm6 ;\n" + " pxor 56(%2), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addl $64, %1 ;\n" + " addl $64, %2 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data) + : "memory" ); + break; + case 3: + __asm__ __volatile__ ( + + " .align 32,0x90 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " movq 16(%1), %%mm2 ;\n" + " pxor 8(%2), %%mm1 ;\n" + " pxor (%3), %%mm0 ;\n" + " pxor 16(%2), %%mm2 ;\n" + " movq %%mm0, (%1) ;\n" + " pxor 8(%3), %%mm1 ;\n" + " pxor 16(%3), %%mm2 ;\n" + " movq 24(%1), %%mm3 ;\n" + " movq %%mm1, 8(%1) ;\n" + " movq 32(%1), %%mm4 ;\n" + " movq 40(%1), %%mm5 ;\n" + " pxor 24(%2), %%mm3 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 32(%2), %%mm4 ;\n" + " pxor 24(%3), %%mm3 ;\n" + " pxor 40(%2), %%mm5 ;\n" + " movq %%mm3, 24(%1) ;\n" + " pxor 32(%3), %%mm4 ;\n" + " pxor 40(%3), %%mm5 ;\n" + " movq 48(%1), %%mm6 ;\n" + " movq %%mm4, 32(%1) ;\n" + " movq 56(%1), %%mm7 ;\n" + " pxor 48(%2), %%mm6 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 56(%2), %%mm7 ;\n" + " pxor 48(%3), %%mm6 ;\n" + " pxor 56(%3), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addl $64, %1 ;\n" + " addl $64, %2 ;\n" + " addl $64, %3 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data) + : "memory" ); + break; + case 4: + __asm__ __volatile__ ( + + " .align 32,0x90 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " movq 16(%1), %%mm2 ;\n" + " pxor 8(%2), %%mm1 ;\n" + " pxor (%3), %%mm0 ;\n" + " pxor 16(%2), %%mm2 ;\n" + " pxor 8(%3), %%mm1 ;\n" + " pxor (%4), %%mm0 ;\n" + " movq 24(%1), %%mm3 ;\n" + " pxor 16(%3), %%mm2 ;\n" + " pxor 8(%4), %%mm1 ;\n" + " movq %%mm0, (%1) ;\n" + " movq 32(%1), %%mm4 ;\n" + " pxor 24(%2), %%mm3 ;\n" + " pxor 16(%4), %%mm2 ;\n" + " movq %%mm1, 8(%1) ;\n" + " movq 40(%1), %%mm5 ;\n" + " pxor 32(%2), %%mm4 ;\n" + " pxor 24(%3), %%mm3 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 40(%2), %%mm5 ;\n" + " pxor 32(%3), %%mm4 ;\n" + " pxor 24(%4), %%mm3 ;\n" + " movq %%mm3, 24(%1) ;\n" + " movq 56(%1), %%mm7 ;\n" + " movq 48(%1), %%mm6 ;\n" + " pxor 40(%3), %%mm5 ;\n" + " pxor 32(%4), %%mm4 ;\n" + " pxor 48(%2), %%mm6 ;\n" + " movq %%mm4, 32(%1) ;\n" + " pxor 56(%2), %%mm7 ;\n" + " pxor 40(%4), %%mm5 ;\n" + " pxor 48(%3), %%mm6 ;\n" + " pxor 56(%3), %%mm7 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 48(%4), %%mm6 ;\n" + " pxor 56(%4), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addl $64, %1 ;\n" + " addl $64, %2 ;\n" + " addl $64, %3 ;\n" + " addl $64, %4 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data), + "r" (bh_ptr[3]->b_data) + : "memory" ); + break; + case 5: + __asm__ __volatile__ ( + + " .align 32,0x90 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " pxor 8(%2), %%mm1 ;\n" + " movq 16(%1), %%mm2 ;\n" + " pxor (%3), %%mm0 ;\n" + " pxor 8(%3), %%mm1 ;\n" + " pxor 16(%2), %%mm2 ;\n" + " pxor (%4), %%mm0 ;\n" + " pxor 8(%4), %%mm1 ;\n" + " pxor 16(%3), %%mm2 ;\n" + " movq 24(%1), %%mm3 ;\n" + " pxor (%5), %%mm0 ;\n" + " pxor 8(%5), %%mm1 ;\n" + " movq %%mm0, (%1) ;\n" + " pxor 16(%4), %%mm2 ;\n" + " pxor 24(%2), %%mm3 ;\n" + " movq %%mm1, 8(%1) ;\n" + " pxor 16(%5), %%mm2 ;\n" + " pxor 24(%3), %%mm3 ;\n" + " movq 32(%1), %%mm4 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 24(%4), %%mm3 ;\n" + " pxor 32(%2), %%mm4 ;\n" + " movq 40(%1), %%mm5 ;\n" + " pxor 24(%5), %%mm3 ;\n" + " pxor 32(%3), %%mm4 ;\n" + " pxor 40(%2), %%mm5 ;\n" + " movq %%mm3, 24(%1) ;\n" + " pxor 32(%4), %%mm4 ;\n" + " pxor 40(%3), %%mm5 ;\n" + " movq 48(%1), %%mm6 ;\n" + " movq 56(%1), %%mm7 ;\n" + " pxor 32(%5), %%mm4 ;\n" + " pxor 40(%4), %%mm5 ;\n" + " pxor 48(%2), %%mm6 ;\n" + " pxor 56(%2), %%mm7 ;\n" + " movq %%mm4, 32(%1) ;\n" + " pxor 48(%3), %%mm6 ;\n" + " pxor 56(%3), %%mm7 ;\n" + " pxor 40(%5), %%mm5 ;\n" + " pxor 48(%4), %%mm6 ;\n" + " pxor 56(%4), %%mm7 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 48(%5), %%mm6 ;\n" + " pxor 56(%5), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addl $64, %1 ;\n" + " addl $64, %2 ;\n" + " addl $64, %3 ;\n" + " addl $64, %4 ;\n" + " addl $64, %5 ;\n" + " decl %0 ;\n" + " jnz 1b ;\n" + + : + : "r" (lines), + "r" (bh_ptr[0]->b_data), + "r" (bh_ptr[1]->b_data), + "r" (bh_ptr[2]->b_data), + "r" (bh_ptr[3]->b_data), + "r" (bh_ptr[4]->b_data) + : "memory" ); + break; + } + + __asm__ __volatile__ ( " frstor %0;\n"::"m"(fpu_save[0]) ); + + if (!(current->flags & PF_USEDFPU)) + stts(); +} +#endif /* __i386__ */ +#endif /* !__sparc_v9__ */ + +#ifdef __sparc_v9__ +/* + * High speed xor_block operation for RAID4/5 utilizing the + * UltraSparc Visual Instruction Set. + * + * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz) + * + * Requirements: + * !(((long)destp | (long)sourcep) & (64 - 1)) && + * !(len & 127) && len >= 256 + * + * It is done in pure assembly, as otherwise gcc makes it + * a non-leaf function, which is not what we want. + * Also, we don't measure the speeds as on other architectures, + * as the measuring routine does not take into account cold caches + * and the fact that xor_block_VIS bypasses the caches. + * xor_block_32regs might be 5% faster if caches are hot and things + * just right, but I think it is better not to pollute the caches. + * Actually, if I'd just fight for speed for hot caches, I could + * write a hybrid VIS/integer routine, which would do always two + * 64B blocks in VIS and two in IEUs, but I really care more about + * caches. + */ +extern void *VISenter(void); +extern void xor_block_VIS SRCDEST; + +void __xor_block_VIS(void) +{ +__asm__ (" + .globl xor_block_VIS +xor_block_VIS: + ldx [%%o1 + %0], %%o1 + ldx [%%o0 + %1], %%o2 + ldx [%%o0 + %0], %%o0 + rd %%fprs, %%o5 + andcc %%o5, %2, %%g0 + be,pt %%icc, 297f + sethi %%hi(%5), %%g1 + jmpl %%g1 + %%lo(%5), %%g7 + add %%g7, 8, %%g7 +297: wr %%g0, %4, %%fprs + wr %%g0, %3, %%asi + membar #LoadStore|#StoreLoad|#StoreStore + sub %%o2, 128, %%o2 + ldda [%%o0] %3, %%f0 + ldda [%%o1] %3, %%f16 +1: ldda [%%o0 + 64] %%asi, %%f32 + fxor %%f0, %%f16, %%f16 + fxor %%f2, %%f18, %%f18 + fxor %%f4, %%f20, %%f20 + fxor %%f6, %%f22, %%f22 + fxor %%f8, %%f24, %%f24 + fxor %%f10, %%f26, %%f26 + fxor %%f12, %%f28, %%f28 + fxor %%f14, %%f30, %%f30 + stda %%f16, [%%o0] %3 + ldda [%%o1 + 64] %%asi, %%f48 + ldda [%%o0 + 128] %%asi, %%f0 + fxor %%f32, %%f48, %%f48 + fxor %%f34, %%f50, %%f50 + add %%o0, 128, %%o0 + fxor %%f36, %%f52, %%f52 + add %%o1, 128, %%o1 + fxor %%f38, %%f54, %%f54 + subcc %%o2, 128, %%o2 + fxor %%f40, %%f56, %%f56 + fxor %%f42, %%f58, %%f58 + fxor %%f44, %%f60, %%f60 + fxor %%f46, %%f62, %%f62 + stda %%f48, [%%o0 - 64] %%asi + bne,pt %%xcc, 1b + ldda [%%o1] %3, %%f16 + ldda [%%o0 + 64] %%asi, %%f32 + fxor %%f0, %%f16, %%f16 + fxor %%f2, %%f18, %%f18 + fxor %%f4, %%f20, %%f20 + fxor %%f6, %%f22, %%f22 + fxor %%f8, %%f24, %%f24 + fxor %%f10, %%f26, %%f26 + fxor %%f12, %%f28, %%f28 + fxor %%f14, %%f30, %%f30 + stda %%f16, [%%o0] %3 + ldda [%%o1 + 64] %%asi, %%f48 + membar #Sync + fxor %%f32, %%f48, %%f48 + fxor %%f34, %%f50, %%f50 + fxor %%f36, %%f52, %%f52 + fxor %%f38, %%f54, %%f54 + fxor %%f40, %%f56, %%f56 + fxor %%f42, %%f58, %%f58 + fxor %%f44, %%f60, %%f60 + fxor %%f46, %%f62, %%f62 + stda %%f48, [%%o0 + 64] %%asi + membar #Sync|#StoreStore|#StoreLoad + retl + wr %%g0, 0, %%fprs + " : : + "i" (&((struct buffer_head *)0)->b_data), + "i" (&((struct buffer_head *)0)->b_size), + "i" (FPRS_FEF|FPRS_DU), "i" (ASI_BLK_P), + "i" (FPRS_FEF), "i" (VISenter)); +} +#endif /* __sparc_v9__ */ + +#if defined(__sparc__) && !defined(__sparc_v9__) +/* + * High speed xor_block operation for RAID4/5 utilizing the + * ldd/std SPARC instructions. + * + * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) + * + */ + +XORBLOCK_TEMPLATE(SPARC) +{ + int size = dest->b_size; + int lines = size / (sizeof (long)) / 8, i; + long *destp = (long *) dest->b_data; + long *sourcep = (long *) source->b_data; + + for (i = lines; i > 0; i--) { + __asm__ __volatile__(" + ldd [%0 + 0x00], %%g2 + ldd [%0 + 0x08], %%g4 + ldd [%0 + 0x10], %%o0 + ldd [%0 + 0x18], %%o2 + ldd [%1 + 0x00], %%o4 + ldd [%1 + 0x08], %%l0 + ldd [%1 + 0x10], %%l2 + ldd [%1 + 0x18], %%l4 + xor %%g2, %%o4, %%g2 + xor %%g3, %%o5, %%g3 + xor %%g4, %%l0, %%g4 + xor %%g5, %%l1, %%g5 + xor %%o0, %%l2, %%o0 + xor %%o1, %%l3, %%o1 + xor %%o2, %%l4, %%o2 + xor %%o3, %%l5, %%o3 + std %%g2, [%0 + 0x00] + std %%g4, [%0 + 0x08] + std %%o0, [%0 + 0x10] + std %%o2, [%0 + 0x18] + " : : "r" (destp), "r" (sourcep) : "g2", "g3", "g4", "g5", "o0", + "o1", "o2", "o3", "o4", "o5", "l0", "l1", "l2", "l3", "l4", "l5"); + + destp += 8; + sourcep += 8; + } +} +#endif /* __sparc_v[78]__ */ + +#ifndef __sparc_v9__ + +/* + * this one works reasonably on any x86 CPU + * (send me an assembly version for inclusion if you can make it faster) + * + * this one is just as fast as written in pure assembly on x86. + * the reason for this separate version is that the + * fast open-coded xor routine "32reg" produces suboptimal code + * on x86, due to lack of registers. + */ +XORBLOCK_TEMPLATE(8regs) +{ + int len = bh_ptr[0]->b_size; + long *destp = (long *) bh_ptr[0]->b_data; + long *source1, *source2, *source3, *source4; + long lines = len / (sizeof (long)) / 8, i; + + switch(count) { + case 2: + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + *(destp + 0) ^= *(source1 + 0); + *(destp + 1) ^= *(source1 + 1); + *(destp + 2) ^= *(source1 + 2); + *(destp + 3) ^= *(source1 + 3); + *(destp + 4) ^= *(source1 + 4); + *(destp + 5) ^= *(source1 + 5); + *(destp + 6) ^= *(source1 + 6); + *(destp + 7) ^= *(source1 + 7); + source1 += 8; + destp += 8; + } + break; + case 3: + source2 = (long *) bh_ptr[2]->b_data; + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + *(destp + 0) ^= *(source1 + 0); + *(destp + 0) ^= *(source2 + 0); + *(destp + 1) ^= *(source1 + 1); + *(destp + 1) ^= *(source2 + 1); + *(destp + 2) ^= *(source1 + 2); + *(destp + 2) ^= *(source2 + 2); + *(destp + 3) ^= *(source1 + 3); + *(destp + 3) ^= *(source2 + 3); + *(destp + 4) ^= *(source1 + 4); + *(destp + 4) ^= *(source2 + 4); + *(destp + 5) ^= *(source1 + 5); + *(destp + 5) ^= *(source2 + 5); + *(destp + 6) ^= *(source1 + 6); + *(destp + 6) ^= *(source2 + 6); + *(destp + 7) ^= *(source1 + 7); + *(destp + 7) ^= *(source2 + 7); + source1 += 8; + source2 += 8; + destp += 8; + } + break; + case 4: + source3 = (long *) bh_ptr[3]->b_data; + source2 = (long *) bh_ptr[2]->b_data; + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + *(destp + 0) ^= *(source1 + 0); + *(destp + 0) ^= *(source2 + 0); + *(destp + 0) ^= *(source3 + 0); + *(destp + 1) ^= *(source1 + 1); + *(destp + 1) ^= *(source2 + 1); + *(destp + 1) ^= *(source3 + 1); + *(destp + 2) ^= *(source1 + 2); + *(destp + 2) ^= *(source2 + 2); + *(destp + 2) ^= *(source3 + 2); + *(destp + 3) ^= *(source1 + 3); + *(destp + 3) ^= *(source2 + 3); + *(destp + 3) ^= *(source3 + 3); + *(destp + 4) ^= *(source1 + 4); + *(destp + 4) ^= *(source2 + 4); + *(destp + 4) ^= *(source3 + 4); + *(destp + 5) ^= *(source1 + 5); + *(destp + 5) ^= *(source2 + 5); + *(destp + 5) ^= *(source3 + 5); + *(destp + 6) ^= *(source1 + 6); + *(destp + 6) ^= *(source2 + 6); + *(destp + 6) ^= *(source3 + 6); + *(destp + 7) ^= *(source1 + 7); + *(destp + 7) ^= *(source2 + 7); + *(destp + 7) ^= *(source3 + 7); + source1 += 8; + source2 += 8; + source3 += 8; + destp += 8; + } + break; + case 5: + source4 = (long *) bh_ptr[4]->b_data; + source3 = (long *) bh_ptr[3]->b_data; + source2 = (long *) bh_ptr[2]->b_data; + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + *(destp + 0) ^= *(source1 + 0); + *(destp + 0) ^= *(source2 + 0); + *(destp + 0) ^= *(source3 + 0); + *(destp + 0) ^= *(source4 + 0); + *(destp + 1) ^= *(source1 + 1); + *(destp + 1) ^= *(source2 + 1); + *(destp + 1) ^= *(source3 + 1); + *(destp + 1) ^= *(source4 + 1); + *(destp + 2) ^= *(source1 + 2); + *(destp + 2) ^= *(source2 + 2); + *(destp + 2) ^= *(source3 + 2); + *(destp + 2) ^= *(source4 + 2); + *(destp + 3) ^= *(source1 + 3); + *(destp + 3) ^= *(source2 + 3); + *(destp + 3) ^= *(source3 + 3); + *(destp + 3) ^= *(source4 + 3); + *(destp + 4) ^= *(source1 + 4); + *(destp + 4) ^= *(source2 + 4); + *(destp + 4) ^= *(source3 + 4); + *(destp + 4) ^= *(source4 + 4); + *(destp + 5) ^= *(source1 + 5); + *(destp + 5) ^= *(source2 + 5); + *(destp + 5) ^= *(source3 + 5); + *(destp + 5) ^= *(source4 + 5); + *(destp + 6) ^= *(source1 + 6); + *(destp + 6) ^= *(source2 + 6); + *(destp + 6) ^= *(source3 + 6); + *(destp + 6) ^= *(source4 + 6); + *(destp + 7) ^= *(source1 + 7); + *(destp + 7) ^= *(source2 + 7); + *(destp + 7) ^= *(source3 + 7); + *(destp + 7) ^= *(source4 + 7); + source1 += 8; + source2 += 8; + source3 += 8; + source4 += 8; + destp += 8; + } + break; + } +} + +/* + * platform independent RAID5 checksum calculation, this should + * be very fast on any platform that has a decent amount of + * registers. (32 or more) + */ +XORBLOCK_TEMPLATE(32regs) +{ + int size = bh_ptr[0]->b_size; + int lines = size / (sizeof (long)) / 8, i; + long *destp = (long *) bh_ptr[0]->b_data; + long *source1, *source2, *source3, *source4; + + /* LOTS of registers available... + We do explicite loop-unrolling here for code which + favours RISC machines. In fact this is almoast direct + RISC assembly on Alpha and SPARC :-) */ + + + switch(count) { + case 2: + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = destp[0]; /* Pull the stuff into registers */ + d1 = destp[1]; /* ... in bursts, if possible. */ + d2 = destp[2]; + d3 = destp[3]; + d4 = destp[4]; + d5 = destp[5]; + d6 = destp[6]; + d7 = destp[7]; + d0 ^= source1[0]; + d1 ^= source1[1]; + d2 ^= source1[2]; + d3 ^= source1[3]; + d4 ^= source1[4]; + d5 ^= source1[5]; + d6 ^= source1[6]; + d7 ^= source1[7]; + destp[0] = d0; /* Store the result (in burts) */ + destp[1] = d1; + destp[2] = d2; + destp[3] = d3; + destp[4] = d4; /* Store the result (in burts) */ + destp[5] = d5; + destp[6] = d6; + destp[7] = d7; + source1 += 8; + destp += 8; + } + break; + case 3: + source2 = (long *) bh_ptr[2]->b_data; + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = destp[0]; /* Pull the stuff into registers */ + d1 = destp[1]; /* ... in bursts, if possible. */ + d2 = destp[2]; + d3 = destp[3]; + d4 = destp[4]; + d5 = destp[5]; + d6 = destp[6]; + d7 = destp[7]; + d0 ^= source1[0]; + d1 ^= source1[1]; + d2 ^= source1[2]; + d3 ^= source1[3]; + d4 ^= source1[4]; + d5 ^= source1[5]; + d6 ^= source1[6]; + d7 ^= source1[7]; + d0 ^= source2[0]; + d1 ^= source2[1]; + d2 ^= source2[2]; + d3 ^= source2[3]; + d4 ^= source2[4]; + d5 ^= source2[5]; + d6 ^= source2[6]; + d7 ^= source2[7]; + destp[0] = d0; /* Store the result (in burts) */ + destp[1] = d1; + destp[2] = d2; + destp[3] = d3; + destp[4] = d4; /* Store the result (in burts) */ + destp[5] = d5; + destp[6] = d6; + destp[7] = d7; + source1 += 8; + source2 += 8; + destp += 8; + } + break; + case 4: + source3 = (long *) bh_ptr[3]->b_data; + source2 = (long *) bh_ptr[2]->b_data; + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = destp[0]; /* Pull the stuff into registers */ + d1 = destp[1]; /* ... in bursts, if possible. */ + d2 = destp[2]; + d3 = destp[3]; + d4 = destp[4]; + d5 = destp[5]; + d6 = destp[6]; + d7 = destp[7]; + d0 ^= source1[0]; + d1 ^= source1[1]; + d2 ^= source1[2]; + d3 ^= source1[3]; + d4 ^= source1[4]; + d5 ^= source1[5]; + d6 ^= source1[6]; + d7 ^= source1[7]; + d0 ^= source2[0]; + d1 ^= source2[1]; + d2 ^= source2[2]; + d3 ^= source2[3]; + d4 ^= source2[4]; + d5 ^= source2[5]; + d6 ^= source2[6]; + d7 ^= source2[7]; + d0 ^= source3[0]; + d1 ^= source3[1]; + d2 ^= source3[2]; + d3 ^= source3[3]; + d4 ^= source3[4]; + d5 ^= source3[5]; + d6 ^= source3[6]; + d7 ^= source3[7]; + destp[0] = d0; /* Store the result (in burts) */ + destp[1] = d1; + destp[2] = d2; + destp[3] = d3; + destp[4] = d4; /* Store the result (in burts) */ + destp[5] = d5; + destp[6] = d6; + destp[7] = d7; + source1 += 8; + source2 += 8; + source3 += 8; + destp += 8; + } + break; + case 5: + source4 = (long *) bh_ptr[4]->b_data; + source3 = (long *) bh_ptr[3]->b_data; + source2 = (long *) bh_ptr[2]->b_data; + source1 = (long *) bh_ptr[1]->b_data; + for (i = lines; i > 0; i--) { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = destp[0]; /* Pull the stuff into registers */ + d1 = destp[1]; /* ... in bursts, if possible. */ + d2 = destp[2]; + d3 = destp[3]; + d4 = destp[4]; + d5 = destp[5]; + d6 = destp[6]; + d7 = destp[7]; + d0 ^= source1[0]; + d1 ^= source1[1]; + d2 ^= source1[2]; + d3 ^= source1[3]; + d4 ^= source1[4]; + d5 ^= source1[5]; + d6 ^= source1[6]; + d7 ^= source1[7]; + d0 ^= source2[0]; + d1 ^= source2[1]; + d2 ^= source2[2]; + d3 ^= source2[3]; + d4 ^= source2[4]; + d5 ^= source2[5]; + d6 ^= source2[6]; + d7 ^= source2[7]; + d0 ^= source3[0]; + d1 ^= source3[1]; + d2 ^= source3[2]; + d3 ^= source3[3]; + d4 ^= source3[4]; + d5 ^= source3[5]; + d6 ^= source3[6]; + d7 ^= source3[7]; + d0 ^= source4[0]; + d1 ^= source4[1]; + d2 ^= source4[2]; + d3 ^= source4[3]; + d4 ^= source4[4]; + d5 ^= source4[5]; + d6 ^= source4[6]; + d7 ^= source4[7]; + destp[0] = d0; /* Store the result (in burts) */ + destp[1] = d1; + destp[2] = d2; + destp[3] = d3; + destp[4] = d4; /* Store the result (in burts) */ + destp[5] = d5; + destp[6] = d6; + destp[7] = d7; + source1 += 8; + source2 += 8; + source3 += 8; + source4 += 8; + destp += 8; + } + break; + } +} + +/* + * (the -6*32 shift factor colors the cache) + */ +#define SIZE (PAGE_SIZE-6*32) + +static void xor_speed ( struct xor_block_template * func, + struct buffer_head *b1, struct buffer_head *b2) +{ + int speed; + unsigned long now; + int i, count, max; + struct buffer_head *bh_ptr[6]; + + func->next = xor_functions; + xor_functions = func; + bh_ptr[0] = b1; + bh_ptr[1] = b2; + + /* + * count the number of XORs done during a whole jiffy. + * calculate the speed of checksumming from this. + * (we use a 2-page allocation to have guaranteed + * color L1-cache layout) + */ + max = 0; + for (i = 0; i < 5; i++) { + now = jiffies; + count = 0; + while (jiffies == now) { + mb(); + func->xor_block(2,bh_ptr); + mb(); + count++; + mb(); + } + if (count > max) + max = count; + } + + speed = max * (HZ*SIZE/1024); + func->speed = speed; + + printk( " %-10s: %5d.%03d MB/sec\n", func->name, + speed / 1000, speed % 1000); +} + +static inline void pick_fastest_function(void) +{ + struct xor_block_template *f, *fastest; + + fastest = xor_functions; + for (f = fastest; f; f = f->next) { + if (f->speed > fastest->speed) + fastest = f; + } +#ifdef CONFIG_X86_XMM + if (boot_cpu_data.mmu_cr4_features & X86_CR4_OSXMMEXCPT) { + fastest = &t_xor_block_pIII_kni; + } +#endif + xor_block = fastest->xor_block; + printk( "using fastest function: %s (%d.%03d MB/sec)\n", fastest->name, + fastest->speed / 1000, fastest->speed % 1000); +} + + +void calibrate_xor_block(void) +{ + struct buffer_head b1, b2; + + memset(&b1,0,sizeof(b1)); + b2 = b1; + + b1.b_data = (char *) md__get_free_pages(GFP_KERNEL,2); + if (!b1.b_data) { + pick_fastest_function(); + return; + } + b2.b_data = b1.b_data + 2*PAGE_SIZE + SIZE; + + b1.b_size = SIZE; + + printk(KERN_INFO "raid5: measuring checksumming speed\n"); + + sti(); /* should be safe */ + +#if defined(__sparc__) && !defined(__sparc_v9__) + printk(KERN_INFO "raid5: using high-speed SPARC checksum routine\n"); + xor_speed(&t_xor_block_SPARC,&b1,&b2); +#endif + +#ifdef CONFIG_X86_XMM + if (boot_cpu_data.mmu_cr4_features & X86_CR4_OSXMMEXCPT) { + printk(KERN_INFO + "raid5: KNI detected, trying cache-avoiding KNI checksum routine\n"); + /* we force the use of the KNI xor block because it + can write around l2. we may also be able + to load into the l1 only depending on how + the cpu deals with a load to a line that is + being prefetched. + */ + xor_speed(&t_xor_block_pIII_kni,&b1,&b2); + } +#endif /* CONFIG_X86_XMM */ + +#ifdef __i386__ + + if (md_cpu_has_mmx()) { + printk(KERN_INFO + "raid5: MMX detected, trying high-speed MMX checksum routines\n"); + xor_speed(&t_xor_block_pII_mmx,&b1,&b2); + xor_speed(&t_xor_block_p5_mmx,&b1,&b2); + } + +#endif /* __i386__ */ + + + xor_speed(&t_xor_block_8regs,&b1,&b2); + xor_speed(&t_xor_block_32regs,&b1,&b2); + + free_pages((unsigned long)b1.b_data,2); + pick_fastest_function(); +} + +#else /* __sparc_v9__ */ + +void calibrate_xor_block(void) +{ + printk(KERN_INFO "raid5: using high-speed VIS checksum routine\n"); + xor_block = xor_block_VIS; +} + +#endif /* __sparc_v9__ */ + +MD_EXPORT_SYMBOL(xor_block); + diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 204ba1433c2e..db7ae184702d 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -37,6 +37,7 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION fi tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8 + tristate 'Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX if [ "$CONFIG_SPECIALIX" != "n" ]; then bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS @@ -168,6 +169,9 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then hex ' ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c fi + dep_tristate 'Zoran ZR36057/36060 support' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV + dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN + #dep_tristate ' Include support for LML33' CONFIG_VIDEO_LML33 $CONFIG_VIDEO_ZORAN fi endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 9b9076b082da..84ba020c46d6 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -132,6 +132,14 @@ else endif endif +ifeq ($(CONFIG_COMPUTONE),y) +L_OBJS += ip2.o ip2main.o +else + ifeq ($(CONFIG_COMPUTONE),m) + M_OBJS += ip2.o ip2main.o + endif +endif + ifeq ($(CONFIG_RISCOM8),y) L_OBJS += riscom8.o else @@ -370,6 +378,30 @@ else endif endif +ifeq ($(CONFIG_VIDEO_ZORAN),y) +L_OBJS += buz.o +else + ifeq ($(CONFIG_VIDEO_ZORAN),m) + M_OBJS += buz.o + endif +endif + +ifeq ($(CONFIG_VIDEO_LML33),y) +L_OBJS += bt856.o bt819.o +else + ifeq ($(CONFIG_VIDEO_LML33),m) + M_OBJS += bt856.o bt819.o + endif +endif + +ifeq ($(CONFIG_VIDEO_BUZ),y) +L_OBJS += saa7111.o saa7185.o +else + ifeq ($(CONFIG_VIDEO_BUZ),m) + M_OBJS += saa7111.o saa7185.o + endif +endif + ifeq ($(CONFIG_VIDEO_PMS),y) L_OBJS += pms.o else diff --git a/drivers/char/README.computone b/drivers/char/README.computone new file mode 100644 index 000000000000..51c3666b6748 --- /dev/null +++ b/drivers/char/README.computone @@ -0,0 +1,195 @@ + +Computone Intelliport II/Plus Multiport Serial Driver +----------------------------------------------------- + +Release Notes For Linux Kernel 2.2 +These notes have been tested on Linux kernels 2.0 and 2.2. + + +Version: 1.2.4 +Date: 08/04/99 +Fixes and Updates: Doug McNash +Historical Author: Andrew Manison + +1. INTRODUCTION + +This driver supports the entire family of Intelliport II/Plus controllers +with the exception of the MicroChannel controllers. + +This driver was developed on the v2.0.x Linux source tree and has been +tested up to v2.2.10; it will probably not work with earlier v1.X kernels, +and has not yet been tested on the v2.1.x tree. The most likely problems +will be in patching the kernel sources to support the driver. For this +reason there are 2 different patch files for 2.0.XX and 2.2.XX kernels. +Make sure you use the right one! +Someday soon it should be included in the 2.3.XX tree. + +2. QUICK INSTALLATION + +Hardware - If you have an ISA card, find a free interrupt and io port. + List those in use with `cat /proc/interrupts` and + `cat /proc/ioports`. Set the card dip switches to that free + address. You may need to configure your BIOS to reserve the + irq for the ISA card. PCI and EISA parameters are set + automagically and need no attention. Insert card into + computer with the power off before or after driver installation. + +Software - + +Module installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (m) module for CONFIG_COMPUTONE under character + devices. CONFIG_PCI and CONFIG_MODULES also may need to be set. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h if needed + or + edit /etc/conf.modules (or /etc/modules.conf) if needed (module). + or both to match this setting. +f) Run "make dep" +g) Run "make modules" +h) Run "make modules_install" +i) Run "/sbin/depmod -a" +i) install driver using `modprobe ip2 ` (options listed below) +j) run mkip2dev + + +Kernel installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (y) kernel for CONFIG_COMPUTONE under character + devices. CONFIG_PCI may need to be set if you have PCI bus. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h +f) Run "make dep" +g) Run "make zImage" or whatever target you prefer. +h) mv /usr/src/linux/arch/i386/boot/zImage to /boot. +i) add new config for this kernel into /etc/lilo.conf, run "lilo" +j) reboot using this kernel +k) make and run ip2/mkip2dev + +3. INSTALLATION + +Previously, the driver sources were packaged with a set of patch files +to update the character drivers' makefile and configuration file, and other +kernel source files. A build script (ip2build) was included which applies +the patches if needed, and build any utilities needed. +What you recieve may be a single patch file in conventional kernel +patch format build script. That form can also be applied by +running patch -p1 < ThePatchFile. Otherwise run ip2build. + +The driver can be installed as a module (recommended) or built into the +kernel. This is selected as for other drivers through the `make config` +command from the root of the Linux source tree. If the driver is built +into the kernel you will need to edit the file ip2.h to match the boards +you are installing. See that file for instructions. If the driver is +installed as a module the configuration can also be specified on the +modprobe command line as follows: + + modprobe ip2 irq=irq1,irq2,irq3,irq4 io=addr1,addr2,addr3,addr4 + +where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11, +12,15) and addr1-4 are the base addresses for up to four controllers. If +the irqs are not specified the driver uses the default in ip2/ip2.h (which +selects polled mode). If no base addresses are specified the defaults in +ip2.h are used. If you are autoloading the driver module with kerneld or +kmod the base addresses and interrupt number must also be set in ip2/ip2.h +and recompile or just insert and options line in /etc/modules.conf or both. +The options line is equivalent to the command line and takes precidence over +what is in ip2.h. + +/etc/modules.conf sample: + options ip2 io=1,0x328 irq=1,10 + alias char-major-71 ip2 + alias char-major-72 ip2 + alias char-major-73 ip2 + +equivelant ip2.h: +static ip2config_t ip2config = +{ + {1,10,0,0}, + { + 0x0001, // Board 0, ttyF0 - ttyF63 /* PCI card */ + 0x0328, // Board 1, ttyF64 - ttyF127 /* ISA card */ + 0x0000, // Board 2, ttyF128 - ttyF191 /* empty */ + 0x0000 // Board 3, ttyF192 - ttyF255 /* empty */ + } +}; + +Specifying an invalid or in-use irq will default the driver into +running in polled mode for that card. If all irq entries are 0 then +all cards will operate in polled mode. + +Tarball Install: + +The whole tarfile should be untarred in the /usr/src/linux/drivers/char/ +directory. Most files required for the driver are placed in the ip2 +subdirectory. Then execute the script + + ip2build + +which will patch the files. + +Kernel Patch Install: + + cd to the Linux source root, run patch -p1 < ThePatchFile. + +Now return to the root directory of the Linux +source tree and run make config or make menuconfig. You will be prompted +for the Computone drivers, either as a module or part of the kernel. +If you have a PCI card you many need to select PCI bios support (CONFIG_PCI) +if not enabled already. Ditto for CONFIG_MODULES if you use modules. + +If you select the driver as part of the kernel run : + + make depend + make zlilo (or whatever you do to create a bootable kernel) + +If you selected a module run : + + make modules && make modules_install + +The utility ip2mkdev creates all the device nodes required by the driver. +For a device to be created it must be configured in the driver and the +board must be installed. Only devices corresponding to real IntelliPort II +ports are created. With multiple boards and expansion boxes this will +leave gaps in the sequence of device names. ip2mkdev uses Linux tty naming +conventions: ttyF0 - ttyF255 for normal devices, and cuf0 - cuf255 for +callout devices. + +4. USING THE DRIVERS + +As noted above, the driver implements the ports in accordance with Linux +conventions, and the devices should be interchangeable with the standard +serial devices. (This is a key point for problem reporting: please make +sure that what you are trying do works on the ttySx/cuax ports first; then +tell us what went wrong with the ip2 ports!) + +Higher speeds can be obtained using the setserial utility which remaps +38,400 bps (extb) to 57,600 bps, 115,200 bps, or a custom speed. +Intelliport II installations using the PowerPort expansion module can +use the custom speed setting to select the highest speeds: 153,600 bps, +230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for +custom baud rate configuration is fixed at 921,600 for cards/expantion +modules with ST654's and 115200 for those with Cirrus CD1400's. This +corresponds to the maximum bit rates those chips are capable. +For example if the baud base is 921600 and the baud divisor is 18 then +the custom rate is 921600/18 = 51200 bps. See the setserial man page for +complete details. Of course if stty accepts the higher rates now you can +use that as well as the standard ioctls(). + +5. NOTES + +This is a release version of the driver, but it is impossible to test it +in all configurations of Linux. If there is any anomalous behaviour that +does not match the standard serial port's behaviour please let us know. + +Author: dmcnash@computine.com +Testing: larryg@computone.com +Spport: support@computone.com diff --git a/drivers/char/buz.c b/drivers/char/buz.c new file mode 100644 index 000000000000..5352245a95ff --- /dev/null +++ b/drivers/char/buz.c @@ -0,0 +1,3481 @@ +#define MAX_KMALLOC_MEM (512*1024) +/* + buz - Iomega Buz driver version 1.0 + + Copyright (C) 1999 Rainer Johanni + + based on + + buz.0.0.3 Copyright (C) 1998 Dave Perks + + and + + bttv - Bt848 frame grabber driver + + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.de) + + 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include "buz.h" +#include +#include + +#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | /* ZR36057_ISR_GIRQ1 | ZR36057_ISR_CodRepIRQ | */ ZR36057_ISR_JPEGRepIRQ ) +#define GPIO_MASK 0xdf + +/* + + BUZ + + GPIO0 = 1, take board out of reset + GPIO1 = 1, take JPEG codec out of sleep mode + GPIO3 = 1, deassert FRAME# to 36060 + + + GIRQ0 signals a vertical sync of the video signal + GIRQ1 signals that ZR36060's DATERR# line is asserted. + + SAA7111A + + In their infinite wisdom, the Iomega engineers decided to + use the same input line for composite and S-Video Color, + although there are two entries not connected at all! + Through this ingenious strike, it is not possible to + keep two running video sources connected at the same time + to Composite and S-VHS input! + + mode 0 - N/C + mode 1 - S-Video Y + mode 2 - noise or something I don't know + mode 3 - Composite and S-Video C + mode 4 - N/C + mode 5 - S-Video (gain C independently selectable of gain Y) + mode 6 - N/C + mode 7 - S-Video (gain C adapted to gain Y) + */ + +#define MAJOR_VERSION 1 /* driver major version */ +#define MINOR_VERSION 0 /* driver minor version */ + +#define BUZ_NAME "Iomega BUZ V-1.0" /* name of the driver */ + +#define DEBUG(x) /* Debug driver */ +#define IDEBUG(x) /* Debug interrupt handler */ +#define IOCTL_DEBUG(x) + + +/* The parameters for this driver */ + +/* + The video mem address of the video card. + The driver has a little database for some videocards + to determine it from there. If your video card is not in there + you have either to give it to the driver as a parameter + or set in in a VIDIOCSFBUF ioctl + */ + +static unsigned long vidmem = 0; /* Video memory base address */ + +/* Special purposes only: */ + +static int triton = 0; /* 0=no, 1=yes */ +static int natoma = 0; /* 0=no, 1=yes */ + +/* + Number and size of grab buffers for Video 4 Linux + The vast majority of applications should not need more than 2, + the very popular BTTV driver actually does ONLY have 2. + Time sensitive applications might need more, the maximum + is VIDEO_MAX_FRAME (defined in ). + + The size is set so that the maximum possible request + can be satisfied. Decrease it, if bigphys_area alloc'd + memory is low. If you don't have the bigphys_area patch, + set it to 128 KB. Will you allow only to grab small + images with V4L, but that's better than nothing. + + v4l_bufsize has to be given in KB ! + + */ + +static int v4l_nbufs = 2; +static int v4l_bufsize = 128; /* Everybody should be able to work with this setting */ + +/* + Default input and video norm at startup of the driver. + */ + +static int default_input = 0; /* 0=Composite, 1=S-VHS */ +static int default_norm = 0; /* 0=PAL, 1=NTSC */ + +MODULE_PARM(vidmem, "i"); +MODULE_PARM(triton, "i"); +MODULE_PARM(natoma, "i"); +MODULE_PARM(v4l_nbufs, "i"); +MODULE_PARM(v4l_bufsize, "i"); +MODULE_PARM(default_input, "i"); +MODULE_PARM(default_norm, "i"); + +/* Anybody who uses more than four? */ +#define BUZ_MAX 4 + +static int zoran_num; /* number of Buzs in use */ +static struct zoran zoran[BUZ_MAX]; + +/* forward references */ + +static void v4l_fbuffer_free(struct zoran *zr); +static void jpg_fbuffer_free(struct zoran *zr); +static void zoran_feed_stat_com(struct zoran *zr); + + + +/* + * Allocate the V4L grab buffers + * + * These have to be pysically contiguous. + * If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc + */ + +static int v4l_fbuffer_alloc(struct zoran *zr) +{ + int i, off; + unsigned char *mem; + + for (i = 0; i < v4l_nbufs; i++) { + if (zr->v4l_gbuf[i].fbuffer) + printk(KERN_WARNING "%s: v4l_fbuffer_alloc: buffer %d allready allocated ?\n", zr->name, i); + + if (v4l_bufsize <= MAX_KMALLOC_MEM) { + /* Use kmalloc */ + + mem = (unsigned char *) kmalloc(v4l_bufsize, GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR "%s: kmalloc for V4L bufs failed\n", zr->name); + v4l_fbuffer_free(zr); + return -ENOBUFS; + } + zr->v4l_gbuf[i].fbuffer = mem; + zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem); + zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem); + for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) + mem_map_reserve(MAP_NR(mem + off)); + DEBUG(printk(BUZ_INFO ": V4L frame %d mem 0x%x (bus: 0x%x=%d)\n", i, mem, virt_to_bus(mem), virt_to_bus(mem))); + } else { + return -ENOBUFS; + } + } + + return 0; +} + +/* free the V4L grab buffers */ +static void v4l_fbuffer_free(struct zoran *zr) +{ + int i, off; + unsigned char *mem; + + for (i = 0; i < v4l_nbufs; i++) { + if (!zr->v4l_gbuf[i].fbuffer) + continue; + + mem = zr->v4l_gbuf[i].fbuffer; + for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) + mem_map_unreserve(MAP_NR(mem + off)); + kfree((void *) zr->v4l_gbuf[i].fbuffer); + zr->v4l_gbuf[i].fbuffer = NULL; + } +} + +/* + * Allocate the MJPEG grab buffers. + * + * If the requested buffer size is smaller than MAX_KMALLOC_MEM, + * kmalloc is used to request a physically contiguous area, + * else we allocate the memory in framgents with get_free_page. + * + * If a Natoma chipset is present and this is a revision 1 zr36057, + * each MJPEG buffer needs to be physically contiguous. + * (RJ: This statement is from Dave Perks' original driver, + * I could never check it because I have a zr36067) + * The driver cares about this because it reduces the buffer + * size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation). + * + * RJ: The contents grab buffers needs never be accessed in the driver. + * Therefore there is no need to allocate them with vmalloc in order + * to get a contiguous virtual memory space. + * I don't understand why many other drivers first allocate them with + * vmalloc (which uses internally also get_free_page, but delivers you + * virtual addresses) and then again have to make a lot of efforts + * to get the physical address. + * + */ + +static int jpg_fbuffer_alloc(struct zoran *zr) +{ + int i, j, off, alloc_contig; + unsigned long mem; + + /* Decide if we should alloc contiguous or fragmented memory */ + /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ + + alloc_contig = (zr->jpg_bufsize < MAX_KMALLOC_MEM); + + for (i = 0; i < zr->jpg_nbufs; i++) { + if (zr->jpg_gbuf[i].frag_tab) + printk(KERN_WARNING "%s: jpg_fbuffer_alloc: buffer %d allready allocated ???\n", zr->name, i); + + /* Allocate fragment table for this buffer */ + + mem = get_free_page(GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR "%s: jpg_fbuffer_alloc: get_free_page (frag_tab) failed for buffer %d\n", zr->name, i); + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + memset((void *) mem, 0, PAGE_SIZE); + zr->jpg_gbuf[i].frag_tab = (u32 *) mem; + zr->jpg_gbuf[i].frag_tab_bus = virt_to_bus((void *) mem); + + if (alloc_contig) { + mem = (unsigned long) kmalloc(zr->jpg_bufsize, GFP_KERNEL); + if (mem == 0) { + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + zr->jpg_gbuf[i].frag_tab[0] = virt_to_bus((void *) mem); + zr->jpg_gbuf[i].frag_tab[1] = ((zr->jpg_bufsize / 4) << 1) | 1; + for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE) + mem_map_reserve(MAP_NR(mem + off)); + } else { + /* jpg_bufsize is alreay page aligned */ + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + mem = get_free_page(GFP_KERNEL); + if (mem == 0) { + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + zr->jpg_gbuf[i].frag_tab[2 * j] = virt_to_bus((void *) mem); + zr->jpg_gbuf[i].frag_tab[2 * j + 1] = (PAGE_SIZE / 4) << 1; + mem_map_reserve(MAP_NR(mem)); + } + + zr->jpg_gbuf[i].frag_tab[2 * j - 1] |= 1; + } + } + + DEBUG(printk("jpg_fbuffer_alloc: %d KB allocated\n", + (zr->jpg_nbufs * zr->jpg_bufsize) >> 10)); + zr->jpg_buffers_allocated = 1; + return 0; +} + +/* free the MJPEG grab buffers */ +static void jpg_fbuffer_free(struct zoran *zr) +{ + int i, j, off, alloc_contig; + unsigned char *mem; + + /* Decide if we should alloc contiguous or fragmented memory */ + /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ + + alloc_contig = (zr->jpg_bufsize < MAX_KMALLOC_MEM); + + for (i = 0; i < zr->jpg_nbufs; i++) { + if (!zr->jpg_gbuf[i].frag_tab) + continue; + + if (alloc_contig) { + if (zr->jpg_gbuf[i].frag_tab[0]) { + mem = (unsigned char *) bus_to_virt(zr->jpg_gbuf[i].frag_tab[0]); + for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE) + mem_map_unreserve(MAP_NR(mem + off)); + kfree((void *) mem); + zr->jpg_gbuf[i].frag_tab[0] = 0; + zr->jpg_gbuf[i].frag_tab[1] = 0; + } + } else { + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + if (!zr->jpg_gbuf[i].frag_tab[2 * j]) + break; + mem_map_unreserve(MAP_NR(bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j]))); + free_page((unsigned long) bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j])); + zr->jpg_gbuf[i].frag_tab[2 * j] = 0; + zr->jpg_gbuf[i].frag_tab[2 * j + 1] = 0; + } + } + + free_page((unsigned long) zr->jpg_gbuf[i].frag_tab); + zr->jpg_gbuf[i].frag_tab = NULL; + } + zr->jpg_buffers_allocated = 0; +} + + +/* ----------------------------------------------------------------------- */ + +/* I2C functions */ + +#define I2C_DELAY 10 + + +/* software I2C functions */ + +static void i2c_setlines(struct i2c_bus *bus, int ctrl, int data) +{ + struct zoran *zr = (struct zoran *) bus->data; + btwrite((data << 1) | ctrl, ZR36057_I2CBR); + btread(ZR36057_I2CBR); + udelay(I2C_DELAY); +} + +static int i2c_getdataline(struct i2c_bus *bus) +{ + struct zoran *zr = (struct zoran *) bus->data; + return (btread(ZR36057_I2CBR) >> 1) & 1; +} + +void attach_inform(struct i2c_bus *bus, int id) +{ + DEBUG(struct zoran *zr = (struct zoran *) bus->data); + DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id)); +} + +void detach_inform(struct i2c_bus *bus, int id) +{ + DEBUG(struct zoran *zr = (struct zoran *) bus->data); + DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id)); +} + +static struct i2c_bus zoran_i2c_bus_template = +{ + "zr36057", + I2C_BUSID_BT848, + NULL, + + SPIN_LOCK_UNLOCKED, + + attach_inform, + detach_inform, + + i2c_setlines, + i2c_getdataline, + NULL, + NULL, +}; + + +/* ----------------------------------------------------------------------- */ + +static void GPIO(struct zoran *zr, unsigned bit, unsigned value) +{ + u32 reg; + u32 mask; + + mask = 1 << (24 + bit); + reg = btread(ZR36057_GPPGCR1) & ~mask; + if (value) { + reg |= mask; + } + btwrite(reg, ZR36057_GPPGCR1); + /* Stop any PCI posting on the GPIO bus */ + btread(ZR36057_I2CBR); +} + + +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the ZR36057 manual in front of + * you [AC]. + * + * PS: The manual is free for download in .pdf format from + * www.zoran.com - nicely done those folks. + */ + +struct tvnorm { + u16 Wt, Wa, Ht, Ha, HStart, VStart; +}; + +static struct tvnorm tvnorms[] = +{ + /* PAL-BDGHI */ + {864, 720, 625, 576, 31, 16}, + /* NTSC */ + {858, 720, 525, 480, 21, 8}, +}; +#define TVNORMS (sizeof(tvnorms) / sizeof(tvnorm)) + +static int format2bpp(int format) +{ + int bpp; + + /* Determine the number of bytes per pixel for the video format requested */ + + switch (format) { + + case VIDEO_PALETTE_YUV422: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB555: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB565: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB24: + bpp = 3; + break; + + case VIDEO_PALETTE_RGB32: + bpp = 4; + break; + + default: + bpp = 0; + } + + return bpp; +} + +/* + * set geometry + */ +static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, + unsigned int video_format) +{ + struct tvnorm *tvn; + unsigned HStart, HEnd, VStart, VEnd; + unsigned DispMode; + unsigned VidWinWid, VidWinHt; + unsigned hcrop1, hcrop2, vcrop1, vcrop2; + unsigned Wa, We, Ha, He; + unsigned X, Y, HorDcm, VerDcm; + u32 reg; + unsigned mask_line_size; + + if (zr->params.norm < 0 || zr->params.norm > 1) { + printk(KERN_ERR "%s: set_vfe: video_norm = %d not valid\n", zr->name, zr->params.norm); + return; + } + if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT) { + printk(KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", zr->name, video_width, video_height); + return; + } + tvn = &tvnorms[zr->params.norm]; + + Wa = tvn->Wa; + Ha = tvn->Ha; + + /* if window has more than half of active height, + switch on interlacing - we want the full information */ + + zr->video_interlace = (video_height > Ha / 2); + +/**** zr36057 ****/ + + /* horizontal */ + VidWinWid = video_width; + X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; + We = (VidWinWid * 64) / X; + HorDcm = 64 - X; + hcrop1 = 2 * ((tvn->Wa - We) / 4); + hcrop2 = tvn->Wa - We - hcrop1; + HStart = tvn->HStart | 1; + HEnd = HStart + tvn->Wa - 1; + HStart += hcrop1; + HEnd -= hcrop2; + reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) + | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); + reg |= ZR36057_VFEHCR_HSPol; + btwrite(reg, ZR36057_VFEHCR); + + /* Vertical */ + DispMode = !zr->video_interlace; + VidWinHt = DispMode ? video_height : video_height / 2; + Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; + He = (VidWinHt * 64) / Y; + VerDcm = 64 - Y; + vcrop1 = (tvn->Ha / 2 - He) / 2; + vcrop2 = tvn->Ha / 2 - He - vcrop1; + VStart = tvn->VStart; + VEnd = VStart + tvn->Ha / 2 - 1; + VStart += vcrop1; + VEnd -= vcrop2; + reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) + | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); + reg |= ZR36057_VFEVCR_VSPol; + btwrite(reg, ZR36057_VFEVCR); + + /* scaler and pixel format */ + reg = 0 // ZR36057_VFESPFR_ExtFl /* Trying to live without ExtFl */ + | (HorDcm << ZR36057_VFESPFR_HorDcm) + | (VerDcm << ZR36057_VFESPFR_VerDcm) + | (DispMode << ZR36057_VFESPFR_DispMode) + | ZR36057_VFESPFR_LittleEndian; + /* RJ: I don't know, why the following has to be the opposite + of the corresponding ZR36060 setting, but only this way + we get the correct colors when uncompressing to the screen */ + reg |= ZR36057_VFESPFR_VCLKPol; + /* RJ: Don't know if that is needed for NTSC also */ + reg |= ZR36057_VFESPFR_TopField; + switch (video_format) { + + case VIDEO_PALETTE_YUV422: + reg |= ZR36057_VFESPFR_YUV422; + break; + + case VIDEO_PALETTE_RGB555: + reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB565: + reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB24: + reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24; + break; + + case VIDEO_PALETTE_RGB32: + reg |= ZR36057_VFESPFR_RGB888; + break; + + default: + printk(KERN_INFO "%s: Unknown color_fmt=%x\n", zr->name, video_format); + return; + + } + if (HorDcm >= 48) { + reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ + } else if (HorDcm >= 32) { + reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ + } else if (HorDcm >= 16) { + reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ + } + btwrite(reg, ZR36057_VFESPFR); + + /* display configuration */ + + reg = (16 << ZR36057_VDCR_MinPix) + | (VidWinHt << ZR36057_VDCR_VidWinHt) + | (VidWinWid << ZR36057_VDCR_VidWinWid); + if (triton) + reg &= ~ZR36057_VDCR_Triton; + else + reg |= ZR36057_VDCR_Triton; + btwrite(reg, ZR36057_VDCR); + + /* Write overlay clipping mask data, but don't enable overlay clipping */ + /* RJ: since this makes only sense on the screen, we use + zr->window.width instead of video_width */ + + mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + reg = virt_to_bus(zr->overlay_mask); + btwrite(reg, ZR36057_MMTR); + reg = virt_to_bus(zr->overlay_mask + mask_line_size); + btwrite(reg, ZR36057_MMBR); + reg = mask_line_size - (zr->window.width + 31) / 32; + if (DispMode == 0) + reg += mask_line_size; + reg <<= ZR36057_OCR_MaskStride; + btwrite(reg, ZR36057_OCR); + +} + +/* + * Switch overlay on or off + */ + +static void zr36057_overlay(struct zoran *zr, int on) +{ + int fmt, bpp; + u32 reg; + + if (on) { + /* do the necessary settings ... */ + + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ + + switch (zr->buffer.depth) { + case 15: + fmt = VIDEO_PALETTE_RGB555; + bpp = 2; + break; + case 16: + fmt = VIDEO_PALETTE_RGB565; + bpp = 2; + break; + case 24: + fmt = VIDEO_PALETTE_RGB24; + bpp = 3; + break; + case 32: + fmt = VIDEO_PALETTE_RGB32; + bpp = 4; + break; + default: + fmt = 0; + bpp = 0; + } + + zr36057_set_vfe(zr, zr->window.width, zr->window.height, fmt); + + /* Start and length of each line MUST be 4-byte aligned. + This should be allready checked before the call to this routine. + All error messages are internal driver checking only! */ + + /* video display top and bottom registers */ + + reg = (u32) zr->buffer.base + + zr->window.x * bpp + + zr->window.y * zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDTR); + if (reg & 3) + printk(KERN_ERR "%s: zr36057_overlay: video_address not aligned\n", zr->name); + if (zr->video_interlace) + reg += zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + + reg = zr->buffer.bytesperline - zr->window.width * bpp; + if (zr->video_interlace) + reg += zr->buffer.bytesperline; + if (reg & 3) + printk(KERN_ERR "%s: zr36057_overlay: video_stride not aligned\n", zr->name); + reg = (reg << ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ + btwrite(reg, ZR36057_VSSFGR); + + /* Set overlay clipping */ + + if (zr->window.clipcount) + btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); + + /* ... and switch it on */ + + btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); + } else { + /* Switch it off */ + + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + } +} + +/* + * The overlay mask has one bit for each pixel on a scan line, + * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. + */ +static void write_overlay_mask(struct zoran *zr, struct video_clip *vp, int count) +{ + unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + u32 *mask; + int x, y, width, height; + unsigned i, j, k; + u32 reg; + + /* fill mask with one bits */ + memset(zr->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); + reg = 0; + + for (i = 0; i < count; ++i) { + /* pick up local copy of clip */ + x = vp[i].x; + y = vp[i].y; + width = vp[i].width; + height = vp[i].height; + + /* trim clips that extend beyond the window */ + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (x + width > zr->window.width) { + width = zr->window.width - x; + } + if (y + height > zr->window.height) { + height = zr->window.height - y; + } + /* ignore degenerate clips */ + if (height <= 0) { + continue; + } + if (width <= 0) { + continue; + } + /* apply clip for each scan line */ + for (j = 0; j < height; ++j) { + /* reset bit for each pixel */ + /* this can be optimized later if need be */ + mask = zr->overlay_mask + (y + j) * mask_line_size; + for (k = 0; k < width; ++k) { + mask[(x + k) / 32] &= ~((u32) 1 << (x + k) % 32); + } + } + } +} + +/* Enable/Disable uncompressed memory grabbing of the 36057 */ + +static void zr36057_set_memgrab(struct zoran *zr, int mode) +{ + if (mode) { + if (btread(ZR36057_VSSFGR) & (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab)) + printk(KERN_WARNING "%s: zr36057_set_memgrab_on with SnapShot or FrameGrab on ???\n", zr->name); + + /* switch on VSync interrupts */ + + btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts + + btor(ZR36057_ICR_GIRQ0, ZR36057_ICR); + + /* enable SnapShot */ + + btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + + /* Set zr36057 video front end and enable video */ + +#ifdef XAWTV_HACK + zr36057_set_vfe(zr, zr->gwidth > 720 ? 720 : zr->gwidth, zr->gheight, zr->gformat); +#else + zr36057_set_vfe(zr, zr->gwidth, zr->gheight, zr->gformat); +#endif + + zr->v4l_memgrab_active = 1; + } else { + zr->v4l_memgrab_active = 0; + + /* switch off VSync interrupts */ + + btand(~ZR36057_ICR_GIRQ0, ZR36057_ICR); + + /* reenable grabbing to screen if it was running */ + + if (zr->v4l_overlay_active) { + zr36057_overlay(zr, 1); + } else { + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + } + } +} + +static int wait_grab_pending(struct zoran *zr) +{ + unsigned long flags; + + /* wait until all pending grabs are finished */ + + if (!zr->v4l_memgrab_active) + return 0; + + while (zr->v4l_pend_tail != zr->v4l_pend_head) { + interruptible_sleep_on(&zr->v4l_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + spin_lock_irqsave(&zr->lock, flags); + zr36057_set_memgrab(zr, 0); + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} + +/* + * V4L Buffer grabbing + */ + +static int v4l_grab(struct zoran *zr, struct video_mmap *mp) +{ + unsigned long flags; + int res, bpp; + + /* + * There is a long list of limitations to what is allowed to be grabbed + * We don't output error messages her, since some programs (e.g. xawtv) + * just try several settings to find out what is valid or not. + */ + + /* No grabbing outside the buffer range! */ + + if (mp->frame >= v4l_nbufs || mp->frame < 0) + return -EINVAL; + + /* Check size and format of the grab wanted */ + + if (mp->height < BUZ_MIN_HEIGHT || mp->width < BUZ_MIN_WIDTH) + return -EINVAL; + if (mp->height > BUZ_MAX_HEIGHT || mp->width > BUZ_MAX_WIDTH) + return -EINVAL; + + bpp = format2bpp(mp->format); + if (bpp == 0) + return -EINVAL; + + /* Check against available buffer size */ + + if (mp->height * mp->width * bpp > v4l_bufsize) + return -EINVAL; + + /* The video front end needs 4-byte alinged line sizes */ + + if ((bpp == 2 && (mp->width & 1)) || (bpp == 3 && (mp->width & 3))) + return -EINVAL; + + /* + * To minimize the time spent in the IRQ routine, we avoid setting up + * the video front end there. + * If this grab has different parameters from a running streaming capture + * we stop the streaming capture and start it over again. + */ + + if (zr->v4l_memgrab_active && + (zr->gwidth != mp->width || zr->gheight != mp->height || zr->gformat != mp->format)) { + res = wait_grab_pending(zr); + if (res) + return res; + } + zr->gwidth = mp->width; + zr->gheight = mp->height; + zr->gformat = mp->format; + zr->gbpl = bpp * zr->gwidth; + + + spin_lock_irqsave(&zr->lock, flags); + + /* make sure a grab isn't going on currently with this buffer */ + + switch (zr->v4l_gbuf[mp->frame].state) { + + default: + case BUZ_STATE_PEND: + res = -EBUSY; /* what are you doing? */ + break; + + case BUZ_STATE_USER: + case BUZ_STATE_DONE: + /* since there is at least one unused buffer there's room for at least one more pend[] entry */ + zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = mp->frame; + zr->v4l_gbuf[mp->frame].state = BUZ_STATE_PEND; + res = 0; + break; + + } + + /* put the 36057 into frame grabbing mode */ + + if (!res && !zr->v4l_memgrab_active) + zr36057_set_memgrab(zr, 1); + + spin_unlock_irqrestore(&zr->lock, flags); + + return res; +} + +/* + * Sync on a V4L buffer + */ + +static int v4l_sync(struct zoran *zr, int frame) +{ + unsigned long flags; + + + /* check passed-in frame number */ + if (frame >= v4l_nbufs || frame < 0) { + printk(KERN_ERR "%s: v4l_sync: frame %d is invalid\n", zr->name, frame); + return -EINVAL; + } + /* Check if is buffer was queued at all */ + + if (zr->v4l_gbuf[frame].state == BUZ_STATE_USER) { +// printk(KERN_ERR "%s: v4l_sync: Trying to sync on a buffer which was not queued?\n", zr->name); + return -EINVAL; + } + /* wait on this buffer to get ready */ + + while (zr->v4l_gbuf[frame].state == BUZ_STATE_PEND) { + interruptible_sleep_on(&zr->v4l_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + /* buffer should now be in BUZ_STATE_DONE */ + + if (zr->v4l_gbuf[frame].state != BUZ_STATE_DONE) + printk(KERN_ERR "%s: v4l_sync - internal error\n", zr->name); + + /* Check if streaming capture has finished */ + + spin_lock_irqsave(&zr->lock, flags); + + if (zr->v4l_pend_tail == zr->v4l_pend_head) + zr36057_set_memgrab(zr, 0); + + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} +/***************************************************************************** + * * + * Set up the Buz-specific MJPEG part * + * * + *****************************************************************************/ + +/* + * Wait til post office is no longer busy + */ + +static int post_office_wait(struct zoran *zr) +{ + u32 por; + u32 ct=0; + + while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { + ct++; + if(ct>100000) + { + printk(KERN_ERR "%s: timeout on post office.\n", zr->name); + return -1; + } + /* wait for something to happen */ + } + if ((por & ZR36057_POR_POPen) != 0) { + printk(KERN_WARNING "%s: pop pending %08x\n", zr->name, por); + return -1; + } +#if 0 + /* The LML 33 gets this bit wrong */ + + if ((por & (ZR36057_POR_POTime | ZR36057_POR_POPen)) != 0) { + printk(KERN_WARNING "%s: pop timeout %08x\n", zr->name, por); + return -1; + } +#endif + return 0; +} + +static int post_office_write(struct zoran *zr, unsigned guest, unsigned reg, unsigned value) +{ + u32 por; + + post_office_wait(zr); + por = ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16) | (value & 0xFF); + btwrite(por, ZR36057_POR); + return post_office_wait(zr); +} + +static int post_office_read(struct zoran *zr, unsigned guest, unsigned reg) +{ + u32 por; + + post_office_wait(zr); + por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); + btwrite(por, ZR36057_POR); + if (post_office_wait(zr) < 0) { + return -1; + } + return btread(ZR36057_POR) & 0xFF; +} + +static int zr36060_write_8(struct zoran *zr, unsigned reg, unsigned val) +{ + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg)) { + return -1; + } + return post_office_write(zr, 0, 3, val); +} + +static int zr36060_write_16(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_8(zr, reg + 0, val >> 8)) { + return -1; + } + return zr36060_write_8(zr, reg + 1, val >> 0); +} + +static int zr36060_write_24(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_8(zr, reg + 0, val >> 16)) { + return -1; + } + return zr36060_write_16(zr, reg + 1, val >> 0); +} + +static int zr36060_write_32(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_16(zr, reg + 0, val >> 16)) { + return -1; + } + return zr36060_write_16(zr, reg + 2, val >> 0); +} + +static u32 zr36060_read_8(struct zoran *zr, unsigned reg) +{ + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg)) { + return -1; + } + return post_office_read(zr, 0, 3) & 0xFF; +} + +static int zr36060_reset(struct zoran *zr) +{ + return post_office_write(zr, 3, 0, 0); +} + +static void zr36060_sleep(struct zoran *zr, int sleep) +{ + GPIO(zr, 1, !sleep); +} + + +static void zr36060_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + int size; + + reg = (1 << 0) /* CodeMstr */ + |(0 << 2) /* CFIS=0 */ + |(0 << 6) /* Endian=0 */ + |(0 << 7); /* Code16=0 */ + zr36060_write_8(zr, 0x002, reg); + + switch (mode) { + + case BUZ_MODE_MOTION_DECOMPRESS: + case BUZ_MODE_STILL_DECOMPRESS: + reg = 0x00; /* Codec mode = decompression */ + break; + + case BUZ_MODE_MOTION_COMPRESS: + case BUZ_MODE_STILL_COMPRESS: + default: + reg = 0xa4; /* Codec mode = compression with variable scale factor */ + break; + + } + zr36060_write_8(zr, 0x003, reg); + + reg = 0x00; /* reserved, mbz */ + zr36060_write_8(zr, 0x004, reg); + + reg = 0xff; /* 510 bits/block */ + zr36060_write_8(zr, 0x005, reg); + + /* JPEG markers */ + reg = (zr->params.jpeg_markers) & 0x38; /* DRI, DQT, DHT */ + if (zr->params.COM_len) + reg |= JPEG_MARKER_COM; + if (zr->params.APP_len) + reg |= JPEG_MARKER_APP; + zr36060_write_8(zr, 0x006, reg); + + reg = (0 << 3) /* DATERR=0 */ + |(0 << 2) /* END=0 */ + |(0 << 1) /* EOI=0 */ + |(0 << 0); /* EOAV=0 */ + zr36060_write_8(zr, 0x007, reg); + + /* code volume */ + + /* Target field size in pixels: */ + tvn = &tvnorms[zr->params.norm]; + size = (tvn->Ha / 2) * (tvn->Wa) / (zr->params.HorDcm) / (zr->params.VerDcm); + + /* Target compressed field size in bits: */ + size = size * 16; /* uncompressed size in bits */ + size = size * zr->params.quality / 400; /* quality = 100 is a compression ratio 1:4 */ + + /* Lower limit (arbitrary, 1 KB) */ + if (size < 8192) + size = 8192; + + /* Upper limit: 7/8 of the code buffers */ + if (size * zr->params.field_per_buff > zr->jpg_bufsize * 7) + size = zr->jpg_bufsize * 7 / zr->params.field_per_buff; + + reg = size; + zr36060_write_32(zr, 0x009, reg); + + /* how do we set initial SF as a function of quality parameter? */ + reg = 0x0100; /* SF=1.0 */ + zr36060_write_16(zr, 0x011, reg); + + reg = 0x00ffffff; /* AF=max */ + zr36060_write_24(zr, 0x013, reg); + + reg = 0x0000; /* test */ + zr36060_write_16(zr, 0x024, reg); +} + +static void zr36060_set_video(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + + reg = (0 << 7) /* Video8=0 */ + |(0 << 6) /* Range=0 */ + |(0 << 3) /* FlDet=0 */ + |(1 << 2) /* FlVedge=1 */ + |(0 << 1) /* FlExt=0 */ + |(0 << 0); /* SyncMstr=0 */ + + /* According to ZR36067 documentation, FlDet should correspond + to the odd_even flag of the ZR36067 */ + if (zr->params.odd_even) + reg |= (1 << 3); + + if (mode != BUZ_MODE_STILL_DECOMPRESS) { + /* limit pixels to range 16..235 as per CCIR-601 */ + reg |= (1 << 6); /* Range=1 */ + } + zr36060_write_8(zr, 0x030, reg); + + reg = (0 << 7) /* VCLKPol=0 */ + |(0 << 6) /* PValPol=0 */ + |(1 << 5) /* PoePol=1 */ + |(0 << 4) /* SImgPol=0 */ + |(0 << 3) /* BLPol=0 */ + |(0 << 2) /* FlPol=0 */ + |(0 << 1) /* HSPol=0, sync on falling edge */ + |(1 << 0); /* VSPol=1 */ + zr36060_write_8(zr, 0x031, reg); + + switch (zr->params.HorDcm) { + default: + case 1: + reg = (0 << 0); + break; /* HScale = 0 */ + + case 2: + reg = (1 << 0); + break; /* HScale = 1 */ + + case 4: + reg = (2 << 0); + break; /* HScale = 2 */ + } + if (zr->params.VerDcm == 2) + reg |= (1 << 2); + zr36060_write_8(zr, 0x032, reg); + + reg = 0x80; /* BackY */ + zr36060_write_8(zr, 0x033, reg); + + reg = 0xe0; /* BackU */ + zr36060_write_8(zr, 0x034, reg); + + reg = 0xe0; /* BackV */ + zr36060_write_8(zr, 0x035, reg); + + /* sync generator */ + + tvn = &tvnorms[zr->params.norm]; + + reg = tvn->Ht - 1; /* Vtotal */ + zr36060_write_16(zr, 0x036, reg); + + reg = tvn->Wt - 1; /* Htotal */ + zr36060_write_16(zr, 0x038, reg); + + reg = 6 - 1; /* VsyncSize */ + zr36060_write_8(zr, 0x03a, reg); + + reg = 100 - 1; /* HsyncSize */ + zr36060_write_8(zr, 0x03b, reg); + + reg = tvn->VStart - 1; /* BVstart */ + zr36060_write_8(zr, 0x03c, reg); + + reg += tvn->Ha / 2; /* BVend */ + zr36060_write_16(zr, 0x03e, reg); + + reg = tvn->HStart - 1; /* BHstart */ + zr36060_write_8(zr, 0x03d, reg); + + reg += tvn->Wa; /* BHend */ + zr36060_write_16(zr, 0x040, reg); + + /* active area */ + reg = zr->params.img_y + tvn->VStart; /* Vstart */ + zr36060_write_16(zr, 0x042, reg); + + reg += zr->params.img_height; /* Vend */ + zr36060_write_16(zr, 0x044, reg); + + reg = zr->params.img_x + tvn->HStart; /* Hstart */ + zr36060_write_16(zr, 0x046, reg); + + reg += zr->params.img_width; /* Hend */ + zr36060_write_16(zr, 0x048, reg); + + /* subimage area */ + reg = zr->params.img_y + tvn->VStart; /* SVstart */ + zr36060_write_16(zr, 0x04a, reg); + + reg += zr->params.img_height; /* SVend */ + zr36060_write_16(zr, 0x04c, reg); + + reg = zr->params.img_x + tvn->HStart; /* SHstart */ + zr36060_write_16(zr, 0x04e, reg); + + reg += zr->params.img_width; /* SHend */ + zr36060_write_16(zr, 0x050, reg); +} + +static void zr36060_set_jpg_SOF(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffc0; /* SOF marker */ + zr36060_write_16(zr, 0x060, reg); + + reg = 17; /* SOF length */ + zr36060_write_16(zr, 0x062, reg); + + reg = 8; /* precision 8 bits */ + zr36060_write_8(zr, 0x064, reg); + + reg = zr->params.img_height / zr->params.VerDcm; /* image height */ + zr36060_write_16(zr, 0x065, reg); + + reg = zr->params.img_width / zr->params.HorDcm; /* image width */ + zr36060_write_16(zr, 0x067, reg); + + reg = 3; /* 3 color components */ + zr36060_write_8(zr, 0x069, reg); + + reg = 0x002100; /* Y component */ + zr36060_write_24(zr, 0x06a, reg); + + reg = 0x011101; /* U component */ + zr36060_write_24(zr, 0x06d, reg); + + reg = 0x021101; /* V component */ + zr36060_write_24(zr, 0x070, reg); +} + +static void zr36060_set_jpg_SOS(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffda; /* SOS marker */ + zr36060_write_16(zr, 0x07a, reg); + + reg = 12; /* SOS length */ + zr36060_write_16(zr, 0x07c, reg); + + reg = 3; /* 3 color components */ + zr36060_write_8(zr, 0x07e, reg); + + reg = 0x0000; /* Y component */ + zr36060_write_16(zr, 0x07f, reg); + + reg = 0x0111; /* U component */ + zr36060_write_16(zr, 0x081, reg); + + reg = 0x0211; /* V component */ + zr36060_write_16(zr, 0x083, reg); + + reg = 0x003f00; /* Start, end spectral scans */ + zr36060_write_24(zr, 0x085, reg); +} + +static void zr36060_set_jpg_DRI(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffdd; /* DRI marker */ + zr36060_write_16(zr, 0x0c0, reg); + + reg = 4; /* DRI length */ + zr36060_write_16(zr, 0x0c2, reg); + + reg = 8; /* length in MCUs */ + zr36060_write_16(zr, 0x0c4, reg); +} + +static void zr36060_set_jpg_DQT(struct zoran *zr) +{ + unsigned i; + unsigned adr; + static const u8 dqt[] = + { + 0xff, 0xdb, /* DHT marker */ + 0x00, 0x84, /* DHT length */ + 0x00, /* table ID 0 */ + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0x01, /* table ID 1 */ + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 + }; + + /* write fixed quantitization tables */ + adr = 0x0cc; + for (i = 0; i < sizeof(dqt); ++i) { + zr36060_write_8(zr, adr++, dqt[i]); + } +} + +static void zr36060_set_jpg_DHT(struct zoran *zr) +{ + unsigned i; + unsigned adr; + static const u8 dht[] = + { + 0xff, 0xc4, /* DHT marker */ + 0x01, 0xa2, /* DHT length */ + 0x00, /* table class 0, ID 0 */ + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 8..16 */ + 0x00, /* values for codes of length 2 */ + 0x01, 0x02, 0x03, 0x04, 0x05, /* values for codes of length 3 */ + 0x06, /* values for codes of length 4 */ + 0x07, /* values for codes of length 5 */ + 0x08, /* values for codes of length 6 */ + 0x09, /* values for codes of length 7 */ + 0x0a, /* values for codes of length 8 */ + 0x0b, /* values for codes of length 9 */ + 0x01, /* table class 0, ID 1 */ + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 9..16 */ + 0x00, 0x01, 0x02, /* values for codes of length 2 */ + 0x03, /* values for codes of length 3 */ + 0x04, /* values for codes of length 4 */ + 0x05, /* values for codes of length 5 */ + 0x06, /* values for codes of length 6 */ + 0x07, /* values for codes of length 7 */ + 0x08, /* values for codes of length 8 */ + 0x09, /* values for codes of length 9 */ + 0x0a, /* values for codes of length 10 */ + 0x0b, /* values for codes of length 11 */ + 0x10, + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, + 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, + 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, + 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, + 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, + 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa + }; + + /* write fixed Huffman tables */ + adr = 0x1d4; + for (i = 0; i < sizeof(dht); ++i) { + zr36060_write_8(zr, adr++, dht[i]); + } +} + +static void zr36060_set_jpg_APP(struct zoran *zr) +{ + unsigned adr; + int len, i; + u32 reg; + + + len = zr->params.APP_len; + if (len < 0) + len = 0; + if (len > 60) + len = 60; + + i = zr->params.APPn; + if (i < 0) + i = 0; + if (i > 15) + i = 15; + + reg = 0xffe0 + i; /* APPn marker */ + zr36060_write_16(zr, 0x380, reg); + + reg = len + 2; /* APPn len */ + zr36060_write_16(zr, 0x382, reg); + + /* write APPn data */ + adr = 0x384; + for (i = 0; i < 60; i++) { + zr36060_write_8(zr, adr++, (i < len ? zr->params.APP_data[i] : 0)); + } +} + +static void zr36060_set_jpg_COM(struct zoran *zr) +{ + unsigned adr; + int len, i; + u32 reg; + + + len = zr->params.COM_len; + if (len < 0) + len = 0; + if (len > 60) + len = 60; + + reg = 0xfffe; /* COM marker */ + zr36060_write_16(zr, 0x3c0, reg); + + reg = len + 2; /* COM len */ + zr36060_write_16(zr, 0x3c2, reg); + + /* write COM data */ + adr = 0x3c4; + for (i = 0; i < 60; i++) { + zr36060_write_8(zr, adr++, (i < len ? zr->params.COM_data[i] : 0)); + } +} + +static void zr36060_set_cap(struct zoran *zr, enum zoran_codec_mode mode) +{ + unsigned i; + u32 reg; + + zr36060_reset(zr); + mdelay(10); + + reg = (0 << 7) /* Load=0 */ + |(1 << 0); /* SynRst=1 */ + zr36060_write_8(zr, 0x000, reg); + + zr36060_set_jpg(zr, mode); + zr36060_set_video(zr, mode); + zr36060_set_jpg_SOF(zr); + zr36060_set_jpg_SOS(zr); + zr36060_set_jpg_DRI(zr); + zr36060_set_jpg_DQT(zr); + zr36060_set_jpg_DHT(zr); + zr36060_set_jpg_APP(zr); + zr36060_set_jpg_COM(zr); + + reg = (1 << 7) /* Load=1 */ + |(0 << 0); /* SynRst=0 */ + zr36060_write_8(zr, 0x000, reg); + + /* wait for codec to unbusy */ + for (i = 0; i < 1000; ++i) { + reg = zr36060_read_8(zr, 0x001); + if ((reg & (1 << 7)) == 0) { + DEBUG(printk(KERN_DEBUG "060: loaded, loops=%u\n", i)); + return; + } + udelay(1000); + } + printk(KERN_INFO "060: stuck busy, statux=%02x\n", reg); +} + +static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + int i; + + tvn = &tvnorms[zr->params.norm]; + + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + + /* re-initialize DMA ring stuff */ + zr->jpg_que_head = 0; + zr->jpg_dma_head = 0; + zr->jpg_dma_tail = 0; + zr->jpg_que_tail = 0; + zr->jpg_seq_num = 0; + for (i = 0; i < BUZ_NUM_STAT_COM; ++i) { + zr->stat_com[i] = 1; /* mark as unavailable to zr36057 */ + } + for (i = 0; i < zr->jpg_nbufs; i++) { + zr->jpg_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ + } + + /* MJPEG compression mode */ + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: + default: + reg = ZR36057_JMC_MJPGCmpMode; + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + reg = ZR36057_JMC_MJPGExpMode; + reg |= ZR36057_JMC_SyncMstr; + /* RJ: The following is experimental - improves the output to screen */ + if (zr->params.VFIFO_FB) + reg |= ZR36057_JMC_VFIFO_FB; + break; + + case BUZ_MODE_STILL_COMPRESS: + reg = ZR36057_JMC_JPGCmpMode; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + reg = ZR36057_JMC_JPGExpMode; + break; + + } + reg |= ZR36057_JMC_JPG; + if (zr->params.field_per_buff == 1) + reg |= ZR36057_JMC_Fld_per_buff; + btwrite(reg, ZR36057_JMC); + + /* vertical */ + btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR); + reg = (6 << ZR36057_VSP_VsyncSize) | (tvn->Ht << ZR36057_VSP_FrmTot); + btwrite(reg, ZR36057_VSP); + reg = ((zr->params.img_y + tvn->VStart) << ZR36057_FVAP_NAY) + | (zr->params.img_height << ZR36057_FVAP_PAY); + btwrite(reg, ZR36057_FVAP); + + /* horizontal */ + btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); + reg = ((tvn->Wt - 100) << ZR36057_HSP_HsyncStart) | (tvn->Wt << ZR36057_HSP_LineTot); + btwrite(reg, ZR36057_HSP); + reg = ((zr->params.img_x + tvn->HStart) << ZR36057_FHAP_NAX) + | (zr->params.img_width << ZR36057_FHAP_PAX); + btwrite(reg, ZR36057_FHAP); + + /* field process parameters */ + if (zr->params.odd_even) + reg = ZR36057_FPP_Odd_Even; + else + reg = 0; + btwrite(reg, ZR36057_FPP); + + /* Set proper VCLK Polarity, else colors will be wrong during playback */ + btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR); + + /* code base address and FIFO threshold */ + reg = virt_to_bus(zr->stat_com); + btwrite(reg, ZR36057_JCBA); + reg = 0x50; + btwrite(reg, ZR36057_JCFT); + + /* JPEG codec guest ID */ + reg = (1 << ZR36057_JCGI_JPEGuestID) | (0 << ZR36057_JCGI_JPEGuestReg); + btwrite(reg, ZR36057_JCGI); + + /* Code transfer guest ID */ + reg = (0 << ZR36057_MCTCR_CodGuestID) | (3 << ZR36057_MCTCR_CodGuestReg); + reg |= ZR36057_MCTCR_CFlush; + btwrite(reg, ZR36057_MCTCR); + + /* deassert P_Reset */ + btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); +} + +static void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + static int zero = 0; + static int one = 1; + + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: + zr36060_set_cap(zr, mode); + zr36057_set_jpg(zr, mode); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero); + + /* deassert P_Reset, assert Code transfer enable */ + btwrite(IRQ_MASK, ZR36057_ISR); + btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &zero); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &one); + zr36060_set_cap(zr, mode); + zr36057_set_jpg(zr, mode); + + /* deassert P_Reset, assert Code transfer enable */ + btwrite(IRQ_MASK, ZR36057_ISR); + btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + break; + + case BUZ_MODE_IDLE: + default: + /* shut down processing */ + btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); + btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); + btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC); + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); + btwrite(0, ZR36057_ISR); + zr36060_reset(zr); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero); + break; + + } + zr->codec_mode = mode; +} + +/* + * Queue a MJPEG buffer for capture/playback + */ + +static int jpg_qbuf(struct zoran *zr, int frame, enum zoran_codec_mode mode) +{ + unsigned long flags; + int res; + + /* Check if buffers are allocated */ + + if (!zr->jpg_buffers_allocated) { + printk(KERN_ERR "%s: jpg_qbuf: buffers not yet allocated\n", zr->name); + return -ENOMEM; + } + /* Does the user want to stop streaming? */ + + if (frame < 0) { + if (zr->codec_mode == mode) { + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + return 0; + } else { + printk(KERN_ERR "%s: jpg_qbuf - stop streaming but not in streaming mode\n", zr->name); + return -EINVAL; + } + } + /* No grabbing outside the buffer range! */ + + if (frame >= zr->jpg_nbufs) { + printk(KERN_ERR "%s: jpg_qbuf: buffer %d out of range\n", zr->name, frame); + return -EINVAL; + } + /* what is the codec mode right now? */ + + if (zr->codec_mode == BUZ_MODE_IDLE) { + /* Ok load up the zr36060 and go */ + zr36057_enable_jpg(zr, mode); + } else if (zr->codec_mode != mode) { + /* wrong codec mode active - invalid */ + printk(KERN_ERR "%s: jpg_qbuf - codec in wrong mode\n", zr->name); + return -EINVAL; + } + spin_lock_irqsave(&zr->lock, flags); + + /* make sure a grab isn't going on currently with this buffer */ + + switch (zr->jpg_gbuf[frame].state) { + + default: + case BUZ_STATE_DMA: + case BUZ_STATE_PEND: + case BUZ_STATE_DONE: + res = -EBUSY; /* what are you doing? */ + break; + + case BUZ_STATE_USER: + /* since there is at least one unused buffer there's room for at least one more pend[] entry */ + zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = frame; + zr->jpg_gbuf[frame].state = BUZ_STATE_PEND; + zoran_feed_stat_com(zr); + res = 0; + break; + + } + + spin_unlock_irqrestore(&zr->lock, flags); + + /* Start the zr36060 when the first frame is queued */ + if (zr->jpg_que_head == 1) { + btor(ZR36057_JMC_Go_en, ZR36057_JMC); + btwrite(ZR36057_JPC_P_Reset | ZR36057_JPC_CodTrnsEn | ZR36057_JPC_Active, ZR36057_JPC); + } + return res; +} + +/* + * Sync on a MJPEG buffer + */ + +static int jpg_sync(struct zoran *zr, struct zoran_sync *bs) +{ + unsigned long flags; + int frame; + + if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && + zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { + return -EINVAL; + } + while (zr->jpg_que_tail == zr->jpg_dma_tail) { + interruptible_sleep_on(&zr->jpg_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + spin_lock_irqsave(&zr->lock, flags); + + frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME]; + + /* buffer should now be in BUZ_STATE_DONE */ + + if (zr->jpg_gbuf[frame].state != BUZ_STATE_DONE) + printk(KERN_ERR "%s: jpg_sync - internal error\n", zr->name); + + *bs = zr->jpg_gbuf[frame].bs; + zr->jpg_gbuf[frame].state = BUZ_STATE_USER; + + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} + +/* when this is called the spinlock must be held */ +static void zoran_feed_stat_com(struct zoran *zr) +{ + /* move frames from pending queue to DMA */ + + int frame, i, max_stat_com; + + max_stat_com = (zr->params.TmpDcm == 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); + + while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com + && zr->jpg_dma_head != zr->jpg_que_head) { + + frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME]; + if (zr->params.TmpDcm == 1) { + /* fill 1 stat_com entry */ + i = zr->jpg_dma_head & BUZ_MASK_STAT_COM; + zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; + } else { + /* fill 2 stat_com entries */ + i = (zr->jpg_dma_head & 1) * 2; + zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; + zr->stat_com[i + 1] = zr->jpg_gbuf[frame].frag_tab_bus; + } + zr->jpg_gbuf[frame].state = BUZ_STATE_DMA; + zr->jpg_dma_head++; + + } +} + +/* when this is called the spinlock must be held */ +static void zoran_reap_stat_com(struct zoran *zr) +{ + /* move frames from DMA queue to done queue */ + + int i; + u32 stat_com; + unsigned int seq; + unsigned int dif; + int frame; + struct zoran_gbuffer *gbuf; + + /* In motion decompress we don't have a hardware frame counter, + we just count the interrupts here */ + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) + zr->jpg_seq_num++; + + while (zr->jpg_dma_tail != zr->jpg_dma_head) { + if (zr->params.TmpDcm == 1) + i = zr->jpg_dma_tail & BUZ_MASK_STAT_COM; + else + i = (zr->jpg_dma_tail & 1) * 2 + 1; + + stat_com = zr->stat_com[i]; + + if ((stat_com & 1) == 0) { + return; + } + frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; + gbuf = &zr->jpg_gbuf[frame]; + get_fast_time(&gbuf->bs.timestamp); + + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + gbuf->bs.length = (stat_com & 0x7fffff) >> 1; + + /* update sequence number with the help of the counter in stat_com */ + + seq = stat_com >> 24; + dif = (seq - zr->jpg_seq_num) & 0xff; + zr->jpg_seq_num += dif; + } else { + gbuf->bs.length = 0; + } + gbuf->bs.seq = zr->params.TmpDcm == 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; + gbuf->state = BUZ_STATE_DONE; + + zr->jpg_dma_tail++; + } +} + +static void zoran_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 stat, astat; + int count; + struct zoran *zr; + unsigned long flags; + + zr = (struct zoran *) dev_id; + count = 0; + + spin_lock_irqsave(&zr->lock, flags); + while (1) { + /* get/clear interrupt status bits */ + stat = btread(ZR36057_ISR); + astat = stat & IRQ_MASK; + if (!astat) { + break; + } + btwrite(astat, ZR36057_ISR); + IDEBUG(printk(BUZ_DEBUG "-%u: astat %08x stat %08x\n", zr->id, astat, stat)); + +#if (IRQ_MASK & ZR36057_ISR_GIRQ0) + if (astat & ZR36057_ISR_GIRQ0) { + + /* Interrupts may still happen when zr->v4l_memgrab_active is switched off. + We simply ignore them */ + + if (zr->v4l_memgrab_active) { + +/* A lot more checks should be here ... */ + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0) + printk(KERN_WARNING "%s: BuzIRQ with SnapShot off ???\n", zr->name); + + if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { + /* There is a grab on a frame going on, check if it has finished */ + + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) { + /* it is finished, notify the user */ + + zr->v4l_gbuf[zr->v4l_grab_frame].state = BUZ_STATE_DONE; + zr->v4l_grab_frame = NO_GRAB_ACTIVE; + zr->v4l_grab_seq++; + zr->v4l_pend_tail++; + } + } + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE) + wake_up_interruptible(&zr->v4l_capq); + + /* Check if there is another grab queued */ + + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE && + zr->v4l_pend_tail != zr->v4l_pend_head) { + + int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME]; + u32 reg; + + zr->v4l_grab_frame = frame; + + /* Set zr36057 video front end and enable video */ + + /* Buffer address */ + + reg = zr->v4l_gbuf[frame].fbuffer_bus; + btwrite(reg, ZR36057_VDTR); + if (zr->video_interlace) + reg += zr->gbpl; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + +#ifdef XAWTV_HACK + reg = (zr->gwidth > 720) ? ((zr->gwidth & ~3) - 720) * zr->gbpl / zr->gwidth : 0; +#else + reg = 0; +#endif + if (zr->video_interlace) + reg += zr->gbpl; + reg = (reg << ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; + reg |= ZR36057_VSSFGR_SnapShot; + reg |= ZR36057_VSSFGR_FrameGrab; + btwrite(reg, ZR36057_VSSFGR); + + btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); + } + } + } +#endif /* (IRQ_MASK & ZR36057_ISR_GIRQ0) */ + +#if (IRQ_MASK & ZR36057_ISR_GIRQ1) + if (astat & ZR36057_ISR_GIRQ1) { + unsigned csr = zr36060_read_8(zr, 0x001); + unsigned isr = zr36060_read_8(zr, 0x008); + + IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_GIRQ1 60_code=%02x 60_intr=%02x\n", + zr->name, csr, isr)); + + btand(~ZR36057_ICR_GIRQ1, ZR36057_ICR); + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + } +#endif /* (IRQ_MASK & ZR36057_ISR_GIRQ1) */ + +#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) + if (astat & ZR36057_ISR_CodRepIRQ) { + IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", zr->name)); + btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); + } +#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ + +#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) + if ((astat & ZR36057_ISR_JPEGRepIRQ) && + (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || + zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) { + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + } +#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ + + count++; + if (count > 10) { + printk(KERN_WARNING "%s: irq loop %d\n", zr->name, count); + if (count > 20) { + btwrite(0, ZR36057_ICR); + printk(KERN_ERR "%s: IRQ lockup, cleared int mask\n", zr->name); + break; + } + } + } + spin_unlock_irqrestore(&zr->lock, flags); +} + +/* Check a zoran_params struct for correctness, insert default params */ + +static int zoran_check_params(struct zoran *zr, struct zoran_params *params) +{ + int err = 0, err0 = 0; + + /* insert constant params */ + + params->major_version = MAJOR_VERSION; + params->minor_version = MINOR_VERSION; + + /* Check input and norm */ + + if (params->input != 0 && params->input != 1) { + err++; + } + if (params->norm != VIDEO_MODE_PAL && params->norm != VIDEO_MODE_NTSC) { + err++; + } + /* Check decimation, set default values for decimation = 1, 2, 4 */ + + switch (params->decimation) { + case 1: + + params->HorDcm = 1; + params->VerDcm = 1; + params->TmpDcm = 1; + params->field_per_buff = 2; + + params->img_x = 0; + params->img_y = 0; + params->img_width = 720; + params->img_height = tvnorms[params->norm].Ha / 2; + break; + + case 2: + + params->HorDcm = 2; + params->VerDcm = 1; + params->TmpDcm = 2; + params->field_per_buff = 1; + + params->img_x = 8; + params->img_y = 0; + params->img_width = 704; + params->img_height = tvnorms[params->norm].Ha / 2; + break; + + case 4: + + params->HorDcm = 4; + params->VerDcm = 2; + params->TmpDcm = 2; + params->field_per_buff = 1; + + params->img_x = 8; + params->img_y = 0; + params->img_width = 704; + params->img_height = tvnorms[params->norm].Ha / 2; + break; + + case 0: + + /* We have to check the data the user has set */ + + if (params->HorDcm != 1 && params->HorDcm != 2 && params->HorDcm != 4) + err0++; + if (params->VerDcm != 1 && params->VerDcm != 2) + err0++; + if (params->TmpDcm != 1 && params->TmpDcm != 2) + err0++; + if (params->field_per_buff != 1 && params->field_per_buff != 2) + err0++; + + if (params->img_x < 0) + err0++; + if (params->img_y < 0) + err0++; + if (params->img_width < 0) + err0++; + if (params->img_height < 0) + err0++; + if (params->img_x + params->img_width > 720) + err0++; + if (params->img_y + params->img_height > tvnorms[params->norm].Ha / 2) + err0++; + if (params->img_width % (16 * params->HorDcm) != 0) + err0++; + if (params->img_height % (8 * params->VerDcm) != 0) + err0++; + + if (err0) { + err++; + } + break; + + default: + err++; + break; + } + + if (params->quality > 100) + params->quality = 100; + if (params->quality < 5) + params->quality = 5; + + if (params->APPn < 0) + params->APPn = 0; + if (params->APPn > 15) + params->APPn = 15; + if (params->APP_len < 0) + params->APP_len = 0; + if (params->APP_len > 60) + params->APP_len = 60; + if (params->COM_len < 0) + params->COM_len = 0; + if (params->COM_len > 60) + params->COM_len = 60; + + if (err) + return -EINVAL; + + return 0; + +} +static void zoran_open_init_params(struct zoran *zr) +{ + int i; + + /* Per default, map the V4L Buffers */ + + zr->map_mjpeg_buffers = 0; + + /* User must explicitly set a window */ + + zr->window_set = 0; + + zr->window.x = 0; + zr->window.y = 0; + zr->window.width = 0; + zr->window.height = 0; + zr->window.chromakey = 0; + zr->window.flags = 0; + zr->window.clips = NULL; + zr->window.clipcount = 0; + + zr->video_interlace = 0; + + zr->v4l_memgrab_active = 0; + zr->v4l_overlay_active = 0; + + zr->v4l_grab_frame = NO_GRAB_ACTIVE; + zr->v4l_grab_seq = 0; + + zr->gwidth = 0; + zr->gheight = 0; + zr->gformat = 0; + zr->gbpl = 0; + + /* DMA ring stuff for V4L */ + + zr->v4l_pend_tail = 0; + zr->v4l_pend_head = 0; + for (i = 0; i < v4l_nbufs; i++) { + zr->v4l_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ + } + + /* Set necessary params and call zoran_check_params to set the defaults */ + + zr->params.decimation = 1; + + zr->params.quality = 50; /* default compression factor 8 */ + zr->params.odd_even = 1; + + zr->params.APPn = 0; + zr->params.APP_len = 0; /* No APPn marker */ + for (i = 0; i < 60; i++) + zr->params.APP_data[i] = 0; + + zr->params.COM_len = 0; /* No COM marker */ + for (i = 0; i < 60; i++) + zr->params.COM_data[i] = 0; + + zr->params.VFIFO_FB = 0; + + memset(zr->params.reserved, 0, sizeof(zr->params.reserved)); + + zr->params.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT; + + i = zoran_check_params(zr, &zr->params); + if (i) + printk(KERN_ERR "%s: zoran_open_init_params internal error\n", zr->name); +} + +/* + * Open a buz card. Right now the flags stuff is just playing + */ + +static int zoran_open(struct video_device *dev, int flags) +{ + struct zoran *zr = (struct zoran *) dev; + + DEBUG(printk(KERN_INFO ": zoran_open\n")); + + switch (flags) { + + case 0: + if (zr->user) + return -EBUSY; + zr->user++; + + if (v4l_fbuffer_alloc(zr) < 0) { + zr->user--; + return -ENOMEM; + } + /* default setup */ + + zoran_open_init_params(zr); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + + btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts + + btor(ZR36057_ICR_IntPinEn, ZR36057_ICR); + + break; + + default: + return -EBUSY; + + } + MOD_INC_USE_COUNT; + return 0; +} + +static void zoran_close(struct video_device *dev) +{ + struct zoran *zr = (struct zoran *) dev; + + DEBUG(printk(KERN_INFO ": zoran_close\n")); + + /* disable interrupts */ + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + + /* wake up sleeping beauties */ + wake_up_interruptible(&zr->v4l_capq); + wake_up_interruptible(&zr->jpg_capq); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr36057_set_memgrab(zr, 0); + if (zr->v4l_overlay_active) + zr36057_overlay(zr, 0); + + zr->user--; + + v4l_fbuffer_free(zr); + jpg_fbuffer_free(zr); + zr->jpg_nbufs = 0; + + MOD_DEC_USE_COUNT; + DEBUG(printk(KERN_INFO ": zoran_close done\n")); +} + + +static long zoran_read(struct video_device *dev, char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +static long zoran_write(struct video_device *dev, const char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +/* + * ioctl routine + */ + + +static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct zoran *zr = (struct zoran *) dev; + + switch (cmd) { + + case VIDIOCGCAP: + { + struct video_capability b; + IOCTL_DEBUG(printk("buz ioctl VIDIOCGCAP\n")); + strncpy(b.name, zr->video_dev.name, sizeof(b.name)); + b.type = VID_TYPE_CAPTURE | + VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | + VID_TYPE_FRAMERAM | + VID_TYPE_SCALES; + /* theoretically we could also flag VID_TYPE_SUBCAPTURE + but this is not even implemented in the BTTV driver */ + + b.channels = 2; /* composite, svhs */ + b.audios = 0; + b.maxwidth = BUZ_MAX_WIDTH; + b.maxheight = BUZ_MAX_HEIGHT; + b.minwidth = BUZ_MIN_WIDTH; + b.minheight = BUZ_MIN_HEIGHT; + if (copy_to_user(arg, &b, sizeof(b))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCGCHAN: + { + struct video_channel v; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCGCHAN for channel %d\n", v.channel)); + switch (v.channel) { + case 0: + strcpy(v.name, "Composite"); + break; + case 1: + strcpy(v.name, "SVHS"); + break; + default: + return -EINVAL; + } + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = zr->params.norm; + if (copy_to_user(arg, &v, sizeof(v))) { + return -EFAULT; + } + return 0; + } + + /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says: + + * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input." + * ^^^^^^^ + * The famos BTTV driver has it implemented with a struct video_channel argument + * and we follow it for compatibility reasons + * + * BTW: this is the only way the user can set the norm! + */ + + case VIDIOCSCHAN: + { + struct video_channel v; + int input; + int on, res; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCSCHAN: channel=%d, norm=%d\n", v.channel, v.norm)); + switch (v.channel) { + case 0: + input = 3; + break; + case 1: + input = 7; + break; + default: + return -EINVAL; + } + + if (v.norm != VIDEO_MODE_PAL + && v.norm != VIDEO_MODE_NTSC) { + return -EINVAL; + } + zr->params.norm = v.norm; + zr->params.input = v.channel; + + /* We switch overlay off and on since a change in the norm + needs different VFE settings */ + + on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); + + if (on) + zr36057_overlay(zr, 1); + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + + return 0; + } + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + + case VIDIOCGPICT: + { + struct video_picture p = zr->picture; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCGPICT\n")); + p.depth = zr->buffer.depth; + switch (zr->buffer.depth) { + case 15: + p.palette = VIDEO_PALETTE_RGB555; + break; + + case 16: + p.palette = VIDEO_PALETTE_RGB565; + break; + + case 24: + p.palette = VIDEO_PALETTE_RGB24; + break; + + case 32: + p.palette = VIDEO_PALETTE_RGB32; + break; + } + + if (copy_to_user(arg, &p, sizeof(p))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCSPICT: + { + struct video_picture p; + + if (copy_from_user(&p, arg, sizeof(p))) { + return -EFAULT; + } + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &p); + IOCTL_DEBUG(printk("buz ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n", + p.brightness, p.hue, p.colour, p.contrast, p.depth, p.palette)); + /* The depth and palette values have no meaning to us, + should we return -EINVAL if they don't fit ? */ + zr->picture = p; + return 0; + } + + case VIDIOCCAPTURE: + { + int v, res; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCCAPTURE: %d\n", v)); + /* If there is nothing to do, return immediatly */ + + if ((v && zr->v4l_overlay_active) || (!v && !zr->v4l_overlay_active)) + return 0; + + if (v == 0) { + zr->v4l_overlay_active = 0; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 0); + /* When a grab is running, the video simply won't be switched on any more */ + } else { + if (!zr->buffer_set || !zr->window_set) { + return -EINVAL; + } + zr->v4l_overlay_active = 1; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 1); + /* When a grab is running, the video will be switched on when grab is finished */ + } + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + return 0; + } + + case VIDIOCGWIN: + { + IOCTL_DEBUG(printk("buz ioctl VIDIOCGWIN\n")); + if (copy_to_user(arg, &zr->window, sizeof(zr->window))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCSWIN: + { + struct video_clip *vcp; + struct video_window vw; + int on, end, res; + + if (copy_from_user(&vw, arg, sizeof(vw))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n", vw.x, vw.y, vw.width, vw.height, vw.clipcount)); + if (!zr->buffer_set) { + return -EINVAL; + } + /* + * The video front end needs 4-byte alinged line sizes, we correct that + * silently here if necessary + */ + + if (zr->buffer.depth == 15 || zr->buffer.depth == 16) { + end = (vw.x + vw.width) & ~1; /* round down */ + vw.x = (vw.x + 1) & ~1; /* round up */ + vw.width = end - vw.x; + } + if (zr->buffer.depth == 24) { + end = (vw.x + vw.width) & ~3; /* round down */ + vw.x = (vw.x + 3) & ~3; /* round up */ + vw.width = end - vw.x; + } +#if 0 + // At least xawtv seems to care about the following - just leave it away + /* + * Also corrected silently (as long as window fits at all): + * video not fitting the screen + */ +#if 0 + if (vw.x < 0 || vw.y < 0 || vw.x + vw.width > zr->buffer.width || + vw.y + vw.height > zr->buffer.height) { + printk(BUZ_ERR ": VIDIOCSWIN: window does not fit frame buffer: %dx%d+%d*%d\n", + vw.width, vw.height, vw.x, vw.y); + return -EINVAL; + } +#else + if (vw.x < 0) + vw.x = 0; + if (vw.y < 0) + vw.y = 0; + if (vw.x + vw.width > zr->buffer.width) + vw.width = zr->buffer.width - vw.x; + if (vw.y + vw.height > zr->buffer.height) + vw.height = zr->buffer.height - vw.y; +#endif +#endif + + /* Check for vaild parameters */ + if (vw.width < BUZ_MIN_WIDTH || vw.height < BUZ_MIN_HEIGHT || + vw.width > BUZ_MAX_WIDTH || vw.height > BUZ_MAX_HEIGHT) { + return -EINVAL; + } +#ifdef XAWTV_HACK + if (vw.width > 720) + vw.width = 720; +#endif + + zr->window.x = vw.x; + zr->window.y = vw.y; + zr->window.width = vw.width; + zr->window.height = vw.height; + zr->window.chromakey = 0; + zr->window.flags = 0; // RJ: Is this intended for interlace on/off ? + + zr->window.clips = NULL; + zr->window.clipcount = vw.clipcount; + + /* + * If an overlay is running, we have to switch it off + * and switch it on again in order to get the new settings in effect. + * + * We also want to avoid that the overlay mask is written + * when an overlay is running. + */ + + on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + /* + * Write the overlay mask if clips are wanted. + */ + if (vw.clipcount) { + vcp = vmalloc(sizeof(struct video_clip) * (vw.clipcount + 4)); + if (vcp == NULL) { + return -ENOMEM; + } + if (copy_from_user(vcp, vw.clips, sizeof(struct video_clip) * vw.clipcount)) { + vfree(vcp); + return -EFAULT; + } + write_overlay_mask(zr, vcp, vw.clipcount); + vfree(vcp); + } + if (on) + zr36057_overlay(zr, 1); + zr->window_set = 1; + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + + return 0; + } + + case VIDIOCGFBUF: + { + IOCTL_DEBUG(printk("buz ioctl VIDIOCGFBUF\n")); + if (copy_to_user(arg, &zr->buffer, sizeof(zr->buffer))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCSFBUF: + { + struct video_buffer v; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n", (u32) v.base, v.width, v.height, v.depth, v.bytesperline)); + if (zr->v4l_overlay_active) { + /* Has the user gotten crazy ... ? */ + return -EINVAL; + } + if (v.depth != 15 + && v.depth != 16 + && v.depth != 24 + && v.depth != 32) { + return -EINVAL; + } + if (v.height <= 0 || v.width <= 0 || v.bytesperline <= 0) { + return -EINVAL; + } + if (v.bytesperline & 3) { + return -EINVAL; + } + if (v.base) { + zr->buffer.base = (void *) ((unsigned long) v.base & ~3); + } + zr->buffer.height = v.height; + zr->buffer.width = v.width; + zr->buffer.depth = v.depth; + zr->buffer.bytesperline = v.bytesperline; + + if (zr->buffer.base) + zr->buffer_set = 1; + zr->window_set = 0; /* The user should set new window parameters */ + return 0; + } + + /* RJ: what is VIDIOCKEY intended to do ??? */ + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + case VIDIOCSYNC: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCSYNC %d\n", v)); + return v4l_sync(zr, v); + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n", + vm.frame, vm.height, vm.width, vm.format)); + return v4l_grab(zr, &vm); + } + + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCGMBUF\n")); + + vm.size = v4l_nbufs * v4l_bufsize; + vm.frames = v4l_nbufs; + for (i = 0; i < v4l_nbufs; i++) { + vm.offsets[i] = i * v4l_bufsize; + } + + /* The next mmap will map the V4L buffers */ + zr->map_mjpeg_buffers = 0; + + if (copy_to_user(arg, &vm, sizeof(vm))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCGUNIT: + { + struct video_unit vu; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCGUNIT\n")); + vu.video = zr->video_dev.minor; + vu.vbi = VIDEO_NO_UNIT; + vu.radio = VIDEO_NO_UNIT; + vu.audio = VIDEO_NO_UNIT; + vu.teletext = VIDEO_NO_UNIT; + if (copy_to_user(arg, &vu, sizeof(vu))) + return -EFAULT; + return 0; + } + + /* + * RJ: In principal we could support subcaptures for V4L grabbing. + * Not even the famous BTTV driver has them, however. + * If there should be a strong demand, one could consider + * to implement them. + */ + case VIDIOCGCAPTURE: + case VIDIOCSCAPTURE: + return -EINVAL; + + case BUZIOC_G_PARAMS: + { + IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_PARAMS\n")); + if (copy_to_user(arg, &(zr->params), sizeof(zr->params))) + return -EFAULT; + return 0; + } + + case BUZIOC_S_PARAMS: + { + struct zoran_params bp; + int input, on; + + if (zr->codec_mode != BUZ_MODE_IDLE) { + return -EINVAL; + } + if (copy_from_user(&bp, arg, sizeof(bp))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_S_PARAMS\n")); + + /* Check the params first before overwriting our internal values */ + + if (zoran_check_params(zr, &bp)) + return -EINVAL; + + zr->params = bp; + + /* Make changes of input and norm go into effect immediatly */ + + /* We switch overlay off and on since a change in the norm + needs different VFE settings */ + + on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + input = zr->params.input == 0 ? 3 : 7; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); + + if (on) + zr36057_overlay(zr, 1); + + if (copy_to_user(arg, &bp, sizeof(bp))) { + return -EFAULT; + } + return 0; + } + + case BUZIOC_REQBUFS: + { + struct zoran_requestbuffers br; + + if (zr->jpg_buffers_allocated) { + return -EINVAL; + } + if (copy_from_user(&br, arg, sizeof(br))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_REQBUFS count = %lu size=%lu\n", + br.count, br.size)); + /* Enforce reasonable lower and upper limits */ + if (br.count < 4) + br.count = 4; /* Could be choosen smaller */ + if (br.count > BUZ_MAX_FRAME) + br.count = BUZ_MAX_FRAME; + br.size = PAGE_ALIGN(br.size); + if (br.size < 8192) + br.size = 8192; /* Arbitrary */ + /* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */ + if (br.size > (512 * 1024)) + br.size = (512 * 1024); /* 512 K should be enough */ + if (zr->need_contiguous && br.size > MAX_KMALLOC_MEM) + br.size = MAX_KMALLOC_MEM; + + zr->jpg_nbufs = br.count; + zr->jpg_bufsize = br.size; + + if (jpg_fbuffer_alloc(zr)) + return -ENOMEM; + + /* The next mmap will map the MJPEG buffers */ + zr->map_mjpeg_buffers = 1; + + if (copy_to_user(arg, &br, sizeof(br))) { + return -EFAULT; + } + return 0; + } + + case BUZIOC_QBUF_CAPT: + { + int nb; + + if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_CAPT %d\n", nb)); + return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS); + } + + case BUZIOC_QBUF_PLAY: + { + int nb; + + if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_PLAY %d\n", nb)); + return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_DECOMPRESS); + } + + case BUZIOC_SYNC: + { + struct zoran_sync bs; + int res; + + IOCTL_DEBUG(printk("buz ioctl BUZIOC_SYNC\n")); + res = jpg_sync(zr, &bs); + if (copy_to_user(arg, &bs, sizeof(bs))) { + return -EFAULT; + } + return res; + } + + case BUZIOC_G_STATUS: + { + struct zoran_status bs; + int norm, input, status; + unsigned long timeout; + + if (zr->codec_mode != BUZ_MODE_IDLE) { + return -EINVAL; + } + if (copy_from_user(&bs, arg, sizeof(bs))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_STATUS\n")); + switch (bs.input) { + case 0: + input = 3; + break; + case 1: + input = 7; + break; + default: + return -EINVAL; + } + + /* Set video norm to VIDEO_MODE_AUTO */ + + norm = VIDEO_MODE_AUTO; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &norm); + + /* sleep 1 second */ + + schedule_timeout(HZ); + + /* Get status of video decoder */ + + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_GET_STATUS, &status); + bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0; + bs.norm = (status & DECODER_STATUS_NTSC) ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL; + bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0; + + /* restore previous input and norm */ + input = zr->params.input == 0 ? 3 : 7; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + + if (copy_to_user(arg, &bs, sizeof(bs))) { + return -EFAULT; + } + return 0; + } + + default: + return -ENOIOCTLCMD; + + } + return 0; +} + + +/* + * This maps the buffers to user space. + * + * Depending on the state of zr->map_mjpeg_buffers + * the V4L or the MJPEG buffers are mapped + * + */ + +static int zoran_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct zoran *zr = (struct zoran *) dev; + unsigned long start = (unsigned long) adr; + unsigned long page, pos, todo, fraglen; + int i, j; + + if (zr->map_mjpeg_buffers) { + /* Map the MJPEG buffers */ + + if (!zr->jpg_buffers_allocated) { + return -ENOMEM; + } + if (size > zr->jpg_nbufs * zr->jpg_bufsize) { + return -EINVAL; + } + + for (i = 0; i < zr->jpg_nbufs; i++) { + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + fraglen = (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & ~1) << 1; + todo = size; + if (todo > fraglen) + todo = fraglen; + pos = (unsigned long) zr->jpg_gbuf[i].frag_tab[2 * j]; + page = virt_to_phys(bus_to_virt(pos)); /* should just be pos on i386 */ + if (remap_page_range(start, page, todo, PAGE_SHARED)) { + printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name); + return -EAGAIN; + } + size -= todo; + start += todo; + if (size == 0) + break; + if (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & 1) + break; /* was last fragment */ + } + if (size == 0) + break; + } + } else { + /* Map the V4L buffers */ + + if (size > v4l_nbufs * v4l_bufsize) { + return -EINVAL; + } + + for (i = 0; i < v4l_nbufs; i++) { + todo = size; + if (todo > v4l_bufsize) + todo = v4l_bufsize; + page = zr->v4l_gbuf[i].fbuffer_phys; + DEBUG(printk("V4L remap page range %d 0x%x %d to 0x%x\n", i, page, todo, start)); + if (remap_page_range(start, page, todo, PAGE_SHARED)) { + printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name); + return -EAGAIN; + } + size -= todo; + start += todo; + if (size == 0) + break; + } + } + return 0; +} + +static int zoran_init_done(struct video_device *dev) +{ + return 0; +} + +static struct video_device zoran_template = +{ + BUZ_NAME, + VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | + VID_TYPE_SCALES | VID_TYPE_SUBCAPTURE, + VID_HARDWARE_BT848, /* Not true, but the buz is not yet in the list */ + zoran_open, + zoran_close, + zoran_read, + zoran_write, + NULL, + zoran_ioctl, + zoran_mmap, + zoran_init_done, + NULL, + 0, + 0 +}; + +static int zr36057_init(int i) +{ + struct zoran *zr = &zoran[i]; + unsigned long mem; + unsigned mem_needed; + int j; + int rev; + + /* reset zr36057 */ + btwrite(0, ZR36057_SPGPPCR); + mdelay(10); + + /* default setup of all parameters which will persist beetween opens */ + + zr->user = 0; + + zr->map_mjpeg_buffers = 0; /* Map V4L buffers by default */ + + zr->jpg_nbufs = 0; + zr->jpg_bufsize = 0; + zr->jpg_buffers_allocated = 0; + + zr->buffer_set = 0; /* Flag if frame buffer has been set */ + zr->buffer.base = (void *) vidmem; + zr->buffer.width = 0; + zr->buffer.height = 0; + zr->buffer.depth = 0; + zr->buffer.bytesperline = 0; + + zr->params.norm = default_norm ? 1 : 0; /* Avoid nonsense settings from user */ + zr->params.input = default_input ? 1 : 0; /* Avoid nonsense settings from user */ + zr->video_interlace = 0; + + /* Should the following be reset at every open ? */ + + zr->picture.colour = 32768; + zr->picture.brightness = 32768; + zr->picture.hue = 32768; + zr->picture.contrast = 32768; + zr->picture.whiteness = 0; + zr->picture.depth = 0; + zr->picture.palette = 0; + + for (j = 0; j < VIDEO_MAX_FRAME; j++) { + zr->v4l_gbuf[i].fbuffer = 0; + zr->v4l_gbuf[i].fbuffer_phys = 0; + zr->v4l_gbuf[i].fbuffer_bus = 0; + } + + zr->stat_com = 0; + + /* default setup (will be repeated at every open) */ + + zoran_open_init_params(zr); + + /* allocate memory *before* doing anything to the hardware in case allocation fails */ + + /* STAT_COM table and overlay mask */ + + mem_needed = (BUZ_NUM_STAT_COM + ((BUZ_MAX_WIDTH + 31) / 32) * BUZ_MAX_HEIGHT) * 4; + mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL); + if (!mem) { + return -ENOMEM; + } + memset((void *) mem, 0, mem_needed); + + zr->stat_com = (u32 *) mem; + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */ + } + zr->overlay_mask = (u32 *) (mem + BUZ_NUM_STAT_COM * 4); + + /* Initialize zr->jpg_gbuf */ + + for (j = 0; j < BUZ_MAX_FRAME; j++) { + zr->jpg_gbuf[j].frag_tab = 0; + zr->jpg_gbuf[j].frag_tab_bus = 0; + zr->jpg_gbuf[j].state = BUZ_STATE_USER; + zr->jpg_gbuf[j].bs.frame = j; + } + + /* take zr36057 out of reset now */ + btwrite(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR); + mdelay(10); + + /* stop all DMA processes */ + btwrite(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + + switch(zr->board) + { + case BOARD_BUZ: + + /* set up GPIO direction */ + btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR); + + /* Set up guest bus timing - Guests 0..3 Tdur=12, Trec=3 */ + btwrite((GPIO_MASK << 24) | 0x8888, ZR36057_GPPGCR1); + mdelay(10); + + /* reset video decoder */ + + GPIO(zr, 0, 0); + mdelay(10); + GPIO(zr, 0, 1); + mdelay(10); + + /* reset JPEG codec */ + zr36060_sleep(zr, 0); + mdelay(10); + zr36060_reset(zr); + mdelay(10); + zr36060_sleep(zr, 1); + mdelay(10); + + /* display codec revision */ + if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) { + printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n", + zr->name, zr36060_read_8(zr, 0x023)); + } else { + printk(KERN_ERR "%s: Zoran ZR36060 not found (Rev=%d)\n", zr->name, rev); + kfree((void *) zr->stat_com); + return -1; + } + break; + + case BOARD_LML33: +// btwrite(btread(ZR36057_SPGPPCR)&~ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR); +// udelay(100); +// btwrite(btread(ZR36057_SPGPPCR)|ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR); +// udelay(1000); + + /* + * Set up the GPIO direction + */ + btwrite(btread(ZR36057_SPGPPCR_SoftReset)|0 , ZR36057_SPGPPCR); + /* Set up guest bus timing - Guests 0..2 Tdur=12, Trec=3 */ + btwrite(0xFF00F888, ZR36057_GPPGCR1); + mdelay(10); + GPIO(zr, 5, 0); /* Analog video bypass */ + udelay(3000); + GPIO(zr, 0, 0); /* Reset 819 */ + udelay(3000); + GPIO(zr, 0, 1); /* 819 back */ + udelay(3000); + /* reset JPEG codec */ + zr36060_sleep(zr, 0); + udelay(3000); + zr36060_reset(zr); + udelay(3000); + zr36060_sleep(zr, 1); + udelay(3000); + + /* display codec revision */ + if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) { + printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n", + zr->name, zr36060_read_8(zr, 0x023)); + } else { + printk(KERN_ERR "%s: Zoran ZR36060 not found (rev=%d)\n", zr->name, rev); +// kfree((void *) zr->stat_com); +// return -1; + } + break; + } + /* i2c */ + memcpy(&zr->i2c, &zoran_i2c_bus_template, sizeof(struct i2c_bus)); + sprintf(zr->i2c.name, "zoran%u%u", zr->id); + zr->i2c.data = zr; + if (i2c_register_bus(&zr->i2c) < 0) { + kfree((void *) zr->stat_com); + return -1; + } + /* + * Now add the template and register the device unit. + */ + memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template)); + sprintf(zr->video_dev.name, "zoran%u", zr->id); + if (video_register_device(&zr->video_dev, VFL_TYPE_GRABBER) < 0) { + i2c_unregister_bus(&zr->i2c); + kfree((void *) zr->stat_com); + return -1; + } + /* toggle JPEG codec sleep to sync PLL */ + zr36060_sleep(zr, 1); + mdelay(10); + zr36060_sleep(zr, 0); + mdelay(10); + + /* Enable bus-mastering */ + pci_set_master(zr->pci_dev); + + j = zr->params.input == 0 ? 3 : 7; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &j); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); + + /* set individual interrupt enables (without GIRQ0) + but don't global enable until zoran_open() */ + + btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ0, ZR36057_ICR); + + if(request_irq(zr->pci_dev->irq, zoran_irq, + SA_SHIRQ | SA_INTERRUPT, zr->name, (void *) zr)<0) + { + printk(KERN_ERR "%s: Can't assign irq.\n", zr->name); + video_unregister_device(&zr->video_dev); + i2c_unregister_bus(&zr->i2c); + kfree((void *) zr->stat_com); + return -1; + } + zr->initialized = 1; + return 0; +} + + + +static void release_zoran(void) +{ + u8 command; + int i; + struct zoran *zr; + + for (i = 0; i < zoran_num; i++) { + zr = &zoran[i]; + + if (!zr->initialized) + continue; + + /* unregister i2c_bus */ + i2c_unregister_bus((&zr->i2c)); + + /* disable PCI bus-mastering */ + pci_read_config_byte(zr->pci_dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(zr->pci_dev, PCI_COMMAND, command); + + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + + free_irq(zr->pci_dev->irq, zr); + + /* unmap and free memory */ + + kfree((void *) zr->stat_com); + + iounmap(zr->zr36057_mem); + + video_unregister_device(&zr->video_dev); + } +} + +/* + * Scan for a Buz card (actually for the PCI controller ZR36057), + * request the irq and map the io memory + */ + +static int find_zr36057(void) +{ + unsigned char latency; + struct zoran *zr; + struct pci_dev *dev = NULL; + int result; + + zoran_num = 0; + + while (zoran_num < BUZ_MAX + && (dev = pci_find_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) { + zr = &zoran[zoran_num]; + zr->pci_dev = dev; + zr->zr36057_mem = NULL; + zr->id = zoran_num; + sprintf(zr->name, "zoran%u", zr->id); + + spin_lock_init(&zr->lock); + + zr->zr36057_adr = zr->pci_dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; + pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision); + if (zr->revision < 2) { + printk(KERN_INFO "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n", + zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr); + } else { + unsigned short ss_vendor_id, ss_id; + + pci_read_config_word(zr->pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor_id); + pci_read_config_word(zr->pci_dev, PCI_SUBSYSTEM_ID, &ss_id); + printk(KERN_INFO "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n", + zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr); + printk(KERN_INFO "%s: subsystem vendor=0x%04x id=0x%04x\n", + zr->name, ss_vendor_id, ss_id); + if(ss_vendor_id==0xFF10 && ss_id == 0xDE41) + { + zr->board = BOARD_LML33; + printk(KERN_INFO "%s: LML33 detected.\n", zr->name); + } + } + + zr->zr36057_mem = ioremap(zr->zr36057_adr, 0x1000); + + /* set PCI latency timer */ + pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, &latency); + if (latency != 48) { + printk(KERN_INFO "%s: Changing PCI latency from %d to 48.\n", zr->name, latency); + latency = 48; + pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, latency); + } + zoran_num++; + } + if (zoran_num == 0) + printk(KERN_INFO "zoran: no cards found.\n"); + + return zoran_num; +} + +#include "chipsets.h" + +static void handle_chipset(void) +{ + int index; + struct pci_dev *dev = NULL; + + for (index = 0; index < sizeof(black) / sizeof(black[0]); index++) { + if ((dev = pci_find_device(black[index].vendor, black[index].device, dev)) != NULL) { + printk(KERN_INFO ": Host bridge: %s, ", black[index].name); + switch (black[index].action) { + + case TRITON: + printk("enabling Triton support.\n"); + triton = 1; + break; + + case NATOMA: + printk("enabling Natoma workaround.\n"); + natoma = 1; + break; + } + } + } +} + +#ifdef MODULE +int init_module(void) +#else +int init_zoran_cards(struct video_init *unused) +#endif +{ + int i; + + + printk(KERN_INFO "Zoran driver 1.00 (c) 1999 Rainer Johanni, Dave Perks.\n"); + + /* Look for Buz cards */ + + if (find_zr36057() <= 0) { + return -EIO; + } + printk(KERN_INFO"zoran: %d zoran card(s) found\n", zoran_num); + + if (zoran_num == 0) + return -ENXIO; + + + /* check the parameters we have been given, adjust if necessary */ + + if (v4l_nbufs < 0) + v4l_nbufs = 0; + if (v4l_nbufs > VIDEO_MAX_FRAME) + v4l_nbufs = VIDEO_MAX_FRAME; + /* The user specfies the in KB, we want them in byte (and page aligned) */ + v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); + if (v4l_bufsize < 32768) + v4l_bufsize = 32768; + /* 2 MB is arbitrary but sufficient for the maximum possible images */ + if (v4l_bufsize > 2048 * 1024) + v4l_bufsize = 2048 * 1024; + + printk(KERN_INFO "zoran: using %d V4L buffers of size %d KB\n", v4l_nbufs, v4l_bufsize >> 10); + + /* Use parameter for vidmem or try to find a video card */ + + if (vidmem) { + printk(KERN_INFO "zoran: Using supplied video memory base address @ 0x%lx\n", vidmem); + } + + /* check if we have a Triton or Natome chipset */ + + handle_chipset(); + + /* take care of Natoma chipset and a revision 1 zr36057 */ + + for (i = 0; i < zoran_num; i++) { + if (natoma && zoran[i].revision <= 1) { + zoran[i].need_contiguous = 1; + printk(KERN_INFO "%s: ZR36057/Natome bug, max. buffer size is 128K\n", zoran[i].name); + } else { + zoran[i].need_contiguous = 0; + } + } + + /* initialize the Buzs */ + + /* We have to know which ones must be released if an error occurs */ + for (i = 0; i < zoran_num; i++) + zoran[i].initialized = 0; + + for (i = 0; i < zoran_num; i++) { + if (zr36057_init(i) < 0) { + release_zoran(); + return -EIO; + } + } + + return 0; +} + + + +#ifdef MODULE + +void cleanup_module(void) +{ + release_zoran(); +} + +#endif diff --git a/drivers/char/buz.h b/drivers/char/buz.h new file mode 100644 index 000000000000..a0df7b01250c --- /dev/null +++ b/drivers/char/buz.h @@ -0,0 +1,319 @@ +/* + buz - Iomega Buz driver + + Copyright (C) 1999 Rainer Johanni + + based on + + buz.0.0.3 Copyright (C) 1998 Dave Perks + + and + + bttv - Bt848 frame grabber driver + Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) + + 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. + */ + +#ifndef _BUZ_H_ +#define _BUZ_H_ + +/* The Buz only supports a maximum width of 720, but some V4L + applications (e.g. xawtv are more happy with 768). + If XAWTV_HACK is defined, we try to fake a device with bigger width */ + +#define XAWTV_HACK + +#ifdef XAWTV_HACK +#define BUZ_MAX_WIDTH 768 /* never display more than 768 pixels */ +#else +#define BUZ_MAX_WIDTH 720 /* never display more than 720 pixels */ +#endif +#define BUZ_MAX_HEIGHT 576 /* never display more than 576 rows */ +#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ +#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ + +struct zoran_requestbuffers { + unsigned long count; /* Number of buffers for MJPEG grabbing */ + unsigned long size; /* Size PER BUFFER in bytes */ +}; + +struct zoran_sync { + unsigned long frame; /* number of buffer that has been free'd */ + unsigned long length; /* number of code bytes in buffer (capture only) */ + unsigned long seq; /* frame sequence number */ + struct timeval timestamp; /* timestamp */ +}; + +struct zoran_status { + int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ + int signal; /* Returned: 1 if valid video signal detected */ + int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int color; /* Returned: 1 if color signal detected */ +}; + +struct zoran_params { + + /* The following parameters can only be queried */ + + int major_version; /* Major version number of driver */ + int minor_version; /* Minor version number of driver */ + + /* Main control parameters */ + + int input; /* Input channel: 0 = Composite, 1 = S-VHS */ + int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int decimation; /* decimation of captured video, + enlargement of video played back. + Valid values are 1, 2, 4 or 0. + 0 is a special value where the user + has full control over video scaling */ + + /* The following parameters only have to be set if decimation==0, + for other values of decimation they provide the data how the image is captured */ + + int HorDcm; /* Horizontal decimation: 1, 2 or 4 */ + int VerDcm; /* Vertical decimation: 1 or 2 */ + int TmpDcm; /* Temporal decimation: 1 or 2, + if TmpDcm==2 in capture every second frame is dropped, + in playback every frame is played twice */ + int field_per_buff; /* Number of fields per buffer: 1 or 2 */ + int img_x; /* start of image in x direction */ + int img_y; /* start of image in y direction */ + int img_width; /* image width BEFORE decimation, + must be a multiple of HorDcm*16 */ + int img_height; /* image height BEFORE decimation, + must be a multiple of VerDcm*8 */ + + /* --- End of parameters for decimation==0 only --- */ + + /* JPEG control parameters */ + + int quality; /* Measure for quality of compressed images. + Scales linearly with the size of the compressed images. + Must be beetween 0 and 100, 100 is a compression + ratio of 1:4 */ + + int odd_even; /* Which field should come first ??? */ + + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ + + unsigned long jpeg_markers; /* Which markers should go into the JPEG output. + Unless you exactly know what you do, leave them untouched. + Inluding less markers will make the resulting code + smaller, but there will be fewer aplications + which can read it. + The presence of the APP and COM marker is + influenced by APP0_len and COM_len ONLY! */ +#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ +#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ +#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ +#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ + + int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. + If this flag is turned on and JPEG decompressing + is going to the screen, the decompress process + is stopped every time the Video Fifo is full. + This enables a smooth decompress to the screen + but the video output signal will get scrambled */ + + /* Misc */ + + char reserved[312]; /* Makes 512 bytes for this structure */ +}; + +/* + Private IOCTL to set up for displaying MJPEG + */ +#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params) +#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params) +#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers) +#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) +#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) +#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync) +#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status) + + +#ifdef __KERNEL__ + +#define BUZ_NUM_STAT_COM 4 +#define BUZ_MASK_STAT_COM 3 + +#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ +#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ + +#if VIDEO_MAX_FRAME <= 32 +#define V4L_MAX_FRAME 32 +#elif VIDEO_MAX_FRAME <= 64 +#define V4L_MAX_FRAME 64 +#else +#error "Too many video frame buffers to handle" +#endif +#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) + + +#include "zr36057.h" + +enum zoran_codec_mode { + BUZ_MODE_IDLE, /* nothing going on */ + BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ + BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ + BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ + BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ +}; + +enum zoran_buffer_state { + BUZ_STATE_USER, /* buffer is owned by application */ + BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */ + BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */ + BUZ_STATE_DONE /* buffer is ready to return to application */ +}; + +struct zoran_gbuffer { + u32 *frag_tab; /* addresses of frag table */ + u32 frag_tab_bus; /* same value cached to save time in ISR */ + enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ + struct zoran_sync bs; /* DONE: info to return to application */ +}; + +struct v4l_gbuffer { + char *fbuffer; /* virtual address of frame buffer */ + unsigned long fbuffer_phys; /* physical address of frame buffer */ + unsigned long fbuffer_bus; /* bus address of frame buffer */ + enum zoran_buffer_state state; /* state: unused/pending/done */ +}; + +struct zoran { + struct video_device video_dev; + struct i2c_bus i2c; + + int initialized; /* flag if zoran has been correctly initalized */ + int user; /* number of current users (0 or 1) */ + + unsigned short id; /* number of this device */ + char name[32]; /* name of this device */ + struct pci_dev *pci_dev; /* PCI device */ + unsigned char revision; /* revision of zr36057 */ + int board; /* Board type */ +#define BOARD_BUZ 0 +#define BOARD_LML33 1 + unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */ + unsigned char *zr36057_mem; /* pointer to mapped IO memory */ + + int map_mjpeg_buffers; /* Flag which bufferset will map by next mmap() */ + + spinlock_t lock; /* Spinlock */ + + /* Video for Linux parameters */ + + struct video_picture picture; /* Current picture params */ + struct video_buffer buffer; /* Current buffer params */ + struct video_window window; /* Current window params */ + int buffer_set, window_set; /* Flags if the above structures are set */ + int video_interlace; /* Image on screen is interlaced */ + + u32 *overlay_mask; + + struct wait_queue *v4l_capq; /* wait here for grab to finish */ + + int v4l_overlay_active; /* Overlay grab is activated */ + int v4l_memgrab_active; /* Memory grab is activated */ + + int v4l_grab_frame; /* Frame number being currently grabbed */ +#define NO_GRAB_ACTIVE (-1) + int v4l_grab_seq; /* Number of frames grabbed */ + int gwidth; /* Width of current memory capture */ + int gheight; /* Height of current memory capture */ + int gformat; /* Format of ... */ + int gbpl; /* byte per line of ... */ + + /* V4L grab queue of frames pending */ + + unsigned v4l_pend_head; + unsigned v4l_pend_tail; + int v4l_pend[V4L_MAX_FRAME]; + + struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME]; /* V4L buffers' info */ + + /* Buz MJPEG parameters */ + + unsigned long jpg_nbufs; /* Number of buffers */ + unsigned long jpg_bufsize; /* Size of mjpeg buffers in bytes */ + int jpg_buffers_allocated; /* Flag if buffers are allocated */ + int need_contiguous; /* Flag if contiguous buffers are needed */ + + enum zoran_codec_mode codec_mode; /* status of codec */ + struct zoran_params params; /* structure with a lot of things to play with */ + + struct wait_queue *jpg_capq; /* wait here for grab to finish */ + + /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ + /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ + /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ + unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ + unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ + unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ + unsigned long jpg_que_tail; /* Index of last buffer in queue */ + unsigned long jpg_seq_num; /* count of frames since grab/play started */ + + /* zr36057's code buffer table */ + u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + + /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ + int jpg_pend[BUZ_MAX_FRAME]; + + /* array indexed by frame number */ + struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME]; /* MJPEG buffers' info */ +}; + +#endif + +/*The following should be done in more portable way. It depends on define + of _ALPHA_BUZ in the Makefile. */ + +#ifdef _ALPHA_BUZ +#define btwrite(dat,adr) writel((dat),(char *) (zr->zr36057_adr+(adr))) +#define btread(adr) readl(zr->zr36057_adr+(adr)) +#else +#define btwrite(dat,adr) writel((dat), (char *) (zr->zr36057_mem+(adr))) +#define btread(adr) readl(zr->zr36057_mem+(adr)) +#endif + +#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) + +#define I2C_TSA5522 0xc2 +#define I2C_TDA9850 0xb6 +#define I2C_HAUPEE 0xa0 +#define I2C_STBEE 0xae +#define I2C_SAA7111 0x48 +#define I2C_SAA7185 0x88 + +#define TDA9850_CON1 0x04 +#define TDA9850_CON2 0x05 +#define TDA9850_CON3 0x06 +#define TDA9850_CON4 0x07 +#define TDA9850_ALI1 0x08 +#define TDA9850_ALI2 0x09 +#define TDA9850_ALI3 0x0a + +#endif diff --git a/drivers/char/chipsets.h b/drivers/char/chipsets.h new file mode 100644 index 000000000000..80c952553547 --- /dev/null +++ b/drivers/char/chipsets.h @@ -0,0 +1,41 @@ +static const struct { + unsigned short vendor; + unsigned short device; + enum { + TRITON, + NATOMA + } action; + const char *name; +} black[] = { + + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, TRITON, "82437" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX, TRITON, "82437VX Triton II" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439, TRITON, "82439HX Triton II" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439TX, TRITON, "82439TX" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, NATOMA, "82441FX Natoma" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_0, NATOMA, "440LX - 82443LX PAC Host" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_1, NATOMA, "440LX - 82443LX PAC AGP" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0, NATOMA, "440BX - 82443BX Host" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_1, NATOMA, "440BX - 82443BX AGP" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, NATOMA, "440BX - 82443BX Host (no AGP)" + }, +}; diff --git a/drivers/char/i2c.c b/drivers/char/i2c.c index c85b95bc3a1a..452583f86441 100644 --- a/drivers/char/i2c.c +++ b/drivers/char/i2c.c @@ -40,6 +40,14 @@ static int bus_count = 0, driver_count = 0; extern int i2c_tuner_init(void); extern int msp3400c_init(void); #endif +#ifdef CONFIG_VIDEO_BUZ +extern int saa7111_init(void); +extern int saa7185_init(void); +#endif +#ifdef CONFIG_VIDEO_LML33 +extern int bt819_init(void); +extern int bt856_init(void); +#endif int i2c_init(void) { @@ -50,6 +58,14 @@ int i2c_init(void) i2c_tuner_init(); msp3400c_init(); #endif +#ifdef CONFIG_VIDEO_BUZ + saa7111_init(); + saa7185_init(); +#endif +#ifdef CONFIG_VIDEO_LML33 + bt819_init(); + bt856_init(); +#endif return 0; } diff --git a/drivers/char/ip2.c b/drivers/char/ip2.c new file mode 100644 index 000000000000..1dbd56ace861 --- /dev/null +++ b/drivers/char/ip2.c @@ -0,0 +1,73 @@ +// ip2.c +// This is a dummy module to make the firmware available when needed +// and allows it to be unloaded when not. Rumor is the __initdata +// macro doesn't always works on all platforms so we use this kludge. +// If not compiled as a module it just makes fip_firm avaliable then +// __initdata should work as advertized +// + +#include +#include +#include +#include +#include + +#include "./ip2/ip2types.h" +#include "./ip2/fip_firm.h" // the meat + +int +ip2_loadmain(int *, int *, unsigned char *, int ); // ref into ip2main.c + +#ifdef MODULE +static int io[IP2_MAX_BOARDS]= { 0,}; +static int irq[IP2_MAX_BOARDS] = { 0,}; + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +MODULE_PARM(irq,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards"); +MODULE_PARM(io,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards"); + + +//====================================================================== +int +init_module(void) +{ + int rc; + + MOD_INC_USE_COUNT; // hold till done + + rc = ip2_loadmain(io,irq,(unsigned char *)fip_firm,sizeof(fip_firm)); + // The call to lock and load main, create dep + + MOD_DEC_USE_COUNT; //done - kerneld now can unload us + return rc; +} + +//====================================================================== +int +ip2_init(void) +{ + // call to this is int tty_io.c so we need this + return 0; +} + +//====================================================================== +void +cleanup_module(void) +{ +} + +#else // !MODULE + +#ifndef NULL +# define NULL ((void *) 0) +#endif + +int +ip2_init(void) { + return ip2_loadmain(NULL,NULL,(unsigned char *)fip_firm,sizeof(fip_firm)); +} + +#endif /* !MODULE */ diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile new file mode 100644 index 000000000000..26fb618ca476 --- /dev/null +++ b/drivers/char/ip2/Makefile @@ -0,0 +1,12 @@ + +all: ip2mkdev ip2trace ip2stat + +ip2mkdev: ip2mkdev.c + cc -o ip2mkdev ip2mkdev.c + +ip2trace: ip2trace.c + cc -o ip2trace ip2trace.c + +ip2stat: ip2stat.c + cc -o ip2stat ip2stat.c + diff --git a/drivers/char/ip2/fip_firm.h b/drivers/char/ip2/fip_firm.h new file mode 100644 index 000000000000..d85c3fc31e11 --- /dev/null +++ b/drivers/char/ip2/fip_firm.h @@ -0,0 +1,2149 @@ +/* fip_firm.h - Intelliport II loadware */ +/* -31232 bytes read from ff.lod */ + +unsigned char fip_firm[] __initdata = { +0x3C,0x42,0x8A,0xE1,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x57,0x65,0x64,0x20,0x4A,0x75,0x6E,0x20,0x32,0x33,0x20,0x31,0x35,0x3A,0x33,0x30, +0x3A,0x31,0x33,0x20,0x31,0x39,0x39,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xE9,0x68,0x0F,0x42,0x65,0x47,0x69,0x4E,0x6E,0x49,0x6E,0x47,0x20,0x6F,0x46,0x20, +0x63,0x4F,0x64,0x45,0xCC,0x13,0x5A,0x15,0xE8,0x16,0x76,0x18,0x04,0x1A,0x92,0x1B, +0x20,0x1D,0xAE,0x1E,0x3C,0x20,0xCA,0x21,0x58,0x23,0xE6,0x24,0x74,0x26,0x02,0x28, +0x90,0x29,0x1E,0x2B,0xAC,0x2C,0x3A,0x2E,0xC8,0x2F,0x56,0x31,0xE4,0x32,0x72,0x34, +0x00,0x36,0x8E,0x37,0x1C,0x39,0xAA,0x3A,0x38,0x3C,0xC6,0x3D,0x54,0x3F,0xE2,0x40, +0x70,0x42,0xFE,0x43,0x8C,0x45,0x1A,0x47,0xA8,0x48,0x36,0x4A,0xC4,0x4B,0x52,0x4D, +0xE0,0x4E,0x6E,0x50,0xFC,0x51,0x8A,0x53,0x18,0x55,0xA6,0x56,0x34,0x58,0xC2,0x59, +0x50,0x5B,0xDE,0x5C,0x6C,0x5E,0xFA,0x5F,0x88,0x61,0x16,0x63,0xA4,0x64,0x32,0x66, +0xC0,0x67,0x4E,0x69,0xDC,0x6A,0x6A,0x6C,0xF8,0x6D,0x86,0x6F,0x14,0x71,0xA2,0x72, +0x30,0x74,0xBE,0x75,0x4C,0x77,0x6C,0x77,0x8C,0x77,0xAC,0x77,0x33,0xDB,0x8A,0xDC, +0x53,0x33,0xDB,0x25,0x07,0x00,0x75,0x0A,0x8A,0x1E,0x08,0x01,0x83,0xE3,0x0C,0xEB, +0x20,0x90,0x3C,0x01,0x75,0x0A,0x8A,0x1E,0x08,0x01,0x80,0xE3,0xC0,0xEB,0x12,0x90, +0x8A,0x1E,0x0D,0x01,0x3C,0x02,0x75,0x06,0x80,0xE3,0x0C,0xEB,0x04,0x90,0x80,0xE3, +0xC0,0x53,0x50,0x8B,0x1E,0xBA,0x13,0x8E,0xDB,0xE8,0x4C,0x65,0x55,0x8B,0xEC,0x53, +0x1E,0x2B,0xC0,0x8E,0xD8,0x8B,0x5E,0x04,0xC1,0xE3,0x04,0x03,0x5E,0x06,0xD1,0xE3, +0x2E,0x8B,0x9F,0x44,0x00,0x8D,0x47,0x2A,0x1E,0x5A,0x1F,0x5B,0x5D,0xC3,0x55,0x8B, +0xEC,0x53,0x1E,0x2B,0xC0,0x8E,0xD8,0x8B,0x5E,0x04,0xC1,0xE3,0x04,0x03,0x5E,0x06, +0xD1,0xE3,0x2E,0x8B,0x9F,0x44,0x00,0x8D,0x47,0x34,0x1E,0x5A,0x1F,0x5B,0x5D,0xC3, +0xFB,0x55,0x8B,0xEC,0x53,0x51,0x52,0x56,0x57,0x1E,0x06,0x1E,0x07,0x33,0xC0,0x8E, +0xD8,0x8B,0x5E,0x04,0x26,0x8A,0x47,0x59,0x25,0x03,0x00,0x8B,0xF0,0xD1,0xE6,0x2E, +0x8B,0xB4,0xC4,0x00,0xC1,0xE0,0x04,0x26,0x02,0x47,0x1A,0xD1,0xE0,0x8B,0xE8,0x2E, +0x8B,0xAE,0x44,0x00,0x89,0x2C,0x26,0x8A,0x47,0x1C,0x88,0x44,0x0F,0x26,0x8A,0x47, +0x1D,0x88,0x44,0x10,0x26,0x8A,0x47,0x1E,0x88,0x44,0x11,0x26,0x8A,0x47,0x1F,0x88, +0x44,0x12,0x26,0x8A,0x47,0x20,0x88,0x44,0x13,0x26,0x8A,0x47,0x23,0x88,0x44,0x14, +0x26,0x8A,0x47,0x24,0x88,0x44,0x15,0x26,0x8A,0x47,0x5A,0x88,0x44,0x0E,0x33,0xC0, +0x89,0x44,0x06,0x89,0x44,0x08,0x88,0x44,0x0B,0x88,0x44,0x0A,0xB0,0x21,0xB4,0x64, +0x89,0x44,0x04,0x89,0x44,0x02,0xB0,0x55,0x88,0x44,0x0D,0x88,0x44,0x0C,0xE8,0x6A, +0x00,0x72,0x5B,0xE8,0xC9,0x00,0xE8,0xBD,0x10,0x89,0x44,0x08,0x80,0x7C,0x0F,0x01, +0x74,0x29,0xE8,0x2B,0x02,0xE8,0x7F,0x02,0x80,0x7C,0x0F,0x03,0x74,0x1D,0xE8,0xA5, +0x10,0x8B,0xF8,0x2B,0x44,0x08,0x3D,0xA0,0x0F,0x72,0x10,0x89,0x7C,0x08,0x33,0xC0, +0x87,0x44,0x06,0x85,0xC0,0x75,0x04,0xC6,0x44,0x0A,0xFF,0x8A,0x44,0x0A,0x84,0xC0, +0x75,0x0B,0xB8,0x08,0x00,0xE8,0x4C,0x4A,0xE8,0xA9,0x01,0x73,0xBF,0xE8,0x4F,0x01, +0x81,0x66,0x48,0x7F,0xFF,0x83,0x66,0x7A,0xBF,0xB0,0x02,0xE8,0x00,0x0E,0x8A,0x44, +0x0A,0x98,0x07,0x1F,0x5F,0x5E,0x5A,0x59,0x5B,0x5D,0xC3,0x81,0x4E,0x48,0x80,0x00, +0xB0,0x40,0xE8,0x1F,0x4A,0xE8,0x6B,0x40,0x73,0x2A,0xE8,0x49,0x10,0x8B,0xD8,0xB0, +0x05,0xE8,0x10,0x4A,0xF6,0x46,0x27,0x02,0x75,0x1A,0xE8,0x39,0x10,0x2B,0xC3,0x3D, +0x58,0x1B,0x72,0xEB,0x81,0x66,0x48,0x7F,0xFF,0xB0,0x02,0xE8,0xC0,0x0D,0xC6,0x44, +0x0A,0x01,0xF9,0xC3,0x83,0x4E,0x7A,0x40,0xF8,0xC3,0xFB,0xB0,0x01,0xE8,0xE4,0x49, +0xFA,0xE8,0x95,0x1E,0xE4,0x0A,0x84,0xC0,0x75,0xF0,0xB0,0x4E,0xE6,0x0A,0xFB,0xB0, +0x01,0xE8,0xD0,0x49,0xFA,0xE8,0x81,0x1E,0xE4,0x0A,0x84,0xC0,0x75,0xF0,0xC3,0xFA, +0xE8,0x76,0x1E,0xE4,0xEC,0x88,0x44,0x16,0xE4,0xE4,0x88,0x44,0x17,0xE4,0xF8,0x88, +0x44,0x18,0xE4,0xF0,0x88,0x44,0x19,0xE4,0x10,0x88,0x44,0x1A,0xE4,0x12,0x88,0x44, +0x1B,0xE4,0x14,0x88,0x44,0x1C,0xE4,0x34,0x88,0x44,0x1D,0xE4,0x36,0x88,0x44,0x1E, +0xE4,0xD8,0x24,0x01,0x8A,0xE0,0xE4,0xDA,0x24,0x02,0x0A,0xC4,0x88,0x44,0x1F,0x8A, +0x44,0x10,0xE8,0xC9,0x1F,0x8A,0x44,0x11,0xE8,0x31,0x21,0x8A,0x44,0x12,0xE8,0x85, +0x21,0x8A,0x44,0x13,0xE8,0x3F,0x21,0xC6,0x86,0xA1,0x00,0x00,0xE4,0x14,0x24,0x10, +0xE6,0x14,0xE4,0x12,0x24,0x3D,0xE6,0x12,0x8A,0x44,0x15,0x3C,0x01,0x72,0x1E,0x77, +0x16,0xB0,0x11,0xE6,0x34,0xB0,0x13,0xE6,0x36,0xE4,0x14,0x0C,0x10,0xE6,0x14,0xE4, +0x12,0x0C,0x40,0xE6,0x12,0xEB,0x06,0xE4,0x12,0x0C,0x02,0xE6,0x12,0x8A,0x44,0x0F, +0x3C,0x01,0x74,0x06,0x3C,0x02,0x74,0x0A,0xEB,0x0E,0xE4,0x12,0x0C,0x08,0xE6,0x12, +0xEB,0x06,0xE4,0x12,0x0C,0x10,0xE6,0x12,0xE8,0x2F,0xFF,0x8A,0x44,0x14,0x3C,0x02, +0x75,0x08,0xB0,0x55,0x88,0x44,0x0C,0x88,0x44,0x0D,0xB0,0x21,0xB4,0x64,0x89,0x44, +0x04,0x89,0x44,0x02,0xE4,0x0C,0x0C,0x10,0xE6,0x0C,0xE8,0xCF,0x39,0xFB,0xC3,0xE8, +0x41,0x3F,0x73,0x08,0xFB,0xB0,0x0A,0xE8,0xEA,0x48,0xEB,0xF3,0xFA,0xE8,0x99,0x1D, +0x8A,0x64,0x16,0x8A,0x44,0x17,0x89,0x86,0x94,0x00,0xE6,0xE4,0x8A,0xC4,0xE6,0xEC, +0x8A,0x64,0x18,0x8A,0x44,0x19,0x89,0x86,0x96,0x00,0xE6,0xF0,0x8A,0xC4,0xE6,0xF8, +0x8A,0x44,0x1A,0xE6,0x10,0x8A,0x44,0x1B,0xE6,0x12,0x8A,0x44,0x1C,0xE6,0x14,0x8A, +0x44,0x1D,0xE6,0x34,0x8A,0x44,0x1E,0xE6,0x36,0x8A,0x44,0x1F,0xE6,0xD8,0xE6,0xDA, +0xE9,0xB7,0xFE,0x90,0xFA,0x8A,0x44,0x0E,0xE6,0xFE,0xE4,0x02,0xA8,0x01,0x75,0x05, +0x33,0xC0,0xFB,0xF8,0xC3,0x33,0xC0,0xE4,0x00,0xFB,0xF9,0xC3,0x8A,0x64,0x14,0x80, +0xFC,0x02,0x74,0x2B,0xFE,0xC0,0xFE,0xC7,0x80,0xFF,0x4E,0x72,0x1C,0x74,0x09,0x80, +0xFF,0x50,0x73,0x08,0xB0,0x0A,0xEB,0x17,0xB0,0x0D,0xEB,0x13,0x02,0xDC,0x32,0xFF, +0x80,0xFB,0x7F,0x7C,0x02,0xB3,0x21,0x8A,0xC3,0x3C,0x7F,0x7C,0x02,0xB0,0x21,0xC3, +0xFA,0x80,0x7C,0x0B,0x04,0x76,0x02,0xFB,0xC3,0x8B,0x46,0x24,0x3D,0x08,0x00,0x72, +0xF6,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x8A,0x44,0x0C,0x8B,0x5C,0x02,0xAA,0xE8,0xAB, +0xFF,0xAA,0xE8,0xA7,0xFF,0xAA,0xE8,0xA3,0xFF,0xAA,0xE8,0x9F,0xFF,0x88,0x44,0x0C, +0x89,0x5C,0x02,0x80,0x44,0x0B,0x04,0x89,0x7E,0x22,0x83,0x6E,0x24,0x04,0x83,0x46, +0x1A,0x04,0x80,0x7E,0x26,0x02,0x74,0x06,0x80,0x66,0x26,0xFD,0xFB,0xC3,0x60,0xB0, +0xFD,0xE8,0xE4,0x3E,0x61,0xFB,0xC3,0xFA,0x80,0x7C,0x0F,0x03,0x75,0x09,0xC6,0x44, +0x0B,0x00,0xE8,0xC7,0x38,0xFB,0xC3,0xC4,0x7E,0x14,0x8B,0x4E,0x3A,0x85,0xC9,0x75, +0x35,0x26,0x8B,0x0D,0x47,0x47,0xE3,0xEA,0x3B,0x7E,0x04,0x76,0x22,0xB8,0x02,0x00, +0x39,0x46,0x2E,0x77,0x07,0xC7,0x46,0x2E,0x00,0x00,0xEB,0x13,0x8B,0x5E,0x2C,0x89, +0x5E,0x04,0x26,0xC7,0x07,0x00,0x00,0x43,0x43,0x89,0x5E,0x2C,0x29,0x46,0x2E,0x85, +0xC9,0x78,0xCE,0x89,0x4E,0x3A,0x8A,0x44,0x0D,0x8B,0x5C,0x04,0x26,0x8A,0x25,0x47, +0x3A,0xC4,0x75,0x16,0xFE,0x4C,0x0B,0xFF,0x44,0x06,0xE8,0x0F,0xFF,0xE2,0xED,0x88, +0x44,0x0D,0x89,0x5C,0x04,0x89,0x4E,0x3A,0xEB,0xA7,0xC6,0x44,0x0A,0xFE,0xE8,0x5B, +0x38,0xFB,0xC3,0x90,0xE8,0xAF,0x0D,0x8A,0xE8,0x8A,0x0E,0xCB,0x13,0xB3,0x07,0x8A, +0xC1,0xEE,0xEB,0x00,0xEC,0x3A,0xC1,0x75,0x09,0x02,0xCD,0xFE,0xCB,0x75,0xF0,0xEB, +0x0C,0x90,0x88,0x0E,0xCB,0x13,0x8A,0xE8,0xBB,0xFF,0xFF,0xF9,0xC3,0x88,0x0E,0xCB, +0x13,0xF8,0xC3,0x90,0xBB,0x3F,0x3F,0x8A,0x8E,0x9E,0x00,0xBA,0xFE,0x00,0xEC,0x8A, +0xE8,0x32,0xC1,0x22,0xC3,0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x90,0xE8,0xE5,0xFF,0x73, +0x01,0xC3,0xBA,0xD0,0x00,0xBB,0x03,0x03,0x8A,0x8E,0x9F,0x00,0xEC,0x8A,0xE8,0x32, +0xC1,0x22,0xC3,0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x90,0x33,0xC0,0x8E,0xD8,0x8E,0xC0, +0x80,0x3E,0xC8,0x13,0x00,0x75,0x07,0xB0,0x0A,0xE8,0x08,0x47,0xEB,0xF2,0xFB,0x33, +0xDB,0x8A,0x1E,0xC9,0x13,0x43,0x43,0x83,0xFB,0x7E,0x76,0x07,0x33,0xDB,0xB0,0x02, +0xE8,0xF1,0x46,0x2E,0x8B,0xAF,0x44,0x00,0x83,0x7E,0x08,0x00,0x74,0xE7,0x88,0x1E, +0xC9,0x13,0xB0,0x02,0xE8,0xDD,0x46,0xFA,0xF7,0x46,0x38,0x40,0x00,0x74,0x14,0xE8, +0x92,0x1B,0xE8,0x7F,0xFF,0x72,0x1C,0x33,0xD2,0x8A,0x96,0x9F,0x00,0x83,0xC2,0x0E, +0xEB,0x0C,0x90,0xE8,0x73,0x1B,0xE8,0x83,0xFF,0x72,0x08,0xBA,0x48,0x00,0xE8,0x33, +0xFF,0x73,0xAB,0x23,0xCB,0x89,0x8E,0x9A,0x00,0x89,0x96,0x9C,0x00,0xFE,0x86,0xB5, +0x00,0xC6,0x06,0xC8,0x13,0x00,0xB0,0x0A,0xE8,0x63,0x0A,0xFB,0xEB,0x89,0x10,0x18, +0x08,0x28,0x33,0xC0,0xA0,0x05,0x01,0x8A,0xC8,0x24,0x40,0x75,0x24,0xC7,0x06,0x7C, +0x12,0x70,0x45,0xC7,0x06,0x42,0x12,0x01,0x00,0xC6,0x06,0x54,0x12,0x02,0xB0,0x08, +0xF6,0xC1,0x01,0x74,0x02,0xB0,0x04,0xA3,0x46,0x12,0xA2,0x4C,0x12,0xA2,0x94,0x12, +0xC3,0xC7,0x06,0x7C,0x12,0x98,0x45,0xA0,0x0F,0x01,0x84,0xC0,0x75,0x0E,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x1E,0x9C,0x0E,0xE8,0xAD,0x0C,0x90,0xC7,0x06,0x44,0x12, +0x01,0x00,0xA3,0x42,0x12,0x8B,0xD8,0xC1,0xE3,0x04,0x88,0x1E,0x94,0x12,0xBE,0xE2, +0x05,0x2B,0xF0,0x8B,0xC8,0x33,0xDB,0x8B,0xFB,0x2E,0xAC,0x88,0x85,0x48,0x12,0x8A, +0xD8,0x0C,0x05,0xE6,0xFE,0x8A,0xE0,0xEB,0x00,0xE4,0xFE,0x32,0xC4,0xA8,0x3F,0x74, +0x03,0xE9,0x9A,0x00,0xE4,0x00,0x88,0x85,0x50,0x12,0x8A,0xE0,0x24,0x30,0xBA,0x10, +0xFF,0x3C,0x30,0x74,0x12,0x80,0xFC,0x04,0x74,0x0A,0xBA,0x04,0x03,0xF6,0x06,0x08, +0x01,0xFE,0x74,0x03,0xBA,0x08,0x0F,0x88,0x95,0x4C,0x12,0x02,0xFA,0x32,0xC0,0xF6, +0xC4,0x08,0x74,0x02,0xB0,0x01,0x88,0x85,0x58,0x12,0x8A,0xC4,0x3C,0x35,0x74,0x57, +0x3C,0x34,0x74,0x53,0x3C,0x04,0x74,0x4F,0x3C,0x14,0x74,0x4B,0x3C,0x15,0x74,0x47, +0xA8,0x40,0x74,0x25,0xC6,0x85,0x54,0x12,0x04,0xD1,0xE7,0xB4,0x03,0x8A,0xC3,0x89, +0x85,0x5C,0x12,0x8A,0xC3,0x8A,0xE3,0x80,0xCC,0x01,0x89,0x85,0x64,0x12,0xD1,0xEF, +0x47,0xE2,0x03,0xEB,0x1A,0x90,0xE9,0x70,0xFF,0xC6,0x85,0x54,0x12,0x02,0xD1,0xE7, +0x8A,0xE6,0x8A,0xC3,0x0C,0x04,0x89,0x85,0x5C,0x12,0xD1,0xEF,0x47,0xE2,0xE7,0x33, +0xC0,0x8A,0xC7,0xA3,0x46,0x12,0xC3,0xC6,0x85,0x54,0x12,0x06,0xEB,0xBB,0xC6,0x85, +0x54,0x12,0x00,0x33,0xC0,0x88,0x85,0x50,0x12,0x88,0x85,0x4C,0x12,0x88,0x85,0x58, +0x12,0xEB,0xA6,0xC7,0x46,0x26,0x02,0x12,0x8B,0x46,0x1E,0x89,0x46,0x00,0x89,0x46, +0x22,0x8B,0x46,0x20,0x89,0x46,0x24,0xC7,0x46,0x1A,0x00,0x00,0xC3,0xC7,0x46,0x3C, +0x80,0x00,0xC7,0x46,0x38,0x01,0x00,0x1E,0x56,0x8B,0x76,0x30,0x89,0x76,0x04,0x89, +0x76,0x14,0x8E,0x5E,0x06,0x33,0xC0,0x89,0x04,0x46,0x46,0x89,0x76,0x2C,0x89,0x46, +0x3A,0x8B,0x46,0x32,0x48,0x48,0x89,0x46,0x2E,0x5E,0x1F,0xC3,0x33,0xC0,0x89,0x46, +0x48,0x89,0x46,0x4A,0xC7,0x46,0x46,0xAE,0x01,0x89,0x46,0x4E,0x8B,0x46,0x44,0x89, +0x46,0x50,0x8B,0x46,0x42,0x89,0x46,0x40,0x89,0x46,0x08,0xC3,0x33,0xC0,0x89,0x46, +0x76,0x89,0x46,0x78,0xC7,0x46,0x7A,0x10,0x00,0x56,0x1E,0x8B,0x76,0x70,0x89,0x76, +0x10,0x89,0x76,0x0C,0x8E,0x5E,0x12,0xC7,0x04,0x00,0x00,0x8B,0x46,0x72,0x89,0x46, +0x74,0x1F,0x5E,0xC3,0x89,0x56,0x18,0x89,0x56,0x02,0x89,0x56,0x06,0x89,0x56,0x0A, +0x89,0x56,0x0E,0x89,0x56,0x12,0x89,0x56,0x16,0x8B,0xD8,0x4B,0x4B,0xC1,0xE3,0x02, +0xBF,0x02,0x00,0x89,0x7E,0x1E,0x03,0xFB,0x89,0x7E,0x30,0x03,0xFB,0x89,0x7E,0x42, +0x03,0xFB,0x89,0x7E,0x70,0x83,0xEB,0x08,0x89,0x5E,0x20,0x89,0x5E,0x32,0x89,0x5E, +0x44,0x89,0x5E,0x72,0x50,0xE8,0x2B,0xFF,0xE8,0x71,0xFF,0xE8,0x3F,0xFF,0xE8,0x8B, +0xFF,0x58,0xC3,0xB8,0x10,0x75,0xC1,0xE8,0x04,0x0E,0x5B,0x03,0xC3,0xA3,0xBA,0x13, +0x83,0x3E,0x42,0x12,0x00,0x74,0x07,0x80,0x3E,0x94,0x12,0x00,0x75,0x0E,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x1E,0x9C,0x0E,0xE8,0xBD,0x0A,0x90,0xB8,0x30,0x7A,0xC1, +0xE8,0x04,0x40,0xA3,0xC0,0x13,0x2B,0x06,0x12,0x01,0xF7,0xD8,0x33,0xD2,0x8B,0xCA, +0x8A,0x0E,0x94,0x12,0xF7,0xF1,0x3D,0x80,0x00,0x77,0x0E,0x6A,0x00,0x1F,0xC6,0x06, +0x93,0x12,0x25,0x9C,0x0E,0xE8,0x90,0x0A,0x90,0x48,0x3D,0xFF,0x07,0x72,0x03,0xB8, +0xFF,0x07,0xA3,0xC2,0x13,0x33,0xC9,0x8A,0x0E,0x94,0x12,0x33,0xF6,0xB8,0x00,0x09, +0x2E,0x8B,0xAC,0x44,0x00,0x89,0x46,0x4C,0x40,0x46,0x46,0xE2,0xF3,0x8A,0x0E,0x94, +0x12,0x33,0xF6,0x8B,0x16,0xC0,0x13,0xA1,0xC2,0x13,0x2E,0x8B,0xAC,0x44,0x00,0xE8, +0x22,0xFF,0x03,0xD0,0x46,0x46,0xE2,0xF2,0xC3,0x33,0xC0,0x2E,0x8B,0xAD,0x44,0x00, +0x89,0x46,0x08,0x47,0x47,0xE2,0xF4,0xC3,0x51,0x33,0xC0,0x0A,0xC2,0x2E,0x8B,0xAD, +0x44,0x00,0x89,0x86,0x9E,0x00,0x81,0x4E,0x38,0x00,0x20,0x47,0x47,0xFE,0xC4,0x80, +0xFC,0x04,0x72,0x04,0x32,0xE4,0xFE,0xC0,0xE2,0xE3,0x59,0x83,0xE9,0x10,0x74,0x05, +0xF7,0xD9,0xE8,0xC4,0xFF,0xC3,0x51,0x33,0xC0,0x0A,0xC2,0x2E,0x8B,0xAD,0x44,0x00, +0x89,0x86,0x9E,0x00,0x83,0x4E,0x38,0x40,0x47,0x47,0x80,0xC4,0x10,0x79,0x04,0x32, +0xE4,0xFE,0xC0,0xE2,0xE6,0x59,0x83,0xE9,0x10,0x74,0x05,0xF7,0xD9,0xE8,0x99,0xFF, +0xC3,0xE8,0xD2,0xFF,0xC3,0x89,0x08,0x98,0x08,0xC6,0x08,0xF1,0x08,0x8B,0x0E,0x42, +0x12,0x33,0xF6,0x51,0x56,0x33,0xDB,0x8B,0xCB,0x8A,0x94,0x48,0x12,0x8A,0x8C,0x4C, +0x12,0x8A,0x9C,0x54,0x12,0x8B,0xFE,0xC1,0xE7,0x05,0x85,0xDB,0x75,0x02,0xB1,0x10, +0x2E,0xFF,0x97,0xF5,0x08,0x5E,0x59,0x46,0xE2,0xD9,0xC3,0x01,0xCC,0x03,0xD0,0x00, +0xE8,0x02,0xD0,0x00,0xE8,0x01,0xD0,0x00,0xE8,0x00,0xD0,0x00,0xE8,0x04,0xD0,0xA8, +0xDA,0x00,0xDC,0x00,0xDE,0x01,0xD8,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x04,0xD0,0xA8, +0xDA,0x20,0xDC,0x00,0xDE,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x00,0xD8,0x03,0xCC,0x03, +0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03, +0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x04,0xD0,0x00, +0xDA,0x20,0xDC,0x03,0xDE,0x01,0xD8,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x00, +0xD8,0x00,0xCC,0x00,0xD0,0x00,0x00,0x56,0x52,0x1E,0x0E,0x1F,0xBE,0x2B,0x09,0x33, +0xD2,0xFC,0xAD,0x85,0xC0,0x74,0x0D,0x8A,0xD4,0xEE,0xAD,0x85,0xC0,0x74,0x05,0x8A, +0xD4,0xEE,0xEB,0xEE,0x1F,0x5A,0x5E,0xC3,0xE4,0x80,0x84,0xC0,0x74,0x16,0x78,0x14, +0xB0,0x27,0xE6,0xFC,0xB0,0x11,0xE6,0x34,0xE4,0xFC,0x3C,0x27,0x75,0x06,0xE4,0x11, +0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x83,0xEA,0x02,0xB0, +0x10,0xEE,0x88,0x86,0xAF,0x00,0xB0,0x11,0x83,0xC2,0x04,0xEE,0x83,0xC2,0x02,0xEE, +0xB0,0x13,0x83,0xC2,0x02,0xEE,0x83,0xC2,0x02,0xEE,0x2E,0xA1,0x2E,0x2D,0x89,0x86, +0x94,0x00,0x83,0xEA,0x0E,0xEE,0x83,0xC2,0x02,0x8A,0xC4,0xEE,0x83,0xC2,0x04,0xB0, +0x03,0xEE,0x88,0x86,0xA8,0x00,0x83,0xEA,0x04,0x32,0xC0,0xEE,0x83,0xC2,0x02,0xB0, +0x89,0xEE,0x88,0x86,0xA6,0x00,0x0C,0x06,0xEE,0xB0,0x40,0xB4,0x38,0x89,0x46,0x1C, +0xC7,0x46,0x36,0x38,0x00,0x83,0xC2,0x04,0x32,0xC0,0xEE,0x88,0x86,0xA7,0x00,0xC3, +0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x83,0xEA,0x02,0xEC,0x3A,0x86,0xAF,0x00,0x75,0x24, +0x83,0xC2,0x04,0xEC,0x3C,0x11,0x75,0x1C,0x83,0xC2,0x06,0xEC,0x3C,0x13,0x75,0x14, +0x83,0xEA,0x08,0x8A,0x86,0xA8,0x00,0xEE,0x83,0xEA,0x02,0xEC,0x24,0xC0,0x3C,0xC0, +0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x33,0xC9,0x8B,0xD1,0x8B,0xF1,0x8A,0x0E,0x94,0x12, +0xC1,0xE9,0x02,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x0E,0x8A, +0x86,0x9E,0x00,0xE6,0xFE,0x32,0xC0,0xE6,0x80,0x42,0xE8,0xFA,0xFE,0x83,0xC6,0x08, +0xE2,0xE1,0x85,0xD2,0x74,0x03,0xE8,0x05,0x08,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E, +0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x06,0xE8,0x73, +0x16,0xE8,0x12,0xFF,0x46,0x46,0xE2,0xEA,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94, +0x12,0xC1,0xE9,0x02,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x16, +0xE8,0x46,0x16,0xE8,0xD2,0xFE,0x73,0x0E,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x1C, +0x9C,0x0E,0xE8,0xE3,0x07,0x90,0x83,0xC6,0x08,0xE2,0xD9,0xC3,0x33,0xC9,0x8B,0xF1, +0x8A,0x0E,0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x16, +0xE8,0x21,0x16,0xE8,0x2A,0xFF,0x73,0x0E,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x1C, +0x9C,0x0E,0xE8,0xB3,0x07,0x90,0x46,0x46,0xE2,0xDA,0xC3,0x0C,0x00,0x00,0x10,0x00, +0x13,0x12,0x00,0x00,0x14,0x00,0x28,0x3C,0x00,0x1B,0x3E,0x00,0x00,0x2A,0x00,0x00, +0x2C,0x00,0x00,0x42,0x00,0x14,0xD8,0x00,0x00,0xDA,0x00,0x00,0x34,0x00,0x11,0x36, +0x00,0x13,0x38,0x00,0x11,0x3A,0x00,0x13,0x00,0x00,0x56,0x50,0x52,0xBE,0x2B,0x0B, +0x2E,0xAD,0x85,0xC0,0x74,0x06,0x92,0x2E,0xAC,0xEE,0xEB,0xF4,0x5A,0x58,0x5E,0xC3, +0x53,0x2E,0xA1,0x5C,0x22,0xE6,0xE4,0xE6,0xF0,0x8A,0xC4,0xE6,0xEC,0xE6,0xF8,0xE8, +0xD8,0xFF,0xB0,0x4B,0xE6,0x10,0xB0,0x50,0xE6,0x12,0xB0,0x38,0xE6,0x14,0xE8,0xAE, +0x15,0xB0,0x46,0xE6,0x0A,0xE8,0xA7,0x15,0xB0,0x1A,0xE6,0x0A,0xE8,0xA0,0x15,0xB0, +0x22,0xE6,0x0A,0xE8,0x99,0x15,0xE8,0xFD,0x06,0x8B,0xD8,0xE4,0x16,0xA8,0x04,0x75, +0x18,0xE8,0xF2,0x06,0x2B,0xC3,0x3D,0x32,0x00,0x72,0xF0,0x6A,0x00,0x1F,0xC6,0x06, +0x93,0x12,0x23,0x9C,0x0E,0xE8,0x10,0x07,0x90,0xE8,0xDA,0x06,0x2B,0xC3,0x3D,0x24, +0x00,0x77,0x1B,0xB0,0x31,0xE6,0xFC,0x56,0x51,0x55,0xB9,0x10,0x00,0x2E,0x8B,0xAC, +0x44,0x00,0x81,0x4E,0x38,0x80,0x00,0x46,0x46,0xE2,0xF2,0x5D,0x59,0x5E,0xE8,0x69, +0xFF,0xE8,0x4B,0x15,0xB0,0x46,0xE6,0x0A,0xE8,0x44,0x15,0x5B,0xC3,0x33,0xF6,0x8B, +0x0E,0x42,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x06,0xE8, +0x17,0x15,0xE8,0x5B,0xFF,0x83,0xC6,0x20,0xE2,0xE9,0xC3,0x8B,0xC2,0x05,0x04,0x00, +0x89,0x46,0x28,0x2E,0xA1,0x2E,0x2D,0x89,0x86,0x8E,0x00,0x89,0x86,0x90,0x00,0x89, +0x86,0x92,0x00,0xC6,0x86,0xA3,0x00,0x0A,0xC6,0x86,0xC3,0x00,0x03,0x52,0x83,0xC2, +0x04,0x8A,0x86,0xA6,0x00,0x0C,0x06,0xEE,0x5A,0x83,0xC2,0x02,0xB0,0x05,0xEE,0x88, +0x86,0xA5,0x00,0xC3,0xE8,0x03,0xFF,0xE8,0xE5,0x14,0xB0,0x42,0xE6,0x0A,0xF7,0x46, +0x38,0x80,0x00,0x74,0x06,0x2E,0xA1,0x98,0x22,0xEB,0x04,0x2E,0xA1,0x68,0x22,0xC7, +0x46,0x1C,0x0C,0x00,0x89,0x86,0x94,0x00,0x89,0x86,0x96,0x00,0x89,0x86,0x8E,0x00, +0x89,0x86,0x90,0x00,0x89,0x86,0x92,0x00,0xE6,0xF0,0xE6,0xE4,0x8A,0xC4,0xE6,0xF8, +0xE6,0xEC,0xC6,0x86,0xC3,0x00,0x03,0xE8,0xA5,0x14,0xB0,0x1A,0xE6,0x0A,0xB0,0x10, +0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94,0x12,0x2E, +0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x06,0xE8,0x76,0x14,0xE8,0x5A, +0xFF,0x46,0x46,0xE2,0xEA,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94,0x12,0x2E,0x8B, +0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x06,0xE8,0x4C,0x14,0xE8,0x74,0xFF, +0x46,0x46,0xE2,0xEA,0xC3,0x90,0x83,0x3E,0x44,0x12,0x00,0x75,0x14,0xB0,0x01,0xBA, +0x06,0x01,0xEE,0x2A,0xC0,0xEE,0xB0,0x02,0xEE,0xB0,0x04,0xEE,0xB8,0x00,0x02,0xEB, +0x0F,0xBA,0x06,0x01,0xB0,0x40,0xEE,0xB8,0x01,0x00,0x8A,0x0E,0x0E,0x01,0xD3,0xE0, +0xA3,0x88,0x12,0xC3,0xA1,0x88,0x12,0xA3,0x84,0x12,0x2D,0x20,0x00,0xA3,0x8A,0x12, +0x2D,0x20,0x00,0xA3,0x82,0x12,0xC7,0x06,0x86,0x12,0x20,0x00,0xC7,0x06,0x80,0x12, +0x32,0x00,0xC3,0x83,0x3E,0x44,0x12,0x00,0x74,0x76,0x8B,0x0E,0x42,0x12,0x33,0xF6, +0x8A,0xA4,0x54,0x12,0x84,0xE4,0x74,0x5F,0x8A,0x84,0x48,0x12,0x0C,0x04,0xE6,0xFE, +0xF6,0xC4,0x04,0x74,0x25,0xB0,0x1B,0xBA,0x00,0x00,0xEE,0xEB,0x00,0x2A,0xC0,0xBA, +0x02,0x00,0xEE,0xEB,0x00,0xB0,0x03,0xEE,0xEB,0x00,0x32,0xC0,0xBA,0x02,0x00,0xEE, +0xEB,0x00,0xBA,0x00,0x00,0xB0,0x00,0xEE,0xEB,0x2D,0xB0,0x1F,0xBA,0x00,0x00,0xEE, +0xEB,0x00,0x2A,0xC0,0xBA,0x02,0x00,0xEE,0xEB,0x00,0xB0,0x03,0xEE,0xEB,0x00,0xD1, +0xE6,0x8A,0x84,0x5D,0x12,0xD1,0xEE,0xF6,0xD0,0xBA,0x02,0x00,0xEE,0xEB,0x00,0xBA, +0x00,0x00,0xB0,0x0A,0xEE,0xEB,0x00,0xE4,0x04,0xEB,0x00,0xE4,0x04,0x46,0xE2,0x90, +0xC3,0x90,0xB8,0x14,0x00,0xBA,0x3E,0xFF,0xEF,0xB8,0x06,0x00,0xBA,0x32,0xFF,0xEF, +0xB8,0x0F,0x00,0xBA,0x34,0xFF,0xEF,0xBA,0x36,0xFF,0xEF,0x83,0x3E,0x44,0x12,0x00, +0x75,0x16,0xB8,0x11,0x00,0xBA,0x38,0xFF,0xEF,0xB8,0x12,0x00,0xBA,0x3A,0xFF,0xEF, +0xB8,0x1B,0x00,0xBA,0x3C,0xFF,0xEF,0xC3,0xB8,0x11,0x00,0xBA,0x38,0xFF,0xEF,0xB8, +0x12,0x00,0xBA,0x3A,0xFF,0xEF,0xB8,0x1B,0x00,0xBA,0x3C,0xFF,0xEF,0xC3,0xB8,0xFC, +0x00,0xBA,0x28,0xFF,0xEF,0xFB,0x83,0x3E,0x44,0x12,0x00,0x74,0x07,0xB8,0xCC,0x00, +0xBA,0x28,0xFF,0xEF,0xC3,0x00,0xFF,0xFF,0x20,0x24,0x28,0xFF,0x2C,0xFF,0xFF,0x30, +0x34,0x38,0xFF,0xFF,0x3C,0x90,0x3C,0x0F,0x77,0x0E,0xBB,0x15,0x0E,0x2E,0xD7,0x3C, +0xFF,0x74,0x05,0x8A,0xD8,0xF8,0xC3,0x90,0x2A,0xDB,0xF9,0xC3,0x83,0x3E,0x44,0x12, +0x00,0x74,0x27,0xA0,0x06,0x01,0x80,0x26,0x06,0x01,0x30,0x80,0x3E,0x06,0x01,0x30, +0x75,0x18,0xB9,0x02,0x00,0xBF,0xC4,0x13,0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xF8, +0xBA,0x04,0x01,0xED,0xAB,0xE2,0xF1,0xEB,0x16,0x90,0xB9,0x04,0x00,0xBF,0xC4,0x13, +0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xF8,0xBA,0x04,0x01,0xEC,0xAA,0xE2,0xF1,0xFA, +0x90,0xBE,0xC4,0x13,0xAD,0x80,0xE4,0x3F,0x80,0xFC,0x02,0x74,0x0E,0x6A,0x00,0x1F, +0xC6,0x06,0x93,0x12,0x0A,0x9C,0x0E,0xE8,0x3E,0x04,0x90,0xAD,0x3C,0x0F,0x75,0xED, +0x8A,0xC4,0xE8,0x81,0xFF,0x72,0xE6,0x88,0x1E,0x1A,0x01,0xC6,0x06,0x8E,0x12,0x00, +0xB0,0x00,0x0A,0x06,0x1A,0x01,0xBA,0x00,0x01,0xEE,0xC6,0x06,0x8F,0x12,0x40,0x83, +0x3E,0x44,0x12,0x00,0x75,0x06,0xB8,0x0C,0x00,0xEB,0x04,0x90,0xB8,0x4C,0x00,0xBA, +0x28,0xFF,0xEF,0xC3,0x83,0x3E,0x44,0x12,0x00,0x75,0x01,0xC3,0xA1,0x50,0x12,0x0B, +0x06,0x52,0x12,0x0A,0xC4,0xA8,0x08,0x74,0xF2,0xA0,0x0F,0x01,0x2A,0xE4,0x50,0xFF, +0x36,0xBA,0x13,0x1F,0xE8,0x36,0x56,0x83,0xC4,0x02,0x6A,0x00,0x1F,0x33,0xC0,0xA3, +0xBC,0x13,0xA0,0x0F,0x01,0xA3,0xBE,0x13,0x8B,0x1E,0xBC,0x13,0x8A,0x87,0x50,0x12, +0xF6,0x87,0x50,0x12,0x08,0x74,0x0D,0x24,0x07,0x8A,0xE0,0xBE,0xCC,0x00,0xA0,0xBC, +0x13,0xE8,0x7A,0x3D,0xFF,0x06,0xBC,0x13,0xFF,0x0E,0xBE,0x13,0x75,0xDA,0xC3,0x90, +0x1E,0x33,0xC0,0x8E,0xD8,0xB0,0x01,0xE8,0x3A,0x3D,0x1F,0xC3,0x33,0xC9,0x8B,0xF1, +0x8A,0x0E,0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xC7,0x46,0x62,0x1A,0x44,0xC7,0x46, +0x7C,0xDE,0x3B,0xC7,0x46,0x7E,0xC4,0x3B,0xC7,0x86,0x80,0x00,0xCE,0x3C,0xE8,0xAB, +0x16,0xC6,0x86,0xC0,0x00,0x11,0x83,0x7E,0x08,0x00,0x74,0x07,0x51,0x56,0xE8,0x19, +0x33,0x5E,0x59,0x46,0x46,0xE2,0xCD,0xC3,0x33,0xC9,0x8B,0xF1,0x8B,0xF9,0x8A,0x0E, +0x94,0x12,0xC1,0xE9,0x02,0xE3,0x13,0x2E,0x8B,0xAC,0x44,0x00,0x8A,0x86,0x9E,0x00, +0x88,0x85,0x6C,0x12,0x83,0xC6,0x08,0x47,0xE2,0xED,0xC3,0xFA,0xFC,0xB0,0xC0,0xBA, +0x00,0x01,0xEE,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8E,0xD0,0xBF,0x16,0x01,0xB9,0xCC, +0x77,0x2B,0xCF,0xD1,0xE9,0xF3,0xAB,0xBC,0x40,0x12,0xE8,0xD9,0x02,0xE8,0x56,0x3C, +0xBE,0xC8,0x0F,0xE8,0xD8,0x3C,0xF4,0x90,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8E,0xD0, +0xF6,0x06,0x0A,0x01,0x80,0x74,0x0B,0xBE,0x17,0x55,0xE8,0xC1,0x3C,0xB0,0x01,0xE8, +0x92,0x3C,0xE8,0xB3,0x00,0xE8,0xFA,0xF5,0xE8,0x08,0xF8,0xE8,0x0F,0xF9,0xE8,0x85, +0xFA,0xE8,0xB6,0xFA,0xE8,0xEF,0xFC,0xE8,0xC2,0x10,0xE8,0xE9,0x3B,0xE8,0xB2,0xFD, +0xE8,0x30,0xFD,0xE8,0x54,0x02,0xC6,0x06,0x8F,0x12,0xC0,0xE8,0xBB,0xFA,0xE8,0xEB, +0xFA,0xE8,0xE9,0xFB,0xE8,0xAF,0xFC,0xE8,0x8D,0xFC,0xE8,0x1F,0xFF,0xE8,0x58,0xFF, +0xE8,0xDB,0xFD,0xE8,0x16,0xFE,0x33,0xC0,0xBE,0x5A,0x05,0xE8,0x70,0x3C,0xE8,0xA3, +0xFE,0xE8,0xE0,0xFC,0xFB,0xBE,0x86,0x44,0xE8,0x63,0x3C,0xE9,0xB0,0x2D,0x56,0x98, +0x8B,0xF0,0x8B,0x42,0x52,0x85,0xC0,0x75,0x27,0xC7,0x42,0x52,0x01,0x00,0x53,0x36, +0x8B,0x9C,0x2C,0x01,0xF6,0xC3,0x01,0x75,0x0C,0x36,0x89,0x68,0x52,0x36,0x89,0xAC, +0x2C,0x01,0x5B,0x5E,0xC3,0x36,0x89,0xAC,0x2C,0x01,0x36,0x89,0xAC,0x1C,0x01,0x5B, +0x5E,0xC3,0x56,0x98,0x8B,0xF0,0x33,0xED,0x36,0x8B,0x84,0x1C,0x01,0xA8,0x01,0x75, +0x15,0x8B,0xE8,0x33,0xC0,0x87,0x42,0x52,0x36,0x89,0x84,0x1C,0x01,0xA8,0x01,0x74, +0x05,0x36,0x89,0x84,0x2C,0x01,0x5E,0xC3,0x56,0x51,0x33,0xF6,0xB8,0x01,0x00,0xB9, +0x08,0x00,0x89,0x84,0x1C,0x01,0x89,0x84,0x2C,0x01,0x46,0x46,0xE2,0xF4,0x59,0x5E, +0xC3,0x90,0xBB,0x01,0x00,0x8B,0xE8,0xFF,0x4E,0x6E,0x74,0x0A,0x8B,0xDD,0x8B,0x46, +0x58,0xA8,0x01,0x74,0xF0,0xC3,0x8B,0x46,0x48,0xA9,0x08,0x00,0x74,0x45,0xF7,0x46, +0x38,0x40,0x00,0x74,0x27,0xE8,0x5C,0x10,0x80,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x24, +0xBF,0x88,0x86,0xA8,0x00,0xEE,0x60,0xB0,0xFE,0xE8,0x6C,0x32,0x61,0xB0,0x02,0xE8, +0x4C,0xFF,0x8B,0x46,0x48,0x24,0xF7,0x89,0x46,0x48,0xEB,0x17,0xE8,0x2A,0x10,0x81, +0x4E,0x26,0x00,0x40,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x8B,0x46,0x48,0xA9,0x04,0x00,0x74,0x14,0xB0,0x02,0xE8,0x21,0xFF,0x8B,0x46,0x48, +0x24,0xFB,0x89,0x46,0x48,0x60,0xB0,0xDF,0xE8,0x2D,0x32,0x61,0x33,0xC0,0x87,0x46, +0x58,0xF6,0xC3,0x01,0x75,0x0B,0x36,0x89,0x47,0x58,0xA8,0x01,0x75,0x0D,0xE9,0x74, +0xFF,0xA3,0x22,0x01,0xA8,0x01,0x75,0x03,0xE9,0x6A,0xFF,0x89,0x1E,0x32,0x01,0xC3, +0xBB,0x01,0x00,0x8B,0xE8,0xF7,0x46,0x38,0x40,0x00,0x74,0x15,0xE8,0xD5,0x0F,0x80, +0xC2,0x0A,0xEC,0xA8,0x40,0x75,0x0A,0x8B,0xDD,0x8B,0x46,0x56,0xA8,0x01,0x74,0xE3, +0xC3,0x8B,0x46,0x26,0x80,0xE4,0xFE,0x80,0xCC,0x02,0x89,0x46,0x26,0xB0,0x02,0xE8, +0xBC,0xFE,0x33,0xC0,0x87,0x46,0x56,0xF6,0xC3,0x01,0x75,0x0A,0x36,0x89,0x47,0x56, +0xA8,0x01,0x75,0x0B,0xEB,0xBD,0xA3,0x20,0x01,0xA8,0x01,0x75,0x02,0xEB,0xB4,0x89, +0x1E,0x30,0x01,0xC3,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA0,0x90,0x12,0x84,0xC0, +0x75,0x49,0xA1,0x22,0x01,0xA8,0x01,0x75,0x03,0xE8,0xF6,0xFE,0xA1,0x20,0x01,0xA8, +0x01,0x75,0x03,0xE8,0x8A,0xFF,0xA1,0xAC,0x13,0x48,0x78,0x05,0x74,0x45,0xA3,0xAC, +0x13,0xA1,0xAE,0x13,0x48,0x78,0x05,0x74,0x51,0xA3,0xAE,0x13,0xA1,0xB0,0x13,0x48, +0x78,0x05,0x74,0x63,0xA3,0xB0,0x13,0xA1,0x7E,0x12,0x40,0x78,0x03,0xA3,0x7E,0x12, +0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0xA0,0x91,0x12,0x40,0x3C, +0x02,0x72,0x0B,0x33,0xC0,0xA2,0x91,0x12,0xFF,0x16,0x7C,0x12,0xEB,0xA4,0xA2,0x91, +0x12,0xEB,0x9F,0xA0,0x8E,0x12,0x32,0x06,0x8F,0x12,0xA2,0x8E,0x12,0x0A,0x06,0x1A, +0x01,0xBA,0x00,0x01,0xEE,0xB8,0x2C,0x01,0xEB,0xA4,0x83,0x3E,0x84,0x12,0x10,0x72, +0x11,0xBA,0x28,0xFF,0xED,0x0C,0x81,0xEF,0xE8,0x39,0x37,0xBA,0x28,0xFF,0xED,0x24, +0x7E,0xEF,0xB8,0x04,0x00,0xEB,0x92,0xC6,0x06,0x8D,0x12,0x01,0xE8,0x25,0x37,0xC6, +0x06,0x8D,0x12,0x00,0xA1,0xB2,0x13,0xEB,0x8B,0x90,0x8A,0x1E,0x0B,0x01,0x2A,0xFF, +0x6B,0xC3,0x19,0xBA,0x62,0xFF,0xEF,0xB8,0x0A,0x00,0xBA,0x60,0xFF,0xEF,0xB8,0x01, +0xE0,0xBA,0x66,0xFF,0xEF,0xB8,0xFF,0xFF,0xBA,0x52,0xFF,0xEF,0xB8,0x09,0xC0,0xBA, +0x56,0xFF,0xEF,0xC7,0x06,0xAC,0x13,0x2C,0x01,0xC7,0x06,0xAE,0x13,0x04,0x00,0xC6, +0x06,0x91,0x12,0x00,0xC3,0x90,0x8A,0x1E,0x0B,0x01,0x2A,0xFF,0x6B,0xC3,0x05,0xD1, +0xE8,0xA3,0x18,0x01,0xC3,0x90,0x52,0xBA,0x50,0xFF,0xED,0x5A,0xC3,0x90,0x53,0x51, +0x8B,0x1E,0x18,0x01,0xB9,0x32,0x05,0x90,0xE2,0xFE,0x4B,0x75,0xF7,0x59,0x5B,0xC3, +0xB0,0x80,0xBA,0x00,0x01,0x0A,0x06,0x1A,0x01,0xEE,0xC3,0x90,0xB0,0x40,0xEB,0xF2, +0xB0,0xC0,0xEB,0xEE,0xB0,0x00,0xEB,0xEA,0xFA,0x60,0x06,0x1E,0x16,0x2B,0xDB,0x8E, +0xDB,0x2E,0xA1,0x9C,0x4C,0x2E,0xA3,0x74,0x4C,0xA0,0x93,0x12,0x98,0x8B,0xE8,0x89, +0x26,0x2D,0x7A,0x80,0x3E,0xCA,0x13,0x00,0x74,0x03,0xE9,0x51,0x42,0xE8,0xC0,0xFF, +0xE8,0xAB,0xFF,0xE8,0xA8,0xFF,0xB0,0x20,0xC6,0x06,0x90,0x12,0x00,0xFF,0x16,0x7C, +0x12,0x8B,0xFD,0x83,0xFF,0x0A,0x72,0x11,0xE8,0xB9,0xFF,0xE8,0x90,0xFF,0xE8,0xAB, +0xFF,0xE8,0x8A,0xFF,0x83,0xEF,0x0A,0xEB,0xEA,0x0B,0xFF,0x74,0x0F,0xE8,0xA4,0xFF, +0xE8,0x7B,0xFF,0xE8,0x9A,0xFF,0xE8,0x75,0xFF,0x4F,0x75,0xF1,0xE8,0x95,0xFF,0xE8, +0x6C,0xFF,0xEB,0xB9,0x8A,0x86,0xA5,0x00,0x24,0xFD,0xEE,0x88,0x86,0xA5,0x00,0xC3, +0x8A,0x86,0xA6,0x00,0x0C,0x02,0xEE,0xC3,0x8B,0x76,0x38,0xF7,0xC6,0x01,0x00,0x74, +0xEF,0x8B,0x4E,0x36,0x8B,0x46,0x2E,0x3B,0xC1,0x73,0x02,0x8B,0xC8,0x2B,0xC1,0x89, +0x46,0x2E,0x01,0x4E,0x34,0xC4,0x7E,0x04,0x26,0x01,0x0D,0x8B,0x7E,0x2C,0x83,0xEA, +0x04,0xF3,0x6C,0x8E,0xC1,0x89,0x7E,0x2C,0x3B,0x46,0x3C,0x72,0x12,0xF7,0xC6,0x20, +0x00,0x75,0x0B,0x83,0xCE,0x20,0x89,0x76,0x38,0xB0,0x00,0xE8,0xA0,0xFC,0xC3,0xF7, +0xC6,0x04,0x00,0x74,0x1B,0x8B,0xD8,0x83,0xCE,0x10,0x89,0x76,0x38,0x8A,0x86,0xA7, +0x00,0x24,0xFE,0x88,0x86,0xA7,0x00,0x83,0xC2,0x08,0xEE,0x83,0xEA,0x08,0x8B,0xC3, +0x3D,0x40,0x00,0x72,0x01,0xC3,0x81,0x4E,0x38,0x00,0x04,0x83,0xC2,0x02,0x8A,0x86, +0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5,0x00,0xEE,0xC3,0x8A,0x86,0xA6,0x00,0x0C,0x02, +0xEE,0xC3,0xF7,0x46,0x38,0x01,0x00,0x74,0xF1,0x8B,0x4E,0x2E,0x32,0xDB,0x8A,0xBE, +0xA3,0x00,0x83,0xC2,0x06,0xC4,0x76,0x04,0x8B,0x7E,0x2C,0x83,0xF9,0x08,0x72,0x2C, +0xEC,0xA8,0x01,0x74,0x16,0x8A,0xE0,0x83,0xEA,0x0A,0xEC,0x83,0xC2,0x0A,0x84,0xE7, +0x75,0x51,0xAA,0xFE,0xC3,0x49,0x83,0xF9,0x08,0x73,0xE5,0x32,0xFF,0x26,0x01,0x1C, +0x01,0x5E,0x34,0x89,0x76,0x04,0x89,0x4E,0x2E,0x89,0x7E,0x2C,0x3B,0x4E,0x3C,0x72, +0x11,0xF6,0x46,0x38,0x20,0x74,0x01,0xC3,0x83,0x4E,0x38,0x20,0xB0,0x00,0xE8,0xFD, +0xFB,0xC3,0xF6,0x46,0x38,0x04,0x74,0x15,0x83,0x4E,0x38,0x10,0x8A,0x86,0xA7,0x00, +0x24,0xFE,0x88,0x86,0xA7,0x00,0x83,0xEA,0x02,0xEE,0x83,0xC2,0x02,0x3D,0x40,0x00, +0x72,0x5D,0xC3,0x32,0xFF,0x26,0x03,0x1C,0x85,0xDB,0x74,0x09,0x26,0x89,0x1C,0x8B, +0xF7,0x47,0x47,0x49,0x49,0x80,0xE4,0x1E,0x80,0xCC,0xC0,0x26,0x89,0x04,0xF6,0xC4, +0x10,0x74,0x27,0x8B,0x76,0x38,0xF7,0xC6,0x00,0x10,0x74,0x0B,0x50,0xFE,0x86,0xB2, +0x00,0xB0,0x0A,0xE8,0xA8,0xFB,0x58,0xF7,0xC6,0x00,0x01,0x74,0x0D,0xE8,0x68,0x26, +0x8B,0x76,0x38,0x8B,0x4E,0x2E,0x8B,0x7E,0x04,0xAB,0x8B,0xF7,0x33,0xC0,0xAB,0x32, +0xDB,0x8A,0xBE,0xA3,0x00,0x49,0x49,0x83,0xF9,0x08,0x72,0x17,0xE9,0x41,0xFF,0x81, +0x4E,0x38,0x00,0x04,0x83,0xC2,0xF8,0x8A,0x86,0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5, +0x00,0xEE,0xC3,0xE9,0x45,0xFF,0x83,0xC2,0x08,0xEC,0x88,0x86,0xAA,0x00,0xC0,0xE8, +0x04,0x8A,0xE0,0x8A,0xC8,0x86,0x86,0xA9,0x00,0x32,0xE0,0x8B,0x5E,0x3E,0x84,0xE3, +0x74,0x4F,0x8A,0xC1,0x8B,0x4E,0x26,0xF6,0xC5,0x04,0x74,0x0C,0xA8,0x08,0x74,0x05, +0x80,0xE1,0xBF,0xEB,0x03,0x80,0xC9,0x40,0xF6,0xC5,0x08,0x74,0x0C,0xA8,0x02,0x74, +0x05,0x80,0xE1,0x7F,0xEB,0x03,0x80,0xC9,0x80,0x88,0x4E,0x26,0x8B,0xF0,0x8A,0x86, +0xA5,0x00,0x84,0xC9,0x74,0x08,0xA8,0x02,0x74,0x15,0x24,0xFD,0xEB,0x06,0xA8,0x02, +0x75,0x0D,0x0C,0x02,0x88,0x86,0xA5,0x00,0x83,0xEA,0x0A,0xEE,0x83,0xC2,0x0A,0x8B, +0xC6,0x84,0xE7,0x75,0x01,0xC3,0xC6,0x86,0xBA,0x00,0x01,0xB0,0x0E,0xE8,0xEE,0xFA, +0xF7,0x46,0x38,0x00,0x02,0x74,0xEE,0x83,0x7E,0x2E,0x06,0x72,0xE8,0x8A,0xA6,0xAA, +0x00,0xC4,0x5E,0x04,0x8B,0x7E,0x2C,0xB0,0xFF,0xAA,0xB0,0x02,0xAB,0x26,0x83,0x07, +0x03,0x83,0x6E,0x2E,0x03,0x89,0x7E,0x2C,0xF6,0x46,0x38,0x20,0x74,0x01,0xC3,0x83, +0x4E,0x38,0x20,0xB0,0x00,0xE8,0xB6,0xFA,0xC3,0x90,0x83,0xEA,0x08,0xE9,0xB4,0xFD, +0x83,0xC2,0x06,0x8B,0x5E,0x26,0xF6,0xC3,0xC0,0x75,0xEF,0x8B,0x4E,0x1C,0xEC,0x88, +0x86,0xA4,0x00,0x83,0xEA,0x0A,0xA8,0x20,0x75,0x02,0x8A,0xCD,0x32,0xED,0x8B,0x46, +0x1A,0x3B,0xC8,0x73,0x18,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A,0xC5,0x76,0x00, +0xF3,0x6E,0x8E,0xD9,0x89,0x76,0x00,0x3D,0x20,0x00,0x72,0x30,0xC3,0x85,0xC0,0x74, +0x31,0x8B,0xC8,0x01,0x46,0x2A,0xC5,0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x80,0xCB,0x02, +0x89,0x5E,0x26,0xE8,0x32,0xF1,0xF6,0xC7,0x01,0x75,0x16,0x83,0xC2,0x02,0xE8,0x53, +0xFD,0xF6,0xC7,0x10,0x75,0x0B,0xB0,0x02,0xE8,0x43,0xFA,0xC3,0xF6,0xC7,0x01,0x74, +0xF0,0xC3,0x80,0xCB,0x02,0x89,0x5E,0x26,0xF6,0xC7,0x01,0x74,0xDE,0x83,0xC2,0x02, +0xE8,0x31,0xFD,0xF6,0x86,0xA4,0x00,0x40,0x74,0x0B,0x80,0xE7,0xFE,0x80,0xCF,0x02, +0x89,0x5E,0x26,0xEB,0xCC,0xB0,0x04,0xE8,0x14,0xFA,0xC3,0xC0,0xC2,0xC8,0xCA,0xC4, +0xC6,0xCC,0xCE,0xD0,0xD2,0xD8,0xDA,0xD4,0xD6,0xDC,0xDE,0x90,0xE9,0x0E,0x01,0xE4, +0xC4,0x8A,0xE0,0xE4,0xC4,0x8B,0xD0,0x83,0xF9,0x08,0x72,0xF0,0x26,0x83,0x3F,0x00, +0x74,0x04,0x8B,0xDF,0x49,0x49,0x8B,0xFB,0x8A,0xDE,0x83,0xE3,0x0F,0x2E,0x8A,0xA7, +0x2B,0x16,0xAB,0xF6,0xC4,0x10,0x74,0x24,0xF7,0xC6,0x00,0x10,0x74,0x0B,0x50,0xFE, +0x86,0xB2,0x00,0xB0,0x0A,0xE8,0xC6,0xF9,0x58,0xF7,0xC6,0x00,0x01,0x74,0x0D,0xE8, +0x86,0x24,0x8B,0x76,0x38,0x8B,0x4E,0x2E,0x8B,0x7E,0x04,0xAB,0x89,0x7E,0x04,0x33, +0xC0,0xAB,0x49,0x49,0x89,0x4E,0x2E,0x89,0x7E,0x2C,0x8B,0xC1,0xEB,0x4E,0x90,0xEB, +0x9E,0x90,0xE4,0xD6,0x84,0xC0,0x79,0x63,0xE6,0xD0,0x8A,0xC8,0x25,0x03,0x00,0x03, +0xD8,0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0x88,0x8E,0xAE,0x00,0x8B,0x4E,0x2E,0xC4, +0x5E,0x04,0x8B,0x7E,0x2C,0x8B,0x76,0x38,0xE4,0x86,0x24,0x07,0x3C,0x03,0x75,0xCF, +0xE4,0x1C,0x91,0x3B,0xC1,0x73,0x02,0x8B,0xC8,0x2B,0xC1,0x89,0x46,0x2E,0x01,0x4E, +0x34,0x26,0x01,0x0F,0xBA,0xC4,0x00,0xF3,0x6C,0x89,0x7E,0x2C,0x3B,0x46,0x3C,0x72, +0x1C,0xF7,0xC6,0x20,0x00,0x75,0x0B,0x83,0xCE,0x20,0x89,0x76,0x38,0xB0,0x00,0xE8, +0x3C,0xF9,0x8A,0x86,0xAE,0x00,0x24,0x3F,0xE6,0xD6,0xC3,0xF9,0xC3,0xF7,0xC6,0x0A, +0x00,0x74,0x35,0xF7,0xC6,0x10,0x00,0x75,0x2F,0x83,0xCE,0x10,0x89,0x76,0x38,0xF7, +0xC6,0x02,0x00,0x74,0x0E,0x50,0xE4,0xD8,0x24,0xFE,0xE6,0xD8,0x58,0xF7,0xC6,0x08, +0x00,0x74,0x15,0x50,0x51,0xB9,0xE8,0x03,0xE4,0x0A,0x84,0xC0,0xE0,0xFA,0x84,0xC0, +0x75,0x04,0xB0,0x24,0xE6,0x0A,0x59,0x58,0x3D,0x40,0x00,0x73,0xB5,0x8A,0x86,0xA5, +0x00,0x24,0xEF,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x81,0xCE,0x10,0x04,0x89,0x76,0x38, +0xEB,0xA0,0x00,0x08,0x04,0x0C,0x01,0x09,0x05,0x0D,0x02,0x0A,0x06,0x0E,0x03,0x0B, +0x07,0x0F,0x00,0x40,0x80,0xC0,0x20,0x60,0xA0,0xE0,0x10,0x50,0x90,0xD0,0x30,0x70, +0xB0,0xF0,0xE4,0xD2,0xE6,0xD0,0x8A,0xC8,0x25,0x03,0x00,0x03,0xD8,0xD1,0xE3,0x2E, +0x8B,0xAF,0x44,0x00,0x88,0x8E,0xAE,0x00,0xE4,0xD8,0xC0,0xE8,0x04,0x8B,0xD8,0x2E, +0x8A,0x87,0x62,0x17,0x8A,0xE0,0x8A,0xC8,0x86,0x86,0xA9,0x00,0x32,0xE0,0xE4,0x98, +0x8B,0x5E,0x3E,0x84,0xE3,0x74,0x54,0x8A,0xC1,0x8B,0x4E,0x26,0xF6,0xC5,0x04,0x74, +0x0C,0xA8,0x08,0x74,0x05,0x80,0xE1,0xBF,0xEB,0x03,0x80,0xC9,0x40,0xF6,0xC5,0x08, +0x74,0x0C,0xA8,0x02,0x74,0x05,0x80,0xE1,0x7F,0xEB,0x03,0x80,0xC9,0x80,0x88,0x4E, +0x26,0x8B,0xF0,0x8A,0x86,0xA5,0x00,0xF6,0xC1,0xFD,0x74,0x08,0xA8,0x06,0x74,0x19, +0x24,0xF9,0xEB,0x0F,0xA8,0x06,0x75,0x11,0xF6,0xC5,0x01,0x75,0x04,0x0C,0x04,0xEB, +0x02,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x8B,0xC6,0x84,0xE7,0x75,0x09,0x8A, +0x86,0xAE,0x00,0x24,0x3F,0xE6,0xD2,0xC3,0xC6,0x86,0xBA,0x00,0x01,0xB0,0x0E,0xE8, +0x1C,0xF8,0xF7,0x46,0x38,0x00,0x02,0x74,0xE6,0x83,0x7E,0x2E,0x06,0x72,0xE0,0x8A, +0x86,0xA9,0x00,0x8A,0xE0,0x86,0x86,0xAA,0x00,0x8A,0xC8,0x32,0xC4,0x80,0xC9,0x0B, +0x22,0xC1,0xC0,0xE4,0x04,0x0A,0xE0,0xC4,0x5E,0x04,0x8B,0x7E,0x2C,0xB0,0xFF,0xAA, +0xB0,0x02,0xAB,0x26,0x83,0x07,0x03,0x83,0x6E,0x2E,0x03,0x89,0x7E,0x2C,0xF6,0x46, +0x38,0x20,0x75,0xAB,0x83,0x4E,0x38,0x20,0xB0,0x00,0xE8,0xD1,0xF7,0xEB,0xA0,0x90, +0xE4,0x12,0x24,0xDF,0xE6,0x12,0x81,0xE3,0xFE,0x9F,0x89,0x5E,0x26,0x83,0x66,0x48, +0xF7,0xEB,0x73,0x90,0xF6,0xC7,0x20,0x75,0xE7,0xE4,0x12,0x0C,0x20,0xE6,0x12,0x32, +0xC0,0xE6,0xC6,0xB0,0x83,0xE6,0xC6,0x80,0xCF,0x20,0x89,0x5E,0x26,0x8A,0x86,0xA5, +0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xEB,0x74,0x90,0xF6,0xC7,0x40,0x75, +0xD3,0xE4,0x12,0x0C,0x20,0xE6,0x12,0x32,0xC0,0xE6,0xC6,0xB0,0x81,0xE6,0xC6,0x80, +0xE7,0xDF,0x80,0xCB,0x01,0x89,0x5E,0x26,0xB0,0x06,0xE8,0x71,0xF7,0x90,0x8A,0x86, +0xA5,0x00,0x24,0xF9,0xE6,0x0C,0x88,0x86,0xA5,0x00,0xEB,0x43,0xE4,0xD4,0xE6,0xD0, +0x8B,0xF8,0x25,0x03,0x00,0x03,0xD8,0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0x8B,0x5E, +0x26,0xF6,0xC7,0x60,0x75,0xB6,0xF6,0xC3,0xC0,0x75,0xD3,0xBA,0xC6,0x00,0x8B,0x4E, +0x1C,0x8B,0x46,0x1A,0x3B,0xC8,0x73,0x1E,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A, +0xC5,0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x89,0x76,0x00,0x3D,0x20,0x00,0x72,0x3D,0x8B, +0xC7,0x24,0x3F,0xE6,0xD4,0xC3,0x85,0xC0,0x74,0x39,0x8B,0xC8,0x01,0x46,0x2A,0xC5, +0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x83,0xCB,0x02,0x89,0x5E,0x26,0xE8,0xD9,0xED,0xF6, +0xC7,0x01,0x75,0x39,0x8A,0x86,0xA5,0x00,0x24,0xF9,0xE6,0x0C,0x88,0x86,0xA5,0x00, +0xF6,0xC7,0x10,0x75,0xCA,0xB0,0x02,0xE8,0xE4,0xF6,0xEB,0xC3,0xF6,0xC7,0x01,0x74, +0xEF,0xEB,0xBC,0xF6,0xC7,0x01,0x74,0xDC,0x8A,0x86,0xA5,0x00,0xA8,0x02,0x74,0x11, +0x81,0xE3,0xFF,0xFE,0x81,0xCB,0x00,0x02,0x89,0x5E,0x26,0xEB,0xC7,0x8A,0x86,0xA5, +0x00,0x24,0xFB,0x0C,0x02,0xE6,0x0C,0x88,0x86,0xA5,0x00,0xEB,0x92,0x90,0xFD,0xF7, +0xDF,0x7F,0xFE,0xFB,0xEF,0xBF,0x00,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x02,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x03,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x02,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x33,0xDB,0x8A,0xD8,0x8A,0x87,0x6C,0x12,0xE6,0xFE, +0xC1,0xE3,0x02,0xE4,0xCE,0xA8,0x04,0x75,0x09,0xA8,0x02,0x74,0x03,0xE9,0x2C,0xFE, +0xF9,0xC3,0x50,0x53,0xE8,0xCB,0xFC,0x5B,0x58,0xA8,0x02,0x74,0x03,0xE9,0x1C,0xFE, +0xF8,0xC3,0x33,0xDB,0x8A,0xD8,0x8A,0x87,0x6C,0x12,0xE6,0xFE,0xC1,0xE3,0x02,0xE9, +0xD0,0xFB,0x96,0x1A,0xC2,0x1A,0x00,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0C,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0E,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0C,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0xC3,0x90,0xD6,0x14,0x90,0x15,0x58,0x13,0xE2,0x13, +0xD6,0x1B,0xD6,0x1B,0xE2,0x13,0xD6,0x1B,0x8B,0x94,0x64,0x12,0xC1,0xE6,0x04,0xA8, +0x01,0x74,0x35,0x50,0x33,0xC0,0x8A,0xC2,0xE6,0xFE,0xE4,0xA0,0x85,0xC0,0x74,0x27, +0x8B,0xD8,0x2E,0x8A,0x9F,0xD6,0x1A,0x52,0x56,0x2E,0x8B,0xA8,0x44,0x00,0x8B,0x56, +0x28,0xEC,0xA8,0x01,0x75,0x0D,0x88,0x86,0xAD,0x00,0x24,0x0E,0x8A,0xD8,0x2E,0xFF, +0x97,0xD8,0x1B,0x5E,0x5A,0xEB,0xCD,0x58,0xA8,0x02,0x74,0x36,0x83,0xC6,0x10,0x33, +0xC0,0x8A,0xC6,0xE6,0xFE,0xE4,0xA0,0x85,0xC0,0x74,0x27,0x8B,0xD8,0x2E,0x8A,0x9F, +0xD6,0x1A,0x52,0x56,0x2E,0x8B,0xA8,0x44,0x00,0x8B,0x56,0x28,0xEC,0xA8,0x01,0x75, +0x0D,0x88,0x86,0xAD,0x00,0x24,0x0E,0x8A,0xD8,0x2E,0xFF,0x97,0xD8,0x1B,0x5E,0x5A, +0xEB,0xCD,0xC3,0x90,0x32,0xE4,0x8B,0xD8,0x8B,0xD0,0x2E,0x8A,0x9F,0x96,0x19,0x2E, +0x22,0x97,0x8E,0x19,0x56,0x52,0x8A,0xC3,0x24,0x03,0x03,0xC6,0x80,0xE3,0x04,0xD0, +0xEB,0x2E,0xFF,0x97,0xD2,0x1A,0x58,0x5E,0xA9,0x55,0x00,0x75,0xD9,0xC3,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x08, +0x33,0xF6,0xE8,0xBF,0xFF,0xEB,0xEE,0x90,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1, +0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x08,0xBE,0x04,0x00,0xE8,0x94,0xFF, +0xEB,0xED,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61, +0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x6B,0xFF,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x74,0xE5,0xBE,0x08,0x00,0xE8,0x5A,0xFF,0xEB,0xDD,0xA1,0x60,0x12,0xE6, +0xFE,0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0xA1,0x5C,0x12,0xE6, +0xFE,0xE4,0x04,0x1F,0xE4,0x04,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4, +0x74,0x19,0xBE,0x04,0x00,0xE8,0x1C,0xFF,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00,0x22, +0xC4,0x74,0xE4,0xBE,0x0C,0x00,0xE8,0x0B,0xFF,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE, +0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x04,0x1F,0xE4,0x04,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x08, +0x33,0xF6,0xE8,0x53,0xFE,0xEB,0xEE,0x90,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07, +0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x08,0xBE,0x02,0x00,0xE8,0x2C,0xFE,0xEB,0xED,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x08,0xBE,0x04,0x00,0xE8, +0x06,0xFE,0xEB,0xED,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4, +0x74,0x08,0xBE,0x06,0x00,0xE8,0xE0,0xFD,0xEB,0xED,0xB8,0x00,0x80,0xBA,0x22,0xFF, +0xEF,0x07,0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12, +0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x37,0xFE,0xA1,0x60,0x12, +0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0xE5,0xBE,0x04,0x00,0xE8,0xAA,0xFD,0xEB,0xDD, +0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0xA1,0x5C,0x12,0xE6,0xFE, +0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4, +0x74,0x19,0xBE,0x04,0x00,0xE8,0xEC,0xFD,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84, +0xC4,0x74,0xE4,0xBE,0x06,0x00,0xE8,0x5F,0xFD,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x75,0xED,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x04,0x07,0xE4,0x04, +0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x27, +0xFD,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0xE5,0xBE,0x08,0x00,0xE8, +0x92,0xFD,0xEB,0xDD,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4, +0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x19, +0xBE,0x02,0x00,0xE8,0xE2,0xFC,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74, +0xE4,0xBE,0x0C,0x00,0xE8,0x4D,0xFD,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF, +0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x9D,0xFC,0xA1,0x60,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0xE5,0xBE,0x04,0x00,0xE8,0x8C,0xFC,0xEB,0xDD,0xA1,0x60, +0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0x07,0x1F,0xB8,0x00,0x80,0xBA,0x22, +0xFF,0xEF,0x61,0xCF,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x19,0xBE,0x02,0x00,0xE8,0x5C,0xFC,0xA1,0x62,0x12,0xE6, +0xFE,0xE4,0x80,0x84,0xC4,0x74,0xE4,0xBE,0x06,0x00,0xE8,0x4B,0xFC,0xEB,0xDC,0xA1, +0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0x07,0x1F,0xB8,0x00,0x80,0xBA, +0x22,0xFF,0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0x90,0x2A,0xC0, +0xE6,0xFE,0xE4,0xCE,0xA8,0x01,0x74,0x14,0x33,0xDB,0xE8,0xD5,0xF6,0xEB,0xEF,0x90, +0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xF6,0x06,0x05,0x01, +0x01,0x75,0xED,0xB0,0x01,0xE6,0xFE,0xE4,0xCE,0xA8,0x01,0x74,0xE3,0xBB,0x04,0x00, +0xE8,0xAF,0xF6,0xEB,0xC9,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0x90,0xFB,0x90, +0xFA,0x2A,0xC0,0xE6,0xFE,0xE4,0xCE,0xA8,0x02,0x74,0x13,0x33,0xDB,0xE8,0xCC,0xF8, +0xEB,0xEC,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xA8,0x04, +0x74,0xF0,0x33,0xDB,0xE8,0x5B,0xF7,0xEB,0xD5,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0x90,0xFB,0x90,0xFA,0xB0,0x01,0xE6,0xFE,0xE4,0xCE,0xA8,0x02,0x74,0x15,0xBB, +0x04,0x00,0xE8,0x97,0xF8,0xEB,0xEB,0x90,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07, +0x1F,0x61,0xCF,0x90,0xA8,0x04,0x74,0xF0,0xBB,0x04,0x00,0xE8,0x24,0xF7,0xEB,0xD2, +0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x09,0x9C,0x0E,0xE8,0x6B,0xF2,0x90,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x29,0x9C,0x0E,0xE8,0x5D,0xF2,0x90,0x6E,0x20,0x6E,0x20, +0x6E,0x20,0xCA,0x1D,0x8E,0x1C,0xE2,0x1C,0x16,0x1E,0x6E,0x20,0x7E,0x1D,0xAA,0x1E, +0x34,0x1F,0x6E,0x20,0x7E,0x1D,0x6E,0x20,0x6E,0x20,0x34,0x1F,0x6E,0x20,0x6E,0x20, +0x6E,0x20,0xF0,0x1D,0xB8,0x1C,0x30,0x1D,0x60,0x1E,0x6E,0x20,0xA4,0x1D,0xEE,0x1E, +0x74,0x1F,0x6E,0x20,0xA4,0x1D,0x6E,0x20,0x6E,0x20,0x74,0x1F,0xFC,0xB9,0x40,0x00, +0x8C,0xCB,0xB8,0x60,0x20,0x2B,0xFF,0xAB,0x93,0xAB,0x93,0xE2,0xFA,0xC7,0x06,0x4C, +0x00,0xA4,0x11,0x83,0x3E,0x44,0x12,0x00,0x75,0x20,0xC7,0x06,0x3C,0x00,0xEA,0x4A, +0xC7,0x06,0x30,0x00,0xB6,0x1F,0xC7,0x06,0x34,0x00,0xF6,0x1F,0xF6,0x06,0x05,0x01, +0x01,0x75,0x06,0xC7,0x06,0x38,0x00,0x2A,0x20,0xC3,0xC7,0x06,0x3C,0x00,0x38,0x4B, +0x33,0xDB,0x8A,0x1E,0x54,0x12,0xC1,0xE3,0x02,0x02,0x1E,0x56,0x12,0x2E,0x8B,0x87, +0x7C,0x20,0xA3,0x30,0x00,0x8A,0x1E,0x55,0x12,0xC1,0xE3,0x02,0x02,0x1E,0x57,0x12, +0x2E,0x8B,0x87,0x9C,0x20,0xA3,0x34,0x00,0xC3,0x8B,0x86,0x9E,0x00,0xE6,0xFE,0x86, +0xC4,0xE6,0xD0,0xC3,0x8B,0x86,0x9E,0x00,0xE6,0xFE,0x33,0xD2,0x8A,0xD4,0xC3,0x51, +0xB9,0x10,0x27,0xE4,0x0A,0x90,0x90,0x84,0xC0,0x74,0x05,0xE2,0xF6,0x59,0xF9,0xC3, +0x59,0xF8,0xC3,0x84,0xC0,0x78,0x1E,0x51,0x8A,0xE8,0x8A,0xC8,0xB8,0x01,0x00,0xD3, +0xE0,0x09,0x86,0x98,0x00,0x3A,0xAE,0xA0,0x00,0x59,0x75,0x10,0xE8,0xA9,0xE5,0x83, +0x4E,0x26,0x02,0xF9,0xC3,0x98,0x89,0x86,0x98,0x00,0xEB,0xF0,0xF8,0xC3,0x84,0xC0, +0x78,0x12,0x51,0x8A,0xE0,0x8A,0xC8,0xB8,0x01,0x00,0xD3,0xE0,0x59,0xF7,0xD0,0x21, +0x86,0x98,0x00,0xC3,0xC7,0x86,0x98,0x00,0x00,0x00,0xC3,0x83,0xC2,0x04,0x8A,0x86, +0xA6,0x00,0x0C,0x04,0xEE,0x83,0xEA,0x04,0xC3,0xE8,0x93,0xFF,0x72,0x04,0xB0,0x82, +0xE6,0x0A,0xC3,0x8B,0x46,0x26,0xA8,0xFD,0x74,0x11,0x8A,0x86,0xA5,0x00,0xA8,0x06, +0x74,0x08,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xF6,0xC4,0x01,0x74,0x0A, +0x8A,0x86,0xA5,0x00,0x24,0xFB,0x0C,0x02,0xEB,0x0C,0xA8,0x02,0x75,0x0F,0x8A,0x86, +0xA5,0x00,0x24,0xFD,0x0C,0x04,0x3A,0x86,0xA5,0x00,0x75,0xD8,0xC3,0x8A,0x86,0xA5, +0x00,0xEB,0xCF,0xE4,0xD8,0x33,0xDB,0x8A,0xD8,0xC0,0xEB,0x04,0x2E,0x8A,0x9F,0x62, +0x17,0x88,0x9E,0xA9,0x00,0x8B,0x5E,0x26,0x80,0xE3,0x3F,0xF6,0xC7,0x04,0x74,0x07, +0xA8,0x10,0x75,0x03,0x80,0xCB,0x40,0xF6,0xC7,0x08,0x74,0x07,0xA8,0x80,0x75,0x03, +0x80,0xCB,0x40,0x88,0x5E,0x26,0x8A,0x86,0xA5,0x00,0xF6,0xC3,0xFD,0x74,0x0D,0xA8, +0x06,0x74,0x08,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xF6,0xC7,0x01,0x74, +0x04,0x0C,0x02,0xEB,0xF0,0xF6,0xC3,0x02,0x75,0xE9,0x0C,0x04,0xEB,0xE7,0xC4,0x04, +0xC4,0x04,0x85,0x04,0x59,0x04,0x48,0x04,0x41,0x04,0xC3,0x03,0x82,0x03,0x41,0x03, +0x82,0x02,0x57,0x02,0x41,0x02,0x82,0x01,0x41,0x01,0x82,0x00,0x41,0x00,0x4E,0x02, +0xAD,0x01,0x57,0x01,0x2D,0x00,0x2B,0x00,0x27,0x00,0x21,0x00,0x16,0x00,0xF4,0x04, +0xF4,0x04,0xA3,0x04,0x6F,0x04,0x5B,0x04,0x51,0x04,0xF4,0x03,0xA3,0x03,0x51,0x03, +0xA3,0x02,0x6D,0x02,0x51,0x02,0xA3,0x01,0x51,0x01,0xA3,0x00,0x51,0x00,0x62,0x02, +0xD9,0x01,0x6D,0x01,0x38,0x00,0x36,0x00,0x31,0x00,0x29,0x00,0x1B,0x00,0x51,0x57, +0xBF,0x02,0x00,0xEB,0x0F,0x90,0x51,0x56,0xBF,0x01,0x00,0xEB,0x07,0x90,0x51,0x56, +0xBF,0x03,0x00,0x90,0x3C,0x19,0x76,0x02,0xB0,0x17,0x98,0x8B,0xF0,0x8A,0x82,0xC4, +0x00,0x2A,0xE4,0x8B,0xF0,0x83,0xFE,0x18,0x73,0x46,0xD1,0xE6,0x2E,0x8B,0x8C,0x4E, +0x22,0xF7,0x46,0x38,0x80,0x00,0x74,0x05,0x2E,0x8B,0x8C,0x7E,0x22,0xF7,0xC7,0x02, +0x00,0x74,0x12,0x3B,0x8E,0x94,0x00,0x74,0x0C,0x89,0x8E,0x94,0x00,0x8A,0xC5,0xE6, +0xEC,0x8A,0xC1,0xE6,0xE4,0xF7,0xC7,0x01,0x00,0x74,0x12,0x3B,0x8E,0x96,0x00,0x74, +0x0C,0x89,0x8E,0x96,0x00,0x8A,0xC5,0xE6,0xF8,0x8A,0xC1,0xE6,0xF0,0x5E,0x59,0xC3, +0x77,0x06,0x8B,0x8E,0x8E,0x00,0xEB,0xC5,0x8B,0x8E,0x90,0x00,0xEB,0xBF,0xD5,0x03, +0xF6,0x00,0x3E,0x00,0x10,0x00,0x04,0x00,0xCA,0x04,0x33,0x01,0x4D,0x00,0x14,0x00, +0x05,0x00,0x01,0x03,0x05,0x07,0x09,0x00,0x01,0x02,0x03,0x04,0x80,0x84,0x1E,0x00, +0xA0,0x25,0x26,0x00,0x00,0x00,0x60,0x8B,0xF0,0x33,0xFF,0x2E,0xA1,0x4C,0x23,0x2E, +0x8B,0x16,0x4E,0x23,0xBB,0x2E,0x23,0xF7,0x46,0x38,0x80,0x00,0x74,0x0C,0x2E,0xA1, +0x50,0x23,0x2E,0x8B,0x16,0x52,0x23,0xBB,0x38,0x23,0xB9,0x05,0x00,0x2E,0x3B,0x31, +0x73,0x0A,0x47,0x47,0xE2,0xF7,0xB8,0xFF,0xFF,0xEB,0x1D,0x90,0xD1,0xEF,0x2E,0x8A, +0x8D,0x42,0x23,0x2A,0xED,0xD1,0xEA,0xD1,0xD8,0xE2,0xFA,0xF7,0xF6,0x05,0x02,0x00, +0xC1,0xE8,0x02,0x2E,0x8A,0xA5,0x47,0x23,0x2E,0xA3,0x54,0x23,0x61,0x2E,0xA1,0x54, +0x23,0xC3,0x08,0x00,0x20,0x00,0x80,0x00,0x00,0x02,0x60,0x09,0x08,0x00,0x20,0x00, +0x80,0x00,0x00,0x02,0x00,0x08,0x00,0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04,0x00, +0x52,0x56,0x57,0x85,0xC0,0x74,0x05,0x3D,0x01,0x09,0x76,0x03,0xB8,0x01,0x09,0xBF, +0x5B,0x01,0xF7,0x46,0x38,0x80,0x00,0x74,0x03,0xBF,0xB2,0x01,0x33,0xF6,0x2E,0x3B, +0x84,0xB2,0x23,0x76,0x04,0x46,0x46,0xEB,0xF5,0xF7,0xE7,0x2E,0x8B,0xBC,0xBC,0x23, +0x03,0xC7,0x83,0xD2,0x00,0xD1,0xE7,0xF7,0xF7,0x2E,0x8A,0xA4,0xC6,0x23,0x5F,0x5E, +0x5A,0xC3,0xE4,0x3E,0x80,0xBE,0xC3,0x00,0x03,0x75,0x0C,0xF7,0x46,0x7A,0x20,0x00, +0x74,0x05,0x0C,0x80,0xE6,0x3E,0xC3,0x24,0x7F,0xE6,0x3E,0xC3,0x24,0x03,0x88,0x86, +0xC3,0x00,0x8A,0xE0,0xE4,0x10,0x24,0xFC,0x0A,0xC4,0xE6,0x10,0x80,0x8E,0xA1,0x00, +0x42,0xE8,0xCE,0xFF,0xC3,0x90,0x56,0x8B,0xF0,0x83,0xE6,0x07,0xD1,0xE6,0x2E,0xFF, +0xA4,0x54,0x24,0x90,0x64,0x24,0x68,0x24,0x6C,0x24,0x70,0x24,0x74,0x24,0x83,0x24, +0x83,0x24,0x83,0x24,0xB4,0x00,0xEB,0x0E,0xB4,0xC0,0xEB,0x0A,0xB4,0x40,0xEB,0x06, +0xB4,0x20,0xEB,0x02,0xB4,0xA0,0xE4,0x10,0x24,0x1F,0x0A,0xC4,0xE6,0x10,0x80,0x8E, +0xA1,0x00,0x42,0x5E,0xC3,0x90,0x3C,0x02,0x77,0x12,0x8A,0xE0,0xE4,0x10,0x24,0xF3, +0xC0,0xE4,0x02,0x0A,0xC4,0xE6,0x10,0x80,0x8E,0xA1,0x00,0x42,0xC3,0x90,0x8B,0x5E, +0x38,0x84,0xC0,0x74,0x1F,0x3C,0x02,0x74,0x20,0x83,0xCB,0x08,0x8B,0x46,0x2E,0x3B, +0x46,0x3C,0x77,0x0C,0xE8,0x88,0xFC,0x72,0x07,0xB0,0x24,0xE6,0x0A,0x83,0xCB,0x10, +0x89,0x5E,0x38,0xC3,0x83,0xE3,0xF7,0xEB,0xF7,0xF7,0xC3,0x10,0x00,0x74,0xF5,0xE8, +0x6D,0xFC,0x72,0xEC,0x8A,0x86,0xC0,0x00,0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xEB,0xE0, +0x8B,0x5E,0x38,0x8B,0x46,0x2E,0x3B,0x46,0x3C,0xE4,0xD8,0x77,0x0B,0x24,0xFE,0x80, +0xCB,0x12,0xE6,0xD8,0x89,0x5E,0x38,0xC3,0x0C,0x01,0x80,0xCB,0x02,0xEB,0xF3,0x50, +0x33,0xDB,0xC1,0xE8,0x04,0x25,0x0F,0x0F,0x8A,0xD8,0x2E,0x8A,0x87,0x62,0x17,0x8A, +0xDC,0x2E,0x8A,0xA7,0x62,0x17,0x09,0x46,0x3E,0x58,0xC3,0x50,0x33,0xDB,0xC1,0xE8, +0x04,0x25,0x0F,0x0F,0x8A,0xD8,0x2E,0x8A,0x87,0x62,0x17,0x8A,0xDC,0x2E,0x8A,0xA7, +0x62,0x17,0xF7,0xD0,0x21,0x46,0x3E,0x58,0xC3,0x8B,0x46,0x3E,0x33,0xDB,0x8A,0xD8, +0x0A,0xDC,0x2E,0x8A,0x87,0x72,0x17,0xE6,0x2C,0x8A,0xE0,0xE4,0x2A,0x24,0x0F,0x0A, +0xC4,0xE6,0x2A,0x8A,0x86,0xA5,0x00,0x84,0xE4,0x75,0x0D,0xA8,0x80,0x74,0x11,0x24, +0x7F,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xA8,0x80,0x75,0x04,0x0C,0x80,0xEB,0xF1, +0xC3,0x1E,0x60,0x33,0xC9,0x33,0xD2,0x33,0xF6,0x8E,0xD9,0x8D,0xBE,0xFD,0x00,0x57, +0x8B,0x05,0x84,0xC0,0x74,0x16,0x8B,0xD1,0x42,0x8B,0xFE,0x4F,0x78,0x09,0x38,0xA3, +0xE4,0x00,0x74,0x08,0x4F,0x79,0xF7,0x88,0xA2,0xE4,0x00,0x46,0x5F,0x83,0xC7,0x09, +0x41,0x83,0xF9,0x10,0x72,0xD9,0x89,0xB6,0x86,0x00,0x89,0x96,0x84,0x00,0x61,0x1F, +0xC3,0x53,0xC7,0x46,0x66,0x00,0x00,0x8B,0x46,0x64,0xA9,0x40,0x00,0x74,0x0D,0xB3, +0x00,0xA9,0x80,0x00,0x74,0x02,0xB3,0x7F,0x88,0x9E,0xC1,0x00,0x32,0xDB,0xA9,0x02, +0x00,0x74,0x03,0x80,0xCB,0x40,0xA9,0x00,0x40,0x74,0x03,0x80,0xCB,0x02,0xA9,0x00, +0x80,0x74,0x03,0x80,0xCB,0x01,0xA9,0x30,0x1E,0x74,0x03,0x80,0xCB,0xBC,0xA9,0x00, +0x20,0x74,0x03,0x80,0xCB,0x08,0xA9,0x04,0x01,0x74,0x03,0x80,0xCB,0x10,0xA9,0x08, +0x00,0x74,0x03,0x80,0xCB,0x20,0x88,0x9E,0xC2,0x00,0x5B,0xC3,0x06,0x51,0x57,0x50, +0x16,0x07,0x8D,0xBE,0xC4,0x00,0xB9,0x1F,0x00,0x33,0xC0,0xAA,0x40,0xE2,0xFC,0x8B, +0x86,0x92,0x00,0x89,0x86,0x8E,0x00,0x89,0x86,0x90,0x00,0x58,0x5F,0x59,0x07,0xC3, +0xE4,0xD8,0xC0,0xE8,0x04,0x53,0x25,0x0F,0x00,0x8B,0xD8,0x2E,0x8A,0x87,0x62,0x17, +0x88,0x86,0xA9,0x00,0x5A,0xC3,0x08,0x86,0xAC,0x00,0xC6,0x86,0xBA,0x00,0x01,0xB0, +0x0E,0xE8,0xEA,0xE9,0xC3,0xAD,0x36,0xA3,0xB4,0x13,0xAD,0x36,0xA3,0xB6,0x13,0xAD, +0x36,0xA3,0xB8,0x13,0x83,0xE9,0x06,0x36,0xF7,0x06,0xB6,0x13,0x0F,0x00,0xC3,0x8A, +0x46,0x26,0xF7,0x46,0x48,0x80,0x00,0x74,0x02,0x0C,0x10,0x88,0x86,0xBD,0x00,0x32, +0xC0,0x83,0x7E,0x1A,0x00,0x75,0x0E,0x8B,0x5E,0x40,0x43,0x80,0xE3,0xFE,0x3B,0x5E, +0x08,0x75,0x02,0x0C,0x01,0x83,0x7E,0x3A,0x00,0x75,0x0D,0x1E,0xC5,0x5E,0x14,0x8B, +0x1F,0x1F,0x85,0xDB,0x75,0x02,0x0C,0x02,0xF7,0x46,0x38,0x10,0x00,0x74,0x02,0x0C, +0x04,0xF7,0x46,0x7A,0x02,0x00,0x74,0x02,0x0C,0x08,0x88,0x86,0xBF,0x00,0xC3,0x90, +0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x0D,0x9C,0x0E,0xE8,0x0B,0xEC,0x90,0xB0,0x02, +0xE6,0xDA,0xF8,0xC3,0x33,0xC0,0xE6,0xDA,0xF8,0xC3,0xB0,0x01,0xE6,0xD8,0xF8,0xC3, +0x33,0xC0,0xE6,0xD8,0xF8,0xC3,0xB0,0xFF,0xE8,0x68,0xFA,0xE8,0xBB,0xFA,0xF8,0xC3, +0xAC,0x49,0xE8,0xC9,0xFB,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x2F,0xFD,0xF8,0xC3,0x90, +0xAC,0x49,0xE8,0x81,0xFD,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x39,0xFD,0xF8,0xC3,0x90, +0xAC,0x49,0xE6,0x34,0xF8,0xC3,0xAC,0x49,0xE6,0x36,0xF8,0xC3,0xAC,0x49,0x3C,0x02, +0x77,0x1F,0x84,0xC0,0x75,0x1D,0xE4,0x14,0x24,0xEF,0xE6,0x14,0xE4,0x12,0x24,0x3F, +0xE6,0x12,0xE4,0x16,0xA8,0x04,0x74,0x09,0xE8,0x04,0xFA,0x72,0x04,0xB0,0x18,0xE6, +0x0A,0xF8,0xC3,0x8A,0xE0,0xE4,0x14,0x0C,0x10,0xE6,0x14,0xE4,0x12,0x0C,0xC0,0xF6, +0xC4,0x01,0x74,0x02,0x24,0x7F,0xE6,0x12,0xF8,0xC3,0xAC,0x49,0xE8,0x3F,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x40,0xE8,0x97,0xFD,0xE8,0xCE,0xFD,0xE8,0xC2,0xFE,0xB0,0x01, +0xE8,0xD3,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x9F,0xFD,0xE8,0xBA,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x10,0xE8,0x77,0xFD,0xE8,0xAE,0xFD,0xE8,0xA2,0xFE,0xB0,0x08, +0xE8,0xB3,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x7F,0xFD,0xE8,0x9A,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x80,0xE8,0x57,0xFD,0xE8,0x8E,0xFD,0xE8,0x82,0xFE,0xB0,0x02, +0xE8,0x93,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x5F,0xFD,0xE8,0x7A,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x20,0xE8,0x37,0xFD,0xE8,0x6E,0xFD,0xE8,0x62,0xFE,0xB0,0x04, +0xE8,0x73,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x3F,0xFD,0xE8,0x5A,0xFD,0xF8, +0xC3,0x90,0xAC,0x49,0xE8,0x48,0x14,0xE4,0x3C,0x24,0xE7,0x0A,0xC4,0xE6,0x3C,0xF8, +0xC3,0x90,0xB8,0xDE,0x3B,0x89,0x46,0x7C,0xE4,0x3C,0x0C,0x18,0xE6,0x3C,0xF8,0xC3, +0xE4,0x12,0x0C,0x02,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFD,0xEB,0xF6,0xE8,0xCF, +0xFC,0xF8,0xC3,0x90,0x83,0x66,0x38,0xFD,0xF8,0xC3,0xAC,0x49,0xA8,0x01,0x74,0x06, +0x83,0x4E,0x7A,0x20,0xEB,0x04,0x83,0x66,0x7A,0xDF,0xE8,0xE5,0xFB,0xF8,0xC3,0x90, +0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x81,0x4E, +0x26,0x01,0x20,0xAC,0x49,0x32,0xE4,0x89,0x46,0x6E,0x83,0x4E,0x48,0x08,0x49,0x46, +0xF9,0xC3,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x81,0x4E,0x26,0x01,0x20,0xAC,0xB4,0x0A,0xF6,0xE4,0xEB,0xD8,0xE8,0xFA,0x13,0xE4, +0x3C,0x24,0xF8,0x0A,0xC4,0xE6,0x3C,0xF8,0xC3,0x90,0xAD,0x49,0x49,0x89,0x46,0x64, +0xA9,0x01,0x00,0x74,0x1B,0x8B,0xD8,0x83,0xE3,0xFA,0x75,0x1A,0xA9,0x04,0x00,0x74, +0x0F,0xE4,0x3E,0x0C,0x02,0xE6,0x3E,0xB8,0x1A,0x44,0x89,0x46,0x62,0xF8,0xC3,0x90, +0xE4,0x3E,0x24,0xFC,0xEB,0xEF,0xE4,0x3E,0x24,0xFC,0xE6,0x3E,0xE8,0x02,0xFD,0xB8, +0x8C,0x40,0xEB,0xE6,0xE8,0x88,0xF8,0x72,0x05,0xB0,0x18,0xE6,0x0A,0xF8,0xC3,0x90, +0xAC,0x49,0xE8,0xE9,0xF9,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0xE9,0xF9,0xF8,0xC3,0x90, +0xE8,0x82,0xFD,0x75,0x06,0x32,0xC0,0xE6,0xDA,0xF8,0xC3,0xB0,0x02,0xE6,0xDA,0x36, +0xA0,0xB4,0x13,0x24,0x10,0x34,0x10,0xE8,0x16,0x01,0x36,0xA1,0xB4,0x13,0xA9,0x01, +0x00,0x74,0x05,0xE8,0xFC,0xFE,0xEB,0x0E,0xA9,0x02,0x00,0x74,0x04,0x32,0xC0,0xEB, +0x02,0xB0,0x01,0xE8,0xDE,0xFE,0x36,0xA1,0xB4,0x13,0xE8,0xB5,0x13,0xE4,0x3C,0x24, +0xF8,0x0A,0xC4,0xE6,0x3C,0x36,0xA1,0xB4,0x13,0xC1,0xE8,0x05,0x25,0x01,0x00,0xE8, +0xFA,0xFE,0x36,0xA0,0xB5,0x13,0x24,0x10,0xE8,0x73,0xFB,0x32,0xC0,0x36,0x8A,0x26, +0xB5,0x13,0xF6,0xC4,0x04,0x74,0x09,0xFE,0xC0,0xF6,0xC4,0x08,0x74,0x02,0xFE,0xC0, +0xE8,0xDB,0xFD,0x36,0xA1,0xB6,0x13,0x25,0x0F,0x00,0xE8,0x71,0xF9,0x36,0xA1,0xB6, +0x13,0xC1,0xE8,0x04,0x25,0x03,0x00,0xE8,0xD2,0xFA,0x36,0xA1,0xB6,0x13,0xC1,0xE8, +0x05,0x25,0x02,0x00,0xE8,0x1F,0xFB,0x36,0xA1,0xB6,0x13,0xF6,0xC4,0x01,0x75,0x04, +0x32,0xC0,0xEB,0x09,0x80,0xE4,0x02,0xD0,0xEC,0xB0,0x02,0x2A,0xC4,0xE8,0xC6,0xFA, +0x36,0xF6,0x06,0xB7,0x13,0x40,0x74,0x05,0xE8,0x83,0xFE,0xEB,0x03,0xE8,0x84,0xFE, +0x36,0xF6,0x06,0xB7,0x13,0x20,0x74,0x05,0xE8,0x65,0xFE,0xEB,0x03,0xE8,0x68,0xFE, +0xF8,0xC3,0xE4,0x12,0x0C,0x01,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFE,0xEB,0xF6, +0xE4,0x14,0x24,0xF0,0x0C,0x05,0xE6,0x14,0xE4,0x2A,0x24,0xF0,0x0C,0x06,0xE6,0x2A, +0xF8,0xC3,0xE4,0x2A,0x24,0xF0,0xE6,0x2A,0xE4,0x14,0x24,0xF0,0x0C,0x07,0xE6,0x14, +0xF8,0xC3,0xAD,0x49,0x49,0xE8,0x7E,0xF9,0x89,0x86,0x8E,0x00,0xF8,0xC3,0xAD,0x49, +0x49,0xE8,0x72,0xF9,0x89,0x86,0x90,0x00,0xF8,0xC3,0x83,0x4E,0x26,0x04,0xE8,0xC2, +0xF7,0xF8,0xC3,0x90,0x83,0x66,0x26,0xFB,0xE8,0xB8,0xF7,0xF8,0xC3,0x90,0xAC,0x49, +0x84,0xC0,0x75,0x0D,0xE4,0x10,0x24,0xEF,0xE6,0x10,0x80,0x8E,0xA1,0x00,0x42,0xF8, +0xC3,0xE4,0x10,0x0C,0x10,0xEB,0xF1,0x90,0xAC,0x49,0x3C,0x02,0x76,0x02,0x32,0xC0, +0xC0,0xE0,0x04,0xA8,0x20,0x74,0x02,0x0C,0x08,0x24,0x18,0x8A,0xE0,0xE4,0x12,0x24, +0xE7,0x0A,0xC4,0xE6,0x12,0x80,0x8E,0xA1,0x00,0x44,0xF8,0xC3,0xAC,0x49,0x88,0x86, +0xC0,0x00,0xF8,0xC3,0xAC,0x49,0xE6,0x3A,0xF8,0xC3,0xAC,0x49,0x84,0xC0,0x74,0x08, +0xE4,0x12,0x0C,0x04,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFB,0xEB,0xF6,0xAC,0x49, +0xE8,0xF0,0xF6,0x73,0x03,0xE8,0x41,0xF7,0xF8,0xC3,0xE4,0x12,0xA8,0x02,0x74,0x04, +0x24,0xFD,0xE6,0x12,0xB8,0xF0,0x00,0xE8,0xA1,0xFA,0x81,0x66,0x26,0xFF,0xF3,0xE8, +0x71,0xF7,0xE8,0xB4,0xFA,0xF8,0xC3,0x90,0xB8,0x80,0x00,0xE8,0x71,0xFA,0x80,0x4E, +0x27,0x08,0xE8,0x5E,0xF7,0xE8,0xA1,0xFA,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x7B,0xFA, +0x81,0x66,0x26,0xFF,0xF7,0xE8,0x4B,0xF7,0xE8,0x8E,0xFA,0xF8,0xC3,0x90,0xB8,0x10, +0x00,0xE8,0x4B,0xFA,0x80,0x4E,0x27,0x04,0xE8,0x38,0xF7,0xE8,0x7B,0xFA,0xF8,0xC3, +0xB8,0x10,0x00,0xE8,0x55,0xFA,0x81,0x66,0x26,0xFF,0xFB,0xE8,0x25,0xF7,0xE8,0x68, +0xFA,0xF8,0xC3,0x90,0x33,0xC0,0xAC,0x49,0x3C,0x01,0x73,0x04,0xB0,0x01,0xEB,0x06, +0x3C,0x0C,0x76,0x02,0xB0,0x0C,0x89,0x46,0x1C,0xF8,0xC3,0x90,0x81,0x4E,0x26,0x00, +0x20,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x83, +0x4E,0x26,0x01,0xF8,0xC3,0x90,0x81,0x4E,0x26,0x00,0x40,0x8A,0x86,0xA5,0x00,0x0C, +0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xF8,0xC3,0x90,0xAC,0x49,0x50,0xE8,0x1F,0xF6, +0x58,0x72,0x08,0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xF8,0xC3,0xF9,0xC3,0x90,0xAC,0x50, +0xAD,0xE8,0x9C,0xF8,0x5A,0xF6,0xC2,0x01,0x74,0x12,0x39,0x86,0x96,0x00,0x74,0x0C, +0x89,0x86,0x96,0x00,0xE6,0xF0,0x86,0xE0,0xE6,0xF8,0x86,0xE0,0xF6,0xC2,0x02,0x74, +0x10,0x39,0x86,0x94,0x00,0x74,0x0A,0x89,0x86,0x94,0x00,0xE6,0xE4,0x86,0xE0,0xE6, +0xEC,0x83,0xE9,0x03,0xC3,0x90,0xE4,0x16,0x88,0x86,0xBC,0x00,0xE8,0x00,0xFB,0x33, +0xDB,0xE4,0x0C,0xA8,0x06,0x74,0x03,0x80,0xCB,0x01,0xA8,0x10,0x74,0x03,0x80,0xCB, +0x02,0xA8,0x80,0x74,0x03,0x80,0xCB,0x04,0xE4,0x12,0x8A,0xE0,0x24,0x18,0x0A,0xD8, +0xE4,0xDA,0xF6,0xC4,0x02,0x74,0x07,0xA8,0x40,0x75,0x03,0x80,0xCB,0x20,0xA8,0x02, +0x75,0x09,0xE4,0x2A,0xA8,0x0F,0x74,0x03,0x80,0xCB,0x40,0xF7,0x46,0x38,0x02,0x00, +0x74,0x09,0xE4,0xD8,0xA8,0x01,0x75,0x03,0x80,0xCB,0x80,0x88,0x9E,0xBE,0x00,0xFE, +0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x76,0xE4,0xF8,0xC3,0xAC,0x49,0x3C,0x02,0x74,0x41, +0x77,0x1F,0x50,0xE8,0x69,0xF5,0x58,0x72,0x0C,0x84,0xC0,0x74,0x0A,0xB0,0x12,0xE6, +0x0A,0x80,0x4E,0x38,0x01,0xF8,0xC3,0xB0,0x11,0xE6,0x0A,0x80,0x66,0x38,0xFE,0xF8, +0xC3,0x8B,0x46,0x38,0x25,0xFF,0xF7,0x89,0x46,0x38,0xA9,0x00,0x04,0x75,0xE6,0x8A, +0x86,0xA5,0x00,0xA8,0x10,0x75,0xDE,0x0C,0x10,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xF8, +0xC3,0x81,0x4E,0x38,0x00,0x08,0x8A,0x86,0xA5,0x00,0xA8,0x10,0x74,0xC7,0x24,0xEF, +0xEB,0xE7,0xAD,0x49,0x49,0x3C,0x01,0x72,0x11,0x3C,0x0C,0x77,0x0D,0x50,0x8A,0xE0, +0xE4,0x14,0x25,0xF0,0x0F,0x0A,0xC4,0xE6,0x14,0x58,0x8A,0xC4,0x84,0xC0,0x74,0x02, +0xE6,0x42,0xF8,0xC3,0xE8,0xE9,0xF9,0xFE,0x86,0xB9,0x00,0xB0,0x0E,0xE8,0xEE,0xE3, +0xF8,0xC3,0x3A,0x86,0xAF,0x00,0x74,0x1F,0x88,0x86,0xAF,0x00,0x8A,0xE0,0x80,0xC2, +0x06,0xB0,0xBF,0xEE,0x80,0xEA,0x02,0x8A,0xC4,0xEE,0x8A,0x86,0xA8,0x00,0x80,0xC2, +0x02,0xEE,0x80,0xEA,0x06,0x8A,0xC4,0xC3,0x8B,0x46,0x3E,0x85,0xC0,0x8A,0x86,0xA5, +0x00,0x74,0x12,0xA8,0x08,0x75,0x0D,0x0C,0x08,0x88,0x86,0xA5,0x00,0x80,0xC2,0x02, +0xEE,0x80,0xEA,0x02,0xC3,0xA8,0x08,0x74,0xFB,0x24,0xF7,0xEB,0xEC,0x8B,0x46,0x26, +0x84,0xC0,0x74,0x16,0x8A,0x86,0xA5,0x00,0xA8,0x02,0x74,0x0D,0x24,0xFD,0x88,0x86, +0xA5,0x00,0x83,0xC2,0x02,0xEE,0x83,0xEA,0x02,0xC3,0x8A,0x86,0xA5,0x00,0xA8,0x02, +0x75,0xF7,0x0C,0x02,0xEB,0xE8,0x52,0x83,0xC2,0x0C,0xEC,0xC0,0xE8,0x04,0x88,0x86, +0xA9,0x00,0x8B,0x5E,0x26,0x80,0xE3,0x3F,0xF6,0xC7,0x04,0x74,0x07,0xA8,0x08,0x75, +0x03,0x80,0xCB,0x40,0xF6,0xC7,0x08,0x74,0x07,0xA8,0x02,0x75,0x03,0x80,0xCB,0x80, +0x88,0x5E,0x26,0x8A,0x86,0xA5,0x00,0x84,0xDB,0x74,0x10,0xA8,0x02,0x74,0x0A,0x24, +0xFD,0x88,0x86,0xA5,0x00,0x83,0xEA,0x0A,0xEE,0x5A,0xC3,0xA8,0x02,0x75,0xFA,0x0C, +0x02,0xEB,0xEE,0x90,0xFF,0xFF,0x00,0x48,0x00,0x30,0xBA,0x20,0xC4,0x1A,0x00,0x18, +0x00,0x12,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x02,0x80,0x01,0xC0,0x00,0x60,0x00, +0x30,0x00,0x18,0x00,0xCD,0x01,0x00,0x01,0x80,0x00,0x10,0x00,0x10,0x00,0x0E,0x00, +0x0C,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x03,0x00,0x02,0x00, +0x01,0x00,0x52,0x51,0x56,0x3C,0x1E,0x77,0x47,0x98,0x8B,0xF0,0x8A,0x82,0xC4,0x00, +0x32,0xE4,0x83,0xFE,0x18,0x74,0x3D,0x83,0xFE,0x19,0x74,0x3E,0x83,0xFE,0x1E,0x77, +0x2F,0xD1,0xE6,0x2E,0x8B,0x8C,0x14,0x2D,0x3B,0x8E,0x94,0x00,0x74,0x22,0x89,0x8E, +0x94,0x00,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x8A,0xE0,0x0C,0x80,0xEE,0x83,0xEA, +0x06,0x8A,0xC1,0xEE,0x83,0xC2,0x02,0x8A,0xC5,0xEE,0x83,0xC2,0x04,0x8A,0xC4,0xEE, +0x5E,0x59,0x5A,0xC3,0x8B,0x8E,0x8E,0x00,0xEB,0xCE,0x8B,0x8E,0x90,0x00,0xEB,0xC8, +0x52,0x51,0x3D,0x05,0x00,0x77,0x03,0xB8,0x05,0x00,0x8B,0xC8,0xBA,0x02,0x00,0xB8, +0x00,0xD0,0xF7,0xF1,0x05,0x01,0x00,0xD1,0xE8,0x59,0x5A,0xC3,0x8B,0x46,0x7A,0xA8, +0x20,0x74,0x0B,0x80,0xBE,0xC3,0x00,0x03,0x75,0x04,0x0C,0x01,0xEB,0x02,0x24,0xFE, +0x89,0x46,0x7A,0xC3,0x24,0x03,0x88,0x86,0xC3,0x00,0x8A,0xA6,0xA8,0x00,0x8A,0xDC, +0x80,0xE4,0xFC,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83,0xC2,0x06, +0xEE,0x83,0xEA,0x06,0xE8,0xC5,0xFF,0xC3,0x00,0x08,0x18,0x38,0x28,0x90,0x3C,0x04, +0x77,0x23,0x32,0xE4,0x8B,0xD8,0x2E,0x8A,0x87,0x08,0x2E,0x8A,0xA6,0xA8,0x00,0x8A, +0xDC,0x80,0xE4,0xC7,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83,0xC2, +0x06,0xEE,0x83,0xEA,0x06,0xC3,0x84,0xC0,0x74,0x02,0xB0,0x04,0x8A,0xA6,0xA8,0x00, +0x8A,0xDC,0x80,0xE4,0xFB,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83, +0xC2,0x06,0xEE,0x83,0xEA,0x06,0xC3,0x90,0x8B,0x5E,0x38,0x84,0xC0,0x74,0x34,0x3C, +0x02,0x74,0x3B,0x8A,0x86,0xAF,0x00,0x0C,0x04,0xE8,0xE6,0xFD,0x8B,0x46,0x2E,0x3B, +0x46,0x3C,0x77,0x1B,0xF7,0xC3,0x00,0x04,0x75,0x15,0x81,0xCB,0x00,0x04,0x83,0xC2, +0x02,0x8A,0x86,0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5,0x00,0xEE,0x83,0xEA,0x02,0x89, +0x5E,0x38,0xC3,0x8A,0x86,0xAF,0x00,0x24,0xFB,0xE8,0xB6,0xFD,0xEB,0xF1,0xF7,0xC3, +0x10,0x00,0x74,0xEF,0xEB,0xED,0x83,0xC2,0x0C,0xEC,0x83,0xEA,0x0C,0xC0,0xE8,0x04, +0x88,0x86,0xA9,0x00,0xC3,0x90,0x8A,0x86,0xA7,0x00,0x0C,0x01,0x88,0x86,0xA7,0x00, +0x8B,0xDA,0x80,0xC2,0x08,0xEE,0x8B,0xD3,0xF8,0xC3,0x8A,0x86,0xA7,0x00,0x24,0xFE, +0xEB,0xEA,0x8A,0x86,0xA7,0x00,0x0C,0x02,0xEB,0xE2,0x8A,0x86,0xA7,0x00,0x24,0xFD, +0xEB,0xDA,0xB0,0xFF,0xE8,0x6C,0xF2,0xE8,0xB1,0xF2,0xF8,0xC3,0xAC,0x49,0xE8,0x61, +0xFE,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0xEB,0xFE,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x35, +0xFF,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x05,0xFF,0xF8,0xC3,0x90,0x52,0x83,0xC2,0x06, +0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x02,0xAC,0x49,0xEE,0x5A,0x8A,0x86,0xA8,0x00,0xEE, +0x5A,0xF8,0xC3,0x90,0x52,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x06,0xEB, +0xE6,0x90,0xAC,0x49,0x3C,0x02,0x77,0x0D,0x84,0xC0,0x75,0x0B,0x8A,0x86,0xAF,0x00, +0x24,0xFD,0xE8,0x0D,0xFD,0xF8,0xC3,0x50,0x8A,0x86,0xAF,0x00,0x0C,0x02,0xE8,0x01, +0xFD,0x5B,0x83,0xC2,0x08,0x8A,0x86,0xA7,0x00,0xF6,0xC3,0x01,0x74,0x0C,0x24,0xDF, +0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA,0x08,0xF8,0xC3,0x0C,0x20,0xEB,0xF2,0xAC,0x49, +0xE8,0xE5,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x83,0xF5,0xE8,0xF9,0xFC,0xE8, +0x24,0xFF,0xB0,0x01,0xE8,0xBF,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x8B,0xF5, +0xE8,0xE5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x63,0xF5,0xE8,0xD9,0xFC,0xE8, +0x04,0xFF,0xB0,0x08,0xE8,0x9F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x6B,0xF5, +0xE8,0xC5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x43,0xF5,0xE8,0xB9,0xFC,0xE8, +0xE4,0xFE,0xB0,0x02,0xE8,0x7F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x4B,0xF5, +0xE8,0xA5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x23,0xF5,0xE8,0x99,0xFC,0xE8, +0xC4,0xFE,0xB0,0x04,0xE8,0x5F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x2B,0xF5, +0xE8,0x85,0xFC,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x34,0x0C,0xF8,0xC3,0x90,0xB8,0xDE, +0x3B,0x89,0x46,0x7C,0xF8,0xC3,0x8A,0x86,0xAF,0x00,0x0C,0x80,0xE8,0x43,0xFC,0xF8, +0xC3,0x90,0x8A,0x86,0xAF,0x00,0x24,0x7F,0xEB,0xF2,0x8A,0x86,0xAF,0x00,0x0C,0x40, +0xE8,0x2F,0xFC,0xF8,0xC3,0x90,0x8A,0x86,0xAF,0x00,0x24,0xBF,0xEB,0xF2,0xAC,0x49, +0xA8,0x01,0x74,0x07,0x83,0x4E,0x7A,0x20,0xEB,0x05,0x90,0x83,0x66,0x7A,0xDF,0xE8, +0x8A,0xFD,0xF8,0xC3,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8, +0x00,0xEE,0x83,0xEA,0x06,0xAC,0x49,0x32,0xE4,0x89,0x46,0x6E,0x83,0x4E,0x26,0x01, +0x83,0x4E,0x48,0x08,0xB0,0x06,0xE8,0xD5,0xDF,0x49,0x46,0xF9,0xC3,0x90,0x83,0xC2, +0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8,0x00,0xEE,0x83,0xEA,0x06,0xAC, +0xB4,0x0A,0xF6,0xE4,0xEB,0xD0,0xE8,0xE0,0x0B,0xF8,0xC3,0x90,0xAD,0x49,0x49,0x89, +0x46,0x64,0xA9,0x01,0x00,0x74,0x19,0x8B,0xD8,0x83,0xE3,0xFA,0x75,0x0A,0xA9,0x04, +0x00,0x74,0x0D,0xB8,0xC4,0x3F,0xEB,0x0B,0xE8,0x06,0xF5,0xB8,0x8C,0x40,0xEB,0x03, +0xB8,0x1A,0x44,0x89,0x46,0x62,0xF8,0xC3,0x8A,0x86,0xAF,0x00,0xA8,0x02,0x74,0x0A, +0x24,0xFD,0xE8,0x8D,0xFB,0x0C,0x02,0xE8,0x88,0xFB,0xF8,0xC3,0xAC,0x49,0xE8,0x81, +0xFC,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x79,0xFC,0xF8,0xC3,0x90,0xE8,0x76,0xF5,0x75, +0x05,0xE8,0xE6,0xFD,0xF8,0xC3,0xE8,0xCD,0xFD,0x36,0xA0,0xB4,0x13,0x24,0x10,0x34, +0x10,0xE8,0x26,0x01,0x36,0xA1,0xB4,0x13,0xA9,0x01,0x00,0x74,0x05,0xE8,0xFE,0xFE, +0xEB,0x0E,0xA9,0x02,0x00,0x74,0x04,0x32,0xC0,0xEB,0x02,0xB0,0x01,0xE8,0xE8,0xFE, +0x36,0xA1,0xB4,0x13,0xE8,0xAB,0x0B,0x36,0xA1,0xB4,0x13,0xC1,0xE8,0x05,0x25,0x01, +0x00,0xE8,0x0C,0xFF,0x36,0xA0,0xB5,0x13,0x24,0x10,0xE8,0x2B,0xFD,0x32,0xC0,0x36, +0x8A,0x26,0xB5,0x13,0xF6,0xC4,0x04,0x74,0x09,0xFE,0xC0,0xF6,0xC4,0x08,0x74,0x02, +0xFE,0xC0,0xE8,0xEF,0xFD,0x36,0xA1,0xB6,0x13,0x25,0x0F,0x00,0xE8,0x03,0xFC,0x36, +0xA1,0xB6,0x13,0xC1,0xE8,0x04,0x25,0x03,0x00,0xE8,0x88,0xFC,0x36,0xA1,0xB6,0x13, +0xC1,0xE8,0x05,0x25,0x02,0x00,0xE8,0xCD,0xFC,0x36,0xA1,0xB6,0x13,0xF6,0xC4,0x01, +0x75,0x04,0x32,0xC0,0xEB,0x09,0x80,0xE4,0x02,0xD0,0xEC,0xB0,0x02,0x2A,0xC4,0xE8, +0x8C,0xFC,0x36,0xF6,0x06,0xB7,0x13,0x40,0x74,0x05,0xE8,0x8D,0xFE,0xEB,0x03,0xE8, +0x94,0xFE,0x36,0xF6,0x06,0xB7,0x13,0x20,0x74,0x05,0xE8,0x69,0xFE,0xEB,0x03,0xE8, +0x70,0xFE,0xF8,0xC3,0xF8,0xC3,0x8B,0x46,0x38,0xA9,0x04,0x00,0x75,0x23,0x0D,0x04, +0x00,0x89,0x46,0x38,0x83,0xC2,0x08,0x8B,0x46,0x2E,0x3B,0x46,0x3C,0x73,0x14,0x83, +0x4E,0x38,0x10,0x8A,0x86,0xA7,0x00,0x24,0xFE,0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA, +0x08,0xF8,0xC3,0x8A,0x86,0xA7,0x00,0x0C,0x01,0xEB,0xEE,0x90,0x8B,0x46,0x38,0xA9, +0x04,0x00,0x74,0x06,0x25,0xFB,0xFF,0x89,0x46,0x38,0xF8,0xC3,0xAD,0x49,0x49,0xE8, +0xBE,0xFB,0x89,0x86,0x8E,0x00,0xF8,0xC3,0xAD,0x49,0x49,0xE8,0xB2,0xFB,0x89,0x86, +0x90,0x00,0xF8,0xC3,0x83,0x4E,0x26,0x04,0xE8,0x92,0xFA,0xF8,0xC3,0x90,0x83,0x66, +0x26,0xFB,0xE8,0x88,0xFA,0xF8,0xC3,0x90,0xAC,0x49,0x84,0xC0,0x75,0x07,0x80,0x8E, +0xA3,0x00,0x04,0xF8,0xC3,0x80,0xA6,0xA3,0x00,0xFB,0xF8,0xC3,0xAC,0x49,0x83,0xC2, +0x08,0x3C,0x02,0x76,0x02,0x32,0xC0,0x3C,0x01,0x74,0x12,0x77,0x0B,0x8A,0x86,0xA7, +0x00,0x24,0xEF,0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA,0x08,0xF8,0xC3,0x8A,0x86,0xA7, +0x00,0x0C,0x10,0xEB,0xEE,0x90,0x52,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2, +0x04,0xAC,0x49,0xEE,0x5A,0x8A,0x86,0xA8,0x00,0xEE,0x5A,0xF8,0xC3,0x90,0x52,0x83, +0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x08,0xEB,0xE6,0x90,0xAC,0x49,0xF8,0xC3, +0xAC,0x49,0xE8,0xCE,0xEE,0x73,0x03,0xE8,0x11,0xEF,0xF8,0xC3,0x8A,0x86,0xAF,0x00, +0x24,0x7F,0xE8,0xBD,0xF9,0xB8,0xF0,0x00,0xE8,0x80,0xF2,0x81,0x66,0x26,0xFF,0xF3, +0xE8,0x23,0xFA,0xE8,0xD2,0xF9,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x51,0xF2,0x80,0x4E, +0x27,0x08,0xE8,0x11,0xFA,0xE8,0xC0,0xF9,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x5B,0xF2, +0x81,0x66,0x26,0xFF,0xF7,0xE8,0xFE,0xF9,0xE8,0xAD,0xF9,0xF8,0xC3,0x90,0xB8,0x10, +0x00,0xE8,0x2B,0xF2,0x80,0x4E,0x27,0x04,0xE8,0xEB,0xF9,0xE8,0x9A,0xF9,0xF8,0xC3, +0xB8,0x10,0x00,0xE8,0x19,0xF2,0x81,0x66,0x26,0xFF,0xFB,0xE8,0xD8,0xF9,0xF8,0xC3, +0xAC,0x49,0xF8,0xC3,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8, +0x00,0xEE,0x83,0xEA,0x06,0xF8,0xC3,0x90,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x24, +0xBF,0xEB,0xEA,0x90,0xAC,0x49,0x8A,0xE0,0x80,0xC2,0x0A,0xEC,0x80,0xEA,0x0A,0xA8, +0x20,0x74,0x05,0x8A,0xC4,0xEE,0xF8,0xC3,0x06,0x51,0x57,0x8B,0x4E,0x24,0xE3,0x34, +0x49,0x89,0x4E,0x24,0xFF,0x46,0x1A,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x8A,0xC4,0xAA, +0x89,0x7E,0x22,0x8B,0x46,0x26,0x24,0xFD,0x89,0x46,0x26,0x75,0x29,0x8A,0x86,0xA5, +0x00,0xA8,0x02,0x75,0x21,0x80,0xC2,0x02,0x0C,0x02,0x88,0x86,0xA5,0x00,0xEE,0x80, +0xEA,0x02,0xEB,0x12,0xC4,0x7E,0x00,0x3B,0x7E,0x1E,0x76,0x0A,0x4F,0x26,0x88,0x25, +0x89,0x7E,0x00,0xFF,0x46,0x1A,0x5F,0x59,0x07,0xF8,0xC3,0x90,0xAC,0xAD,0x83,0xE9, +0x03,0x85,0xC0,0x74,0x05,0x3D,0x00,0x20,0x72,0x05,0xB8,0xFF,0xFF,0xEB,0x03,0xC1, +0xE0,0x03,0x3B,0x86,0x94,0x00,0x74,0x26,0x89,0x86,0x94,0x00,0x8B,0xD8,0x52,0x83, +0xC2,0x06,0x8A,0x86,0xA8,0x00,0x8A,0xE0,0x0C,0x80,0xEE,0x83,0xEA,0x06,0x8A,0xC3, +0xEE,0x83,0xC2,0x02,0x8A,0xC7,0xEE,0x83,0xC2,0x04,0x8A,0xC4,0xEE,0x5A,0xF8,0xC3, +0xB0,0x88,0x88,0x86,0xBC,0x00,0xE8,0xA6,0xF2,0x33,0xDB,0x8A,0x86,0xA5,0x00,0xA8, +0x02,0x74,0x03,0x80,0xCB,0x01,0xA8,0x05,0x74,0x03,0x80,0xCB,0x02,0xA8,0x08,0x74, +0x03,0x80,0xCB,0x04,0xF6,0x86,0xA7,0x00,0x10,0x74,0x03,0x80,0xCB,0x10,0x8A,0x86, +0xA9,0x00,0xF6,0xC3,0x04,0x75,0x0A,0x83,0xC2,0x0C,0xEC,0x83,0xEA,0x0C,0xC0,0xE8, +0x04,0x8A,0xE0,0x8A,0x86,0xAF,0x00,0xA8,0x80,0x74,0x08,0xF6,0xC4,0x01,0x75,0x03, +0x80,0xCB,0x20,0xF6,0x86,0xA7,0x00,0x02,0x75,0x0A,0xF7,0x46,0x38,0x04,0x00,0x74, +0x03,0x80,0xCB,0x40,0x88,0x9E,0xBE,0x00,0xFE,0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x0D, +0xDC,0xF8,0xC3,0xFE,0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x02,0xDC,0xF8,0xC3,0xAC,0x49, +0x3C,0x02,0x74,0x37,0x77,0x10,0x84,0xC0,0x74,0x06,0x80,0x4E,0x38,0x01,0xF8,0xC3, +0x80,0x66,0x38,0xFE,0xF8,0xC3,0x8B,0x46,0x38,0x25,0xFF,0xF7,0x89,0x46,0x38,0xA9, +0x00,0x04,0x75,0xEA,0x8A,0x86,0xA5,0x00,0xA8,0x01,0x75,0xE2,0x0C,0x05,0x83,0xC2, +0x02,0x88,0x86,0xA5,0x00,0xEE,0x83,0xEA,0x02,0xF8,0xC3,0x81,0x4E,0x38,0x00,0x08, +0x8A,0x86,0xA5,0x00,0xA8,0x01,0x74,0xC6,0x24,0xFA,0xEB,0xE2,0xAD,0x49,0x49,0xF8, +0xC3,0x90,0xE8,0x11,0xFA,0xFE,0x86,0xB9,0x00,0xB0,0x0E,0xE8,0xA0,0xDB,0xF8,0xC3, +0xB0,0xFF,0xE8,0xD9,0xEC,0xF8,0xC3,0x90,0x83,0x66,0x7A,0xFB,0xB0,0x00,0xE8,0x8D, +0xDB,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x6D,0xD9,0x72,0x11,0x36,0x88,0x1E,0x1A,0x01, +0x36,0xA0,0x8E,0x12,0x0A,0xC3,0x52,0xBA,0x00,0x01,0xEE,0x5A,0xF8,0xC3,0xAC,0x49, +0x32,0xE4,0x36,0xA3,0x86,0x12,0x05,0x06,0x00,0x36,0x8B,0x1E,0x88,0x12,0x2B,0xD8, +0x36,0x89,0x1E,0x8A,0x12,0xF8,0xC3,0x90,0xAD,0x8B,0xD8,0xAD,0x83,0xE9,0x04,0x03, +0xC3,0x2B,0x46,0x76,0x89,0x46,0x78,0xF7,0x46,0x7A,0x02,0x00,0x74,0x0A,0x83,0x66, +0x7A,0xFD,0xB8,0x00,0x00,0xE8,0x36,0xDB,0xF8,0xC3,0x06,0x16,0x07,0xAC,0x49,0x25, +0x0F,0x00,0x6B,0xC0,0x09,0x8D,0xBE,0xFD,0x00,0x03,0xF8,0xAC,0x49,0x25,0x0F,0x00, +0xAA,0x85,0xC0,0x74,0x08,0x2B,0xC8,0x51,0x8B,0xC8,0xF3,0xA4,0x59,0xE8,0x41,0xF0, +0xE8,0x44,0x03,0x07,0xF8,0xC3,0x33,0xC0,0xAC,0x49,0x36,0xA3,0xB2,0x13,0x36,0xA3, +0xB0,0x13,0xF8,0xC3,0x83,0x66,0x7A,0xEF,0xE8,0x2C,0x03,0xF8,0xC3,0x90,0x83,0x4E, +0x7A,0x10,0xEB,0xF4,0xE8,0xB5,0xF0,0xF8,0xC3,0x90,0xAD,0x3C,0x19,0x77,0x0E,0x3C, +0x19,0x77,0x0A,0x8B,0xF8,0x81,0xE7,0xFF,0x00,0x88,0xA6,0xC4,0x00,0xF8,0xC3,0x90, +0x83,0x4E,0x26,0x20,0xAC,0x49,0x32,0xE4,0xD1,0xE0,0x8B,0xD8,0xC1,0xE3,0x02,0x03, +0xC3,0x89,0x46,0x6E,0x83,0x4E,0x48,0x04,0xB0,0x06,0xE8,0xB1,0xDA,0x49,0x46,0xF9, +0xC3,0x90,0xFE,0x86,0xB3,0x00,0xB0,0x0A,0xE8,0xA3,0xDA,0xF8,0xC3,0x90,0x33,0xC0, +0xAC,0x49,0x6B,0xC0,0x0A,0x89,0x86,0x8A,0x00,0xF8,0xC3,0x90,0xAC,0x49,0x32,0xE4, +0x3D,0x0A,0x00,0x77,0x05,0xB8,0x0A,0x00,0xEB,0x08,0x3D,0x5A,0x00,0x72,0x03,0xB8, +0x5A,0x00,0x51,0xF7,0xD8,0x05,0x64,0x00,0x8B,0xC8,0x8B,0x46,0x44,0xF7,0xE1,0xB9, +0x64,0x00,0xF7,0xF1,0x89,0x46,0x46,0x59,0xF8,0xC3,0xAC,0x49,0xE8,0x9F,0xEB,0xF8, +0xC3,0x90,0xAC,0x49,0x84,0xC0,0x75,0x07,0x81,0x66,0x38,0xFF,0xFD,0xF8,0xC3,0x81, +0x4E,0x38,0x00,0x02,0xF7,0x46,0x38,0x40,0x00,0x75,0x08,0x8A,0x86,0xA9,0x00,0x88, +0x86,0xAA,0x00,0xF8,0xC3,0x90,0x51,0x56,0xE8,0x7F,0x0C,0x5E,0x59,0xF8,0xC3,0x90, +0xFE,0x86,0xB6,0x00,0xB0,0x0A,0xE8,0x25,0xDA,0xF8,0xC3,0x90,0xFE,0x86,0xB7,0x00, +0xB0,0x0A,0xE8,0x19,0xDA,0xF8,0xC3,0x90,0xFE,0x86,0xB8,0x00,0xB0,0x0A,0xE8,0x0D, +0xDA,0xF8,0xC3,0x90,0x00,0x90,0x51,0x55,0xAC,0x2E,0xA2,0x34,0x36,0x33,0xC9,0xAD, +0x8B,0xF9,0xC1,0xE7,0x05,0xA9,0x01,0x00,0x74,0x23,0x2E,0x8B,0xAD,0x44,0x00,0x83, +0x7E,0x08,0x00,0x74,0x18,0x2E,0x80,0x3E,0x34,0x36,0x01,0x74,0x09,0x60,0xB0,0x04, +0xE8,0xBB,0x0C,0x61,0xEB,0x07,0x60,0xB0,0xFB,0xE8,0xEC,0x0C,0x61,0x47,0x47,0xD1, +0xE8,0x75,0xD2,0x41,0x83,0xF9,0x04,0x72,0xC6,0x5D,0x59,0x83,0xE9,0x05,0xF7,0x46, +0x38,0x40,0x00,0x74,0x05,0xE8,0xA1,0xEA,0xF8,0xC3,0xE8,0xA7,0xEA,0xF8,0xC3,0x90, +0x36,0xC6,0x06,0xC8,0x13,0x01,0xF8,0xC3,0x33,0xC0,0xAC,0x49,0x36,0xA3,0x80,0x12, +0xAC,0x49,0x36,0x2B,0x06,0x88,0x12,0xF7,0xD8,0x36,0xA3,0x82,0x12,0xF8,0xC3,0x90, +0xC0,0x26,0xC0,0x26,0xCE,0x26,0xD4,0x26,0xDA,0x26,0xE0,0x26,0xE6,0x26,0xF0,0x26, +0xF8,0x26,0x00,0x27,0x08,0x27,0x10,0x27,0x16,0x27,0xA0,0x34,0xA8,0x34,0xB4,0x34, +0x1C,0x27,0x5A,0x27,0x62,0x27,0x76,0x27,0x82,0x27,0x96,0x27,0xA2,0x27,0xB6,0x27, +0xC2,0x27,0xD6,0x27,0xE2,0x27,0xF2,0x27,0xCE,0x34,0xC0,0x26,0x00,0x28,0x08,0x28, +0x0E,0x28,0x14,0x28,0x1A,0x28,0x30,0x28,0x6C,0x28,0xE8,0x34,0x0A,0x35,0x7A,0x28, +0xA0,0x28,0xB4,0x28,0xC0,0x28,0xC8,0x28,0x36,0x35,0x44,0x35,0x4E,0x35,0xD0,0x28, +0xA2,0x29,0xAA,0x29,0xB0,0x29,0xC2,0x29,0x54,0x35,0x5A,0x35,0xD2,0x29,0xDE,0x29, +0x70,0x35,0xEA,0x29,0xF4,0x29,0xFE,0x29,0x92,0x35,0x18,0x2A,0x9E,0x35,0x3C,0x2A, +0x44,0x2A,0x4A,0x2A,0xAC,0x35,0x5E,0x2A,0xDA,0x35,0x6A,0x2A,0x88,0x2A,0x9A,0x2A, +0xAE,0x2A,0xC0,0x2A,0xD4,0x2A,0xE2,0x35,0xEC,0x2A,0x06,0x2B,0x06,0x36,0x1A,0x2B, +0x2E,0x2B,0x66,0x2B,0x10,0x36,0x1C,0x36,0x28,0x36,0x36,0x36,0xCA,0x2B,0x90,0x36, +0x22,0x2C,0x44,0x2C,0x98,0x36,0x52,0x28,0xC0,0x26,0xC0,0x26,0xB6,0x2E,0xCA,0x2E, +0xD2,0x2E,0xDA,0x2E,0xE2,0x2E,0xEC,0x2E,0xF4,0x2E,0xFC,0x2E,0x04,0x2F,0x0C,0x2F, +0x24,0x2F,0xA0,0x34,0xA8,0x34,0xB4,0x34,0x32,0x2F,0x6E,0x2F,0x76,0x2F,0x8A,0x2F, +0x96,0x2F,0xAA,0x2F,0xB6,0x2F,0xCA,0x2F,0xD6,0x2F,0xEA,0x2F,0xF6,0x2F,0xFE,0x2F, +0xCE,0x34,0xC0,0x26,0x06,0x30,0x12,0x30,0x1A,0x30,0x26,0x30,0x2E,0x30,0x44,0x30, +0x86,0x30,0xE8,0x34,0x0A,0x35,0x8C,0x30,0xB0,0x30,0xB8,0x30,0xCC,0x30,0xD4,0x30, +0x36,0x35,0x44,0x35,0x4E,0x35,0xDC,0x30,0xA4,0x31,0xA4,0x31,0xA6,0x31,0xDC,0x31, +0x54,0x35,0x5A,0x35,0xEC,0x31,0xF8,0x31,0x70,0x35,0x04,0x32,0x0E,0x32,0x18,0x32, +0x92,0x35,0x2C,0x32,0x9E,0x35,0x56,0x32,0x6E,0x32,0x7C,0x32,0xAC,0x35,0x80,0x32, +0xDA,0x35,0x8C,0x32,0xA8,0x32,0xBA,0x32,0xCE,0x32,0xE0,0x32,0xF0,0x32,0xE2,0x35, +0xF4,0x32,0x08,0x33,0x06,0x36,0x14,0x33,0x7C,0x33,0xC0,0x33,0x10,0x36,0x1C,0x36, +0x28,0x36,0x36,0x36,0x3E,0x34,0x90,0x36,0x8C,0x34,0x92,0x34,0x98,0x36,0x6E,0x30, +0xE3,0x28,0xF7,0x46,0x38,0x40,0x00,0x75,0x32,0xE8,0xFD,0xE8,0x33,0xC0,0xAC,0x49, +0x3D,0x5B,0x00,0x77,0x19,0x8B,0xD8,0xD1,0xE3,0x2E,0xFF,0x97,0xB0,0x36,0x72,0x0B, +0x85,0xC9,0x75,0xE8,0x8B,0x46,0x48,0xE8,0x1A,0x0C,0xC3,0x4E,0x41,0xC3,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x0C,0x9C,0x0E,0xE8,0x7D,0xDA,0xE8,0xD6,0xE8,0x33,0xC0, +0xAC,0x49,0x3D,0x5B,0x00,0x77,0xE7,0x8B,0xD8,0xD1,0xE3,0x2E,0xFF,0x97,0x68,0x37, +0x72,0xD9,0x85,0xC9,0x75,0xE8,0xC3,0xF7,0x46,0x7A,0x10,0x00,0x75,0x0F,0x83,0xBE, +0x84,0x00,0x00,0x74,0x08,0xB8,0x2A,0x3A,0x89,0x86,0x80,0x00,0xC3,0x81,0xBE,0x80, +0x00,0xCE,0x3C,0x74,0xF7,0x83,0xBE,0x88,0x00,0x00,0x75,0x05,0xB8,0xCE,0x3C,0xEB, +0xE7,0xF7,0x46,0x7A,0x08,0x00,0x75,0x40,0x1E,0x60,0x8B,0x8E,0x88,0x00,0x3B,0x4E, +0x74,0x77,0x33,0x3B,0x4E,0x78,0x77,0x2E,0xC4,0x7E,0x10,0x8B,0xDF,0x26,0x03,0x3D, +0x47,0x47,0x33,0xC0,0x8E,0xD8,0x8D,0xB6,0xF4,0x00,0x8B,0xC1,0xF7,0x46,0x7A,0x01, +0x00,0x75,0x1D,0xF3,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46, +0x74,0xB0,0x0C,0xE8,0x58,0xD7,0x61,0x1F,0xC7,0x86,0x88,0x00,0x00,0x00,0xEB,0xAC, +0xE3,0xE3,0x50,0x90,0xAC,0x24,0x7F,0xAA,0xE2,0xFA,0x58,0xEB,0xD8,0x90,0x8B,0x8E, +0x88,0x00,0xE3,0x46,0x8B,0x9E,0x8A,0x00,0x85,0xDB,0x74,0x3E,0xBA,0x50,0xFF,0xED, +0x2B,0x86,0x82,0x00,0x3B,0xC3,0x72,0x37,0x8D,0xB6,0xF4,0x00,0xC4,0x7E,0x10,0x8B, +0xDF,0x26,0x03,0x3D,0x47,0x47,0x8B,0xC1,0x16,0x1F,0xF7,0x46,0x7A,0x01,0x00,0x75, +0x24,0xF3,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0xC7, +0x86,0x88,0x00,0x00,0x00,0xB0,0x0C,0xE8,0xF4,0xD6,0x83,0x66,0x7A,0xF7,0xC3,0xB0, +0x00,0xE8,0xEA,0xD6,0xC3,0xE3,0xDC,0x50,0xAC,0x24,0x7F,0xAA,0xE2,0xFA,0x58,0xEB, +0xD2,0x90,0x1E,0x60,0x33,0xC0,0x8E,0xD8,0x8D,0xB6,0xFD,0x00,0x8B,0x86,0x88,0x00, +0x8B,0x96,0x84,0x00,0x3A,0x04,0x75,0x10,0x8B,0xDE,0x46,0x8B,0xC8,0x8D,0xBE,0xF4, +0x00,0xF3,0xA6,0x74,0x66,0x8B,0xF3,0x90,0x83,0xC6,0x09,0x4A,0x75,0xE6,0x8D,0xB6, +0xFD,0x00,0x8B,0x96,0x84,0x00,0x3A,0x04,0x73,0x10,0x8B,0xDE,0x46,0x8B,0xC8,0x8D, +0xBE,0xF4,0x00,0xF3,0xA6,0x74,0x76,0x8B,0xF3,0x90,0x83,0xC6,0x09,0x4A,0x75,0xE6, +0x8D,0xB6,0xF4,0x00,0xAC,0xF7,0x46,0x7A,0x01,0x00,0x74,0x02,0x24,0x7F,0x1E,0xC5, +0x5E,0x10,0x8B,0x37,0x88,0x40,0x02,0x46,0x89,0x37,0xFF,0x4E,0x78,0xFF,0x46,0x76, +0xFF,0x4E,0x74,0x1F,0x8B,0x8E,0x88,0x00,0x49,0x89,0x8E,0x88,0x00,0xE3,0x43,0x8D, +0xB6,0xF4,0x00,0x8B,0xFE,0x46,0xF3,0xA4,0xE9,0x7D,0xFF,0xC5,0x76,0x10,0x8B,0x1C, +0x85,0xDB,0x74,0x08,0x03,0xF3,0x83,0xC6,0x03,0x83,0xE6,0xFE,0x8B,0x86,0x84,0x00, +0x2B,0xC2,0xB4,0x80,0x89,0x04,0x46,0x46,0xC7,0x04,0x00,0x00,0x89,0x76,0x10,0x83, +0x4E,0x7A,0x04,0xC7,0x86,0x88,0x00,0x00,0x00,0x61,0x1F,0xF9,0xC3,0x33,0xC0,0x61, +0x1F,0xC3,0xB0,0x80,0x84,0xC0,0x61,0x1F,0xC3,0x90,0x8B,0x4E,0x78,0x2B,0x8E,0x88, +0x00,0x76,0x27,0x89,0xB6,0x8C,0x00,0x8B,0x5E,0x74,0x3B,0xCB,0x72,0x02,0x8B,0xCB, +0x3B,0xC8,0x72,0x02,0x8B,0xC8,0x8B,0xC1,0xE3,0x44,0x33,0xD2,0x8E,0xC2,0x8B,0xD1, +0x83,0xBE,0x88,0x00,0x00,0x74,0x06,0xE9,0x8E,0x00,0x33,0xC0,0xC3,0x8B,0x5E,0x10, +0x03,0x1F,0x43,0x43,0x52,0xF7,0x46,0x7A,0x01,0x00,0x75,0x2A,0xAC,0x8D,0xBE,0xE4, +0x00,0x8B,0x8E,0x86,0x00,0xF2,0xAE,0x74,0x34,0x88,0x07,0x43,0x4A,0x75,0xED,0x58, +0x8B,0x5E,0x10,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0x8B,0xC6, +0x2B,0x86,0x8C,0x00,0xC3,0x90,0xAC,0x8D,0xBE,0xE4,0x00,0x8B,0x8E,0x86,0x00,0xF2, +0xAE,0x74,0x0A,0x24,0x7F,0x88,0x07,0x43,0x4A,0x75,0xEB,0xEB,0xD2,0x88,0x86,0xF4, +0x00,0xC7,0x86,0x88,0x00,0x01,0x00,0x58,0x2B,0xC2,0x74,0x0E,0x8B,0x5E,0x10,0x01, +0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0x40,0xE8,0x94,0xFE,0x72,0xBE, +0x4A,0x75,0x15,0x83,0xBE,0x8A,0x00,0x00,0x74,0xB4,0xBA,0x50,0xFF,0xED,0x89,0x86, +0x82,0x00,0x83,0x4E,0x7A,0x08,0xEB,0xA6,0x8D,0xBE,0xF4,0x00,0x03,0xBE,0x88,0x00, +0xA4,0xFF,0x86,0x88,0x00,0xE8,0x6A,0xFE,0x72,0x94,0x79,0x06,0x4A,0x74,0x8F,0xE9, +0x5B,0xFF,0x4A,0x74,0xCE,0xEB,0xE1,0x90,0x50,0xE8,0x2B,0xCC,0x8B,0x46,0x74,0x39, +0x46,0x72,0x74,0x27,0x1E,0x56,0x51,0x33,0xC9,0xC5,0x76,0x0C,0xAD,0x74,0x10,0x78, +0x09,0x03,0xC8,0x05,0x01,0x00,0x24,0xFE,0x03,0xF0,0x3B,0x76,0x10,0x76,0xED,0x29, +0x4E,0x76,0x01,0x4E,0x78,0xE8,0x51,0xCC,0x59,0x5E,0x1F,0x58,0xC3,0x90,0xC4,0x7E, +0x10,0x26,0x8B,0x1D,0x83,0xC3,0x03,0x26,0x89,0x1D,0x4B,0x03,0xFB,0xAB,0x91,0xAA, +0xB8,0x03,0x00,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0xC3,0x90,0xC4,0x7E, +0x10,0x26,0x8B,0x1D,0x43,0x26,0x89,0x1D,0x43,0x03,0xFB,0xAA,0xFF,0x4E,0x78,0xFF, +0x46,0x76,0xFF,0x4E,0x74,0xC3,0xE8,0xE5,0xFF,0xC3,0x80,0x81,0x84,0x85,0x82,0x83, +0x86,0x87,0x50,0x53,0x8A,0xDC,0x83,0xE3,0x0E,0xD1,0xEB,0x2E,0x8A,0x87,0x7A,0x3B, +0x08,0x86,0xB0,0x00,0xFE,0x86,0xB1,0x00,0xB0,0x0A,0xE8,0xA1,0xD4,0x5B,0x58,0xC3, +0x50,0x8A,0xC8,0xB8,0xFF,0x00,0xE8,0x95,0xFF,0x58,0xC3,0x90,0x8A,0x86,0xBB,0x00, +0xE8,0xAB,0xFF,0xC3,0xE8,0xCB,0xFF,0xE8,0xF2,0xFF,0xC3,0x90,0xE8,0xC3,0xFF,0xE8, +0xB4,0xFF,0xC3,0x90,0x33,0xC0,0xE8,0x95,0xFF,0xC3,0xB8,0xFF,0x00,0x33,0xC9,0xE8, +0x6C,0xFF,0xC3,0x90,0xB8,0xFF,0x01,0xB1,0x10,0xE8,0x62,0xFF,0xC3,0x90,0xC3,0xDE, +0x3B,0xC4,0x3B,0xD4,0x3B,0xD4,0x3B,0xDE,0x3B,0xC4,0x3B,0xCA,0x3B,0xCA,0x3B,0xDE, +0x3B,0xC4,0x3B,0xCA,0x3B,0xCA,0x3B,0xDE,0x3B,0xC4,0x3B,0xC4,0x3B,0xC4,0x3B,0x00, +0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, +0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, +0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x51, +0x53,0x8B,0x4E,0x38,0x81,0xE1,0xFF,0xEE,0xA8,0x04,0x74,0x04,0x81,0xC9,0x00,0x01, +0x8A,0xE0,0x80,0xE4,0x03,0x24,0x18,0xD0,0xE4,0x0A,0xC4,0x33,0xDB,0x8A,0xD8,0x2E, +0x8B,0x87,0xDF,0x3B,0x89,0x46,0x7C,0x2E,0x0B,0x8F,0xFF,0x3B,0x89,0x4E,0x38,0xD1, +0xEB,0x2E,0x8A,0xA7,0x1F,0x3C,0x5B,0x59,0xC3,0xAC,0x49,0x3C,0x01,0x72,0x1D,0x74, +0x20,0x3C,0x03,0x72,0x23,0x74,0x28,0x3C,0x08,0x72,0x2B,0x74,0x30,0x3C,0x20,0x72, +0x37,0x74,0x3A,0xBB,0xBC,0x3B,0x32,0xE4,0x89,0x5E,0x7E,0xC3,0xBB,0x82,0x3B,0xEB, +0xF5,0xBB,0x76,0x3B,0xB4,0x01,0xEB,0xF0,0xBB,0xDE,0x3B,0xB4,0x02,0xEB,0xE9,0xBB, +0xC4,0x3B,0xB4,0x03,0xEB,0xE2,0xBB,0xA0,0x3B,0xB4,0x04,0xEB,0xDB,0xBB,0xAC,0x3B, +0xAC,0x49,0x88,0x86,0xBB,0x00,0xEB,0xCE,0xBB,0xB4,0x3B,0xEB,0xF3,0xBB,0xDE,0x3B, +0xEB,0xC4,0xA9,0x04,0x00,0x75,0xD1,0xA9,0x08,0x00,0x75,0xDA,0xEB,0xD1,0x8B,0x5E, +0x74,0x8B,0x4E,0x78,0x3B,0xCB,0x72,0x02,0x8B,0xCB,0x3B,0xC8,0x72,0x02,0x8B,0xC8, +0x8B,0xC1,0xE3,0x2C,0xC4,0x7E,0x10,0x8B,0xDF,0x26,0x03,0x3D,0x47,0x47,0xF7,0x46, +0x7A,0x01,0x00,0x75,0x1C,0xF7,0xC7,0x01,0x00,0x74,0x02,0x49,0xA4,0xD1,0xE9,0xF3, +0xA5,0x73,0x01,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74, +0xC3,0x50,0x53,0xBB,0x7F,0x7F,0xF7,0xC7,0x01,0x00,0x74,0x05,0x49,0xAC,0x22,0xC3, +0xAA,0xD1,0xE9,0xE3,0x1D,0x9C,0xAD,0x23,0xC3,0xAB,0x49,0x74,0x14,0xAD,0x23,0xC3, +0xAB,0x49,0x74,0x0D,0xAD,0x23,0xC3,0xAB,0x49,0x74,0x06,0xAD,0x23,0xC3,0xAB,0xE2, +0xE5,0x9D,0x73,0x04,0xAC,0x22,0xC3,0xAB,0x5B,0x58,0xEB,0xB8,0xE8,0xE8,0xC9,0x8B, +0x5E,0x38,0xF7,0xC3,0x10,0x04,0x75,0x01,0xC3,0xF7,0xC3,0x40,0x00,0x74,0x05,0xE8, +0xD2,0xE3,0xEB,0x03,0xE8,0xC2,0xE3,0x81,0x66,0x38,0xEF,0xFB,0xF6,0xC3,0x10,0x74, +0x3C,0xF6,0xC3,0x02,0x74,0x06,0xE4,0xD8,0x0C,0x01,0xE6,0xD8,0xF6,0xC3,0x04,0x74, +0x11,0x83,0xC2,0x08,0x8A,0x86,0xA7,0x00,0x0C,0x01,0xEE,0x88,0x86,0xA7,0x00,0x83, +0xEA,0x08,0xF6,0xC3,0x08,0x74,0x0F,0xE8,0xA5,0xE3,0x72,0x0A,0x8A,0x86,0xC0,0x00, +0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xF7,0xC3,0x00,0x04,0x75,0x01,0xC3,0xF7,0xC3,0x00, +0x08,0x75,0xF9,0x8A,0x86,0xA5,0x00,0xF6,0xC3,0x40,0x75,0x0D,0xA8,0x10,0x75,0xEC, +0x0C,0x10,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xA8,0x01,0x75,0xDF,0x83,0xC2,0x02, +0x0C,0x05,0xEE,0x88,0x86,0xA5,0x00,0xC3,0xB0,0x00,0xE8,0x61,0xD2,0xEB,0x0F,0xB0, +0x02,0xE8,0x90,0x0E,0xEB,0x08,0x83,0x66,0x38,0xDF,0x83,0x4E,0x7A,0x02,0x33,0xC0, +0x8E,0xD8,0xFA,0xA0,0x92,0x12,0x40,0xA2,0x92,0x12,0x3C,0x05,0x72,0x1E,0xC6,0x06, +0x92,0x12,0x00,0xFB,0xB0,0x01,0xE8,0x6B,0x0E,0xFA,0xA1,0x26,0x01,0x23,0x06,0x2A, +0x01,0xA8,0x01,0x75,0x07,0xE8,0xE2,0x07,0xE8,0x61,0x09,0x90,0xB0,0x00,0xE8,0x51, +0xD2,0xFB,0x85,0xED,0x74,0xB9,0xFA,0xF7,0x46,0x7A,0x46,0x00,0x75,0xC0,0x8B,0x46, +0x78,0x3D,0x0A,0x00,0x72,0xB0,0x8B,0x4E,0x74,0x83,0xF9,0x50,0x72,0x9A,0x83,0x66, +0x38,0xDF,0xC5,0x76,0x14,0x8B,0x46,0x3A,0x85,0xC0,0x75,0x58,0xAD,0x85,0xC0,0x75, +0x0F,0xE8,0xF8,0xFE,0xF7,0x46,0x7A,0x08,0x00,0x74,0x93,0xE8,0xA0,0xFA,0xEB,0x8E, +0x3B,0x76,0x04,0x76,0x21,0xB9,0x02,0x00,0x39,0x4E,0x2E,0x77,0x05,0xC7,0x46,0x2E, +0x00,0x00,0x56,0x8B,0x76,0x2C,0x89,0x76,0x04,0xC7,0x04,0x00,0x00,0x46,0x46,0x89, +0x76,0x2C,0x29,0x4E,0x2E,0x5E,0x85,0xC0,0x79,0x17,0xF6,0xC4,0x10,0x74,0x05,0xFF, +0x56,0x7C,0xEB,0x03,0xFF,0x56,0x7E,0x89,0x76,0x14,0xB0,0x0C,0xE8,0x9F,0xD1,0xEB, +0x86,0x89,0x46,0x3A,0xFF,0x96,0x80,0x00,0x29,0x46,0x3A,0x89,0x76,0x14,0xB0,0x0C, +0xE8,0x8B,0xD1,0xE9,0x71,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x04, +0x10,0x02,0x01,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +0xC0,0x80,0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x30,0x41,0x5A,0x41,0xB2,0x41,0xD6,0x41,0xE8,0x41, +0xFA,0x41,0xC3,0x90,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x89,0x7E,0x6C,0x80,0x66,0x27, +0xFD,0x8B,0x56,0x24,0x83,0xFA,0x04,0x72,0xE9,0x83,0xEA,0x02,0x8B,0xD9,0x3B,0xCA, +0x76,0x02,0x8B,0xCA,0xB0,0x0A,0x57,0x51,0x8B,0xFE,0xF2,0xAE,0x8B,0xC1,0x59,0x5F, +0x75,0x1E,0x50,0x40,0x2B,0xC8,0x74,0x06,0x2B,0xD1,0x2B,0xD9,0xF3,0xA4,0x59,0x4B, +0x4A,0x4A,0xB0,0x0D,0xAA,0xA4,0x3B,0xCA,0x76,0x02,0x8B,0xCA,0xE3,0x13,0xEB,0xD4, +0x2B,0xD9,0xF7,0xC6,0x01,0x00,0x74,0x02,0xA4,0x49,0xD1,0xE9,0xF3,0xA5,0x73,0x01, +0xA4,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x29,0x7E,0x24,0x01,0x7E,0x1A,0x8B,0xCB,0x80, +0x7E,0x26,0x02,0x74,0x05,0x80,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD,0xE8,0x18,0x03, +0x61,0xC3,0xC3,0x90,0xE8,0x7C,0x02,0x72,0xF9,0x90,0x83,0x4E,0x26,0x20,0x8B,0x46, +0x6A,0x89,0x46,0x6E,0x8B,0x46,0x48,0x0D,0x04,0x00,0x25,0xBF,0xFF,0x89,0x46,0x48, +0xB0,0x06,0xE8,0xD9,0xCF,0xC3,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x01,0x7E,0x1A,0x29, +0x7E,0x24,0x80,0x7E,0x26,0x02,0x74,0x05,0x83,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD, +0xE8,0xD5,0x02,0x61,0xC3,0x90,0x8A,0xBE,0xC2,0x00,0xEB,0x24,0xF7,0x46,0x48,0x40, +0x00,0x75,0xB1,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x89,0x7E,0x6C,0x8B,0x56,0x24,0x83, +0xEA,0x0A,0x78,0x9E,0x03,0xD7,0x80,0x66,0x27,0xFD,0x33,0xC0,0x8A,0xBE,0xC2,0x00, +0xE3,0xB4,0x3B,0xFA,0x77,0xB0,0xAC,0x49,0x93,0x2E,0x8A,0x87,0xB6,0x3E,0x93,0x22, +0xDF,0x75,0x17,0xAA,0xE3,0xA0,0x3B,0xFA,0x77,0x9C,0xAC,0x49,0x93,0x2E,0x8A,0x87, +0xB6,0x3E,0x93,0x22,0xDF,0x75,0x03,0xAA,0xEB,0xD6,0xF6,0xC3,0x7F,0x75,0x05,0xFF, +0x46,0x66,0xEB,0xDF,0xF6,0xC3,0x40,0x75,0x0C,0x8B,0xD8,0x83,0xEB,0x08,0xD1,0xE3, +0x2E,0xFF,0xA7,0xB6,0x3F,0xFF,0x46,0x66,0x2C,0x20,0xEB,0xC7,0x85,0xC0,0x74,0x2C, +0x89,0x46,0x6A,0x83,0x4E,0x48,0x40,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x01,0x7E,0x1A, +0x29,0x7E,0x24,0x80,0x7E,0x26,0x02,0x74,0x08,0x83,0x66,0x26,0xFD,0xE8,0xA3,0x01, +0xC3,0x60,0xB0,0xFD,0xE8,0x31,0x02,0x61,0xE8,0x98,0x01,0xC3,0xE9,0x57,0xFF,0x90, +0x8B,0x5E,0x66,0x4B,0x78,0x03,0x89,0x5E,0x66,0xAA,0x8B,0x5E,0x64,0xF7,0xC3,0x00, +0x20,0x75,0x03,0xE9,0x40,0xFF,0xF7,0xC3,0x40,0x00,0x74,0x08,0x8A,0x86,0xC1,0x00, +0xAA,0xE9,0x32,0xFF,0xB8,0x32,0x00,0xEB,0xA3,0x90,0x8B,0x5E,0x66,0x89,0x5E,0x68, +0x83,0xC3,0x08,0x80,0xE3,0xF8,0x89,0x5E,0x66,0x8B,0x5E,0x64,0x81,0xE3,0x00,0x18, +0x81,0xFB,0x00,0x18,0x74,0x2D,0xAA,0x85,0xDB,0x74,0x25,0xF7,0x46,0x64,0x40,0x00, +0x75,0x18,0x81,0xFB,0x00,0x10,0x74,0x0C,0x8B,0x46,0x66,0x2B,0x46,0x68,0xC1,0xE0, +0x04,0xE9,0x68,0xFF,0xB8,0x64,0x00,0xE9,0x62,0xFF,0x8A,0x86,0xC1,0x00,0xAA,0xAA, +0xE9,0xE3,0xFE,0x51,0x8B,0x4E,0x66,0x2B,0x4E,0x68,0xB0,0x20,0xF3,0xAA,0x59,0xE9, +0xD4,0xFE,0x8B,0x5E,0x66,0x89,0x5E,0x68,0x8B,0x5E,0x64,0xF7,0xC3,0x24,0x00,0x74, +0x10,0xC7,0x46,0x66,0x00,0x00,0xF7,0xC3,0x04,0x00,0x74,0x05,0xB0,0x0D,0xAA,0xB0, +0x0A,0xAA,0xEB,0x48,0x90,0x90,0xAA,0xF7,0x46,0x64,0x00,0x40,0x74,0x06,0xB8,0xD0, +0x07,0xE9,0x18,0xFF,0xE9,0x9F,0xFE,0x90,0xAA,0xF7,0x46,0x64,0x00,0x80,0x74,0x06, +0xB8,0xD0,0x07,0xE9,0x06,0xFF,0xE9,0x8D,0xFE,0x90,0x8B,0x5E,0x66,0x89,0x5E,0x68, +0x85,0xDB,0x75,0x0C,0x8B,0x5E,0x64,0xF7,0xC3,0x10,0x00,0x74,0x06,0xE9,0x76,0xFE, +0x8B,0x5E,0x64,0xF7,0xC3,0x08,0x00,0x74,0x27,0xB0,0x0A,0xAA,0xF7,0xC3,0x20,0x00, +0x75,0x1F,0xF7,0xC3,0x00,0x01,0x75,0x03,0xE9,0x5B,0xFE,0xF7,0xC3,0x40,0x00,0x75, +0x06,0xB8,0x64,0x00,0xE9,0xC5,0xFE,0x8A,0x86,0xC1,0x00,0xAA,0xAA,0xE9,0x46,0xFE, +0xAA,0xC7,0x46,0x66,0x00,0x00,0xF7,0xC3,0x00,0x06,0x74,0xF1,0xF7,0xC3,0x40,0x00, +0x74,0x19,0x8A,0x86,0xC1,0x00,0x81,0xE3,0x00,0x06,0x81,0xFB,0x00,0x04,0x72,0x06, +0x76,0x02,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xE9,0x1B,0xFE,0x81,0xE3,0x00,0x06,0x81, +0xFB,0x00,0x04,0x72,0x0E,0x76,0x06,0xB8,0x96,0x00,0xE9,0x7F,0xFE,0xB8,0x64,0x00, +0xE9,0x79,0xFE,0x8B,0x46,0x68,0xE9,0x73,0xFE,0x90,0x36,0x8B,0x0E,0xDA,0x12,0x83, +0xF9,0x32,0x73,0x1D,0x1E,0x06,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8D,0x76,0x4C,0xBF, +0xDC,0x12,0x03,0xF9,0xA5,0xA5,0xA5,0x83,0xC1,0x06,0x89,0x0E,0xDA,0x12,0x07,0x1F, +0xC3,0xB0,0x08,0xE8,0x88,0xCD,0xC3,0x90,0x83,0x66,0x48,0xFE,0xE8,0xAD,0xC4,0xE8, +0xC8,0xFF,0xC3,0xF6,0x46,0x27,0x02,0x75,0x0F,0x9C,0xFA,0x83,0x7E,0x1A,0x00,0x74, +0x09,0x80,0x4E,0x27,0x01,0x9D,0xF9,0xC3,0xF8,0xC3,0x50,0x52,0xF7,0x46,0x38,0x40, +0x00,0x74,0x1D,0xE8,0x4E,0xDE,0x83,0xC2,0x0A,0xEC,0xA8,0x40,0x75,0x27,0x83,0xEA, +0x08,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xEE,0x5A,0x58,0xEB,0xD1, +0xE8,0x26,0xDE,0x8A,0x86,0xA5,0x00,0x24,0xFB,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6, +0x0C,0x5A,0x58,0xEB,0xBC,0x80,0x4E,0x27,0x02,0x5A,0x58,0x9D,0xF8,0xC3,0x08,0x46, +0x26,0x9C,0xFA,0x8A,0x8E,0xA5,0x00,0xF7,0x46,0x38,0x40,0x00,0x75,0x14,0xF6,0xC1, +0x06,0x74,0x23,0xE8,0xF3,0xDD,0x8A,0xC1,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x9D,0xC3,0xF6,0xC1,0x02,0x74,0x0F,0xE8,0xEA,0xDD,0x83,0xC2,0x02,0x8A,0xC1,0x24, +0xFD,0x88,0x86,0xA5,0x00,0xEE,0x9D,0xC3,0x8B,0x5E,0x26,0x22,0xC3,0x88,0x46,0x26, +0x74,0x01,0xC3,0x80,0x66,0x27,0xFD,0x9C,0xFA,0x8A,0x8E,0xA5,0x00,0xF7,0x46,0x38, +0x40,0x00,0x75,0x16,0xF6,0xC1,0x04,0x75,0x0F,0xE8,0xAD,0xDD,0x8A,0xC1,0x24,0xFD, +0x0C,0x04,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x9D,0xC3,0xF6,0xC1,0x02,0x75,0xF9,0xE8, +0xA2,0xDD,0x83,0xC2,0x0A,0xEC,0xA8,0x20,0x75,0x0E,0x83,0xEA,0x08,0x8A,0xC1,0x0C, +0x02,0x88,0x86,0xA5,0x00,0xEE,0x9D,0xC3,0x83,0xEA,0x0A,0x33,0xC9,0x8A,0x4E,0x1C, +0x8B,0x46,0x1A,0x3B,0xC8,0x73,0x1B,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A,0x1E, +0xC5,0x76,0x00,0xF3,0x6E,0x1F,0x89,0x76,0x00,0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00, +0xEB,0xCD,0x85,0xC0,0x74,0x12,0x01,0x46,0x2A,0x8B,0xC8,0x1E,0xC5,0x76,0x00,0xF3, +0x6E,0x1F,0x89,0x76,0x00,0x89,0x4E,0x1A,0xF6,0xC7,0x01,0x75,0x23,0x80,0xCB,0x02, +0x89,0x5E,0x26,0xE8,0x22,0xC3,0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00,0x24,0xFD,0xEE, +0x88,0x86,0xA5,0x00,0xF6,0xC7,0x10,0x75,0x05,0xB0,0x02,0xE8,0x30,0xCC,0x9D,0xC3, +0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00,0xEB,0x86,0x90,0x8B,0xD1,0x8B,0x46,0x24,0x3B, +0xC8,0x76,0x02,0x8B,0xC8,0x2B,0xD1,0x2B,0xC1,0x8B,0xD9,0xE3,0x22,0x80,0x66,0x27, +0xFD,0x8E,0x46,0x02,0x8B,0x7E,0x22,0xF7,0xC6,0x01,0x00,0x74,0x02,0xA4,0x49,0xD1, +0xE9,0xF3,0xA5,0x73,0x01,0xA4,0x89,0x7E,0x22,0x89,0x46,0x24,0x01,0x5E,0x1A,0x8B, +0xCA,0x80,0x7E,0x26,0x02,0x74,0x05,0x80,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD,0xE8, +0xF6,0xFE,0x61,0xC3,0x50,0xE4,0x0A,0x84,0xC0,0x75,0x0A,0x86,0x86,0xA1,0x00,0x84, +0xC0,0x74,0x0A,0xE6,0x0A,0x58,0x0C,0x20,0x89,0x46,0x48,0xF9,0xC3,0x58,0x24,0xDF, +0x89,0x46,0x48,0xF8,0xC3,0x90,0xFB,0xB0,0x02,0xE8,0xE8,0x07,0xFA,0xE8,0x2E,0x01, +0xFB,0xB0,0x01,0xE8,0xDE,0x07,0xFA,0xB0,0x02,0xE8,0xD6,0xCB,0xFB,0x85,0xED,0x74, +0xE5,0xFA,0x8E,0x5E,0x0A,0xFB,0x90,0xFA,0x8B,0x46,0x48,0x8B,0x76,0x40,0xA8,0x8C, +0x75,0xDE,0xA8,0x20,0x74,0x1A,0x50,0xE8,0x6F,0xDC,0x58,0xE8,0xA6,0xFF,0x73,0x10, +0xB0,0x02,0xE8,0x79,0xCB,0xEB,0xC9,0x90,0x25,0xFF,0x00,0x8B,0xC8,0xEB,0x36,0x90, +0xA8,0x01,0x75,0x22,0x46,0x83,0xE6,0xFE,0x3B,0x76,0x08,0x74,0x79,0xAD,0x8A,0xFC, +0xB3,0xF0,0x22,0xFB,0x3A,0xFB,0x74,0xE0,0x3A,0xBE,0xA0,0x00,0x74,0x2E,0xE8,0xD2, +0xFD,0x73,0x77,0xEB,0x9B,0x90,0x8A,0xE0,0x24,0xFC,0x88,0x46,0x48,0x8B,0x4E,0x4A, +0xF6,0xC4,0x02,0x74,0x1D,0xE8,0xBB,0xFD,0x72,0x86,0xE8,0x13,0xF3,0x89,0x76,0x40, +0xE3,0x93,0x83,0x4E,0x48,0x03,0x89,0x4E,0x4A,0xE9,0x74,0xFF,0x25,0xFF,0x0F,0x8B, +0xC8,0x90,0x8B,0x86,0x98,0x00,0x85,0xC0,0x74,0x1A,0x51,0x8A,0x8E,0xA0,0x00,0xC0, +0xE9,0x04,0xBA,0x01,0x00,0xD3,0xE2,0x59,0x23,0xC2,0x74,0x08,0x03,0xF1,0x89,0x76, +0x40,0xE9,0x61,0xFF,0xFF,0x56,0x62,0xE3,0xF5,0x83,0x4E,0x48,0x01,0x89,0x4E,0x4A, +0x89,0x76,0x40,0xE9,0x3A,0xFF,0x81,0x4E,0x26,0x00,0x10,0x8B,0x46,0x50,0x3B,0x46, +0x46,0x77,0x03,0xE8,0x52,0xFD,0xE9,0x27,0xFF,0x90,0x88,0xBE,0xA0,0x00,0xEB,0xAC, +0x0A,0x06,0x90,0x12,0x8A,0xE0,0xBA,0x06,0x01,0xB0,0x04,0xEE,0xEC,0x84,0xC0,0x75, +0x12,0xB0,0x04,0xEE,0x8A,0xC4,0xEE,0x32,0xE4,0xA8,0x80,0x74,0x06,0xC7,0x06,0x84, +0x12,0x00,0x00,0x88,0x26,0x90,0x12,0xC3,0x0A,0x06,0x90,0x12,0x8A,0xE0,0xBA,0x06, +0x01,0xEC,0xA8,0x01,0x75,0xED,0xBA,0x08,0x01,0x8A,0xC4,0xEE,0x32,0xE4,0xA8,0x80, +0x74,0xE1,0xC7,0x06,0x84,0x12,0x00,0x00,0x88,0x26,0x90,0x12,0xC3,0x90,0x36,0xF7, +0x06,0x24,0x01,0x01,0x00,0x75,0x30,0x36,0x8B,0x0E,0xDA,0x12,0x80,0xF9,0x36,0x73, +0x26,0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0xBF,0xDC,0x12,0x03,0xF9,0xB0,0x08,0xE8,0x91, +0xCA,0x85,0xED,0x74,0x0E,0x8D,0x76,0x4C,0xA5,0xA5,0xA5,0x80,0xC1,0x06,0x80,0xF9, +0x36,0x72,0xE9,0x89,0x0E,0xDA,0x12,0xC3,0xC3,0x90,0xF7,0x06,0x26,0x01,0x01,0x00, +0x75,0xF6,0x8B,0x0E,0x20,0x13,0x85,0xC9,0x75,0xEE,0x33,0xC0,0x8E,0xC0,0x8E,0xD8, +0xBF,0x24,0x13,0xB9,0x36,0x00,0xB0,0x0A,0xE8,0x57,0xCA,0x85,0xED,0x75,0x06,0xE9, +0x12,0x01,0xE9,0x0A,0x01,0x33,0xDB,0x8A,0x46,0x4C,0x8A,0xA6,0xB3,0x00,0xFE,0xCC, +0x78,0x0E,0x88,0xA6,0xB3,0x00,0x0A,0xDC,0xB4,0x0A,0xAB,0x83,0xE9,0x02,0x76,0xE2, +0x8A,0xA6,0xB2,0x00,0xFE,0xCC,0x78,0x0E,0x88,0xA6,0xB2,0x00,0x0A,0xDC,0xB4,0x08, +0xAB,0x83,0xE9,0x02,0x76,0xCC,0x8A,0xA6,0xB1,0x00,0xFE,0xCC,0x78,0x18,0x8A,0xBE, +0xB0,0x00,0x75,0x04,0x88,0xA6,0xB0,0x00,0x88,0xA6,0xB1,0x00,0x0A,0xDC,0x8A,0xE7, +0xAB,0x83,0xE9,0x02,0x76,0xAC,0x8A,0xA6,0xB4,0x00,0xFE,0xCC,0x78,0x1F,0x88,0xA6, +0xB4,0x00,0x0A,0xDC,0xB4,0x0B,0xAB,0x8A,0x86,0xBC,0x00,0x8A,0xA6,0xBD,0x00,0xAB, +0x8B,0x86,0xBE,0x00,0xAB,0x83,0xE9,0x06,0x76,0x88,0x8A,0x46,0x4C,0x8A,0xA6,0xB6, +0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB6,0x00,0x0A,0xDC,0xB4,0x0C,0xAB,0xE8,0xF5, +0xCB,0xAB,0x8B,0x46,0x2A,0xAB,0x83,0xE9,0x06,0x76,0x74,0x8A,0x46,0x4C,0x8A,0xA6, +0xB7,0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB7,0x00,0x0A,0xDC,0xB4,0x0D,0xAB,0xE8, +0xD4,0xCB,0xAB,0x8B,0x46,0x34,0xAB,0x83,0xE9,0x06,0x76,0x53,0x8A,0x46,0x4C,0x8A, +0xA6,0xB8,0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB8,0x00,0x0A,0xDC,0xB4,0x0E,0xAB, +0xA1,0x50,0x12,0xAB,0xA1,0x52,0x12,0xAB,0x83,0xE9,0x06,0x76,0x32,0x8A,0x46,0x4C, +0x8A,0xA6,0xB5,0x00,0xFE,0xCC,0x78,0x18,0x88,0xA6,0xB5,0x00,0x0A,0xDC,0xB4,0x0F, +0xAB,0x8B,0x86,0x9A,0x00,0xAB,0x8B,0x86,0x9C,0x00,0xAB,0x83,0xE9,0x06,0x76,0x0F, +0x84,0xDB,0x75,0x03,0xE9,0xEF,0xFE,0xB0,0x0A,0xE8,0x12,0xC9,0xE9,0xE7,0xFE,0xB0, +0x0A,0xE8,0x0A,0xC9,0xF7,0xD9,0x83,0xC1,0x36,0x8B,0xC1,0x0D,0x80,0x00,0x86,0xC4, +0xA3,0x22,0x13,0x41,0x41,0x89,0x0E,0x20,0x13,0xC3,0xA1,0x84,0x12,0x2B,0xC1,0x72, +0x11,0xA3,0x84,0x12,0xBE,0x22,0x13,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0x20,0x13, +0xF8,0xC3,0xF9,0xC3,0xC3,0x81,0xEF,0x6A,0x13,0x74,0xF9,0x8B,0xC7,0x0D,0x80,0x00, +0x86,0xC4,0xA3,0x68,0x13,0x47,0x47,0x89,0x3E,0x66,0x13,0xC3,0xF7,0x06,0x2A,0x01, +0x01,0x00,0x75,0xE0,0x8B,0x0E,0x66,0x13,0xE3,0x07,0x80,0xF9,0x20,0x77,0xD5,0x49, +0x49,0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0xBF,0x6A,0x13,0x8B,0xF7,0x03,0xF9,0x83,0xC6, +0x34,0x3B,0xFE,0x77,0xC0,0xB0,0x0E,0xE8,0xC8,0xC8,0x85,0xED,0x74,0xB7,0x8A,0x46, +0x4C,0x8A,0xB6,0xB9,0x00,0xFE,0xCE,0x78,0x15,0x88,0xB6,0xB9,0x00,0x8A,0xA6,0xA9, +0x00,0x80,0xCC,0xC0,0xAB,0x84,0xF6,0x74,0x05,0xB0,0x0E,0xE8,0x70,0xC8,0x8A,0xB6, +0xBA,0x00,0xFE,0xCE,0x78,0xCB,0x8A,0x9E,0xA9,0x00,0x8A,0xBE,0xAB,0x00,0x8A,0x56, +0x3F,0x8A,0xF3,0x32,0xF7,0x0A,0xB6,0xAC,0x00,0xC6,0x86,0xAC,0x00,0x00,0x22,0xF2, +0x74,0x4B,0xF6,0xC6,0x08,0x74,0x0F,0xB4,0x02,0xF6,0xC3,0x08,0x75,0x02,0xB4,0x03, +0xAB,0x80,0xE6,0xF7,0x74,0x37,0xF6,0xC6,0x01,0x74,0x0F,0xB4,0x00,0xF6,0xC3,0x01, +0x75,0x02,0xB4,0x01,0xAB,0x80,0xE6,0xFE,0x74,0x23,0xF6,0xC6,0x02,0x74,0x0F,0xB4, +0x04,0xF6,0xC3,0x02,0x75,0x02,0xB4,0x05,0xAB,0x80,0xE6,0xFD,0x74,0x0F,0xF6,0xC6, +0x04,0x74,0x0A,0xB4,0x06,0xF6,0xC3,0x04,0x75,0x02,0xB4,0x07,0xAB,0xC6,0x86,0xBA, +0x00,0x00,0x88,0x9E,0xAB,0x00,0xE9,0x58,0xFF,0x90,0xA1,0x84,0x12,0x2B,0xC1,0x72, +0x11,0xA3,0x84,0x12,0xBE,0x68,0x13,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0x66,0x13, +0xF8,0xC3,0xF9,0xC3,0xA1,0x84,0x12,0x41,0x41,0x2B,0xC1,0x72,0x23,0xA3,0x84,0x12, +0x8B,0xC1,0x48,0x48,0x32,0xE4,0x0C,0x80,0x86,0xC4,0xEF,0x90,0x90,0x90,0x90,0x90, +0xBE,0xDC,0x12,0x49,0x49,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0xDA,0x12,0xF8,0xC3, +0xF9,0xC3,0x8A,0xC8,0x8A,0x46,0x4C,0xB4,0x01,0x83,0xEB,0x06,0xEF,0x90,0x90,0x90, +0x90,0x90,0xB8,0x01,0x00,0xEF,0x90,0x90,0x90,0x90,0x90,0x8A,0xC1,0xEF,0x90,0x90, +0x90,0x90,0x90,0xE9,0x97,0x00,0xE9,0xAC,0x00,0x33,0xC0,0x8E,0xD8,0x89,0x1E,0x84, +0x12,0xC3,0x36,0x8B,0x1E,0x84,0x12,0xFB,0x90,0xFA,0xB0,0x0C,0xE8,0xA3,0xC7,0x85, +0xED,0x74,0xE6,0xC5,0x76,0x0C,0x83,0xFB,0x14,0x72,0xDB,0xFB,0x90,0xFA,0xAD,0x85, +0xC0,0x78,0xAF,0x74,0xE2,0x8B,0xFE,0x03,0xF8,0x36,0x8B,0x0E,0x86,0x12,0x3B,0xC1, +0x77,0x02,0x8B,0xC8,0x83,0xEB,0x04,0x3B,0xD9,0x77,0x02,0x8B,0xCB,0x33,0xC0,0x8A, +0x46,0x4C,0xEF,0x90,0x90,0x90,0x90,0x90,0x8B,0xC1,0xEF,0x90,0x90,0x90,0x90,0x90, +0x41,0x80,0xE1,0xFE,0x2B,0xD9,0x51,0xD1,0xE9,0xF3,0x6F,0x90,0x59,0x8B,0xC7,0x40, +0x24,0xFE,0x3B,0xC6,0x74,0x27,0x2B,0xFE,0x4E,0x4E,0x53,0x8B,0x5E,0x10,0x3B,0xF3, +0x72,0x13,0x03,0x1F,0x83,0xC3,0x03,0x80,0xE3,0xFE,0xC7,0x07,0x00,0x00,0x83,0x6E, +0x74,0x02,0x89,0x5E,0x10,0x5B,0x89,0x3C,0x89,0x76,0x0C,0xEB,0x89,0x89,0x76,0x0C, +0x39,0x76,0x10,0x77,0x81,0x72,0x08,0x83,0x3C,0x00,0x74,0x03,0xE9,0x77,0xFF,0xE8, +0x27,0xBE,0xE9,0x62,0xFF,0x36,0x89,0x1E,0x84,0x12,0xB0,0x0C,0xE8,0xCF,0xC6,0x33, +0xC0,0x8E,0xD8,0xC3,0xA1,0x84,0x12,0x3D,0x10,0x00,0x72,0x77,0xBA,0x04,0x01,0x3B, +0x06,0x88,0x12,0x75,0x06,0xC7,0x06,0x7E,0x12,0x00,0x00,0x8B,0x0E,0xDA,0x12,0xE3, +0x0B,0xE8,0xD0,0xFE,0x72,0x57,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0x8B,0x0E,0x20,0x13, +0xE3,0x0B,0xE8,0xA5,0xFD,0x72,0x46,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0x8B,0x0E,0x66, +0x13,0xE3,0x0B,0xE8,0x94,0xFE,0x72,0x35,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0xA1,0x28, +0x01,0xA9,0x01,0x00,0x75,0x03,0xE8,0xF9,0xFE,0x80,0x3E,0x8D,0x12,0x00,0x75,0x1D, +0xA1,0x84,0x12,0x3D,0x20,0x00,0x76,0x15,0x3B,0x06,0x82,0x12,0x76,0x09,0xA1,0x7E, +0x12,0x3B,0x06,0x80,0x12,0x72,0x0C,0x80,0x0E,0x90,0x12,0x80,0xC3,0xB0,0x80,0xFF, +0x16,0x7C,0x12,0xC3,0x80,0x0E,0x90,0x12,0x40,0xC3,0x6A,0x00,0x1F,0xC6,0x06,0x93, +0x12,0x17,0x9C,0x0E,0xE8,0xD1,0xC8,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x20,0x9C, +0x0E,0xE8,0xC4,0xC8,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x16,0x9C,0x0E,0xE8,0xB7, +0xC8,0x90,0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xCA,0xFB,0x90,0xFA,0xBA,0x04,0x01, +0xED,0x90,0x90,0x90,0x90,0x90,0x3A,0x06,0x94,0x12,0x77,0xBE,0x33,0xDB,0x8A,0xD8, +0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0xC4,0x7E,0x08,0x85,0xFF,0x74,0xB9,0xF6,0xC4, +0xC0,0x75,0x55,0x32,0xC0,0xC1,0xE0,0x02,0x80,0xE4,0xF0,0x8B,0xF0,0xED,0x90,0x90, +0x90,0x90,0x90,0x85,0xC0,0x74,0xBB,0x8B,0xC8,0x41,0x80,0xE1,0xFE,0x0B,0xC6,0x8B, +0x5E,0x50,0x4B,0x4B,0x2B,0xD9,0x78,0x9C,0xAB,0x8B,0xC1,0x40,0x40,0x01,0x46,0x4E, +0xD1,0xE9,0xF3,0x6D,0x90,0x89,0x5E,0x50,0x89,0x7E,0x08,0x8B,0x46,0x26,0x80,0xE4, +0xEF,0x89,0x46,0x26,0xF6,0xC4,0x01,0x75,0x0C,0xF7,0x46,0x48,0x0C,0x00,0x75,0x05, +0xB0,0x02,0xE8,0x99,0xC5,0xE9,0x7A,0xFF,0x86,0xC4,0x8B,0xC8,0x83,0xE1,0x3F,0x41, +0x80,0xE1,0xFE,0xE3,0x0A,0x3C,0x80,0x72,0x09,0x24,0x3F,0xB4,0xF0,0xEB,0xB0,0xE9, +0x60,0xFF,0x25,0x3F,0x00,0x33,0xFF,0x8E,0xC7,0xBF,0x96,0x12,0x8B,0xF7,0xD1,0xE9, +0xF3,0x6D,0x90,0x8B,0xC8,0xE8,0x48,0xED,0xE9,0x47,0xFF,0x90,0x6A,0x00,0x1F,0xC6, +0x06,0x93,0x12,0x1B,0x9C,0x0E,0xE8,0xEF,0xC7,0x90,0x60,0x1E,0x06,0x33,0xC0,0x8E, +0xD8,0x8E,0xC0,0xBA,0x06,0x01,0xEC,0xA8,0x04,0x74,0xE1,0xB0,0x06,0xEE,0xEC,0xA2, +0x8C,0x12,0xA8,0x40,0x74,0x11,0xA1,0x88,0x12,0xA3,0x84,0x12,0xC6,0x06,0x8D,0x12, +0x00,0xE8,0x60,0xFE,0xA0,0x8C,0x12,0xA8,0x80,0x74,0x03,0xE8,0x04,0xFF,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0x6A,0x00,0x1F,0xC6,0x06,0x93, +0x12,0x1B,0x9C,0x0E,0xE8,0xA1,0xC7,0x90,0x60,0x1E,0x06,0x33,0xC0,0x8E,0xD8,0x8E, +0xC0,0xBA,0x06,0x01,0xEC,0xA8,0x04,0x74,0xE1,0xBA,0x08,0x01,0xEC,0xA2,0x8C,0x12, +0xA8,0x40,0x74,0x11,0xA1,0x88,0x12,0xA3,0x84,0x12,0xC6,0x06,0x8D,0x12,0x00,0xE8, +0x12,0xFE,0xA0,0x8C,0x12,0xA8,0x80,0x74,0x03,0xE8,0xB6,0xFE,0xB8,0x00,0x80,0xBA, +0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xEE,0x86,0xE0,0xEE,0x86,0xE0,0xEC,0x86, +0xE0,0xEC,0x86,0xE0,0x80,0xE1,0xFE,0xF3,0x6C,0x90,0x80,0xE1,0xFE,0xF3,0x6E,0x90, +0x05,0x00,0x57,0x47,0x8A,0x4B,0x05,0x00,0x57,0x48,0x8A,0x4B,0x05,0x00,0x85,0x48, +0x8A,0x4B,0x05,0x00,0x17,0x49,0x8A,0x4B,0x06,0x00,0x7A,0x48,0x78,0x4B,0x06,0x00, +0x9C,0x48,0x78,0x4B,0x06,0x00,0xA5,0x48,0x78,0x4B,0x06,0x00,0xAD,0x48,0x78,0x4B, +0x06,0x00,0x02,0x49,0x78,0x4B,0x06,0x00,0x0A,0x49,0x78,0x4B,0x06,0x00,0x30,0x4A, +0x7E,0x4B,0x06,0x00,0x5D,0x4A,0x7E,0x4B,0x05,0x00,0x80,0x4A,0x84,0x4B,0x05,0x00, +0xCE,0x4A,0x84,0x4B,0x00,0x00,0x1E,0x06,0x83,0x3E,0x44,0x12,0x00,0x74,0x09,0xA0, +0x06,0x01,0x24,0x30,0x3C,0x30,0x74,0x1A,0x8C,0xC8,0x8E,0xD8,0x8E,0xC0,0xBB,0x90, +0x4B,0x8B,0x0F,0xE3,0x0D,0x8B,0x7F,0x02,0x8B,0x77,0x04,0xF3,0xA4,0x83,0xC3,0x06, +0xEB,0xEF,0x07,0x1F,0xC3,0x90,0x33,0xC0,0xA3,0x3E,0x01,0xB9,0x0C,0x01,0xBE,0x40, +0x01,0x8B,0xFE,0x81,0xC6,0xB4,0x0F,0x89,0x04,0x8B,0xC6,0x2B,0xF1,0x3B,0xC7,0x77, +0xF6,0xA3,0x3C,0x01,0xC3,0x90,0x1E,0x06,0x60,0x36,0x8B,0x2E,0x3E,0x01,0x8B,0x5E, +0x00,0x3B,0xEB,0x74,0x2B,0x8B,0x76,0x02,0x89,0x1C,0x89,0x77,0x02,0x36,0xA1,0x3C, +0x01,0x89,0x46,0x00,0x36,0x89,0x2E,0x3C,0x01,0x8B,0xEB,0xFF,0x4E,0x06,0x74,0x08, +0x8B,0x6E,0x00,0xFF,0x4E,0x06,0x75,0xF8,0x36,0x89,0x2E,0x3E,0x01,0x8B,0x66,0x04, +0x61,0x07,0x1F,0xC3,0x1E,0x06,0x60,0x36,0x8B,0x2E,0x3E,0x01,0x98,0x89,0x46,0x06, +0x89,0x66,0x04,0x3B,0x6E,0x00,0x74,0x10,0x8B,0x6E,0x00,0xFF,0x4E,0x06,0x75,0xF8, +0x36,0x89,0x2E,0x3E,0x01,0x8B,0x66,0x04,0x61,0x07,0x1F,0xC3,0xC3,0x90,0x1E,0x06, +0x60,0x9C,0xFA,0x33,0xED,0x8E,0xDD,0x8B,0x2E,0x3C,0x01,0x85,0xED,0x74,0x3D,0x8B, +0x4E,0x00,0x89,0x0E,0x3C,0x01,0x8B,0xCC,0x8D,0xA6,0x0A,0x01,0x56,0x1E,0x06,0x60, +0x89,0x66,0x04,0xC7,0x46,0x08,0x0F,0x1A,0xC7,0x46,0x06,0x01,0x00,0x8B,0x1E,0x3E, +0x01,0x85,0xDB,0x74,0x1D,0x8B,0xC5,0x87,0x07,0x89,0x46,0x00,0x89,0x5E,0x02,0x8B, +0xD8,0x89,0x6F,0x02,0x8B,0xE1,0x9D,0x61,0x07,0x1F,0xF8,0xC3,0x9D,0x61,0x07,0x1F, +0xF9,0xC3,0x89,0x2E,0x3E,0x01,0x89,0x6E,0x00,0x89,0x6E,0x02,0x87,0xE1,0x9D,0x8B, +0xE1,0xEB,0xE4,0x00,0x0D,0x0A,0x54,0x65,0x72,0x6D,0x69,0x6E,0x61,0x6C,0x73,0x20, +0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x65,0x64,0x3A,0x0D,0x0A,0x31,0x29,0x20,0x41, +0x4E,0x53,0x49,0x20,0x63,0x6F,0x6D,0x70,0x61,0x74,0x69,0x62,0x6C,0x65,0x0D,0x0A, +0x32,0x29,0x20,0x57,0x79,0x73,0x65,0x20,0x33,0x30,0x0D,0x0A,0x50,0x6C,0x65,0x61, +0x73,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,0x3A,0x20,0x00,0x0D,0x0A,0x63,0x6F, +0x64,0x65,0x20,0x73,0x65,0x67,0x6D,0x65,0x6E,0x74,0x3D,0x00,0x0D,0x0A,0x4D,0x6F, +0x6E,0x69,0x74,0x6F,0x72,0x20,0x76,0x32,0x2E,0x35,0x0A,0x0D,0x0A,0x3E,0x00,0x0D, +0x0A,0x50,0x61,0x72,0x64,0x6F,0x6E,0x3F,0x00,0x0D,0x0A,0x4E,0x6F,0x20,0x61,0x64, +0x64,0x72,0x65,0x73,0x73,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x00, +0x0D,0x0A,0x3A,0x00,0x0D,0x0A,0x00,0x4C,0x6F,0x63,0x3D,0x00,0x0D,0x0A,0x46,0x41, +0x54,0x41,0x4C,0x20,0x45,0x52,0x52,0x4F,0x52,0x3D,0x00,0x0D,0x0A,0x4D,0x6F,0x6E, +0x69,0x74,0x6F,0x72,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x73,0x3A,0x2D,0x0D, +0x0A,0x20,0x20,0x20,0x44,0x2C,0x64,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78, +0x78,0x78,0x78,0x5D,0x20,0x2D,0x20,0x64,0x75,0x6D,0x70,0x20,0x6D,0x65,0x6D,0x6F, +0x72,0x79,0x0D,0x0A,0x20,0x20,0x20,0x4C,0x2C,0x6C,0x5B,0x5B,0x78,0x78,0x78,0x78, +0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20,0x2D,0x20,0x64,0x75,0x6D,0x70,0x20,0x73, +0x69,0x6E,0x67,0x6C,0x65,0x20,0x6C,0x69,0x6E,0x65,0x0D,0x0A,0x20,0x20,0x20,0x45, +0x2C,0x65,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20, +0x2D,0x20,0x65,0x64,0x69,0x74,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x79,0x0D,0x0A,0x20, +0x20,0x20,0x46,0x2C,0x66,0x5B,0x5B,0x78,0x78,0x78,0x78,0x20,0x5D,0x78,0x78,0x78, +0x78,0x5D,0x20,0x2D,0x20,0x66,0x69,0x6C,0x6C,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x79, +0x20,0x70,0x61,0x72,0x61,0x67,0x72,0x61,0x70,0x68,0x73,0x0D,0x0A,0x20,0x20,0x20, +0x49,0x5B,0x78,0x78,0x78,0x78,0x5D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x2D,0x20,0x77,0x6F,0x72,0x64,0x20,0x69,0x6E,0x70,0x75,0x74,0x20,0x66,0x72, +0x6F,0x6D,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x69,0x5B,0x78,0x78, +0x78,0x78,0x5D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62, +0x79,0x74,0x65,0x20,0x69,0x6E,0x70,0x75,0x74,0x20,0x66,0x72,0x6F,0x6D,0x20,0x70, +0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x4F,0x78,0x78,0x78,0x78,0x20,0x78,0x78, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x6F,0x75,0x74,0x70,0x75, +0x74,0x20,0x77,0x6F,0x72,0x64,0x20,0x74,0x6F,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A, +0x20,0x20,0x20,0x6F,0x78,0x78,0x78,0x78,0x20,0x78,0x78,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x2D,0x20,0x6F,0x75,0x74,0x70,0x75,0x74,0x20,0x62,0x79,0x74, +0x65,0x20,0x74,0x6F,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x47,0x5B, +0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20,0x20,0x20,0x2D, +0x20,0x67,0x6F,0x74,0x6F,0x20,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x0D,0x0A,0x20, +0x20,0x20,0x57,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D, +0x20,0x20,0x20,0x2D,0x20,0x77,0x61,0x74,0x63,0x68,0x20,0x61,0x20,0x77,0x6F,0x72, +0x64,0x0D,0x0A,0x20,0x20,0x20,0x43,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x69,0x6E,0x74,0x65,0x72,0x72,0x75, +0x70,0x74,0x73,0x20,0x6F,0x66,0x66,0x0D,0x0A,0x20,0x20,0x20,0x53,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x69, +0x6E,0x74,0x65,0x72,0x72,0x75,0x70,0x74,0x73,0x20,0x6F,0x6E,0x0D,0x0A,0x20,0x20, +0x20,0x73,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x2D,0x20,0x73,0x69,0x6E,0x67,0x6C,0x65,0x20,0x73,0x74,0x65,0x70,0x0D, +0x0A,0x20,0x20,0x20,0x42,0x78,0x78,0x78,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62,0x72,0x65,0x61,0x6B,0x70,0x6F,0x69,0x6E, +0x74,0x20,0x73,0x65,0x74,0x0D,0x0A,0x20,0x20,0x20,0x62,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62,0x72,0x65, +0x61,0x6B,0x70,0x6F,0x69,0x6E,0x74,0x20,0x63,0x6C,0x65,0x61,0x72,0x0D,0x0A,0x20, +0x20,0x20,0x52,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x2D,0x20,0x72,0x65,0x73,0x74,0x61,0x72,0x74,0x20,0x62,0x72,0x65, +0x61,0x6B,0x70,0x6F,0x69,0x6E,0x74,0x0D,0x0A,0x20,0x20,0x20,0x72,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x72, +0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x20,0x61,0x74,0x20,0x62,0x72,0x6B,0x70, +0x74,0x0D,0x0A,0x20,0x20,0x20,0x58,0x2C,0x78,0x20,0x6E,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x65,0x78,0x61,0x6D,0x69,0x6E,0x65, +0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x6E,0x0D,0x0A,0x20,0x20,0x20,0x48, +0x2C,0x3F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x2D,0x20,0x74,0x68,0x69,0x73,0x20,0x6D,0x65,0x73,0x73,0x61,0x67,0x65,0x00,0x1B, +0x5B,0x32,0x4A,0x1B,0x5B,0x31,0x3B,0x31,0x48,0x41,0x4E,0x53,0x49,0x20,0x54,0x65, +0x72,0x6D,0x69,0x6E,0x61,0x6C,0x0D,0x0A,0x0A,0x00,0x1B,0x5B,0x4B,0x00,0x1B,0x5B, +0x4A,0x00,0x1B,0x5B,0x32,0x4A,0x1B,0x5B,0x31,0x3B,0x31,0x48,0x00,0x1B,0x5B,0x44, +0x20,0x1B,0x5B,0x44,0x00,0x1B,0x5B,0x31,0x3B,0x37,0x32,0x48,0x00,0x1B,0x5B,0x00, +0x3B,0x00,0x48,0x00,0x1B,0x5B,0x73,0x00,0x1B,0x5B,0x75,0x00,0x1B,0x7A,0x2B,0x0B, +0x7F,0x1B,0x7A,0x2E,0x0C,0x7F,0x1B,0x7A,0x2D,0x08,0x7F,0x1B,0x7A,0x2C,0x0A,0x7F, +0x1B,0x7A,0x22,0x08,0x7F,0x1A,0x57,0x79,0x73,0x65,0x20,0x33,0x30,0x20,0x54,0x65, +0x72,0x6D,0x69,0x6E,0x61,0x6C,0x0D,0x0A,0x00,0x1B,0x54,0x00,0x1B,0x59,0x00,0x1A, +0x00,0x1E,0x00,0x08,0x20,0x08,0x00,0x00,0x1B,0x3D,0x00,0x00,0x00,0x1B,0x46,0x00, +0x0D,0x00,0x3F,0x44,0x64,0x45,0x65,0x46,0x66,0x47,0x67,0x48,0x68,0x49,0x69,0x4F, +0x6F,0x43,0x63,0x53,0x73,0x42,0x62,0x52,0x72,0x57,0x77,0x58,0x78,0x4C,0x6C,0x1E, +0x60,0xB6,0x57,0xB6,0x57,0x32,0x58,0x32,0x58,0xB8,0x59,0xB8,0x59,0x96,0x59,0x96, +0x59,0x1E,0x60,0x1E,0x60,0x4E,0x57,0x2A,0x57,0x08,0x57,0xE8,0x56,0x72,0x57,0x72, +0x57,0x7A,0x57,0x2A,0x5F,0xEE,0x5E,0x3A,0x5F,0x15,0x5F,0x22,0x5F,0x82,0x57,0x82, +0x57,0xE0,0x59,0xE0,0x59,0xBE,0x57,0xBE,0x57,0x6A,0x61,0x7A,0x61,0xA2,0x61,0xAE, +0x61,0xBA,0x61,0xD8,0x61,0xE4,0x61,0x04,0x62,0xDA,0x56,0x2C,0x62,0x3A,0x62,0x42, +0x59,0x20,0x20,0x66,0x6C,0x61,0x67,0x73,0x3D,0x00,0x20,0x20,0x61,0x78,0x3D,0x00, +0x20,0x20,0x62,0x78,0x3D,0x00,0x20,0x20,0x63,0x78,0x3D,0x00,0x20,0x20,0x64,0x78, +0x3D,0x00,0x20,0x20,0x63,0x73,0x3D,0x00,0x20,0x20,0x64,0x73,0x3D,0x00,0x20,0x20, +0x65,0x73,0x3D,0x00,0x20,0x20,0x73,0x73,0x3D,0x00,0x20,0x20,0x64,0x69,0x3D,0x00, +0x20,0x20,0x73,0x69,0x3D,0x00,0x20,0x20,0x62,0x70,0x3D,0x00,0x20,0x20,0x73,0x70, +0x3D,0x00,0x20,0x20,0x69,0x70,0x3D,0x00,0x20,0x63,0x68,0x61,0x6E,0x65,0x6C,0x3D, +0x00,0x20,0x20,0x20,0x20,0x73,0x65,0x67,0x3D,0x00,0x20,0x74,0x69,0x5F,0x73,0x74, +0x72,0x3D,0x00,0x20,0x74,0x69,0x5F,0x74,0x6F,0x73,0x3D,0x00,0x20,0x74,0x69,0x5F, +0x6D,0x61,0x78,0x3D,0x00,0x20,0x74,0x69,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x74, +0x69,0x5F,0x73,0x69,0x7A,0x3D,0x00,0x20,0x74,0x69,0x5F,0x73,0x74,0x66,0x3D,0x00, +0x20,0x74,0x69,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20,0x74,0x69,0x5F,0x66,0x6C,0x67, +0x3D,0x00,0x20,0x74,0x69,0x5F,0x74,0x6F,0x74,0x3D,0x00,0x20,0x72,0x69,0x5F,0x70, +0x63,0x6E,0x3D,0x00,0x20,0x72,0x69,0x5F,0x73,0x74,0x72,0x3D,0x00,0x20,0x72,0x69, +0x5F,0x73,0x74,0x66,0x3D,0x00,0x20,0x72,0x69,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20, +0x72,0x69,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x73,0x69,0x7A,0x3D, +0x00,0x20,0x72,0x69,0x5F,0x74,0x6F,0x74,0x3D,0x00,0x20,0x72,0x69,0x5F,0x6D,0x69, +0x6E,0x3D,0x00,0x20,0x72,0x69,0x5F,0x66,0x6C,0x67,0x3D,0x00,0x20,0x72,0x69,0x5F, +0x74,0x6F,0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x74,0x68,0x72,0x3D,0x00,0x20,0x74, +0x68,0x5F,0x73,0x74,0x66,0x3D,0x00,0x20,0x74,0x68,0x5F,0x73,0x74,0x72,0x3D,0x00, +0x20,0x74,0x68,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x74,0x68,0x5F,0x73,0x69,0x7A, +0x3D,0x00,0x20,0x74,0x68,0x5F,0x74,0x72,0x67,0x3D,0x00,0x20,0x74,0x68,0x5F,0x66, +0x6C,0x67,0x3D,0x00,0x20,0x74,0x68,0x5F,0x63,0x6E,0x74,0x3D,0x00,0x20,0x72,0x68, +0x5F,0x73,0x74,0x72,0x3D,0x00,0x20,0x72,0x68,0x5F,0x73,0x74,0x66,0x3D,0x00,0x20, +0x72,0x68,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x72,0x68,0x5F,0x73,0x69,0x7A,0x3D, +0x00,0x20,0x72,0x68,0x5F,0x73,0x70,0x61,0x3D,0x00,0x20,0x72,0x68,0x5F,0x61,0x73, +0x6F,0x3D,0x00,0x20,0x72,0x68,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20,0x72,0x68,0x5F, +0x66,0x6C,0x67,0x3D,0x00,0x20,0x6D,0x5F,0x63,0x61,0x72,0x65,0x3D,0x00,0x20,0x70, +0x74,0x5F,0x66,0x6C,0x6F,0x3D,0x00,0x20,0x61,0x73,0x5F,0x66,0x6C,0x6F,0x3D,0x00, +0x20,0x72,0x6D,0x5F,0x66,0x6C,0x6F,0x3D,0x00,0x20,0x20,0x20,0x71,0x5F,0x69,0x6E, +0x3D,0x00,0x20,0x20,0x71,0x5F,0x6F,0x75,0x74,0x3D,0x00,0x20,0x71,0x5F,0x64,0x72, +0x61,0x6E,0x3D,0x00,0x20,0x20,0x71,0x5F,0x74,0x69,0x6D,0x3D,0x00,0x20,0x20,0x20, +0x71,0x5F,0x66,0x63,0x3D,0x00,0x20,0x71,0x5F,0x73,0x74,0x61,0x74,0x3D,0x00,0x20, +0x71,0x5F,0x64,0x61,0x74,0x61,0x3D,0x00,0x20,0x71,0x5F,0x6D,0x6F,0x64,0x6D,0x3D, +0x00,0x20,0x68,0x61,0x6E,0x64,0x5F,0x6F,0x3D,0x00,0x20,0x68,0x61,0x6E,0x64,0x5F, +0x62,0x3D,0x00,0x20,0x68,0x61,0x6E,0x64,0x5F,0x65,0x3D,0x00,0x20,0x68,0x61,0x6E, +0x64,0x5F,0x69,0x3D,0x00,0x20,0x20,0x6F,0x70,0x6F,0x73,0x74,0x3D,0x00,0x20,0x20, +0x74,0x69,0x6D,0x65,0x6F,0x3D,0x00,0x20,0x63,0x75,0x73,0x74,0x6D,0x31,0x3D,0x00, +0x20,0x63,0x75,0x73,0x74,0x6D,0x32,0x3D,0x00,0x20,0x63,0x75,0x73,0x74,0x6D,0x64, +0x3D,0x00,0x20,0x74,0x78,0x72,0x61,0x74,0x65,0x3D,0x00,0x20,0x72,0x78,0x72,0x61, +0x74,0x65,0x3D,0x00,0x20,0x20,0x63,0x5F,0x6D,0x61,0x70,0x3D,0x00,0x20,0x63,0x5F, +0x61,0x64,0x64,0x72,0x3D,0x00,0x20,0x63,0x5F,0x61,0x69,0x73,0x72,0x3D,0x00,0x20, +0x63,0x5F,0x78,0x74,0x61,0x67,0x3D,0x00,0x20,0x63,0x5F,0x64,0x65,0x66,0x72,0x3D, +0x00,0x20,0x63,0x5F,0x66,0x6C,0x73,0x68,0x3D,0x00,0x20,0x74,0x78,0x6D,0x61,0x78, +0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x65,0x6D,0x73,0x3D,0x00,0x20,0x20,0x63,0x5F, +0x6C,0x73,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x69,0x65,0x72,0x3D,0x00,0x20,0x20, +0x63,0x5F,0x66,0x63,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x6D,0x63,0x72,0x3D,0x00, +0x20,0x20,0x63,0x5F,0x6C,0x63,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x64,0x73,0x73, +0x3D,0x00,0x20,0x63,0x5F,0x64,0x73,0x73,0x69,0x3D,0x00,0x20,0x63,0x5F,0x64,0x73, +0x73,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x69,0x73,0x72,0x3D,0x00,0x20,0x20,0x63, +0x5F,0x63,0x61,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x65,0x66,0x72,0x3D,0x00,0x20, +0x63,0x5F,0x65,0x72,0x73,0x74,0x3D,0x00,0x20,0x63,0x5F,0x65,0x63,0x6E,0x74,0x3D, +0x00,0x20,0x63,0x5F,0x62,0x72,0x6B,0x63,0x3D,0x00,0x20,0x63,0x5F,0x62,0x6F,0x6B, +0x63,0x3D,0x00,0x20,0x63,0x5F,0x72,0x65,0x70,0x6C,0x3D,0x00,0x20,0x63,0x5F,0x63, +0x63,0x73,0x72,0x3D,0x00,0x20,0x63,0x5F,0x73,0x74,0x74,0x31,0x3D,0x00,0x20,0x63, +0x5F,0x73,0x74,0x74,0x32,0x3D,0x00,0x2B,0xC0,0x8E,0xD8,0x8E,0xC0,0xE8,0xC2,0x00, +0xE8,0xE5,0x00,0xFA,0xBF,0x84,0x00,0xC7,0x05,0xBE,0x56,0x8C,0x4D,0x02,0xBF,0x0C, +0x00,0xC7,0x05,0x50,0x5E,0x8C,0x4D,0x02,0xBF,0x04,0x00,0xC7,0x05,0x9C,0x5E,0x8C, +0x4D,0x02,0xE8,0xF1,0x00,0x90,0xE8,0x49,0x01,0xE8,0x16,0x00,0xF4,0x90,0xE8,0xE5, +0x00,0xBE,0x9C,0x4D,0xE8,0x09,0x0C,0xA0,0x93,0x12,0xE8,0x5D,0x0C,0xE8,0xC2,0x09, +0xEB,0xE4,0xE8,0xD5,0x0C,0xE8,0xC4,0x0C,0x0A,0xC0,0x74,0xF6,0x8B,0x1E,0xF8,0x79, +0x3C,0x0D,0x74,0x2E,0x3C,0x08,0x74,0x17,0x3C,0x7F,0x74,0x13,0x83,0xFB,0x20,0x7F, +0xE1,0x88,0x87,0xD6,0x79,0x43,0x89,0x1E,0xF8,0x79,0xE8,0x77,0x0C,0xEB,0xD3,0x0B, +0xDB,0x74,0xCF,0x4B,0x89,0x1E,0xF8,0x79,0x8B,0x36,0x16,0x7A,0xE8,0xC1,0x0B,0xEB, +0xC1,0x90,0xE8,0x02,0x00,0xEB,0xBB,0xC6,0x87,0xD6,0x79,0x00,0x0B,0xDB,0x74,0x1E, +0xA0,0xD6,0x79,0xBF,0x42,0x51,0xB9,0x1D,0x00,0x8B,0xD9,0x06,0x0E,0x07,0xF2,0xAE, +0x07,0x75,0x17,0x41,0x2B,0xD9,0xD1,0xE3,0x2E,0xFF,0x97,0x5F,0x51,0x90,0x33,0xC0, +0xA3,0xF8,0x79,0xBE,0x6B,0x4D,0xE8,0x87,0x0B,0xC3,0xBE,0x6F,0x4D,0xE8,0x80,0x0B, +0xEB,0xEC,0xBA,0x00,0x02,0xB0,0x93,0xEE,0xB0,0x55,0xEE,0xBA,0x10,0x02,0xB0,0x93, +0xEE,0xB0,0xAA,0xEE,0xBA,0x00,0x02,0xEC,0x3C,0x55,0x75,0x08,0xBA,0x10,0x02,0xEC, +0x3C,0xAA,0x74,0x03,0xE8,0x2F,0xF6,0xC3,0xBA,0x04,0x02,0xB0,0x1A,0xEE,0xB0,0x20, +0xEE,0xB0,0x30,0xEE,0xB0,0x40,0xEE,0xB0,0x80,0xEE,0xBA,0x00,0x02,0xB0,0x13,0xEE, +0xB0,0x07,0xEE,0xBA,0x08,0x02,0xB0,0x80,0xEE,0xBA,0x02,0x02,0xB0,0xBB,0xEE,0xBA, +0x04,0x02,0xB0,0x05,0xEE,0xC3,0xC6,0x06,0xCA,0x13,0x01,0xC7,0x06,0xF8,0x79,0x00, +0x00,0xC6,0x06,0xF6,0x79,0x01,0xC7,0x06,0xD0,0x79,0x00,0x00,0xC7,0x06,0xD2,0x79, +0x00,0x00,0xC7,0x06,0xD4,0x79,0x00,0x00,0xC7,0x06,0xFA,0x79,0x00,0x00,0xC7,0x06, +0xFC,0x79,0x00,0x00,0xC7,0x06,0xFE,0x79,0x00,0x00,0xC7,0x06,0x00,0x7A,0x00,0x00, +0xC7,0x06,0x02,0x7A,0xB0,0x59,0x8C,0x0E,0x04,0x7A,0xC7,0x06,0x06,0x7A,0x00,0x00, +0xC7,0x06,0x27,0x7A,0x00,0x00,0xC6,0x06,0x29,0x7A,0x00,0xC6,0x06,0x2A,0x7A,0x00, +0xC3,0x90,0xBE,0x04,0x4D,0xE8,0xC8,0x0A,0xE8,0x3F,0x00,0x2C,0x31,0x3C,0x01,0x77, +0xF7,0xE8,0x81,0x09,0x8B,0x36,0x0C,0x7A,0xE8,0xB5,0x0A,0xBE,0x4C,0x4D,0xE8,0xAF, +0x0A,0x0E,0x58,0xE8,0xF8,0x0A,0xBE,0x5C,0x4D,0xE8,0xA4,0x0A,0xC3,0x90,0x60,0xD1, +0xE3,0x83,0xFB,0x18,0x73,0x11,0x1E,0xBA,0x00,0x00,0x8E,0xDA,0x2E,0xFF,0x97,0x99, +0x51,0x8B,0xEC,0x89,0x46,0x10,0x1F,0x61,0xCF,0x90,0xE8,0x4F,0x0B,0x0A,0xC0,0x75, +0x05,0xE8,0x56,0x0B,0xEB,0xF4,0xC3,0x90,0x83,0x3E,0xF8,0x79,0x01,0x74,0x16,0xBE, +0xD7,0x79,0xE8,0x31,0x0A,0x8B,0xD0,0xAC,0x3C,0x2C,0x74,0x04,0x3C,0x20,0x75,0x05, +0xE8,0x23,0x0A,0xEE,0xC3,0xE9,0xD2,0xFE,0x83,0x3E,0xF8,0x79,0x01,0x74,0xF6,0xBE, +0xD7,0x79,0xE8,0x11,0x0A,0x8B,0xD0,0xAC,0x3C,0x2C,0x74,0x08,0x3C,0x20,0x74,0x04, +0xE9,0xB7,0xFE,0x90,0xE8,0xFF,0x09,0xEF,0xC3,0x90,0x8B,0x16,0x06,0x7A,0x83,0x3E, +0xF8,0x79,0x01,0x74,0x0B,0xBE,0xD7,0x79,0xE8,0xEB,0x09,0x8B,0xD0,0xA3,0x06,0x7A, +0xB0,0x20,0xE8,0x57,0x0B,0x8B,0x16,0x06,0x7A,0xEC,0xE8,0x6F,0x0B,0xC3,0x8B,0x16, +0x06,0x7A,0x83,0x3E,0xF8,0x79,0x01,0x74,0x0B,0xBE,0xD7,0x79,0xE8,0xC7,0x09,0x8B, +0xD0,0xA3,0x06,0x7A,0xB0,0x20,0xE8,0x33,0x0B,0x8B,0x16,0x06,0x7A,0xED,0xE8,0x67, +0x0B,0xC3,0xFA,0xC6,0x06,0xF6,0x79,0x00,0xC3,0x90,0xC6,0x06,0xF6,0x79,0x01,0xFB, +0xC3,0x90,0x06,0xE8,0x58,0x09,0xB0,0x20,0xE8,0x11,0x0B,0x26,0x8B,0x05,0xE8,0x47, +0x0B,0xB0,0x08,0xE8,0x06,0x0B,0xE8,0x03,0x0B,0xE8,0x00,0x0B,0xE8,0xFD,0x0A,0xB8, +0x01,0x00,0xE8,0xCF,0xF4,0xBA,0x02,0x02,0xEC,0x24,0x01,0x75,0x02,0xEB,0xDC,0xBA, +0x06,0x02,0xEC,0x07,0xC3,0x90,0xC7,0x06,0x08,0x7A,0x10,0x00,0xEB,0x06,0xC7,0x06, +0x08,0x7A,0x01,0x00,0x06,0x8E,0x06,0xFC,0x79,0x8B,0x3E,0xFA,0x79,0xE8,0x0E,0x09, +0xE8,0x0B,0x00,0x89,0x3E,0xFA,0x79,0x8C,0x06,0xFC,0x79,0x07,0xC3,0x90,0xBE,0x94, +0x4D,0xE8,0x7C,0x09,0x8B,0x16,0x08,0x7A,0x52,0xE8,0x2A,0x09,0xE8,0x0F,0x0A,0xE8, +0x0C,0x0A,0x33,0xDB,0xB9,0x10,0x00,0x90,0x26,0x8A,0x01,0xE8,0xBC,0x09,0xE8,0xFD, +0x09,0x43,0xE2,0xF4,0xE8,0xF7,0x09,0xE8,0xF4,0x09,0x33,0xDB,0xB9,0x10,0x00,0x90, +0x26,0x8A,0x01,0x3C,0x20,0x72,0x05,0x3C,0x7E,0x76,0x03,0x90,0xB0,0x2E,0xE8,0xE3, +0x09,0x43,0xE2,0xEC,0xBE,0x94,0x4D,0xE8,0x36,0x09,0x83,0xC7,0x10,0x5A,0x4A,0x75, +0xB7,0xC3,0x06,0x8E,0x06,0x00,0x7A,0x8B,0x3E,0xFE,0x79,0xE8,0xA0,0x08,0x89,0x3E, +0xFE,0x79,0x8C,0x06,0x00,0x7A,0x57,0x8B,0x36,0x0E,0x7A,0xE8,0x12,0x09,0xC7,0x06, +0x08,0x7A,0x10,0x00,0xBA,0x00,0x02,0xE8,0xE8,0x00,0xE8,0x81,0xFF,0x5F,0xBA,0x00, +0x00,0xE8,0xDE,0x00,0xBE,0x97,0x4D,0xE8,0xF6,0x08,0x8C,0xC0,0xE8,0x3F,0x09,0xB0, +0x3A,0xE8,0x90,0x09,0x8B,0xC7,0xE8,0x35,0x09,0xE8,0x7E,0x08,0xE8,0xC3,0x00,0x90, +0xE8,0xB7,0x09,0xE8,0xA6,0x09,0x0A,0xC0,0x74,0xF6,0x3C,0x0B,0x75,0x06,0x83,0xEF, +0x10,0xEB,0x19,0x90,0x3C,0x0A,0x75,0x06,0x83,0xC7,0x10,0xEB,0x0F,0x90,0x3C,0x0C, +0x75,0x04,0x47,0xEB,0x07,0x90,0x3C,0x08,0x75,0x24,0x4F,0x90,0x8B,0x36,0xFE,0x79, +0x8B,0xC7,0x2B,0xC6,0x3D,0x00,0x01,0x72,0xA5,0x3D,0x10,0x01,0x72,0x04,0x83,0xEE, +0x20,0x90,0x83,0xC6,0x10,0x89,0x36,0xFE,0x79,0x57,0x8B,0xFE,0xEB,0x80,0x3C,0x2E, +0x75,0x08,0xBA,0x01,0x13,0xE8,0x6A,0x00,0x07,0xC3,0xC6,0x06,0x0A,0x7A,0x02,0x32, +0xC9,0x90,0x3C,0x30,0x72,0x4C,0x3C,0x39,0x76,0x0C,0x24,0x5F,0x3C,0x41,0x72,0x42, +0x3C,0x46,0x77,0x3E,0x2C,0x07,0x2C,0x30,0x50,0xE8,0xCC,0x08,0x58,0x02,0xC8,0xFE, +0x0E,0x0A,0x7A,0x74,0x0F,0xC0,0xE1,0x04,0xE8,0x2F,0x09,0xE8,0x1E,0x09,0x0A,0xC0, +0x74,0xF6,0xEB,0xCE,0x26,0x88,0x0D,0xE8,0xE0,0x07,0x8A,0xD0,0xE8,0x23,0x00,0x8A, +0xC1,0x3C,0x20,0x72,0x05,0x3C,0x7E,0x76,0x03,0x90,0xB0,0x2E,0xE8,0xD5,0x08,0xE9, +0x70,0xFF,0xE8,0xC5,0x07,0xE8,0x0A,0x00,0x26,0x8A,0x05,0xE8,0x7C,0x08,0xE9,0x1D, +0xFF,0x90,0xF6,0x06,0x26,0x7A,0x02,0x75,0x02,0x86,0xF2,0x52,0x8B,0x36,0x1A,0x7A, +0xE8,0x0D,0x08,0x5A,0x52,0x8A,0xC6,0x02,0x06,0x24,0x7A,0xF6,0x06,0x26,0x7A,0x01, +0x75,0x06,0xE8,0x9F,0x08,0xEB,0x0D,0x90,0x32,0xE4,0xE8,0x0D,0x08,0x8B,0x36,0x1C, +0x7A,0xE8,0xEC,0x07,0x5A,0x8A,0xC2,0x02,0x06,0x25,0x7A,0xF6,0x06,0x26,0x7A,0x01, +0x75,0x06,0xE8,0x7F,0x08,0xEB,0x06,0x90,0x32,0xE4,0xE8,0xED,0x07,0x8B,0x36,0x1E, +0x7A,0xE8,0xCC,0x07,0xC3,0x90,0x06,0x8E,0x06,0x04,0x7A,0x8B,0x3E,0x02,0x7A,0xE8, +0x3C,0x07,0x89,0x3E,0x02,0x7A,0x8C,0x06,0x04,0x7A,0x07,0xFF,0x1E,0x02,0x7A,0xC3, +0xBE,0x79,0x4D,0xE8,0xAA,0x07,0xCB,0x90,0x06,0x57,0xBE,0xD7,0x79,0xE8,0x66,0x07, +0x8B,0xD8,0xE8,0x61,0x07,0x8B,0xC8,0x2B,0xCB,0x78,0x11,0x8E,0xC3,0xBF,0x00,0x00, +0xB8,0xFF,0xFF,0x51,0xB9,0x08,0x00,0xF3,0xAB,0x59,0xE2,0xF7,0x5F,0x07,0xC3,0x90, +0x06,0xBE,0xD7,0x79,0xE8,0x3F,0x07,0x8B,0xD8,0xD1,0xE3,0x2E,0x8B,0x9F,0x44,0x00, +0xBE,0x08,0x52,0xE8,0xF1,0x08,0x8B,0xC3,0xE8,0xDD,0x08,0xB8,0x01,0x00,0xE8,0x73, +0xF2,0xE8,0xE0,0x08,0xBE,0x11,0x52,0xE8,0xDD,0x08,0x8B,0x47,0x18,0xE8,0xC8,0x08, +0xBE,0x59,0x52,0xE8,0xD1,0x08,0x8B,0x47,0x26,0xE8,0xBC,0x08,0xBE,0x35,0x52,0xE8, +0xC5,0x08,0x8B,0x47,0x1E,0xE8,0xB0,0x08,0xBE,0x3E,0x52,0xE8,0xB9,0x08,0x8B,0x47, +0x20,0xE8,0xA4,0x08,0xBE,0x50,0x52,0xE8,0xAD,0x08,0x8B,0x47,0x24,0xE8,0x98,0x08, +0xBE,0x62,0x52,0xE8,0xA1,0x08,0x8B,0x47,0x2A,0xE8,0x8C,0x08,0xE8,0x95,0x08,0xBE, +0x1A,0x52,0xE8,0x92,0x08,0x8B,0x07,0xE8,0x7E,0x08,0xBE,0x23,0x52,0xE8,0x87,0x08, +0x8B,0x47,0x1A,0xE8,0x72,0x08,0xBE,0x2C,0x52,0xE8,0x7B,0x08,0x8B,0x47,0x1C,0xE8, +0x66,0x08,0xBE,0x47,0x52,0xE8,0x6F,0x08,0x8B,0x47,0x22,0xE8,0x5A,0x08,0xE8,0x63, +0x08,0xBE,0xB3,0x52,0xE8,0x60,0x08,0x8B,0x47,0x38,0xE8,0x4B,0x08,0xBE,0x8F,0x52, +0xE8,0x54,0x08,0x8B,0x47,0x30,0xE8,0x3F,0x08,0xBE,0x98,0x52,0xE8,0x48,0x08,0x8B, +0x47,0x32,0xE8,0x33,0x08,0xBE,0x86,0x52,0xE8,0x3C,0x08,0x8B,0x47,0x2E,0xE8,0x27, +0x08,0xBE,0xA1,0x52,0xE8,0x30,0x08,0x8B,0x47,0x34,0xE8,0x1B,0x08,0xE8,0x24,0x08, +0xBE,0x6B,0x52,0xE8,0x21,0x08,0x8B,0x47,0x04,0xE8,0x0C,0x08,0xBE,0x74,0x52,0xE8, +0x15,0x08,0x8B,0x47,0x14,0xE8,0x00,0x08,0xBE,0x7D,0x52,0xE8,0x09,0x08,0x8B,0x47, +0x2C,0xE8,0xF4,0x07,0xBE,0xAA,0x52,0xE8,0xFD,0x07,0x8B,0x47,0x36,0xE8,0xE8,0x07, +0xBE,0xBC,0x52,0xE8,0xF1,0x07,0x8B,0x47,0x3A,0xE8,0xDC,0x07,0xBE,0xC5,0x52,0xE8, +0xE5,0x07,0x8B,0x47,0x3C,0xE8,0xD0,0x07,0xE8,0xD9,0x07,0xBE,0xFB,0x52,0xE8,0xD6, +0x07,0x8B,0x47,0x48,0xE8,0xC1,0x07,0xBE,0xE0,0x52,0xE8,0xCA,0x07,0x8B,0x47,0x42, +0xE8,0xB5,0x07,0xBE,0xE9,0x52,0xE8,0xBE,0x07,0x8B,0x47,0x44,0xE8,0xA9,0x07,0xBE, +0x5E,0x53,0xE8,0xB2,0x07,0x8B,0x47,0x4C,0xE8,0x9D,0x07,0xBE,0x67,0x53,0xE8,0xA6, +0x07,0x8B,0x47,0x4E,0xE8,0x91,0x07,0xBE,0x70,0x53,0xE8,0x9A,0x07,0x8B,0x47,0x50, +0xE8,0x85,0x07,0xE8,0x8E,0x07,0xBE,0x04,0x53,0xE8,0x8B,0x07,0x8B,0x47,0x4A,0xE8, +0x76,0x07,0xBE,0xCE,0x52,0xE8,0x7F,0x07,0x8B,0x47,0x08,0xE8,0x6A,0x07,0xBE,0xD7, +0x52,0xE8,0x73,0x07,0x8B,0x47,0x40,0xE8,0x5E,0x07,0xBE,0xF2,0x52,0xE8,0x67,0x07, +0x8B,0x47,0x46,0xE8,0x52,0x07,0xE8,0x5B,0x07,0xBE,0x4C,0x53,0xE8,0x58,0x07,0x8B, +0x47,0x7A,0xE8,0x43,0x07,0xBE,0x1F,0x53,0xE8,0x4C,0x07,0x8B,0x47,0x70,0xE8,0x37, +0x07,0xBE,0x28,0x53,0xE8,0x40,0x07,0x8B,0x47,0x72,0xE8,0x2B,0x07,0xBE,0x31,0x53, +0xE8,0x34,0x07,0x8B,0x47,0x74,0xE8,0x1F,0x07,0xE8,0x28,0x07,0xBE,0x0D,0x53,0xE8, +0x25,0x07,0x8B,0x47,0x0C,0xE8,0x10,0x07,0xBE,0x16,0x53,0xE8,0x19,0x07,0x8B,0x47, +0x10,0xE8,0x04,0x07,0xBE,0x3A,0x53,0xE8,0x0D,0x07,0x8B,0x47,0x76,0xE8,0xF8,0x06, +0xBE,0x43,0x53,0xE8,0x01,0x07,0x8B,0x47,0x78,0xE8,0xEC,0x06,0xBE,0x55,0x53,0xE8, +0xF5,0x06,0x8B,0x47,0x3E,0xE8,0xE0,0x06,0xE8,0xE9,0x06,0xBE,0x79,0x53,0xE8,0xE6, +0x06,0x8B,0x47,0x52,0xE8,0xD1,0x06,0xBE,0x82,0x53,0xE8,0xDA,0x06,0x8B,0x47,0x54, +0xE8,0xC5,0x06,0xBE,0x8B,0x53,0xE8,0xCE,0x06,0x8B,0x47,0x56,0xE8,0xB9,0x06,0xBE, +0x94,0x53,0xE8,0xC2,0x06,0x8B,0x47,0x58,0xE8,0xAD,0x06,0xBE,0x9D,0x53,0xE8,0xB6, +0x06,0x8B,0x47,0x5A,0xE8,0xA1,0x06,0xBE,0xA6,0x53,0xE8,0xAA,0x06,0x8B,0x47,0x5C, +0xE8,0x95,0x06,0xE8,0x9E,0x06,0xBE,0xAF,0x53,0xE8,0x9B,0x06,0x8B,0x47,0x5E,0xE8, +0x86,0x06,0xBE,0xB8,0x53,0xE8,0x8F,0x06,0x8B,0x47,0x60,0xE8,0x7A,0x06,0xBE,0xC1, +0x53,0xE8,0x83,0x06,0x8B,0x47,0x62,0xE8,0x6E,0x06,0xBE,0xCA,0x53,0xE8,0x77,0x06, +0x8B,0x47,0x7C,0xE8,0x62,0x06,0xBE,0xD3,0x53,0xE8,0x6B,0x06,0x8B,0x47,0x7E,0xE8, +0x56,0x06,0xBE,0xDC,0x53,0xE8,0x5F,0x06,0x8B,0x87,0x80,0x00,0xE8,0x49,0x06,0xE8, +0x52,0x06,0xBE,0x24,0x54,0xE8,0x4F,0x06,0x8B,0x87,0x9E,0x00,0xE8,0x39,0x06,0xBE, +0xE5,0x53,0xE8,0x42,0x06,0x8B,0x47,0x64,0xE8,0x2D,0x06,0xBE,0xEE,0x53,0xE8,0x36, +0x06,0x8B,0x47,0x6E,0xE8,0x21,0x06,0xBE,0xF7,0x53,0xE8,0x2A,0x06,0x8B,0x87,0x8E, +0x00,0xE8,0x14,0x06,0xBE,0x00,0x54,0xE8,0x1D,0x06,0x8B,0x87,0x90,0x00,0xE8,0x07, +0x06,0xBE,0x09,0x54,0xE8,0x10,0x06,0x8B,0x87,0x92,0x00,0xE8,0xFA,0x05,0xE8,0x03, +0x06,0xBE,0x12,0x54,0xE8,0x00,0x06,0x8B,0x87,0x94,0x00,0xE8,0xEA,0x05,0xBE,0x1B, +0x54,0xE8,0xF3,0x05,0x8B,0x87,0x96,0x00,0xE8,0xDD,0x05,0xBE,0x51,0x54,0xE8,0xE6, +0x05,0x8B,0x87,0x98,0x00,0xE8,0xD0,0x05,0xBE,0x3F,0x54,0xE8,0xD9,0x05,0x8A,0x87, +0xA0,0x00,0xE8,0xA7,0x05,0xBE,0x36,0x54,0xE8,0xCC,0x05,0x8A,0x47,0x28,0xE8,0x9B, +0x05,0xBE,0x48,0x54,0xE8,0xC0,0x05,0x8A,0x87,0xA1,0x00,0xE8,0x8E,0x05,0xE8,0xB3, +0x05,0xBE,0x5A,0x54,0xE8,0xB0,0x05,0x8A,0x87,0xA2,0x00,0xE8,0x7E,0x05,0xBE,0x63, +0x54,0xE8,0xA3,0x05,0x8A,0x87,0xA3,0x00,0xE8,0x71,0x05,0xBE,0x6C,0x54,0xE8,0x96, +0x05,0x8A,0x87,0xA4,0x00,0xE8,0x64,0x05,0xBE,0x75,0x54,0xE8,0x89,0x05,0x8A,0x87, +0xA5,0x00,0xE8,0x57,0x05,0xBE,0x7E,0x54,0xE8,0x7C,0x05,0x8A,0x87,0xA6,0x00,0xE8, +0x4A,0x05,0xBE,0x87,0x54,0xE8,0x6F,0x05,0x8A,0x87,0xA7,0x00,0xE8,0x3D,0x05,0xBE, +0x90,0x54,0xE8,0x62,0x05,0x8A,0x87,0xA8,0x00,0xE8,0x30,0x05,0xE8,0x55,0x05,0xBE, +0x99,0x54,0xE8,0x52,0x05,0x8A,0x87,0xA9,0x00,0xE8,0x20,0x05,0xBE,0xA2,0x54,0xE8, +0x45,0x05,0x8A,0x87,0xAA,0x00,0xE8,0x13,0x05,0xBE,0xAB,0x54,0xE8,0x38,0x05,0x8A, +0x87,0xAB,0x00,0xE8,0x06,0x05,0xBE,0xB4,0x54,0xE8,0x2B,0x05,0x8A,0x87,0xAD,0x00, +0xE8,0xF9,0x04,0xBE,0xBD,0x54,0xE8,0x1E,0x05,0x8A,0x87,0xAE,0x00,0xE8,0xEC,0x04, +0xBE,0xC6,0x54,0xE8,0x11,0x05,0x8A,0x87,0xAF,0x00,0xE8,0xDF,0x04,0xBE,0xCF,0x54, +0xE8,0x04,0x05,0x8A,0x87,0xB0,0x00,0xE8,0xD2,0x04,0xE8,0xF7,0x04,0xBE,0xD8,0x54, +0xE8,0xF4,0x04,0x8A,0x87,0xB1,0x00,0xE8,0xC2,0x04,0xBE,0xE1,0x54,0xE8,0xE7,0x04, +0x8A,0x87,0xB2,0x00,0xE8,0xB5,0x04,0xBE,0xEA,0x54,0xE8,0xDA,0x04,0x8A,0x87,0xB3, +0x00,0xE8,0xA8,0x04,0xBE,0xF3,0x54,0xE8,0xCD,0x04,0x8A,0x87,0xBB,0x00,0xE8,0x9B, +0x04,0xE8,0xC0,0x04,0xBE,0xFC,0x54,0xE8,0xBD,0x04,0x8A,0x87,0xBC,0x00,0xE8,0x8B, +0x04,0xBE,0x05,0x55,0xE8,0xB0,0x04,0x8A,0x87,0xBE,0x00,0xE8,0x7E,0x04,0xBE,0x0E, +0x55,0xE8,0xA3,0x04,0x8A,0x87,0xBF,0x00,0xE8,0x71,0x04,0xE8,0x96,0x04,0x07,0xC3, +0x60,0x06,0x1E,0x16,0x8B,0xEC,0xFF,0x4E,0x16,0xF7,0x46,0x1A,0x00,0x02,0x74,0x01, +0xFB,0xB8,0x00,0x00,0x8E,0xD8,0x8E,0xC0,0x89,0x2E,0x2D,0x7A,0xE8,0xCB,0x00,0x81, +0x66,0x1A,0xFF,0xFE,0xC6,0x06,0x2A,0x7A,0x00,0xE8,0xD8,0x00,0xB8,0xE2,0x5E,0xA3, +0x2B,0x7A,0xE8,0x5D,0x00,0x80,0x3E,0x2A,0x7A,0x00,0x74,0x0A,0x81,0x4E,0x1A,0x00, +0x01,0xC6,0x06,0x2A,0x7A,0x00,0x17,0x1F,0x07,0x61,0xCF,0x90,0x60,0x06,0x1E,0x16, +0x8B,0xEC,0xF7,0x46,0x1A,0x00,0x02,0x74,0x01,0xFB,0xB8,0x00,0x00,0x8E,0xD8,0x8E, +0xC0,0x89,0x2E,0x2D,0x7A,0x81,0x66,0x1A,0xFF,0xFE,0xC6,0x06,0x2A,0x7A,0x00,0xE8, +0x92,0x00,0xB8,0xE2,0x5E,0xA3,0x2B,0x7A,0xE8,0x17,0x00,0x80,0x3E,0x2A,0x7A,0x00, +0x74,0x0A,0x81,0x4E,0x1A,0x00,0x01,0xC6,0x06,0x2A,0x7A,0x00,0x17,0x1F,0x07,0x61, +0xCF,0x90,0xB8,0xF0,0x00,0xE8,0x8C,0xED,0xFF,0x26,0x2B,0x7A,0xC3,0x90,0x06,0x53, +0x56,0x80,0x3E,0x29,0x7A,0x00,0x74,0x03,0xE8,0x3F,0x00,0xBE,0xD7,0x79,0xE8,0x25, +0x02,0x8B,0xD8,0xA3,0x27,0x7A,0x2E,0x8A,0x07,0xA2,0x29,0x7A,0xB0,0xCC,0x2E,0x88, +0x07,0x5E,0x5B,0x07,0xC3,0xC6,0x06,0x2A,0x7A,0x00,0xB8,0xEC,0x5E,0xA3,0x2B,0x7A, +0xC3,0x90,0x8B,0x2E,0x2D,0x7A,0xE8,0x2B,0x00,0xC3,0xC6,0x06,0x2A,0x7A,0x01,0xE8, +0x08,0x00,0xB8,0xEC,0x5E,0xA3,0x2B,0x7A,0xC3,0x90,0x57,0x80,0x3E,0x29,0x7A,0x00, +0x74,0x0F,0x8B,0x3E,0x27,0x7A,0xA0,0x29,0x7A,0x2E,0x88,0x05,0xC6,0x06,0x29,0x7A, +0x00,0x5F,0xC3,0x90,0xBE,0x94,0x4D,0xE8,0x06,0x02,0xBE,0xBA,0x51,0xE8,0x00,0x02, +0xFF,0x76,0x14,0x58,0xE8,0x47,0x02,0xBE,0xC0,0x51,0xE8,0xF3,0x01,0xFF,0x76,0x0E, +0x58,0xE8,0x3A,0x02,0xBE,0xC6,0x51,0xE8,0xE6,0x01,0xFF,0x76,0x12,0x58,0xE8,0x2D, +0x02,0xBE,0xCC,0x51,0xE8,0xD9,0x01,0xFF,0x76,0x10,0x58,0xE8,0x20,0x02,0xBE,0xF6, +0x51,0xE8,0xCC,0x01,0xFF,0x76,0x0A,0x58,0xE8,0x13,0x02,0xBE,0xFC,0x51,0xE8,0xBF, +0x01,0xFF,0x76,0x0C,0x58,0xE8,0x06,0x02,0xBE,0xB1,0x51,0xE8,0xB2,0x01,0xFF,0x76, +0x1A,0x58,0xE8,0xF9,0x01,0xBE,0x94,0x4D,0xE8,0xA5,0x01,0xBE,0xD2,0x51,0xE8,0x9F, +0x01,0xFF,0x76,0x18,0x58,0xE8,0xE6,0x01,0xBE,0xD8,0x51,0xE8,0x92,0x01,0xFF,0x76, +0x02,0x58,0xE8,0xD9,0x01,0xBE,0xDE,0x51,0xE8,0x85,0x01,0xFF,0x76,0x04,0x58,0xE8, +0xCC,0x01,0xBE,0xE4,0x51,0xE8,0x78,0x01,0xFF,0x76,0x00,0x58,0xE8,0xBF,0x01,0xBE, +0xEA,0x51,0xE8,0x6B,0x01,0xFF,0x76,0x06,0x58,0xE8,0xB2,0x01,0xBE,0xF0,0x51,0xE8, +0x5E,0x01,0xFF,0x76,0x08,0x58,0xE8,0xA5,0x01,0xBE,0x02,0x52,0xE8,0x51,0x01,0xFF, +0x76,0x16,0x58,0xE8,0x98,0x01,0xBE,0x6B,0x4D,0xE8,0x44,0x01,0xC3,0x90,0xBE,0xAB, +0x4D,0xE8,0x3C,0x01,0xC3,0x3C,0x00,0x74,0x05,0x3C,0x01,0x74,0x59,0xC3,0xC7,0x06, +0x0C,0x7A,0xAF,0x50,0xC7,0x06,0x0E,0x7A,0xD2,0x50,0xC7,0x06,0x10,0x7A,0xCA,0x50, +0xC7,0x06,0x12,0x7A,0xCE,0x50,0xC7,0x06,0x14,0x7A,0xD6,0x50,0xC7,0x06,0x16,0x7A, +0xDD,0x50,0xC7,0x06,0x18,0x7A,0xE5,0x50,0xC7,0x06,0x1A,0x7A,0xED,0x50,0xC7,0x06, +0x1C,0x7A,0xF0,0x50,0xC7,0x06,0x1E,0x7A,0xF2,0x50,0xC7,0x06,0x20,0x7A,0xF4,0x50, +0xC7,0x06,0x22,0x7A,0xF8,0x50,0xC6,0x06,0x24,0x7A,0x01,0xC6,0x06,0x25,0x7A,0x01, +0xC6,0x06,0x26,0x7A,0x03,0xC3,0xC7,0x06,0x0C,0x7A,0xFC,0x50,0xC7,0x06,0x0E,0x7A, +0x2F,0x51,0xC7,0x06,0x10,0x7A,0x29,0x51,0xC7,0x06,0x12,0x7A,0x2C,0x51,0xC7,0x06, +0x14,0x7A,0x31,0x51,0xC7,0x06,0x16,0x7A,0x33,0x51,0xC7,0x06,0x18,0x7A,0x37,0x51, +0xC7,0x06,0x1A,0x7A,0x38,0x51,0xC7,0x06,0x1C,0x7A,0x3B,0x51,0xC7,0x06,0x1E,0x7A, +0x3C,0x51,0xC7,0x06,0x20,0x7A,0x3D,0x51,0xC7,0x06,0x22,0x7A,0x40,0x51,0xC6,0x06, +0x24,0x7A,0x20,0xC6,0x06,0x25,0x7A,0x20,0xC6,0x06,0x26,0x7A,0x02,0xC3,0xA1,0xF8, +0x79,0x48,0x74,0x14,0xBE,0xD7,0x79,0xE8,0x3C,0x00,0x8B,0xF8,0xAC,0x3C,0x3A,0x75, +0x07,0x8E,0xC7,0xE8,0x30,0x00,0x8B,0xF8,0xC3,0x90,0x8B,0xC7,0x2B,0x06,0xFE,0x79, +0x8A,0xF0,0x24,0x0F,0x8A,0xD0,0x02,0xD0,0x02,0xD0,0x80,0xC2,0x0B,0xC0,0xEE,0x04, +0x80,0xC6,0x03,0x04,0x3D,0xC3,0x8C,0xC0,0xE8,0x93,0x00,0xB0,0x3A,0xE8,0xE4,0x00, +0x8B,0xC7,0xE8,0x89,0x00,0xC3,0x51,0x33,0xC9,0x90,0xAC,0x3C,0x20,0x74,0xFB,0x90, +0x0A,0xC0,0x74,0x26,0x2C,0x30,0x72,0x22,0x3C,0x09,0x76,0x14,0x3C,0x11,0x72,0x1A, +0x2C,0x07,0x3C,0x0F,0x76,0x0A,0x3C,0x2A,0x72,0x10,0x2C,0x20,0x3C,0x0F,0x77,0x0A, +0x98,0xC1,0xE1,0x04,0x03,0xC8,0xAC,0xEB,0xD7,0x90,0x4E,0x8B,0xC1,0x59,0xC3,0x90, +0x06,0x8C,0xC8,0x8E,0xC0,0xE8,0x02,0x00,0x07,0xC3,0x26,0x8A,0x04,0x46,0x0A,0xC0, +0x74,0x06,0xE8,0x8F,0x00,0xEB,0xF3,0x90,0xC3,0x90,0x0B,0xC0,0x74,0x7A,0x51,0x33, +0xD2,0xB9,0xE8,0x03,0xF7,0xF1,0x8B,0xCA,0xE8,0x03,0x00,0x8B,0xC1,0x59,0xBA,0x64, +0x00,0xF6,0xF2,0xE8,0x0C,0x00,0x8A,0xC4,0x98,0xB2,0x0A,0xF6,0xF2,0xE8,0x02,0x00, +0x8A,0xC4,0x50,0x0A,0xF0,0x74,0x05,0x04,0x30,0xE8,0x58,0x00,0x58,0xC3,0x86,0xC4, +0xE8,0x07,0x00,0x86,0xC4,0xE8,0x02,0x00,0xC3,0x90,0xC1,0xC8,0x04,0xE8,0x08,0x00, +0xC1,0xC0,0x04,0xE8,0x02,0x00,0xC3,0x90,0x53,0x50,0x24,0x0F,0xBB,0xAC,0x62,0x2E, +0xD7,0xE8,0x30,0x00,0x58,0x5B,0xC3,0x90,0x86,0xC4,0xE8,0x07,0x00,0x86,0xC4,0xE8, +0x02,0x00,0xC3,0x90,0x50,0xB9,0x08,0x00,0x8A,0xE0,0x32,0xC0,0xD1,0xC0,0x04,0x30, +0xE8,0x11,0x00,0xE2,0xF5,0x58,0xC3,0x90,0xB0,0x30,0xE8,0x07,0x00,0xC3,0xB0,0x20, +0xE8,0x01,0x00,0xC3,0x56,0x8B,0x36,0xD0,0x79,0x88,0x84,0xD0,0x77,0x46,0x81,0xE6, +0xFF,0x01,0xFF,0x06,0xD4,0x79,0x89,0x36,0xD0,0x79,0x81,0x3E,0xD4,0x79,0xFE,0x01, +0x75,0x08,0x56,0xE8,0x14,0x00,0x5E,0xEB,0xF1,0x90,0x5E,0xC3,0xBA,0x02,0x02,0xEC, +0x24,0x01,0x74,0x04,0xBA,0x06,0x02,0xEC,0xC3,0x90,0x80,0x3E,0xF6,0x79,0x00,0x74, +0x09,0x60,0xB8,0x01,0x00,0xE8,0x2C,0xEA,0x61,0x90,0xBA,0x02,0x02,0xEC,0xA8,0x04, +0x74,0x28,0x8B,0x36,0xD2,0x79,0x83,0x3E,0xD4,0x79,0x00,0x74,0x1D,0x8A,0x84,0xD0, +0x77,0x46,0x81,0xE6,0xFF,0x01,0x89,0x36,0xD2,0x79,0xFF,0x0E,0xD4,0x79,0xBA,0x06, +0x02,0xEE,0xBA,0x02,0x02,0xEC,0xA8,0x04,0x75,0xDC,0xA1,0xD4,0x79,0xC3,0x52,0xBA, +0x06,0x02,0xEE,0x5A,0xC3,0x90,0x52,0x50,0xBA,0x02,0x02,0xEC,0xA8,0x04,0x74,0x08, +0x58,0x5A,0xE8,0xE9,0xFF,0xF9,0xC3,0x90,0x58,0x5A,0xF8,0xC3,0x52,0x50,0xBA,0x02, +0x02,0xEC,0xA8,0x04,0x74,0xFB,0x58,0x5A,0xE8,0xD3,0xFF,0xC3,0x30,0x31,0x32,0x33, +0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46,0x53,0x50,0x8A,0xE0, +0x80,0xE4,0x0F,0xBB,0xAC,0x62,0xC0,0xE8,0x04,0x2E,0xD7,0xE8,0xCE,0xFF,0x8A,0xC4, +0x2E,0xD7,0xE8,0xC7,0xFF,0x58,0x5B,0xC3,0x86,0xE0,0xE8,0xDF,0xFF,0x86,0xE0,0xE8, +0xDA,0xFF,0xC3,0x90,0xBE,0x94,0x4D,0x50,0x2E,0xAC,0x3C,0x00,0x74,0x05,0xE8,0xAB, +0xFF,0xEB,0xF5,0x58,0xC3,0x90,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0xBF, +0x04,0x00,0xC7,0x46,0xFC,0x00,0x00,0xC7,0x46,0xFA,0x00,0x00,0xC7,0x46,0xF8,0x00, +0x00,0x83,0x7E,0x06,0x00,0x75,0x0E,0x56,0xE8,0xB6,0x0E,0x59,0x0B,0xC0,0x75,0x05, +0x8B,0xC7,0xE9,0x5B,0x01,0x8B,0x46,0xFC,0x89,0x46,0xFE,0x0B,0xFF,0x75,0x05,0xB8, +0x01,0x00,0xEB,0x02,0x33,0xC0,0x50,0x56,0xE8,0xA4,0x0D,0x59,0x59,0xB4,0x00,0x89, +0x46,0xFC,0x8B,0x5E,0xFC,0x83,0xFB,0x08,0x76,0x03,0xE9,0x2B,0x01,0xD1,0xE3,0x2E, +0xFF,0xA7,0x94,0x64,0xB8,0x03,0x00,0xE9,0x26,0x01,0x83,0x7E,0xFA,0x00,0x74,0x14, +0xC7,0x46,0xFA,0x00,0x00,0x8A,0x44,0x58,0x98,0x50,0x8A,0x44,0x59,0x98,0x50,0xE8, +0xC2,0x0F,0x59,0x59,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00,0x00,0x56, +0xE8,0x9B,0x08,0x59,0x83,0x7E,0x06,0x00,0x75,0x05,0x8B,0xC7,0xE9,0xF1,0x00,0x83, +0xFF,0x04,0x75,0x03,0xE9,0xE6,0x00,0x8B,0xC7,0xE9,0xE4,0x00,0x83,0x7E,0xFE,0x00, +0x75,0x03,0xBF,0x02,0x00,0xE9,0xD5,0x00,0x83,0x7E,0xFE,0x00,0x75,0x03,0xBF,0x01, +0x00,0xE9,0xC9,0x00,0x8B,0x5E,0xFE,0x83,0xFB,0x07,0x76,0x03,0xE9,0x86,0x00,0xD1, +0xE3,0x2E,0xFF,0xA7,0x84,0x64,0x33,0xFF,0xE9,0x7F,0x00,0xBF,0x04,0x00,0x80,0x7C, +0x58,0x0F,0x74,0x22,0x83,0x7E,0xF8,0x00,0x75,0x1C,0xFE,0x44,0x58,0x6A,0x08,0x56, +0xE8,0x7E,0x0C,0x59,0x59,0x8A,0x44,0x58,0x04,0x80,0x50,0x56,0xE8,0x72,0x0C,0x59, +0x59,0xC7,0x46,0xFA,0x01,0x00,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00, +0x00,0x56,0xE8,0x19,0x08,0x59,0xEB,0x42,0xBF,0x04,0x00,0x80,0x7C,0x58,0x00,0x74, +0x22,0x83,0x7E,0xF8,0x00,0x75,0x1C,0xFE,0x4C,0x58,0x6A,0x08,0x56,0xE8,0x41,0x0C, +0x59,0x59,0x8A,0x44,0x58,0x04,0x80,0x50,0x56,0xE8,0x35,0x0C,0x59,0x59,0xC7,0x46, +0xFA,0x01,0x00,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00,0x00,0x56,0xE8, +0xDC,0x07,0x59,0xEB,0x05,0xBF,0x04,0x00,0xEB,0x00,0xEB,0x31,0xBF,0x04,0x00,0xEB, +0x2C,0xC7,0x46,0xF8,0x01,0x00,0x6A,0x08,0x56,0xE8,0x05,0x0C,0x59,0x59,0x80,0x7C, +0x58,0x09,0x7D,0x04,0xB0,0x0F,0xEB,0x02,0xB0,0x00,0x04,0x80,0x50,0x56,0xE8,0xF0, +0x0B,0x59,0x59,0xBF,0x04,0x00,0xEB,0x05,0xBF,0x04,0x00,0xEB,0x00,0xE9,0xA5,0xFE, +0x5F,0x5E,0xC9,0xC3,0xC6,0x63,0x45,0x64,0x45,0x64,0x45,0x64,0x45,0x64,0xCB,0x63, +0x08,0x64,0x33,0x64,0x5A,0x63,0x9C,0x63,0xA8,0x63,0x78,0x64,0xB4,0x63,0x4C,0x64, +0x4C,0x64,0x51,0x64,0x54,0x63,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x8B, +0x7E,0x08,0x6A,0x01,0x56,0xE8,0xA9,0x0B,0x59,0x59,0x8A,0x46,0x06,0xC0,0xE0,0x06, +0x04,0x80,0x50,0x56,0xE8,0x9A,0x0B,0x59,0x59,0xC7,0x46,0xFE,0x00,0x00,0x89,0x7E, +0xF8,0xEB,0x03,0xFF,0x46,0xFE,0x8B,0x5E,0xF8,0xFF,0x46,0xF8,0x80,0x3F,0x00,0x75, +0xF2,0x83,0x7E,0xFE,0x10,0x7D,0x25,0xB8,0x10,0x00,0x2B,0x46,0xFE,0xD1,0xF8,0x89, +0x46,0xFC,0xC7,0x46,0xFA,0x00,0x00,0xEB,0x0B,0x6A,0x20,0x56,0xE8,0x62,0x0B,0x59, +0x59,0xFF,0x46,0xFA,0x8B,0x46,0xFA,0x3B,0x46,0xFC,0x7C,0xED,0xEB,0x0C,0x8B,0xDF, +0x47,0x8A,0x07,0x50,0x56,0xE8,0x49,0x0B,0x59,0x59,0x80,0x3D,0x00,0x75,0xEF,0x6A, +0x02,0x56,0xE8,0x3C,0x0B,0x59,0x59,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x04,0x00, +0x00,0x56,0x57,0x8B,0x7E,0x04,0xC7,0x46,0xFE,0x00,0x00,0xBE,0x14,0x00,0xE9,0x09, +0x01,0x8B,0x5E,0xFE,0x83,0xC3,0x04,0x2B,0xDF,0x8A,0x87,0xAC,0x0B,0x88,0x44,0x5A, +0xC6,0x44,0x58,0x08,0x8A,0x46,0xFE,0x88,0x44,0x59,0xC7,0x44,0x06,0x00,0x00,0xC6, +0x44,0x19,0x00,0xC6,0x44,0x1A,0x00,0xC6,0x44,0x1B,0x00,0xC6,0x44,0x1D,0x0D,0xC6, +0x44,0x1E,0x03,0xC6,0x44,0x1F,0x00,0xC6,0x44,0x20,0x00,0xC6,0x44,0x21,0x00,0xC6, +0x44,0x5B,0x00,0xC6,0x44,0x5D,0x00,0xC6,0x44,0x5E,0x00,0xC6,0x44,0x5F,0x00,0xC6, +0x44,0x60,0x00,0xC7,0x46,0xFC,0x00,0x00,0xEB,0x0D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7, +0x40,0x30,0x00,0x00,0xFF,0x46,0xFC,0x83,0x7E,0xFC,0x10,0x7C,0xED,0xC7,0x46,0xFC, +0x00,0x00,0xEB,0x0A,0x8B,0x5E,0xFC,0xC6,0x40,0x50,0x00,0xFF,0x46,0xFC,0x83,0x7E, +0xFC,0x04,0x7C,0xF0,0xC7,0x44,0x54,0x00,0x00,0xC7,0x44,0x56,0x00,0x00,0x8A,0x44, +0x5A,0x98,0xBA,0xF8,0x00,0x23,0xD0,0xB8,0x05,0x00,0x0B,0xC2,0x89,0x46,0xFC,0x9C, +0xFA,0x8A,0x46,0xFC,0xBA,0xFE,0x00,0xEE,0xBA,0x00,0x00,0xEC,0x9D,0x24,0x08,0x88, +0x46,0xFC,0x83,0x7E,0xFC,0x00,0x75,0x02,0xEB,0x4A,0xFF,0x76,0xFE,0xE8,0x7A,0x0C, +0x59,0x68,0x35,0x02,0x56,0xE8,0x32,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x34,0x68,0x38, +0x02,0x56,0xE8,0x25,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x27,0x68,0x42,0x02,0x56,0xE8, +0x18,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x1A,0x68,0x4C,0x02,0x56,0xE8,0x0B,0x0A,0x59, +0x59,0x0B,0xC0,0x75,0x0D,0x68,0x56,0x02,0x56,0xE8,0xFE,0x09,0x59,0x59,0x0B,0xC0, +0x74,0x02,0xEB,0x00,0xFF,0x46,0xFE,0x83,0xC6,0x62,0x39,0x7E,0xFE,0x7D,0x03,0xE9, +0xEF,0xFE,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x46, +0x04,0xBA,0x62,0x00,0xF7,0xEA,0x05,0x14,0x00,0x8B,0xF0,0x83,0x7E,0x06,0x00,0x74, +0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x08,0x00,0x89,0x44,0x04,0x8A,0x46,0x08,0x88, +0x44,0x5C,0x56,0xE8,0x59,0x04,0x59,0x8B,0xF8,0x8B,0xC7,0x89,0x44,0x56,0x89,0x44, +0x54,0x8A,0x44,0x5D,0x88,0x44,0x2F,0x0B,0xFF,0x75,0x1D,0x68,0xC2,0x0F,0x6A,0x01, +0x56,0xE8,0x02,0xFE,0x83,0xC4,0x06,0xEB,0x00,0x6A,0x01,0x56,0xE8,0x47,0xFC,0x59, +0x59,0x0B,0xC0,0x75,0xF4,0xBF,0x01,0x00,0x89,0x7E,0xFA,0xB9,0x05,0x00,0xBB,0xCB, +0x6A,0x2E,0x8B,0x07,0x3B,0x46,0xFA,0x74,0x07,0x43,0x43,0xE2,0xF4,0xE9,0xA4,0x03, +0x2E,0xFF,0x67,0x0A,0xC7,0x44,0x06,0x02,0x00,0xC7,0x44,0x08,0xF4,0x08,0x8B,0x5E, +0x04,0xD1,0xE3,0x8B,0x87,0xFC,0x08,0x89,0x44,0x0A,0x33,0xC0,0x8B,0xF8,0x89,0x44, +0x54,0xE9,0x80,0x03,0x56,0xE8,0xBB,0x05,0x59,0xBF,0x01,0x00,0x8A,0x44,0x5D,0x88, +0x44,0x60,0xE9,0x6F,0x03,0x83,0x7C,0x04,0x08,0x75,0x30,0x80,0x7C,0x5C,0x01,0x75, +0x15,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0xE4,0x08,0x56,0xE8, +0xF7,0x08,0x59,0x59,0xEB,0x13,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF, +0xB7,0xC4,0x08,0x56,0xE8,0xE2,0x08,0x59,0x59,0xEB,0x2E,0x80,0x7C,0x5C,0x01,0x75, +0x15,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0xD4,0x08,0x56,0xE8, +0xC7,0x08,0x59,0x59,0xEB,0x13,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF, +0xB7,0xB4,0x08,0x56,0xE8,0xB2,0x08,0x59,0x59,0x6A,0x01,0x56,0xE8,0x87,0xFB,0x59, +0x59,0x8B,0xD8,0x83,0xFB,0x03,0x77,0x2A,0xD1,0xE3,0x2E,0xFF,0xA7,0xC3,0x6A,0xBF, +0x01,0x00,0x8A,0x44,0x5D,0x88,0x44,0x5E,0xEB,0x18,0x8A,0x44,0x5D,0x04,0xFF,0x24, +0x07,0x88,0x44,0x5D,0xEB,0x0C,0x8A,0x44,0x5D,0xFE,0xC0,0x24,0x07,0x88,0x44,0x5D, +0xEB,0x00,0xE9,0xCF,0x02,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7, +0xFD,0x02,0x56,0xE8,0x63,0x08,0x59,0x59,0x68,0x1D,0x03,0x56,0xE8,0x5A,0x08,0x59, +0x59,0x6A,0x01,0x56,0xE8,0x2F,0xFB,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x03,0x77,0x36, +0xD1,0xE3,0x2E,0xFF,0xA7,0xBB,0x6A,0xBF,0x01,0x00,0x8A,0x44,0x5D,0x88,0x44,0x5F, +0xEB,0x24,0x8A,0x44,0x5D,0x04,0xFF,0x8A,0x54,0x04,0x80,0xC2,0xFF,0x22,0xC2,0x88, +0x44,0x5D,0xEB,0x12,0x8A,0x44,0x5D,0xFE,0xC0,0x8A,0x54,0x04,0x80,0xC2,0xFF,0x22, +0xC2,0x88,0x44,0x5D,0xEB,0x00,0xE9,0x6B,0x02,0x8B,0x5C,0x06,0x83,0xC3,0xFE,0xD1, +0xE3,0x8B,0x40,0x08,0x89,0x04,0x8B,0x1C,0xFF,0x77,0x06,0x6A,0x00,0x56,0xE8,0x85, +0xFC,0x83,0xC4,0x06,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x40,0x08,0x89,0x44,0x02, +0x8B,0x5C,0x02,0xFF,0x77,0x06,0x6A,0x01,0x56,0xE8,0x6A,0xFC,0x83,0xC4,0x06,0x6A, +0x01,0x56,0xE8,0xB1,0xFA,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x03,0x76,0x03,0xE9,0x1F, +0x02,0xD1,0xE3,0x2E,0xFF,0xA7,0xB3,0x6A,0x8B,0x5C,0x02,0x8B,0x47,0x04,0x89,0x44, +0x02,0x8B,0x5C,0x02,0x80,0x3F,0x44,0x75,0x0D,0x8B,0x5C,0x02,0x8A,0x47,0x01,0xB4, +0x00,0x3B,0x44,0x04,0x7D,0xE2,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x1C,0x03,0xD8,0x8B, +0x44,0x02,0x89,0x47,0x08,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x44,0x02,0x89,0x40, +0x08,0xE9,0xDE,0x01,0x8B,0x5C,0x02,0x8B,0x47,0x02,0x89,0x44,0x02,0x8B,0x5C,0x02, +0x80,0x3F,0x44,0x75,0x0D,0x8B,0x5C,0x02,0x8A,0x47,0x01,0xB4,0x00,0x3B,0x44,0x04, +0x7D,0xE2,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x1C,0x03,0xD8,0x8B,0x44,0x02,0x89,0x47, +0x08,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x44,0x02,0x89,0x40,0x08,0xE9,0xA2,0x01, +0xBF,0x01,0x00,0xE9,0x9C,0x01,0x8B,0x5C,0x02,0x8A,0x07,0xB4,0x00,0x89,0x46,0xF8, +0xB9,0x0C,0x00,0xBB,0x83,0x6A,0x2E,0x8B,0x07,0x3B,0x46,0xF8,0x74,0x07,0x43,0x43, +0xE2,0xF4,0xE9,0x77,0x01,0x2E,0xFF,0x67,0x18,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x5C, +0x02,0x03,0xD8,0x8B,0x47,0x08,0x8B,0x5C,0x06,0xFF,0x44,0x06,0xD1,0xE3,0x89,0x40, +0x08,0x8B,0x1C,0x80,0x7F,0x01,0x00,0x74,0x12,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B, +0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA,0x88,0x40,0x18,0xE9,0x40,0x01,0xFF,0x4C, +0x06,0xE9,0x3A,0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B,0x1C,0x8A,0x57,0x01,0xB6, +0x00,0x8B,0xDA,0x88,0x40,0x18,0xE9,0x25,0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B, +0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA,0x88,0x40,0x18,0xFF,0x4C,0x06,0xE9,0x0D, +0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B,0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA, +0x30,0x40,0x18,0xE9,0xF8,0x00,0xB8,0xF0,0x10,0x8B,0xF8,0x89,0x44,0x54,0x8A,0x44, +0x5F,0x88,0x44,0x5D,0xE9,0xE7,0x00,0x8A,0x44,0x1C,0x98,0x3D,0x02,0x00,0x74,0x07, +0x3D,0x03,0x00,0x74,0x02,0xEB,0x07,0xC7,0x46,0xFE,0x00,0x00,0xEB,0x2B,0x8A,0x44, +0x1C,0x98,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0x69,0x02,0x56,0xE8,0x6B,0x06,0x59,0x59, +0x6A,0x01,0x56,0xE8,0x40,0xF9,0x59,0x59,0x89,0x46,0xFE,0x83,0x7E,0xFE,0x00,0x74, +0x06,0x83,0x7E,0xFE,0x03,0x75,0xE9,0xEB,0x00,0x83,0x7E,0xFE,0x03,0x74,0x62,0x8A, +0x44,0x1C,0x98,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0x6D,0x02,0x56,0xE8,0x3A,0x06,0x59, +0x59,0x56,0xE8,0x6B,0x97,0x59,0x89,0x46,0xFC,0x8B,0x5E,0xFC,0x83,0xEB,0xFE,0x83, +0xFB,0x03,0x77,0x33,0xD1,0xE3,0x2E,0xFF,0xA7,0x7B,0x6A,0x68,0xAC,0x02,0x56,0xE8, +0x17,0x06,0x59,0x59,0xEB,0x23,0x68,0x8F,0x02,0x56,0xE8,0x0C,0x06,0x59,0x59,0xEB, +0x18,0x68,0x75,0x02,0x56,0xE8,0x01,0x06,0x59,0x59,0xEB,0x0D,0x68,0xC6,0x02,0x56, +0xE8,0xF6,0x05,0x59,0x59,0xEB,0x02,0xEB,0x00,0x6A,0x01,0x56,0xE8,0xC7,0xF8,0x59, +0x59,0xBF,0x01,0x00,0xEB,0x38,0x68,0xDD,0x02,0x56,0xE8,0xDC,0x05,0x59,0x59,0x6A, +0x01,0x56,0xE8,0xB1,0xF8,0x59,0x59,0xBF,0x01,0x00,0xEB,0x22,0xB8,0xD0,0x30,0x8B, +0xF8,0x89,0x44,0x54,0x8A,0x44,0x60,0x88,0x44,0x5D,0xEB,0x12,0xB8,0xE0,0x20,0x8B, +0xF8,0x89,0x44,0x54,0x8A,0x44,0x5E,0x88,0x44,0x5D,0xEB,0x02,0xEB,0x00,0xEB,0x02, +0xEB,0x00,0xEB,0x00,0xE9,0x41,0xFC,0x5F,0x5E,0xC9,0xC3,0xFB,0x69,0x06,0x6A,0x11, +0x6A,0x1C,0x6A,0x00,0x00,0x01,0x00,0x02,0x00,0x04,0x00,0x41,0x00,0x42,0x00,0x43, +0x00,0x44,0x00,0x80,0x00,0x81,0x00,0x82,0x00,0xFF,0x00,0xF9,0x68,0x36,0x6A,0x5C, +0x6A,0x87,0x69,0x34,0x69,0x76,0x69,0x4C,0x6A,0x49,0x69,0x34,0x69,0x61,0x69,0x49, +0x69,0x2E,0x69,0xD6,0x68,0x58,0x68,0x94,0x68,0xD0,0x68,0xD7,0x67,0xE2,0x67,0xF4, +0x67,0xD7,0x67,0x7F,0x67,0x8A,0x67,0x96,0x67,0x7F,0x67,0x00,0x00,0x01,0x00,0xF0, +0x10,0xE0,0x20,0xD0,0x30,0x09,0x68,0xD4,0x66,0xA5,0x67,0x05,0x67,0xF4,0x66,0xC8, +0x04,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x8A,0x44,0x59,0x98,0x89,0x46,0xFC,0x6A, +0x09,0x8B,0x46,0xFC,0x05,0x84,0x01,0x50,0xE8,0x93,0x08,0x59,0x59,0x8B,0xF8,0x8B, +0xC7,0x25,0x00,0xF0,0x3D,0x00,0x10,0x75,0x55,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xF0, +0x00,0x75,0x4B,0x8B,0xC7,0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x8B,0x44, +0x04,0x3B,0x46,0xFE,0x7D,0x05,0x33,0xC0,0xE9,0xEF,0x00,0x8B,0xC7,0x25,0x0F,0x00, +0xBA,0x0F,0x00,0x2B,0xD0,0x3B,0x56,0xFE,0x74,0x05,0x33,0xC0,0xE9,0xDB,0x00,0xC7, +0x44,0x02,0x04,0x09,0x8A,0x46,0xFE,0x88,0x44,0x5F,0x88,0x44,0x5D,0x8B,0x5E,0xFC, +0xD1,0xE3,0xC7,0x87,0xFC,0x08,0x04,0x09,0xB8,0xF0,0x10,0xE9,0xBC,0x00,0x8B,0xC7, +0x25,0x00,0xF0,0x3D,0x00,0x20,0x75,0x52,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xE0,0x00, +0x75,0x48,0x8B,0xC7,0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x83,0x7E,0xFE, +0x08,0x7E,0x05,0x33,0xC0,0xE9,0x92,0x00,0x8B,0xC7,0x25,0x0F,0x00,0xBA,0x0F,0x00, +0x2B,0xD0,0x3B,0x56,0xFE,0x74,0x05,0x33,0xC0,0xEB,0x7F,0x90,0xC7,0x44,0x02,0x0C, +0x09,0x8A,0x46,0xFE,0x88,0x44,0x5E,0x88,0x44,0x5D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7, +0x87,0xFC,0x08,0x0C,0x09,0xB8,0xE0,0x20,0xEB,0x60,0x8B,0xC7,0x25,0x00,0xF0,0x3D, +0x00,0x30,0x75,0x52,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xD0,0x00,0x75,0x48,0x8B,0xC7, +0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x8B,0x44,0x04,0x3B,0x46,0xFE,0x7D, +0x04,0x33,0xC0,0xEB,0x35,0x8B,0xC7,0x25,0x0F,0x00,0xBA,0x0F,0x00,0x2B,0xD0,0x3B, +0x56,0xFE,0x74,0x04,0x33,0xC0,0xEB,0x22,0xC7,0x44,0x02,0x14,0x09,0x8A,0x46,0xFE, +0x88,0x44,0x60,0x88,0x44,0x5D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7,0x87,0xFC,0x08,0x14, +0x09,0xB8,0xD0,0x30,0xEB,0x04,0x33,0xC0,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x06, +0x00,0x00,0x56,0x8B,0x76,0x04,0x6A,0x08,0x56,0xE8,0x35,0x04,0x59,0x59,0x8A,0x44, +0x58,0x04,0x80,0x50,0x56,0xE8,0x29,0x04,0x59,0x59,0x8B,0x44,0x54,0x3B,0x44,0x56, +0x75,0x0A,0x8A,0x44,0x5D,0x3A,0x44,0x2F,0x75,0x02,0xEB,0x64,0x8B,0x44,0x54,0x89, +0x44,0x56,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x88,0x44,0x2F,0x8A,0x44,0x5D,0xB4,0x00, +0xC1,0xE0,0x08,0x8B,0x54,0x54,0x0B,0xD0,0x8A,0x44,0x5D,0xB4,0x00,0xBB,0x0F,0x00, +0x2B,0xD8,0x0B,0xD3,0x89,0x56,0xFE,0x6A,0x10,0x8A,0x44,0x59,0x98,0x05,0x04,0x00, +0x99,0x05,0x40,0x01,0x83,0xD2,0x00,0x52,0x50,0xE8,0x54,0x08,0x83,0xC4,0x06,0x89, +0x56,0xFC,0x89,0x46,0xFA,0x8B,0x46,0xFE,0x09,0x46,0xFA,0x83,0x4E,0xFC,0x00,0x6A, +0x19,0xFF,0x76,0xFC,0xFF,0x76,0xFA,0xE8,0x73,0x07,0x83,0xC4,0x06,0xE8,0xFE,0x07, +0x5E,0xC9,0xC3,0xC8,0x1C,0x00,0x00,0x56,0x57,0x8B,0x5E,0x04,0x8A,0x47,0x59,0x98, +0x8B,0xF0,0x8B,0x5E,0x04,0x8A,0x47,0x5D,0xB4,0x00,0x89,0x46,0xE6,0x83,0x7E,0xE6, +0x00,0x7D,0x0A,0x8B,0x5E,0x04,0x8B,0x47,0x04,0x48,0x89,0x46,0xE6,0x8B,0x5E,0x04, +0x8B,0x47,0x04,0x3B,0x46,0xE6,0x7F,0x05,0xC7,0x46,0xE6,0x00,0x00,0x8B,0x5E,0x04, +0x8A,0x46,0xE6,0x88,0x47,0x5D,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47, +0x02,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47,0x03,0x30,0x8B,0xDE, +0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x47,0x02,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F, +0x61,0x02,0xC6,0x47,0x03,0x30,0x8B,0x46,0xE6,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00, +0x74,0x18,0x8B,0x46,0xFA,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0x88,0x57,0x03,0xBB,0x0A,0x00,0x8B,0x46,0xFA, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74,0x18,0x8B,0x46,0xFA, +0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F, +0x59,0x02,0x88,0x57,0x02,0x8B,0x46,0xE6,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74, +0x18,0x8B,0x46,0xFA,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE, +0xD1,0xE3,0x8B,0x9F,0x61,0x02,0x88,0x57,0x03,0xBB,0x0A,0x00,0x8B,0x46,0xFA,0x33, +0xD2,0xF7,0xF3,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74,0x18,0x8B,0x46,0xFA,0xBB, +0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61, +0x02,0x88,0x57,0x02,0x8B,0x5E,0xE6,0xD1,0xE3,0xFF,0xB7,0x12,0x02,0x6A,0x00,0xFF, +0x76,0x04,0xE8,0xD1,0xF6,0x83,0xC4,0x06,0x68,0xD3,0x0F,0x6A,0x01,0xFF,0x76,0x04, +0xE8,0xC3,0xF6,0x83,0xC4,0x06,0xFF,0x76,0xE6,0x56,0xE8,0x1F,0x93,0x59,0x59,0x89, +0x56,0xF2,0x89,0x46,0xF0,0xFF,0x76,0xE6,0x56,0xE8,0x32,0x93,0x59,0x59,0x89,0x56, +0xEE,0x89,0x46,0xEC,0x9C,0xFA,0xC4,0x5E,0xF0,0x26,0x8B,0x07,0x89,0x46,0xEA,0xC4, +0x5E,0xEC,0x26,0x8B,0x07,0x89,0x46,0xE8,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFE,0x9D, +0xC7,0x46,0xE4,0x01,0x00,0xE8,0x08,0xA1,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFC,0x8B, +0x46,0xFC,0x2B,0x46,0xFE,0x3D,0xE8,0x03,0x73,0x03,0xE9,0x80,0x01,0x9C,0xFA,0xBA, +0x50,0xFF,0xED,0x89,0x46,0xFC,0x8B,0x46,0xFC,0x2B,0x46,0xFE,0x89,0x46,0xF8,0xC4, +0x5E,0xF0,0x26,0x8B,0x07,0x2B,0x46,0xEA,0x89,0x46,0xF6,0xC4,0x5E,0xF0,0x26,0x8B, +0x07,0x89,0x46,0xEA,0xC4,0x5E,0xEC,0x26,0x8B,0x07,0x2B,0x46,0xE8,0x89,0x46,0xF4, +0xC4,0x5E,0xEC,0x26,0x8B,0x07,0x89,0x46,0xE8,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFE, +0x9D,0x81,0x7E,0xF8,0xE8,0x03,0x76,0x1C,0xFF,0x76,0xF8,0xFF,0x76,0xF6,0xE8,0x76, +0x01,0x59,0x59,0x89,0x46,0xF6,0xFF,0x76,0xF8,0xFF,0x76,0xF4,0xE8,0x68,0x01,0x59, +0x59,0x89,0x46,0xF4,0xBF,0x0E,0x00,0xEB,0x17,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59, +0x02,0xC6,0x01,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x01,0x20,0x47, +0x83,0xFF,0x11,0x76,0xE4,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47,0x0D, +0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x47,0x0D,0x30,0x83,0x7E,0xF6, +0x09,0x77,0x05,0xB8,0x0D,0x00,0xEB,0x26,0x83,0x7E,0xF6,0x63,0x77,0x05,0xB8,0x0E, +0x00,0xEB,0x1B,0x81,0x7E,0xF6,0xE7,0x03,0x77,0x05,0xB8,0x0F,0x00,0xEB,0x0F,0x81, +0x7E,0xF6,0x0F,0x27,0x77,0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x11,0x00,0x8B,0xF8, +0xEB,0x25,0x8B,0x46,0xF6,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0x88,0x11,0x4F,0xBB,0x0A,0x00,0x8B,0x46,0xF6, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xF6,0x83,0x7E,0xF6,0x00,0x75,0xD5,0x83,0x7E,0xF4, +0x09,0x77,0x05,0xB8,0x0D,0x00,0xEB,0x26,0x83,0x7E,0xF4,0x63,0x77,0x05,0xB8,0x0E, +0x00,0xEB,0x1B,0x81,0x7E,0xF4,0xE7,0x03,0x77,0x05,0xB8,0x0F,0x00,0xEB,0x0F,0x81, +0x7E,0xF4,0x0F,0x27,0x77,0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x11,0x00,0x8B,0xF8, +0xEB,0x25,0x8B,0x46,0xF4,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0x88,0x11,0x4F,0xBB,0x0A,0x00,0x8B,0x46,0xF4, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xF4,0x83,0x7E,0xF4,0x00,0x75,0xD5,0x8B,0xDE,0xD1, +0xE3,0xFF,0xB7,0x59,0x02,0xFF,0x76,0x04,0xE8,0x6E,0x00,0x59,0x59,0x8B,0xDE,0xD1, +0xE3,0xFF,0xB7,0x61,0x02,0xFF,0x76,0x04,0xE8,0x5E,0x00,0x59,0x59,0x6A,0x00,0xFF, +0x76,0x04,0xE8,0x31,0xF3,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x04,0x77,0x1F,0xD1,0xE3, +0x2E,0xFF,0xA7,0xFD,0x6F,0xEB,0x22,0xC7,0x46,0xE4,0x00,0x00,0xFF,0x4E,0xE6,0xEB, +0x0C,0xC7,0x46,0xE4,0x00,0x00,0xFF,0x46,0xE6,0xEB,0x02,0xEB,0x00,0x83,0x7E,0xE4, +0x00,0x74,0x03,0xE9,0x2A,0xFE,0xE9,0xD4,0xFC,0x5F,0x5E,0xC9,0xC3,0xD5,0x6F,0xD7, +0x6F,0xE1,0x6F,0xD5,0x6F,0xEB,0x6F,0x55,0x8B,0xEC,0x8B,0x46,0x04,0xB9,0xE8,0x03, +0xF7,0xE1,0x8B,0x4E,0x06,0xF7,0xF1,0x5D,0xC3,0x55,0x8B,0xEC,0x56,0x8B,0x76,0x06, +0xEB,0x0E,0x8B,0xDE,0x46,0x8A,0x07,0x50,0xFF,0x76,0x04,0xE8,0x33,0x00,0x59,0x59, +0x80,0x3C,0x00,0x75,0xED,0xEB,0x00,0x5E,0x5D,0xC3,0x55,0x8B,0xEC,0x56,0x8B,0x76, +0x06,0xEB,0x14,0x8B,0xDE,0x46,0x8A,0x07,0x50,0xFF,0x76,0x04,0xE8,0x45,0x00,0x59, +0x59,0x0B,0xC0,0x74,0x02,0xEB,0x07,0x80,0x3C,0x00,0x75,0xE7,0xEB,0x00,0x5E,0x5D, +0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE, +0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x02,0x74, +0x06,0x9D,0xE8,0xAB,0x9E,0xEB,0xE9,0xBA,0x00,0x00,0x8A,0x46,0x06,0xEE,0x9D,0xEB, +0x00,0x5E,0xC9,0xC3,0xC8,0x04,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98, +0x89,0x46,0xFE,0xE8,0x00,0xA2,0x89,0x46,0xFC,0xE8,0xFA,0xA1,0x2B,0x46,0xFC,0x3D, +0xB8,0x0B,0x76,0x05,0xB8,0x01,0x00,0xEB,0x23,0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE, +0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x02,0x74,0x06,0x9D,0xE8,0x62,0x9E,0xEB,0xD9, +0xBA,0x00,0x00,0x8A,0x46,0x06,0xEE,0x9D,0x33,0xC0,0xEB,0x00,0x5E,0xC9,0xC3,0xC8, +0x04,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x83,0x7E,0x06,0x00,0x74,0x07,0x56,0xE8, +0x03,0x01,0x59,0xEB,0x05,0x56,0xE8,0xA2,0x00,0x59,0x88,0x46,0xFF,0x80,0x7E,0xFF, +0x08,0x77,0x06,0x8A,0x46,0xFF,0xE9,0x84,0x00,0x80,0x7E,0xFF,0x0F,0x76,0x03,0xEB, +0x79,0x90,0x8A,0x46,0xFF,0xB4,0x00,0x2D,0x0A,0x00,0x8B,0xD8,0x83,0xFB,0x04,0x77, +0x67,0xD1,0xE3,0x2E,0xFF,0xA7,0x91,0x71,0xB0,0x00,0xEB,0x61,0x56,0xE8,0x6B,0x00, +0x59,0xB4,0x00,0x25,0x0F,0x00,0x89,0x46,0xFC,0x56,0xE8,0x5E,0x00,0x59,0xB4,0x00, +0x8B,0xF8,0x56,0xE8,0x55,0x00,0x59,0xB4,0x00,0xC1,0xE0,0x08,0x8B,0xD7,0x03,0xD0, +0x8B,0xFA,0x8B,0x5E,0xFC,0xD1,0xE3,0x89,0x78,0x30,0xEB,0x2E,0x56,0xE8,0x3B,0x00, +0x59,0x88,0x44,0x5B,0xEB,0x24,0x56,0xE8,0x31,0x00,0x59,0x88,0x44,0x50,0x56,0xE8, +0x29,0x00,0x59,0x88,0x44,0x51,0x56,0xE8,0x21,0x00,0x59,0x88,0x44,0x52,0x56,0xE8, +0x19,0x00,0x59,0x88,0x44,0x53,0xEB,0x02,0xEB,0x00,0xE9,0x5B,0xFF,0x5F,0x5E,0xC9, +0xC3,0x28,0x71,0x88,0x71,0x2C,0x71,0x5C,0x71,0x66,0x71,0xC8,0x04,0x00,0x00,0x56, +0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE,0x9C,0xFA,0x8A,0x46,0xFE,0xBA, +0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x01,0x75,0x06,0x9D,0xE8,0x71,0x9D,0xEB, +0xE9,0xBA,0x00,0x00,0xEC,0x88,0x46,0xFD,0x9D,0x8A,0x46,0xFD,0xEB,0x00,0x5E,0xC9, +0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE, +0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0x32,0xE4,0x24, +0x01,0x9D,0x5E,0xC9,0xC3,0xC8,0x06,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A, +0x98,0x89,0x46,0xFE,0xE8,0x9F,0xA0,0x89,0x46,0xFA,0xE8,0x99,0xA0,0x2B,0x46,0xFA, +0x3D,0xB8,0x0B,0x76,0x04,0xB0,0x08,0xEB,0x24,0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE, +0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x01,0x75,0x06,0x9D,0xE8,0x02,0x9D,0xEB,0xDA, +0xBA,0x00,0x00,0xEC,0x88,0x46,0xFD,0x9D,0x8A,0x46,0xFD,0xEB,0x00,0x5E,0xC9,0xC3, +0x55,0x8B,0xEC,0x56,0x8B,0x56,0x04,0x8A,0x46,0x06,0xEE,0x33,0xF6,0xEB,0x03,0x50, +0x58,0x46,0x83,0xFE,0x14,0x7C,0xF8,0x5E,0x5D,0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B, +0x56,0x04,0xEC,0x88,0x46,0xFF,0x33,0xF6,0xEB,0x03,0x50,0x58,0x46,0x83,0xFE,0x14, +0x7C,0xF8,0x8A,0x46,0xFF,0xEB,0x00,0x5E,0xC9,0xC3,0xC8,0x02,0x00,0x00,0x56,0x57, +0x8B,0x76,0x04,0x83,0x3E,0xB0,0x0B,0x00,0x75,0x1F,0xBA,0x88,0x01,0xB0,0x00,0xEE, +0xBA,0x86,0x01,0xB0,0x00,0xEE,0x6A,0x09,0x6A,0x00,0x68,0x30,0x01,0xE8,0x7D,0x01, +0x83,0xC4,0x06,0xC7,0x06,0xB0,0x0B,0x01,0x00,0x6A,0x09,0x8B,0xC6,0x05,0x80,0x01, +0x50,0xE8,0xDA,0x00,0x59,0x59,0x8B,0xF8,0x8B,0xC7,0xC1,0xE8,0x0C,0x25,0x0F,0x00, +0x89,0x46,0xFE,0x8B,0xC7,0xC1,0xE8,0x08,0x25,0x0F,0x00,0x8B,0x56,0xFE,0x83,0xF2, +0x0C,0x3B,0xC2,0x75,0x21,0x8B,0xC7,0xC1,0xE8,0x04,0x25,0x0F,0x00,0x8B,0x56,0xFE, +0x83,0xF2,0x06,0x3B,0xC2,0x75,0x0F,0x8B,0xC7,0x25,0x0F,0x00,0x8B,0x56,0xFE,0x83, +0xF2,0x09,0x3B,0xC2,0x74,0x0D,0x6A,0x07,0x56,0xE8,0x38,0x00,0x59,0x59,0xC7,0x46, +0xFE,0x07,0x00,0x8A,0x46,0xFE,0x04,0x80,0xA2,0x33,0x02,0x8B,0xC6,0xBA,0x62,0x00, +0xF7,0xEA,0x8A,0x56,0xFE,0x8B,0xD8,0x88,0x97,0x6C,0x00,0x68,0x32,0x02,0x8B,0xC6, +0xBA,0x62,0x00,0xF7,0xEA,0x05,0x14,0x00,0x50,0xE8,0x0E,0xFD,0x59,0x59,0xEB,0x00, +0x5F,0x5E,0xC9,0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x06,0x83,0xE6,0x0F,0x8B, +0xC6,0xC1,0xE0,0x0C,0x8B,0xD6,0x83,0xF2,0x0C,0xC1,0xE2,0x08,0x0B,0xC2,0x8B,0xD6, +0x83,0xF2,0x06,0xC1,0xE2,0x04,0x0B,0xC2,0x8B,0xD6,0x83,0xF2,0x09,0x0B,0xC2,0x89, +0x46,0xFE,0x6A,0x19,0x6A,0x10,0x8B,0x46,0x04,0x99,0x05,0x40,0x01,0x83,0xD2,0x00, +0x52,0x50,0xE8,0x6B,0x01,0x83,0xC4,0x06,0x0B,0x46,0xFE,0x83,0xCA,0x00,0x52,0x50, +0xE8,0x9A,0x00,0x83,0xC4,0x06,0xE8,0x25,0x01,0xEB,0x00,0x5E,0xC9,0xC3,0x55,0x8B, +0xEC,0x56,0x57,0x33,0xFF,0x6A,0x01,0x68,0x86,0x01,0xE8,0xA3,0xFE,0x59,0x59,0xB1, +0x10,0x2A,0x4E,0x06,0xD3,0x66,0x04,0x33,0xF6,0xEB,0x2E,0x81,0x7E,0x04,0x00,0x80, +0x72,0x04,0xB0,0x01,0xEB,0x02,0xB0,0x00,0x50,0x68,0x88,0x01,0xE8,0x81,0xFE,0x59, +0x59,0x6A,0x03,0x68,0x86,0x01,0xE8,0x77,0xFE,0x59,0x59,0x6A,0x01,0x68,0x86,0x01, +0xE8,0x6D,0xFE,0x59,0x59,0xD1,0x66,0x04,0x46,0x3B,0x76,0x06,0x7C,0xCD,0x33,0xF6, +0xEB,0x24,0xD1,0xE7,0x6A,0x03,0x68,0x86,0x01,0xE8,0x54,0xFE,0x59,0x59,0x6A,0x01, +0x68,0x86,0x01,0xE8,0x4A,0xFE,0x59,0x59,0x68,0x88,0x01,0xE8,0x5C,0xFE,0x59,0x98, +0x25,0x01,0x00,0x0B,0xF8,0x46,0x83,0xFE,0x10,0x7C,0xD7,0x6A,0x00,0x68,0x86,0x01, +0xE8,0x2D,0xFE,0x59,0x59,0x8B,0xC7,0xEB,0x00,0x5F,0x5E,0x5D,0xC3,0x55,0x8B,0xEC, +0x56,0x57,0x8B,0x7E,0x08,0x6A,0x01,0x68,0x86,0x01,0xE8,0x13,0xFE,0x59,0x59,0xB8, +0x20,0x00,0x2B,0xC7,0x50,0xFF,0x76,0x06,0xFF,0x76,0x04,0xE8,0xA2,0x00,0x83,0xC4, +0x06,0x89,0x56,0x06,0x89,0x46,0x04,0x33,0xF6,0xEB,0x47,0x81,0x7E,0x06,0x00,0x80, +0x72,0x0C,0x75,0x06,0x83,0x7E,0x04,0x00,0x72,0x04,0xB0,0x01,0xEB,0x02,0xB0,0x00, +0x50,0x68,0x88,0x01,0xE8,0xD9,0xFD,0x59,0x59,0x6A,0x03,0x68,0x86,0x01,0xE8,0xCF, +0xFD,0x59,0x59,0x6A,0x01,0x68,0x86,0x01,0xE8,0xC5,0xFD,0x59,0x59,0x6A,0x01,0xFF, +0x76,0x06,0xFF,0x76,0x04,0xE8,0x58,0x00,0x83,0xC4,0x06,0x89,0x56,0x06,0x89,0x46, +0x04,0x46,0x3B,0xF7,0x7C,0xB5,0x6A,0x00,0x68,0x86,0x01,0xE8,0xA2,0xFD,0x59,0x59, +0x6A,0x00,0x68,0x86,0x01,0xE8,0x98,0xFD,0x59,0x59,0x5F,0x5E,0x5D,0xC3,0x55,0x8B, +0xEC,0x56,0x6A,0x01,0x68,0x86,0x01,0xE8,0x86,0xFD,0x59,0x59,0x33,0xF6,0xEB,0x00, +0x68,0x88,0x01,0xE8,0x94,0xFD,0x59,0xA8,0x01,0x75,0x08,0x8B,0xC6,0x46,0x3D,0x64, +0x00,0x7C,0xED,0x6A,0x00,0x68,0x86,0x01,0xE8,0x65,0xFD,0x59,0x59,0x5E,0x5D,0xC3, +0xC8,0x04,0x00,0x00,0x8B,0x46,0x04,0x8B,0x56,0x06,0x8B,0x4E,0x08,0xE3,0x06,0xD1, +0xE0,0xD1,0xD2,0xE2,0xFA,0x89,0x46,0xFC,0x89,0x56,0xFE,0x8B,0x56,0xFE,0x8B,0x46, +0xFC,0xEB,0x00,0xC9,0xC3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x50,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x4D,0x65,0x6E,0x75,0x00,0x42,0x65, +0x67,0x69,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x6F,0x72,0x74, +0x20,0x30,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x00,0x50,0x6F,0x72,0x74,0x20,0x32, +0x00,0x50,0x6F,0x72,0x74,0x20,0x33,0x00,0x50,0x6F,0x72,0x74,0x20,0x34,0x00,0x50, +0x6F,0x72,0x74,0x20,0x35,0x00,0x50,0x6F,0x72,0x74,0x20,0x36,0x00,0x50,0x6F,0x72, +0x74,0x20,0x37,0x00,0x50,0x6F,0x72,0x74,0x20,0x38,0x00,0x50,0x6F,0x72,0x74,0x20, +0x39,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x30,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x31,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x32,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x33,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x34,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x35,0x00,0x9C,0x01,0xA3,0x01,0xAA,0x01,0xB1,0x01,0xB8,0x01,0xBF,0x01,0xC6,0x01, +0xCD,0x01,0xD4,0x01,0xDB,0x01,0xE2,0x01,0xEA,0x01,0xF2,0x01,0xFA,0x01,0x02,0x02, +0x0A,0x02,0x08,0x00,0x00,0x07,0x81,0x00,0x03,0x80,0x80,0x80,0x9F,0x91,0x95,0x91, +0x9F,0x00,0x03,0x81,0x84,0x8E,0x95,0x84,0x84,0x84,0x84,0x00,0x03,0x82,0x84,0x84, +0x84,0x84,0x95,0x8E,0x84,0x00,0x04,0x88,0x00,0xB2,0x0B,0xC6,0x0B,0xDA,0x0B,0xEE, +0x0B,0x02,0x0C,0x16,0x0C,0x2A,0x0C,0x3E,0x0C,0x52,0x0C,0x77,0x0C,0x9C,0x0C,0xBE, +0x0C,0xE0,0x0C,0x02,0x0D,0x01,0x80,0x20,0x54,0x65,0x73,0x74,0x20,0x50,0x61,0x73, +0x73,0x65,0x64,0x20,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01, +0x80,0x20,0x4D,0x69,0x73,0x73,0x69,0x6E,0x67,0x20,0x52,0x78,0x20,0x44,0x61,0x74, +0x61,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20,0x42, +0x61,0x64,0x20,0x52,0x78,0x20,0x44,0x61,0x74,0x61,0x20,0x1F,0x20,0x50,0x72,0x65, +0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20,0x58,0x6D,0x74,0x72,0x20,0x42,0x75, +0x73,0x79,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20, +0x6E,0x6F,0x74,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x6C,0x79,0x1F,0x20,0x20, +0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x65,0x64,0x02,0x00,0x24,0x0D,0x2F, +0x0D,0x3A,0x0D,0x45,0x0D,0x50,0x0D,0x5B,0x0D,0x66,0x0D,0x71,0x0D,0x7C,0x0D,0x87, +0x0D,0x92,0x0D,0x9D,0x0D,0xA8,0x0D,0xB3,0x0D,0xBE,0x0D,0xC9,0x0D,0x53,0x80,0x2C, +0x32,0x54,0x44,0x20,0x53,0x86,0x2C,0x33,0x44,0x54,0x52,0x20,0x53,0x82,0x2C,0x33, +0x52,0x54,0x53,0x20,0x1F,0x53,0x81,0x2C,0x32,0x52,0x44,0x20,0x53,0x85,0x2C,0x32, +0x43,0x44,0x20,0x53,0x83,0x2C,0x33,0x43,0x54,0x53,0x20,0x53,0x84,0x2C,0x33,0x44, +0x53,0x52,0x20,0x53,0x87,0x2C,0x32,0x52,0x49,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x30,0x1F,0x27,0x53,0x85, +0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89, +0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x53,0x52, +0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x31,0x1F,0x27,0x53,0x84,0x2E,0x31,0x81, +0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C, +0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x43,0x54,0x53,0x20,0x2D,0x20, +0x70,0x69,0x6E,0x20,0x34,0x1F,0x27,0x53,0x83,0x2E,0x31,0x81,0x82,0x63,0x90,0x80, +0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27, +0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32, +0x32,0x1F,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84, +0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x2F,0x38,0x1F, +0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x27,0x53,0x82,0x2E, +0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F,0x27,0x53,0x81,0x2E,0x30,0x53,0x4D,0x81, +0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C, +0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78,0x44,0x20,0x2D,0x20, +0x70,0x69,0x6E,0x20,0x33,0x1F,0x27,0x53,0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63, +0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E, +0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69, +0x6E,0x20,0x35,0x1F,0x27,0x53,0x85,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F, +0x27,0x53,0x84,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x43,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x1F,0x27,0x53,0x83,0x2E, +0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D, +0x20,0x28,0x6E,0x2E,0x63,0x2E,0x29,0x1F,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63, +0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E, +0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69, +0x6E,0x20,0x32,0x1F,0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x37,0x1F, +0x27,0x53,0x82,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x52,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x1F,0x27,0x53,0x81,0x2E, +0x30,0x53,0x4D,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88, +0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78, +0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x33,0x1F,0x27,0x53,0x80,0x2E,0x30,0x53, +0x4D,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x43,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x85,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x84,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x43,0x54,0x53,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x83,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x28,0x6E,0x2E,0x63,0x2E,0x29,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x54,0x52,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x86,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x37,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x82,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x81,0x2E, +0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27, +0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20, +0x33,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63, +0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x30,0x1F,0x20,0x20,0x20, +0x20,0x27,0x53,0x85,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85, +0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70, +0x69,0x6E,0x20,0x31,0x31,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x84,0x2E,0x31,0x81, +0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x43,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x34,0x1F,0x20,0x20, +0x20,0x20,0x27,0x53,0x83,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84, +0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x70, +0x69,0x6E,0x20,0x32,0x32,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x87,0x2E,0x31,0x81, +0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x2F,0x38,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x82,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x81,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80, +0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78, +0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x33,0x1F,0x20,0x20,0x20,0x20,0x27,0x53, +0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x27,0x02,0x00,0x68,0x04,0x96,0x04,0xB6,0x03,0x3C,0x04,0x0E,0x04,0x89,0x03, +0x5C,0x03,0xE2,0x03,0x60,0x08,0x8A,0x08,0xBE,0x07,0x38,0x08,0x0E,0x08,0x95,0x07, +0x6C,0x07,0xE6,0x07,0x1C,0x05,0x74,0x05,0xFA,0x05,0xC4,0x04,0xF0,0x04,0xCC,0x05, +0xA0,0x05,0x48,0x05,0x78,0x06,0xC8,0x06,0x42,0x07,0x28,0x06,0x50,0x06,0x18,0x07, +0xF0,0x06,0xA0,0x06,0x00,0x00,0xF4,0x08,0xF4,0x08,0xD4,0x0D,0x04,0x09,0x04,0x09, +0x04,0x09,0x04,0x09,0x42,0x00,0x0C,0x09,0x1C,0x09,0xE5,0x0D,0x02,0x00,0x14,0x09, +0x04,0x09,0xF4,0x0D,0x43,0x00,0x1C,0x09,0x0C,0x09,0x05,0x0E,0x00,0x04,0x04,0x09, +0x14,0x09,0x12,0x0E,0x2C,0x09,0x2C,0x09,0x2C,0x09,0x2C,0x09,0x00,0x00,0x3C,0x09, +0x6C,0x09,0x1E,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x01,0x4C,0x09, +0x2C,0x09,0x2D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x02,0x5C,0x09, +0x3C,0x09,0x3D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x03,0x6C,0x09, +0x4C,0x09,0x4D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0xFF,0x00,0x2C,0x09, +0x5C,0x09,0x00,0x00,0x00,0x05,0x84,0x09,0xEC,0x09,0x5E,0x0E,0xF4,0x09,0xF4,0x09, +0xF4,0x09,0xF4,0x09,0x00,0x06,0x94,0x09,0x74,0x09,0x68,0x0E,0xAC,0x0A,0xAC,0x0A, +0xAC,0x0A,0xAC,0x0A,0x00,0x07,0xA4,0x09,0x84,0x09,0x72,0x0E,0xBC,0x0A,0xBC,0x0A, +0xBC,0x0A,0xBC,0x0A,0x00,0x08,0xB4,0x09,0x94,0x09,0x7C,0x0E,0xD4,0x0A,0xD4,0x0A, +0xD4,0x0A,0xD4,0x0A,0x00,0x0B,0xC4,0x09,0xA4,0x09,0x83,0x0E,0xFC,0x0A,0xFC,0x0A, +0xFC,0x0A,0xFC,0x0A,0x00,0x0C,0xD4,0x09,0xB4,0x09,0x90,0x0E,0x14,0x0B,0x14,0x0B, +0x14,0x0B,0x14,0x0B,0x00,0x02,0xE4,0x09,0xC4,0x09,0xA0,0x0E,0x2C,0x0B,0x2C,0x0B, +0x2C,0x0B,0x2C,0x0B,0x04,0x00,0xEC,0x09,0xD4,0x09,0x0E,0x00,0xFF,0x00,0x74,0x09, +0xE4,0x09,0x00,0x00,0x82,0x01,0xFC,0x09,0xA4,0x0A,0xAC,0x0E,0x82,0x02,0x04,0x0A, +0xF4,0x09,0xAF,0x0E,0x82,0x03,0x0C,0x0A,0xFC,0x09,0xB2,0x0E,0x82,0x04,0x14,0x0A, +0x04,0x0A,0xB6,0x0E,0x82,0x05,0x1C,0x0A,0x0C,0x0A,0xBC,0x0E,0x82,0x06,0x24,0x0A, +0x14,0x0A,0xC0,0x0E,0x82,0x07,0x2C,0x0A,0x1C,0x0A,0xC4,0x0E,0x82,0x08,0x34,0x0A, +0x24,0x0A,0xC8,0x0E,0x82,0x09,0x3C,0x0A,0x2C,0x0A,0xCC,0x0E,0x82,0x0A,0x44,0x0A, +0x34,0x0A,0xD1,0x0E,0x82,0x10,0x4C,0x0A,0x3C,0x0A,0xD6,0x0E,0x82,0x0B,0x54,0x0A, +0x44,0x0A,0xDB,0x0E,0x82,0x11,0x5C,0x0A,0x4C,0x0A,0xE0,0x0E,0x82,0x0C,0x64,0x0A, +0x54,0x0A,0xE5,0x0E,0x82,0x12,0x6C,0x0A,0x5C,0x0A,0xEA,0x0E,0x82,0x0D,0x74,0x0A, +0x64,0x0A,0xEF,0x0E,0x82,0x0E,0x7C,0x0A,0x6C,0x0A,0xF4,0x0E,0x82,0x0F,0x84,0x0A, +0x74,0x0A,0xFB,0x0E,0x82,0x13,0x8C,0x0A,0x7C,0x0A,0x02,0x0F,0x82,0x14,0x94,0x0A, +0x84,0x0A,0x09,0x0F,0x82,0x15,0x9C,0x0A,0x8C,0x0A,0x10,0x0F,0x82,0x16,0xA4,0x0A, +0x94,0x0A,0x17,0x0F,0x82,0x17,0xF4,0x09,0x9C,0x0A,0x1E,0x0F,0x82,0x02,0xB4,0x0A, +0xB4,0x0A,0x26,0x0F,0x82,0x03,0xAC,0x0A,0xAC,0x0A,0x2D,0x0F,0x82,0x00,0xC4,0x0A, +0xCC,0x0A,0x34,0x0F,0x82,0x01,0xCC,0x0A,0xBC,0x0A,0x3F,0x0F,0x82,0x02,0xBC,0x0A, +0xC4,0x0A,0x4D,0x0F,0x82,0x00,0xDC,0x0A,0xF4,0x0A,0x59,0x0F,0x82,0x01,0xE4,0x0A, +0xD4,0x0A,0x63,0x0F,0x82,0x02,0xEC,0x0A,0xDC,0x0A,0x6E,0x0F,0x82,0x03,0xF4,0x0A, +0xE4,0x0A,0x7A,0x0F,0x82,0x04,0xD4,0x0A,0xEC,0x0A,0x87,0x0F,0x82,0x00,0x04,0x0B, +0x0C,0x0B,0x93,0x0F,0x82,0x01,0x0C,0x0B,0xFC,0x0A,0x9B,0x0F,0x82,0x02,0xFC,0x0A, +0x04,0x0B,0xA7,0x0F,0x82,0x00,0x1C,0x0B,0x24,0x0B,0xB0,0x0F,0x82,0x01,0x24,0x0B, +0x14,0x0B,0xB5,0x0F,0x82,0x02,0x14,0x0B,0x1C,0x0B,0xBE,0x0F,0x44,0x00,0x34,0x0B, +0xA4,0x0B,0x9C,0x01,0x44,0x01,0x3C,0x0B,0x2C,0x0B,0xA3,0x01,0x44,0x02,0x44,0x0B, +0x34,0x0B,0xAA,0x01,0x44,0x03,0x4C,0x0B,0x3C,0x0B,0xB1,0x01,0x44,0x04,0x54,0x0B, +0x44,0x0B,0xB8,0x01,0x44,0x05,0x5C,0x0B,0x4C,0x0B,0xBF,0x01,0x44,0x06,0x64,0x0B, +0x54,0x0B,0xC6,0x01,0x44,0x07,0x6C,0x0B,0x5C,0x0B,0xCD,0x01,0x44,0x08,0x74,0x0B, +0x64,0x0B,0xD4,0x01,0x44,0x09,0x7C,0x0B,0x6C,0x0B,0xDB,0x01,0x44,0x0A,0x84,0x0B, +0x74,0x0B,0xE2,0x01,0x44,0x0B,0x8C,0x0B,0x7C,0x0B,0xEA,0x01,0x44,0x0C,0x94,0x0B, +0x84,0x0B,0xF2,0x01,0x44,0x0D,0x9C,0x0B,0x8C,0x0B,0xFA,0x01,0x44,0x0E,0xA4,0x0B, +0x94,0x0B,0x02,0x02,0x44,0x0F,0x2C,0x0B,0x9C,0x0B,0x0A,0x02,0x17,0x1F,0x0F,0x2F, +0x00,0x00,0x01,0x80,0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A, +0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80,0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63, +0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80,0x78,0x78,0x3A,0x20, +0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80, +0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A, +0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A, +0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63, +0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20, +0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0, +0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A, +0x02,0x00,0x01,0x80,0x49,0x6E,0x73,0x74,0x61,0x6C,0x6C,0x20,0x4C,0x6F,0x6F,0x70, +0x62,0x61,0x63,0x6B,0x1F,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x20,0x74,0x6F,0x20, +0x73,0x74,0x61,0x72,0x74,0x02,0x00,0x01,0x80,0x20,0x43,0x61,0x62,0x6C,0x65,0x20, +0x74,0x6F,0x20,0x52,0x65,0x6D,0x6F,0x74,0x65,0x1F,0x50,0x72,0x65,0x73,0x73,0x20, +0x80,0x20,0x74,0x6F,0x20,0x73,0x74,0x61,0x72,0x74,0x02,0x00,0x01,0x80,0x20,0x4C, +0x6F,0x63,0x61,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x20,0x1F,0x20, +0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E,0x02,0x00,0x01,0x80, +0x52,0x65,0x6D,0x6F,0x74,0x65,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x20, +0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E,0x02,0x00, +0x01,0x80,0x20,0x49,0x6E,0x74,0x72,0x6E,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61, +0x63,0x6B,0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E, +0x02,0x00,0x01,0x80,0x54,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,0x50,0x61,0x74, +0x74,0x65,0x72,0x6E,0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E, +0x2E,0x2E,0x02,0x00,0x01,0x80,0x20,0x20,0x30,0x3A,0x20,0x27,0x43,0x80,0x00,0x01, +0x80,0x20,0x20,0x31,0x3A,0x20,0x27,0x43,0x81,0x00,0x01,0x80,0x20,0x20,0x32,0x3A, +0x20,0x27,0x43,0x82,0x00,0x01,0x80,0x20,0x20,0x33,0x3A,0x20,0x27,0x43,0x83,0x00, +0x01,0x80,0x20,0x20,0x34,0x3A,0x20,0x27,0x43,0x84,0x00,0x01,0x80,0x20,0x20,0x35, +0x3A,0x20,0x27,0x43,0x85,0x00,0x01,0x80,0x20,0x20,0x36,0x3A,0x20,0x27,0x43,0x86, +0x00,0x01,0x80,0x20,0x20,0x37,0x3A,0x20,0x27,0x43,0x87,0x00,0x01,0x80,0x20,0x20, +0x38,0x3A,0x20,0x27,0x43,0x88,0x00,0x01,0x80,0x20,0x20,0x39,0x3A,0x20,0x27,0x43, +0x89,0x00,0x01,0x80,0x20,0x31,0x30,0x3A,0x20,0x27,0x43,0x8A,0x00,0x01,0x80,0x20, +0x31,0x31,0x3A,0x20,0x27,0x43,0x8B,0x00,0x01,0x80,0x20,0x31,0x32,0x3A,0x20,0x27, +0x43,0x8C,0x00,0x01,0x80,0x20,0x31,0x33,0x3A,0x20,0x27,0x43,0x8D,0x00,0x01,0x80, +0x20,0x31,0x34,0x3A,0x20,0x27,0x43,0x8E,0x00,0x01,0x80,0x20,0x31,0x35,0x3A,0x20, +0x27,0x43,0x8F,0x00,0x2A,0x2A,0x20,0x4D,0x61,0x69,0x6E,0x20,0x20,0x4D,0x65,0x6E, +0x75,0x20,0x2A,0x2A,0x00,0x4D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x20,0x50, +0x6F,0x72,0x74,0x00,0x4D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x20,0x53,0x69, +0x67,0x6E,0x61,0x6C,0x00,0x45,0x73,0x74,0x69,0x6D,0x61,0x74,0x65,0x20,0x43,0x50, +0x53,0x00,0x44,0x69,0x61,0x67,0x6E,0x6F,0x73,0x74,0x69,0x63,0x73,0x00,0x4C,0x6F, +0x63,0x61,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x52,0x65,0x6D, +0x6F,0x74,0x65,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x49,0x6E,0x74, +0x72,0x6E,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x54,0x72,0x61, +0x6E,0x73,0x6D,0x69,0x74,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x00,0x42,0x61, +0x75,0x64,0x20,0x52,0x61,0x74,0x65,0x00,0x44,0x61,0x74,0x61,0x20,0x42,0x69,0x74, +0x73,0x00,0x53,0x74,0x6F,0x70,0x20,0x42,0x69,0x74,0x73,0x00,0x50,0x61,0x72,0x69, +0x74,0x79,0x00,0x44,0x61,0x74,0x61,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x00, +0x54,0x78,0x20,0x46,0x6C,0x6F,0x77,0x20,0x43,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x00, +0x50,0x6F,0x72,0x74,0x20,0x4E,0x75,0x6D,0x62,0x65,0x72,0x00,0x35,0x30,0x00,0x37, +0x35,0x00,0x31,0x31,0x30,0x00,0x31,0x33,0x34,0x2E,0x35,0x00,0x31,0x35,0x30,0x00, +0x32,0x30,0x30,0x00,0x33,0x30,0x30,0x00,0x36,0x30,0x30,0x00,0x31,0x32,0x30,0x30, +0x00,0x31,0x38,0x30,0x30,0x00,0x32,0x30,0x30,0x30,0x00,0x32,0x34,0x30,0x30,0x00, +0x33,0x36,0x30,0x30,0x00,0x34,0x38,0x30,0x30,0x00,0x37,0x32,0x30,0x30,0x00,0x39, +0x36,0x30,0x30,0x00,0x31,0x39,0x2C,0x32,0x30,0x30,0x00,0x33,0x38,0x2C,0x34,0x30, +0x30,0x00,0x35,0x36,0x2C,0x30,0x30,0x30,0x00,0x35,0x37,0x2C,0x36,0x30,0x30,0x00, +0x36,0x34,0x2C,0x30,0x30,0x30,0x00,0x37,0x36,0x2C,0x38,0x30,0x30,0x00,0x31,0x31, +0x35,0x2C,0x32,0x30,0x30,0x00,0x37,0x20,0x62,0x69,0x74,0x73,0x00,0x38,0x20,0x62, +0x69,0x74,0x73,0x00,0x31,0x20,0x73,0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x00,0x31, +0x2E,0x35,0x20,0x73,0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x73,0x00,0x32,0x20,0x73, +0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x73,0x00,0x6E,0x6F,0x20,0x70,0x61,0x72,0x69, +0x74,0x79,0x00,0x6F,0x64,0x64,0x20,0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x65,0x76, +0x65,0x6E,0x20,0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x73,0x70,0x61,0x63,0x65,0x20, +0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x6D,0x61,0x72,0x6B,0x20,0x70,0x61,0x72,0x69, +0x74,0x79,0x00,0x43,0x6F,0x6C,0x75,0x6D,0x6E,0x73,0x00,0x42,0x61,0x72,0x62,0x65, +0x72,0x20,0x50,0x6F,0x6C,0x65,0x00,0x55,0x55,0x55,0x55,0x55,0x2E,0x2E,0x2E,0x00, +0x4E,0x6F,0x6E,0x65,0x00,0x58,0x6F,0x6E,0x2F,0x58,0x6F,0x66,0x66,0x00,0x43,0x54, +0x53,0x00,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x20,0x66,0x6F,0x72,0x20,0x6D,0x65, +0x6E,0x75,0x00,0x28,0x63,0x6F,0x75,0x6E,0x74,0x69,0x6E,0x67,0x2E,0x2E,0x2E,0x29, +0x00,0x00,0x65,0x4E,0x64,0x20,0x4F,0x66,0x20,0x43,0x6F,0x44,0x65,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; diff --git a/drivers/char/ip2/i2cmd.c b/drivers/char/ip2/i2cmd.c new file mode 100644 index 000000000000..ce4216631886 --- /dev/null +++ b/drivers/char/ip2/i2cmd.c @@ -0,0 +1,264 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definition table for In-line and Bypass commands. Applicable +* only when the standard loadware is active. (This is included +* source code, not a separate compilation module.) +* +*******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects additional commands. +// 24 February 1992 MAG Additional commands for 1.4.x loadware +// 11 March 1992 MAG Additional commands +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Discovered commands 39 & 40 must be at the end of a +// packet: affects implementation. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include "i2cmd.h" /* To get some bit-defines */ + +//------------------------------------------------------------------------------ +// Here is the table of global arrays which represent each type of command +// supported in the IntelliPort standard loadware. See also i2cmd.h +// for a more complete explanation of what is going on. +//------------------------------------------------------------------------------ + +// Here are the various globals: note that the names are not used except through +// the macros defined in i2cmd.h. Also note that although they are character +// arrays here (for extendability) they are cast to structure pointers in the +// i2cmd.h macros. See i2cmd.h for flags definitions. + +// Length Flags Command +static UCHAR ct02[] = { 1, BTH, 0x02 }; // DTR UP +static UCHAR ct03[] = { 1, BTH, 0x03 }; // DTR DN +static UCHAR ct04[] = { 1, BTH, 0x04 }; // RTS UP +static UCHAR ct05[] = { 1, BTH, 0x05 }; // RTS DN +static UCHAR ct06[] = { 1, BYP, 0x06 }; // START FL +static UCHAR ct07[] = { 2, BTH, 0x07,0 }; // BAUD +static UCHAR ct08[] = { 2, BTH, 0x08,0 }; // BITS +static UCHAR ct09[] = { 2, BTH, 0x09,0 }; // STOP +static UCHAR ct10[] = { 2, BTH, 0x0A,0 }; // PARITY +static UCHAR ct11[] = { 2, BTH, 0x0B,0 }; // XON +static UCHAR ct12[] = { 2, BTH, 0x0C,0 }; // XOFF +static UCHAR ct13[] = { 1, BTH, 0x0D }; // STOP FL +static UCHAR ct14[] = { 1, BYP|VIP, 0x0E }; // ACK HOTK +//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0 }; // IRQ SET +static UCHAR ct16[] = { 2, INL, 0x10,0 }; // IXONOPTS +static UCHAR ct17[] = { 2, INL, 0x11,0 }; // OXONOPTS +static UCHAR ct18[] = { 1, INL, 0x12 }; // CTSENAB +static UCHAR ct19[] = { 1, BTH, 0x13 }; // CTSDSAB +static UCHAR ct20[] = { 1, INL, 0x14 }; // DCDENAB +static UCHAR ct21[] = { 1, BTH, 0x15 }; // DCDDSAB +static UCHAR ct22[] = { 1, BTH, 0x16 }; // DSRENAB +static UCHAR ct23[] = { 1, BTH, 0x17 }; // DSRDSAB +static UCHAR ct24[] = { 1, BTH, 0x18 }; // RIENAB +static UCHAR ct25[] = { 1, BTH, 0x19 }; // RIDSAB +static UCHAR ct26[] = { 2, BTH, 0x1A,0 }; // BRKENAB +static UCHAR ct27[] = { 1, BTH, 0x1B }; // BRKDSAB +//static UCHAR ct28[]={ 2, BTH, 0x1C,0 }; // MAXBLOKSIZE +//static UCHAR ct29[]={ 2, 0, 0x1D,0 }; // reserved +static UCHAR ct30[] = { 1, INL, 0x1E }; // CTSFLOWENAB +static UCHAR ct31[] = { 1, INL, 0x1F }; // CTSFLOWDSAB +static UCHAR ct32[] = { 1, INL, 0x20 }; // RTSFLOWENAB +static UCHAR ct33[] = { 1, INL, 0x21 }; // RTSFLOWDSAB +static UCHAR ct34[] = { 2, BTH, 0x22,0 }; // ISTRIPMODE +static UCHAR ct35[] = { 2, BTH|END, 0x23,0 }; // SENDBREAK +static UCHAR ct36[] = { 2, BTH, 0x24,0 }; // SETERRMODE +//static UCHAR ct36a[]={ 3, INL, 0x24,0,0 }; // SET_REPLACE + +// The following is listed for completeness, but should never be sent directly +// by user-level code. It is sent only by library routines in response to data +// movement. +//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0 }; // FLOW PACKET + +// Back to normal +static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ +//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0 }; // OPOSTON +//static UCHAR ct40[]={ 1, BTH|END, 0x28 }; // OPOSTOFF +static UCHAR ct41[] = { 1, BYP, 0x29 }; // RESUME +//static UCHAR ct42[]={ 2, BTH, 0x2A,0 }; // TXBAUD +//static UCHAR ct43[]={ 2, BTH, 0x2B,0 }; // RXBAUD +//static UCHAR ct44[]={ 2, BTH, 0x2C,0 }; // MS PING +//static UCHAR ct45[]={ 1, BTH, 0x2D }; // HOTENAB +//static UCHAR ct46[]={ 1, BTH, 0x2E }; // HOTDSAB +static UCHAR ct47[] = { 7, BTH, 0x2F,0,0,0,0,0,0 }; // UNIX FLAGS +//static UCHAR ct48[]={ 1, BTH, 0x30 }; // DSRFLOWENAB +//static UCHAR ct49[]={ 1, BTH, 0x31 }; // DSRFLOWDSAB +//static UCHAR ct50[]={ 1, BTH, 0x32 }; // DTRFLOWENAB +//static UCHAR ct51[]={ 1, BTH, 0x33 }; // DTRFLOWDSAB +//static UCHAR ct52[]={ 1, BTH, 0x34 }; // BAUDTABRESET +static UCHAR ct53[] = { 3, BTH, 0x35,0,0 }; // BAUDREMAP +static UCHAR ct54[] = { 3, BTH, 0x36,0,0 }; // CUSTOMBAUD1 +static UCHAR ct55[] = { 3, BTH, 0x37,0,0 }; // CUSTOMBAUD2 +static UCHAR ct56[] = { 2, BTH|END, 0x38,0 }; // PAUSE +static UCHAR ct57[] = { 1, BYP, 0x39 }; // SUSPEND +static UCHAR ct58[] = { 1, BYP, 0x3A }; // UNSUSPEND +static UCHAR ct59[] = { 2, BTH, 0x3B,0 }; // PARITYCHK +static UCHAR ct60[] = { 1, INL|VIP, 0x3C }; // BOOKMARKREQ +//static UCHAR ct61[]={ 2, BTH, 0x3D,0 }; // INTERNALLOOP +//static UCHAR ct62[]={ 2, BTH, 0x3E,0 }; // HOTKTIMEOUT +static UCHAR ct63[] = { 2, INL, 0x3F,0 }; // SETTXON +static UCHAR ct64[] = { 2, INL, 0x40,0 }; // SETTXOFF +//static UCHAR ct65[]={ 2, BTH, 0x41,0 }; // SETAUTORTS +//static UCHAR ct66[]={ 2, BTH, 0x42,0 }; // SETHIGHWAT +//static UCHAR ct67[]={ 2, BYP, 0x43,0 }; // STARTSELFL +//static UCHAR ct68[]={ 2, INL, 0x44,0 }; // ENDSELFL +//static UCHAR ct69[]={ 1, BYP, 0x45 }; // HWFLOW_OFF +//static UCHAR ct70[]={ 1, BTH, 0x46 }; // ODSRFL_ENAB +//static UCHAR ct71[]={ 1, BTH, 0x47 }; // ODSRFL_DSAB +//static UCHAR ct72[]={ 1, BTH, 0x48 }; // ODCDFL_ENAB +//static UCHAR ct73[]={ 1, BTH, 0x49 }; // ODCDFL_DSAB +//static UCHAR ct74[]={ 2, BTH, 0x4A,0 }; // LOADLEVEL +//static UCHAR ct75[]={ 2, BTH, 0x4B,0 }; // STATDATA +//static UCHAR ct76[]={ 1, BYP, 0x4C }; // BREAK_ON +//static UCHAR ct77[]={ 1, BYP, 0x4D }; // BREAK_OFF +//static UCHAR ct78[]={ 1, BYP, 0x4E }; // GETFC +static UCHAR ct79[] = { 2, BYP, 0x4F,0 }; // XMIT_NOW +//static UCHAR ct80[]={ 4, BTH, 0x50,0,0,0 }; // DIVISOR_LATCH +//static UCHAR ct81[]={ 1, BYP, 0x51 }; // GET_STATUS +//static UCHAR ct82[]={ 1, BYP, 0x52 }; // GET_TXCNT +//static UCHAR ct83[]={ 1, BYP, 0x53 }; // GET_RXCNT +//static UCHAR ct84[]={ 1, BYP, 0x54 }; // GET_BOXIDS +//static UCHAR ct85[]={10, BYP, 0x55,0,0,0,0,0,0,0,0,0 }; // ENAB_MULT +//static UCHAR ct86[]={ 2, BTH, 0x56,0 }; // RCV_ENABLE +static UCHAR ct87[] = { 1, BYP, 0x57 }; // HW_TEST +//static UCHAR ct88[]={ 3, BTH, 0x58,0,0 }; // RCV_THRESHOLD +//static UCHAR ct89[]={ 1, BYP, 0x59 }; // DSS_NOW +//static UCHAR ct90[]={ 3, BYP, 0x5A,0,0 }; // Set SILO +//static UCHAR ct91[]={ 2, BYP, 0x5B,0 }; // timed break + +// Some composite commands as well +//static UCHAR cc01[]={ 2, BTH, 0x02,0x04 }; // DTR & RTS UP +//static UCHAR cc02[]={ 2, BTH, 0x03,0x05 }; // DTR & RTS DN + +//******** +//* Code * +//******** + +//****************************************************************************** +// Function: i2cmdSetSeq(type, size, string) +// Parameters: type - sequence number +// size - length of sequence +// string - substitution string +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 38 Define Hot Key sequence (alias +// "special receive sequence"). Returns a pointer to the structure. Endeavours +// to be bullet-proof in that the sequence number is forced in range, and any +// out-of-range sizes are forced to zero. +//****************************************************************************** +cmdSyntaxPtr +i2cmdSetSeq(unsigned char type, unsigned char size, unsigned char *string) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct38; + unsigned char *pc; + + pCM->cmd[1] = ((type > 0xf) ? 0xf : type); // Sequence number + size = ((size > 0x8) ? 0 : size); // size + pCM->cmd[2] = size; + pCM->length = 3 + size; // UPDATES THE LENGTH! + + pc = &(pCM->cmd[3]); + + while(size--) { + *pc++ = *string++; + } + return pCM; +} + +//****************************************************************************** +// Function: i2cmdUnixFlags(iflag, cflag, lflag) +// Parameters: Unix tty flags +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 47 and returns a pointer to the +// appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47; + + pCM->cmd[1] = (unsigned char) iflag; + pCM->cmd[2] = (unsigned char) (iflag >> 8); + pCM->cmd[3] = (unsigned char) cflag; + pCM->cmd[4] = (unsigned char) (cflag >> 8); + pCM->cmd[5] = (unsigned char) lflag; + pCM->cmd[6] = (unsigned char) (lflag >> 8); + return pCM; +} + +//****************************************************************************** +// Function: i2cmdBaudRemap(dest,src) +// Parameters: ? +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 53 and returns a pointer to the +// appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdBaudRemap(unsigned char dest, unsigned char src) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct53; + + pCM->cmd[1] = dest; + pCM->cmd[2] = src; + return pCM; +} + +//****************************************************************************** +// Function: i2cmdBaudDef(which, rate) +// Parameters: ? +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of commands 54 or 55 (according to the +// argument which), and returns a pointer to the appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdBaudDef(int which, unsigned short rate) +{ + cmdSyntaxPtr pCM; + + switch(which) + { + case 1: + pCM = (cmdSyntaxPtr) ct54; + break; + default: + case 2: + pCM = (cmdSyntaxPtr) ct55; + break; + } + pCM->cmd[1] = (unsigned char) rate; + pCM->cmd[2] = (unsigned char) (rate >> 8); + return pCM; +} + diff --git a/drivers/char/ip2/i2cmd.h b/drivers/char/ip2/i2cmd.h new file mode 100644 index 000000000000..83475167f491 --- /dev/null +++ b/drivers/char/ip2/i2cmd.h @@ -0,0 +1,660 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions and support for In-line and Bypass commands. +* Applicable only when the standard loadware is active. +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects some new commands +// 20 February 1992 MAG CMD_HOTACK corrected: no argument. +// 24 February 1992 MAG Support added for new commands for 1.4.x loadware. +// 11 March 1992 MAG Additional commands. +// 16 March 1992 MAG Additional commands. +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Changed CMD_OPOST +// +//------------------------------------------------------------------------------ +#ifndef I2CMD_H // To prevent multiple includes +#define I2CMD_H 1 + +#include "ip2types.h" + +// This module is designed to provide a uniform method of sending commands to +// the board through command packets. The difficulty is, some commands take +// parameters, others do not. Furthermore, it is often useful to send several +// commands to the same channel as part of the same packet. (See also i2pack.h.) +// +// This module is designed so that the caller should not be responsible for +// remembering the exact syntax of each command, or at least so that the +// compiler could check things somewhat. I'll explain as we go... +// +// First, a structure which can embody the syntax of each type of command. +// +typedef struct _cmdSyntax +{ + UCHAR length; // Number of bytes in the command + UCHAR flags; // Information about the command (see below) + + // The command and its parameters, which may be of arbitrary length. Don't + // worry yet how the parameters will be initialized; macros later take care + // of it. Also, don't worry about the arbitrary length issue; this structure + // is never used to allocate space (see i2cmd.c). + UCHAR cmd[2]; +} cmdSyntax, *cmdSyntaxPtr; + +// Bit assignments for flags + +#define INL 1 // Set if suitable for inline commands +#define BYP 2 // Set if suitable for bypass commands +#define BTH (INL|BYP) // suitable for either! +#define END 4 // Set if this must be the last command in a block +#define VIP 8 // Set if this command is special in some way and really + // should only be sent from the library-level and not + // directly from user-level +#define VAR 0x10 // This command is of variable length! + +//----------------------------------- +// External declarations for i2cmd.c +//----------------------------------- +// Routine to set up parameters for the "define hot-key sequence" command. Since +// there is more than one parameter to assign, we must use a function rather +// than a macro (used usually). +// +extern cmdSyntaxPtr i2cmdSetSeq(UCHAR seqno, UCHAR size, UCHAR *string); +extern cmdSyntaxPtr i2cmdUnixFlags(USHORT iflag,USHORT cflag,USHORT lflag); +extern cmdSyntaxPtr i2cmdBaudRemap(UCHAR dest, UCHAR src); +extern cmdSyntaxPtr i2cmdBaudDef(int which, USHORT rate); + +// Declarations for the global arrays used to bear the commands and their +// arguments. +// +// Note: Since these are globals and the arguments might change, it is important +// that the library routine COPY these into buffers from whence they would be +// sent, rather than merely storing the pointers. In multi-threaded +// environments, important that the copy should obtain before any context switch +// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND +// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call. +// +static UCHAR ct02[]; +static UCHAR ct03[]; +static UCHAR ct04[]; +static UCHAR ct05[]; +static UCHAR ct06[]; +static UCHAR ct07[]; +static UCHAR ct08[]; +static UCHAR ct09[]; +static UCHAR ct10[]; +static UCHAR ct11[]; +static UCHAR ct12[]; +static UCHAR ct13[]; +static UCHAR ct14[]; +static UCHAR ct15[]; +static UCHAR ct16[]; +static UCHAR ct17[]; +static UCHAR ct18[]; +static UCHAR ct19[]; +static UCHAR ct20[]; +static UCHAR ct21[]; +static UCHAR ct22[]; +static UCHAR ct23[]; +static UCHAR ct24[]; +static UCHAR ct25[]; +static UCHAR ct26[]; +static UCHAR ct27[]; +static UCHAR ct28[]; +static UCHAR ct29[]; +static UCHAR ct30[]; +static UCHAR ct31[]; +static UCHAR ct32[]; +static UCHAR ct33[]; +static UCHAR ct34[]; +static UCHAR ct35[]; +static UCHAR ct36[]; +static UCHAR ct36a[]; +static UCHAR ct41[]; +static UCHAR ct42[]; +static UCHAR ct43[]; +static UCHAR ct44[]; +static UCHAR ct45[]; +static UCHAR ct46[]; +static UCHAR ct48[]; +static UCHAR ct49[]; +static UCHAR ct50[]; +static UCHAR ct51[]; +static UCHAR ct52[]; +static UCHAR ct56[]; +static UCHAR ct57[]; +static UCHAR ct58[]; +static UCHAR ct59[]; +static UCHAR ct60[]; +static UCHAR ct61[]; +static UCHAR ct62[]; +static UCHAR ct63[]; +static UCHAR ct64[]; +static UCHAR ct65[]; +static UCHAR ct66[]; +static UCHAR ct67[]; +static UCHAR ct68[]; +static UCHAR ct69[]; +static UCHAR ct70[]; +static UCHAR ct71[]; +static UCHAR ct72[]; +static UCHAR ct73[]; +static UCHAR ct74[]; +static UCHAR ct75[]; +static UCHAR ct76[]; +static UCHAR ct77[]; +static UCHAR ct78[]; +static UCHAR ct79[]; +static UCHAR ct80[]; +static UCHAR ct81[]; +static UCHAR ct82[]; +static UCHAR ct83[]; +static UCHAR ct84[]; +static UCHAR ct85[]; +static UCHAR ct86[]; +static UCHAR ct87[]; +static UCHAR ct88[]; +static UCHAR ct89[]; +static UCHAR ct90[]; +static UCHAR ct91[]; +static UCHAR cc01[]; +static UCHAR cc02[]; + +// Now, refer to i2cmd.c, and see the character arrays defined there. They are +// cast here to cmdSyntaxPtr. +// +// There are library functions for issuing bypass or inline commands. These +// functions take one or more arguments of the type cmdSyntaxPtr. The routine +// then can figure out how long each command is supposed to be and easily add it +// to the list. +// +// For ease of use, we define manifests which return pointers to appropriate +// cmdSyntaxPtr things. But some commands also take arguments. If a single +// argument is used, we define a macro which performs the single assignment and +// (through the expedient of a comma expression) references the appropriate +// pointer. For commands requiring several arguments, we actually define a +// function to perform the assignments. + +#define CMD_DTRUP (cmdSyntaxPtr)(ct02) // Raise DTR +#define CMD_DTRDN (cmdSyntaxPtr)(ct03) // Lower DTR +#define CMD_RTSUP (cmdSyntaxPtr)(ct04) // Raise RTS +#define CMD_RTSDN (cmdSyntaxPtr)(ct05) // Lower RTS +#define CMD_STARTFL (cmdSyntaxPtr)(ct06) // Start Flushing Data + +#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01) // Raise DTR and RTS +#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02) // Lower DTR and RTS + +// Set Baud Rate for transmit and receive +#define CMD_SETBAUD(arg) \ + (((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07)) + +#define CBR_50 1 +#define CBR_75 2 +#define CBR_110 3 +#define CBR_134 4 +#define CBR_150 5 +#define CBR_200 6 +#define CBR_300 7 +#define CBR_600 8 +#define CBR_1200 9 +#define CBR_1800 10 +#define CBR_2400 11 +#define CBR_4800 12 +#define CBR_9600 13 +#define CBR_19200 14 +#define CBR_38400 15 +#define CBR_2000 16 +#define CBR_3600 17 +#define CBR_7200 18 +#define CBR_56000 19 +#define CBR_57600 20 +#define CBR_64000 21 +#define CBR_76800 22 +#define CBR_115200 23 +#define CBR_C1 24 // Custom baud rate 1 +#define CBR_C2 25 // Custom baud rate 2 +#define CBR_153600 26 +#define CBR_230400 27 +#define CBR_307200 28 +#define CBR_460800 29 +#define CBR_921600 30 + +// Set Character size +// +#define CMD_SETBITS(arg) \ + (((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08)) + +#define CSZ_5 0 +#define CSZ_6 1 +#define CSZ_7 2 +#define CSZ_8 3 + +// Set number of stop bits +// +#define CMD_SETSTOP(arg) \ + (((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09)) + +#define CST_1 0 +#define CST_15 1 // 1.5 stop bits +#define CST_2 2 + +// Set parity option +// +#define CMD_SETPAR(arg) \ + (((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10)) + +#define CSP_NP 0 // no parity +#define CSP_OD 1 // odd parity +#define CSP_EV 2 // Even parity +#define CSP_SP 3 // Space parity +#define CSP_MK 4 // Mark parity + +// Define xon char for transmitter flow control +// +#define CMD_DEF_IXON(arg) \ + (((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11)) + +// Define xoff char for transmitter flow control +// +#define CMD_DEF_IXOFF(arg) \ + (((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12)) + +#define CMD_STOPFL (cmdSyntaxPtr)(ct13) // Stop Flushing data + +// Acknowledge receipt of hotkey signal +// +#define CMD_HOTACK (cmdSyntaxPtr)(ct14) + +// Define irq level to use. Should actually be sent by library-level code, not +// directly from user... +// +#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command + // is sent, board processing doesn't really start. +#define CMD_SET_IRQ(arg) \ + (((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15)) + +#define CIR_POLL 0 // No IRQ - Poll +#define CIR_3 3 // IRQ 3 +#define CIR_4 4 // IRQ 4 +#define CIR_5 5 // IRQ 5 +#define CIR_7 7 // IRQ 7 +#define CIR_10 10 // IRQ 10 +#define CIR_11 11 // IRQ 11 +#define CIR_12 12 // IRQ 12 +#define CIR_15 15 // IRQ 15 + +// Select transmit flow xon/xoff options +// +#define CMD_IXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16)) + +#define CIX_NONE 0 // Incoming Xon/Xoff characters not special +#define CIX_XON 1 // Xoff disable, Xon enable +#define CIX_XANY 2 // Xoff disable, any key enable + +// Select receive flow xon/xoff options +// +#define CMD_OXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17)) + +#define COX_NONE 0 // Don't send Xon/Xoff +#define COX_XON 1 // Send xon/xoff to start/stop incoming data + + +#define CMD_CTS_REP (cmdSyntaxPtr)(ct18) // Enable CTS reporting +#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting + +#define CMD_DCD_REP (cmdSyntaxPtr)(ct20) // Enable DCD reporting +#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting + +#define CMD_DSR_REP (cmdSyntaxPtr)(ct22) // Enable DSR reporting +#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting + +#define CMD_RI_REP (cmdSyntaxPtr)(ct24) // Enable RI reporting +#define CMD_RI_NREP (cmdSyntaxPtr)(ct25) // Disable RI reporting + +// Enable break reporting and select style +// +#define CMD_BRK_REP(arg) \ + (((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26)) + +#define CBK_STAT 0x00 // Report breaks as a status (exception,irq) +#define CBK_NULL 0x01 // Report breaks as a good null +#define CBK_STAT_SEQ 0x02 // Report breaks as a status AND as in-band character + // sequence FFh, 01h, 10h +#define CBK_SEQ 0x03 // Report breaks as the in-band + //sequence FFh, 01h, 10h ONLY. +#define CBK_FLSH 0x04 // if this bit set also flush input data +#define CBK_POSIX 0x08 // if this bit set report as FF,0,0 sequence +#define CBK_SINGLE 0x10 // if this bit set with CBK_SEQ or CBK_STAT_SEQ + //then reports single null instead of triple + +#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting + +// Specify maximum block size for received data +// +#define CMD_MAX_BLOCK(arg) \ + (((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28)) + +// -- COMMAND 29 is reserved -- + +#define CMD_CTSFL_ENAB (cmdSyntaxPtr)(ct30) // Enable CTS flow control +#define CMD_CTSFL_DSAB (cmdSyntaxPtr)(ct31) // Disable CTS flow control +#define CMD_RTSFL_ENAB (cmdSyntaxPtr)(ct32) // Enable RTS flow control +#define CMD_RTSFL_DSAB (cmdSyntaxPtr)(ct33) // Disable RTS flow control + +// Specify istrip option +// +#define CMD_ISTRIP_OPT(arg) \ + (((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34)) + +#define CIS_NOSTRIP 0 // Strip characters to character size +#define CIS_STRIP 1 // Strip any 8-bit characters to 7 bits + +// Send a break of arg milliseconds +// +#define CMD_SEND_BRK(arg) \ + (((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35)) + +// Set error reporting mode +// +#define CMD_SET_ERROR(arg) \ + (((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36)) + +#define CSE_ESTAT 0 // Report error in a status packet +#define CSE_NOREP 1 // Treat character as though it were good +#define CSE_DROP 2 // Discard the character +#define CSE_NULL 3 // Replace with a null +#define CSE_MARK 4 // Replace with a 3-character sequence (as Unix) + +#define CMD_SET_REPLACEMENT(arg,ch) \ + (((cmdSyntaxPtr)(ct36a))->cmd[1] = (arg), \ + (((cmdSyntaxPtr)(ct36a))->cmd[2] = (ch), \ + (cmdSyntaxPtr)(ct36a)) + +#define CSE_REPLACE 0x8 // Replace the errored character with the + // replacement character defined here + +#define CSE_STAT_REPLACE 0x18 // Replace the errored character with the + // replacement character defined here AND + // report the error as a status packet (as in + // CSE_ESTAT). + + +// COMMAND 37, to send flow control packets, is handled only by low-level +// library code in response to data movement and shouldn't ever be sent by the +// user code. See i2pack.h and the body of i2lib.c for details. + +// COMMAND 38: Define the hot-key sequence +// seqno: sequence number 0-15 +// size: number of characters in sequence (1-8) +// string: pointer to the characters +// (if size == 0, "undefines" this sequence +// +#define CMD_SET_SEQ(seqno,size,string) i2cmdSetSeq(seqno,size,string) + +// Enable on-board post-processing, using options given in oflag argument. +// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command +// because the loadware does not permit sending back-to-back CMD_OPOST_ON +// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that +// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a +// solo packet). This means the caller must specify separately CMD_OPOST_OFF, +// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure +// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok. +// +#define CMD_OPOST_ON(oflag) \ + (*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \ + (cmdSyntaxPtr)(ct39)) + +#define CMD_OPOST_OFF (cmdSyntaxPtr)(ct40) // Disable on-board post-proc + +#define CMD_RESUME (cmdSyntaxPtr)(ct41) // Resume: behave as though an XON + // were received; + +// Set Transmit baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_TX(arg) \ + (((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42)) + +// Set Receive baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_RX(arg) \ + (((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43)) + +// Request interrupt from board each arg milliseconds. Interrupt will specify +// "received data", even though there may be no data present. If arg == 0, +// disables any such interrupts. +// +#define CMD_PING_REQ(arg) \ + (((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44)) + +#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking +#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking + +// COMMAND 47: Send Protocol info via Unix flags: +// iflag = Unix tty t_iflag +// cflag = Unix tty t_cflag +// lflag = Unix tty t_lflag +// See System V Unix/Xenix documentation for the meanings of the bit fields +// within these flags +// +#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag) + +#define CMD_DSRFL_ENAB (cmdSyntaxPtr)(ct48) // Enable DSR receiver ctrl +#define CMD_DSRFL_DSAB (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl +#define CMD_DTRFL_ENAB (cmdSyntaxPtr)(ct50) // Enable DTR flow control +#define CMD_DTRFL_DSAB (cmdSyntaxPtr)(ct51) // Disable DTR flow control +#define CMD_BAUD_RESET (cmdSyntaxPtr)(ct52) // Reset baudrate table + +// COMMAND 53: Remap baud rate table +// dest = index of table entry to be changed +// src = index value to substitute. +// at default mapping table is f(x) = x +// +#define CMD_BAUD_REMAP(dest,src) i2cmdBaudRemap(dest,src) + +// COMMAND 54: Define custom rate #1 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate) + +// COMMAND 55: Define custom rate #2 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate) + +// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.) +// +#define CMD_PAUSE(arg) \ + (((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56)) + +#define CMD_SUSPEND (cmdSyntaxPtr)(ct57) // Suspend output +#define CMD_UNSUSPEND (cmdSyntaxPtr)(ct58) // Un-Suspend output + +// Set parity-checking options +// +#define CMD_PARCHK(arg) \ + (((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59)) + +#define CPK_ENAB 0 // Enable parity checking on input +#define CPK_DSAB 1 // Disable parity checking on input + +#define CMD_BMARK_REQ (cmdSyntaxPtr)(ct60) // Bookmark request + + +// Enable/Disable internal loopback mode +// +#define CMD_INLOOP(arg) \ + (((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61)) + +#define CIN_DISABLE 0 // Normal operation (default) +#define CIN_ENABLE 1 // Internal (local) loopback +#define CIN_REMOTE 2 // Remote loopback + +// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0 +// --> no timeout: wait forever. +// +#define CMD_HOT_TIME(arg) \ + (((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62)) + + +// Define (outgoing) xon for receive flow control +// +#define CMD_DEF_OXON(arg) \ + (((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63)) + +// Define (outgoing) xoff for receiver flow control +// +#define CMD_DEF_OXOFF(arg) \ + (((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64)) + +// Enable/Disable RTS on transmit (1/2 duplex-style) +// +#define CMD_RTS_XMIT(arg) \ + (((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65)) + +#define CHD_DISABLE 0 +#define CHD_ENABLE 1 + +// Set high-water-mark level (debugging use only) +// +#define CMD_SETHIGHWAT(arg) \ + (((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66)) + +// Start flushing tagged data (tag = 0-14) +// +#define CMD_START_SELFL(tag) \ + (((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67)) + +// End flushing tagged data (tag = 0-14) +// +#define CMD_END_SELFL(tag) \ + (((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68)) + +#define CMD_HWFLOW_OFF (cmdSyntaxPtr)(ct69) // Disable HW TX flow control +#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c +#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c +#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c +#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c + +// Set transmit interrupt load level. Count should be an even value 2-12 +// +#define CMD_LOADLEVEL(count) \ + (((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74)) + +// If reporting DSS changes, map to character sequence FFh, 2, MSR +// +#define CMD_STATDATA(arg) \ + (((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75)) + +#define CSTD_DISABLE// Report DSS changes as status packets only (default) +#define CSTD_ENABLE // Report DSS changes as in-band data sequence as well as + // by status packet. + +#define CMD_BREAK_ON (cmdSyntaxPtr)(ct76)// Set break and stop xmit +#define CMD_BREAK_OFF (cmdSyntaxPtr)(ct77)// End break and restart xmit +#define CMD_GETFC (cmdSyntaxPtr)(ct78)// Request for flow control packet + // from board. + +// Transmit this character immediately +// +#define CMD_XMIT_NOW(ch) \ + (((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79)) + +// Set baud rate via "divisor latch" +// +#define CMD_DIVISOR_LATCH(which,value) \ + (((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \ + *(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \ + (cmdSyntaxPtr)(ct80)) + +#define CDL_RX 1 // Set receiver rate +#define CDL_TX 2 // Set transmit rate + // (CDL_TX | CDL_RX) Set both rates + +// Request for special diagnostic status pkt from the board. +// +#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81) + +// Request time-stamped transmit character count packet. +// +#define CMD_GET_TXCNT (cmdSyntaxPtr)(ct82) + +// Request time-stamped receive character count packet. +// +#define CMD_GET_RXCNT (cmdSyntaxPtr)(ct83) + +// Request for box/board I.D. packet. +#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84) + +// Enable or disable multiple channels according to bit-mapped ushorts box 1-4 +// +#define CMD_ENAB_MULT(enable, box1, box2, box3, box4) \ + (((cmdSytaxPtr)(ct85))->cmd[1] = (enable), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \ + (cmdSyntaxPtr)(ct85)) + +#define CEM_DISABLE 0 +#define CEM_ENABLE 1 + +// Enable or disable receiver or receiver interrupts (default both enabled) +// +#define CMD_RCV_ENABLE(ch) \ + (((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86)) + +#define CRE_OFF 0 // Disable the receiver +#define CRE_ON 1 // Enable the receiver +#define CRE_INTOFF 2 // Disable receiver interrupts (to loadware) +#define CRE_INTON 3 // Enable receiver interrupts (to loadware) + +// Starts up a hardware test process, which runs transparently, and sends a +// STAT_HWFAIL packet in case a hardware failure is detected. +// +#define CMD_HW_TEST (cmdSyntaxPtr)(ct87) + +// Change receiver threshold and timeout value: +// Defaults: timeout = 20mS +// threshold count = 8 when DTRflow not in use, +// threshold count = 5 when DTRflow in use. +// +#define CMD_RCV_THRESHOLD(count,ms) \ + (((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \ + ((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \ + (cmdSyntaxPtr)(ct88)) + +// Makes the loadware report DSS signals for this channel immediately. +// +#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89) + +// Set the receive silo parameters +// timeout is ms idle wait until delivery (~VTIME) +// threshold is max characters cause interrupt (~VMIN) +// +#define CMD_SET_SILO(timeout,threshold) \ + (((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \ + ((cmdSyntaxPtr)(ct90))->cmd[2] = (threshold), \ + (cmdSyntaxPtr)(ct90)) + +// Set timed break in decisecond (1/10s) +// +#define CMD_LBREAK(ds) \ + (((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66)) + + + +#endif // I2CMD_H diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c new file mode 100644 index 000000000000..3717630eb199 --- /dev/null +++ b/drivers/char/ip2/i2ellis.c @@ -0,0 +1,1470 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Low-level interface code for the device driver +* (This is included source code, not a separate compilation +* module.) +* +*******************************************************************************/ +//--------------------------------------------- +// Function declarations private to this module +//--------------------------------------------- +// Functions called only indirectly through i2eBordStr entries. + +static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int); + +static unsigned short iiReadWord16(i2eBordStrPtr); +static unsigned short iiReadWord8(i2eBordStrPtr); +static void iiWriteWord16(i2eBordStrPtr, unsigned short); +static void iiWriteWord8(i2eBordStrPtr, unsigned short); + +static int iiWaitForTxEmptyII(i2eBordStrPtr, int); +static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int); +static int iiTxMailEmptyII(i2eBordStrPtr); +static int iiTxMailEmptyIIEX(i2eBordStrPtr); +static int iiTrySendMailII(i2eBordStrPtr, unsigned char); +static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char); + +static unsigned short iiGetMailII(i2eBordStrPtr); +static unsigned short iiGetMailIIEX(i2eBordStrPtr); + +static void iiEnableMailIrqII(i2eBordStrPtr); +static void iiEnableMailIrqIIEX(i2eBordStrPtr); +static void iiWriteMaskII(i2eBordStrPtr, unsigned char); +static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char); + +static void ii2DelayTimer(unsigned int); +static void ii2DelayWakeup(unsigned long id); +static void ii2Nop(void); + +//*************** +//* Static Data * +//*************** + +static int ii2Safe = 0; // Safe I/O address for delay routine + +static int iiDelayed = 0; // Set when the iiResetDelay function is + // called. Cleared when ANY board is reset. +static struct timer_list * pDelayTimer; // Used by iiDelayTimer +static struct wait_queue * pDelayWait; // Used by iiDelayTimer +static spinlock_t Dl_spinlock; + +//******** +//* Code * +//******** + +//======================================================= +// Initialization Routines +// +// iiSetAddress +// iiReset +// iiResetDelay +// iiInitialize +//======================================================= + +//****************************************************************************** +// Function: iiEllisInit() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// This routine performs any required initialization of the iiEllis subsystem. +// +//****************************************************************************** +static void +iiEllisInit(void) +{ + pDelayTimer = kmalloc ( sizeof (struct timer_list), GFP_KERNEL ); + pDelayWait = NULL; + LOCK_INIT(&Dl_spinlock); +} + +//****************************************************************************** +// Function: iiEllisCleanup() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// This routine performs any required cleanup of the iiEllis subsystem. +// +//****************************************************************************** +static void +iiEllisCleanup(void) +{ + if ( pDelayTimer != NULL ) { + kfree ( pDelayTimer ); + } +} + +//****************************************************************************** +// Function: iiSetAddress(pB, address, delay) +// Parameters: pB - pointer to the board structure +// address - the purported I/O address of the board +// delay - pointer to the 1-ms delay function to use +// in this and any future operations to this board +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// This routine (roughly) checks for address validity, sets the i2eValid OK and +// sets the state to II_STATE_COLD which means that we haven't even sent a reset +// yet. +// +//****************************************************************************** +static int +iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay ) +{ + // Should any failure occur before init is finished... + pB->i2eValid = I2E_INCOMPLETE; + + // Cannot check upper limit except extremely: Might be microchannel + // Address must be on an 8-byte boundary + + if ((unsigned int)address <= 0x100 + || (unsigned int)address >= 0xfff8 + || (address & 0x7) + ) + { + COMPLETE(pB,I2EE_BADADDR); + } + + // Initialize accelerators + pB->i2eBase = address; + pB->i2eData = address + FIFO_DATA; + pB->i2eStatus = address + FIFO_STATUS; + pB->i2ePointer = address + FIFO_PTR; + pB->i2eXMail = address + FIFO_MAIL; + pB->i2eXMask = address + FIFO_MASK; + + // Initialize i/o address for ii2DelayIO + ii2Safe = address + FIFO_NOP; + + // Initialize the delay routine + pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop); + + pB->i2eValid = I2E_MAGIC; + pB->i2eState = II_STATE_COLD; + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReset(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to reset the board (see also i2hw.h). Normally, we would use this to +// reset a board immediately after iiSetAddress(), but it is valid to reset a +// board from any state, say, in order to change or re-load loadware. (Under +// such circumstances, no reason to re-run iiSetAddress(), which is why it is a +// separate routine and not included in this routine. +// +//****************************************************************************** +static int +iiReset(i2eBordStrPtr pB) +{ + // Magic number should be set, else even the address is suspect + if (pB->i2eValid != I2E_MAGIC) + { + COMPLETE(pB, I2EE_BADMAGIC); + } + + OUTB(pB->i2eBase + FIFO_RESET, 0); // Any data will do + iiDelay(pB, 50); // Pause between resets + OUTB(pB->i2eBase + FIFO_RESET, 0); // Second reset + + // We must wait before even attempting to read anything from the FIFO: the + // board's P.O.S.T may actually attempt to read and write its end of the + // FIFO in order to check flags, loop back (where supported), etc. On + // completion of this testing it would reset the FIFO, and on completion + // of all // P.O.S.T., write the message. We must not mistake data which + // might have been sent for testing as part of the reset message. To + // better utilize time, say, when resetting several boards, we allow the + // delay to be performed externally; in this way the caller can reset + // several boards, delay a single time, then call the initialization + // routine for all. + + pB->i2eState = II_STATE_RESET; + + iiDelayed = 0; // i.e., the delay routine hasn't been called since the most + // recent reset. + + // Ensure anything which would have been of use to standard loadware is + // blanked out, since board has now forgotten everything!. + + pB->i2eUsingIrq = IRQ_UNDEFINED; // Not set up to use an interrupt yet + pB->i2eWaitingForEmptyFifo = 0; + pB->i2eOutMailWaiting = 0; + pB->i2eChannelPtr = NULL; + pB->i2eChannelCnt = 0; + + pB->i2eLeadoffWord[0] = 0; + pB->i2eFifoInInts = 0; + pB->i2eFifoOutInts = 0; + pB->i2eFatalTrap = NULL; + pB->i2eFatal = 0; + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiResetDelay(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Using the delay defined in board structure, waits two seconds (for board to +// reset). +// +//****************************************************************************** +static int +iiResetDelay(i2eBordStrPtr pB) +{ + if (pB->i2eValid != I2E_MAGIC) { + COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_RESET) { + COMPLETE(pB, I2EE_BADSTATE); + } + iiDelay(pB,2000); /* Now we wait for two seconds. */ + iiDelayed = 1; /* Delay has been called: ok to initialize */ + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiInitialize(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to read the Power-on reset message. Initializes any remaining fields +// in the pB structure. +// +// This should be called as the third step of a process beginning with +// iiReset(), then iiResetDelay(). This routine checks to see that the structure +// is "valid" and in the reset state, also confirms that the delay routine has +// been called since the latest reset (to any board! overly strong!). +// +//****************************************************************************** +static int +iiInitialize(i2eBordStrPtr pB) +{ + int itemp; + unsigned char c; + unsigned short utemp; + unsigned int ilimit; + + if (pB->i2eValid != I2E_MAGIC) + { + COMPLETE(pB, I2EE_BADMAGIC); + } + + if (pB->i2eState != II_STATE_RESET || !iiDelayed) + { + COMPLETE(pB, I2EE_BADSTATE); + } + + // In case there is a failure short of our completely reading the power-up + // message. + pB->i2eValid = I2E_INCOMPLETE; + + + // Now attempt to read the message. + + for (itemp = 0; itemp < sizeof(porStr); itemp++) + { + // We expect the entire message is ready. + if (HAS_NO_INPUT(pB)) + { + pB->i2ePomSize = itemp; + COMPLETE(pB, I2EE_PORM_SHORT); + } + + pB->i2ePom.c[itemp] = c = BYTE_FROM(pB); + + // We check the magic numbers as soon as they are supposed to be read + // (rather than after) to minimize effect of reading something we + // already suspect can't be "us". + if ( (itemp == POR_1_INDEX && c != POR_MAGIC_1) || + (itemp == POR_2_INDEX && c != POR_MAGIC_2)) + { + pB->i2ePomSize = itemp+1; + COMPLETE(pB, I2EE_BADMAGIC); + } + } + + pB->i2ePomSize = itemp; + + // Ensure that this was all the data... + if (HAS_INPUT(pB)) + COMPLETE(pB, I2EE_PORM_LONG); + + // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper: + // Implying we will not be able to download any code either: That's ok: the + // condition is pretty explicit. + if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER) + { + COMPLETE(pB, I2EE_POSTERR); + } + + // Determine anything which must be done differently depending on the family + // of boards! + switch (pB->i2ePom.e.porID & POR_ID_FAMILY) + { + case POR_ID_FII: // IntelliPort-II + + pB->i2eFifoStyle = FIFO_II; + pB->i2eFifoSize = 512; // 512 bytes, always + pB->i2eDataWidth16 = NO; + + pB->i2eMaxIrq = 15; // Because board cannot tell us it is in an 8-bit + // slot, we do allow it to be done (documentation!) + + pB->i2eGoodMap[1] = + pB->i2eGoodMap[2] = + pB->i2eGoodMap[3] = + pB->i2eChannelMap[1] = + pB->i2eChannelMap[2] = + pB->i2eChannelMap[3] = 0; + + switch (pB->i2ePom.e.porID & POR_ID_SIZE) + { + case POR_ID_II_4: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x0f; // four-port + + // Since porPorts1 is based on the Hardware ID register, the numbers + // should always be consistent for IntelliPort-II. Ditto below... + if (pB->i2ePom.e.porPorts1 != 4) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_8: + case POR_ID_II_8R: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0xff; // Eight port + if (pB->i2ePom.e.porPorts1 != 8) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_6: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x3f; // Six Port + if (pB->i2ePom.e.porPorts1 != 6) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + } + + // Fix up the "good channel list based on any errors reported. + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1) + { + pB->i2eGoodMap[0] &= ~0x0f; + } + + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2) + { + pB->i2eGoodMap[0] &= ~0xf0; + } + + break; // POR_ID_FII case + + case POR_ID_FIIEX: // IntelliPort-IIEX + + pB->i2eFifoStyle = FIFO_IIEX; + + itemp = pB->i2ePom.e.porFifoSize; + + // Implicit assumption that fifo would not grow beyond 32k, + // nor would ever be less than 256. + + if (itemp < 8 || itemp > 15) + { + COMPLETE(pB, I2EE_INCONSIST); + } + pB->i2eFifoSize = (1 << itemp); + + // These are based on what P.O.S.T thinks should be there, based on + // box ID registers + ilimit = pB->i2ePom.e.porNumBoxes; + if (ilimit > ABS_MAX_BOXES) + { + ilimit = ABS_MAX_BOXES; + } + + // For as many boxes as EXIST, gives the type of box. + // Added 8/6/93: check for the ISA-4 (asic) which looks like an + // expandable but for whom "8 or 16?" is not the right question. + + utemp = pB->i2ePom.e.porFlags; + if (utemp & POR_CEX4) + { + pB->i2eChannelMap[0] = 0x000f; + } else { + utemp &= POR_BOXES; + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eChannelMap[itemp] = + ((utemp & POR_BOX_16) ? 0xffff : 0x00ff); + utemp >>= 1; + } + } + + // These are based on what P.O.S.T actually found. + + utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1; + + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eGoodMap[itemp] = 0; + if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f; + if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0; + if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00; + if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000; + utemp >>= 4; + } + + // Now determine whether we should transfer in 8 or 16-bit mode. + switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) ) + { + case POR_BUS_SLOT16 | POR_BUS_DIP16: + pB->i2eDataWidth16 = YES; + pB->i2eMaxIrq = 15; + break; + + case POR_BUS_SLOT16: + pB->i2eDataWidth16 = NO; + pB->i2eMaxIrq = 15; + break; + + case 0: + case POR_BUS_DIP16: // In an 8-bit slot, DIP switch don't care. + default: + pB->i2eDataWidth16 = NO; + pB->i2eMaxIrq = 7; + break; + } + break; // POR_ID_FIIEX case + + default: // Unknown type of board + COMPLETE(pB, I2EE_BAD_FAMILY); + break; + } // End the switch based on family + + // Temporarily, claim there is no room in the outbound fifo. + // We will maintain this whenever we check for an empty outbound FIFO. + pB->i2eFifoRemains = 0; + + // Now, based on the bus type, should we expect to be able to re-configure + // interrupts (say, for testing purposes). + switch (pB->i2ePom.e.porBus & POR_BUS_TYPE) + { + case POR_BUS_T_ISA: + case POR_BUS_T_UNK: // If the type of bus is undeclared, assume ok. + pB->i2eChangeIrq = YES; + break; + case POR_BUS_T_MCA: + case POR_BUS_T_EISA: + pB->i2eChangeIrq = NO; + break; + default: + COMPLETE(pB, I2EE_BADBUS); + } + + if (pB->i2eDataWidth16 == YES) + { + pB->i2eWriteBuf = iiWriteBuf16; + pB->i2eReadBuf = iiReadBuf16; + pB->i2eWriteWord = iiWriteWord16; + pB->i2eReadWord = iiReadWord16; + } else { + pB->i2eWriteBuf = iiWriteBuf8; + pB->i2eReadBuf = iiReadBuf8; + pB->i2eWriteWord = iiWriteWord8; + pB->i2eReadWord = iiReadWord8; + } + + switch(pB->i2eFifoStyle) + { + case FIFO_II: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII; + pB->i2eTxMailEmpty = iiTxMailEmptyII; + pB->i2eTrySendMail = iiTrySendMailII; + pB->i2eGetMail = iiGetMailII; + pB->i2eEnableMailIrq = iiEnableMailIrqII; + pB->i2eWriteMask = iiWriteMaskII; + + break; + + case FIFO_IIEX: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX; + pB->i2eTxMailEmpty = iiTxMailEmptyIIEX; + pB->i2eTrySendMail = iiTrySendMailIIEX; + pB->i2eGetMail = iiGetMailIIEX; + pB->i2eEnableMailIrq = iiEnableMailIrqIIEX; + pB->i2eWriteMask = iiWriteMaskIIEX; + + break; + + default: + COMPLETE(pB, I2EE_INCONSIST); + } + + // Initialize state information. + pB->i2eState = II_STATE_READY; // Ready to load loadware. + + // Some Final cleanup: + // For some boards, the bootstrap firmware may perform some sort of test + // resulting in a stray character pending in the incoming mailbox. If one is + // there, it should be read and discarded, especially since for the standard + // firmware, it's the mailbox that interrupts the host. + + pB->i2eStartMail = iiGetMail(pB); + + // Everything is ok now, return with good status/ + + pB->i2eValid = I2E_MAGIC; + COMPLETE(pB, I2EE_GOOD); +} + +//======================================================= +// Delay Routines +// +// iiDelayIO +// iiNop +//======================================================= + +static void +ii2DelayWakeup(unsigned long id) +{ + wake_up_interruptible ( &pDelayWait ); +} + +//****************************************************************************** +// Function: ii2DelayTimer(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It uses the +// Linux timer_list mechanism. +// +// The Linux timers use a unit called "jiffies" which are 10mS in the Intel +// architecture. This function rounds the delay period up to the next "jiffy". +// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended +// for Alpha platforms at this time. +// +//****************************************************************************** +static void +ii2DelayTimer(unsigned int mseconds) +{ + init_timer ( pDelayTimer ); + + pDelayTimer->expires = jiffies + ( mseconds + 9 ) / 10; + pDelayTimer->function = ii2DelayWakeup; + pDelayTimer->data = 0; + + add_timer ( pDelayTimer ); + interruptible_sleep_on ( &pDelayWait ); + del_timer ( pDelayTimer ); +} + +#if 0 +//static void ii2DelayIO(unsigned int); +//****************************************************************************** +// !!! Not Used, this is DOS crap, some of you young folks may be interested in +// in how things were done in the stone age of caculating machines !!! +// Function: ii2DelayIO(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It is intended +// for use where a clock-based function is impossible: for example, DOS drivers. +// +// This function uses the IN instruction to place bounds on the timing and +// assumes that ii2Safe has been set. This is because I/O instructions are not +// subject to caching and will therefore take a certain minimum time. To ensure +// the delay is at least long enough on fast machines, it is based on some +// fastest-case calculations. On slower machines this may cause VERY long +// delays. (3 x fastest case). In the fastest case, everything is cached except +// the I/O instruction itself. +// +// Timing calculations: +// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O +// operation in question is a byte operation to an odd address. For 8-bit +// operations, the architecture generally enforces two wait states. At 10 MHz, a +// single cycle time is 100nS. A read operation at two wait states takes 6 +// cycles for a total time of 600nS. Therefore approximately 1666 iterations +// would be required to generate a single millisecond delay. The worst +// (reasonable) case would be an 8MHz system with no cacheing. In this case, the +// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code +// fetch of other instructions in the loop would take time (zero wait states, +// however) and would be hard to estimate. This is minimized by using in-line +// assembler for the in inner loop of IN instructions. This consists of just a +// few bytes. So we'll guess about four code fetches per loop. Each code fetch +// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is +// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS. +// +// So much for theoretical timings: results using 1666 value on some actual +// machines: +// IBM 286 6MHz 3.15 mS +// Zenith 386 33MHz 2.45 mS +// (brandX) 386 33MHz 1.90 mS (has cache) +// (brandY) 486 33MHz 2.35 mS +// NCR 486 ?? 1.65 mS (microchannel) +// +// For most machines, it is probably safe to scale this number back (remember, +// for robust operation use an actual timed delay if possible), so we are using +// a value of 1190. This yields 1.17 mS for the fastest machine in our sample, +// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine. +// +// 1/29/93: +// The above timings are too slow. Actual cycle times might be faster. ISA cycle +// times could approach 500 nS, and ... +// The IBM model 77 being microchannel has no wait states for 8-bit reads and +// seems to be accessing the I/O at 440 nS per access (from start of one to +// start of next). This would imply we need 1000/.440 = 2272 iterations to +// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in +// fact enough. For diagnostics, we keep the level at 1190, but developers note +// this needs tuning. +// +// Safe assumption: 2270 i/o reads = 1 millisecond +// +//****************************************************************************** + + +static int ii2DelValue = 1190; // See timing calculations below + // 1666 for fastest theoretical machine + // 1190 safe for most fast 386 machines + // 1000 for fastest machine tested here + // 540 (sic) for AT286/6Mhz +static void +ii2DelayIO(unsigned int mseconds) +{ + if (!ii2Safe) + return; /* Do nothing if this variable uninitialized */ + + while(mseconds--) { + int i = ii2DelValue; + while ( i-- ) { + INB ( ii2Safe ); + } + } +} +#endif + +//****************************************************************************** +// Function: ii2Nop() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This +// saves checking for a NULL pointer at every call. +//****************************************************************************** +static void +ii2Nop(void) +{ + return; // no mystery here +} + +//======================================================= +// Routines which are available in 8/16-bit versions, or +// in different fifo styles. These are ALL called +// indirectly through the board structure. +//======================================================= + +//****************************************************************************** +// Function: iiWriteBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + OUTSW ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiWriteBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). This is to be consistant with the 16-bit version. +// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + /* Rudimentary sanity checking here */ + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + OUTSB ( pB->i2eData, address, count ); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + INSW ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). This to match the 16-bit behaviour. Uses +// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + INSB ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadWord16(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses a 16-bit operation. Is called indirectly through +// pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord16(i2eBordStrPtr pB) +{ + return (unsigned short)( INW(pB->i2eData) ); +} + +//****************************************************************************** +// Function: iiReadWord8(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is +// called indirectly through pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord8(i2eBordStrPtr pB) +{ + unsigned short urs; + + urs = INB ( pB->i2eData ); + + return ( ( INB ( pB->i2eData ) << 8 ) | urs ); +} + +//****************************************************************************** +// Function: iiWriteWord16(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses 16-bit operation. Is called indirectly through +// pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord16(i2eBordStrPtr pB, unsigned short value) +{ + WORD_TO(pB, (int)value); +} + +//****************************************************************************** +// Function: iiWriteWord8(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations (writes LSB first). Is called +// indirectly through pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord8(i2eBordStrPtr pB, unsigned short value) +{ + BYTE_TO(pB, (char)value); + BYTE_TO(pB, (char)(value >> 8) ); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyII(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-II - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + int itemp; + + for (;;) + { + // This routine hinges on being able to see the "other" status register + // (as seen by the local processor). His incoming fifo is our outgoing + // FIFO. + // + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + // + // Even worse, however, this routine "shifts" the status register to + // point to the local status register which is not the usual situation. + // Therefore for extra safety, we force the critical section to be + // completely atomic, and pick up after ourselves before allowing any + // interrupts of any kind. + + + WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) + OUTB(pB->i2ePointer, SEL_COMMAND); + OUTB(pB->i2ePointer, SEL_CMD_SH); + + itemp = INB(pB->i2eStatus); + + OUTB(pB->i2ePointer, SEL_COMMAND); + OUTB(pB->i2ePointer, SEL_CMD_UNSH); + + if (itemp & ST_IN_EMPTY) + { + UPDATE_FIFO_ROOM(pB); + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + COMPLETE(pB, I2EE_GOOD); + } + + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); /* 1 mS granularity on checking condition */ + } + COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyIIEX(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + + for (;;) + { + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + + WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) + + if (INB(pB->i2eStatus) & STE_OUT_MT) { + UPDATE_FIFO_ROOM(pB); + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + COMPLETE(pB, I2EE_GOOD); + } + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); // 1 mS granularity on checking condition + } + COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiTxMailEmptyII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyII(i2eBordStrPtr pB) +{ + int port = pB->i2ePointer; + OUTB ( port, SEL_OUTMAIL ); + return ( INB(port) == 0 ); +} + +//****************************************************************************** +// Function: iiTxMailEmptyIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyIIEX(i2eBordStrPtr pB) +{ + return !(INB(pB->i2eStatus) & STE_OUT_MAIL); +} + +//****************************************************************************** +// Function: iiTrySendMailII(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail) +{ + int port = pB->i2ePointer; + + OUTB(port, SEL_OUTMAIL); + if (INB(port) == 0) { + OUTB(port, SEL_OUTMAIL); + OUTB(port, mail); + return 1; + } + return 0; +} + +//****************************************************************************** +// Function: iiTrySendMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail) +{ + if(INB(pB->i2eStatus) & STE_OUT_MAIL) { + return 0; + } + OUTB(pB->i2eXMail, mail); + return 1; +} + +//****************************************************************************** +// Function: iiGetMailII(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailII(i2eBordStrPtr pB) +{ + if (HAS_MAIL(pB)) { + OUTB(pB->i2ePointer, SEL_INMAIL); + return INB(pB->i2ePointer); + } else { + return NO_MAIL_HERE; + } +} + +//****************************************************************************** +// Function: iiGetMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailIIEX(i2eBordStrPtr pB) +{ + if (HAS_MAIL(pB)) { + return INB(pB->i2eXMail); + } else { + return NO_MAIL_HERE; + } +} + +//****************************************************************************** +// Function: iiEnableMailIrqII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqII(i2eBordStrPtr pB) +{ + OUTB(pB->i2ePointer, SEL_MASK); + OUTB(pB->i2ePointer, ST_IN_MAIL); +} + +//****************************************************************************** +// Function: iiEnableMailIrqIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqIIEX(i2eBordStrPtr pB) +{ + OUTB(pB->i2eXMask, MX_IN_MAIL); +} + +//****************************************************************************** +// Function: iiWriteMaskII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskII(i2eBordStrPtr pB, unsigned char value) +{ + OUTB(pB->i2ePointer, SEL_MASK); + OUTB(pB->i2ePointer, value); +} + +//****************************************************************************** +// Function: iiWriteMaskIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value) +{ + OUTB(pB->i2eXMask, value); +} + +//****************************************************************************** +// Function: iiDownloadBlock(pB, pSource, isStandard) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// +// Returns: Success or Failure +// +// Description: +// +// Downloads a single block (at pSource)to the board referenced by pB. Caller +// sets isStandard to true/false according to whether the "standard" loadware is +// what's being loaded. The normal process, then, is to perform an iiInitialize +// to the board, then perform some number of iiDownloadBlocks using the returned +// state to determine when download is complete. +// +// Possible return values: (see I2ELLIS.H) +// II_DOWN_BADVALID +// II_DOWN_BADFILE +// II_DOWN_CONTINUING +// II_DOWN_GOOD +// II_DOWN_BAD +// II_DOWN_BADSTATE +// II_DOWN_TIMEOUT +// +// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to +// determine whether this is the first block, whether to check for magic +// numbers, how many blocks there are to go... +// +//****************************************************************************** +static int +iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard) +{ + int itemp; + int loadedFirst; + + if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID; + + switch(pB->i2eState) + { + case II_STATE_READY: + + // Loading the first block after reset. Must check the magic number of the + // loadfile, store the number of blocks we expect to load. + if (pSource->e.loadMagic != MAGIC_LOADFILE) + { + return II_DOWN_BADFILE; + } + + // Next we store the total number of blocks to load, including this one. + pB->i2eToLoad = 1 + pSource->e.loadBlocksMore; + + // Set the state, store the version numbers. ('Cause this may have come + // from a file - we might want to report these versions and revisions in + // case of an error! + pB->i2eState = II_STATE_LOADING; + pB->i2eLVersion = pSource->e.loadVersion; + pB->i2eLRevision = pSource->e.loadRevision; + pB->i2eLSub = pSource->e.loadSubRevision; + + // The time and date of compilation is also available but don't bother + // storing it for normal purposes. + loadedFirst = 1; + break; + + case II_STATE_LOADING: + loadedFirst = 0; + break; + + default: + return II_DOWN_BADSTATE; + } + + // Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad + // must be positive still, because otherwise we would have cleaned up last + // time and set the state to II_STATE_LOADED. + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + + if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) { + return II_DOWN_BADVALID; + } + + // If we just loaded the first block, wait for the fifo to empty an extra + // long time to allow for any special startup code in the firmware, like + // sending status messages to the LCD's. + + if (loadedFirst) { + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) { + return II_DOWN_TIMEOUT; + } + } + + // Determine whether this was our last block! + if (--(pB->i2eToLoad)) { + return II_DOWN_CONTINUING; // more to come... + } + + // It WAS our last block: Clean up operations... + // ...Wait for last buffer to drain from the board... + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + // If there were only a single block written, this would come back + // immediately and be harmless, though not strictly necessary. + itemp = MAX_DLOAD_ACK_TIME/10; + while (--itemp) { + if (HAS_INPUT(pB)) { + switch(BYTE_FROM(pB)) + { + case LOADWARE_OK: + pB->i2eState = + isStandard ? II_STATE_STDLOADED :II_STATE_LOADED; + + // Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2) + // will, // if there is a debug port attached, require some + // time to send information to the debug port now. It will do + // this before // executing any of the code we just downloaded. + // It may take up to 700 milliseconds. + if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) { + iiDelay(pB, 700); + } + + return II_DOWN_GOOD; + + case LOADWARE_BAD: + default: + return II_DOWN_BAD; + } + } + + iiDelay(pB, 10); // 10 mS granularity on checking condition + } + + // Drop-through --> timed out waiting for firmware confirmation + + pB->i2eState = II_STATE_BADLOAD; + return II_DOWN_TIMEOUT; +} + +//****************************************************************************** +// Function: iiDownloadAll(pB, pSource, isStandard, size) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// size - size of data to download (in bytes) +// +// Returns: Success or Failure +// +// Description: +// +// Given a pointer to a board structure, a pointer to the beginning of some +// loadware, whether it is considered the "standard loadware", and the size of +// the array in bytes loads the entire array to the board as loadware. +// +// Assumes the board has been freshly reset and the power-up reset message read. +// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be +// too much or too little data to load, or if iiDownloadBlock complains. +//****************************************************************************** +static int +iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size) +{ + int status; + + // We know (from context) board should be ready for the first block of + // download. Complain if not. + if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE; + + while (size > 0) { + size -= LOADWARE_BLOCK_SIZE; // How much data should there be left to + // load after the following operation ? + + // Note we just bump pSource by "one", because its size is actually that + // of an entire block, same as LOADWARE_BLOCK_SIZE. + status = iiDownloadBlock(pB, pSource++, isStandard); + + switch(status) + { + case II_DOWN_GOOD: + return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD); + + case II_DOWN_CONTINUING: + break; + + default: + return status; + } + } + + // We shouldn't drop out: it means "while" caught us with nothing left to + // download, yet the previous DownloadBlock did not return complete. Ergo, + // not enough data to match the size byte in the header. + return II_DOWN_UNDER; +} diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h new file mode 100644 index 000000000000..56fd03fd2962 --- /dev/null +++ b/drivers/char/ip2/i2ellis.h @@ -0,0 +1,609 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// i2ellis.h +// +// IntelliPort-II and IntelliPort-IIEX +// +// Extremely +// Low +// Level +// Interface +// Services +// +// Structure Definitions and declarations for "ELLIS" service routines found in +// i2ellis.c +// +// These routines are based on properties of the IntelliPort-II and -IIEX +// hardware and bootstrap firmware, and are not sensitive to particular +// conventions of any particular loadware. +// +// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material +// here and in i2ellis.c is intended to provice a useful, but not required, +// layer of insulation from the hardware specifics. +//------------------------------------------------------------------------------ +#ifndef I2ELLIS_H /* To prevent multiple includes */ +#define I2ELLIS_H 1 +//------------------------------------------------ +// Revision History: +// +// 30 September 1991 MAG First Draft Started +// 12 October 1991 ...continued... +// +// 20 December 1996 AKM Linux version +//------------------------------------------------- + +//---------------------- +// Mandatory Includes: +//---------------------- +#include "ip2types.h" +#include "i2hw.h" // The hardware definitions + +//------------------------------------------ +// STAT_BOXIDS packets +//------------------------------------------ +#define MAX_BOX 4 + +typedef struct _bidStat +{ + unsigned char bid_value[MAX_BOX]; +} bidStat, *bidStatPtr; + +// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX +// boards, reports the hardware-specific "asynchronous resource register" on +// each expansion box. Boxes not present report 0xff. For -II boards, the first +// element contains 0x80 for 8-port, 0x40 for 4-port boards. + +// Box IDs aka ARR or Async Resource Register (more than you want to know) +// 7 6 5 4 3 2 1 0 +// F F N N L S S S +// ============================= +// F F - Product Family Designator +// =====+++++++++++++++++++++++++++++++ +// 0 0 - Intelliport II EX / ISA-8 +// 1 0 - IntelliServer +// 0 1 - SAC - Port Device (Intelliport III ??? ) +// =====+++++++++++++++++++++++++++++++++++++++ +// N N - Number of Ports +// 0 0 - 8 (eight) +// 0 1 - 4 (four) +// 1 0 - 12 (twelve) +// 1 1 - 16 (sixteen) +// =++++++++++++++++++++++++++++++++++ +// L - LCD Display Module Present +// 0 - No +// 1 - LCD module present +// =========+++++++++++++++++++++++++++++++++++++ +// S S S - Async Signals Supported Designator +// 0 0 0 - 8dss, Mod DCE DB25 Female +// 0 0 1 - 6dss, RJ-45 +// 0 1 0 - RS-232/422 dss, DB25 Female +// 0 1 1 - RS-232/422 dss, separate 232/422 DB25 Female +// 1 0 0 - 6dss, 921.6 I/F with ST654's +// 1 0 1 - RS-423/232 8dss, RJ-45 10Pin +// 1 1 0 - 6dss, Mod DCE DB25 Female +// 1 1 1 - NO BOX PRESENT + +#define FF(c) ((c & 0xC0) >> 6) +#define NN(c) ((c & 0x30) >> 4) +#define L(c) ((c & 0x08) >> 3) +#define SSS(c) (c & 0x07) + +#define BID_HAS_654(x) (SSS(x) == 0x04) +#define BID_NO_BOX 0xff /* no box */ +#define BID_8PORT 0x80 /* IP2-8 port */ +#define BID_4PORT 0x81 /* IP2-4 port */ +#define BID_EXP_MASK 0x30 /* IP2-EX */ +#define BID_EXP_8PORT 0x00 /* 8, */ +#define BID_EXP_4PORT 0x10 /* 4, */ +#define BID_EXP_UNDEF 0x20 /* UNDEF, */ +#define BID_EXP_16PORT 0x30 /* 16, */ +#define BID_LCD_CTRL 0x08 /* LCD Controller */ +#define BID_LCD_NONE 0x00 /* - no controller present */ +#define BID_LCD_PRES 0x08 /* - controller present */ +#define BID_CON_MASK 0x07 /* - connector pinouts */ +#define BID_CON_DB25 0x00 /* - DB-25 F */ +#define BID_CON_RJ45 0x01 /* - rj45 */ + +//------------------------------------------------------------------------------ +// i2eBordStr +// +// This structure contains all the information the ELLIS routines require in +// dealing with a particular board. +//------------------------------------------------------------------------------ +// There are some queues here which are guaranteed to never contain the entry +// for a single channel twice. So they must be slightly larger to allow +// unambiguous full/empty management +// +#define CH_QUEUE_SIZE ABS_MOST_PORTS+2 + +typedef struct _i2eBordStr +{ + porStr i2ePom; // Structure containing the power-on message. + + unsigned short i2ePomSize; + // The number of bytes actually read if + // different from sizeof i2ePom, indicates + // there is an error! + + unsigned short i2eStartMail; + // Contains whatever inbound mailbox data + // present at startup. NO_MAIL_HERE indicates + // nothing was present. No special + // significance as of this writing, but may be + // useful for diagnostic reasons. + + unsigned short i2eValid; + // Indicates validity of the structure; if + // i2eValid == I2E_MAGIC, then we can trust + // the other fields. Some (especially + // initialization) functions are good about + // checking for validity. Many functions do + // not, it being assumed that the larger + // context assures we are using a valid + // i2eBordStrPtr. + + unsigned short i2eError; + // Used for returning an error condition from + // several functions which use i2eBordStrPtr + // as an argument. + + // Accelerators to characterize separate features of a board, derived from a + // number of sources. + + unsigned short i2eFifoSize; + // Always, the size of the FIFO. For + // IntelliPort-II, always the same, for -IIEX + // taken from the Power-On reset message. + + volatile + unsigned short i2eFifoRemains; + // Used during normal operation to indicate a + // lower bound on the amount of data which + // might be in the outbound fifo. + + unsigned char i2eFifoStyle; + // Accelerator which tells which style (-II or + // -IIEX) FIFO we are using. + + unsigned char i2eDataWidth16; + // Accelerator which tells whether we should + // do 8 or 16-bit data transfers. + + unsigned char i2eMaxIrq; + // The highest allowable IRQ, based on the + // slot size. + + unsigned char i2eChangeIrq; + // Whether tis valid to change IRQ's + // ISA = ok, EISA, MicroChannel, no + + // Accelerators for various addresses on the board + int i2eBase; // I/O Address of the Board + int i2eData; // From here data transfers happen + int i2eStatus; // From here status reads happen + int i2ePointer; // (IntelliPort-II: pointer/commands) + int i2eXMail; // (IntelliPOrt-IIEX: mailboxes + int i2eXMask; // (IntelliPort-IIEX: mask write + + //------------------------------------------------------- + // Information presented in a common format across boards + // For each box, bit map of the channels present. Box closest to + // the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable) + // is taken to be box 0. These are derived from product i.d. registers. + + unsigned short i2eChannelMap[ABS_MAX_BOXES]; + + // Same as above, except each is derived from firmware attempting to detect + // the uart presence (by reading a valid GFRCR register). If bits are set in + // i2eChannelMap and not in i2eGoodMap, there is a potential problem. + + unsigned short i2eGoodMap[ABS_MAX_BOXES]; + + // --------------------------- + // For indirect function calls + + // Routine to cause an N-millisecond delay: Patched by the ii2Initialize + // function. + + void (*i2eDelay)(unsigned int); + + // Routine to write N bytes to the board through the FIFO. Returns true if + // all copacetic, otherwise returns false and error is in i2eError field. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Routine to read N bytes from the board through the FIFO. Returns true if + // copacetic, otherwise returns false and error in i2eError. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Returns a word from FIFO. Will use 2 byte operations if needed. + + unsigned short (*i2eReadWord)(struct _i2eBordStr *); + + // Writes a word to FIFO. Will use 2 byte operations if needed. + + void (*i2eWriteWord)(struct _i2eBordStr *, unsigned short); + + // Waits specified time for the Transmit FIFO to go empty. Returns true if + // ok, otherwise returns false and error in i2eError. + + int (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int); + + // Returns true or false according to whether the outgoing mailbox is empty. + + int (*i2eTxMailEmpty)(struct _i2eBordStr *); + + // Checks whether outgoing mailbox is empty. If so, sends mail and returns + // true. Otherwise returns false. + + int (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char); + + // If no mail available, returns NO_MAIL_HERE, else returns the value in the + // mailbox (guaranteed can't be NO_MAIL_HERE). + + unsigned short (*i2eGetMail)(struct _i2eBordStr *); + + // Enables the board to interrupt the host when it writes to the mailbox. + // Irqs will not occur, however, until the loadware separately enables + // interrupt generation to the host. The standard loadware does this in + // response to a command packet sent by the host. (Also, disables + // any other potential interrupt sources from the board -- other than the + // inbound mailbox). + + void (*i2eEnableMailIrq)(struct _i2eBordStr *); + + // Writes an arbitrary value to the mask register. + + void (*i2eWriteMask)(struct _i2eBordStr *, unsigned char); + + + // State information + + // During downloading, indicates the number of blocks remaining to download + // to the board. + + short i2eToLoad; + + // State of board (see manifests below) (e.g., whether in reset condition, + // whether standard loadware is installed, etc. + + unsigned char i2eState; + + // These three fields are only valid when there is loadware running on the + // board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED ) + + unsigned char i2eLVersion; // Loadware version + unsigned char i2eLRevision; // Loadware revision + unsigned char i2eLSub; // Loadware subrevision + + // Flags which only have meaning in the context of the standard loadware. + // Somewhat violates the layering concept, but there is so little additional + // needed at the board level (while much additional at the channel level), + // that this beats maintaining two different per-board structures. + + // Indicates which IRQ the board has been initialized (from software) to use + // For MicroChannel boards, any value different from IRQ_UNDEFINED means + // that the software command has been sent to enable interrupts (or specify + // they are disabled). Special value: IRQ_UNDEFINED indicates that the + // software command to select the interrupt has not yet been sent, therefore + // (since the standard loadware insists that it be sent before any other + // packets are sent) no other packets should be sent yet. + + unsigned short i2eUsingIrq; + + // This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us + // putting more in the mailbox until an appropriate mailbox message is + // received. + + unsigned char i2eWaitingForEmptyFifo; + + // Any mailbox bits waiting to be sent to the board are OR'ed in here. + + unsigned char i2eOutMailWaiting; + + // The head of any incoming packet is read into here, is then examined and + // we dispatch accordingly. + + unsigned short i2eLeadoffWord[1]; + + // Running counter of interrupts where the mailbox indicated incoming data. + + unsigned short i2eFifoInInts; + + // Running counter of interrupts where the mailbox indicated outgoing data + // had been stripped. + + unsigned short i2eFifoOutInts; + + // If not void, gives the address of a routine to call if fatal board error + // is found (only applies to standard l/w). + + void (*i2eFatalTrap)(struct _i2eBordStr *); + + // Will point to an array of some sort of channel structures (whose format + // is unknown at this level, being a function of what loadware is + // installed and the code configuration (max sizes of buffers, etc.)). + + void *i2eChannelPtr; + + // Set indicates that the board has gone fatal. + + unsigned short i2eFatal; + + // The number of elements pointed to by i2eChannelPtr. + + unsigned short i2eChannelCnt; + + // Ring-buffers of channel structures whose channels have particular needs. + + spinlock_t Fbuf_spinlock; + volatile + unsigned short i2Fbuf_strip; // Strip index + volatile + unsigned short i2Fbuf_stuff; // Stuff index + void *i2Fbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // flow control packets. + spinlock_t Dbuf_spinlock; + volatile + unsigned short i2Dbuf_strip; // Strip index + volatile + unsigned short i2Dbuf_stuff; // Stuff index + void *i2Dbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // data or in-line command packets. + spinlock_t Bbuf_spinlock; + volatile + unsigned short i2Bbuf_strip; // Strip index + volatile + unsigned short i2Bbuf_stuff; // Stuff index + void *i2Bbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // bypass command packets. + + /* + * A set of flags to indicate that certain events have occurred on at least + * one of the ports on this board. We use this to decide whether to spin + * through the channels looking for breaks, etc. + */ + int got_input; + int status_change; + bidStat channelBtypes; + + /* + * Debugging counters, etc. + */ + unsigned long debugFlowQueued; + unsigned long debugInlineQueued; + unsigned long debugDataQueued; + unsigned long debugBypassQueued; + unsigned long debugFlowCount; + unsigned long debugInlineCount; + unsigned long debugBypassCount; + + spinlock_t read_fifo_spinlock; + spinlock_t write_fifo_spinlock; + +} i2eBordStr, *i2eBordStrPtr; + +//------------------------------------------------------------------- +// Macro Definitions for the indirect calls defined in the i2eBordStr +//------------------------------------------------------------------- +// +#define iiDelay(a,b) (*(a)->i2eDelay)(b) +#define iiWriteBuf(a,b,c) (*(a)->i2eWriteBuf)(a,b,c) +#define iiReadBuf(a,b,c) (*(a)->i2eReadBuf)(a,b,c) + +#define iiWriteWord(a,b) (*(a)->i2eWriteWord)(a,b) +#define iiReadWord(a) (*(a)->i2eReadWord)(a) + +#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b) + +#define iiTxMailEmpty(a) (*(a)->i2eTxMailEmpty)(a) +#define iiTrySendMail(a,b) (*(a)->i2eTrySendMail)(a,b) + +#define iiGetMail(a) (*(a)->i2eGetMail)(a) +#define iiEnableMailIrq(a) (*(a)->i2eEnableMailIrq)(a) +#define iiDisableMailIrq(a) (*(a)->i2eWriteMask)(a,0) +#define iiWriteMask(a,b) (*(a)->i2eWriteMask)(a,b) + +//------------------------------------------- +// Manifests for i2eBordStr: +//------------------------------------------- + +#define YES 1 +#define NO 0 + +#define NULLFUNC (void (*)(void))0 +#define NULLPTR (void *)0 + +typedef void (*delayFunc_t)(unsigned int); + +// i2eValid +// +#define I2E_MAGIC 0x4251 // Structure is valid. +#define I2E_INCOMPLETE 0x1122 // Structure failed during init. + + +// i2eError +// +#define I2EE_GOOD 0 // Operation successful +#define I2EE_BADADDR 1 // Address out of range +#define I2EE_BADSTATE 2 // Attempt to perform a function when the board + // structure was in the incorrect state +#define I2EE_BADMAGIC 3 // Bad magic number from Power On test (i2ePomSize + // reflects what was read +#define I2EE_PORM_SHORT 4 // Power On message too short +#define I2EE_PORM_LONG 5 // Power On message too long +#define I2EE_BAD_FAMILY 6 // Un-supported board family type +#define I2EE_INCONSIST 7 // Firmware reports something impossible, + // e.g. unexpected number of ports... Almost no + // excuse other than bad FIFO... +#define I2EE_POSTERR 8 // Power-On self test reported a bad error +#define I2EE_BADBUS 9 // Unknown Bus type declared in message +#define I2EE_TXE_TIME 10 // Timed out waiting for TX Fifo to empty +#define I2EE_INVALID 11 // i2eValid field does not indicate a valid and + // complete board structure (for functions which + // require this be so.) +#define I2EE_BAD_PORT 12 // Discrepancy between channels actually found and + // what the product is supposed to have. Check + // i2eGoodMap vs i2eChannelMap for details. +#define I2EE_BAD_IRQ 13 // Someone specified an unsupported IRQ +#define I2EE_NOCHANNELS 14 // No channel structures have been defined (for + // functions requiring this). + +// i2eFifoStyle +// +#define FIFO_II 0 /* IntelliPort-II style: see also i2hw.h */ +#define FIFO_IIEX 1 /* IntelliPort-IIEX style */ + +// i2eGetMail +// +#define NO_MAIL_HERE 0x1111 // Since mail is unsigned char, cannot possibly + // promote to 0x1111. +// i2eState +// +#define II_STATE_COLD 0 // Addresses have been defined, but board not even + // reset yet. +#define II_STATE_RESET 1 // Board,if it exists, has just been reset +#define II_STATE_READY 2 // Board ready for its first block +#define II_STATE_LOADING 3 // Board continuing load +#define II_STATE_LOADED 4 // Board has finished load: status ok +#define II_STATE_BADLOAD 5 // Board has finished load: failed! +#define II_STATE_STDLOADED 6 // Board has finished load: standard firmware + +// i2eUsingIrq +// +#define IRQ_UNDEFINED 0x1352 // No valid irq (or polling = 0) can ever + // promote to this! +//------------------------------------------ +// Handy Macros for i2ellis.c and others +// Note these are common to -II and -IIEX +//------------------------------------------ + +// Given a pointer to the board structure, does the input FIFO have any data or +// not? +// +#define HAS_INPUT(pB) !(INB(pB->i2eStatus) & ST_IN_EMPTY) +#define HAS_NO_INPUT(pB) (INB(pB->i2eStatus) & ST_IN_EMPTY) + +// Given a pointer to board structure, read a byte or word from the fifo +// +#define BYTE_FROM(pB) (unsigned char)INB(pB->i2eData) +#define WORD_FROM(pB) (unsigned short)INW(pB->i2eData) + +// Given a pointer to board structure, is there room for any data to be written +// to the data fifo? +// +#define HAS_OUTROOM(pB) !(INB(pB->i2eStatus) & ST_OUT_FULL) +#define HAS_NO_OUTROOM(pB) (INB(pB->i2eStatus) & ST_OUT_FULL) + +// Given a pointer to board structure, write a single byte to the fifo +// structure. Note that for 16-bit interfaces, the high order byte is undefined +// and unknown. +// +#define BYTE_TO(pB, c) OUTB(pB->i2eData,(c)) + +// Write a word to the fifo structure. For 8-bit interfaces, this may have +// unknown results. +// +#define WORD_TO(pB, c) OUTW(pB->i2eData,(c)) + +// Given a pointer to the board structure, is there anything in the incoming +// mailbox? +// +#define HAS_MAIL(pB) (INB(pB->i2eStatus) & ST_IN_MAIL) + +#define UPDATE_FIFO_ROOM(pB) (pB)->i2eFifoRemains=(pB)->i2eFifoSize + +// Handy macro to round up a number (like the buffer write and read routines do) +// +#define ROUNDUP(number) (((number)+1) & (~1)) + +//------------------------------------------ +// Function Declarations for i2ellis.c +//------------------------------------------ +// +// Functions called directly +// +// Initialization of a board & structure is in four (five!) parts: +// +// 0) iiEllisInit() - Initialize iiEllis subsystem. +// 1) iiSetAddress() - Define the board address & delay function for a board. +// 2) iiReset() - Reset the board (provided it exists) +// -- Note you may do this to several boards -- +// 3) iiResetDelay() - Delay for 2 seconds (once for all boards) +// 4) iiInitialize() - Attempt to read Power-up message; further initialize +// accelerators +// +// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write +// loadware. To change loadware, you must begin again with step 2, resetting +// the board again (step 1 not needed). + +static void iiEllisInit(void); +static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t ); +static int iiReset(i2eBordStrPtr); +static int iiResetDelay(i2eBordStrPtr); +static int iiInitialize(i2eBordStrPtr); + +// Routine to validate that all channels expected are there. +// +extern int iiValidateChannels(i2eBordStrPtr); + +// Routine used to download a block of loadware. +// +static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int); + +// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile: +// +#define II_DOWN_BADVALID 0 // board structure is invalid +#define II_DOWN_CONTINUING 1 // So far, so good, firmware expects more +#define II_DOWN_GOOD 2 // Download complete, CRC good +#define II_DOWN_BAD 3 // Download complete, but CRC bad +#define II_DOWN_BADFILE 4 // Bad magic number in loadware file +#define II_DOWN_BADSTATE 5 // Board is in an inappropriate state for + // downloading loadware. (see i2eState) +#define II_DOWN_TIMEOUT 6 // Timeout waiting for firmware +#define II_DOWN_OVER 7 // Too much data +#define II_DOWN_UNDER 8 // Not enough data +#define II_DOWN_NOFILE 9 // Loadware file not found + +// Routine to download an entire loadware module: Return values are a subset of +// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING +// +static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int); + +// Called indirectly always. Needed externally so the routine might be +// SPECIFIED as an argument to iiReset() +// +//static void ii2DelayIO(unsigned int); // N-millisecond delay using + //hardware spin +//static void ii2DelayTimer(unsigned int); // N-millisecond delay using Linux + //timer + +// Many functions defined here return True if good, False otherwise, with an +// error code in i2eError field. Here is a handy macro for setting the error +// code and returning. +// +#define COMPLETE(pB,code) \ + if(1){ \ + pB->i2eError = code; \ + return (code == I2EE_GOOD);\ + } + +#endif // I2ELLIS_H diff --git a/drivers/char/ip2/i2hw.h b/drivers/char/ip2/i2hw.h new file mode 100644 index 000000000000..9e5b07ca0510 --- /dev/null +++ b/drivers/char/ip2/i2hw.h @@ -0,0 +1,648 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions limited to properties of the hardware or the +* bootstrap firmware. As such, they are applicable regardless of +* operating system or loadware (standard or diagnostic). +* +*******************************************************************************/ +#ifndef I2HW_H +#define I2HW_H 1 +//------------------------------------------------------------------------------ +// Revision History: +// +// 23 September 1991 MAG First Draft Started...through... +// 11 October 1991 ... Continuing development... +// 6 August 1993 Added support for ISA-4 (asic) which is architected +// as an ISA-CEX with a single 4-port box. +// +// 20 December 1996 AKM Version for Linux +// +//------------------------------------------------------------------------------ +/*------------------------------------------------------------------------------ + +HARDWARE DESCRIPTION: + +Introduction: + +The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8) +addresses in the host's I/O space. + +Some addresses are used to transfer data to/from the board, some to transfer +so-called "mailbox" messages, and some to read bit-mapped status information. +While all the products in the line are functionally similar, some use a 16-bit +data path to transfer data while others use an 8-bit path. Also, the use of +command /status/mailbox registers differs slightly between the II and IIEX +branches of the family. + +The host determines what type of board it is dealing with by reading a string of +sixteen characters from the board. These characters are always placed in the +fifo by the board's local processor whenever the board is reset (either from +power-on or under software control) and are known as the "Power-on Reset +Message." In order that this message can be read from either type of board, the +hardware registers used in reading this message are the same. Once this message +has been read by the host, then it has the information required to operate. + +General Differences between boards: + +The greatest structural difference is between the -II and -IIEX families of +product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support +the data path, mailbox registers, and status registers. This chip contains some +features which are not used in the IntelliPort-II products; a description of +these is omitted here. Because of these many features, it contains many +registers, too many to access directly within a small address space. They are +accessed by first writing a value to a "pointer" register. This value selects +the register to be accessed. The next read or write to that address accesses +the selected register rather than the pointer register. + +The -IIEX boards use a proprietary design similar to the Am4701 in function. But +because of a simpler, more streamlined design it doesn't require so many +registers. This means they can be accessed directly in single operations rather +than through a pointer register. + +Besides these differences, there are differences in whether 8-bit or 16-bit +transfers are used to move data to the board. + +The -II boards are capable only of 8-bit data transfers, while the -IIEX boards +may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP +switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit +transfers are supported (and will be expected by the standard loadware). The +on-board firmware can determine the position of the switch, and whether the +board is installed in a 16-bit slot; it supplies this information to the host as +part of the power-up reset message. + +The configuration switch (#8) and slot selection do not directly configure the +hardware. It is up to the on-board loadware and host-based drivers to act +according to the selected options. That is, loadware and drivers could be +written to perform 8-bit transfers regardless of the state of the DIP switch or +slot (and in a diagnostic environment might well do so). Likewise, 16-bit +transfers could be performed as long as the card is in a 16-bit slot. + +Note the slot selection and DIP switch selection are provided separately: a +board running in 8-bit mode in a 16-bit slot has a greater range of possible +interrupts to choose from; information of potential use to the host. + +All 8-bit data transfers are done in the same way, regardless of whether on a +-II board or a -IIEX board. + +The host must consider two things then: 1) whether a -II or -IIEX product is +being used, and 2) whether an 8-bit or 16-bit data path is used. + +A further difference is that -II boards always have a 512-byte fifo operating in +each direction. -IIEX boards may use fifos of varying size; this size is +reported as part of the power-up message. + +I/O Map Of IntelliPort-II and IntelliPort-IIEX boards: +(Relative to the chosen base address) + +Addr R/W IntelliPort-II IntelliPort-IIEX +---- --- -------------- ---------------- +0 R/W Data Port (byte) Data Port (byte or word) +1 R/W (Not used) (MSB of word-wide data written to Data Port) +2 R Status Register Status Register +2 W Pointer Register Interrupt Mask Register +3 R/W (Not used) Mailbox Registers (6 bits: 11111100) +4,5 -- Reserved for future products +6 -- Reserved for future products +7 R Guaranteed to have no effect +7 W Hardware reset of board. + + +Rules: +All data transfers are performed using the even i/o address. If byte-wide data +transfers are being used, do INB/OUTB operations on the data port. If word-wide +transfers are used, do INW/OUTW operations. In some circumstances (such as +reading the power-up message) you will do INB from the data port, but in this +case the MSB of each word read is lost. When accessing all other unreserved +registers, use byte operations only. +------------------------------------------------------------------------------*/ + +//------------------------------------------------ +// Mandatory Includes: +//------------------------------------------------ +// +#include "ip2types.h" +#include "i2os.h" /* For any o.s., compiler, or host-related issues */ + +//------------------------------------------------------------------------- +// Manifests for the I/O map: +//------------------------------------------------------------------------- +// R/W: Data port (byte) for IntelliPort-II, +// R/W: Data port (byte or word) for IntelliPort-IIEX +// Incoming or outgoing data passes through a FIFO, the status of which is +// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is +// the primary means of transferring data, commands, flow-control, and status +// information between the host and board. +// +#define FIFO_DATA 0 + +// Another way of passing information between the the board and the host is +// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of +// data. Writing data to the mailbox causes a status bit to be set, and +// potentially interrupting the intended receiver. The sender has some way to +// determine whether the data has been read yet; as soon as it has, it may send +// more. The mailboxes are handled differently on -II and -IIEX products, as +// suggested below. +//------------------------------------------------------------------------------ +// Read: Status Register for IntelliPort-II or -IIEX +// The presence of any bit set here will cause an interrupt to the host, +// provided the corresponding bit has been unmasked in the interrupt mask +// register. Furthermore, interrupts to the host are disabled globally until the +// loadware selects the irq line to use. With the exception of STN_MR, the bits +// remain set so long as the associated condition is true. +// +#define FIFO_STATUS 2 + +// Bit map of status bits which are identical for -II and -IIEX +// +#define ST_OUT_FULL 0x40 // Outbound FIFO full +#define ST_IN_EMPTY 0x20 // Inbound FIFO empty +#define ST_IN_MAIL 0x04 // Inbound Mailbox full + +// The following exists only on the Intelliport-IIEX, and indicates that the +// board has not read the last outgoing mailbox data yet. In the IntelliPort-II, +// the outgoing mailbox may be read back: a zero indicates the board has read +// the data. +// +#define STE_OUT_MAIL 0x80 // Outbound mailbox full (!) + +// The following bits are defined differently for -II and -IIEX boards. Code +// which relies on these bits will need to be functionally different for the two +// types of boards and should be generally avoided because of the additional +// complexity this creates: + +// Bit map of status bits only on -II + +// Fifo has been RESET (cleared when the status register is read). Note that +// this condition cannot be masked and would always interrupt the host, except +// that the hardware reset also disables interrupts globally from the board +// until re-enabled by loadware. This could also arise from the +// Am4701-supported command to reset the chip, but this command is generally not +// used here. +// +#define STN_MR 0x80 + +// See the AMD Am4701 data sheet for details on the following four bits. They +// are not presently used by Computone drivers. +// +#define STN_OUT_AF 0x10 // Outbound FIFO almost full (programmable) +#define STN_IN_AE 0x08 // Inbound FIFO almost empty (programmable) +#define STN_BD 0x02 // Inbound byte detected +#define STN_PE 0x01 // Parity/Framing condition detected + +// Bit-map of status bits only on -IIEX +// +#define STE_OUT_HF 0x10 // Outbound FIFO half full +#define STE_IN_HF 0x08 // Inbound FIFO half full +#define STE_IN_FULL 0x02 // Inbound FIFO full +#define STE_OUT_MT 0x01 // Outbound FIFO empty + +//------------------------------------------------------------------------------ + +// Intelliport-II -- Write Only: the pointer register. +// Values are written to this register to select the Am4701 internal register to +// be accessed on the next operation. +// +#define FIFO_PTR 0x02 + +// Values for the pointer register +// +#define SEL_COMMAND 0x1 // Selects the Am4701 command register + +// Some possible commands: +// +#define SEL_CMD_MR 0x80 // Am4701 command to reset the chip +#define SEL_CMD_SH 0x40 // Am4701 command to map the "other" port into the + // status register. +#define SEL_CMD_UNSH 0 // Am4701 command to "unshift": port maps into its + // own status register. +#define SEL_MASK 0x2 // Selects the Am4701 interrupt mask register. The + // interrupt mask register is bit-mapped to match + // the status register (FIFO_STATUS) except for + // STN_MR. (See above.) +#define SEL_BYTE_DET 0x3 // Selects the Am4701 byte-detect register. (Not + // normally used except in diagnostics.) +#define SEL_OUTMAIL 0x4 // Selects the outbound mailbox (R/W). Reading back + // a value of zero indicates that the mailbox has + // been read by the board and is available for more + // data./ Writing to the mailbox optionally + // interrupts the board, depending on the loadware's + // setting of its interrupt mask register. +#define SEL_AEAF 0x5 // Selects AE/AF threshold register. +#define SEL_INMAIL 0x6 // Selects the inbound mailbox (Read) + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX -- Write Only: interrupt mask (and misc flags) register: +// Unlike IntelliPort-II, bit assignments do NOT match those of the status +// register. +// +#define FIFO_MASK 0x2 + +// Mailbox readback select: +// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If +// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox. +// This is the normal situation. The clearing of a mailbox is determined on +// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback +// capability is provided for diagnostic purposes only. +// +#define MX_OUTMAIL_RSEL 0x80 + +#define MX_IN_MAIL 0x40 // Enables interrupts when incoming mailbox goes + // full (ST_IN_MAIL set). +#define MX_IN_FULL 0x20 // Enables interrupts when incoming FIFO goes full + // (STE_IN_FULL). +#define MX_IN_MT 0x08 // Enables interrupts when incoming FIFO goes empty + // (ST_IN_MT). +#define MX_OUT_FULL 0x04 // Enables interrupts when outgoing FIFO goes full + // (ST_OUT_FULL). +#define MX_OUT_MT 0x01 // Enables interrupts when outgoing FIFO goes empty + // (STE_OUT_MT). + +// Any remaining bits are reserved, and should be written to ZERO for +// compatibility with future Computone products. + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two +// bits always read back 0). +// Read: One of the mailboxes, usually Inbound. +// Inbound Mailbox (MX_OUTMAIL_RSEL = 0) +// Outbound Mailbox (MX_OUTMAIL_RSEL = 1) +// Write: Outbound Mailbox +// For the IntelliPort-II boards, the outbound mailbox is read back to determine +// whether the board has read the data (0 --> data has been read). For the +// IntelliPort-IIEX, this is done by reading a status register. To determine +// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit +// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by +// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the +// case with the -II boards. For this reason, FIFO_MAIL is normally used to read +// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for +// MX_OUTMAIL_RSEL description.) +// +#define FIFO_MAIL 0x3 + +//------------------------------------------------------------------------------ +// WRITE ONLY: Resets the board. (Data doesn't matter). +// +#define FIFO_RESET 0x7 + +//------------------------------------------------------------------------------ +// READ ONLY: Will have no effect. (Data is undefined.) +// Actually, there will be an effect, in that the operation is sure to generate +// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short +// delays when no comparable time constant is available. +// +#define FIFO_NOP 0x7 + +//------------------------------------------------------------------------------ +// RESET & POWER-ON RESET MESSAGE +/*------------------------------------------------------------------------------ +RESET: + +The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel +reset, and via a write to the reset register described above. For products using +the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses, +the Power-up and channel reset sources cause additional hardware initialization +which should only occur at system startup time. + +The third type of reset, called a "command reset", is done by writing any data +to the FIFO_RESET address described above. This resets the on-board processor, +FIFO, UARTS, and associated hardware. + +This passes control of the board to the bootstrap firmware, which performs a +Power-On Self Test and which detects its current configuration. For example, +-IIEX products determine the size of FIFO which has been installed, and the +number and type of expansion boxes attached. + +This and other information is then written to the FIFO in a 16-byte data block +to be read by the host. This block is guaranteed to be present within two (2) +seconds of having received the command reset. The firmware is now ready to +receive loadware from the host. + +It is good practice to perform a command reset to the board explicitly as part +of your software initialization. This allows your code to properly restart from +a soft boot. (Many systems do not issue channel reset on soft boot). + +Because of a hardware reset problem on some of the Cirrus Logic 1400's which are +used on the product, it is recommended that you reset the board twice, separated +by an approximately 50 milliseconds delay. (VERY approximately: probably ok to +be off by a factor of five. The important point is that the first command reset +in fact generates a reset pulse on the board. This pulse is guaranteed to last +less than 10 milliseconds. The additional delay ensures the 1400 has had the +chance to respond sufficiently to the first reset. Why not a longer delay? Much +more than 50 milliseconds gets to be noticable, but the board would still work. + +Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap +firmware is ready to receive loadware. + +Note on Power-on Reset Message format: +The various fields have been designed with future expansion in view. +Combinations of bitfields and values have been defined which define products +which may not currently exist. This has been done to allow drivers to anticipate +the possible introduction of products in a systematic fashion. This is not +intended to suggest that each potential product is actually under consideration. +------------------------------------------------------------------------------*/ + +//---------------------------------------- +// Format of Power-on Reset Message +//---------------------------------------- + +typedef union _porStr // "por" stands for Power On Reset +{ + unsigned char c[16]; // array used when considering the message as a + // string of undifferentiated characters + + struct // Elements used when considering values + { + // The first two bytes out of the FIFO are two magic numbers. These are + // intended to establish that there is indeed a member of the + // IntelliPort-II(EX) family present. The remaining bytes may be + // expected // to be valid. When reading the Power-on Reset message, + // if the magic numbers do not match it is probably best to stop + // reading immediately. You are certainly not reading our board (unless + // hardware is faulty), and may in fact be reading some other piece of + // hardware. + + unsigned char porMagic1; // magic number: first byte == POR_MAGIC_1 + unsigned char porMagic2; // magic number: second byte == POR_MAGIC_2 + + // The Version, Revision, and Subrevision are stored as absolute numbers + // and would normally be displayed in the format V.R.S (e.g. 1.0.2) + + unsigned char porVersion; // Bootstrap firmware version number + unsigned char porRevision; // Bootstrap firmware revision number + unsigned char porSubRev; // Bootstrap firmware sub-revision number + + unsigned char porID; // Product ID: Bit-mapped according to + // conventions described below. Among other + // things, this allows us to distinguish + // IntelliPort-II boards from IntelliPort-IIEX + // boards. + + unsigned char porBus; // IntelliPort-II: Unused + // IntelliPort-IIEX: Bus Information: + // Bit-mapped below + + unsigned char porMemory; // On-board DRAM size: in 32k blocks + + // porPorts1 (and porPorts2) are used to determine the ports which are + // available to the board. For non-expandable product, a single number + // is sufficient. For expandable product, the board may be connected + // to as many as four boxes. Each box may be (so far) either a 16-port + // or an 8-port size. Whenever an 8-port box is used, the remaining 8 + // ports leave gaps between existing channels. For that reason, + // expandable products must report a MAP of available channels. Since + // each UART supports four ports, we represent each UART found by a + // single bit. Using two bytes to supply the mapping information we + // report the presense or absense of up to 16 UARTS, or 64 ports in + // steps of 4 ports. For -IIEX products, the ports are numbered + // starting at the box closest to the controller in the "chain". + + // Interpreted Differently for IntelliPort-II and -IIEX. + // -II: Number of ports (Derived actually from product ID). See + // Diag1&2 to indicate if uart was actually detected. + // -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This + // bitmap is based on detecting the uarts themselves; + // see porFlags for information from the box i.d's. + unsigned char porPorts1; + + unsigned char porDiag1; // Results of on-board P.O.S.T, 1st byte + unsigned char porDiag2; // Results of on-board P.O.S.T, 2nd byte + unsigned char porSpeed; // Speed of local CPU: given as MHz x10 + // e.g., 16.0 MHz CPU is reported as 160 + unsigned char porFlags; // Misc information (see manifests below) + // Bit-mapped: CPU type, UART's present + + unsigned char porPorts2; // -II: Undefined + // -IIEX: Bit-map of UARTS found, MSB (see + // above for LSB) + + // IntelliPort-II: undefined + // IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the + // host interface FIFO, in each direction. When running the -IIEX in + // 8-bit mode, fifo capacity is halved. The bootstrap firmware will + // have already accounted for this fact in generating this number. + unsigned char porFifoSize; + + // IntelliPort-II: undefined + // IntelliPort-IIEX: The number of boxes connected. (Presently 1-4) + unsigned char porNumBoxes; + } e; +} porStr, *porStrPtr; + +//-------------------------- +// Values for porStr fields +//-------------------------- + +//--------------------- +// porMagic1, porMagic2 +//---------------------- +// +#define POR_MAGIC_1 0x96 // The only valid value for porMagic1 +#define POR_MAGIC_2 0x35 // The only valid value for porMagic2 +#define POR_1_INDEX 0 // Byte position of POR_MAGIC_1 +#define POR_2_INDEX 1 // Ditto for POR_MAGIC_2 + +//---------------------- +// porID +//---------------------- +// +#define POR_ID_FAMILY 0xc0 // These bits indicate the general family of + // product. +#define POR_ID_FII 0x00 // Family is "IntelliPort-II" +#define POR_ID_FIIEX 0x40 // Family is "IntelliPort-IIEX" + +// These bits are reserved, presently zero. May be used at a later date to +// convey other product information. +// +#define POR_ID_RESERVED 0x3c + +#define POR_ID_SIZE 0x03 // Remaining bits indicate number of ports & + // Connector information. +#define POR_ID_II_8 0x00 // For IntelliPort-II, indicates 8-port using + // standard brick. +#define POR_ID_II_8R 0x01 // For IntelliPort-II, indicates 8-port using + // RJ11's (no CTS) +#define POR_ID_II_6 0x02 // For IntelliPort-II, indicates 6-port using + // RJ45's +#define POR_ID_II_4 0x03 // For IntelliPort-II, indicates 4-port using + // 4xRJ45 connectors +#define POR_ID_EX 0x00 // For IntelliPort-IIEX, indicates standard + // expandable controller (other values reserved) + +//---------------------- +// porBus +//---------------------- + +// IntelliPort-IIEX only: Board is installed in a 16-bit slot +// +#define POR_BUS_SLOT16 0x20 + +// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface +// operation. +// +#define POR_BUS_DIP16 0x10 + +// Bits 0-2 indicate type of bus: This information is stored in the bootstrap +// loadware, different loadware being used on different products for different +// buses. For most situations, the drivers do not need this information; but it +// is handy in a diagnostic environment. For example, on microchannel boards, +// you would not want to try to test several interrupts, only the one for which +// you were configured. +// +#define POR_BUS_TYPE 0x07 + +// Unknown: this product doesn't know what bus it is running in. (e.g. if same +// bootstrap firmware were wanted for two different buses.) +// +#define POR_BUS_T_UNK 0 + +// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK +// state, since the same bootstrap firmware is used for each. + +#define POR_BUS_T_MCA 1 // MCA BUS */ +#define POR_BUS_T_EISA 2 // EISA BUS */ +#define POR_BUS_T_ISA 3 // ISA BUS */ + +// Values 4-7 Reserved + +// Remaining bits are reserved + +//---------------------- +// porDiag1 +//---------------------- + +#define POR_BAD_MAPPER 0x80 // HW failure on P.O.S.T: Chip mapper failed + +// These two bits valid only for the IntelliPort-II +// +#define POR_BAD_UART1 0x01 // First 1400 bad +#define POR_BAD_UART2 0x02 // Second 1400 bad + +//---------------------- +// porDiag2 +//---------------------- + +#define POR_DEBUG_PORT 0x80 // debug port was detected by the P.O.S.T +#define POR_DIAG_OK 0x00 // Indicates passage: Failure codes not yet + // available. + // Other bits undefined. +//---------------------- +// porFlags +//---------------------- + +#define POR_CPU 0x03 // These bits indicate supposed CPU type +#define POR_CPU_8 0x01 // Board uses an 80188 (no such thing yet) +#define POR_CPU_6 0x02 // Board uses an 80186 (all existing products) +#define POR_CEX4 0x04 // If set, this is an ISA-CEX/4: An ISA-4 (asic) + // which is architected like an ISA-CEX connected + // to a (hitherto impossible) 4-port box. +#define POR_BOXES 0xf0 // Valid for IntelliPort-IIEX only: Map of Box + // sizes based on box I.D. +#define POR_BOX_16 0x10 // Set indicates 16-port, clear 8-port + +//------------------------------------- +// LOADWARE and DOWNLOADING CODE +//------------------------------------- + +/* +Loadware may be sent to the board in two ways: +1) It may be read from a (binary image) data file block by block as each block + is sent to the board. This is only possible when the initialization is + performed by code which can access your file system. This is most suitable + for diagnostics and appications which use the interface library directly. + +2) It may be hard-coded into your source by including a .h file (typically + supplied by Computone), which declares a data array and initializes every + element. This acheives the same result as if an entire loadware file had + been read into the array. + + This requires more data space in your program, but access to the file system + is not required. This method is more suited to driver code, which typically + is running at a level too low to access the file system directly. + +At present, loadware can only be generated at Computone. + +All Loadware begins with a header area which has a particular format. This +includes a magic number which identifies the file as being (purportedly) +loadware, CRC (for the loader), and version information. +*/ + + +//----------------------------------------------------------------------------- +// Format of loadware block +// +// This is defined as a union so we can pass a pointer to one of these items +// and (if it is the first block) pick out the version information, etc. +// +// Otherwise, to deal with this as a simple character array +//------------------------------------------------------------------------------ + +#define LOADWARE_BLOCK_SIZE 512 // Number of bytes in each block of loadware + +typedef union _loadHdrStr +{ + unsigned char c[LOADWARE_BLOCK_SIZE]; // Valid for every block + + struct // These fields are valid for only the first block of loadware. + { + unsigned char loadMagic; // Magic number: see below + unsigned char loadBlocksMore; // How many more blocks? + unsigned char loadCRC[2]; // Two CRC bytes: used by loader + unsigned char loadVersion; // Version number + unsigned char loadRevision; // Revision number + unsigned char loadSubRevision; // Sub-revision number + unsigned char loadSpares[9]; // Presently unused + unsigned char loadDates[32]; // Null-terminated string which can give + // date and time of compilation + } e; +} loadHdrStr, *loadHdrStrPtr; + +//------------------------------------ +// Defines for downloading code: +//------------------------------------ + +// The loadMagic field in the first block of the loadfile must be this, else the +// file is not valid. +// +#define MAGIC_LOADFILE 0x3c + +// How do we know the load was successful? On completion of the load, the +// bootstrap firmware returns a code to indicate whether it thought the download +// was valid and intends to execute it. These are the only possible valid codes: +// +#define LOADWARE_OK 0xc3 // Download was ok +#define LOADWARE_BAD 0x5a // Download was bad (CRC error) + +// Constants applicable to writing blocks of loadware: +// The first block of loadware might take 600 mS to load, in extreme cases. +// (Expandable board: worst case for sending startup messages to the LCD's). +// The 600mS figure is not really a calculation, but a conservative +// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks. +// +#define MAX_DLOAD_START_TIME 1000 // 1000 mS +#define MAX_DLOAD_READ_TIME 100 // 100 mS + +// Firmware should respond with status (see above) within this long of host +// having sent the final block. +// +#define MAX_DLOAD_ACK_TIME 100 // 100 mS, again! + +//------------------------------------------------------ +// MAXIMUM NUMBER OF PORTS PER BOARD: +// This is fixed for now (with the expandable), but may +// be expanding according to even newer products. +//------------------------------------------------------ +// +#define ABS_MAX_BOXES 4 // Absolute most boxes per board +#define ABS_BIGGEST_BOX 16 // Absolute the most ports per box +#define ABS_MOST_PORTS (ABS_MAX_BOXES * ABS_BIGGEST_BOX) + +#endif // I2HW_H + diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c new file mode 100644 index 000000000000..f8d6f3b169b2 --- /dev/null +++ b/drivers/char/ip2/i2lib.c @@ -0,0 +1,2255 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: High-level interface code for the device driver. Uses the +* Extremely Low Level Interface Support (i2ellis.c). Provides an +* interface to the standard loadware, to support drivers or +* application code. (This is included source code, not a separate +* compilation module.) +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Note on Strategy: +// Once the board has been initialized, it will interrupt us when: +// 1) It has something in the fifo for us to read (incoming data, flow control +// packets, or whatever). +// 2) It has stripped whatever we have sent last time in the FIFO (and +// consequently is ready for more). +// +// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This +// worsens performance considerably, but is done so that a great many channels +// might use only a little memory. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Revision History: +// +// 0.00 - 4/16/91 --- First Draft +// 0.01 - 4/29/91 --- 1st beta release +// 0.02 - 6/14/91 --- Changes to allow small model compilation +// 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with +// in-line asm added for moving data to/from ring buffers, +// replacing a variety of methods used previously. +// 0.04 - 6/21/91 MAG Initial flow-control packets not queued until +// i2_enable_interrupts time. Former versions would enqueue +// them at i2_init_channel time, before we knew how many +// channels were supposed to exist! +// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now; +// supports new 16-bit protocol and expandable boards. +// - 10/24/91 MAG Most changes in place and stable. +// 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no +// argument. +// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt +// level (mostly responses to specific commands.) +// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet +// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE +// turning on the interrupt. +// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check +// some incoming. +// +// 1.1 - 12/25/96 AKM Linux version. +// - 10/09/98 DMC Revised Linux version. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include +#include "i2lib.h" + + +//*********************** +//* Function Prototypes * +//*********************** +static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int); +static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int ); +static void i2StripFifo(i2eBordStrPtr); +static void i2StuffFifoBypass(i2eBordStrPtr); +static void i2StuffFifoFlow(i2eBordStrPtr); +static void i2StuffFifoInline(i2eBordStrPtr); +static int i2RetryFlushOutput(i2ChanStrPtr); + +// Not a documented part of the library routines (careful...) but the Diagnostic +// i2diag.c finds them useful to help the throughput in certain limited +// single-threaded operations. +static void iiSendPendingMail(i2eBordStrPtr); +static void serviceOutgoingFifo(i2eBordStrPtr); + +// Functions defined in ip2.c as part of interrupt handling +static void do_input(i2ChanStrPtr); +static void do_status(i2ChanStrPtr); + +//*************** +//* Debug Data * +//*************** +#ifdef DEBUG_FIFO + +unsigned char DBGBuf[0x4000]; +unsigned short I = 0; + +static void +WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) +{ + char *p = src; + + // XXX: We need a spin lock here if we ever use this again + + while (*s) { // copy label + DBGBuf[I] = *s++; + I = I++ & 0x3fff; + } + while (n--) { // copy data + DBGBuf[I] = *p++; + I = I++ & 0x3fff; + } +} + +static void +fatality(i2eBordStrPtr pB ) +{ + int i; + + for (i=0;i= ' ' && DBGBuf[i] <= '~') { + printk(" %c ",DBGBuf[i]); + } else { + printk(" . "); + } + } + printk("\n"); + printk("Last index %x\n",I); +} +#endif /* DEBUG_FIFO */ + +//******** +//* Code * +//******** + +inline int +i2Validate ( i2ChanStrPtr pCh ) +{ + //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity, + // (CHANNEL_MAGIC | CHANNEL_SUPPORT)); + return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) + == (CHANNEL_MAGIC | CHANNEL_SUPPORT)); +} + +//****************************************************************************** +// Function: iiSendPendingMail(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// If any outgoing mail bits are set and there is outgoing mailbox is empty, +// send the mail and clear the bits. +//****************************************************************************** +static inline void +iiSendPendingMail(i2eBordStrPtr pB) +{ + if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) + { + if (iiTrySendMail(pB, pB->i2eOutMailWaiting)) + { + /* If we were already waiting for fifo to empty, + * or just sent MB_OUT_STUFFED, then we are + * still waiting for it to empty, until we should + * receive an MB_IN_STRIPPED from the board. + */ + pB->i2eWaitingForEmptyFifo |= + (pB->i2eOutMailWaiting & MB_OUT_STUFFED); + pB->i2eOutMailWaiting = 0; + } + } +} + +//****************************************************************************** +// Function: i2InitChannels(pB, nChannels, pCh) +// Parameters: Pointer to Ellis Board structure +// Number of channels to initialize +// Pointer to first element in an array of channel structures +// Returns: Success or failure +// +// Description: +// +// This function patches pointers, back-pointers, and initializes all the +// elements in the channel structure array. +// +// This should be run after the board structure is initialized, through having +// loaded the standard loadware (otherwise it complains). +// +// In any case, it must be done before any serious work begins initializing the +// irq's or sending commands... +// +//****************************************************************************** +static int +i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) +{ + int index, stuffIndex; + i2ChanStrPtr *ppCh; + + if (pB->i2eValid != I2E_MAGIC) { + COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_STDLOADED) { + COMPLETE(pB, I2EE_BADSTATE); + } + + LOCK_INIT(&pB->read_fifo_spinlock); + LOCK_INIT(&pB->write_fifo_spinlock); + LOCK_INIT(&pB->Dbuf_spinlock); + LOCK_INIT(&pB->Bbuf_spinlock); + LOCK_INIT(&pB->Fbuf_spinlock); + + // NO LOCK needed yet - this is init + + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nChannels; + + pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0; + pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0; + pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0; + + memset ( pCh, 0, sizeof (i2ChanStr) * nChannels ); + + for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf); + nChannels && index < ABS_MOST_PORTS; + index++) + { + if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { + continue; + } + LOCK_INIT(&pCh->Ibuf_spinlock); + LOCK_INIT(&pCh->Obuf_spinlock); + LOCK_INIT(&pCh->Cbuf_spinlock); + LOCK_INIT(&pCh->Pbuf_spinlock); + // NO LOCK needed yet - this is init + // Set up validity flag according to support level + if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { + pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT; + } else { + pCh->validity = CHANNEL_MAGIC; + } + pCh->pMyBord = pB; /* Back-pointer */ + + // Prepare an outgoing flow-control packet to send as soon as the chance + // occurs. + if ( pCh->validity & CHANNEL_SUPPORT ) { + pCh->infl.hd.i2sChannel = index; + pCh->infl.hd.i2sCount = 5; + pCh->infl.hd.i2sType = PTYPE_BYPASS; + pCh->infl.fcmd = 37; + pCh->infl.asof = 0; + pCh->infl.room = IBUF_SIZE - 1; + + pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full + + // The following is similar to calling i2QueueNeeds, except that this + // is done in longhand, since we are setting up initial conditions on + // many channels at once. + pCh->channelNeeds = NEED_FLOW; // Since starting from scratch + pCh->sinceLastFlow = 0; // No bytes received since last flow + // control packet was queued + stuffIndex++; + *ppCh++ = pCh; // List this channel as needing + // initial flow control packet sent + } + + // Don't allow anything to be sent until the status packets come in from + // the board. + + pCh->outfl.asof = 0; + pCh->outfl.room = 0; + + // Initialize all the ring buffers + + pCh->Ibuf_stuff = pCh->Ibuf_strip = 0; + pCh->Obuf_stuff = pCh->Obuf_strip = 0; + pCh->Cbuf_stuff = pCh->Cbuf_strip = 0; + + memset( &pCh->icount, 0, sizeof (struct async_icount) ); + pCh->hotKeyIn = HOT_CLEAR; + pCh->channelOptions = 0; + pCh->bookMarks = 0; + pCh->pBookmarkWait = NULL; + + pCh->open_wait = NULL; + pCh->close_wait = NULL; + pCh->delta_msr_wait = NULL; + + // Set base and divisor so default custom rate is 9600 + pCh->BaudBase = 921600; // MAX for ST654, changed after we get + pCh->BaudDivisor = 96; // the boxids (UART types) later + + pCh->dataSetIn = 0; + pCh->dataSetOut = 0; + + pCh->wopen = 0; + pCh->throttled = 0; + + pCh->speed = CBR_9600; + + pCh->flags = 0; + pCh->session = 0; + pCh->pgrp = 0; + + pCh->ClosingDelay = 5*HZ/10; + pCh->ClosingWaitTime = 30*HZ; + +#ifdef USE_IQ + // Initialize task queue objects + pCh->tqueue_input.routine = (void(*)(void*)) do_input; + pCh->tqueue_input.data = pCh; + pCh->tqueue_status.routine = (void(*)(void*)) do_status; + pCh->tqueue_status.data = pCh; +#endif + + pCh->trace = ip2trace; + + ++pCh; + --nChannels; + } + // No need to check for wrap here; this is initialization. + pB->i2Fbuf_stuff = stuffIndex; + COMPLETE(pB, I2EE_GOOD); + +} + +//****************************************************************************** +// Function: i2DeQueueNeeds(pB, type) +// Parameters: Pointer to a board structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: +// Pointer to a channel structure +// +// Description: Returns pointer struct of next channel that needs service of +// the type specified. Otherwise returns a NULL reference. +// +//****************************************************************************** +static i2ChanStrPtr +i2DeQueueNeeds(i2eBordStrPtr pB, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + i2ChanStrPtr pCh = NULL; + + switch(type) { + + case NEED_INLINE: + + WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) + { + queueIndex = pB->i2Dbuf_strip; + pCh = pB->i2Dbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Dbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_INLINE; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + break; + + case NEED_BYPASS: + + WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) + { + queueIndex = pB->i2Bbuf_strip; + pCh = pB->i2Bbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Bbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_BYPASS; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + break; + + case NEED_FLOW: + + WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) + { + queueIndex = pB->i2Fbuf_strip; + pCh = pB->i2Fbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Fbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_FLOW; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + break; + default: + printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); + break; + } + return pCh; +} + +//****************************************************************************** +// Function: i2QueueNeeds(pB, pCh, type) +// Parameters: Pointer to a board structure +// Pointer to a channel structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: Nothing +// +// Description: +// For each type of need selected, if the given channel is not already in the +// queue, adds it, and sets the flag indicating it is in the queue. +//****************************************************************************** +static void +i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + // We turn off all the interrupts during this brief process, since the + // interrupt-level code might want to put things on the queue as well. + + switch (type) { + + case NEED_INLINE: + + WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + if ( !(pCh->channelNeeds & NEED_INLINE) ) + { + pCh->channelNeeds |= NEED_INLINE; + queueIndex = pB->i2Dbuf_stuff; + pB->i2Dbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Dbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + break; + + case NEED_BYPASS: + + WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) + { + pCh->channelNeeds |= NEED_BYPASS; + queueIndex = pB->i2Bbuf_stuff; + pB->i2Bbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Bbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + break; + + case NEED_FLOW: + + WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) + { + pCh->channelNeeds |= NEED_FLOW; + queueIndex = pB->i2Fbuf_stuff; + pB->i2Fbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Fbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + break; + + case NEED_CREDIT: + pCh->channelNeeds |= NEED_CREDIT; + break; + default: + printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type); + break; + } + return; +} + +//****************************************************************************** +// Function: i2QueueCommands(type, pCh, timeout, nCommands, pCs,...) +// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE +// pointer to the channel structure +// maximum period to wait +// number of commands (n) +// n commands +// Returns: Number of commands sent, or -1 for error +// +// get board lock before calling +// +// Description: +// Queues up some commands to be sent to a channel. To send possibly several +// bypass or inline commands to the given channel. The timeout parameter +// indicates how many HUNDREDTHS OF SECONDS to wait until there is room: +// 0 = return immediately if no room, -ive = wait forever, +ive = number of +// 1/100 seconds to wait. Return values: +// -1 Some kind of nasty error: bad channel structure or invalid arguments. +// 0 No room to send all the commands +// (+) Number of commands sent +//****************************************************************************** +static int +i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, + cmdSyntaxPtr pCs0,...) +{ + int totalsize = 0; + int blocksize; + int lastended; + cmdSyntaxPtr *ppCs; + cmdSyntaxPtr pCs; + int count; + int flag; + i2eBordStrPtr pB; + + unsigned short maxBlock; + unsigned short maxBuff; + short bufroom; + unsigned short stuffIndex; + unsigned char *pBuf; + unsigned char *pInsert; + unsigned char *pDest, *pSource; + unsigned short channel; + int cnt; + unsigned long flags = 0; + spinlock_t *lock_var_p = NULL; + + // Make sure the channel exists, otherwise do nothing + if ( !i2Validate ( pCh ) ) { + return -1; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 ); +#endif + pB = pCh->pMyBord; + + // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT + if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == IRQ_UNDEFINED) { + return -2; + } + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if ( pB->i2eFatalTrap ) { + (*(pB)->i2eFatalTrap)(pB); + } + return -3; + } + // Set up some variables, Which buffers are we using? How big are they? + switch(type) + { + case PTYPE_INLINE: + flag = INL; + maxBlock = MAX_OBUF_BLOCK; + maxBuff = OBUF_SIZE; + pBuf = pCh->Obuf; + break; + case PTYPE_BYPASS: + flag = BYP; + maxBlock = MAX_CBUF_BLOCK; + maxBuff = CBUF_SIZE; + pBuf = pCh->Cbuf; + break; + default: + return -4; + } + // Determine the total size required for all the commands + totalsize = blocksize = sizeof(i2CmdHeader); + lastended = 0; + ppCs = &pCs0; + for ( count = nCommands; count; count--, ppCs++) + { + pCs = *ppCs; + cnt = pCs->length; + // Will a new block be needed for this one? + // Two possible reasons: too + // big or previous command has to be at the end of a packet. + if ((blocksize + cnt > maxBlock) || lastended) { + blocksize = sizeof(i2CmdHeader); + totalsize += sizeof(i2CmdHeader); + } + totalsize += cnt; + blocksize += cnt; + + // If this command had to end a block, then we will make sure to + // account for it should there be any more blocks. + lastended = pCs->flags & END; + } + for (;;) { + // Make sure any pending flush commands go out before we add more data. + if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) { + // How much room (this time through) ? + switch(type) { + case PTYPE_INLINE: + lock_var_p = &pCh->Obuf_spinlock; + WRITE_LOCK_IRQSAVE(lock_var_p,flags); + stuffIndex = pCh->Obuf_stuff; + bufroom = pCh->Obuf_strip - stuffIndex; + break; + case PTYPE_BYPASS: + lock_var_p = &pCh->Cbuf_spinlock; + WRITE_LOCK_IRQSAVE(lock_var_p,flags); + stuffIndex = pCh->Cbuf_stuff; + bufroom = pCh->Cbuf_strip - stuffIndex; + break; + default: + return -5; + } + if (--bufroom < 0) { + bufroom += maxBuff; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom ); +#endif + // Check for overflow + if (totalsize <= bufroom) { + // Normal Expected path - We still hold LOCK + break; /* from for()- Enough room: goto proceed */ + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 3, 1, totalsize ); +#endif + // Prepare to wait for buffers to empty + WRITE_UNLOCK_IRQRESTORE(lock_var_p,flags); + serviceOutgoingFifo(pB); // Dump what we got + + if (timeout == 0) { + return 0; // Tired of waiting + } + if (timeout > 0) + timeout--; // So negative values == forever + + if (!in_interrupt()) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; + schedule_timeout(1); // short nap + } else { + // we cannot sched/sleep in interrrupt silly + return 0; + } + if (signal_pending(current)) { + return 0; // Wake up! Time to die!!! + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 4, 0 ); +#endif + } // end of for(;;) + + // At this point we have room and the lock - stick them in. + channel = pCh->infl.hd.i2sChannel; + pInsert = &pBuf[stuffIndex]; // Pointer to start of packet + pDest = CMD_OF(pInsert); // Pointer to start of command + + // When we start counting, the block is the size of the header + for (blocksize = sizeof(i2CmdHeader), count = nCommands, + lastended = 0, ppCs = &pCs0; + count; + count--, ppCs++) + { + pCs = *ppCs; // Points to command protocol structure + + // If this is a bookmark request command, post the fact that a bookmark + // request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ + // has no parameters! The more general solution would be to reference + // pCs->cmd[0]. + if (pCs == CMD_BMARK_REQ) { + pCh->bookMarks++; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks ); +#endif + } + cnt = pCs->length; + + // If this command would put us over the maximum block size or + // if the last command had to be at the end of a block, we end + // the existing block here and start a new one. + if ((blocksize + cnt > maxBlock) || lastended) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 5, 0 ); +#endif + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + pInsert = &pBuf[stuffIndex]; // Pointer to start of next pkt + pDest = CMD_OF(pInsert); + blocksize = sizeof(i2CmdHeader); + } + // Now we know there is room for this one in the current block + + blocksize += cnt; // Total bytes in this command + pSource = pCs->cmd; // Copy the command into the buffer + while (cnt--) { + *pDest++ = *pSource++; + } + // If this command had to end a block, then we will make sure to account + // for it should there be any more blocks. + lastended = pCs->flags & END; + } // end for + // Clean up the final block by writing header, etc + + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + // Updates the index, and post the need for service. When adding these to + // the queue of channels, we turn off the interrupt while doing so, + // because at interrupt level we might want to push a channel back to the + // end of the queue. + switch(type) + { + case PTYPE_INLINE: + pCh->Obuf_stuff = stuffIndex; // Store buffer pointer + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + + pB->debugInlineQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; + + case PTYPE_BYPASS: + pCh->Cbuf_stuff = stuffIndex; // Store buffer pointer + WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + + pB->debugBypassQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_BYPASS); + break; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands ); +#endif + return nCommands; // Good status: number of commands sent +} + +//****************************************************************************** +// Function: i2GetStatus(pCh,resetBits) +// Parameters: Pointer to a channel structure +// Bit map of status bits to clear +// Returns: Bit map of current status bits +// +// Description: +// Returns the state of data set signals, and whether a break has been received, +// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status +// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared +// AFTER the condition is passed. If pCh does not point to a valid channel, +// returns -1 (which would be impossible otherwise. +//****************************************************************************** +static int +i2GetStatus(i2ChanStrPtr pCh, int resetBits) +{ + unsigned short status; + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits ); +#endif + + // Make sure the channel exists, otherwise do nothing */ + if ( !i2Validate ( pCh ) ) + return -1; + + pB = pCh->pMyBord; + + status = pCh->dataSetIn; + + // Clear any specified error bits: but note that only actual error bits can + // be cleared, regardless of the value passed. + if (resetBits) + { + pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR)); + pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI); + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn ); +#endif + + return status; +} + +//****************************************************************************** +// Function: i2Input(pChpDest,count) +// Parameters: Pointer to a channel structure +// Pointer to data buffer +// Number of bytes to read +// Returns: Number of bytes read, or -1 for error +// +// Description: +// Strips data from the input buffer and writes it to pDest. If there is a +// collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes read. +//****************************************************************************** +static int +i2Input(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned short stripIndex; + int count; + unsigned long flags = 0; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0); +#endif + + // Ensure channel structure seems real + if ( !i2Validate( pCh ) ) { + count = -1; + goto i2Input_exit; + } + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + + // initialize some accelerators and private copies + stripIndex = pCh->Ibuf_strip; + + count = pCh->Ibuf_stuff - stripIndex; + + // If buffer is empty or requested data count was 0, (trivial case) return + // without any further thought. + if ( count == 0 ) { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + goto i2Input_exit; + } + // Adjust for buffer wrap + if ( count < 0 ) { + count += IBUF_SIZE; + } + // Don't give more than can be taken by the line discipline + amountToMove = pCh->pTTY->ldisc.receive_room( pCh->pTTY ); + if (count > amountToMove) { + count = amountToMove; + } + // How much could we copy without a wrap? + amountToMove = IBUF_SIZE - stripIndex; + + if (amountToMove > count) { + amountToMove = count; + } + // Move the first block + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + &(pCh->Ibuf[stripIndex]), NULL, amountToMove ); + // If we needed to wrap, do the second data move + if (count > amountToMove) { + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + pCh->Ibuf, NULL, count - amountToMove ); + } + // Bump and wrap the stripIndex all at once by the amount of data read. This + // method is good regardless of whether the data was in one or two pieces. + stripIndex += count; + if (stripIndex >= IBUF_SIZE) { + stripIndex -= IBUF_SIZE; + } + pCh->Ibuf_strip = stripIndex; + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + pCh->infl.asof += count; + + if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) { + pCh->sinceLastFlow -= pCh->whenSendFlow; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + } + +i2Input_exit: + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count); +#endif + return count; +} + +//****************************************************************************** +// Function: i2InputFlush(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes stripped, or -1 for error +// +// Description: +// Strips any data from the input buffer. If there is a collosal blunder, +// (invalid structure pointers or the like), returns -1. Otherwise, returns the +// number of bytes stripped. +//****************************************************************************** +static int +i2InputFlush(i2ChanStrPtr pCh) +{ + int count; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, 10, 0); +#endif + + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + + // Adjust for buffer wrap + if (count < 0) { + count += IBUF_SIZE; + } + + // Expedient way to zero out the buffer + pCh->Ibuf_strip = pCh->Ibuf_stuff; + + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + + pCh->infl.asof += count; + + if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow ) + { + pCh->sinceLastFlow -= pCh->whenSendFlow; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, 19, 1, count); +#endif + return count; +} + +//****************************************************************************** +// Function: i2InputAvailable(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes available, or -1 for error +// +// Description: +// If there is a collosal blunder, (invalid structure pointers or the like), +// returns -1. Otherwise, returns the number of bytes stripped. Otherwise, +// returns the number of bytes available in the buffer. +//****************************************************************************** +#if 0 +static int +i2InputAvailable(i2ChanStrPtr pCh) +{ + int count; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) return -1; + + + // initialize some accelerators and private copies + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + + // Adjust for buffer wrap + if (count < 0) + { + count += IBUF_SIZE; + } + + return count; +} +#endif + +//****************************************************************************** +// Function: i2Output(pCh, pSource, count) +// Parameters: Pointer to channel structure +// Pointer to source data +// Number of bytes to send +// Returns: Number of bytes sent, or -1 for error +// +// Description: +// Queues the data at pSource to be sent as data packets to the board. If there +// is a collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes written. What if there is not enough +// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then +// we transfer as many characters as we can now, then return. If this bit is +// clear (default), routine will spin along until all the data is buffered. +// Should this occur, the 1-ms delay routine is called while waiting to avoid +// applications that one cannot break out of. +//****************************************************************************** +static int +i2Output(i2ChanStrPtr pCh, const char *pSource, int count, int user ) +{ + i2eBordStrPtr pB; + unsigned char *pInsert; + int amountToMove; + int countOriginal = count; + unsigned short channel; + unsigned short stuffIndex; + unsigned long flags; + int rc = 0; + + int bailout = 10; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, user ); +#endif + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + + // initialize some accelerators and private copies + pB = pCh->pMyBord; + channel = pCh->infl.hd.i2sChannel; + + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return -1; + } + // Proceed as though we would do everything + while ( count > 0 ) { + + // How much room in output buffer is there? + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // Subtract off the headers size and see how much room there is for real + // data. If this is negative, we will discover later. + amountToMove -= sizeof (i2DataHeader); + + // Don't move more (now) than can go in a single packet + if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) { + amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader); + } + // Don't move more than the count we were given + if (amountToMove > count) { + amountToMove = count; + } + // Now we know how much we must move: NB because the ring buffers have + // an overflow area at the end, we needn't worry about wrapping in the + // middle of a packet. + +// Small WINDOW here with no LOCK but I can't call Flush with LOCK +// We would be flushing (or ending flush) anyway + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove ); +#endif + if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) + && amountToMove > 0 ) + { + WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + stuffIndex = pCh->Obuf_stuff; + + // Had room to move some data: don't know whether the block size, + // buffer space, or what was the limiting factor... + pInsert = &(pCh->Obuf[stuffIndex]); + + // Set up the header + CHANNEL_OF(pInsert) = channel; + PTYPE_OF(pInsert) = PTYPE_DATA; + TAG_OF(pInsert) = 0; + ID_OF(pInsert) = ID_ORDINARY_DATA; + DATA_COUNT_OF(pInsert) = amountToMove; + + // Move the data + if ( user ) { + rc=copy_from_user((char*)(DATA_OF(pInsert)), pSource, + amountToMove ); + } else { + memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove ); + } + // Adjust pointers and indices + pSource += amountToMove; + pCh->Obuf_char_count += amountToMove; + stuffIndex += amountToMove + sizeof(i2DataHeader); + count -= amountToMove; + + if (stuffIndex >= OBUF_SIZE) { + stuffIndex = 0; + } + pCh->Obuf_stuff = stuffIndex; + + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex ); +#endif + + } else { + + // Cannot move data + // becuz we need to stuff a flush + // or amount to move is <= 0 + +#ifdef IP2DEBUG_TRACE + ip2trace(CHANN, ITRC_OUTPUT, 14, 3, + amountToMove, pB->i2eFifoRemains, pB->i2eWaitingForEmptyFifo ); +#endif + // Put this channel back on queue + // this ultimatly gets more data or wakes write output + i2QueueNeeds(pB, pCh, NEED_INLINE); + + if ( pB->i2eWaitingForEmptyFifo ) { + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 16, 0 ); +#endif + // or schedule + if (!in_interrupt()) { + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 61, 0 ); +#endif + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; + schedule_timeout(2); + if (signal_pending(current)) { + break; + } + continue; + } else { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 62, 0 ); +#endif + // let interrupt in = WAS restore_flags() + // We hold no lock nor is irq off anymore??? + + break; + } + break; // from while(count) + } + else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 19, 2, pB->i2eFifoRemains, + pB->i2eTxMailEmpty ); +#endif + break; // from while(count) + } else if ( pCh->channelNeeds & NEED_CREDIT ) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 22, 0 ); +#endif + break; // from while(count) + } else if ( --bailout) { + + // Try to throw more things (maybe not us) in the fifo if we're + // not already waiting for it. + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 20, 0 ); +#endif + serviceOutgoingFifo(pB); + //break; CONTINUE; + } else { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 21, 3, pB->i2eFifoRemains, + pB->i2eOutMailWaiting, pB->i2eWaitingForEmptyFifo ); +#endif + break; // from while(count) + } + } + } // End of while(count) + + i2QueueNeeds(pB, pCh, NEED_INLINE); + + // We drop through either when the count expires, or when there is some + // count left, but there was a non-blocking write. + if (countOriginal > count) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count ); +#endif + serviceOutgoingFifo( pB ); + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count ); +#endif + + return countOriginal - count; +} + +//****************************************************************************** +// Function: i2FlushOutput(pCh) +// Parameters: Pointer to a channel structure +// Returns: Nothing +// +// Description: +// Sends bypass command to start flushing (waiting possibly forever until there +// is room), then sends inline command to stop flushing output, (again waiting +// possibly forever). +//****************************************************************************** +static inline void +i2FlushOutput(i2ChanStrPtr pCh) +{ + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags ); +#endif + + if (pCh->flush_flags) + return; + + if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + pCh->flush_flags = STARTFL_FLAG; // Failed - flag for later +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 2, 0 ); +#endif + } else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) { + pCh->flush_flags = STOPFL_FLAG; // Failed - flag for later +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 3, 0 ); +#endif + } +} + +static int +i2RetryFlushOutput(i2ChanStrPtr pCh) +{ + int old_flags = pCh->flush_flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags ); +#endif + + pCh->flush_flags = 0; // Clear flag so we can avoid recursion + // and queue the commands + + if ( old_flags & STARTFL_FLAG ) { + if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + old_flags = STOPFL_FLAG; //Success - send stop flush + } else { + old_flags = STARTFL_FLAG; //Failure - Flag for retry later + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags ); +#endif + } + if ( old_flags & STOPFL_FLAG ) { + if ( 1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) > 0 ) { + old_flags = 0; // Success - clear flags + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags ); +#endif + } + pCh->flush_flags = old_flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags ); +#endif + return old_flags; +} + +//****************************************************************************** +// Function: i2DrainOutput(pCh,timeout) +// Parameters: Pointer to a channel structure +// Maximum period to wait +// Returns: ? +// +// Description: +// Uses the bookmark request command to ask the board to send a bookmark back as +// soon as all the data is completely sent. +//****************************************************************************** +static void +i2DrainWakeup(i2ChanStrPtr pCh) +{ +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires ); +#endif + pCh->BookmarkTimer.expires = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); +} + +static void +i2DrainOutput(i2ChanStrPtr pCh, int timeout) +{ + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires); +#endif + pB = pCh->pMyBord; + // If the board has gone fatal, return bad, + // and also hit the trap routine if it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return; + } + if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) { + // One per customer (channel) + init_timer( &(pCh->BookmarkTimer) ); + pCh->BookmarkTimer.expires = jiffies + timeout; + pCh->BookmarkTimer.function = (void*)(unsigned long)i2DrainWakeup; + pCh->BookmarkTimer.data = (unsigned long)pCh; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires ); +#endif + + add_timer( &(pCh->BookmarkTimer) ); + } + + i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ ); + serviceOutgoingFifo( pB ); + + interruptible_sleep_on( &(pCh->pBookmarkWait) ); + + // if expires == 0 then timer poped, then do not need to del_timer + if ((timeout > 0) && pCh->BookmarkTimer.expires && + (pCh->BookmarkTimer.expires > jiffies)) { + del_timer( &(pCh->BookmarkTimer) ); + pCh->BookmarkTimer.expires = 0; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires ); +#endif + + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires ); +#endif + return; +} + +//****************************************************************************** +// Function: i2OutputFree(pCh) +// Parameters: Pointer to a channel structure +// Returns: Space in output buffer +// +// Description: +// Returns -1 if very gross error. Otherwise returns the amount of bytes still +// free in the output buffer. +//****************************************************************************** +static int +i2OutputFree(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) { + return -1; + } + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // If this is negative, we will discover later + amountToMove -= sizeof(i2DataHeader); + + return (amountToMove < 0) ? 0 : amountToMove; +} +static void + +ip2_owake( PTTY tp) +{ + i2ChanStrPtr pCh; + + if (tp == NULL) return; + + pCh = tp->driver_data; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags, (1 << TTY_DO_WRITE_WAKEUP) ); +#endif + wake_up_interruptible ( &tp->write_wait ); + if ( ( tp->flags & (1 << TTY_DO_WRITE_WAKEUP) ) + && tp->ldisc.write_wakeup ) + { + (tp->ldisc.write_wakeup) ( tp ); +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 11, 0 ); +#endif + } +} + +static inline void +set_baud_params(i2eBordStrPtr pB) +{ + int i,j; + i2ChanStrPtr *pCh; + + pCh = (i2ChanStrPtr *) pB->i2eChannelPtr; + + for (i = 0; i < ABS_MAX_BOXES; i++) { + if (pB->channelBtypes.bid_value[i]) { + if (BID_HAS_654(pB->channelBtypes.bid_value[i])) { + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 921600; // MAX for ST654 + (pCh[i*16+j])->BaudDivisor = 96; + } + } else { // has cirrus cd1400 + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 115200; // MAX for CD1400 + (pCh[i*16+j])->BaudDivisor = 12; + } + } + } + } +} + +//****************************************************************************** +// Function: i2StripFifo(pB) +// Parameters: Pointer to a board structure +// Returns: ? +// +// Description: +// Strips all the available data from the incoming FIFO, identifies the type of +// packet, and either buffers the data or does what needs to be done. +// +// Note there is no overflow checking here: if the board sends more data than it +// ought to, we will not detect it here, but blindly overflow... +//****************************************************************************** + +// A buffer for reading in blocks for unknown channels +static unsigned char junkBuffer[IBUF_SIZE]; + +// A buffer to read in a status packet. Because of the size of the count field +// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE +static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4]; + +// This table changes the bit order from MSR order given by STAT_MODEM packet to +// status bits used in our library. +static char xlatDss[16] = { +0 | 0 | 0 | 0 , +0 | 0 | 0 | I2_CTS , +0 | 0 | I2_DSR | 0 , +0 | 0 | I2_DSR | I2_CTS , +0 | I2_RI | 0 | 0 , +0 | I2_RI | 0 | I2_CTS , +0 | I2_RI | I2_DSR | 0 , +0 | I2_RI | I2_DSR | I2_CTS , +I2_DCD | 0 | 0 | 0 , +I2_DCD | 0 | 0 | I2_CTS , +I2_DCD | 0 | I2_DSR | 0 , +I2_DCD | 0 | I2_DSR | I2_CTS , +I2_DCD | I2_RI | 0 | 0 , +I2_DCD | I2_RI | 0 | I2_CTS , +I2_DCD | I2_RI | I2_DSR | 0 , +I2_DCD | I2_RI | I2_DSR | I2_CTS }; + +static inline void +i2StripFifo(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + int channel; + int count; + unsigned short stuffIndex; + int amountToRead; + unsigned char *pc, *pcLimit; + unsigned char uc; + unsigned char dss_change; + unsigned long bflags,cflags; + +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 ); +#endif + + while (HAS_INPUT(pB)) { +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 ); +#endif + // Process packet from fifo a one atomic unit + WRITE_LOCK_IRQSAVE(&pB->read_fifo_spinlock,bflags); + + // The first word (or two bytes) will have channel number and type of + // packet, possibly other information + pB->i2eLeadoffWord[0] = iiReadWord(pB); + + switch(PTYPE_OF(pB->i2eLeadoffWord)) + { + case PTYPE_DATA: + pB->got_input = 1; + +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 ); +#endif + channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */ + count = iiReadWord(pB); /* Count is in the next word */ + +// NEW: Check the count for sanity! Should the hardware fail, our death +// is more pleasant. While an oversize channel is acceptable (just more +// than the driver supports), an over-length count clearly means we are +// sick! + if ( ((unsigned int)count) > IBUF_SIZE ) { + pB->i2eFatal = 2; + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + return; /* Bail out ASAP */ + } + // Channel is illegally big ? + if (channel >= pB->i2eChannelCnt || + (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) == NULL) + { + iiReadBuf(pB, junkBuffer, count); + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + break; /* From switch: ready for next packet */ + } + + // Channel should be valid, then + + // If this is a hot-key, merely post its receipt for now. These are + // always supposed to be 1-byte packets, so we won't even check the + // count. Also we will post an acknowledgement to the board so that + // more data can be forthcoming. Note that we are not trying to use + // these sequences in this driver, merely to robustly ignore them. + if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY) + { + pCh->hotKeyIn = iiReadWord(pB) & 0xff; + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK); + break; /* From the switch: ready for next packet */ + } + + // Normal data! We crudely assume there is room for the data in our + // buffer because the board wouldn't have exceeded his credit limit. + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,cflags);// We have 2 locks now + stuffIndex = pCh->Ibuf_stuff; + amountToRead = IBUF_SIZE - stuffIndex; + if (amountToRead > count) + amountToRead = count; + + // stuffIndex would have been already adjusted so there would + // always be room for at least one, and count is always at least + // one. + + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + + // Update the stuffIndex by the amount of data moved. Note we could + // never ask for more data than would just fit. However, we might + // have read in one more byte than we wanted because the read + // rounds up to even bytes. If this byte is on the end of the + // packet, and is padding, we ignore it. If the byte is part of + // the actual data, we need to move it. + + stuffIndex += amountToRead; + + if (stuffIndex >= IBUF_SIZE) { + if ((amountToRead & 1) && (count > amountToRead)) { + pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE]; + amountToRead++; + stuffIndex = 1; + } else { + stuffIndex = 0; + } + } + + // If there is anything left over, read it as well + if (count > amountToRead) { + amountToRead = count - amountToRead; + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + stuffIndex += amountToRead; + } + + // Update stuff index + pCh->Ibuf_stuff = stuffIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,cflags); + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + +#ifdef USE_IQ + queue_task(&pCh->tqueue_input, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + do_input(pCh); +#endif + + // Note we do not need to maintain any flow-control credits at this + // time: if we were to increment .asof and decrement .room, there + // would be no net effect. Instead, when we strip data, we will + // increment .asof and leave .room unchanged. + + break; // From switch: ready for next packet + + case PTYPE_STATUS: +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 ); +#endif + + count = CMD_COUNT_OF(pB->i2eLeadoffWord); + + iiReadBuf(pB, cmdBuffer, count); + // We can release early with buffer grab + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + + pc = cmdBuffer; + pcLimit = &(cmdBuffer[count]); + + while (pc < pcLimit) { + channel = *pc++; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc ); +#endif + /* check for valid channel */ + if (channel < pB->i2eChannelCnt + && + (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL + ) + { + dss_change = 0; + + switch (uc = *pc++) + { + /* Breaks and modem signals are easy: just update status */ + case STAT_CTS_UP: + if ( !(pCh->dataSetIn & I2_CTS) ) + { + pCh->dataSetIn |= I2_DCTS; + ++pCh->icount.cts; + dss_change = 1; + } + pCh->dataSetIn |= I2_CTS; + break; + + case STAT_CTS_DN: + if ( pCh->dataSetIn & I2_CTS ) + { + pCh->dataSetIn |= I2_DCTS; + ++pCh->icount.cts; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_CTS; + break; + + case STAT_DCD_UP: +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn ); +#endif + if ( !(pCh->dataSetIn & I2_DCD) ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_MODEM, 2, 0 ); +#endif + pCh->dataSetIn |= I2_DDCD; + ++pCh->icount.dcd; + dss_change = 1; + } + pCh->dataSetIn |= I2_DCD; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn ); +#endif + break; + + case STAT_DCD_DN: +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn ); +#endif + if ( pCh->dataSetIn & I2_DCD ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 5, 0 ); +#endif + pCh->dataSetIn |= I2_DDCD; + ++pCh->icount.dcd; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DCD; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn ); +#endif + break; + + case STAT_DSR_UP: + if ( !(pCh->dataSetIn & I2_DSR) ) + { + pCh->dataSetIn |= I2_DDSR; + ++pCh->icount.dsr; + dss_change = 1; + } + pCh->dataSetIn |= I2_DSR; + break; + + case STAT_DSR_DN: + if ( pCh->dataSetIn & I2_DSR ) + { + pCh->dataSetIn |= I2_DDSR; + ++pCh->icount.dsr; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DSR; + break; + + case STAT_RI_UP: + if ( !(pCh->dataSetIn & I2_RI) ) + { + pCh->dataSetIn |= I2_DRI; + ++pCh->icount.rng; + dss_change = 1; + } + pCh->dataSetIn |= I2_RI ; + break; + + case STAT_RI_DN: + if ( pCh->dataSetIn & I2_RI ) + { + pCh->dataSetIn |= I2_DRI; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_RI ; + break; + + case STAT_BRK_DET: + pCh->dataSetIn |= I2_BRK; + dss_change = 1; + break; + + // Bookmarks? one less request we're waiting for + case STAT_BMARK: + pCh->bookMarks--; + if (pCh->bookMarks <= 0 ) { + pCh->bookMarks = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires ); +#endif + } + break; + + // Flow control packets? Update the new credits, and if + // someone was waiting for output, queue him up again. + case STAT_FLOW: + pCh->outfl.room = + ((flowStatPtr)pc)->room - + (pCh->outfl.asof - ((flowStatPtr)pc)->asof); +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room ); +#endif + if (pCh->channelNeeds & NEED_CREDIT) + { +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds); +#endif + pCh->channelNeeds &= ~NEED_CREDIT; + i2QueueNeeds(pB, pCh, NEED_INLINE); + if ( pCh->pTTY ) + ip2_owake(pCh->pTTY); + } +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds); +#endif + pc += sizeof(flowStat); + break; + + /* Special packets: */ + /* Just copy the information into the channel structure */ + + case STAT_STATUS: + + pCh->channelStatus = *((debugStatPtr)pc); + pc += sizeof(debugStat); + break; + + case STAT_TXCNT: + + pCh->channelTcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_RXCNT: + + pCh->channelRcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_BOXIDS: + pB->channelBtypes = *((bidStatPtr)pc); + pc += sizeof(bidStat); +//printk("boxids: %x %x %x %x\n", +// pB->channelBtypes.bid_value[0],pB->channelBtypes.bid_value[1], +// pB->channelBtypes.bid_value[2],pB->channelBtypes.bid_value[3]); + set_baud_params(pB); + break; + + case STAT_HWFAIL: + i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST); + pCh->channelFail = *((failStatPtr)pc); + pc += sizeof(failStat); + break; + + /* No explicit match? then + * Might be an error packet... + */ + default: + switch (uc & STAT_MOD_ERROR) + { + case STAT_ERROR: + if (uc & STAT_E_PARITY) + pCh->dataSetIn |= I2_PAR; + if (uc & STAT_E_FRAMING) + pCh->dataSetIn |= I2_FRA; + if (uc & STAT_E_OVERRUN) + pCh->dataSetIn |= I2_OVR; + break; + + case STAT_MODEM: + pCh->dataSetIn = (pCh->dataSetIn + & ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) ) + | xlatDss[uc & 0xf]; + default: + break; + } + } /* End of switch on status type */ + if (dss_change) { +#ifdef USE_IQ + queue_task(&pCh->tqueue_status, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + do_status(pCh); +#endif + } + } + else /* Or else, channel is invalid */ + { + // Even though the channel is invalid, we must test the + // status to see how much additional data it has (to be + // skipped) + switch (*pc++) + { + case STAT_FLOW: + pc += 4; /* Skip the data */ + break; + + case STAT_CTS_UP: + case STAT_CTS_DN: + case STAT_DCD_UP: + case STAT_DCD_DN: + case STAT_DSR_UP: + case STAT_DSR_DN: + case STAT_RI_UP: + case STAT_RI_DN: + case STAT_BRK_DET: + case STAT_BMARK: + default: + break; + } + } + } // End of while (there is still some status packet left) + break; + + default: // Neither packet? should be impossible +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1, + PTYPE_OF(pB->i2eLeadoffWord) ); +#endif + break; + } // End of switch on type of packets + } //while(board HAS_INPUT) +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 ); +#endif + // Send acknowledgement to the board even if there was no data! + pB->i2eOutMailWaiting |= MB_IN_STRIPPED; + return; +} + +//****************************************************************************** +// Function: i2Write2Fifo(pB,address,count) +// Parameters: Pointer to a board structure, source address, byte count +// Returns: bytes written +// +// Description: +// Writes count bytes to board io address(implied) from source +// Adjusts count, leaves reserve for next time around bypass cmds +//****************************************************************************** +static int +i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve) +{ + int rc = 0; + unsigned long flags; + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + if (!pB->i2eWaitingForEmptyFifo) { + if (pB->i2eFifoRemains > (count+reserve)) { + pB->i2eFifoRemains -= count; + iiWriteBuf(pB, source, count); + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + rc = count; + } + } + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + return rc; +} +//****************************************************************************** +// Function: i2StuffFifoBypass(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many bypass commands into the fifo as possible. This is simpler +// than stuffing data or inline commands to fifo, since we do not have +// flow-control to deal with. +//****************************************************************************** +static inline void +i2StuffFifoBypass(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned long flags; + + int bailout = 1000; + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS)))) + { + WRITE_LOCK_IRQSAVE(&pCh->Cbuf_spinlock,flags); + stripIndex = pCh->Cbuf_strip; + + // as long as there are packets for this channel... + + while (stripIndex != pCh->Cbuf_stuff) { + pRemove = &(pCh->Cbuf[stripIndex]); + packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader); + paddedSize = ROUNDUP(packetSize); + + if (paddedSize > 0) { + if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) { + notClogged = 0; /* fifo full */ + i2QueueNeeds(pB, pCh, NEED_BYPASS); // Put back on queue + break; // Break from the channel + } + } +#ifdef DEBUG_FIFO +WriteDBGBuf("BYPS", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugBypassCount++; + + pRemove += packetSize; + stripIndex += packetSize; + if (stripIndex >= CBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Cbuf; + } + } + // Done with this channel. Move to next, removing this one from + // the queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Cbuf_strip = stripIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + } // Either clogged or finished all the work + +#ifdef IP2DEBUG_TRACE + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 ); + } +#endif +} + +//****************************************************************************** +// Function: i2StuffFifoFlow(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many flow control packets into the fifo as possible. This is easier +// even than doing normal bypass commands, because there is always at most one +// packet, already assembled, for each channel. +//****************************************************************************** +static inline void +i2StuffFifoFlow(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned short paddedSize = ROUNDUP(sizeof(flowIn)); + +#ifdef IP2DEBUG_TRACE +ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2, pB->i2eFifoRemains, paddedSize ); +#endif + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) { + pB->debugFlowCount++; + + // NO Chan LOCK needed ??? + if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) { + break; + } +#ifdef DEBUG_FIFO +WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize); +#endif /* DEBUG_FIFO */ + + } // Either clogged or finished all the work + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 ); +#endif +} + +//****************************************************************************** +// Function: i2StuffFifoInline(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as much data and inline commands into the fifo as possible. This is +// the most complex fifo-stuffing operation, since there if now the channel +// flow-control issue to deal with. +//****************************************************************************** +static inline void +i2StuffFifoInline(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned short flowsize; + unsigned long flags; + + int bailout = 1000; + int bailout2; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains, + pB->i2Dbuf_strip, pB->i2Dbuf_stuff ); +#endif + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) ) + { + WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + stripIndex = pCh->Obuf_strip; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff ); +#endif + // as long as there are packets for this channel... + bailout2 = 1000; + while ( --bailout2 && stripIndex != pCh->Obuf_stuff) { + pRemove = &(pCh->Obuf[stripIndex]); + + // Must determine whether this be a data or command packet to + // calculate correctly the header size and the amount of + // flow-control credit this type of packet will use. + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + flowsize = DATA_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2DataHeader); + } else { + flowsize = CMD_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2CmdHeader); + } + flowsize = CREDIT_USAGE(flowsize); + paddedSize = ROUNDUP(packetSize); + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize ); +#endif + // If we don't have enough credits from the board to send the data, + // flag the channel that we are waiting for flow control credit, and + // break out. This will clean up this channel and remove us from the + // queue of hot things to do. +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize ); +#endif + if (pCh->outfl.room <= flowsize) { + // Do Not have the credits to send this packet. + i2QueueNeeds(pB, pCh, NEED_CREDIT); + notClogged = 0; + break; // So to do next channel + } + if ( (paddedSize > 0) + && ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) { + // Do Not have room in fifo to send this packet. + notClogged = 0; + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; // Break from the channel + } +#ifdef DEBUG_FIFO +WriteDBGBuf("DATA", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugInlineCount++; + + // Update current credits + pCh->outfl.room -= flowsize; + pCh->outfl.asof += flowsize; + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove); + } + pRemove += packetSize; + stripIndex += packetSize; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip); +#endif + if (stripIndex >= OBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Obuf; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex ); +#endif + } + } /* while */ + if ( !bailout2 ) { + ip2trace (CHANN, ITRC_ERROR, 3, 0 ); + } + // Done with this channel. Move to next, removing this one from the + // queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Obuf_strip = stripIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + if ( notClogged ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 8, 0 ); +#endif + if ( pCh->pTTY ) { + ip2_owake(pCh->pTTY); + } + } + } // Either clogged or finished all the work +#ifdef IP2DEBUG_TRACE + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 ); + } +#endif + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip); +#endif +} + +//****************************************************************************** +// Function: serviceOutgoingFifo(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Helper routine to put data in the outgoing fifo, if we aren't already waiting +// for something to be there. If the fifo has only room for a very little data, +// go head and hit the board with a mailbox hit immediately. Otherwise, it will +// have to happen later in the interrupt processing. Since this routine may be +// called both at interrupt and foreground time, we must turn off interrupts +// during the entire process. +//****************************************************************************** +static void +serviceOutgoingFifo(i2eBordStrPtr pB) +{ + // If we aren't currently waiting for the board to empty our fifo, service + // everything that is pending, in priority order (especially, Bypass before + // Inline). + if ( ! pB->i2eWaitingForEmptyFifo ) + { + i2StuffFifoFlow(pB); + i2StuffFifoBypass(pB); + i2StuffFifoInline(pB); + + iiSendPendingMail(pB); + } +} + +//****************************************************************************** +// Function: i2ServiceBoard(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Normally this is called from interrupt level, but there is deliberately +// nothing in here specific to being called from interrupt level. All the +// hardware-specific, interrupt-specific things happen at the outer levels. +// +// For example, a timer interrupt could drive this routine for some sort of +// polled operation. The only requirement is that the programmer deal with any +// atomiticity/concurrency issues that result. +// +// This routine responds to the board's having sent mailbox information to the +// host (which would normally cause an interrupt). This routine reads the +// incoming mailbox. If there is no data in it, this board did not create the +// interrupt and/or has nothing to be done to it. (Except, if we have been +// waiting to write mailbox data to it, we may do so. +// +// Based on the value in the mailbox, we may take various actions. +// +// No checking here of pB validity: after all, it shouldn't have been called by +// the handler unless pB were on the list. +//****************************************************************************** +static inline int +i2ServiceBoard ( i2eBordStrPtr pB ) +{ + unsigned inmail; + unsigned long flags; + + + inmail = iiGetMail(pB); + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail ); +#endif + + if (inmail != NO_MAIL_HERE) { + // If the board has gone fatal, nothing to do but hit a bit that will + // alert foreground tasks to protest! + if ( inmail & MB_FATAL_ERROR ) { + pB->i2eFatal = 1; + goto exit_i2ServiceBoard; + } + + /* Assuming no fatal condition, we proceed to do work */ + if ( inmail & MB_IN_STUFFED ) { + pB->i2eFifoInInts++; + i2StripFifo(pB); /* There might be incoming packets */ + } + + if (inmail & MB_OUT_STRIPPED) { + pB->i2eFifoOutInts++; + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + pB->i2eFifoRemains = pB->i2eFifoSize; + pB->i2eWaitingForEmptyFifo = 0; + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains ); +#endif + } + serviceOutgoingFifo(pB); + } + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 ); +#endif + +exit_i2ServiceBoard: + + return 0; +} diff --git a/drivers/char/ip2/i2lib.h b/drivers/char/ip2/i2lib.h new file mode 100644 index 000000000000..5e5b8ea9c5a9 --- /dev/null +++ b/drivers/char/ip2/i2lib.h @@ -0,0 +1,350 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Header file for high level library functions +* +*******************************************************************************/ +#ifndef I2LIB_H +#define I2LIB_H 1 +//------------------------------------------------------------------------------ +// I2LIB.H +// +// IntelliPort-II and IntelliPort-IIEX +// +// Defines, structure definitions, and external declarations for i2lib.c +//------------------------------------------------------------------------------ +//-------------------------------------- +// Mandatory Includes: +//-------------------------------------- +#include "ip2types.h" +#include "i2ellis.h" +#include "i2pack.h" +#include "i2cmd.h" + +//------------------------------------------------------------------------------ +// i2ChanStr -- Channel Structure: +// Used to track per-channel information for the library routines using standard +// loadware. Note also, a pointer to an array of these structures is patched +// into the i2eBordStr (see i2ellis.h) +//------------------------------------------------------------------------------ +// +// If we make some limits on the maximum block sizes, we can avoid dealing with +// buffer wrap. The wrapping of the buffer is based on where the start of the +// packet is. Then there is always room for the packet contiguously. +// +// Maximum total length of an outgoing data or in-line command block. The limit +// of 36 on data is quite arbitrary and based more on DOS memory limitations +// than the board interface. However, for commands, the maximum packet length is +// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits +// (see I2PACK.H) in such packets. For data packets, the count field size is not +// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE, +// but be careful if wanting to modify either. +// +#define MAX_OBUF_BLOCK 36 + +// Another note on maximum block sizes: we are buffering packets here. Data is +// put into the buffer (if there is room) regardless of the credits from the +// board. The board sends new credits whenever it has removed from his buffers a +// number of characters equal to 80% of total buffer size. (Of course, the total +// buffer size is what is reported when the very first set of flow control +// status packets are received from the board. Therefore, to be robust, you must +// always fill the board to at least 80% of the current credit limit, else you +// might not give it enough to trigger a new report. These conditions are +// obtained here so long as the maximum output block size is less than 20% the +// size of the board's output buffers. This is true at present by "coincidence" +// or "infernal knowledge": the board's output buffers are at least 700 bytes +// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest +// strategy might be to trap the first flow control report and guarantee that +// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first +// reported buffer credit. +// +#define MAX_CBUF_BLOCK 6 // Maximum total length of a bypass command block + +#define IBUF_SIZE 500 // character capacity of input buffer per channel +#define OBUF_SIZE 2048// character capacity of output buffer per channel +#define CBUF_SIZE 10 // character capacity of output bypass buffer + +typedef struct _i2ChanStr +{ + // First, back-pointers so that given a pointer to this structure, you can + // determine the correct board and channel number to reference, (say, when + // issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.) + + int port_index; // Index of port in channel structure array attached + // to board structure. + PTTY pTTY; // Pointer to tty structure for port (OS specific) + USHORT validity; // Indicates whether the given channel has been + // initialized, really exists (or is a missing + // channel, e.g. channel 9 on an 8-port box.) + + i2eBordStrPtr pMyBord; // Back-pointer to this channel's board structure + + int wopen; // waiting fer carrier + + int throttled; // Set if upper layer can take no data + + int flags; // Defined in tty.h + int session; // Defined in tty.h + int pgrp; // Defined in tty.h + + PWAITQ open_wait; // Pointer for OS sleep function. + PWAITQ close_wait; // Pointer for OS sleep function. + PWAITQ delta_msr_wait;// Pointer for OS sleep function. + + struct timer_list BookmarkTimer; // Used by i2DrainOutput + struct wait_queue *pBookmarkWait; // Used by i2DrainOutput + + struct termios NormalTermios; + struct termios CalloutTermios; + + int BaudBase; + int BaudDivisor; + + USHORT ClosingDelay; + USHORT ClosingWaitTime; + + volatile + flowIn infl; // This structure is initialized as a completely + // formed flow-control command packet, and as such + // has the channel number, also the capacity and + // "as-of" data needed continuously. + + USHORT sinceLastFlow; // Counts the number of characters read from input + // buffers, since the last time flow control info + // was sent. + + USHORT whenSendFlow; // Determines when new flow control is to be sent to + // the board. Note unlike earlier manifestations of + // the driver, these packets can be sent from + // in-place. + + USHORT channelNeeds; // Bit map of important things which must be done + // for this channel. (See bits below ) + + volatile + flowStat outfl; // Same type of structure is used to hold current + // flow control information used to control our + // output. "asof" is kept updated as data is sent, + // and "room" never goes to zero. + + // The incoming ring buffer + // Unlike the outgoing buffers, this holds raw data, not packets. The two + // extra bytes are used to hold the byte-padding when there is room for an + // odd number of bytes before we must wrap. + // + UCHAR Ibuf[IBUF_SIZE + 2]; + volatile + USHORT Ibuf_stuff; // Stuffing index + volatile + USHORT Ibuf_strip; // Stripping index + + // The outgoing ring-buffer: Holds Data and command packets. N.B., even + // though these are in the channel structure, the channel is also written + // here, the easier to send it to the fifo when ready. HOWEVER, individual + // packets here are NOT padded to even length: the routines for writing + // blocks to the the fifo will pad to even byte counts. + // + UCHAR Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4]; + volatile + USHORT Obuf_stuff; // Stuffing index + volatile + USHORT Obuf_strip; // Stripping index + int Obuf_char_count; + + // The outgoing bypass-command buffer. Unlike earlier manifestations, the + // flow control packets are sent directly from the structures. As above, the + // channel number is included in the packet, but they are NOT padded to even + // size. + // + UCHAR Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2]; + volatile + USHORT Cbuf_stuff; // Stuffing index + volatile + USHORT Cbuf_strip; // Stripping index + + // The temporary buffer for the Linux tty driver PutChar entry. + // + UCHAR Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)]; + volatile + USHORT Pbuf_stuff; // Stuffing index + + // The state of incoming data-set signals + // + USHORT dataSetIn; // Bit-mapped according to below. Also indicates + // whether a break has been detected since last + // inquiry. + + // The state of outcoming data-set signals (as far as we can tell!) + // + USHORT dataSetOut; // Bit-mapped according to below. + + // Most recent hot-key identifier detected + // + USHORT hotKeyIn; // Hot key as sent by the board, HOT_CLEAR indicates + // no hot key detected since last examined. + + // Counter of outstanding requests for bookmarks + // + short bookMarks; // Number of outstanding bookmark requests, (+ive + // whenever a bookmark request if queued up, -ive + // whenever a bookmark is received). + + // Misc options + // + USHORT channelOptions; // See below + + // To store various incoming special packets + // + debugStat channelStatus; + cntStat channelRcount; + cntStat channelTcount; + failStat channelFail; + + // To store the last values for line characteristics we sent to the board. + // + int speed; + + int flush_flags; + + void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...); + +#ifdef __KERNEL__ + /* + * Kernel counters for the 4 input interrupts + */ + struct async_icount icount; + + /* + * Task queues for processing input packets from the board. + */ + struct tq_struct tqueue_input; + struct tq_struct tqueue_status; + struct tq_struct tqueue_hangup; +#endif + + spinlock_t Ibuf_spinlock; + spinlock_t Obuf_spinlock; + spinlock_t Cbuf_spinlock; + spinlock_t Pbuf_spinlock; + +} i2ChanStr, *i2ChanStrPtr; + +//--------------------------------------------------- +// Manifests and bit-maps for elements in i2ChanStr +//--------------------------------------------------- +// +// flush flags +// +#define STARTFL_FLAG 1 +#define STOPFL_FLAG 2 + +// validity +// +#define CHANNEL_MAGIC_BITS 0xff00 +#define CHANNEL_MAGIC 0x5300 // (validity & CHANNEL_MAGIC_BITS) == + // CHANNEL_MAGIC --> structure good + +#define CHANNEL_SUPPORT 0x0001 // Indicates channel is supported, exists, + // and passed P.O.S.T. + +// channelNeeds +// +#define NEED_FLOW 1 // Indicates flow control has been queued +#define NEED_INLINE 2 // Indicates inline commands or data queued +#define NEED_BYPASS 4 // Indicates bypass commands queued +#define NEED_CREDIT 8 // Indicates would be sending except has not sufficient + // credit. The data is still in the channel structure, + // but the channel is not enqueued in the board + // structure again until there is a credit received from + // the board. + +// dataSetIn (Also the bits for i2GetStatus return value) +// +#define I2_DCD 1 +#define I2_CTS 2 +#define I2_DSR 4 +#define I2_RI 8 + +// dataSetOut (Also the bits for i2GetStatus return value) +// +#define I2_DTR 1 +#define I2_RTS 2 + +// i2GetStatus() can optionally clear these bits +// +#define I2_BRK 0x10 // A break was detected +#define I2_PAR 0x20 // A parity error was received +#define I2_FRA 0x40 // A framing error was received +#define I2_OVR 0x80 // An overrun error was received + +// i2GetStatus() automatically clears these bits */ +// +#define I2_DDCD 0x100 // DCD changed from its former value +#define I2_DCTS 0x200 // CTS changed from its former value +#define I2_DDSR 0x400 // DSR changed from its former value +#define I2_DRI 0x800 // RI changed from its former value + +// hotKeyIn +// +#define HOT_CLEAR 0x1322 // Indicates that no hot-key has been detected + +// channelOptions +// +#define CO_NBLOCK_WRITE 1 // Writes don't block waiting for buffer. (Default + // is, they do wait.) + +// fcmodes +// +#define I2_OUTFLOW_CTS 0x0001 +#define I2_INFLOW_RTS 0x0002 +#define I2_INFLOW_DSR 0x0004 +#define I2_INFLOW_DTR 0x0008 +#define I2_OUTFLOW_DSR 0x0010 +#define I2_OUTFLOW_DTR 0x0020 +#define I2_OUTFLOW_XON 0x0040 +#define I2_OUTFLOW_XANY 0x0080 +#define I2_INFLOW_XON 0x0100 + +#define I2_CRTSCTS (I2_OUTFLOW_CTS|I2_INFLOW_RTS) +#define I2_IXANY_MODE (I2_OUTFLOW_XON|I2_OUTFLOW_XANY) + +//------------------------------------------- +// Macros used from user level like functions +//------------------------------------------- + +// Macros to set and clear channel options +// +#define i2SetOption(pCh, option) pCh->channelOptions |= option +#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option + +// Macro to set fatal-error trap +// +#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine + +//-------------------------------------------- +// Declarations and prototypes for i2lib.c +//-------------------------------------------- +// +static int i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr); +static int i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...); +static int i2GetStatus(i2ChanStrPtr, int); +static int i2Input(i2ChanStrPtr); +static int i2InputFlush(i2ChanStrPtr); +static int i2Output(i2ChanStrPtr, const char *, int, int); +static int i2OutputFree(i2ChanStrPtr); +static int i2ServiceBoard(i2eBordStrPtr); +static void i2DrainOutput(i2ChanStrPtr, int); + +// Argument to i2QueueCommands +// +#define C_IN_LINE 1 +#define C_BYPASS 0 + +#endif // I2LIB_H diff --git a/drivers/char/ip2/i2os.h b/drivers/char/ip2/i2os.h new file mode 100644 index 000000000000..f85822c1a261 --- /dev/null +++ b/drivers/char/ip2/i2os.h @@ -0,0 +1,145 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Defines, definitions and includes which are heavily dependant +* on O/S, host, compiler, etc. This file is tailored for: +* Linux v2.0.0 and later +* Gnu gcc c2.7.2 +* 80x86 architecture +* +*******************************************************************************/ + +#ifndef I2OS_H /* To prevent multiple includes */ +#define I2OS_H 1 + +#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) + +//------------------------------------------------- +// Required Includes +//------------------------------------------------- + +#include "ip2types.h" +#include /* For inb, etc */ + +//------------------------------------ +// Defines for I/O instructions: +//------------------------------------ + +#define INB(port) inb(port) +#define OUTB(port,value) outb((value),(port)) +#define INW(port) inw(port) +#define OUTW(port,value) outw((value),(port)) +#define OUTSW(port,addr,count) outsw((port),(addr),(((count)+1)/2)) +#define OUTSB(port,addr,count) outsb((port),(addr),(((count)+1))&-2) +#define INSW(port,addr,count) insw((port),(addr),(((count)+1)/2)) +#define INSB(port,addr,count) insb((port),(addr),(((count)+1))&-2) + +//-------------------------------------------- +// Interrupt control +//-------------------------------------------- + +#if LINUX_VERSION_CODE < 0x00020100 +typedef int spinlock_t; +#define spin_lock_init() +#define spin_lock(a) +#define spin_unlock(a) +#define spin_lock_irqsave(a,b) {save_flags((b));cli();} +#define spin_unlock_irqrestore(a,b) {restore_flags((b));} +#define write_lock_irqsave(a,b) spin_lock_irqsave(a,b) +#define write_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) +#define read_lock_irqsave(a,b) spin_lock_irqsave(a,b) +#define read_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) +#endif + +//#define SAVE_AND_DISABLE_INTS(a,b) spin_lock_irqsave(a,b) +//#define RESTORE_INTS(a,b) spin_unlock_irqrestore(a,b) + +#define LOCK_INIT(a) spin_lock_init(a) + +#define SAVE_AND_DISABLE_INTS(a,b) { \ + /* printk("get_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + spin_lock_irqsave(a,b); \ +} + +#define RESTORE_INTS(a,b) { \ + /* printk("rel_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + spin_unlock_irqrestore(a,b); \ +} + +#define READ_LOCK_IRQSAVE(a,b) { \ + /* printk("get_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + read_lock_irqsave(a,b); \ +} + +#define READ_UNLOCK_IRQRESTORE(a,b) { \ + /* printk("rel_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + read_unlock_irqrestore(a,b); \ +} + +#define WRITE_LOCK_IRQSAVE(a,b) { \ + /* printk("get_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + write_lock_irqsave(a,b); \ +} + +#define WRITE_UNLOCK_IRQRESTORE(a,b) { \ + /* printk("rel_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + write_unlock_irqrestore(a,b); \ +} + + +//------------------------------------------------------------------------------ +// Hardware-delay loop +// +// Probably used in only one place (see i2ellis.c) but this helps keep things +// together. Note we have unwound the IN instructions. On machines with a +// reasonable cache, the eight instructions (1 byte each) should fit in cache +// nicely, and on un-cached machines, the code-fetch would tend not to dominate. +// Note that cx is shifted so that "count" still reflects the total number of +// iterations assuming no unwinding. +//------------------------------------------------------------------------------ + +//#define DELAY1MS(port,count,label) + +//------------------------------------------------------------------------------ +// Macros to switch to a new stack, saving stack pointers, and to restore the +// old stack (Used, for example, in i2lib.c) "heap" is the address of some +// buffer which will become the new stack (working down from highest address). +// The two words at the two lowest addresses in this stack are for storing the +// SS and SP. +//------------------------------------------------------------------------------ + +//#define TO_NEW_STACK(heap,size) +//#define TO_OLD_STACK(heap) + +//------------------------------------------------------------------------------ +// Macros to save the original IRQ vectors and masks, and to patch in new ones. +//------------------------------------------------------------------------------ + +//#define SAVE_IRQ_MASKS(dest) +//#define WRITE_IRQ_MASKS(src) +//#define SAVE_IRQ_VECTOR(value,dest) +//#define WRITE_IRQ_VECTOR(value,src) + +//------------------------------------------------------------------------------ +// Macro to copy data from one far pointer to another. +//------------------------------------------------------------------------------ + +#define I2_MOVE_DATA(fpSource,fpDest,count) memmove(fpDest,fpSource,count); + +//------------------------------------------------------------------------------ +// Macros to issue eoi's to host interrupt control (IBM AT 8259-style). +//------------------------------------------------------------------------------ + +//#define MASTER_EOI +//#define SLAVE_EOI + +#endif /* I2OS_H */ + + diff --git a/drivers/char/ip2/i2pack.h b/drivers/char/ip2/i2pack.h new file mode 100644 index 000000000000..e9b87a78622c --- /dev/null +++ b/drivers/char/ip2/i2pack.h @@ -0,0 +1,364 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions of the packets used to transfer data and commands +* Host <--> Board. Information provided here is only applicable +* when the standard loadware is active. +* +*******************************************************************************/ +#ifndef I2PACK_H +#define I2PACK_H 1 + +//----------------------------------------------- +// Revision History: +// +// 10 October 1991 MAG First draft +// 24 February 1992 MAG Additions for 1.4.x loadware +// 11 March 1992 MAG New status packets +// +//----------------------------------------------- + +//------------------------------------------------------------------------------ +// Packet Formats: +// +// Information passes between the host and board through the FIFO in packets. +// These have headers which indicate the type of packet. Because the fifo data +// path may be 16-bits wide, the protocol is constrained such that each packet +// is always padded to an even byte count. (The lower-level interface routines +// -- i2ellis.c -- are designed to do this). +// +// The sender (be it host or board) must place some number of complete packets +// in the fifo, then place a message in the mailbox that packets are available. +// Placing such a message interrupts the "receiver" (be it board or host), who +// reads the mailbox message and determines that there are incoming packets +// ready. Since there are no partial packets, and the length of a packet is +// given in the header, the remainder of the packet can be read without checking +// for FIFO empty condition. The process is repeated, packet by packet, until +// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to +// signal the board that it has read the data. Only then can the sender place +// additional data in the fifo. +//------------------------------------------------------------------------------ +// +//------------------------------------------------ +// Definition of Packet Header Area +//------------------------------------------------ +// +// Caution: these only define header areas. In actual use the data runs off +// beyond the end of these structures. +// +// Since these structures are based on sequences of bytes which go to the board, +// there cannot be ANY padding between the elements. +#pragma pack(1) + +//---------------------------- +// DATA PACKETS +//---------------------------- + +typedef struct _i2DataHeader +{ + unsigned char i2sChannel; /* The channel number: 0-255 */ + + // -- Bitfields are allocated LSB first -- + + // For incoming data, indicates whether this is an ordinary packet or a + // special one (e.g., hot key hit). + unsigned i2sId : 2 __attribute__ ((__packed__)); + + // For tagging data packets. There are flush commands which flush only data + // packets bearing a particular tag. (used in implementing IntelliView and + // IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has + // meaning internally to the loadware). + unsigned i2sTag : 4; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + + // The count of data to follow: does not include the possible additional + // padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0. + unsigned short i2sCount; + +} i2DataHeader, *i2DataHeaderPtr; + +// Structure is immediately followed by the data, proper. + +//---------------------------- +// NON-DATA PACKETS +//---------------------------- + +typedef struct _i2CmdHeader +{ + unsigned char i2sChannel; // The channel number: 0-255 (Except where noted + // - see below + + // Number of bytes of commands, status or whatever to follow + unsigned i2sCount : 6; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + +} i2CmdHeader, *i2CmdHeaderPtr; + +// Structure is immediately followed by the applicable data. + +//--------------------------------------- +// Flow Control Packets (Outbound) +//--------------------------------------- + +// One type of outbound command packet is so important that the entire structure +// is explicitly defined here. That is the flow-control packet. This is never +// sent by user-level code (as would be the commands to raise/lower DTR, for +// example). These are only sent by the library routines in response to reading +// incoming data into the buffers. +// +// The parameters inside the command block are maintained in place, then the +// block is sent at the appropriate time. + +typedef struct _flowIn +{ + i2CmdHeader hd; // Channel #, count, type (see above) + unsigned char fcmd; // The flow control command (37) + unsigned short asof; // As of byte number "asof" (LSB first!) I have room + // for "room" bytes + unsigned short room; +} flowIn, *flowInPtr; + +//---------------------------------------- +// (Incoming) Status Packets +//---------------------------------------- + +// Incoming packets which are non-data packets are status packets. In this case, +// the channel number in the header is unimportant. What follows are one or more +// sub-packets, the first word of which consists of the channel (first or low +// byte) and the status indicator (second or high byte), followed by possibly +// more data. + +#define STAT_CTS_UP 0 /* CTS raised (no other bytes) */ +#define STAT_CTS_DN 1 /* CTS dropped (no other bytes) */ +#define STAT_DCD_UP 2 /* DCD raised (no other bytes) */ +#define STAT_DCD_DN 3 /* DCD dropped (no other bytes) */ +#define STAT_DSR_UP 4 /* DSR raised (no other bytes) */ +#define STAT_DSR_DN 5 /* DSR dropped (no other bytes) */ +#define STAT_RI_UP 6 /* RI raised (no other bytes) */ +#define STAT_RI_DN 7 /* RI dropped (no other bytes) */ +#define STAT_BRK_DET 8 /* BRK detect (no other bytes) */ +#define STAT_FLOW 9 /* Flow control(-- more: see below */ +#define STAT_BMARK 10 /* Bookmark (no other bytes) + * Bookmark is sent as a response to + * a command 60: request for bookmark + */ +#define STAT_STATUS 11 /* Special packet: see below */ +#define STAT_TXCNT 12 /* Special packet: see below */ +#define STAT_RXCNT 13 /* Special packet: see below */ +#define STAT_BOXIDS 14 /* Special packet: see below */ +#define STAT_HWFAIL 15 /* Special packet: see below */ + +#define STAT_MOD_ERROR 0xc0 +#define STAT_MODEM 0xc0/* If status & STAT_MOD_ERROR: + * == STAT_MODEM, then this is a modem + * status packet, given in response to a + * CMD_DSS_NOW command. + * The low nibble has each data signal: + */ +#define STAT_MOD_DCD 0x8 +#define STAT_MOD_RI 0x4 +#define STAT_MOD_DSR 0x2 +#define STAT_MOD_CTS 0x1 + +#define STAT_ERROR 0x80/* If status & STAT_MOD_ERROR + * == STAT_ERROR, then + * sort of error on the channel. + * The remaining seven bits indicate + * what sort of error it is. + */ +/* The low three bits indicate parity, framing, or overrun errors */ + +#define STAT_E_PARITY 4 /* Parity error */ +#define STAT_E_FRAMING 2 /* Framing error */ +#define STAT_E_OVERRUN 1 /* (uxart) overrun error */ + +//--------------------------------------- +// STAT_FLOW packets +//--------------------------------------- + +typedef struct _flowStat +{ + unsigned short asof; + unsigned short room; +}flowStat, *flowStatPtr; + +// flowStat packets are received from the board to regulate the flow of outgoing +// data. A local copy of this structure is also kept to track the amount of +// credits used and credits remaining. "room" is the amount of space in the +// board's buffers, "as of" having received a certain byte number. When sending +// data to the fifo, you must calculate how much buffer space your packet will +// use. Add this to the current "asof" and subtract it from the current "room". +// +// The calculation for the board's buffer is given by CREDIT_USAGE, where size +// is the un-rounded count of either data characters or command characters. +// (Which is to say, the count rounded up, plus two). + +#define CREDIT_USAGE(size) (((size) + 3) & ~1) + +//--------------------------------------- +// STAT_STATUS packets +//--------------------------------------- + +typedef struct _debugStat +{ + unsigned char d_ccsr; + unsigned char d_txinh; + unsigned char d_stat1; + unsigned char d_stat2; +} debugStat, *debugStatPtr; + +// debugStat packets are sent to the host in response to a CMD_GET_STATUS +// command. Each byte is bit-mapped as described below: + +#define D_CCSR_XON 2 /* Has received XON, ready to transmit */ +#define D_CCSR_XOFF 4 /* Has received XOFF, not transmitting */ +#define D_CCSR_TXENAB 8 /* Transmitter is enabled */ +#define D_CCSR_RXENAB 0x80 /* Receiver is enabled */ + +#define D_TXINH_BREAK 1 /* We are sending a break */ +#define D_TXINH_EMPTY 2 /* No data to send */ +#define D_TXINH_SUSP 4 /* Output suspended via command 57 */ +#define D_TXINH_CMD 8 /* We are processing an in-line command */ +#define D_TXINH_LCD 0x10 /* LCD diagnostics are running */ +#define D_TXINH_PAUSE 0x20 /* We are processing a PAUSE command */ +#define D_TXINH_DCD 0x40 /* DCD is low, preventing transmission */ +#define D_TXINH_DSR 0x80 /* DSR is low, preventing transmission */ + +#define D_STAT1_TXEN 1 /* Transmit INTERRUPTS enabled */ +#define D_STAT1_RXEN 2 /* Receiver INTERRUPTS enabled */ +#define D_STAT1_MDEN 4 /* Modem (data set sigs) interrupts enabled */ +#define D_STAT1_RLM 8 /* Remote loopback mode selected */ +#define D_STAT1_LLM 0x10 /* Local internal loopback mode selected */ +#define D_STAT1_CTS 0x20 /* CTS is low, preventing transmission */ +#define D_STAT1_DTR 0x40 /* DTR is low, to stop remote transmission */ +#define D_STAT1_RTS 0x80 /* RTS is low, to stop remote transmission */ + +#define D_STAT2_TXMT 1 /* Transmit buffers are all empty */ +#define D_STAT2_RXMT 2 /* Receive buffers are all empty */ +#define D_STAT2_RXINH 4 /* Loadware has tried to inhibit remote + * transmission: dropped DTR, sent XOFF, + * whatever... + */ +#define D_STAT2_RXFLO 8 /* Loadware can send no more data to host + * until it receives a flow-control packet + */ +//----------------------------------------- +// STAT_TXCNT and STAT_RXCNT packets +//---------------------------------------- + +typedef struct _cntStat +{ + unsigned short cs_time; // (Assumes host is little-endian!) + unsigned short cs_count; +} cntStat, *cntStatPtr; + +// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT +// bypass command. cs_time is a running 1 Millisecond counter which acts as a +// time stamp. cs_count is a running counter of data sent or received from the +// uxarts. (Not including data added by the chip itself, as with CRLF +// processing). +//------------------------------------------ +// STAT_HWFAIL packets +//------------------------------------------ + +typedef struct _failStat +{ + unsigned char fs_written; + unsigned char fs_read; + unsigned short fs_address; +} failStat, *failStatPtr; + +// This packet is sent whenever the on-board diagnostic process detects an +// error. At startup, this process is dormant. The host can wake it up by +// issuing the bypass command CMD_HW_TEST. The process runs at low priority and +// performs continuous hardware verification; writing data to certain on-board +// registers, reading it back, and comparing. If it detects an error, this +// packet is sent to the host, and the process goes dormant again until the host +// sends another CMD_HW_TEST. It then continues with the next register to be +// tested. + +//------------------------------------------------------------------------------ +// Macros to deal with the headers more easily! Note that these are defined so +// they may be used as "left" as well as "right" expressions. +//------------------------------------------------------------------------------ + +// Given a pointer to the packet, reference the channel number +// +#define CHANNEL_OF(pP) ((i2DataHeaderPtr)(pP))->i2sChannel + +// Given a pointer to the packet, reference the Packet type +// +#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType + +// The possible types of packets +// +#define PTYPE_DATA 0 /* Host <--> Board */ +#define PTYPE_BYPASS 1 /* Host ---> Board */ +#define PTYPE_INLINE 2 /* Host ---> Board */ +#define PTYPE_STATUS 2 /* Host <--- Board */ + +// Given a pointer to a Data packet, reference the Tag +// +#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag + +// Given a pointer to a Data packet, reference the data i.d. +// +#define ID_OF(pP) ((i2DataHeaderPtr)(pP))->i2sId + +// The possible types of ID's +// +#define ID_ORDINARY_DATA 0 +#define ID_HOT_KEY 1 + +// Given a pointer to a Data packet, reference the count +// +#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount + +// Given a pointer to a Data packet, reference the beginning of data +// +#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header + +// Given a pointer to a Non-Data packet, reference the count +// +#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount + +#define MAX_CMD_PACK_SIZE 62 // Maximum size of such a count + +// Given a pointer to a Non-Data packet, reference the beginning of data +// +#define CMD_OF(pP) &((unsigned char *)(pP))[2] // 2 = size of header + +//-------------------------------- +// MailBox Bits: +//-------------------------------- + +//-------------------------- +// Outgoing (host to board) +//-------------------------- +// +#define MB_OUT_STUFFED 0x80 // Host has placed output in fifo +#define MB_IN_STRIPPED 0x40 // Host has read in all input from fifo + +//-------------------------- +// Incoming (board to host) +//-------------------------- +// +#define MB_IN_STUFFED 0x80 // Board has placed input in fifo +#define MB_OUT_STRIPPED 0x40 // Board has read all output from fifo +#define MB_FATAL_ERROR 0x20 // Board has encountered a fatal error + +#pragma pack(4) // Reset padding to command-line default + +#endif // I2PACK_H + diff --git a/drivers/char/ip2/ip2.h b/drivers/char/ip2/ip2.h new file mode 100644 index 000000000000..7236744b0245 --- /dev/null +++ b/drivers/char/ip2/ip2.h @@ -0,0 +1,109 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2_H +#define IP2_H + +#include "ip2types.h" +#include "i2cmd.h" + +/*************/ +/* Constants */ +/*************/ + +/* Device major numbers + * The first set are the major numbers allocated from the Linux Device Registry. + * This was expanded from 64 to 128 with version 2.0.26. If this code is built + * under earlier versions we use majors from the LOCAL/EXPERIMENTAL range. + */ +#if MAX_CHRDEV > 64 +# define IP2_TTY_MAJOR 71 +# define IP2_CALLOUT_MAJOR 72 +# define IP2_IPL_MAJOR 73 +#else +# define IP2_TTY_MAJOR 60 +# define IP2_CALLOUT_MAJOR 61 +# define IP2_IPL_MAJOR 62 +#endif + + +/* Board configuration array. + * This array defines the hardware irq and address for up to IP2_MAX_BOARDS + * (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified, + * PCI and EISA boards are probed for and automagicly configed + * iff the addresses are set to 1 and 2 respectivily. + * 0x0100 - 0x03f0 == ISA + * 1 == PCI + * 2 == EISA + * 0 == (skip this board) + * This array defines the hardware addresses for them. Special + * addresses are EISA and PCI which go sniffing for boards. + + * In a multiboard system the position in the array determines which port + * devices are assigned to each board: + * board 0 is assigned ttyF0.. to ttyF63, + * board 1 is assigned ttyF64 to ttyF127, + * board 2 is assigned ttyF128 to ttyF191, + * board 3 is assigned ttyF192 to ttyF255. + * + * In PCI and EISA bus systems each range is mapped to card in + * monotonically increasing slot number order, ISA position is as specified + * here. + + * If the irqs are ALL set to 0,0,0,0 all boards operate in + * polled mode. For interrupt operation ISA boards require that the IRQ be + * specified, while PCI and EISA boards any nonzero entry + * will enable interrupts using the BIOS configured irq for the board. + * An invalid irq entry will default to polled mode for that card and print + * console warning. + + * When the driver is loaded as a module these setting can be overridden on the + * modprobe command line or on an option line in /etc/conf.modules + * or /etc/modules.conf depending on your distrubution. + * If the driver is built-in the configuration must be + * set here for ISA cards and address set to 1 and 2 for PCI and EISA. + * + * Here is an example that shows most if not all possibe combinations: + + *static ip2config_t ip2config = + *{ + * {11,1,0,0}, // irqs + * { // Addresses + * 0x0308, // Board 0, ttyF0 - ttyF63// ISA card at io=0x308, irq=11 + * 0x0001, // Board 1, ttyF64 - ttyF127//PCI card configured by BIOS + * 0x0000, // Board 2, ttyF128 - ttyF191// Slot skipped + * 0x0002 // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS + * // but polled not irq driven + * } + *}; + */ + + /* this structure is zeroed out because the suggested method is to configure + * the driver as a module, set up the parameters with an options line in + * /etc/modules.conf or /etc/conf.modules and load with modprobe, kerneld or + * kmod, the kernel module loader + */ +static ip2config_t ip2config = +{ + {0,0,0,0}, // irqs + { // Addresses + 0x0000, // Board 0, ttyF0 - ttyF63 + 0x0000, // Board 1, ttyF64 - ttyF127 + 0x0000, // Board 2, ttyF128 - ttyF191 + 0x0000 // Board 3, ttyF192 - ttyF255 + } +}; + +#endif diff --git a/drivers/char/ip2/ip2ioctl.h b/drivers/char/ip2/ip2ioctl.h new file mode 100644 index 000000000000..aa0a9da85e05 --- /dev/null +++ b/drivers/char/ip2/ip2ioctl.h @@ -0,0 +1,35 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ + +#ifndef IP2IOCTL_H +#define IP2IOCTL_H + +//************* +//* Constants * +//************* + +// High baud rates (if not defined elsewhere. +#ifndef B153600 +# define B153600 0010005 +#endif +#ifndef B307200 +# define B307200 0010006 +#endif +#ifndef B921600 +# define B921600 0010007 +#endif + +#endif diff --git a/drivers/char/ip2/ip2trace.h b/drivers/char/ip2/ip2trace.h new file mode 100644 index 000000000000..020aabb735a7 --- /dev/null +++ b/drivers/char/ip2/ip2trace.h @@ -0,0 +1,43 @@ + +// +union ip2breadcrumb +{ + struct { + unsigned char port, cat, codes, label; + } __attribute__ ((packed)) hdr; + unsigned long value; +}; + +#define ITRC_NO_PORT 0xFF +#define PORTN (port->port_index) +#define CHANN (pCh->port_index) + +#define ITRC_ERROR '!' +#define ITRC_INIT 'A' +#define ITRC_OPEN 'B' +#define ITRC_CLOSE 'C' +#define ITRC_DRAIN 'D' +#define ITRC_IOCTL 'E' +#define ITRC_FLUSH 'F' +#define ITRC_STATUS 'G' +#define ITRC_HANGUP 'H' +#define ITRC_INTR 'I' +#define ITRC_SFLOW 'J' +#define ITRC_SBCMD 'K' +#define ITRC_SICMD 'L' +#define ITRC_MODEM 'M' +#define ITRC_INPUT 'N' +#define ITRC_OUTPUT 'O' +#define ITRC_PUTC 'P' +#define ITRC_QUEUE 'Q' +#define ITRC_STFLW 'R' +#define ITRC_SFIFO 'S' +#define ITRC_VERIFY 'V' +#define ITRC_WRITE 'W' + +#define ITRC_ENTER 0x00 +#define ITRC_RETURN 0xFF + +#define ITRC_QUEUE_ROOM 2 +#define ITRC_QUEUE_CMD 6 + diff --git a/drivers/char/ip2/ip2types.h b/drivers/char/ip2/ip2types.h new file mode 100644 index 000000000000..69a727cc3bed --- /dev/null +++ b/drivers/char/ip2/ip2types.h @@ -0,0 +1,54 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants and type definitions. +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2TYPES_H +#define IP2TYPES_H + +//************* +//* Constants * +//************* + +// Define some limits for this driver. Ports per board is a hardware limitation +// that will not change. Current hardware limits this to 64 ports per board. +// Boards per driver is a self-imposed limit. +// +#define IP2_MAX_BOARDS 4 +#define IP2_PORTS_PER_BOARD ABS_MOST_PORTS +#define IP2_MAX_PORTS (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD) + +#define ISA 0 +#define PCI 1 +#define EISA 2 + +//******************** +//* Type Definitions * +//******************** + +typedef struct tty_struct * PTTY; +typedef struct wait_queue * PWAITQ; + +typedef unsigned char UCHAR; +typedef unsigned int UINT; +typedef unsigned short USHORT; +typedef unsigned long ULONG; + +typedef struct +{ + short irq[IP2_MAX_BOARDS]; + unsigned short addr[IP2_MAX_BOARDS]; + int type[IP2_MAX_BOARDS]; +} ip2config_t; + +#endif diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c new file mode 100644 index 000000000000..77e0f95af802 --- /dev/null +++ b/drivers/char/ip2main.c @@ -0,0 +1,3236 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +/************/ +/* Includes */ +/************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#define pcibios_strerror(status) \ + printk( KERN_ERR "IP2: PCI error 0x%x \n", status ); + +#include "./ip2/ip2trace.h" +#include "./ip2/ip2ioctl.h" +#include "./ip2/ip2.h" +#include "./ip2/i2ellis.h" +#include "./ip2/i2lib.h" + +/***************** + * /proc/ip2mem * + *****************/ + +#include + +int ip2_read_procmem(char *, char **, off_t, int, int ); +int ip2_read_proc(char *, char **, off_t, int, int *, void * ); + +struct proc_dir_entry ip2_proc_entry = { + 0, + 6,"ip2mem", + S_IFREG | S_IRUGO, + 1, 0, 0, + 0, + NULL, + ip2_read_procmem +}; + +/********************/ +/* Type Definitions */ +/********************/ + +/*************/ +/* Constants */ +/*************/ + +/* String constants to identify ourselves */ +static char *pcName = "Computone IntelliPort Plus multiport driver"; +static char *pcVersion = "1.2.4"; + +/* String constants for port names */ +static char *pcDriver_name = "ip2"; +static char *pcTty = "ttyf"; +static char *pcCallout = "cuf"; +static char *pcIpl = "ip2ipl"; + +/* Serial subtype definitions */ +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +// cheezy kludge or genius - you decide? +int ip2_loadmain(int *, int *, unsigned char *, int); +static unsigned char *Fip_firmware; +static int Fip_firmware_size; + +/***********************/ +/* Function Prototypes */ +/***********************/ + +/* Global module entry functions */ +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif + +int old_ip2_init(void); + +/* Private (static) functions */ +static int ip2_open(PTTY, struct file *); +static void ip2_close(PTTY, struct file *); +static int ip2_write(PTTY, int, const unsigned char *, int); +static void ip2_putchar(PTTY, unsigned char); +static void ip2_flush_chars(PTTY); +static int ip2_write_room(PTTY); +static int ip2_chars_in_buf(PTTY); +static void ip2_flush_buffer(PTTY); +static int ip2_ioctl(PTTY, struct file *, UINT, ULONG); +static void ip2_set_termios(PTTY, struct termios *); +static void ip2_set_line_discipline(PTTY); +static void ip2_throttle(PTTY); +static void ip2_unthrottle(PTTY); +static void ip2_stop(PTTY); +static void ip2_start(PTTY); +static void ip2_hangup(PTTY); + +static void set_irq(int, int); +static void ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static void ip2_poll(unsigned long arg); +static inline void service_all_boards(void); +static inline void do_input(i2ChanStrPtr pCh); +static inline void do_status(i2ChanStrPtr pCh); + +static void ip2_wait_until_sent(PTTY,int); + +static void set_params (i2ChanStrPtr, struct termios *); +static int get_modem_info(i2ChanStrPtr, unsigned int *); +static int set_modem_info(i2ChanStrPtr, unsigned int, unsigned int *); +static int get_serial_info(i2ChanStrPtr, struct serial_struct *); +static int set_serial_info(i2ChanStrPtr, struct serial_struct *); + +static ssize_t ip2_ipl_read(struct file *, char *, size_t, loff_t *) ; +static ssize_t ip2_ipl_write(struct file *, const char *, size_t, loff_t *); +static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG); +static int ip2_ipl_open(struct inode *, struct file *); + +void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...); +static int DumpTraceBuffer(char *, int); +static int DumpFifoBuffer( char *, int); + +static void ip2_init_board(int); +static unsigned short find_eisa_board(int); + +/***************/ +/* Static Data */ +/***************/ + +static struct tty_driver ip2_tty_driver; +static struct tty_driver ip2_callout_driver; + +static int ref_count; + +/* Here, then is a table of board pointers which the interrupt routine should + * scan through to determine who it must service. + */ +static unsigned short i2nBoards = 0; // Number of boards here + +static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS]; + +static i2ChanStrPtr DevTable[IP2_MAX_PORTS]; +//DevTableMem just used to save addresses for kfree +static void *DevTableMem[IP2_MAX_BOARDS] = {NULL,NULL,NULL,NULL}; + +static struct tty_struct * TtyTable[IP2_MAX_PORTS]; +static struct termios * Termios[IP2_MAX_PORTS]; +static struct termios * TermiosLocked[IP2_MAX_PORTS]; + +/* This is the driver descriptor for the ip2ipl device, which is used to + * download the loadware to the boards. + */ +static struct file_operations +ip2_ipl = { + NULL, + ip2_ipl_read, + ip2_ipl_write, + NULL, + NULL, + ip2_ipl_ioctl, + NULL, + ip2_ipl_open, + NULL, + NULL, + NULL, + NULL, + NULL, + /* NULL, NULL 2.2 */ +}; + +static long irq_counter = 0; +static long bh_counter = 0; + +// Use immediate queue to service interrupts +//#define USE_IQI // PCI&2.2 needs work +//#define USE_IQ // PCI&2.2 needs work + +/* The timer_list entry for our poll routine. If interrupt operation is not + * selected, the board is serviced periodically to see if anything needs doing. + */ +#define POLL_TIMEOUT (jiffies + 1) +static struct timer_list PollTimer = { NULL, NULL, 0, 0, ip2_poll }; +// next, prev, expires,data, func() +static char TimerOn = 0; + +#ifdef IP2DEBUG_TRACE +/* Trace (debug) buffer data */ +#define TRACEMAX 1000 +static unsigned long tracebuf[TRACEMAX]; +static int tracestuff = 0; +static int tracestrip = 0; +static int tracewrap = 0; +#endif + +/**********/ +/* Macros */ +/**********/ + +#if defined(MODULE) && defined(IP2DEBUG_OPEN) +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ + kdevname(tty->device),(pCh->flags),ref_count, \ + tty->count,/*GET_USE_COUNT(module)*/0,s) +#else +#define DBG_CNT(s) +#endif + +#define MIN(a,b) ( ( (a) < (b) ) ? (a) : (b) ) +#define MAX(a,b) ( ( (a) > (b) ) ? (a) : (b) ) + +/********/ +/* Code */ +/********/ + +#include "./ip2/i2ellis.c" /* Extremely low-level interface services */ +#include "./ip2/i2cmd.c" /* Standard loadware command definitions */ +#include "./ip2/i2lib.c" /* High level interface services */ + +/* Configuration area for modprobe */ +#ifdef MODULE +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +#endif /* MODULE */ + +static int poll_only = 0; + +static int Pci_index = 0; +static int Eisa_irq = 0; +static int Eisa_slot = 0; + +static int iindx = 0; +static char rirqs[IP2_MAX_BOARDS] = {0,}; +static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; + +/******************************************************************************/ +/* Initialisation Section */ +/******************************************************************************/ +int +ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) +{ + int i; + /* process command line arguments to modprobe or insmod i.e. iop & irqp */ + /* otherwise ip2config is initialized by what's in ip2/ip2.h */ + /* command line trumps initialization in ip2.h */ + /* first two args are null if builtin to kernel */ + if ((irqp != NULL) || (iop != NULL)) { + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (irqp && irqp[i]) { + ip2config.irq[i] = irqp[i]; + } + if (iop && iop[i]) { + ip2config.addr[i] = iop[i]; + } + } + } + Fip_firmware = firmware; + Fip_firmware_size = firmsize; + return old_ip2_init(); +} + +// Some functions to keep track of what irq's we have + +__initfunc(static int +is_valid_irq(int irq) ) +{ + int *i = Valid_Irqs; + + while ((*i != 0) && (*i != irq)) { + i++; + } + return (*i); +} + +__initfunc( static void +mark_requested_irq( char irq )) +{ + rirqs[iindx++] = irq; +} + +__initfunc( static int +clear_requested_irq( char irq )) +{ + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) { + rirqs[i] = 0; + return 1; + } + } + return 0; +} + +__initfunc( static int +have_requested_irq( char irq )) +{ + // array init to zeros so 0 irq will not be requested as a side effect + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) + return 1; + } + return 0; +} + +/******************************************************************************/ +/* Function: init_module() */ +/* Parameters: None */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It simply calls */ +/* the driver initialisation function and returns what it returns. */ +/******************************************************************************/ +#ifdef MODULE +int +init_module(void) +{ +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Loading module ...\n" ); +#endif + //was return old_ip2_init(); + return 0; +} +#endif /* MODULE */ + +/******************************************************************************/ +/* Function: cleanup_module() */ +/* Parameters: None */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It has to return */ +/* the device and the driver to a passive state. It should not be necessary */ +/* to reset the board fully, especially as the loadware is downloaded */ +/* externally rather than in the driver. We just want to disable the board */ +/* and clear the loadware to a reset state. To allow this there has to be a */ +/* way to detect whether the board has the loadware running at init time to */ +/* handle subsequent installations of the driver. All memory allocated by the */ +/* driver should be returned since it may be unloaded from memory. */ +/******************************************************************************/ +#ifdef MODULE +void +cleanup_module(void) +{ + int err; + int i; + +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion ); +#endif + + + /* Stop poll timer if we had one. */ + if ( TimerOn ) { + del_timer ( &PollTimer ); + TimerOn = 0; + } + + /* Reset the boards we have. */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiReset ( i2BoardPtrTable[i] ); + } + } + + /* The following is done at most once, if any boards were installed. */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiResetDelay( i2BoardPtrTable[i] ); + /* free io addresses and Tibet */ + release_region( ip2config.addr[i], 8 ); + } + /* Disable and remove interrupt handler. */ + if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) { + free_irq ( ip2config.irq[i], (void *)&pcName); + clear_requested_irq( ip2config.irq[i]); + } + } + if ( ( err = tty_unregister_driver ( &ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err); + } + if ( ( err = tty_unregister_driver ( &ip2_callout_driver ) ) ) { + printk(KERN_ERR "IP2: failed to unregister callout driver (%d)\n", err); + } + if ( ( err = unregister_chrdev ( IP2_IPL_MAJOR, pcIpl ) ) ) { + printk(KERN_ERR "IP2: failed to unregister IPL driver (%d)\n", err); + } + if ( ( err = proc_unregister( &proc_root, ip2_proc_entry.low_ino ) ) ) { + printk(KERN_ERR "IP2: failed to unregister read_procmem (%d)\n", err); + } + + // free memory + for (i = 0; i < IP2_MAX_BOARDS; i++) { + void *pB; + if ((pB = i2BoardPtrTable[i]) != 0 ) { + kfree ( pB ); + i2BoardPtrTable[i] = NULL; + } + if ((DevTableMem[i]) != NULL ) { + kfree ( DevTableMem[i] ); + DevTableMem[i] = NULL; + } + } + + /* Cleanup the iiEllis subsystem. */ + iiEllisCleanup(); +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "IP2 Unloaded\n" ); +#endif +} +#endif /* MODULE */ + +/******************************************************************************/ +/* Function: old_ip2_init() */ +/* Parameters: irq, io from command line of insmod et. al. */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This was the required entry point for all drivers (now in ip2.c) */ +/* It performs all */ +/* initialisation of the devices and driver structures, and registers itself */ +/* with the relevant kernel modules. */ +/******************************************************************************/ +/* SA_INTERRUPT- if set blocks all interrupts else only this line */ +/* SA_SHIRQ - for shared irq PCI or maybe EISA only */ +/* SA_RANDOM - can be source for cert. random number generators */ +#define IP2_SA_FLAGS 0 + +__initfunc( int +old_ip2_init(void)) +{ + int i; + int err; + int status = 0; + i2eBordStrPtr pB = NULL; + int rc = -1; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 ); +#endif + + /* Announce our presence */ + printk( KERN_INFO "%s version %s\n", pcName, pcVersion ); + + /* if all irq config is zero we shall poll_only */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + poll_only |= ip2config.irq[i]; + } + poll_only = !poll_only; + + /* Initialise the iiEllis subsystem. */ + iiEllisInit(); + + /* Initialize arrays. */ + memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable ); + memset( DevTable, 0, sizeof DevTable ); + memset( TtyTable, 0, sizeof TtyTable ); + memset( Termios, 0, sizeof Termios ); + memset( TermiosLocked, 0, sizeof TermiosLocked ); + + /* Initialise all the boards we can find (up to the maximum). */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + switch ( ip2config.addr[i] ) { + case 0: /* skip this slot even if card is present */ + break; + default: /* ISA */ + /* ISA address must be specified */ + if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) { + printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n", + i, ip2config.addr[i] ); + ip2config.addr[i] = 0; + } else { + ip2config.type[i] = ISA; + + /* Check for valid irq argument, set for polling if invalid */ + if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) { + printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]); + ip2config.irq[i] = 0;// 0 is polling and is valid in that sense + } + } + break; + case PCI: +#ifdef CONFIG_PCI + if (pci_present()) { + struct pci_dev *pci_dev_i = NULL; + pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE, + PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i); + if (pci_dev_i != NULL) { + unsigned int addr; + unsigned char pci_irq; + + ip2config.type[i] = PCI; + /* + * Update Pci_index, so that the next time we go + * searching for a PCI board we find a different + * one. + */ + ++Pci_index; + status = + pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr); + if ( addr & 1 ) { + ip2config.addr[i]=(USHORT)(addr&0xfffe); + } else { + printk( KERN_ERR "IP2: PCI I/O address error\n"); + } + status = + pci_read_config_byte(pci_dev_i, PCI_INTERRUPT_LINE, &pci_irq); + + if (!is_valid_irq(pci_irq)) { + printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq); + pci_irq = 0; + } + ip2config.irq[i] = pci_irq; + } else { // ann error + ip2config.addr[i] = 0; + if (status == PCIBIOS_DEVICE_NOT_FOUND) { + printk( KERN_ERR "IP2: PCI board %d not found\n", i ); + } else { + pcibios_strerror(status); + } + } + } +#else + printk( KERN_ERR "IP2: PCI card specified but PCI support not\n"); + printk( KERN_ERR "IP2: configured in this kernel.\n"); + printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n"); +#endif /* CONFIG_PCI */ + break; + case EISA: + if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) { + /* Eisa_irq set as side effect, boo */ + ip2config.type[i] = EISA; + } + ip2config.irq[i] = Eisa_irq; + break; + } /* switch */ + } /* for */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ip2config.addr[i] ) { + pB = kmalloc( sizeof(i2eBordStr), GFP_KERNEL); + if ( pB != NULL ) { + i2BoardPtrTable[i] = pB; + memset( pB, 0, sizeof(i2eBordStr) ); + iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer ); + iiReset( pB ); + } else { + printk(KERN_ERR "IP2: board memory allocation error\n"); + } + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ( pB = i2BoardPtrTable[i] ) != NULL ) { + iiResetDelay( pB ); + break; + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] != NULL ) { + ip2_init_board( i ); + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 ); +#endif + + /* Zero out the normal tty device structure. */ + memset ( &ip2_tty_driver, 0, sizeof ip2_tty_driver ); + + /* Initialise the relevant fields. */ + ip2_tty_driver.magic = TTY_DRIVER_MAGIC; + ip2_tty_driver.name = pcTty; + ip2_tty_driver.driver_name = pcDriver_name; + ip2_tty_driver.read_proc = ip2_read_proc; + ip2_tty_driver.major = IP2_TTY_MAJOR; + ip2_tty_driver.minor_start = 0; + ip2_tty_driver.num = IP2_MAX_PORTS; + ip2_tty_driver.type = TTY_DRIVER_TYPE_SERIAL; + ip2_tty_driver.subtype = SERIAL_TYPE_NORMAL; + ip2_tty_driver.init_termios = tty_std_termios; + ip2_tty_driver.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + ip2_tty_driver.flags = TTY_DRIVER_REAL_RAW; + ip2_tty_driver.refcount = &ref_count; + ip2_tty_driver.table = TtyTable; + ip2_tty_driver.termios = Termios; + ip2_tty_driver.termios_locked = TermiosLocked; + + /* Setup the pointers to the implemented functions. */ + ip2_tty_driver.open = ip2_open; + ip2_tty_driver.close = ip2_close; + ip2_tty_driver.write = ip2_write; + ip2_tty_driver.put_char = ip2_putchar; + ip2_tty_driver.flush_chars = ip2_flush_chars; + ip2_tty_driver.write_room = ip2_write_room; + ip2_tty_driver.chars_in_buffer = ip2_chars_in_buf; + ip2_tty_driver.flush_buffer = ip2_flush_buffer; + ip2_tty_driver.ioctl = ip2_ioctl; + ip2_tty_driver.throttle = ip2_throttle; + ip2_tty_driver.unthrottle = ip2_unthrottle; + ip2_tty_driver.set_termios = ip2_set_termios; + ip2_tty_driver.set_ldisc = ip2_set_line_discipline; + ip2_tty_driver.stop = ip2_stop; + ip2_tty_driver.start = ip2_start; + ip2_tty_driver.hangup = ip2_hangup; + + /* Initialise the callout driver structure from the tty driver, and + * make the needed adjustments. + */ + ip2_callout_driver = ip2_tty_driver; + ip2_callout_driver.name = pcCallout; + ip2_callout_driver.driver_name = pcDriver_name; + ip2_callout_driver.read_proc = NULL; + ip2_callout_driver.major = IP2_CALLOUT_MAJOR; + ip2_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 ); +#endif + + /* Register the tty devices. */ + if ( ( err = tty_register_driver ( &ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err); + } else + if ( ( err = tty_register_driver ( &ip2_callout_driver ) ) ) { + printk(KERN_ERR "IP2: failed to register callout driver (%d)\n", err); + } else + /* Register the IPL driver. */ + if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) { + printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err ); + } else + /* Register the read_procmem thing */ + if ( ( err = proc_register( &proc_root, &ip2_proc_entry ) ) ) { + printk(KERN_ERR "IP2: failed to register read_procmem (%d)\n", err ); + } else { + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 ); +#endif + /* Register the interrupt handler or poll handler, depending upon the + * specified interrupt. + */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( 0 == ip2config.addr[i] ) { + continue; + } + if (poll_only) { + ip2config.irq[i] = CIR_POLL; + } + if ( ip2config.irq[i] == CIR_POLL ) { +retry: + if (!TimerOn) { + PollTimer.expires = POLL_TIMEOUT; + add_timer ( &PollTimer ); + TimerOn = 1; + printk( KERN_INFO "IP2: polling\n"); + } + } else { + if (have_requested_irq(ip2config.irq[i])) + continue; + rc = request_irq( ip2config.irq[i], ip2_interrupt, + IP2_SA_FLAGS | (ip2config.type[i] == PCI ? SA_SHIRQ : 0), + pcName, (void *)&pcName); + if (rc) { + printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc); + ip2config.irq[i] = CIR_POLL; + printk( KERN_INFO "IP2: Polling %ld/sec.\n", + (POLL_TIMEOUT - jiffies)); + goto retry; + } + mark_requested_irq(ip2config.irq[i]); + /* Initialise the interrupt handler bottom half (aka slih). */ + } + } + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */ + } + } + } +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 ); +#endif + + return 0; +} + +/******************************************************************************/ +/* Function: ip2_init_board() */ +/* Parameters: Index of board in configuration structure */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This function initializes the specified board. The loadware is copied to */ +/* the board, the channel structures are initialized, and the board details */ +/* are reported on the console. */ +/******************************************************************************/ +__initfunc( static void +ip2_init_board( int boardnum )) +{ + int i,rc; + int nports = 0, nboxes = 0; + i2ChanStrPtr pCh; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + + if ( !iiInitialize ( pB ) ) { + printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n", + pB->i2eBase, pB->i2eError ); + kfree ( pB ); + i2BoardPtrTable[boardnum] = NULL; + return; + } + printk(KERN_INFO "Board %d: addr=0x%x irq=%d ", boardnum + 1, + ip2config.addr[boardnum], ip2config.irq[boardnum] ); + + if (0 != ( rc = check_region( ip2config.addr[boardnum], 8))) { + i2BoardPtrTable[boardnum] = NULL; + printk(KERN_ERR "bad addr=0x%x rc = %d\n", + ip2config.addr[boardnum], rc ); + return; + } + request_region( ip2config.addr[boardnum], 8, pcName ); + + if ( iiDownloadAll ( pB, (loadHdrStrPtr)Fip_firmware, 1, Fip_firmware_size ) + != II_DOWN_GOOD ) { + printk ( KERN_ERR "IP2:failed to download loadware " ); + } else { + printk ( KERN_INFO "fv=%d.%d.%d lv=%d.%d.%d\n", + pB->i2ePom.e.porVersion, + pB->i2ePom.e.porRevision, + pB->i2ePom.e.porSubRev, pB->i2eLVersion, + pB->i2eLRevision, pB->i2eLSub ); + } + + switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) { + + default: + printk( KERN_ERR "IP2: Unknown board type, ID = %x", + pB->i2ePom.e.porID ); + nports = 0; + goto ex_exit; + break; + + case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */ + printk ( KERN_INFO "ISA-4" ); + nports = 4; + break; + + case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */ + printk ( KERN_INFO "ISA-8 std" ); + nports = 8; + break; + + case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */ + printk ( KERN_INFO "ISA-8 RJ11" ); + nports = 8; + break; + + case POR_ID_FIIEX: /* IntelliPort IIEX */ + { + int portnum = IP2_PORTS_PER_BOARD * boardnum; + int box; + + for( box = 0; box < ABS_MAX_BOXES; ++box ) { + if ( pB->i2eChannelMap[box] != 0 ) { + ++nboxes; + } + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & 1<< i ) { + ++nports; + } + } + } + DevTableMem[boardnum] = pCh = + kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL ); + if ( !i2InitChannels( pB, nports, pCh ) ) { + printk(KERN_ERR "i2InitChannels failed: %d\n",pB->i2eError); + } + pB->i2eChannelPtr = &DevTable[portnum]; + pB->i2eChannelCnt = ABS_MOST_PORTS; + + for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) { + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & (1 << i) ) { + DevTable[portnum + i] = pCh; + pCh->port_index = portnum + i; + pCh++; + } + } + } + printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit", + nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 ); + } + goto ex_exit; + break; + } + DevTableMem[boardnum] = pCh = + kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL ); + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nports; + i2InitChannels ( pB, pB->i2eChannelCnt, pCh ); + pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum]; + + for( i = 0; i < pB->i2eChannelCnt; ++i ) { + DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh; + pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i; + pCh++; + } +ex_exit: + printk ( KERN_INFO "\n" ); +} + +/******************************************************************************/ +/* Function: find_eisa_board ( int start_slot ) */ +/* Parameters: First slot to check */ +/* Returns: Address of EISA IntelliPort II controller */ +/* */ +/* Description: */ +/* This function searches for an EISA IntelliPort controller, starting */ +/* from the specified slot number. If the motherboard is not identified as an */ +/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */ +/* it returns the base address of the controller. */ +/******************************************************************************/ +__initfunc( static unsigned short +find_eisa_board( int start_slot )) +{ + int i, j; + unsigned int idm = 0; + unsigned int idp = 0; + unsigned int base = 0; + unsigned int value; + int setup_address; + int setup_irq; + int ismine = 0; + + /* + * First a check for an EISA motherboard, which we do by comparing the + * EISA ID registers for the system board and the first couple of slots. + * No slot ID should match the system board ID, but on an ISA or PCI + * machine the odds are that an empty bus will return similar values for + * each slot. + */ + i = 0x0c80; + value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3); + for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) { + j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3); + if ( value == j ) + return 0; + } + + /* + * OK, so we are inclined to believe that this is an EISA machine. Find + * an IntelliPort controller. + */ + for( i = start_slot; i < 16; i++ ) { + base = i << 12; + idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff); + idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff); + ismine = 0; + if ( idm == 0x0e8e ) { + if ( idp == 0x0281 || idp == 0x0218 ) { + ismine = 1; + } else if ( idp == 0x0282 || idp == 0x0283 ) { + ismine = 3; /* Can do edge-trigger */ + } + if ( ismine ) { + Eisa_slot = i; + break; + } + } + } + if ( !ismine ) + return 0; + + /* It's some sort of EISA card, but at what address is it configured? */ + + setup_address = base + 0xc88; + value = inb(base + 0xc86); + setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0; + + if ( (ismine & 2) && !(value & 0x10) ) { + ismine = 1; /* Could be edging, but not */ + } + + if ( Eisa_irq == 0 ) { + Eisa_irq = setup_irq; + } else if ( Eisa_irq != setup_irq ) { + printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" ); + } + +#ifdef IP2DEBUG_INIT +printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x", + base >> 12, idm, idp, setup_address); + if ( Eisa_irq ) { + printk(KERN_DEBUG ", Interrupt %d %s\n", + setup_irq, (ismine & 2) ? "(edge)" : "(level)"); + } else { + printk(KERN_DEBUG ", (polled)\n"); + } +#endif + return setup_address; +} + +/******************************************************************************/ +/* Function: set_irq() */ +/* Parameters: index to board in board table */ +/* IRQ to use */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/******************************************************************************/ +static void +set_irq( int boardnum, int boardIrq ) +{ + i2ChanStrPtr pCh; + unsigned char tempCommand[16]; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + unsigned long flags; + + /* + * Notify the boards they may generate interrupts. This is done by + * sending an in-line command to channel 0 on each board. This is why + * the channels have to be defined already. For each board, if the + * interrupt has never been defined, we must do so NOW, directly, since + * board will not send flow control or even give an interrupt until this + * is done. If polling we must send 0 as the interrupt parameter. + */ + + pCh = (i2ChanStrPtr) pB->i2eChannelPtr; + + // We will get an interrupt here at the end of this function + + iiDisableMailIrq(pB); + + /* We build up the entire packet header. */ + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_INLINE; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ; + (CMD_OF(tempCommand))[1] = boardIrq; + /* + * Write to FIFO; don't bother to adjust fifo capacity for this, since + * board will respond almost immediately after SendMail hit. + */ + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 4); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + pB->i2eUsingIrq = boardIrq; + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + + /* Need to update number of boards before you enable mailbox int */ + ++i2nBoards; + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 6; + (CMD_OF(tempCommand))[0] = 88; // SILO + (CMD_OF(tempCommand))[1] = 64; // chars + (CMD_OF(tempCommand))[2] = 32; // ms + + (CMD_OF(tempCommand))[3] = 28; // MAX_BLOCK + (CMD_OF(tempCommand))[4] = 64; // chars + + (CMD_OF(tempCommand))[5] = 87; // HW_TEST + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 8); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 1; + (CMD_OF(tempCommand))[0] = 84; /* get BOX_IDS */ + iiWriteBuf(pB, tempCommand, 3); + +#ifdef XXX + // enable heartbeat for test porpoises + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = 44; /* get ping */ + (CMD_OF(tempCommand))[1] = 200; /* 200 ms */ + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 4); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); +#endif + + iiEnableMailIrq(pB); + iiSendPendingMail(pB); +} + +/******************************************************************************/ +/* Interrupt Handler Section */ +/******************************************************************************/ + +static inline void +service_all_boards() +{ + int i; + i2eBordStrPtr pB; + + /* Service every board on the list */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + i2ServiceBoard( pB ); + } + } +} + + +#ifdef USE_IQI +static struct tq_struct +senior_service = +{ // it's the death that worse than fate + NULL, + 0, + (void(*)(void*)) service_all_boards, + NULL, //later - board address XXX +}; +#endif + +/******************************************************************************/ +/* Function: ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) */ +/* Parameters: irq - interrupt number */ +/* pointer to optional device ID structure */ +/* pointer to register structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + int i; + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq ); +#endif + +#ifdef USE_IQI + + queue_task(&senior_service, &tq_immediate); + mark_bh(IMMEDIATE_BH); + +#else + /* Service just the boards on the list using this irq */ + for( i = 0; i < i2nBoards; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB && (pB->i2eUsingIrq == irq) ) { + i2ServiceBoard( pB ); + } + } + +#endif /* USE_IQI */ + + ++irq_counter; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_poll(unsigned long arg) */ +/* Parameters: ? */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function calls the library routine i2ServiceBoard for each board in */ +/* the board table. This is used instead of the interrupt routine when polled */ +/* mode is specified. */ +/******************************************************************************/ +static void +ip2_poll(unsigned long arg) +{ +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 ); +#endif + TimerOn = 0; // it's the truth but not checked in service + + bh_counter++; + +#ifdef USE_IQI + + queue_task(&senior_service, &tq_immediate); + mark_bh(IMMEDIATE_BH); + +#else + // Just polled boards, service_all might be better + ip2_interrupt(0, NULL, NULL); + +#endif /* USE_IQI */ + + PollTimer.expires = POLL_TIMEOUT; + add_timer( &PollTimer ); + TimerOn = 1; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +#endif +} + +static inline void +do_input( i2ChanStrPtr pCh ) +{ + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace(PORTN, ITRC_INPUT, 21, 0 ); +#endif + // Data input + if ( pCh->pTTY != NULL ) { + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) { + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + i2Input( pCh ); + } else + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + } else { +#ifdef IP2DEBUG_TRACE + ip2trace(PORTN, ITRC_INPUT, 22, 0 ); +#endif + i2InputFlush( pCh ); + } +} + +// code duplicated from n_tty (ldisc) +static inline void +isig(int sig, struct tty_struct *tty, int flush) +{ + if (tty->pgrp > 0) + kill_pg(tty->pgrp, sig, 1); + if (flush || !L_NOFLSH(tty)) { + if ( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer(tty); + i2InputFlush( tty->driver_data ); + } +} + +static inline void +do_status( i2ChanStrPtr pCh ) +{ + int status; + + status = i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_STATUS, 21, 1, status ); +#endif + + if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) { + if ( (status & I2_BRK) ) { + // code duplicated from n_tty (ldisc) + if (I_IGNBRK(pCh->pTTY)) + goto skip_this; + if (I_BRKINT(pCh->pTTY)) { + isig(SIGINT, pCh->pTTY, 1); + goto skip_this; + } + wake_up_interruptible(&pCh->pTTY->read_wait); + } +#ifdef NEVER_HAPPENS_AS_SETUP_XXX + // and can't work because we don't know the_char + // as the_char is reported on a seperate path + // The intelligent board does this stuff as setup + { + char brkf = TTY_NORMAL; + unsigned char brkc = '\0'; + unsigned char tmp; + if ( (status & I2_BRK) ) { + brkf = TTY_BREAK; + brkc = '\0'; + } + else if (status & I2_PAR) { + brkf = TTY_PARITY; + brkc = the_char; + } else if (status & I2_FRA) { + brkf = TTY_FRAME; + brkc = the_char; + } else if (status & I2_OVR) { + brkf = TTY_OVERRUN; + brkc = the_char; + } + tmp = pCh->pTTY->real_raw; + pCh->pTTY->real_raw = 0; + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); + pCh->pTTY->real_raw = tmp; + } +#endif /* NEVER_HAPPENS_AS_SETUP_XXX */ + } +skip_this: + + if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) { + wake_up_interruptible(&pCh->delta_msr_wait); + + if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) { + if ( status & I2_DCD ) { + if ( pCh->wopen ) { + wake_up_interruptible ( &pCh->open_wait ); + } + } else if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) ) { + if (pCh->pTTY && (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) { + tty_hangup( pCh->pTTY ); + } + } + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_STATUS, 26, 0 ); +#endif +} + +/******************************************************************************/ +/* Device Open/Close/Ioctl Entry Point Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: open_sanity_check() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* Verifies the structure magic numbers and cross links. */ +/******************************************************************************/ +#ifdef IP2DEBUG_OPEN +static void +open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd ) +{ + if ( pBrd->i2eValid != I2E_MAGIC ) { + printk(KERN_ERR "IP2: invalid board structure\n" ); + } else if ( pBrd != pCh->pMyBord ) { + printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n", + pCh->pMyBord ); + } else if ( pBrd->i2eChannelCnt < pCh->port_index ) { + printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index ); + } else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) { + } else { + printk(KERN_INFO "IP2: all pointers check out!\n" ); + } +} +#endif + + +/******************************************************************************/ +/* Function: ip2_open() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: (MANDATORY) */ +/* A successful device open has to run a gauntlet of checks before it */ +/* completes. After some sanity checking and pointer setup, the function */ +/* blocks until all conditions are satisfied. It then initialises the port to */ +/* the default characteristics and returns. */ +/******************************************************************************/ +static int +ip2_open( PTTY tty, struct file *pFile ) +{ + int rc = 0; + int do_clocal = 0; + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + +#ifdef IP2DEBUG_TRACE + ip2trace (MINOR(tty->device), ITRC_OPEN, ITRC_ENTER, 0 ); +#endif + + if ( pCh == NULL ) { + return -ENODEV; + } + /* Setup pointer links in device and tty structures */ + pCh->pTTY = tty; + tty->driver_data = pCh; + MOD_INC_USE_COUNT; + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG \ + "IP2:open(tty=%p,pFile=%p):dev=%x,maj=%d,min=%d,ch=%d,idx=%d\n", + tty, pFile, tty->device, MAJOR(tty->device), MINOR(tty->device), + pCh->infl.hd.i2sChannel, pCh->port_index); + open_sanity_check ( pCh, pCh->pMyBord ); +#endif + + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, + CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP); + serviceOutgoingFifo( pCh->pMyBord ); + + /* Block here until the port is ready (per serial and istallion) */ + /* + * 1. If the port is in the middle of closing wait for the completion + * and then return the appropriate error. + */ + if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) { + if ( pCh->flags & ASYNC_CLOSING ) { + interruptible_sleep_on( &pCh->close_wait); + } + if ( tty_hung_up_p(pFile) ) { + return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS; + } + } + /* + * 2. If this is a callout device, make sure the normal port is not in + * use, and that someone else doesn't have the callout device locked. + * (These are the only tests the standard serial driver makes for + * callout devices.) + */ + if ( tty->driver.subtype == SERIAL_TYPE_CALLOUT ) { + if ( pCh->flags & ASYNC_NORMAL_ACTIVE ) { + return -EBUSY; + } + if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) && + ( pCh->flags & ASYNC_SESSION_LOCKOUT ) && + ( pCh->session != current->session ) ) { + return -EBUSY; + } + if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) && + ( pCh->flags & ASYNC_PGRP_LOCKOUT ) && + ( pCh->pgrp != current->pgrp ) ) { + return -EBUSY; + } + pCh->flags |= ASYNC_CALLOUT_ACTIVE; + goto noblock; + } + /* + * 3. Handle a non-blocking open of a normal port. + */ + if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<flags & ASYNC_CALLOUT_ACTIVE ) { + return -EBUSY; + } + pCh->flags |= ASYNC_NORMAL_ACTIVE; + goto noblock; + } + /* + * 4. Now loop waiting for the port to be free and carrier present + * (if required). + */ + if ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) { + if ( pCh->NormalTermios.c_cflag & CLOCAL ) { + do_clocal = 1; + } + } else { + if ( tty->termios->c_cflag & CLOCAL ) { + do_clocal = 1; + } + } + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal); +#endif + + ++pCh->wopen; + for(;;) { + if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE)) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + serviceOutgoingFifo( pCh->pMyBord ); + } + if ( tty_hung_up_p(pFile) ) { + return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS; + } + if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) && + !(pCh->flags & ASYNC_CLOSING) && + (do_clocal || (pCh->dataSetIn & I2_DCD) )) { + rc = 0; + break; + } + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "ASYNC_CALLOUT_ACTIVE = %s\n", + (pCh->flags & ASYNC_CALLOUT_ACTIVE)?"True":"False"); + printk(KERN_DEBUG "ASYNC_CLOSING = %s\n", + (pCh->flags & ASYNC_CLOSING)?"True":"False"); + printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n"); +#endif +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, 3, 2, (pCh->flags & ASYNC_CALLOUT_ACTIVE), + (pCh->flags & ASYNC_CLOSING) ); +#endif + /* check for signal */ + if (signal_pending(current)) { + rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS); + break; + } + interruptible_sleep_on(&pCh->open_wait); + } + --pCh->wopen; //why count? +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, 4, 0 ); +#endif + if (rc != 0 ) { + return rc; + } + pCh->flags |= ASYNC_NORMAL_ACTIVE; + +noblock: + + /* first open - Assign termios structure to port */ + if ( tty->count == 1 ) { + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + if ( pCh->flags & ASYNC_SPLIT_TERMIOS ) { + if ( tty->driver.subtype == SERIAL_TYPE_NORMAL ) { + *tty->termios = pCh->NormalTermios; + } else { + *tty->termios = pCh->CalloutTermios; + } + } + /* Now we must send the termios settings to the loadware */ + set_params( pCh, NULL ); + } + + /* override previous and never reset ??? */ + pCh->session = current->session; + pCh->pgrp = current->pgrp; + + /* + * Now set any i2lib options. These may go away if the i2lib code ends + * up rolled into the mainline. + */ + pCh->channelOptions |= CO_NBLOCK_WRITE; + +#ifdef IP2DEBUG_OPEN + printk (KERN_DEBUG "IP2: open completed\n" ); +#endif + serviceOutgoingFifo( pCh->pMyBord ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, ITRC_RETURN, 0 ); +#endif + return 0; +} + +/******************************************************************************/ +/* Function: ip2_close() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_close( PTTY tty, struct file *pFile ) +{ + i2ChanStrPtr pCh = tty->driver_data; + + if ( !pCh ) { + return; + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, ITRC_ENTER, 0 ); +#endif + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "IP2:close ttyF%02X:\n",MINOR(tty->device)); +#endif + + if ( tty_hung_up_p ( pFile ) ) { + MOD_DEC_USE_COUNT; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, 2, 1, 2 ); +#endif + return; + } + if ( tty->count > 1 ) { /* not the last close */ + MOD_DEC_USE_COUNT; +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, 2, 1, 3 ); +#endif + return; + } + pCh->flags |= ASYNC_CLOSING; // last close actually + + /* + * Save the termios structure, since this port may have separate termios + * for callout and dialin. + */ + if (pCh->flags & ASYNC_NORMAL_ACTIVE) + pCh->NormalTermios = *tty->termios; + if (pCh->flags & ASYNC_CALLOUT_ACTIVE) + pCh->CalloutTermios = *tty->termios; + + tty->closing = 1; + + if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) { + /* + * Before we drop DTR, make sure the transmitter has completely drained. + * This uses an timeout, after which the close + * completes. + */ + ip2_wait_until_sent(tty, pCh->ClosingWaitTime ); + } + /* + * At this point we stop accepting input. Here we flush the channel + * input buffer which will allow the board to send up more data. Any + * additional input is tossed at interrupt/poll time. + */ + i2InputFlush( pCh ); + + /* disable DSS reporting */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); + if ( !tty || (tty->termios->c_cflag & HUPCL) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + + serviceOutgoingFifo ( pCh->pMyBord ); + + if ( tty->driver.flush_buffer ) + tty->driver.flush_buffer(tty); + if ( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + + pCh->pTTY = NULL; + + if (pCh->wopen) { + if (pCh->ClosingDelay) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; + schedule_timeout(pCh->ClosingDelay); + } + wake_up_interruptible(&pCh->open_wait); + } + + pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&pCh->close_wait); + +#ifdef IP2DEBUG_OPEN + DBG_CNT("ip2_close: after wakeups--"); +#endif + + MOD_DEC_USE_COUNT; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, ITRC_RETURN, 1, 1 ); +#endif + return; +} + +/******************************************************************************/ +/* Function: ip2_hangup() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_hangup ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_HANGUP, ITRC_ENTER, 0 ); +#endif + + ip2_flush_buffer(tty); + + /* disable DSS reporting */ + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP); + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + if ( !tty || (tty->termios->c_cflag & HUPCL) ) { + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + i2QueueCommands(PTYPE_INLINE, pCh, 1, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + serviceOutgoingFifo ( pCh->pMyBord ); + + wake_up_interruptible ( &pCh->delta_msr_wait ); + + pCh->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + pCh->pTTY = NULL; + wake_up_interruptible ( &pCh->open_wait ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_HANGUP, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Output Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_write() */ +/* Parameters: Pointer to tty structure */ +/* Flag denoting data is in user (1) or kernel (0) space */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Number of bytes actually written */ +/* */ +/* Description: (MANDATORY) */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_write( PTTY tty, int user, const unsigned char *pData, int count) +{ + i2ChanStrPtr pCh = tty->driver_data; + int bytesSent = 0; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 ); +#endif + + /* Flush out any buffered data left over from ip2_putchar() calls. */ + ip2_flush_chars( tty ); + + /* This is the actual move bit. Make sure it does what we need!!!!! */ + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + bytesSent = i2Output( pCh, pData, count, user ); + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent ); +#endif + return bytesSent > 0 ? bytesSent : 0; +} + +/******************************************************************************/ +/* Function: ip2_putchar() */ +/* Parameters: Pointer to tty structure */ +/* Character to write */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_putchar( PTTY tty, unsigned char ch ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, ITRC_ENTER, 1, ch ); +#endif + + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + pCh->Pbuf[pCh->Pbuf_stuff++] = ch; + if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) { + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + ip2_flush_chars( tty ); + } else + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, ITRC_RETURN, 1, ch ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_flush_chars() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static void +ip2_flush_chars( PTTY tty ) +{ + int strip; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + if ( pCh->Pbuf_stuff ) { +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, 10, 1, strip ); +#endif + // + // We may need to restart i2Output if it does not fullfill this request + // + strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff, 0 ); + if ( strip != pCh->Pbuf_stuff ) { + memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip ); + } + pCh->Pbuf_stuff -= strip; + } + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); +} + +/******************************************************************************/ +/* Function: ip2_write_room() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes that the driver can accept */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static int +ip2_write_room ( PTTY tty ) +{ + int bytesFree; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff; + READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, 11, 1, bytesFree ); +#endif + + return ((bytesFree > 0) ? bytesFree : 0); +} + +/******************************************************************************/ +/* Function: ip2_chars_in_buf() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes queued for transmission */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_chars_in_buf ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + int rc; + unsigned long flags; +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff ); +#endif +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n", + pCh->Obuf_char_count + pCh->Pbuf_stuff, + pCh->Obuf_char_count, pCh->Pbuf_stuff ); +#endif + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + rc = pCh->Obuf_char_count; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + rc += pCh->Pbuf_stuff; + READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + return rc; +} + +/******************************************************************************/ +/* Function: ip2_flush_buffer() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_flush_buffer( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_FLUSH, ITRC_ENTER, 0 ); +#endif +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: flush buffer\n" ); +#endif + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + pCh->Pbuf_stuff = 0; + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + i2FlushOutput( pCh ); + ip2_owake(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_FLUSH, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_wait_until_sent() */ +/* Parameters: Pointer to tty structure */ +/* Timeout for wait. */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function is used in place of the normal tty_wait_until_sent, which */ +/* only waits for the driver buffers to be empty (or rather, those buffers */ +/* reported by chars_in_buffer) which doesn't work for IP2 due to the */ +/* indeterminate number of bytes buffered on the board. */ +/******************************************************************************/ +static void +ip2_wait_until_sent ( PTTY tty, int timeout ) +{ + int i = jiffies; + i2ChanStrPtr pCh = tty->driver_data; + + tty_wait_until_sent(tty, timeout ); + if ( (i = timeout - (jiffies -i)) > 0) + i2DrainOutput( pCh, i ); +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Input Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_throttle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_throttle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: throttle\n" ); +#endif + /* + * Signal the poll/interrupt handlers not to forward incoming data to + * the line discipline. This will cause the buffers to fill up in the + * library and thus cause the library routines to send the flow control + * stuff. + */ + pCh->throttled = 1; +} + +/******************************************************************************/ +/* Function: ip2_unthrottle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_unthrottle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: unthrottle\n" ); +#endif + + /* Pass incoming data up to the line discipline again. */ + pCh->throttled = 0; + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + serviceOutgoingFifo( pCh->pMyBord ); + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) { + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "i2Input called from unthrottle\n" ); +#endif + i2Input( pCh ); + } else + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) +} + +static void +ip2_start ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: start tx\n" ); +#endif +} + +static void +ip2_stop ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: stop tx\n" ); +#endif +} + +/******************************************************************************/ +/* Device Ioctl Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_ioctl() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + int rc = 0; + + if ( pCh == NULL ) { + return -ENODEV; + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg ); +#endif + +#ifdef IP2DEBUG_IOCTL + printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg ); +#endif + + switch(cmd) { + case TIOCGSERIAL: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 2, 1, rc ); +#endif + rc = get_serial_info(pCh, (struct serial_struct *) arg); + if (rc) + return rc; + break; + + case TIOCSSERIAL: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 3, 1, rc ); +#endif + rc = set_serial_info(pCh, (struct serial_struct *) arg); + if (rc) + return rc; + break; + + case TCXONC: + rc = tty_check_change(tty); + if (rc) + return rc; + switch (arg) { + case TCOOFF: + //return -ENOIOCTLCMD; + break; + case TCOON: + //return -ENOIOCTLCMD; + break; + case TCIOFF: + if (STOP_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(STOP_CHAR(tty))); + } + break; + case TCION: + if (START_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(START_CHAR(tty))); + } + break; + default: + return -EINVAL; + } + return 0; + + case TCSBRK: /* SVID version: non-zero arg --> no break */ + rc = tty_check_change(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 4, 1, rc ); +#endif + if (!rc) { + ip2_wait_until_sent(tty,0); + if (!arg) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250)); + serviceOutgoingFifo( pCh->pMyBord ); + } + } + break; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + rc = tty_check_change(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 5, 1, rc ); +#endif + if (!rc) { + ip2_wait_until_sent(tty,0); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_SEND_BRK(arg ? arg*100 : 250)); + serviceOutgoingFifo ( pCh->pMyBord ); + } + break; + + case TIOCGSOFTCAR: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 6, 1, rc ); +#endif + rc=put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + if (rc) + return rc; + break; + + case TIOCSSOFTCAR: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 7, 1, rc ); +#endif + rc=get_user(arg,(unsigned long *) arg); + if (rc) + return rc; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) + | (arg ? CLOCAL : 0)); + + break; + + case TIOCMGET: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 8, 1, rc ); +#endif + rc = get_modem_info(pCh, (unsigned int *) arg); + if (rc) + return rc; + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 9, 0 ); +#endif + rc = set_modem_info(pCh, cmd, (unsigned int *) arg); + break; + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - mask + * passed in arg for lines of interest (use |'ed TIOCM_RNG/DSR/CD/CTS + * for masking). Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + cprev = pCh->icount; /* note the counters on entry */ + for(;;) { +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 10, 0 ); +#endif + interruptible_sleep_on(&pCh->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + cnow = pCh->icount; /* atomic copy */ + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + rc = -EIO; /* no change => rc */ + break; + } + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + rc = 0; + break; + } + cprev = cnow; + } + /* NOTREACHED */ + break; + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for RI where + * only 0->1 is counted. The controller is quite capable of counting + * both, but this done to preserve compatibility with the standard + * serial driver. + */ + case TIOCGICOUNT: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 11, 1, rc ); +#endif + cnow = pCh->icount; + p_cuser = (struct serial_icounter_struct *) arg; + put_user(cnow.cts, &p_cuser->cts); + put_user(cnow.dsr, &p_cuser->dsr); + put_user(cnow.rng, &p_cuser->rng); + put_user(cnow.dcd, &p_cuser->dcd); + break; + + /* + * The rest are not supported by this driver. By returning -ENOIOCTLCMD they + * will be passed to the line discipline for it to handle. + */ + case TIOCSERCONFIG: + case TIOCSERGWILD: + case TIOCSERGETLSR: + case TIOCSERSWILD: + case TIOCSERGSTRUCT: + case TIOCSERGETMULTI: + case TIOCSERSETMULTI: + + default: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 12, 0 ); +#endif + rc = -ENOIOCTLCMD; + break; + } +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, ITRC_RETURN, 0 ); +#endif + return rc; +} +/******************************************************************************/ +/* Function: get_modem_info() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to destination for data */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This returns the current settings of the dataset signal inputs to the user */ +/* program. */ +/******************************************************************************/ +static int +get_modem_info(i2ChanStrPtr pCh, unsigned int *value) +{ + unsigned short status; + unsigned int result; + int rc; + + status = pCh->dataSetIn; // snapshot settings + result = ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0) + | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0) + | ((status & I2_DCD) ? TIOCM_CAR : 0) + | ((status & I2_RI) ? TIOCM_RNG : 0) + | ((status & I2_DSR) ? TIOCM_DSR : 0) + | ((status & I2_CTS) ? TIOCM_CTS : 0); + rc=put_user(result,value); + return rc; +} + +/******************************************************************************/ +/* Function: set_modem_info() */ +/* Parameters: Pointer to channel structure */ +/* Specific ioctl command */ +/* Pointer to source for new settings */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This returns the current settings of the dataset signal inputs to the user */ +/* program. */ +/******************************************************************************/ +static int +set_modem_info(i2ChanStrPtr pCh, unsigned cmd, unsigned int *value) +{ + int rc; + unsigned int arg; + + rc=get_user(arg,value); + if (rc) + return rc; + switch(cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); + pCh->dataSetOut |= I2_RTS; + } + if (arg & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); + pCh->dataSetOut |= I2_DTR; + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); + pCh->dataSetOut &= ~I2_RTS; + } + if (arg & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); + pCh->dataSetOut &= ~I2_DTR; + } + break; + case TIOCMSET: + if ( (arg & TIOCM_RTS) && !(pCh->dataSetOut & I2_RTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); + pCh->dataSetOut |= I2_RTS; + } else if ( !(arg & TIOCM_RTS) && (pCh->dataSetOut & I2_RTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); + pCh->dataSetOut &= ~I2_RTS; + } + if ( (arg & TIOCM_DTR) && !(pCh->dataSetOut & I2_DTR) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); + pCh->dataSetOut |= I2_DTR; + } else if ( !(arg & TIOCM_DTR) && (pCh->dataSetOut & I2_DTR) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); + pCh->dataSetOut &= ~I2_DTR; + } + break; + default: + return -EINVAL; + } + serviceOutgoingFifo( pCh->pMyBord ); + return 0; +} + +/******************************************************************************/ +/* Function: GetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is to support the setserial command, and requires processing of the */ +/* standard Linux serial structure. */ +/******************************************************************************/ +static int +get_serial_info ( i2ChanStrPtr pCh, struct serial_struct *retinfo ) +{ + struct serial_struct tmp; + int rc=0; + + if ( !retinfo ) { + return -EFAULT; + } + + memset ( &tmp, 0, sizeof(tmp) ); + tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16]; + if (BID_HAS_654(tmp.type)) { + tmp.type = PORT_16650; + } else { + tmp.type = PORT_CIRRUS; + } + tmp.line = pCh->port_index; + tmp.port = pCh->pMyBord->i2eBase; + tmp.irq = ip2config.irq[pCh->port_index/64]; + tmp.flags = pCh->flags; + tmp.baud_base = pCh->BaudBase; + tmp.close_delay = pCh->ClosingDelay; + tmp.closing_wait = pCh->ClosingWaitTime; + tmp.custom_divisor = pCh->BaudDivisor; + if(copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + rc= -EFAULT; + return rc; +} + +/******************************************************************************/ +/* Function: SetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function provides support for setserial, which uses the TIOCSSERIAL */ +/* ioctl. Not all setserial parameters are relevant. If the user attempts to */ +/* change the IRQ, address or type of the port the ioctl fails. */ +/******************************************************************************/ +static int +set_serial_info( i2ChanStrPtr pCh, struct serial_struct *new_info ) +{ + struct serial_struct ns; + int old_flags, old_baud_divisor; + int rc = 0; + + if ( !new_info ) { + return -EFAULT; + } + rc=copy_from_user(&ns, new_info, sizeof (ns) ); + if (rc) { + return rc; + } + /* + * We don't allow setserial to change IRQ, board address, type or baud + * base. Also line nunber as such is meaningless but we use it for our + * array index so it is fixed also. + */ + if ( ns.irq != ip2config.irq + || (int) ns.port != ((int) pCh->pMyBord->i2eBase) + || ns.baud_base != pCh->BaudBase + || ns.line != pCh->port_index ) { + return -EINVAL; + } + + old_flags = pCh->flags; + old_baud_divisor = pCh->BaudDivisor; + + if ( !suser() ) { + if ( ( ns.close_delay != pCh->ClosingDelay ) || + ( (ns.flags & ~ASYNC_USR_MASK) != + (pCh->flags & ~ASYNC_USR_MASK) ) ) { + return -EPERM; + } + + pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) | + (ns.flags & ASYNC_USR_MASK); + pCh->BaudDivisor = ns.custom_divisor; + } else { + pCh->flags = (pCh->flags & ~ASYNC_FLAGS) | + (ns.flags & ASYNC_FLAGS); + pCh->BaudDivisor = ns.custom_divisor; + pCh->ClosingDelay = ns.close_delay * HZ/100; + pCh->ClosingWaitTime = ns.closing_wait * HZ/100; + } + + if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) ) + || (old_baud_divisor != pCh->BaudDivisor) ) { + // Invalidate speed and reset parameters + set_params( pCh, NULL ); + } + + return rc; +} + +/******************************************************************************/ +/* Function: ip2_set_termios() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_termios( PTTY tty, struct termios *old_termios ) +{ + i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data; + +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set termios %p\n", old_termios ); +#endif + + set_params( pCh, old_termios ); +} + +/******************************************************************************/ +/* Function: ip2_set_line_discipline() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: Does nothing */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_line_discipline ( PTTY tty ) +{ +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set line discipline\n" ); +#endif +#ifdef IP2DEBUG_TRACE + ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: SetLine Characteristics() */ +/* Parameters: Pointer to channel structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This routine is called to update the channel structure with the new line */ +/* characteristics, and send the appropriate commands to the board when they */ +/* change. */ +/******************************************************************************/ +static void +set_params( i2ChanStrPtr pCh, struct termios *o_tios ) +{ + tcflag_t cflag, iflag, lflag; + char stop_char, start_char; + struct termios dummy; + + lflag = pCh->pTTY->termios->c_lflag; + cflag = pCh->pTTY->termios->c_cflag; + iflag = pCh->pTTY->termios->c_iflag; + + if (o_tios == NULL) { + dummy.c_lflag = ~lflag; + dummy.c_cflag = ~cflag; + dummy.c_iflag = ~iflag; + o_tios = &dummy; + } + + { + switch ( cflag & CBAUD ) { + case B0: + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag); + goto service_it; + break; + case B38400: + /* + * This is the speed that is overloaded with all the other high + * speeds, depending upon the flag settings. + */ + if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) { + pCh->speed = CBR_57600; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) { + pCh->speed = CBR_115200; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) { + pCh->speed = CBR_C1; + } else { + pCh->speed = CBR_38400; + } + break; + case B50: pCh->speed = CBR_50; break; + case B75: pCh->speed = CBR_75; break; + case B110: pCh->speed = CBR_110; break; + case B134: pCh->speed = CBR_134; break; + case B150: pCh->speed = CBR_150; break; + case B200: pCh->speed = CBR_200; break; + case B300: pCh->speed = CBR_300; break; + case B600: pCh->speed = CBR_600; break; + case B1200: pCh->speed = CBR_1200; break; + case B1800: pCh->speed = CBR_1800; break; + case B2400: pCh->speed = CBR_2400; break; + case B4800: pCh->speed = CBR_4800; break; + case B9600: pCh->speed = CBR_9600; break; + case B19200: pCh->speed = CBR_19200; break; + case B57600: pCh->speed = CBR_57600; break; + case B115200: pCh->speed = CBR_115200; break; + case B153600: pCh->speed = CBR_153600; break; + case B230400: pCh->speed = CBR_230400; break; + case B307200: pCh->speed = CBR_307200; break; + case B460800: pCh->speed = CBR_460800; break; + case B921600: pCh->speed = CBR_921600; break; + default: pCh->speed = CBR_9600; break; + } + if ( pCh->speed == CBR_C1 ) { + // Process the custom speed parameters. + int bps = pCh->BaudBase / pCh->BaudDivisor; + if ( bps == 921600 ) { + pCh->speed = CBR_921600; + } else { + bps = bps/10; + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) ); + } + } + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed)); + + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + } + if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1)); + } + if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETPAR( + (cflag & PARENB ? (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP) + ) + ); + } + /* byte size and parity */ + if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag)) + { + int datasize; + switch ( cflag & CSIZE ) { + case CS5: datasize = CSZ_5; break; + case CS6: datasize = CSZ_6; break; + case CS7: datasize = CSZ_7; break; + case CS8: datasize = CSZ_8; break; + default: datasize = CSZ_5; break; /* as per serial.c */ + } + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) ); + } + /* Process CTS flow control flag setting */ + if ( (cflag & CRTSCTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB); + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + } + // + // Process XON/XOFF flow control flags settings + // + stop_char = STOP_CHAR(pCh->pTTY); + start_char = START_CHAR(pCh->pTTY); + + //////////// can't be \000 + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; + } + if (start_char == __DISABLED_CHAR ) + { + start_char = ~__DISABLED_CHAR; + } + ///////////////////////////////// + + if ( o_tios->c_cc[VSTART] != start_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char)); + } + if ( o_tios->c_cc[VSTOP] != stop_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char)); + } + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; //TEST123 + goto no_xoff; + } + if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF))) + { + if ( iflag & IXOFF ) { // Enable XOFF output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON)); + } else { // Disable XOFF output flow control +no_xoff: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE)); + } + } + if (start_char == __DISABLED_CHAR ) + { + goto no_xon; + } + if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY))) + { + if ( iflag & IXON ) { + if ( iflag & IXANY ) { // Enable XON/XANY output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY)); + } else { // Enable XON output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON)); + } + } else { // Disable XON output flow control +no_xon: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE)); + } + } + if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0))); + } + if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB)); + } + + if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) + ^ ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) ) + { + char brkrpt = 0; + char parrpt = 0; + + if ( iflag & IGNBRK ) { /* Ignore breaks altogether */ + /* Ignore breaks altogether */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP); + } else { + if ( iflag & BRKINT ) { + if ( iflag & PARMRK ) { + brkrpt = 0x0a; // exception an inline triple + } else { + brkrpt = 0x1a; // exception and NULL + } + brkrpt |= 0x04; // flush input + } else { + if ( iflag & PARMRK ) { + brkrpt = 0x0b; //POSIX triple \0377 \0 \0 + } else { + brkrpt = 0x01; // Null only + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt)); + } + + if (iflag & IGNPAR) { + parrpt = 0x20; + /* would be 2 for not cirrus bug */ + /* would be 0x20 cept for cirrus bug */ + } else { + if ( iflag & PARMRK ) { + /* + * Replace error characters with 3-byte sequence (\0377,\0,char) + */ + parrpt = 0x04 ; + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0)); + } else { + parrpt = 0x03; + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt)); + } + if (cflag & CLOCAL) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); + pCh->flags &= ~ASYNC_CHECK_CD; + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); + pCh->flags |= ASYNC_CHECK_CD; + } + +#ifdef XXX +do_flags_thing: // This is a test, we don't do the flags thing + + if ( (cflag & CRTSCTS) ) { + cflag |= 014000000000; + } + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, + CMD_UNIX_FLAGS(iflag,cflag,lflag)); +#endif + +service_it: + i2DrainOutput( pCh, 100 ); +} + +/******************************************************************************/ +/* IPL Device Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_ipl_read() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to read */ +/* Returns: Success or failure */ +/* */ +/* Description: Ugly */ +/* */ +/* */ +/******************************************************************************/ + +static +ssize_t +ip2_ipl_read(struct file *pFile, char *pData, size_t count, loff_t *off ) +{ + unsigned int minor = MINOR( pFile->f_dentry->d_inode->i_rdev ); + int rc = 0; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count ); +#endif + + switch( minor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + rc = -EINVAL; + break; + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + rc = DumpTraceBuffer ( pData, count ); + break; + case 4: // Trace device + rc = DumpFifoBuffer ( pData, count ); + break; + default: + rc = -ENODEV; + break; + } + return rc; +} + +static int +DumpFifoBuffer ( char *pData, int count ) +{ +#ifdef DEBUG_FIFO + int rc=0; + if(copy_to_user(pData, DBGBuf, count)) + rc=-EFAULT; + + printk(KERN_DEBUG "Last index %d\n", I ); + + return count; +#endif /* DEBUG_FIFO */ + return 0; +} + +static int +DumpTraceBuffer ( char *pData, int count ) +{ +#ifdef IP2DEBUG_TRACE + int rc; + int dumpcount; + int chunk; + int *pIndex = (int*)pData; + + if ( count < (sizeof(int) * 6) ) { + return -EIO; + } + put_user(tracewrap, pIndex ); + put_user(TRACEMAX, ++pIndex ); + put_user(tracestrip, ++pIndex ); + put_user(tracestuff, ++pIndex ); + pData += sizeof(int) * 6; + count -= sizeof(int) * 6; + + dumpcount = tracestuff - tracestrip; + if ( dumpcount < 0 ) { + dumpcount += TRACEMAX; + } + if ( dumpcount > count ) { + dumpcount = count; + } + chunk = TRACEMAX - tracestrip; + if ( dumpcount > chunk ) { + rc=copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) )?-EFAULT:0; + pData += chunk * sizeof(tracebuf[0]); + tracestrip = 0; + chunk = dumpcount - chunk; + } else { + chunk = dumpcount; + } + rc=copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) )?-EFAULT:0 + tracestrip += chunk; + tracewrap = 0; + + put_user(tracestrip, ++pIndex ); + put_user(tracestuff, ++pIndex ); + + return dumpcount; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* Function: ip2_ipl_write() */ +/* Parameters: */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static ssize_t +ip2_ipl_write(struct file *pFile, const char *pData, size_t count, loff_t *off) +{ +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count ); +#endif + return 0; +} + +/******************************************************************************/ +/* Function: ip2_ipl_ioctl() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg ) +{ + unsigned int iplminor = MINOR(pInode->i_rdev); + int rc = 0; + ULONG *pIndex = (ULONG*)arg; + i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4]; + i2ChanStrPtr pCh; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg ); +#endif + + switch ( iplminor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + case 5: + case 9: + case 13: + switch ( cmd ) { + case 64: /* Driver - ip2stat */ + put_user(ref_count, pIndex++ ); + put_user(irq_counter, pIndex++ ); + put_user(bh_counter, pIndex++ ); + break; + + case 65: /* Board - ip2stat */ + if ( pB ) { + if(copy_to_user((char*)arg, (char*)pB, sizeof(i2eBordStr) )) + rc=-EFAULT; + put_user(INB(pB->i2eStatus), + (ULONG*)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) ); + } else { + rc = -ENODEV; + } + break; + + default: + pCh = DevTable[cmd]; + if ( pCh ) + { + if(copy_to_user((char*)arg, (char*)pCh, sizeof(i2ChanStr) )) + rc = -EFAULT; + } else { + rc = cmd < 64 ? -ENODEV : -EINVAL; + } + } + break; + + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + if ( cmd == 1 ) { + put_user(iiSendPendingMail, pIndex++ ); + put_user(i2InitChannels, pIndex++ ); + put_user(i2QueueNeeds, pIndex++ ); + put_user(i2QueueCommands, pIndex++ ); + put_user(i2GetStatus, pIndex++ ); + put_user(i2Input, pIndex++ ); + put_user(i2InputFlush, pIndex++ ); + put_user(i2Output, pIndex++ ); + put_user(i2FlushOutput, pIndex++ ); + put_user(i2DrainWakeup, pIndex++ ); + put_user(i2DrainOutput, pIndex++ ); + put_user(i2OutputFree, pIndex++ ); + put_user(i2StripFifo, pIndex++ ); + put_user(i2StuffFifoBypass, pIndex++ ); + put_user(i2StuffFifoFlow, pIndex++ ); + put_user(i2StuffFifoInline, pIndex++ ); + put_user(i2ServiceBoard, pIndex++ ); + put_user(serviceOutgoingFifo, pIndex++ ); + // put_user(ip2_init, pIndex++ ); + put_user(ip2_init_board, pIndex++ ); + put_user(find_eisa_board, pIndex++ ); + put_user(set_irq, pIndex++ ); + put_user(ip2_interrupt, pIndex++ ); + put_user(ip2_poll, pIndex++ ); + put_user(service_all_boards, pIndex++ ); + put_user(do_input, pIndex++ ); + put_user(do_status, pIndex++ ); +#ifndef IP2DEBUG_OPEN + put_user(0, pIndex++ ); +#else + put_user(open_sanity_check, pIndex++ ); +#endif + put_user(ip2_open, pIndex++ ); + put_user(ip2_close, pIndex++ ); + put_user(ip2_hangup, pIndex++ ); + put_user(ip2_write, pIndex++ ); + put_user(ip2_putchar, pIndex++ ); + put_user(ip2_flush_chars, pIndex++ ); + put_user(ip2_write_room, pIndex++ ); + put_user(ip2_chars_in_buf, pIndex++ ); + put_user(ip2_flush_buffer, pIndex++ ); + + //put_user(ip2_wait_until_sent, pIndex++ ); + put_user(0, pIndex++ ); + + put_user(ip2_throttle, pIndex++ ); + put_user(ip2_unthrottle, pIndex++ ); + put_user(ip2_ioctl, pIndex++ ); + put_user(get_modem_info, pIndex++ ); + put_user(set_modem_info, pIndex++ ); + put_user(get_serial_info, pIndex++ ); + put_user(set_serial_info, pIndex++ ); + put_user(ip2_set_termios, pIndex++ ); + put_user(ip2_set_line_discipline, pIndex++ ); + put_user(set_params, pIndex++ ); + } else { + rc = -EINVAL; + } + + break; + + default: + rc = -ENODEV; + break; + } + return rc; +} + +/******************************************************************************/ +/* Function: ip2_ipl_open() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ipl_open( struct inode *pInode, struct file *pFile ) +{ + unsigned int iplminor = MINOR(pInode->i_rdev); + i2eBordStrPtr pB; + i2ChanStrPtr pCh; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: open\n" ); +#endif + + //MOD_INC_USE_COUNT; // Needs close entry with decrement. + + switch(iplminor) { + // These are the IPL devices + case 0: + case 4: + case 8: + case 12: + break; + + // These are the status devices + case 1: + case 5: + case 9: + case 13: + break; + + // These are the debug devices + case 2: + case 6: + case 10: + case 14: + pB = i2BoardPtrTable[iplminor / 4]; + pCh = (i2ChanStrPtr) pB->i2eChannelPtr; + break; + + // This is the trace device + case 3: + break; + } + return 0; +} +/******************************************************************************/ +/* Function: ip2_read_procmem */ +/* Parameters: */ +/* */ +/* Returns: Length of output */ +/* */ +/* Description: */ +/* Supplies some driver operating parameters */ +/* Not real useful unless your debugging the fifo */ +/* */ +/******************************************************************************/ + +#define LIMIT (PAGE_SIZE - 120) + +int +ip2_read_procmem(char *buf, char **start, off_t offset, int len, int unused) +{ + i2eBordStrPtr pB; + i2ChanStrPtr pCh; + PTTY tty; + int i; + + len = 0; + +#define FMTLINE "%3d: 0x%08x 0x%08x 0%011o 0%011o\n" +#define FMTLIN2 " 0x%04x 0x%04x tx flow 0x%x\n" +#define FMTLIN3 " 0x%04x 0x%04x rc flow\n" + + len += sprintf(buf+len,"\n"); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + len += sprintf(buf+len,"board %d:\n",i); + len += sprintf(buf+len,"\tFifo rem: %d mty: %x outM %x\n", + pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting); + } + } + + len += sprintf(buf+len,"#: tty flags, port flags, cflags, iflags\n"); + for (i=0; i < IP2_MAX_PORTS; i++) { + if (len > LIMIT) + break; + pCh = DevTable[i]; + if (pCh) { + tty = pCh->pTTY; + if (tty && tty->count) { + len += sprintf(buf+len,FMTLINE,i,(int)tty->flags,pCh->flags, + tty->termios->c_cflag,tty->termios->c_iflag); + + len += sprintf(buf+len,FMTLIN2, + pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds); + len += sprintf(buf+len,FMTLIN3,pCh->infl.asof,pCh->infl.room); + } + } + } + return len; +} + +/* + * This is the handler for /proc/tty/driver/ip2 + * + * This stretch of code has been largely plagerized from at least three + * different sources including ip2mkdev.c and a couple of other drivers. + * The bugs are all mine. :-) =mhw= + */ +int ip2_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int i, j, box; + int len = 0; + int boxes = 0; + int ports = 0; + int tports = 0; + off_t begin = 0; + i2eBordStrPtr pB; + + len += sprintf(page, "ip2info: 1.0 driver: %s\n", pcVersion ); + len += sprintf(page+len, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n", + IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR, + IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + /* This need to be reset for a board by board count... */ + boxes = 0; + pB = i2BoardPtrTable[i]; + if( pB ) { + switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) + { + case POR_ID_FIIEX: + len += sprintf( page+len, "Board %d: EX ports=", i ); + for( box = 0; box < ABS_MAX_BOXES; ++box ) + { + ports = 0; + + if( pB->i2eChannelMap[box] != 0 ) ++boxes; + for( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if( pB->i2eChannelMap[box] & 1<< j ) { + ++ports; + } + } + len += sprintf( page+len, "%d,", ports ); + tports += ports; + } + + --len; /* Backup over that last comma */ + + len += sprintf( page+len, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8 ); + break; + + case POR_ID_II_4: + len += sprintf(page+len, "Board %d: ISA-4 ports=4 boxes=1", i ); + tports = ports = 4; + break; + + case POR_ID_II_8: + len += sprintf(page+len, "Board %d: ISA-8-std ports=8 boxes=1", i ); + tports = ports = 8; + break; + + case POR_ID_II_8R: + len += sprintf(page+len, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i ); + tports = ports = 8; + break; + + default: + len += sprintf(page+len, "Board %d: unknown", i ); + /* Don't try and probe for minor numbers */ + tports = ports = 0; + } + + } else { + /* Don't try and probe for minor numbers */ + len += sprintf(page+len, "Board %d: vacant", i ); + tports = ports = 0; + } + + if( tports ) { + len += sprintf(page+len, " minors=" ); + + for ( box = 0; box < ABS_MAX_BOXES; ++box ) + { + for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if ( pB->i2eChannelMap[box] & (1 << j) ) + { + len += sprintf (page+len,"%d,", + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES)); + } + } + } + + page[ len - 1 ] = '\n'; /* Overwrite that last comma */ + } else { + len += sprintf (page+len,"\n" ); + } + + if (len+begin > off+count) + break; + if (len+begin < off) { + begin += len; + len = 0; + } + } + + if (i >= IP2_MAX_BOARDS) + *eof = 1; + if (off >= len+begin) + return 0; + + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); + } + +/******************************************************************************/ +/* Function: ip2trace() */ +/* Parameters: Value to add to trace buffer */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +void +ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...) +{ +#ifdef IP2DEBUG_TRACE + long flags; + unsigned long *pCode = &codes; + union ip2breadcrumb bc; + i2ChanStrPtr pCh; + + + tracebuf[tracestuff++] = jiffies; + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + bc.hdr.port = 0xff & pn; + bc.hdr.cat = cat; + bc.hdr.codes = (unsigned char)( codes & 0xff ); + bc.hdr.label = label; + tracebuf[tracestuff++] = bc.value; + + for (;;) { + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + if ( !codes-- ) + break; + + tracebuf[tracestuff++] = *++pCode; + } +#endif +} + + diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index ba22ff90b708..d31b73606518 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1808,6 +1808,7 @@ static void unregister_drivers(void) static int register_isr(void) { int count, done=0, card; + int flag; unsigned char request; for (count=0; count < BOARD_COUNT; count++ ) { if (isi_card[count].base) { @@ -1829,8 +1830,12 @@ static int register_isr(void) isi_card[count].base = 0; break; } + flag=0; + if(isi_card[count].isa == NO) + flag |= SA_SHIRQ; + if (request == YES) { - if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT, ISICOM_NAME, NULL)) { + if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT|flag, ISICOM_NAME, NULL)) { printk(KERN_WARNING "ISICOM: Could not install handler at Irq %d. Card%d will be disabled.\n", isi_card[count].irq, count+1); release_region(isi_card[count].base,16); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 9cee14e44bda..4976518a4356 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -52,14 +52,8 @@ extern void mda_console_init(void); #if defined(CONFIG_PPC) || defined(CONFIG_MAC) extern void adbdev_init(void); #endif -#ifdef CONFIG_USB_UHCI -int uhci_init(void); -#endif -#ifdef CONFIG_USB_OHCI -int ohci_init(void); -#endif -#ifdef CONFIG_USB_OHCI_HCD -int ohci_hcd_init(void); +#ifdef CONFIG_USB +extern void usb_init(void); #endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, @@ -618,15 +612,7 @@ __initfunc(int chr_dev_init(void)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); rand_initialize(); #ifdef CONFIG_USB -#ifdef CONFIG_USB_UHCI - uhci_init(); -#endif -#ifdef CONFIG_USB_OHCI - ohci_init(); -#endif -#ifdef CONFIG_USB_OHCI_HCD - ohci_hcd_init(); -#endif + usb_init(); #endif #if defined (CONFIG_FB) fbmem_init(); diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 11e7eadcf883..9eda26551fb7 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -479,7 +479,7 @@ static void pc_set_checksum( void ) #ifdef CONFIG_PROC_FS static char *floppy_types[] = { - "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M" + "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M", "3.5'' 2.88M" }; static char *gfx_types[] = { @@ -521,14 +521,14 @@ static int pc_proc_infos( unsigned char *nvram, char *buffer, int *len, PRINT_PROC( "HD 0 type : " ); type = nvram[4] >> 4; if (type) - PRINT_PROC( " %02x\n", type == 0x0f ? nvram[11] : type ); + PRINT_PROC( "%02x\n", type == 0x0f ? nvram[11] : type ); else PRINT_PROC( "none\n" ); PRINT_PROC( "HD 1 type : " ); type = nvram[4] & 0x0f; if (type) - PRINT_PROC( " %02x\n", type == 0x0f ? nvram[12] : type ); + PRINT_PROC( "%02x\n", type == 0x0f ? nvram[12] : type ); else PRINT_PROC( "none\n" ); diff --git a/drivers/char/pcwd.c b/drivers/char/pcwd.c index ded35916ced6..8c31de05faee 100644 --- a/drivers/char/pcwd.c +++ b/drivers/char/pcwd.c @@ -243,7 +243,10 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, return i ? -EFAULT : 0; case WDIOC_GETSTATUS: + if (revision == PCWD_REVISION_A) cdat = inb(current_readport); + else + cdat = inb(current_readport + 1 ); rv = 0; if (revision == PCWD_REVISION_A) diff --git a/drivers/char/saa7111.c b/drivers/char/saa7111.c new file mode 100644 index 000000000000..1c14027b3d5e --- /dev/null +++ b/drivers/char/saa7111.c @@ -0,0 +1,421 @@ +/* + saa7111 - Philips SAA7111A video decoder driver version 0.0.3 + + Copyright (C) 1998 Dave Perks + + 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define DEBUG(x) /* Debug driver */ + +/* ----------------------------------------------------------------------- */ + +struct saa7111 { + struct i2c_bus *bus; + int addr; + unsigned char reg[32]; + + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_SAA7111 0x48 + +#define I2C_DELAY 10 + +/* ----------------------------------------------------------------------- */ + +static int saa7111_write(struct saa7111 *dev, unsigned char subaddr, unsigned char data) +{ + int ack; + unsigned long flags; + + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + i2c_sendbyte(dev->bus, subaddr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, data, I2C_DELAY); + dev->reg[subaddr] = data; + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + return ack; +} + +static int saa7111_write_block(struct saa7111 *dev, unsigned const char *data, unsigned int len) +{ + int ack; + unsigned subaddr; + unsigned long flags; + + while (len > 1) { + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (subaddr = *data++), I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + while (len > 1 && *data == ++subaddr) { + data++; + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + } + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + } + return ack; +} + +static int saa7111_read(struct saa7111 *dev, unsigned char subaddr) +{ + int data; + unsigned long flags; + + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + i2c_sendbyte(dev->bus, subaddr, I2C_DELAY); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr | 1, I2C_DELAY); + data = i2c_readbyte(dev->bus, 1); + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + return data; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7111_attach(struct i2c_device *device) +{ + int i; + struct saa7111 *decoder; + + static const unsigned char init[] = + { + 0x00, 0x00, /* 00 - ID byte */ + 0x01, 0x00, /* 01 - reserved */ + + /*front end */ + 0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */ + 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ + 0x04, 0x00, /* 04 - GAI1=256 */ + 0x05, 0x00, /* 05 - GAI2=256 */ + + /* decoder */ + 0x06, 0xf6, /* 06 - HSB at 13(50Hz) / 17(60Hz) pixels after end of last line */ + 0x07, 0xdd, /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */ + 0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */ + 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */ + 0x0a, 0x80, /* 0a - BRIG=128 */ + 0x0b, 0x47, /* 0b - CONT=1.109 */ + 0x0c, 0x40, /* 0c - SATN=1.0 */ + 0x0d, 0x00, /* 0d - HUE=0 */ + 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ + 0x0f, 0x00, /* 0f - reserved */ + 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ + 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ + 0x12, 0x00, /* 12 - output control 2 */ + 0x13, 0x00, /* 13 - output control 3 */ + 0x14, 0x00, /* 14 - reserved */ + 0x15, 0x00, /* 15 - VBI */ + 0x16, 0x00, /* 16 - VBI */ + 0x17, 0x00, /* 17 - VBI */ + }; + + device->data = decoder = kmalloc(sizeof(struct saa7111), GFP_KERNEL); + if (decoder == NULL) { + return -ENOMEM; + } + MOD_INC_USE_COUNT; + + memset(decoder, 0, sizeof(struct saa7111)); + strcpy(device->name, "saa7111"); + decoder->bus = device->bus; + decoder->addr = device->addr; + decoder->norm = VIDEO_MODE_NTSC; + decoder->input = 0; + decoder->enable = 1; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + + i = saa7111_write_block(decoder, init, sizeof(init)); + if (i < 0) { + printk(KERN_ERR "%s_attach: init status %d\n", device->name, i); + } else { + printk(KERN_INFO "%s_attach: chip version %x\n", device->name, saa7111_read(decoder, 0x00)); + } + return 0; +} + + +static int saa7111_detach(struct i2c_device *device) +{ + kfree(device->data); + MOD_DEC_USE_COUNT; + return 0; +} + +static int saa7111_command(struct i2c_device *device, unsigned int cmd, void *arg) +{ + struct saa7111 *decoder = device->data; + + switch (cmd) { + +#if defined(DECODER_DUMP) + case DECODER_DUMP: + { + int i; + + for (i = 0; i < 32; i += 16) { + int j; + + printk("KERN_DEBUG %s: %03x", device->name, i); + for (j = 0; j < 16; ++j) { + printk(" %02x", saa7111_read(decoder, i + j)); + } + printk("\n"); + } + } + break; +#endif /* defined(DECODER_DUMP) */ + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + + cap->flags + = VIDEO_DECODER_PAL + | VIDEO_DECODER_NTSC + | VIDEO_DECODER_AUTO + | VIDEO_DECODER_CCIR; + cap->inputs = 8; + cap->outputs = 1; + } + break; + + case DECODER_GET_STATUS: + { + int *iarg = arg; + int status; + int res; + + status = saa7111_read(decoder, 0x1f); + res = 0; + if ((status & (1 << 6)) == 0) { + res |= DECODER_STATUS_GOOD; + } + switch (decoder->norm) { + case VIDEO_MODE_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case VIDEO_MODE_PAL: + res |= DECODER_STATUS_PAL; + break; + default: + case VIDEO_MODE_AUTO: + if ((status & (1 << 5)) != 0) { + res |= DECODER_STATUS_NTSC; + } else { + res |= DECODER_STATUS_PAL; + } + break; + } + if ((status & (1 << 0)) != 0) { + res |= DECODER_STATUS_COLOR; + } + *iarg = res; + } + break; + + case DECODER_SET_NORM: + { + int *iarg = arg; + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0x3f) | 0x40); + break; + + case VIDEO_MODE_PAL: + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0x3f) | 0x00); + break; + + case VIDEO_MODE_AUTO: + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0x3f) | 0x80); + break; + + default: + return -EINVAL; + + } + decoder->norm = *iarg; + } + break; + + case DECODER_SET_INPUT: + { + int *iarg = arg; + + if (*iarg < 0 || *iarg > 7) { + return -EINVAL; + } + if (decoder->input != *iarg) { + decoder->input = *iarg; + /* select mode */ + saa7111_write(decoder, 0x02, (decoder->reg[0x02] & 0xf8) | decoder->input); + /* bypass chrominance trap for modes 4..7 */ + saa7111_write(decoder, 0x09, (decoder->reg[0x09] & 0x7f) | ((decoder->input > 3) ? 0x80 : 0)); + } + } + break; + + case DECODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case DECODER_ENABLE_OUTPUT: + { + int *iarg = arg; + int enable = (*iarg != 0); + + if (decoder->enable != enable) { + decoder->enable = enable; + +// RJ: If output should be disabled (for playing videos), we also need a open PLL. + // The input is set to 0 (where no input source is connected), although this + // is not necessary. + // + // If output should be enabled, we have to reverse the above. + + if (decoder->enable) { + saa7111_write(decoder, 0x02, (decoder->reg[0x02] & 0xf8) | decoder->input); + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0xfb)); + saa7111_write(decoder, 0x11, (decoder->reg[0x11] & 0xf3) | 0x0c); + } else { + saa7111_write(decoder, 0x02, (decoder->reg[0x02] & 0xf8)); + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0xfb) | 0x04); + saa7111_write(decoder, 0x11, (decoder->reg[0x11] & 0xf3)); + } + } + } + break; + + case DECODER_SET_PICTURE: + { + struct video_picture *pic = arg; + + if (decoder->bright != pic->brightness) { + /* We want 0 to 255 we get 0-65535 */ + decoder->bright = pic->brightness; + saa7111_write(decoder, 0x0a, decoder->bright >> 8); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 127 we get 0-65535 */ + decoder->contrast = pic->contrast; + saa7111_write(decoder, 0x0b, decoder->contrast >> 9); + } + if (decoder->sat != pic->colour) { + /* We want 0 to 127 we get 0-65535 */ + decoder->sat = pic->colour; + saa7111_write(decoder, 0x0c, decoder->sat >> 9); + } + if (decoder->hue != pic->hue) { + /* We want -128 to 127 we get 0-65535 */ + decoder->hue = pic->hue; + saa7111_write(decoder, 0x0d, (decoder->hue - 32768) >> 8); + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_saa7111 = +{ + "saa7111", /* name */ + I2C_DRIVERID_VIDEODECODER, /* ID */ + I2C_SAA7111, I2C_SAA7111 + 1, + + saa7111_attach, + saa7111_detach, + saa7111_command +}; + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +int init_module(void) +#else +int saa7111_init(void) +#endif +{ + return i2c_register_driver(&i2c_driver_saa7111); +} + + + +#ifdef MODULE + +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_saa7111); +} + +#endif diff --git a/drivers/char/saa7185.c b/drivers/char/saa7185.c new file mode 100644 index 000000000000..c42b29c37ed8 --- /dev/null +++ b/drivers/char/saa7185.c @@ -0,0 +1,379 @@ +/* + saa7185 - Philips SAA7185B video encoder driver version 0.0.3 + + Copyright (C) 1998 Dave Perks + + 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define DEBUG(x) x /* Debug driver */ + +/* ----------------------------------------------------------------------- */ + +struct saa7185 { + struct i2c_bus *bus; + int addr; + unsigned char reg[128]; + + int norm; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_SAA7185 0x88 + +#define I2C_DELAY 10 + +/* ----------------------------------------------------------------------- */ + +static int saa7185_write(struct saa7185 *dev, unsigned char subaddr, unsigned char data) +{ + int ack; + unsigned long flags; + + LOCK_I2C_BUS(dev->bus); + + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + i2c_sendbyte(dev->bus, subaddr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, data, I2C_DELAY); + dev->reg[subaddr] = data; + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + return ack; +} + +static int saa7185_write_block(struct saa7185 *dev, unsigned const char *data, unsigned int len) +{ + int ack; + unsigned subaddr; + unsigned long flags; + + while (len > 1) { + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (subaddr = *data++), I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + while (len > 1 && *data == ++subaddr) { + data++; + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + } + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + } + return ack; +} + +/* ----------------------------------------------------------------------- */ + +static const unsigned char init_common[] = +{ + 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, YUV2C=1, MY2C=1, MUV2C=1 */ + + 0x42, 0x6b, /* OVLY0=107 */ + 0x43, 0x00, /* OVLU0=0 white */ + 0x44, 0x00, /* OVLV0=0 */ + 0x45, 0x22, /* OVLY1=34 */ + 0x46, 0xac, /* OVLU1=172 yellow */ + 0x47, 0x0e, /* OVLV1=14 */ + 0x48, 0x03, /* OVLY2=3 */ + 0x49, 0x1d, /* OVLU2=29 cyan */ + 0x4a, 0xac, /* OVLV2=172 */ + 0x4b, 0xf0, /* OVLY3=240 */ + 0x4c, 0xc8, /* OVLU3=200 green */ + 0x4d, 0xb9, /* OVLV3=185 */ + 0x4e, 0xd4, /* OVLY4=212 */ + 0x4f, 0x38, /* OVLU4=56 magenta */ + 0x50, 0x47, /* OVLV4=71 */ + 0x51, 0xc1, /* OVLY5=193 */ + 0x52, 0xe3, /* OVLU5=227 red */ + 0x53, 0x54, /* OVLV5=84 */ + 0x54, 0xa3, /* OVLY6=163 */ + 0x55, 0x54, /* OVLU6=84 blue */ + 0x56, 0xf2, /* OVLV6=242 */ + 0x57, 0x90, /* OVLY7=144 */ + 0x58, 0x00, /* OVLU7=0 black */ + 0x59, 0x00, /* OVLV7=0 */ + + 0x5a, 0x00, /* CHPS=0 */ + 0x5b, 0x76, /* GAINU=118 */ + 0x5c, 0xa5, /* GAINV=165 */ + 0x5d, 0x3c, /* BLCKL=60 */ + 0x5e, 0x3a, /* BLNNL=58 */ + 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ + 0x60, 0x00, /* NULL */ + +/* 0x61 - 0x66 set according to norm */ + + 0x67, 0x00, /* 0 : caption 1st byte odd field */ + 0x68, 0x00, /* 0 : caption 2nd byte odd field */ + 0x69, 0x00, /* 0 : caption 1st byte even field */ + 0x6a, 0x00, /* 0 : caption 2nd byte even field */ + + 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ + 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, CBLF=0, ORCV2=0, PRCV2=0 */ + 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ + + 0x6e, 0x0e, /* HTRIG=0x00e, approx. centered, at least for PAL */ + 0x6f, 0x00, /* HTRIG upper bits */ + 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ + +/* The following should not be needed */ + + 0x71, 0x15, /* BMRQ=0x115 */ + 0x72, 0x90, /* EMRQ=0x690 */ + 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ + 0x74, 0x00, /* NULL */ + 0x75, 0x00, /* NULL */ + 0x76, 0x00, /* NULL */ + 0x77, 0x15, /* BRCV=0x115 */ + 0x78, 0x90, /* ERCV=0x690 */ + 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ + +/* Field length controls */ + + 0x7a, 0x70, /* FLC=0 */ + +/* The following should not be needed if SBLN = 1 */ + + 0x7b, 0x16, /* FAL=22 */ + 0x7c, 0x35, /* LAL=244 */ + 0x7d, 0x20, /* LAL=244, FAL=22 */ +}; + +static const unsigned char init_pal[] = +{ + 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ + 0x63, 0xcb, /* FSC0 */ + 0x64, 0x8a, /* FSC1 */ + 0x65, 0x09, /* FSC2 */ + 0x66, 0x2a, /* FSC3 */ +}; + +static const unsigned char init_ntsc[] = +{ + 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ + 0x63, 0x1f, /* FSC0 */ + 0x64, 0x7c, /* FSC1 */ + 0x65, 0xf0, /* FSC2 */ + 0x66, 0x21, /* FSC3 */ +}; + +static int saa7185_attach(struct i2c_device *device) +{ + int i; + struct saa7185 *encoder; + + device->data = encoder = kmalloc(sizeof(struct saa7185), GFP_KERNEL); + if (encoder == NULL) { + return -ENOMEM; + } + MOD_INC_USE_COUNT; + + memset(encoder, 0, sizeof(struct saa7185)); + strcpy(device->name, "saa7185"); + encoder->bus = device->bus; + encoder->addr = device->addr; + encoder->norm = VIDEO_MODE_NTSC; + encoder->enable = 1; + + i = saa7185_write_block(encoder, init_common, sizeof(init_common)); + if (i >= 0) { + i = saa7185_write_block(encoder, init_ntsc, sizeof(init_ntsc)); + } + if (i < 0) { + printk(KERN_ERR "%s_attach: init error %d\n", device->name, i); + } + return 0; +} + + +static int saa7185_detach(struct i2c_device *device) +{ + kfree(device->data); + MOD_DEC_USE_COUNT; + return 0; +} + +static int saa7185_command(struct i2c_device *device, unsigned int cmd, void *arg) +{ + struct saa7185 *encoder = device->data; + + switch (cmd) { + + case ENCODER_GET_CAPABILITIES: + { + struct video_encoder_capability *cap = arg; + + cap->flags + = VIDEO_ENCODER_PAL + | VIDEO_ENCODER_NTSC + | VIDEO_ENCODER_SECAM + | VIDEO_ENCODER_CCIR; + cap->inputs = 1; + cap->outputs = 1; + } + break; + + case ENCODER_SET_NORM: + { + int *iarg = arg; + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + saa7185_write_block(encoder, init_ntsc, sizeof(init_ntsc)); + break; + + case VIDEO_MODE_PAL: + saa7185_write_block(encoder, init_pal, sizeof(init_pal)); + break; + + case VIDEO_MODE_SECAM: + default: + return -EINVAL; + + } + encoder->norm = *iarg; + } + break; + + case ENCODER_SET_INPUT: + { + int *iarg = arg; + +#if 0 + /* not much choice of inputs */ + if (*iarg != 0) { + return -EINVAL; + } +#else + /* RJ: *iarg = 0: input is from SA7111 + *iarg = 1: input is from ZR36060 */ + + switch (*iarg) { + + case 0: + /* Switch RTCE to 1 */ + saa7185_write(encoder, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + break; + + case 1: + /* Switch RTCE to 0 */ + saa7185_write(encoder, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); + break; + + default: + return -EINVAL; + + } +#endif + } + break; + + case ENCODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case ENCODER_ENABLE_OUTPUT: + { + int *iarg = arg; + + encoder->enable = !!*iarg; + saa7185_write(encoder, 0x61, (encoder->reg[0x61] & 0xbf) | (encoder->enable ? 0x00 : 0x40)); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_saa7185 = +{ + "saa7185", /* name */ + I2C_DRIVERID_VIDEOENCODER, /* ID */ + I2C_SAA7185, I2C_SAA7185 + 1, + + saa7185_attach, + saa7185_detach, + saa7185_command +}; + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +int init_module(void) +#else +int saa7185_init(void) +#endif +{ + return i2c_register_driver(&i2c_driver_saa7185); +} + + + +#ifdef MODULE + +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_saa7185); +} + +#endif diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 9d8297000528..9a33cd86124e 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2136,6 +2136,9 @@ __initfunc(int tty_init(void)) #ifdef CONFIG_SERIAL rs_init(); #endif +#ifdef CONFIG_COMPUTONE + ip2_init(); +#endif #ifdef CONFIG_MAC_SERIAL macserial_init(); #endif diff --git a/drivers/char/zr36057.h b/drivers/char/zr36057.h new file mode 100644 index 000000000000..b672357bc666 --- /dev/null +++ b/drivers/char/zr36057.h @@ -0,0 +1,168 @@ +/* + zr36057.h - zr36057 register offsets + + Copyright (C) 1998 Dave Perks + + 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. + */ + +#ifndef _ZR36057_H_ +#define _ZR36057_H_ + + +/* Zoran ZR36057 registers */ + +#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ +#define ZR36057_VFEHCR_HSPol (1<<30) +#define ZR36057_VFEHCR_HStart 10 +#define ZR36057_VFEHCR_HEnd 0 +#define ZR36057_VFEHCR_Hmask 0x3ff + +#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ +#define ZR36057_VFEVCR_VSPol (1<<30) +#define ZR36057_VFEVCR_VStart 10 +#define ZR36057_VFEVCR_VEnd 0 +#define ZR36057_VFEVCR_Vmask 0x3ff + +#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ +#define ZR36057_VFESPFR_ExtFl (1<<26) +#define ZR36057_VFESPFR_TopField (1<<25) +#define ZR36057_VFESPFR_VCLKPol (1<<24) +#define ZR36057_VFESPFR_HFilter 21 +#define ZR36057_VFESPFR_HorDcm 14 +#define ZR36057_VFESPFR_VerDcm 8 +#define ZR36057_VFESPFR_DispMode 6 +#define ZR36057_VFESPFR_YUV422 (0<<3) +#define ZR36057_VFESPFR_RGB888 (1<<3) +#define ZR36057_VFESPFR_RGB565 (2<<3) +#define ZR36057_VFESPFR_RGB555 (3<<3) +#define ZR36057_VFESPFR_ErrDif (1<<2) +#define ZR36057_VFESPFR_Pack24 (1<<1) +#define ZR36057_VFESPFR_LittleEndian (1<<0) + +#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ + +#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ + +#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ +#define ZR36057_VSSFGR_DispStride 16 +#define ZR36057_VSSFGR_VidOvf (1<<8) +#define ZR36057_VSSFGR_SnapShot (1<<1) +#define ZR36057_VSSFGR_FrameGrab (1<<0) + +#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ +#define ZR36057_VDCR_VidEn (1<<31) +#define ZR36057_VDCR_MinPix 24 +#define ZR36057_VDCR_Triton (1<<24) +#define ZR36057_VDCR_VidWinHt 12 +#define ZR36057_VDCR_VidWinWid 0 + +#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ + +#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ + +#define ZR36057_OCR 0x024 /* Overlay Control Register */ +#define ZR36057_OCR_OvlEnable (1 << 15) +#define ZR36057_OCR_MaskStride 0 + +#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ +#define ZR36057_SPGPPCR_SoftReset (1<<24) + +#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ + +#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ + +#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ +#define ZR36057_MCTCR_CodTime (1 << 30) +#define ZR36057_MCTCR_CEmpty (1 << 29) +#define ZR36057_MCTCR_CFlush (1 << 28) +#define ZR36057_MCTCR_CodGuestID 20 +#define ZR36057_MCTCR_CodGuestReg 16 + +#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ + +#define ZR36057_ISR 0x03c /* Interrupt Status Register */ +#define ZR36057_ISR_GIRQ1 (1<<30) +#define ZR36057_ISR_GIRQ0 (1<<29) +#define ZR36057_ISR_CodRepIRQ (1<<28) +#define ZR36057_ISR_JPEGRepIRQ (1<<27) + +#define ZR36057_ICR 0x040 /* Interrupt Control Register */ +#define ZR36057_ICR_GIRQ1 (1<<30) +#define ZR36057_ICR_GIRQ0 (1<<29) +#define ZR36057_ICR_CodRepIRQ (1<<28) +#define ZR36057_ICR_JPEGRepIRQ (1<<27) +#define ZR36057_ICR_IntPinEn (1<<24) + +#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ +#define ZR36057_I2CBR_SDA (1<<1) +#define ZR36057_I2CBR_SCL (1<<0) + +#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ +#define ZR36057_JMC_JPG (1 << 31) +#define ZR36057_JMC_JPGExpMode (0 << 29) +#define ZR36057_JMC_JPGCmpMode (1 << 29) +#define ZR36057_JMC_MJPGExpMode (2 << 29) +#define ZR36057_JMC_MJPGCmpMode (3 << 29) +#define ZR36057_JMC_RTBUSY_FB (1 << 6) +#define ZR36057_JMC_Go_en (1 << 5) +#define ZR36057_JMC_SyncMstr (1 << 4) +#define ZR36057_JMC_Fld_per_buff (1 << 3) +#define ZR36057_JMC_VFIFO_FB (1 << 2) +#define ZR36057_JMC_CFIFO_FB (1 << 1) +#define ZR36057_JMC_Stll_LitEndian (1 << 0) + +#define ZR36057_JPC 0x104 /* JPEG Process Control */ +#define ZR36057_JPC_P_Reset (1 << 7) +#define ZR36057_JPC_CodTrnsEn (1 << 5) +#define ZR36057_JPC_Active (1 << 0) + +#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ +#define ZR36057_VSP_VsyncSize 16 +#define ZR36057_VSP_FrmTot 0 + +#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ +#define ZR36057_HSP_HsyncStart 16 +#define ZR36057_HSP_LineTot 0 + +#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ +#define ZR36057_FHAP_NAX 16 +#define ZR36057_FHAP_PAX 0 + +#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ +#define ZR36057_FVAP_NAY 16 +#define ZR36057_FVAP_PAY 0 + +#define ZR36057_FPP 0x118 /* Field Process Parameters */ +#define ZR36057_FPP_Odd_Even (1 << 0) + +#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ + +#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ + +#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ +#define ZR36057_JCGI_JPEGuestID 4 +#define ZR36057_JCGI_JPEGuestReg 0 + +#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ + +#define ZR36057_POR 0x200 /* Post Office Register */ +#define ZR36057_POR_POPen (1<<25) +#define ZR36057_POR_POTime (1<<24) +#define ZR36057_POR_PODir (1<<23) + +#define ZR36057_STR 0x300 /* "Still" Transfer Register */ + +#endif diff --git a/drivers/char/zr36060.h b/drivers/char/zr36060.h new file mode 100644 index 000000000000..d94f56b0b9ef --- /dev/null +++ b/drivers/char/zr36060.h @@ -0,0 +1,35 @@ +/* + zr36060.h - zr36060 register offsets + + Copyright (C) 1998 Dave Perks + + 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. + */ + +#ifndef _ZR36060_H_ +#define _ZR36060_H_ + + +/* Zoran ZR36060 registers */ + +#define ZR36060_LoadParameters 0x000 +#define ZR36060_Load (1<<7) +#define ZR36060_SyncRst (1<<0) + +#define ZR36060_CodeFifoStatus 0x001 +#define ZR36060_Load (1<<7) +#define ZR36060_SyncRst (1<<0) + +#endif diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 6fbd00a08861..ab8ead024177 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -192,13 +192,14 @@ adb_notify_sleep(struct notifier_block *this, unsigned long code, void *x) int ret; switch (code) { - case PBOOK_SLEEP: - ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); - if (ret & NOTIFY_STOP_MASK) - return -EBUSY; - case PBOOK_WAKE: - adb_reset_bus(); - break; + case PBOOK_SLEEP: + ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); + if (ret & NOTIFY_STOP_MASK) + return -EBUSY; + break; + case PBOOK_WAKE: + adb_reset_bus(); + break; } return NOTIFY_DONE; } diff --git a/drivers/macintosh/mac_keyb.c b/drivers/macintosh/mac_keyb.c index 9df6dc373634..7ebc8de11b78 100644 --- a/drivers/macintosh/mac_keyb.c +++ b/drivers/macintosh/mac_keyb.c @@ -66,10 +66,10 @@ static unsigned char dont_repeat[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* esc...option */ - 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* fn, num lock */ + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, /* fn, num lock */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* scroll lock */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* R modifiers */ }; /* Simple translation table for the SysRq keys */ @@ -741,9 +741,12 @@ mackeyb_probe(void) /* Enable full feature set of the keyboard ->get it to send separate codes for left and right shift, control, option keys */ +#if 0 /* handler 5 doesn't send separate codes for R modifiers */ if (adb_try_handler_change(id, 5)) printk("ADB keyboard at %d, handler set to 5\n", id); - else if (adb_try_handler_change(id, 3)) + else +#endif + if (adb_try_handler_change(id, 3)) printk("ADB keyboard at %d, handler set to 3\n", id); else printk("ADB keyboard at %d, handler 1\n", id); @@ -908,13 +911,10 @@ init_turbomouse(int id) adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(id,3), 0x20 | id, 4); - - adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(3)); adb_request(&req, NULL, ADBREQ_SYNC, 9, - ADB_WRITEREG(id,2), + ADB_WRITEREG(3,2), 0xe7, 0x8c, 0, @@ -924,10 +924,10 @@ init_turbomouse(int id) 0xff, 0x94); - adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(3)); adb_request(&req, NULL, ADBREQ_SYNC, 9, - ADB_WRITEREG(id,2), + ADB_WRITEREG(3,2), 0xa5, 0x14, 0, @@ -977,4 +977,3 @@ init_microspeed(int id) adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); } - diff --git a/drivers/net/Config.in b/drivers/net/Config.in index d9b0a339b67d..9ed6ac195301 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -231,6 +231,11 @@ fi endmenu +bool 'Fibre Channel driver support' CONFIG_NET_FC +if [ "$CONFIG_NET_FC" = "y" ]; then + tristate 'Interphase 5526 Tachyon chipset based adaptor support' CONFIG_IPHASE5526 +fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Red Creek Hardware VPN (EXPERIMENTAL)' CONFIG_RCPCI tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 9cc1e7361099..d35732b13bbb 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -5,7 +5,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda +ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc L_TARGET := net.a L_OBJS := auto_irq.o @@ -1138,6 +1138,15 @@ else endif endif +ifeq ($(CONFIG_NET_FC),y) +SUB_DIRS += fc +MOD_IN_SUB_DIRS += fc +else + ifeq ($(CONFIG_NET_FC),m) + MOD_IN_SUB_DIRS += fc + endif +endif + include $(TOPDIR)/Rules.make clean: diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 303c6d1965d7..67a1d7d6b5df 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -136,6 +136,9 @@ extern int apfddi_init(struct device *dev); /* HIPPI boards */ extern int rr_hippi_probe(struct device *); +/* Fibre Channel adapters */ +extern int iph5526_probe(struct device *dev); + struct devprobe { int (*probe)(struct device *dev); @@ -574,6 +577,25 @@ static int hippi_probe(struct device *dev) } #endif + +#ifdef CONFIG_NET_FC +static int fcif_probe(struct device *dev) +{ + if (dev->base_addr == -1) + return 1; + + if (1 +#ifdef CONFIG_IPHASE5526 + && iph5526_probe(dev) +#endif + && 1 ) { + return 1; /* -ENODEV or -EAGAIN would be more accurate. */ + } + return 0; +} +#endif /* CONFIG_NET_FC */ + + #ifdef CONFIG_ETHERTAP static struct device tap0_dev = { "tap0", 0, 0, 0, 0, NETLINK_TAPBASE, 0, 0, 0, 0, NEXT_DEV, ethertap_probe, }; # undef NEXT_DEV @@ -821,9 +843,20 @@ static struct device tr0_dev = { # undef NEXT_DEV # define NEXT_DEV (&bif_dev) #endif + + +#ifdef CONFIG_NET_FC + static struct device fc1_dev = { + "fc1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, fcif_probe}; + static struct device fc0_dev = { + "fc0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fc1_dev, fcif_probe}; +# undef NEXT_DEV +# define NEXT_DEV (&fc0_dev) +#endif + #ifdef CONFIG_NET_SB1000 - extern int sb1000_init(struct device *dev); + extern int sb1000_probe(struct device *dev); static struct device sb1000_dev = { "cm0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, sb1000_probe }; # undef NEXT_DEV diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c index 391f6194a072..0f6e95bb6365 100644 --- a/drivers/net/eth16i.c +++ b/drivers/net/eth16i.c @@ -886,7 +886,7 @@ static int eth16i_check_signature(int ioaddr) creg[0] &= 0x0F; /* Mask collision cnr */ creg[2] &= 0x7F; /* Mask DCLEN bit */ -#ifdef 0 +#if 0 /* This was removed because the card was sometimes left to state from which it couldn't be find anymore. If there is need diff --git a/drivers/net/fc/Makefile b/drivers/net/fc/Makefile new file mode 100644 index 000000000000..6fd921f523d5 --- /dev/null +++ b/drivers/net/fc/Makefile @@ -0,0 +1,28 @@ + +# Makefile for linux/drivers/net/fc +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +L_TARGET := fc.a +L_OBJS := +M_OBJS := +MX_OBJS := +MOD_LIST_NAME := FC_MODULES +FC_SRCS = $(wildcard $(L_OBJS:%.o=%.c)) + +ifeq ($(CONFIG_IPHASE5526),y) +L_OBJS += iph5526.o +else + ifeq ($(CONFIG_IPHASE5526),m) + M_OBJS += iph5526.o + endif +endif + +include $(TOPDIR)/Rules.make + +clean: + rm *.o + diff --git a/drivers/net/fc/iph5526.c b/drivers/net/fc/iph5526.c new file mode 100644 index 000000000000..09cc7c02199f --- /dev/null +++ b/drivers/net/fc/iph5526.c @@ -0,0 +1,4703 @@ +/********************************************************************** + * iph5526.c: IP/SCSI driver for the Interphase 5526 PCI Fibre Channel + * Card. + * Copyright (C) 1999 Vineet M Abraham + * + * 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. + *********************************************************************/ +/********************************************************************** +Log: +Vineet M Abraham +02.12.99 Support multiple cards. +03.15.99 Added Fabric support. +04.04.99 Added N_Port support. +04.15.99 Added SCSI support. +06.18.99 Added ABTS Protocol. +06.24.99 Fixed data corruption when multiple XFER_RDYs are received. +07.07.99 Can be loaded as part of the Kernel. Changed semaphores. Added + more checks before invalidating SEST entries. +07.08.99 Added Broadcast IP stuff and fixed an unicast timeout bug. +***********************************************************************/ +/* TODO: + R_T_TOV set to 15msec in Loop topology. Need to be 100 msec. + SMP testing. + Fix ADISC Tx before completing FLOGI. +*/ + +static const char *version = + "iph5526.c:v1.0 07.08.99 Vineet Abraham (vma@iol.unh.edu)\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* had the declarations for init_fcdev among others + includes if_fcdevice.h */ + +#include +#include "../../scsi/sd.h" +#include "../../scsi/scsi.h" +#include "../../scsi/hosts.h" +#include "../../fc4/fcp.h" + +/* driver specific header files */ +#include "tach.h" +#include "tach_structs.h" +#include "iph5526_ip.h" +#include "iph5526_scsi.h" +#include "iph5526_novram.c" + +#define RUN_AT(x) (jiffies + (x)) + +#define DEBUG_5526_0 0 +#define DEBUG_5526_1 0 +#define DEBUG_5526_2 0 + +#if DEBUG_5526_0 +#define DPRINTK(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a); \ + printk("\n");} +#define ENTER(x) {printk("%s: ", fi->name); \ + printk("iph5526.c : entering %s()\n", x);} +#define LEAVE(x) {printk("%s: ", fi->name); \ + printk("iph5526.c : leaving %s()\n",x);} + +#else +#define DPRINTK(format, a...) {} +#define ENTER(x) {} +#define LEAVE(x) {} +#endif + +#if DEBUG_5526_1 +#define DPRINTK1(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a); \ + printk("\n");} +#else +#define DPRINTK1(format, a...) {} +#endif + +#if DEBUG_5526_2 +#define DPRINTK2(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a); \ + printk("\n");} +#else +#define DPRINTK2(format, a...) {} +#endif + +#define T_MSG(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a);\ + printk("\n");} + +#define ALIGNED_SFS_ADDR(addr) ((((unsigned long)(addr) + (SFS_BUFFER_SIZE - 1)) & ~(SFS_BUFFER_SIZE - 1)) - (unsigned long)(addr)) +#define ALIGNED_ADDR(addr, len) ((((unsigned long)(addr) + (len - 1)) & ~(len - 1)) - (unsigned long)(addr)) + + +#define MAX_FC_CARDS 2 +static struct fc_info *fc[MAX_FC_CARDS+1]; +static unsigned int pci_irq_line = 0; +static struct { + unsigned short vendor_id; + unsigned short device_id; + char *name; +} +clone_list[] __initdata = { + {PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_5526, "Interphase Fibre Channel HBA"}, + {PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_55x6, "Interphase Fibre Channel HBA"}, + {0,} +}; + +static void tachyon_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void tachyon_interrupt_handler(int irq, void* dev_id, struct pt_regs* regs); + +static int initialize_register_pointers(struct fc_info *fi); +void clean_up_memory(struct fc_info *fi); + +static int tachyon_init(struct fc_info *fi); +static int build_queues(struct fc_info *fi); +static void build_tachyon_header(struct fc_info *fi, u_int my_id, u_int r_ctl, u_int d_id, u_int type, u_char seq_id, u_char df_ctl, u_short ox_id, u_short rx_id, char *data); +static int get_free_header(struct fc_info *fi); +static void build_EDB(struct fc_info *fi, char *data, u_short flags, u_short len); +static int get_free_EDB(struct fc_info *fi); +static void build_ODB(struct fc_info *fi, u_char seq_id, u_int d_id, u_int len, u_int cntl, u_short mtu, u_short ox_id, u_short rx_id, int NW_header, int int_required, u_int frame_class); +static void write_to_tachyon_registers(struct fc_info *fi); +static void reset_latch(struct fc_info *fi); +static void reset_tachyon(struct fc_info *fi, u_int value); +static void take_tachyon_offline(struct fc_info *fi); +static void read_novram(struct fc_info *fi); +static void reset_ichip(struct fc_info *fi); +static void update_OCQ_indx(struct fc_info *fi); +static void update_IMQ_indx(struct fc_info *fi, int count); +static void update_SFSBQ_indx(struct fc_info *fi); +static void update_MFSBQ_indx(struct fc_info *fi, int count); +static void update_tachyon_header_indx(struct fc_info *fi); +static void update_EDB_indx(struct fc_info *fi); +static void handle_FM_interrupt(struct fc_info *fi); +static void handle_MFS_interrupt(struct fc_info *fi); +static void handle_OOO_interrupt(struct fc_info *fi); +static void handle_SFS_interrupt(struct fc_info *fi); +static void handle_OCI_interrupt(struct fc_info *fi); +static void handle_SFS_BUF_WARN_interrupt(struct fc_info *fi); +static void handle_MFS_BUF_WARN_interrupt(struct fc_info *fi); +static void handle_IMQ_BUF_WARN_interrupt(struct fc_info *fi); +static void handle_Unknown_Frame_interrupt(struct fc_info *fi); +static void handle_Busied_Frame_interrupt(struct fc_info *fi); +static void handle_Bad_SCSI_Frame_interrupt(struct fc_info *fi); +static void handle_Inbound_SCSI_Status_interrupt(struct fc_info *fi); +static void handle_Inbound_SCSI_Command_interrupt(struct fc_info *fi); +static void completion_message_handler(struct fc_info *fi, u_int imq_int_type); +static void fill_login_frame(struct fc_info *fi, u_int logi); + +static int tx_exchange(struct fc_info *fi, char *data, u_int len, u_int r_ctl, u_int type, u_int d_id, u_int mtu, int int_required, u_short ox_id, u_int frame_class); +static int tx_sequence(struct fc_info *fi, char *data, u_int len, u_int mtu, u_int d_id, u_short ox_id, u_short rx_id, u_char seq_id, int NW_flag, int int_required, u_int frame_class); +static int validate_login(struct fc_info *fi, u_int *base_ptr); +static void add_to_address_cache(struct fc_info *fi, u_int *base_ptr); +static void remove_from_address_cache(struct fc_info *fi, u_int *data, u_int cmnd_code); +static int node_logged_in_prev(struct fc_info *fi, u_int *buff_addr); +static int sid_logged_in(struct fc_info *fi, u_int s_id); +static struct fc_node_info *look_up_cache(struct fc_info *fi, char *data); +static int display_cache(struct fc_info *fi); + +static void tx_logi(struct fc_info *fi, u_int logi, u_int d_id); +static void tx_logi_acc(struct fc_info *fi, u_int logi, u_int d_id, u_short received_ox_id); +static void tx_prli(struct fc_info *fi, u_int command_code, u_int d_id, u_short received_ox_id); +static void tx_logo(struct fc_info *fi, u_int d_id, u_short received_ox_id); +static void tx_adisc(struct fc_info *fi, u_int cmnd_code, u_int d_id, u_short received_ox_id); +static void tx_ls_rjt(struct fc_info *fi, u_int d_id, u_short received_ox_id, u_short reason_code, u_short expln_code); +static u_int plogi_ok(struct fc_info *fi, u_int *buff_addr, int size); +static void tx_acc(struct fc_info *fi, u_int d_id, u_short received_ox_id); +static void tx_name_server_req(struct fc_info *fi, u_int req); +static void rscn_handler(struct fc_info *fi, u_int node_id); +static void tx_scr(struct fc_info *fi); +static void scr_timer(unsigned long data); +static void explore_fabric(struct fc_info *fi, u_int *buff_addr); +static void perform_adisc(struct fc_info *fi); +static void local_port_discovery(struct fc_info *fi); +static void add_to_ox_id_list(struct fc_info *fi, u_int transaction_id, u_int cmnd_code); +static u_int remove_from_ox_id_list(struct fc_info *fi, u_short received_ox_id); +static void add_display_cache_timer(struct fc_info *fi); + +/* Timers... */ +static void nos_ols_timer(unsigned long data); +static void loop_timer(unsigned long data); +static void fabric_explore_timer(unsigned long data); +static void port_discovery_timer(unsigned long data); +static void display_cache_timer(unsigned long data); + +/* SCSI Stuff */ +static int add_to_sest(struct fc_info *fi, Scsi_Cmnd *Cmnd, struct fc_node_info *ni); +static struct fc_node_info *resolve_target(struct fc_info *fi, u_char target); +static void update_FCP_CMND_indx(struct fc_info *fi); +static int get_free_SDB(struct fc_info *fi); +static void update_SDB_indx(struct fc_info *fi); +static void mark_scsi_sid(struct fc_info *fi, u_int *buff_addr, u_char action); +static void invalidate_SEST_entry(struct fc_info *fi, u_short received_ox_id); +static int abort_exchange(struct fc_info *fi, u_short ox_id); +static void flush_tachyon_cache(struct fc_info *fi, u_short ox_id); +static int get_scsi_oxid(struct fc_info *fi); +static void update_scsi_oxid(struct fc_info *fi); + +Scsi_Host_Template driver_template = IPH5526_SCSI_FC; + + +#ifdef CONFIG_PCI +static int iph5526_probe_pci(struct device *dev); +#endif + + +__initfunc(int iph5526_probe(struct device *dev)) +{ +#ifdef CONFIG_PCI + if (pci_present() && (iph5526_probe_pci(dev) == 0)) + return 0; +#endif + return -ENODEV; +} + +#ifdef CONFIG_PCI +__initfunc(static int iph5526_probe_pci(struct device *dev)) +{ +#ifndef MODULE +struct fc_info *fi; +static int count = 0; +#endif +#ifdef MODULE +struct fc_info *fi = (struct fc_info *)dev->priv; +#endif + +#ifndef MODULE + if(fc[count] != NULL) { + if (dev == NULL) { + dev = init_fcdev(NULL, 0); + if (dev == NULL) + return -ENOMEM; + } + fi = fc[count]; +#endif + fi->dev = dev; + dev->base_addr = fi->base_addr; + dev->irq = fi->irq; + if (dev->priv == NULL) + dev->priv = fi; + fcdev_init(dev); + /* Assign ur MAC address. + */ + dev->dev_addr[0] = (fi->g.my_port_name_high & 0x0000FF00) >> 8; + dev->dev_addr[1] = fi->g.my_port_name_high; + dev->dev_addr[2] = (fi->g.my_port_name_low & 0xFF000000) >> 24; + dev->dev_addr[3] = (fi->g.my_port_name_low & 0x00FF0000) >> 16; + dev->dev_addr[4] = (fi->g.my_port_name_low & 0x0000FF00) >> 8; + dev->dev_addr[5] = fi->g.my_port_name_low; +#ifndef MODULE + count++; + } + else + return -ENODEV; +#endif + display_cache(fi); + return 0; +} +#endif /* CONFIG_PCI */ + +__initfunc(static int fcdev_init(struct device *dev)) +{ + dev->open = iph5526_open; + dev->stop = iph5526_close; + dev->hard_start_xmit = iph5526_send_packet; + dev->get_stats = iph5526_get_stats; + dev->set_multicast_list = NULL; + dev->change_mtu = iph5526_change_mtu; +#ifndef MODULE + fc_setup(dev); +#endif + return 0; +} + +/* initialize tachyon and take it OnLine */ +static int tachyon_init(struct fc_info *fi) +{ + ENTER("tachyon_init"); + if (build_queues(fi) == 0) { + T_MSG("build_queues() failed"); + return 0; + } + + /* Retrieve your port/node name. + */ + read_novram(fi); + + reset_ichip(fi); + + reset_tachyon(fi, SOFTWARE_RESET); + + LEAVE("tachyon_init"); + return 1; +} + +/* Build the 4 Qs - IMQ, OCQ, MFSBQ, SFSBQ */ +/* Lots of dma_pages needed as Tachyon DMAs almost everything into + * host memory. + */ +static int build_queues(struct fc_info *fi) +{ +int i,j; +u_char *addr; + ENTER("build_queues"); + /* Initializing Queue Variables. + */ + fi->q.ptr_host_ocq_cons_indx = NULL; + fi->q.ptr_host_hpcq_cons_indx = NULL; + fi->q.ptr_host_imq_prod_indx = NULL; + + fi->q.ptr_ocq_base = NULL; + fi->q.ocq_len = 0; + fi->q.ocq_end = 0; + fi->q.ocq_prod_indx = 0; + + fi->q.ptr_imq_base = NULL; + fi->q.imq_len = 0; + fi->q.imq_end = 0; + fi->q.imq_cons_indx = 0; + fi->q.imq_prod_indx = 0; + + fi->q.ptr_mfsbq_base = NULL; + fi->q.mfsbq_len = 0; + fi->q.mfsbq_end = 0; + fi->q.mfsbq_prod_indx = 0; + fi->q.mfsbq_cons_indx = 0; + fi->q.mfsbuff_len = 0; + fi->q.mfsbuff_end = 0; + fi->g.mfs_buffer_count = 0; + + fi->q.ptr_sfsbq_base = NULL; + fi->q.sfsbq_len = 0; + fi->q.sfsbq_end = 0; + fi->q.sfsbq_prod_indx = 0; + fi->q.sfsbq_cons_indx = 0; + fi->q.sfsbuff_len = 0; + fi->q.sfsbuff_end = 0; + + fi->q.sdb_indx = 0; + fi->q.fcp_cmnd_indx = 0; + + fi->q.ptr_edb_base = NULL; + fi->q.edb_buffer_indx = 0; + fi->q.ptr_tachyon_header_base = NULL; + fi->q.tachyon_header_indx = 0; + fi->node_info_list = NULL; + fi->ox_id_list = NULL; + fi->g.loop_up = FALSE; + fi->g.ptp_up = FALSE; + fi->g.link_up = FALSE; + fi->g.fabric_present = FALSE; + fi->g.n_port_try = FALSE; + fi->g.dont_init = FALSE; + fi->g.nport_timer_set = FALSE; + fi->g.lport_timer_set = FALSE; + fi->g.no_of_targets = 0; + fi->g.sem = 0; + fi->g.perform_adisc = FALSE; + fi->g.e_i = 0; + + /* build OCQ */ + if ( (fi->q.ptr_ocq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get OCQ page"); + return 0; + } + /* set up the OCQ structures */ + for (i = 0; i < OCQ_LENGTH; i++) + fi->q.ptr_odb[i] = fi->q.ptr_ocq_base + NO_OF_ENTRIES*i; + + /* build IMQ */ + if ( (fi->q.ptr_imq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get IMQ page"); + return 0; + } + for (i = 0; i < IMQ_LENGTH; i++) + fi->q.ptr_imqe[i] = fi->q.ptr_imq_base + NO_OF_ENTRIES*i; + + /* build MFSBQ */ + if ( (fi->q.ptr_mfsbq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get MFSBQ page"); + return 0; + } + memset((char *)fi->q.ptr_mfsbq_base, 0, MFSBQ_LENGTH * 32); + /* Allocate one huge chunk of memory... helps while reassembling + * frames. + */ + if ( (addr = (u_char *)__get_free_pages(GFP_KERNEL, 5) ) == 0) { + T_MSG("failed to get MFSBQ page"); + return 0; + } + /* fill in addresses of empty buffers */ + for (i = 0; i < MFSBQ_LENGTH; i++) { + for (j = 0; j < NO_OF_ENTRIES; j++) { + *(fi->q.ptr_mfsbq_base + i*NO_OF_ENTRIES + j) = htonl(virt_to_bus(addr)); + addr += MFS_BUFFER_SIZE; + } + } + + /* The number of entries in each MFS buffer is 8. There are 8 + * MFS buffers. That leaves us with 4096-256 bytes. We use them + * as temporary space for ELS frames. This is done to make sure that + * the addresses are aligned. + */ + fi->g.els_buffer[0] = fi->q.ptr_mfsbq_base + MFSBQ_LENGTH*NO_OF_ENTRIES; + for (i = 1; i < MAX_PENDING_FRAMES; i++) + fi->g.els_buffer[i] = fi->g.els_buffer[i-1] + 64; + + /* build SFSBQ */ + if ( (fi->q.ptr_sfsbq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get SFSBQ page"); + return 0; + } + memset((char *)fi->q.ptr_sfsbq_base, 0, SFSBQ_LENGTH * 32); + /* fill in addresses of empty buffers */ + for (i = 0; i < SFSBQ_LENGTH; i++) + for (j = 0; j < NO_OF_ENTRIES; j++){ + addr = kmalloc(SFS_BUFFER_SIZE*2, GFP_KERNEL); + if (addr == NULL){ + T_MSG("ptr_sfs_buffer : memory not allocated"); + return 0; + } + else { + int offset = ALIGNED_SFS_ADDR(addr); + memset((char *)addr, 0, SFS_BUFFER_SIZE); + fi->q.ptr_sfs_buffers[i*NO_OF_ENTRIES +j] = (u_int *)addr; + addr += offset; + *(fi->q.ptr_sfsbq_base + i*NO_OF_ENTRIES + j) = htonl(virt_to_bus(addr)); + } + } + + /* The number of entries in each SFS buffer is 8. There are 8 + * MFS buffers. That leaves us with 4096-256 bytes. We use them + * as temporary space for ARP frames. This is done inorder to + * support HW_Types of 0x1 and 0x6. + */ + fi->g.arp_buffer = (char *)fi->q.ptr_sfsbq_base + SFSBQ_LENGTH*NO_OF_ENTRIES*4; + + /* build EDB */ + if ((fi->q.ptr_edb_base = (u_int *)__get_free_pages(GFP_KERNEL, 5) ) == 0) { + T_MSG("failed to get EDB page"); + return 0; + } + for (i = 0; i < EDB_LEN; i++) + fi->q.ptr_edb[i] = fi->q.ptr_edb_base + 2*i; + + /* build SEST */ + + /* OX_IDs range from 0x0 - 0x4FFF. + */ + if ((fi->q.ptr_sest_base = (u_int *)__get_free_pages(GFP_KERNEL, 5)) == 0) { + T_MSG("failed to get SEST page"); + return 0; + } + for (i = 0; i < SEST_LENGTH; i++) + fi->q.ptr_sest[i] = fi->q.ptr_sest_base + NO_OF_ENTRIES*i; + + if ((fi->q.ptr_sdb_base = (u_int *)__get_free_pages(GFP_KERNEL, 5)) == 0) { + T_MSG("failed to get SDB page"); + return 0; + } + for (i = 0 ; i < NO_OF_SDB_ENTRIES; i++) + fi->q.ptr_sdb_slot[i] = fi->q.ptr_sdb_base + (SDB_SIZE/4)*i; + + if ((fi->q.ptr_fcp_cmnd_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get FCP_CMND page"); + return 0; + } + for (i = 0; i < NO_OF_FCP_CMNDS; i++) + fi->q.ptr_fcp_cmnd[i] = fi->q.ptr_fcp_cmnd_base + NO_OF_ENTRIES*i; + + /* Allocate space for Tachyon Header as well... + */ + if ((fi->q.ptr_tachyon_header_base = (u_int *)__get_free_pages(GFP_KERNEL, 0) ) == 0) { + T_MSG("failed to get tachyon_header page"); + return 0; + } + for (i = 0; i < NO_OF_TACH_HEADERS; i++) + fi->q.ptr_tachyon_header[i] = fi->q.ptr_tachyon_header_base + 16*i; + + /* Allocate memory for indices. + * Indices should be aligned on 32 byte boundries. + */ + fi->q.host_ocq_cons_indx = kmalloc(2*32, GFP_KERNEL); + if (fi->q.host_ocq_cons_indx == NULL){ + T_MSG("fi->q.host_ocq_cons_indx : memory not allocated"); + return 0; + } + fi->q.ptr_host_ocq_cons_indx = fi->q.host_ocq_cons_indx; + if ((u_long)(fi->q.host_ocq_cons_indx) % 32) + fi->q.host_ocq_cons_indx++; + + fi->q.host_hpcq_cons_indx = kmalloc(2*32, GFP_KERNEL); + if (fi->q.host_hpcq_cons_indx == NULL){ + T_MSG("fi->q.host_hpcq_cons_indx : memory not allocated"); + return 0; + } + fi->q.ptr_host_hpcq_cons_indx= fi->q.host_hpcq_cons_indx; + if ((u_long)(fi->q.host_hpcq_cons_indx) % 32) + fi->q.host_hpcq_cons_indx++; + + fi->q.host_imq_prod_indx = kmalloc(2*32, GFP_KERNEL); + if (fi->q.host_imq_prod_indx == NULL){ + T_MSG("fi->q.host_imq_prod_indx : memory not allocated"); + return 0; + } + fi->q.ptr_host_imq_prod_indx = fi->q.host_imq_prod_indx; + if ((u_long)(fi->q.host_imq_prod_indx) % 32) + fi->q.host_imq_prod_indx++; + + LEAVE("build_queues"); + return 1; +} + + +static void write_to_tachyon_registers(struct fc_info *fi) +{ +u_int bus_addr, bus_indx_addr, i; + + ENTER("write_to_tachyon_registers"); + + /* Clear Queues each time Tachyon is reset */ + memset((char *)fi->q.ptr_ocq_base, 0, OCQ_LENGTH * 32); + memset((char *)fi->q.ptr_imq_base, 0, IMQ_LENGTH * 32); + memset((char *)fi->q.ptr_edb_base, 0, EDB_LEN * 8); + memset((char *)fi->q.ptr_sest_base, 0, SEST_LENGTH * 32); + memset((char *)fi->q.ptr_sdb_base, 0, NO_OF_SDB_ENTRIES * SDB_SIZE); + memset((char *)fi->q.ptr_tachyon_header_base, 0xFF, NO_OF_TACH_HEADERS * TACH_HEADER_SIZE); + for (i = 0; i < SEST_LENGTH; i++) + fi->q.free_scsi_oxid[i] = OXID_AVAILABLE; + for (i = 0; i < NO_OF_SDB_ENTRIES; i++) + fi->q.sdb_slot_status[i] = SDB_FREE; + + take_tachyon_offline(fi); + writel(readl(fi->t_r.ptr_tach_config_reg) | SCSI_ENABLE | WRITE_STREAM_SIZE | READ_STREAM_SIZE | PARITY_EVEN | OOO_REASSEMBLY_DISABLE, fi->t_r.ptr_tach_config_reg); + + /* Write OCQ registers */ + fi->q.ocq_prod_indx = 0; + *(fi->q.host_ocq_cons_indx) = 0; + + /* The Tachyon needs to be passed the "real" address */ + bus_addr = virt_to_bus(fi->q.ptr_ocq_base); + writel(bus_addr, fi->t_r.ptr_ocq_base_reg); + writel(OCQ_LENGTH - 1, fi->t_r. ptr_ocq_len_reg); + bus_indx_addr = virt_to_bus(fi->q.host_ocq_cons_indx); + writel(bus_indx_addr, fi->t_r.ptr_ocq_cons_indx_reg); + + /* Write IMQ registers */ + fi->q.imq_cons_indx = 0; + *(fi->q.host_imq_prod_indx) = 0; + bus_addr = virt_to_bus(fi->q.ptr_imq_base); + writel(bus_addr, fi->t_r.ptr_imq_base_reg); + writel(IMQ_LENGTH - 1, fi->t_r.ptr_imq_len_reg); + bus_indx_addr = virt_to_bus(fi->q.host_imq_prod_indx); + writel(bus_indx_addr, fi->t_r.ptr_imq_prod_indx_reg); + + /* Write MFSBQ registers */ + fi->q.mfsbq_prod_indx = MFSBQ_LENGTH - 1; + fi->q.mfsbuff_end = MFS_BUFFER_SIZE - 1; + fi->q.mfsbq_cons_indx = 0; + bus_addr = virt_to_bus(fi->q.ptr_mfsbq_base); + writel(bus_addr, fi->t_r.ptr_mfsbq_base_reg); + writel(MFSBQ_LENGTH - 1, fi->t_r.ptr_mfsbq_len_reg); + writel(fi->q.mfsbuff_end, fi->t_r.ptr_mfsbuff_len_reg); + /* Do this last as tachyon will prefetch the + * first entry as soon as we write to it. + */ + writel(fi->q.mfsbq_prod_indx, fi->t_r.ptr_mfsbq_prod_reg); + + /* Write SFSBQ registers */ + fi->q.sfsbq_prod_indx = SFSBQ_LENGTH - 1; + fi->q.sfsbuff_end = SFS_BUFFER_SIZE - 1; + fi->q.sfsbq_cons_indx = 0; + bus_addr = virt_to_bus(fi->q.ptr_sfsbq_base); + writel(bus_addr, fi->t_r.ptr_sfsbq_base_reg); + writel(SFSBQ_LENGTH - 1, fi->t_r.ptr_sfsbq_len_reg); + writel(fi->q.sfsbuff_end, fi->t_r.ptr_sfsbuff_len_reg); + /* Do this last as tachyon will prefetch the first + * entry as soon as we write to it. + */ + writel(fi->q.sfsbq_prod_indx, fi->t_r.ptr_sfsbq_prod_reg); + + /* Write SEST registers */ + bus_addr = virt_to_bus(fi->q.ptr_sest_base); + writel(bus_addr, fi->t_r.ptr_sest_base_reg); + writel(SEST_LENGTH - 1, fi->t_r.ptr_sest_len_reg); + /* the last 2 bits _should_ be 1 */ + writel(SEST_BUFFER_SIZE - 1, fi->t_r.ptr_scsibuff_len_reg); + + /* write AL_TIME & E_D_TOV into the registers */ + writel(TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + /* Tell Tachyon to pick a Soft Assigned AL_PA */ + writel(LOOP_INIT_SOFT_ADDRESS, fi->t_r.ptr_fm_config_reg); + + /* Read the WWN from EEPROM . But, for now we assign it here. */ + writel(WORLD_WIDE_NAME_LOW, fi->t_r.ptr_fm_wwn_low_reg); + writel(WORLD_WIDE_NAME_HIGH, fi->t_r.ptr_fm_wwn_hi_reg); + + DPRINTK1("TACHYON initializing as L_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + + LEAVE("write_to_tachyon_registers"); +} + + +static void tachyon_interrupt(int irq, void* dev_id, struct pt_regs* regs) +{ +struct Scsi_Host *host = dev_id; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +struct fc_info *fi = hostdata->fi; +u_long flags; + spin_lock_irqsave(&fi->fc_lock, flags); + tachyon_interrupt_handler(irq, dev_id, regs); + spin_unlock_irqrestore(&fi->fc_lock, flags); +} + +static void tachyon_interrupt_handler(int irq, void* dev_id, struct pt_regs* regs) +{ +struct Scsi_Host *host = dev_id; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +struct fc_info *fi = hostdata->fi; +u_int *ptr_imq_entry; +u_int imq_int_type, current_IMQ_index = 0, prev_IMQ_index; +int index, no_of_entries = 0; + + DPRINTK("\n"); + ENTER("tachyon_interrupt"); + if (fi->q.host_imq_prod_indx != NULL) { + current_IMQ_index = ntohl(*(fi->q.host_imq_prod_indx)); + } + else { + /* _Should not_ happen */ + T_MSG("IMQ_indx NULL. DISABLING INTERRUPTS!!!\n"); + writel(0x0, fi->i_r.ptr_ichip_hw_control_reg); + } + + if (current_IMQ_index > fi->q.imq_cons_indx) + no_of_entries = current_IMQ_index - fi->q.imq_cons_indx; + else + if (current_IMQ_index < fi->q.imq_cons_indx) + no_of_entries = IMQ_LENGTH - (fi->q.imq_cons_indx - current_IMQ_index); + + if (no_of_entries == 0) { + u_int ichip_status; + ichip_status = readl(fi->i_r.ptr_ichip_hw_status_reg); + if (ichip_status & 0x20) { + /* Should _never_ happen. Might require a hard reset */ + T_MSG("Too bad... PCI Bus Error. Resetting (i)chip"); + reset_ichip(fi); + T_MSG("DISABLING INTERRUPTS!!!\n"); + writel(0x0, fi->i_r.ptr_ichip_hw_control_reg); + } + } + + prev_IMQ_index = current_IMQ_index; + for (index = 0; index < no_of_entries; index++) { + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + imq_int_type = ntohl(*ptr_imq_entry); + + completion_message_handler(fi, imq_int_type); + if ((fi->g.link_up == FALSE) && ((imq_int_type == MFS_BUF_WARN) || (imq_int_type == SFS_BUF_WARN) || (imq_int_type == IMQ_BUF_WARN))) + break; + update_IMQ_indx(fi, 1); + + /* Check for more entries */ + current_IMQ_index = ntohl(*(fi->q.host_imq_prod_indx)); + if (current_IMQ_index != prev_IMQ_index) { + no_of_entries++; + prev_IMQ_index = current_IMQ_index; + } + } /*end of for loop*/ + return; + LEAVE("tachyon_interrupt"); +} + + +static void handle_SFS_BUF_WARN_interrupt(struct fc_info *fi) +{ +int i; + ENTER("handle_SFS_BUF_WARN_interrupt"); + if (fi->g.link_up == FALSE) { + reset_tachyon(fi, SOFTWARE_RESET); + return; + } + /* Free up all but one entry in the Q. + */ + for (i = 0; i < ((SFSBQ_LENGTH - 1) * NO_OF_ENTRIES); i++) { + handle_SFS_interrupt(fi); + update_IMQ_indx(fi, 1); + } + LEAVE("handle_SFS_BUF_WARN_interrupt"); +} + +/* Untested_Code_Begin */ +static void handle_MFS_BUF_WARN_interrupt(struct fc_info *fi) +{ +int i; + ENTER("handle_MFS_BUF_WARN_interrupt"); + if (fi->g.link_up == FALSE) { + reset_tachyon(fi, SOFTWARE_RESET); + return; + } + /* FIXME: freeing up 8 entries. + */ + for (i = 0; i < NO_OF_ENTRIES; i++) { + handle_MFS_interrupt(fi); + update_IMQ_indx(fi, 1); + } + LEAVE("handle_MFS_BUF_WARN_interrupt"); +} +/*Untested_Code_End */ + +static void handle_IMQ_BUF_WARN_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +u_int imq_int_type, current_IMQ_index = 0, temp_imq_cons_indx; +int index, no_of_entries = 0; + + ENTER("handle_IMQ_BUF_WARN_interrupt"); + if (fi->g.link_up == FALSE) { + reset_tachyon(fi, SOFTWARE_RESET); + return; + } + current_IMQ_index = ntohl(*(fi->q.host_imq_prod_indx)); + + if (current_IMQ_index > fi->q.imq_cons_indx) + no_of_entries = current_IMQ_index - fi->q.imq_cons_indx; + else + if (current_IMQ_index < fi->q.imq_cons_indx) + no_of_entries = IMQ_LENGTH - (fi->q.imq_cons_indx - current_IMQ_index); + /* We dont want to look at the same IMQ entry again. + */ + temp_imq_cons_indx = fi->q.imq_cons_indx + 1; + if (no_of_entries != 0) + no_of_entries -= 1; + for (index = 0; index < no_of_entries; index++) { + ptr_imq_entry = fi->q.ptr_imqe[temp_imq_cons_indx]; + imq_int_type = ntohl(*ptr_imq_entry); + if (imq_int_type != IMQ_BUF_WARN) + completion_message_handler(fi, imq_int_type); + temp_imq_cons_indx++; + if (temp_imq_cons_indx == IMQ_LENGTH) + temp_imq_cons_indx = 0; + } /*end of for loop*/ + if (no_of_entries != 0) + update_IMQ_indx(fi, no_of_entries); + LEAVE("handle_IMQ_BUF_WARN_interrupt"); +} + +static void completion_message_handler(struct fc_info *fi, u_int imq_int_type) +{ + switch(imq_int_type) { + case OUTBOUND_COMPLETION: + DPRINTK("OUTBOUND_COMPLETION message received"); + break; + case OUTBOUND_COMPLETION_I: + DPRINTK("OUTBOUND_COMPLETION_I message received"); + handle_OCI_interrupt(fi); + break; + case OUT_HI_PRI_COMPLETION: + DPRINTK("OUT_HI_PRI_COMPLETION message received"); + break; + case OUT_HI_PRI_COMPLETION_I: + DPRINTK("OUT_HI_PRI_COMPLETION_I message received"); + break; + case INBOUND_MFS_COMPLETION: + DPRINTK("INBOUND_MFS_COMPLETION message received"); + handle_MFS_interrupt(fi); + break; + case INBOUND_OOO_COMPLETION: + DPRINTK("INBOUND_OOO_COMPLETION message received"); + handle_OOO_interrupt(fi); + break; + case INBOUND_SFS_COMPLETION: + DPRINTK("INBOUND_SFS_COMPLETION message received"); + handle_SFS_interrupt(fi); + break; + case INBOUND_UNKNOWN_FRAME_I: + DPRINTK("INBOUND_UNKNOWN_FRAME message received"); + handle_Unknown_Frame_interrupt(fi); + break; + case INBOUND_BUSIED_FRAME: + DPRINTK("INBOUND_BUSIED_FRAME message received"); + handle_Busied_Frame_interrupt(fi); + break; + case FRAME_MGR_INTERRUPT: + DPRINTK("FRAME_MGR_INTERRUPT message received"); + handle_FM_interrupt(fi); + break; + case READ_STATUS: + DPRINTK("READ_STATUS message received"); + break; + case SFS_BUF_WARN: + DPRINTK("SFS_BUF_WARN message received"); + handle_SFS_BUF_WARN_interrupt(fi); + break; + case MFS_BUF_WARN: + DPRINTK("MFS_BUF_WARN message received"); + handle_MFS_BUF_WARN_interrupt(fi); + break; + case IMQ_BUF_WARN: + DPRINTK("IMQ_BUF_WARN message received"); + handle_IMQ_BUF_WARN_interrupt(fi); + break; + case INBOUND_C1_TIMEOUT: + DPRINTK("INBOUND_C1_TIMEOUT message received"); + break; + case BAD_SCSI_FRAME: + DPRINTK("BAD_SCSI_FRAME message received"); + handle_Bad_SCSI_Frame_interrupt(fi); + break; + case INB_SCSI_STATUS_COMPLETION: + DPRINTK("INB_SCSI_STATUS_COMPL message received"); + handle_Inbound_SCSI_Status_interrupt(fi); + break; + case INBOUND_SCSI_COMMAND: + DPRINTK("INBOUND_SCSI_COMMAND message received"); + handle_Inbound_SCSI_Command_interrupt(fi); + break; + case INBOUND_SCSI_DATA_COMPLETION: + DPRINTK("INBOUND_SCSI_DATA message received"); + /* Only for targets */ + break; + default: + T_MSG("DEFAULT message received, type = %x", imq_int_type); + return; + } + reset_latch(fi); +} + +static void handle_OCI_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +u_long transaction_id = 0; +unsigned short status, seq_count, transmitted_ox_id; +struct Scsi_Host *host = fi->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +Scsi_Cmnd *Cmnd; +u_int tag; + + ENTER("handle_OCI_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + transaction_id = ntohl(*(ptr_imq_entry + 1)); + status = ntohl(*(ptr_imq_entry + 2)) >> 16; + seq_count = ntohl(*(ptr_imq_entry + 3)); + DPRINTK("transaction_id= %x", (u_int)transaction_id); + tag = transaction_id & 0xFFFF0000; + transmitted_ox_id = transaction_id; + + /* The INT could be either due to TIME_OUT | BAD_ALPA. + * But we check only for TimeOuts. Bad AL_PA will + * caught by FM_interrupt handler. + */ + + if ((status == OCM_TIMEOUT_OR_BAD_ALPA) && (!fi->g.port_discovery) && (!fi->g.perform_adisc)){ + DPRINTK("Frame TimeOut on OX_ID = %x", (u_int)transaction_id); + + /* Is it a SCSI frame that is timing out ? Not a very good check... + */ + if ((transmitted_ox_id <= MAX_SCSI_OXID) && ((tag == FC_SCSI_BAD_TARGET) || (tag < 0x00FF0000))) { + /* If it is a Bad AL_PA, we report it as BAD_TARGET. + * Else, we allow the command to time-out. A Link + * re-initialization could be taking place. + */ + if (tag == FC_SCSI_BAD_TARGET) { + Cmnd = hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID]; + hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID] = NULL; + if (Cmnd != NULL) { + Cmnd->result = DID_BAD_TARGET << 16; + (*Cmnd->scsi_done) (Cmnd); + } + else + T_MSG("NULL Command out of handler!"); + } /* if Bad Target */ + else { + u_char missing_target = tag >> 16; + struct fc_node_info *q = fi->node_info_list; + /* A Node that we thought was logged in has gone + * away. We are the optimistic kind and we keep + * hoping that our dear little Target will come back + * to us. For now we log him out. + */ + DPRINTK2("Missing Target = %d", missing_target); + while (q != NULL) { + if (q->target_id == missing_target) { + T_MSG("Target %d Logged out", q->target_id); + q->login = LOGIN_ATTEMPTED; + if (fi->num_nodes > 0) + fi->num_nodes--; + tx_logi(fi, ELS_PLOGI, q->d_id); + break; + } + else + q = q->next; + } + } + } /* End of SCSI frame timing out. */ + else { + if (seq_count > 1) { + /* An IP frame was transmitted to a Bad AL_PA. Free up + * the skb used. + */ + dev_kfree_skb((struct sk_buff *)(bus_to_virt(transaction_id))); + } + } /* End of IP frame timing out. */ + } /* End of frame timing out. */ + else { + /* Frame was transmitted successfully. Check if it was an ELS + * frame or an IP frame or a Bad_Target_Notification frame (in + * case of a ptp_link). Ugly! + */ + if ((status == 0) && (seq_count == 0)) { + u_int tag = transaction_id & 0xFFFF0000; + /* Continue with port discovery after an ELS is successfully + * transmitted. (status == 0). + */ + DPRINTK("tag = %x", tag); + switch(tag) { + case ELS_FLOGI: + /* Letz use the Name Server instead */ + fi->g.explore_fabric = TRUE; + fi->g.port_discovery = FALSE; + fi->g.alpa_list_index = MAX_NODES; + add_to_ox_id_list(fi, transaction_id, tag); + break; + case ELS_PLOGI: + if (fi->g.fabric_present && (fi->g.name_server == FALSE)) + add_to_ox_id_list(fi,transaction_id,ELS_NS_PLOGI); + else + add_to_ox_id_list(fi, transaction_id, tag); + break; + case FC_SCSI_BAD_TARGET: + Cmnd = hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID]; + hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID] = NULL; + if (Cmnd != NULL) { + Cmnd->result = DID_BAD_TARGET << 16; + (*Cmnd->scsi_done) (Cmnd); + } + else + T_MSG("NULL Command out of handler!"); + break; + default: + add_to_ox_id_list(fi, transaction_id, tag); + } + + if (fi->g.alpa_list_index >= MAX_NODES) { + if (fi->g.port_discovery == TRUE) { + fi->g.port_discovery = FALSE; + add_display_cache_timer(fi); + } + fi->g.alpa_list_index = MAX_NODES; + } + if (fi->g.port_discovery == TRUE) + local_port_discovery(fi); + } + else { + /* An IP frame has been successfully transmitted. + * Free the skb that was used for this IP frame. + */ + if ((status == 0) && (seq_count > 1)) { + dev_kfree_skb((struct sk_buff *)(bus_to_virt(transaction_id))); + } + } + } + LEAVE("handle_OCI_interrupt"); +} + +/* Right now we discard OOO frames */ +static void handle_OOO_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset, payload_size; +int no_of_buffers = 1; /* header is in a separate buffer */ + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + payload_size = ntohl(*(ptr_imq_entry + 2)) - TACHYON_HEADER_LEN; + /* Calculate total number of buffers */ + no_of_buffers += payload_size / MFS_BUFFER_SIZE; + if (payload_size % MFS_BUFFER_SIZE) + no_of_buffers++; + + /* provide Tachyon will another set of buffers */ + fi->g.mfs_buffer_count += no_of_buffers; + if (fi->g.mfs_buffer_count >= NO_OF_ENTRIES) { + int count = fi->g.mfs_buffer_count / NO_OF_ENTRIES; + fi->g.mfs_buffer_count -= NO_OF_ENTRIES * count; + update_MFSBQ_indx(fi, count); + } +} + +static void handle_MFS_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry, *buff_addr; +u_int type_of_frame, s_id; +int queue_indx, offset, payload_size, starting_indx, starting_offset; +u_short received_ox_id; +int no_of_buffers = 1; /* header is in a separate buffer */ +struct sk_buff *skb; +int wrap_around = FALSE, no_of_wrap_buffs = NO_OF_ENTRIES - 1; + ENTER("handle_MFS_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + DPRINTK("queue_indx = %d, offset = %d\n", queue_indx, offset); + payload_size = ntohl(*(ptr_imq_entry + 2)) - TACHYON_HEADER_LEN; + DPRINTK("payload_size = %d", payload_size); + /* Calculate total number of buffers */ + no_of_buffers += payload_size / MFS_BUFFER_SIZE; + if (payload_size % MFS_BUFFER_SIZE) + no_of_buffers++; + DPRINTK("no_of_buffers = %d", no_of_buffers); + + if ((no_of_buffers - 1) <= offset) { + starting_offset = offset - (no_of_buffers - 1); + starting_indx = queue_indx; + } + else { + int temp = no_of_buffers - (offset + 1); + int no_of_queues = temp / NO_OF_ENTRIES; + starting_offset = temp % NO_OF_ENTRIES; + if (starting_offset != 0) { + no_of_wrap_buffs = starting_offset - 1; //exclude header + starting_offset = NO_OF_ENTRIES - starting_offset; + no_of_queues++; + } + starting_indx = queue_indx - no_of_queues; + if (starting_indx < 0) { + no_of_wrap_buffs -= (starting_indx + 1) * NO_OF_ENTRIES; + starting_indx = MFSBQ_LENGTH + starting_indx; + wrap_around = TRUE; + } + } + + DPRINTK("starting_indx = %d, starting offset = %d no_of_wrap_buffs = %d\n", starting_indx, starting_offset, no_of_wrap_buffs); + /* Get Tachyon Header from first buffer */ + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_mfsbq_base + starting_indx*NO_OF_ENTRIES + starting_offset))); + + + /* extract Type of Frame */ + type_of_frame = (u_int)ntohl(*(buff_addr + 4)) & 0xFF000000; + s_id = (u_int)ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + received_ox_id = ntohl(*(buff_addr + 6)) >> 16; + buff_addr += MFS_BUFFER_SIZE/4; + DPRINTK("type_of_frame = %x, s_id = %x, ox_id = %x", type_of_frame, s_id, received_ox_id); + + switch(type_of_frame) { + case TYPE_LLC_SNAP: + skb = dev_alloc_skb(payload_size); + if (skb == NULL) { + printk(KERN_NOTICE "%s: In handle_MFS_interrupt() Memory squeeze, dropping packet.\n", fi->name); + fi->fc_stats.rx_dropped++; + fi->g.mfs_buffer_count += no_of_buffers; + if (fi->g.mfs_buffer_count >= NO_OF_ENTRIES) { + int count = fi->g.mfs_buffer_count / NO_OF_ENTRIES; + fi->g.mfs_buffer_count -= NO_OF_ENTRIES * count; + update_MFSBQ_indx(fi, count); + return; + } + } + if (wrap_around) { + int wrap_size = no_of_wrap_buffs * MFS_BUFFER_SIZE; + int tail_size = payload_size - wrap_size; + DPRINTK("wrap_size = %d, tail_size = %d\n", wrap_size, tail_size); + if (no_of_wrap_buffs) + memcpy(skb_put(skb, wrap_size), buff_addr, wrap_size); + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_mfsbq_base))); + memcpy(skb_put(skb, tail_size), buff_addr, tail_size); + } + else + memcpy(skb_put(skb, payload_size), buff_addr, payload_size); + rx_net_mfs_packet(fi, skb); + break; + default: + T_MSG("Unknown Frame Type received. Type = %x", type_of_frame); + } + + /* provide Tachyon will another set of buffers */ + fi->g.mfs_buffer_count += no_of_buffers; + if (fi->g.mfs_buffer_count >= NO_OF_ENTRIES) { + int count = fi->g.mfs_buffer_count / NO_OF_ENTRIES; + fi->g.mfs_buffer_count -= NO_OF_ENTRIES * count; + update_MFSBQ_indx(fi, count); + } + LEAVE("handle_MFS_interrupt"); +} + +static void handle_Unknown_Frame_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset; + ENTER("handle_Unknown_Frame_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + /* We discard the "unknown" frame */ + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Unknown_Frame_interrupt"); +} + +static void handle_Busied_Frame_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset; + ENTER("handle_Busied_Frame_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + /* We discard the "busied" frame */ + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Busied_Frame_interrupt"); +} + +static void handle_Bad_SCSI_Frame_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry, *buff_addr, *tach_header, *ptr_edb; +u_int s_id, rctl, frame_class, burst_len, transfered_len, len = 0; +int queue_indx, offset, payload_size, i; +u_short ox_id, rx_id, x_id, mtu = 512; +u_char target_id = 0xFF; + + ENTER("handle_Bad_SCSI_Frame_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + payload_size = ntohl(*(ptr_imq_entry + 2)); + + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + + rctl = ntohl(*(buff_addr + 2)) & 0xFF000000; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + ox_id = ntohl(*(buff_addr + 6)) >> 16; + rx_id = ntohl(*(buff_addr + 6)); + x_id = ox_id & MAX_SCSI_XID; + + /* Any frame that comes in with OX_ID that matches an OX_ID + * that has been allocated for SCSI, will be called a Bad + * SCSI frame if the Exchange is not valid any more. + * + * We will also get a Bad SCSI frame interrupt if we receive + * a XFER_RDY with offset != 0. Tachyon washes its hands off + * this Exchange. We have to take care of ourselves. Grrr... + */ + if (rctl == DATA_DESCRIPTOR) { + struct fc_node_info *q = fi->node_info_list; + while (q != NULL) { + if (q->d_id == s_id) { + target_id = q->target_id; + mtu = q->mtu; + break; + } + else + q = q->next; + } + frame_class = target_id; + transfered_len = ntohl(*(buff_addr + 8)); + burst_len = ntohl(*(buff_addr + 9)); + + build_ODB(fi, fi->g.seq_id, s_id, burst_len, 0, mtu, ox_id, rx_id, 0, 0, frame_class << 16); + /* Update the SEQ_ID and Relative Offset in the + * Tachyon Header Structure. + */ + tach_header = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 5))); + *(tach_header + 5) = htonl(fi->g.seq_id << 24); + *(tach_header + 7) = htonl(transfered_len); + fi->g.odb.hdr_addr = *(fi->q.ptr_sest[x_id] + 5); + + /* Invalidate the EDBs used + */ + ptr_edb = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 7))); + + for (i = 0; i < EDB_LEN; i++) + if (fi->q.ptr_edb[i] == ptr_edb) + break; + ptr_edb--; + + if (i < EDB_LEN) { + int j; + do { + ptr_edb += 2; + len += (htonl(*ptr_edb) & 0xFFFF); + j = i; + fi->q.free_edb_list[i++] = EDB_FREE; + if (i == EDB_LEN) { + i = 0; + ptr_edb = fi->q.ptr_edb_base - 1; + } + } while (len < transfered_len); + if (len > transfered_len) { + ptr_edb--; + fi->q.free_edb_list[j] = EDB_BUSY; + } + else + ptr_edb++; + } + else { + T_MSG("EDB not found while freeing"); + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + return; + } + + /* Update the EDB pointer in the ODB. + */ + fi->g.odb.edb_addr = htonl(virt_to_bus(ptr_edb)); + memcpy(fi->q.ptr_odb[fi->q.ocq_prod_indx], &(fi->g.odb), sizeof(ODB)); + /* Update the EDB pointer in the SEST entry. We might need + * this if get another XFER_RDY for the same Exchange. + */ + *(fi->q.ptr_sest[x_id] + 7) = htonl(virt_to_bus(ptr_edb)); + + update_OCQ_indx(fi); + if (fi->g.seq_id == MAX_SEQ_ID) + fi->g.seq_id = 0; + else + fi->g.seq_id++; + } + else + /* Could be a BA_ACC or a BA_RJT. + */ + if (rctl == RCTL_BASIC_ACC) { + u_int bls_type = remove_from_ox_id_list(fi, ox_id); + DPRINTK1("BA_ACC received from S_ID 0x%x with OX_ID = %x in response to %x", s_id, ox_id, bls_type); + if (bls_type == RCTL_BASIC_ABTS) { + u_int STE_bit; + /* Invalidate resources for that Exchange. + */ + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + if (STE_bit & SEST_V) { + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + invalidate_SEST_entry(fi, ox_id); + } + } + } + else + if (rctl == RCTL_BASIC_RJT) { + u_int bls_type = remove_from_ox_id_list(fi, ox_id); + DPRINTK1("BA_RJT received from S_ID 0x%x with OX_ID = %x in response to %x", s_id, ox_id, bls_type); + if (bls_type == RCTL_BASIC_ABTS) { + u_int STE_bit; + /* Invalidate resources for that Exchange. + */ + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + if (STE_bit & SEST_V) { + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + invalidate_SEST_entry(fi, ox_id); + } + } + } + else + DPRINTK1("Frame with R_CTL = %x received from S_ID 0x%x with OX_ID %x", rctl, s_id, ox_id); + + /* Else, discard the "Bad" SCSI frame. + */ + + /* provide Tachyon will another set of buffers + */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Bad_SCSI_Frame_interrupt"); +} + +static void handle_Inbound_SCSI_Status_interrupt(struct fc_info *fi) +{ +struct Scsi_Host *host = fi->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +u_int *ptr_imq_entry, *buff_addr, *ptr_rsp_info, *ptr_sense_info = NULL; +int queue_indx, offset, payload_size; +u_short received_ox_id, x_id; +Scsi_Cmnd *Cmnd; +u_int fcp_status, fcp_rsp_info_len = 0, fcp_sense_info_len = 0, s_id; + ENTER("handle_SCSI_status_interrupt"); + + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + payload_size = ntohl(*(ptr_imq_entry + 2)); + received_ox_id = ntohl(*(buff_addr + 6)) >> 16; + + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + + fcp_status = ntohl(*(buff_addr + 10)); + ptr_rsp_info = buff_addr + 14; + if (fcp_status & FCP_STATUS_RSP_LEN) + fcp_rsp_info_len = ntohl(*(buff_addr + 13)); + + if (fcp_status & FCP_STATUS_SENSE_LEN) { + ptr_sense_info = ptr_rsp_info + fcp_rsp_info_len / 4; + fcp_sense_info_len = ntohl(*(buff_addr + 12)); + DPRINTK("sense_info = %x", (u_int)ntohl(*ptr_sense_info)); + } + DPRINTK("fcp_status = %x, fcp_rsp_len = %x", fcp_status, fcp_rsp_info_len); + x_id = received_ox_id & MAX_SCSI_XID; + Cmnd = hostdata->cmnd_handler[x_id]; + hostdata->cmnd_handler[x_id] = NULL; + if (Cmnd != NULL) { + memset(Cmnd->sense_buffer, 0, sizeof(Cmnd->sense_buffer)); + /* Check if there is a Sense field */ + if (fcp_status & FCP_STATUS_SENSE_LEN) { + int size = sizeof(Cmnd->sense_buffer); + if (fcp_sense_info_len < size) + size = fcp_sense_info_len; + memcpy(Cmnd->sense_buffer, (char *)ptr_sense_info, size); + } + Cmnd->result = fcp_status & FCP_STATUS_MASK; + (*Cmnd->scsi_done) (Cmnd); + } + else + T_MSG("NULL Command out of handler!"); + + invalidate_SEST_entry(fi, received_ox_id); + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + fi->q.free_scsi_oxid[x_id] = OXID_AVAILABLE; + + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_SCSI_status_interrupt"); +} + +static void invalidate_SEST_entry(struct fc_info *fi, u_short received_ox_id) +{ +u_short x_id = received_ox_id & MAX_SCSI_XID; + /* Invalidate SEST entry if it is an OutBound SEST Entry + */ + if (!(received_ox_id & SCSI_READ_BIT)) { + u_int *ptr_tach_header, *ptr_edb; + u_short temp_ox_id = NOT_SCSI_XID; + int i; + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + + /* Invalidate the Tachyon Header structure + */ + ptr_tach_header = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 5))); + for (i = 0; i < NO_OF_TACH_HEADERS; i++) + if(fi->q.ptr_tachyon_header[i] == ptr_tach_header) + break; + if (i < NO_OF_TACH_HEADERS) + memset(ptr_tach_header, 0xFF, 32); + else + T_MSG("Tachyon Header not found while freeing in invalidate_SEST_entry()"); + + /* Invalidate the EDB used + */ + ptr_edb = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 7))); + for (i = 0; i < EDB_LEN; i++) + if (fi->q.ptr_edb[i] == ptr_edb) + break; + ptr_edb--; + if (i < EDB_LEN) { + do { + ptr_edb += 2; + fi->q.free_edb_list[i++] = EDB_FREE; + if (i == EDB_LEN) { + i = 0; + ptr_edb = fi->q.ptr_edb_base - 1; + } + } while ((htonl(*ptr_edb) & 0x80000000) != 0x80000000); + } + else + T_MSG("EDB not found while freeing in invalidate_SEST_entry()"); + + /* Search for its other header structure and destroy it! + */ + if ((ptr_tach_header + 16) < (fi->q.ptr_tachyon_header_base + (MY_PAGE_SIZE/4))) + ptr_tach_header += 16; + else + ptr_tach_header = fi->q.ptr_tachyon_header_base; + while (temp_ox_id != x_id) { + temp_ox_id = ntohl(*(ptr_tach_header + 6)) >> 16; + if (temp_ox_id == x_id) { + /* Paranoid checking... + */ + for (i = 0; i < NO_OF_TACH_HEADERS; i++) + if(fi->q.ptr_tachyon_header[i] == ptr_tach_header) + break; + if (i < NO_OF_TACH_HEADERS) + memset(ptr_tach_header, 0xFF, 32); + else + T_MSG("Tachyon Header not found while freeing in invalidate_SEST_entry()"); + break; + } + else { + if ((ptr_tach_header + 16) < (fi->q.ptr_tachyon_header_base + (MY_PAGE_SIZE/4))) + ptr_tach_header += 16; + else + ptr_tach_header = fi->q.ptr_tachyon_header_base; + } + } + } + else { + u_short sdb_table_indx; + /* An Inbound Command has completed or needs to be Aborted. + * Clear up the SDB buffers. + */ + sdb_table_indx = *(fi->q.ptr_sest[x_id] + 5); + fi->q.sdb_slot_status[sdb_table_indx] = SDB_FREE; + } +} + +static void handle_Inbound_SCSI_Command_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset; + ENTER("handle_Inbound_SCSI_Command_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + /* We discard the SCSI frame as we shouldn't be receiving + * a SCSI Command in the first place + */ + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Inbound_SCSI_Command_interrupt"); +} + +static void handle_SFS_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry, *buff_addr; +u_int class_of_frame, type_of_frame, s_id, els_type = 0, rctl; +int queue_indx, offset, payload_size, login_state; +u_short received_ox_id, fs_cmnd_code; + ENTER("handle_SFS_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + DPRINTK("queue_indx = %d, offset = %d\n", queue_indx, offset); + payload_size = ntohl(*(ptr_imq_entry + 2)); + DPRINTK("payload_size = %d", payload_size); + + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + + /* extract Type of Frame */ + type_of_frame = ntohl(*(buff_addr + 4)) & 0xFF000000; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + received_ox_id = ntohl(*(buff_addr + 6)) >> 16; + switch(type_of_frame) { + case TYPE_BLS: + rctl = ntohl(*(buff_addr + 2)) & 0xFF000000; + switch(rctl) { + case RCTL_BASIC_ABTS: + /* As an Initiator, we should never be receiving + * this. + */ + DPRINTK1("ABTS received from S_ID 0x%x with OX_ID = %x", s_id, received_ox_id); + break; + } + break; + case TYPE_ELS: + class_of_frame = ntohl(*(buff_addr + 8)); + login_state = sid_logged_in(fi, s_id); + switch(class_of_frame & 0xFF000000) { + case ELS_PLOGI: + if (s_id != fi->g.my_id) { + u_int ret_code; + DPRINTK1("PLOGI received from D_ID 0x%x with 0X_ID = %x", s_id, received_ox_id); + if ((ret_code = plogi_ok(fi, buff_addr, payload_size)) == 0){ + tx_logi_acc(fi, ELS_ACC, s_id, received_ox_id); + add_to_address_cache(fi, buff_addr); + } + else { + u_short cmnd_code = ret_code >> 16; + u_short expln_code = ret_code; + tx_ls_rjt(fi, s_id, received_ox_id, cmnd_code, expln_code); + } + } + break; + case ELS_ACC: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("ELS_ACC received from D_ID 0x%x in response to ELS %x", s_id, els_type); + switch(els_type) { + case ELS_PLOGI: + add_to_address_cache(fi, buff_addr); + tx_prli(fi, ELS_PRLI, s_id, OX_ID_FIRST_SEQUENCE); + break; + case ELS_FLOGI: + add_to_address_cache(fi, buff_addr); + fi->g.my_id = ntohl(*(buff_addr + 2)) & 0x00FFFFFF; + fi->g.fabric_present = TRUE; + fi->g.my_ddaa = fi->g.my_id & 0xFFFF00; + /* Login to the Name Server + */ + tx_logi(fi, ELS_PLOGI, DIRECTORY_SERVER); + break; + case ELS_NS_PLOGI: + fi->g.name_server = TRUE; + add_to_address_cache(fi, buff_addr); + tx_name_server_req(fi, FCS_RFC_4); + tx_scr(fi); + /* Some devices have a delay before + * registering with the Name Server + */ + udelay(500); + tx_name_server_req(fi, FCS_GP_ID4); + break; + case ELS_PRLI: + mark_scsi_sid(fi, buff_addr, ADD_ENTRY); + break; + case ELS_ADISC: + if (!(validate_login(fi, buff_addr))) + tx_logo(fi, s_id, OX_ID_FIRST_SEQUENCE); + break; + } + break; + case ELS_PDISC: + DPRINTK1("ELS_PDISC received from D_ID 0x%x", s_id); + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ADISC: + DPRINTK1("ELS_ADISC received from D_ID 0x%x", s_id); + if (node_logged_in_prev(fi, buff_addr)) + tx_adisc(fi, ELS_ACC, s_id, received_ox_id); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_PRLI: + DPRINTK1("ELS_PRLI received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) { + tx_prli(fi, ELS_ACC, s_id, received_ox_id); + mark_scsi_sid(fi, buff_addr, ADD_ENTRY); + } + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_PRLO: + DPRINTK1("ELS_PRLO received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_OUT) || (login_state == NODE_NOT_PRESENT)) + tx_logo(fi, s_id, received_ox_id); + else + if (login_state == NODE_LOGGED_IN) + + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + if (login_state == NODE_PROCESS_LOGGED_IN) { + tx_prli(fi, ELS_ACC, s_id, received_ox_id); + mark_scsi_sid(fi, buff_addr, DELETE_ENTRY); + } + break; + case ELS_LS_RJT: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("ELS_LS_RJT received from D_ID 0x%x in response to %x", s_id, els_type); + /* We should be chking the reason code. + */ + switch (els_type) { + case ELS_ADISC: + tx_logi(fi, ELS_PLOGI, s_id); + break; + } + break; + case ELS_LOGO: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("ELS_LOGO received from D_ID 0x%x in response to %x", s_id, els_type); + remove_from_address_cache(fi, buff_addr, ELS_LOGO); + tx_acc(fi, s_id, received_ox_id); + if (els_type == ELS_ADISC) + tx_logi(fi, ELS_PLOGI, s_id); + break; + case ELS_RSCN: + DPRINTK1("ELS_RSCN received from D_ID 0x%x", s_id); + tx_acc(fi, s_id, received_ox_id); + remove_from_address_cache(fi, buff_addr, ELS_RSCN); + break; + case ELS_FARP_REQ: + /* We do not support FARP. + So, silently discard it */ + DPRINTK1("ELS_FARP_REQ received from D_ID 0x%x", s_id); + break; + case ELS_ABTX: + DPRINTK1("ELS_ABTX received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FLOGI: + DPRINTK1("ELS_FLOGI received from D_ID 0x%x", s_id); + if (fi->g.ptp_up == TRUE) { + /* The node could have come up as an N_Port + * in a Loop! So,try initializing as an NL_port + */ + take_tachyon_offline(fi); + /* write AL_TIME & E_D_TOV into the registers */ + writel(TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + writel(LOOP_INIT_SOFT_ADDRESS, fi->t_r.ptr_fm_config_reg); + DPRINTK1("FLOGI received, TACHYON initializing as L_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + else { + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + } + break; + case ELS_ADVC: + DPRINTK1("ELS_ADVC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ECHO: + DPRINTK1("ELS_ECHO received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ESTC: + DPRINTK1("ELS_ESTC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ESTS: + DPRINTK1("ELS_ESTS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RCS: + DPRINTK1("ELS_RCS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RES: + DPRINTK1("ELS_RES received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RLS: + DPRINTK1("ELS_RLS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RRQ: + DPRINTK1("ELS_RRQ received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RSS: + DPRINTK1("ELS_RSS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RTV: + DPRINTK1("ELS_RTV received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RSI: + DPRINTK1("ELS_RSI received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_TEST: + /* No reply sequence */ + DPRINTK1("ELS_TEST received from D_ID 0x%x", s_id); + break; + case ELS_RNC: + DPRINTK1("ELS_RNC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RVCS: + DPRINTK1("ELS_RVCS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_TPLS: + DPRINTK1("ELS_TPLS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_GAID: + DPRINTK1("ELS_GAID received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FACT: + DPRINTK1("ELS_FACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FAN: + /* Hmmm... You don't support FAN ??? */ + DPRINTK1("ELS_FAN received from D_ID 0x%x", s_id); + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + break; + case ELS_FDACT: + DPRINTK1("ELS_FDACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_NACT: + DPRINTK1("ELS_NACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_NDACT: + DPRINTK1("ELS_NDACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_QoSR: + DPRINTK1("ELS_QoSR received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FDISC: + DPRINTK1("ELS_FDISC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + default: + DPRINTK1("ELS Frame %x received from D_ID 0x%x", class_of_frame, s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + } + break; + case TYPE_FC_SERVICES: + fs_cmnd_code = (ntohl(*(buff_addr + 10)) & 0xFFFF0000) >>16; + switch(fs_cmnd_code) { + case FCS_ACC: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("FCS_ACC received from D_ID 0x%x in response to %x", s_id, els_type); + if (els_type == FCS_GP_ID4) + explore_fabric(fi, buff_addr); + break; + case FCS_REJECT: + DPRINTK1("FCS_REJECT received from D_ID 0x%x in response to %x", s_id, els_type); + break; + } + break; + case TYPE_LLC_SNAP: + rx_net_packet(fi, (u_char *)buff_addr, payload_size); + break; + default: + T_MSG("Frame Type %x received from %x", type_of_frame, s_id); + } + + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_SFS_interrupt"); +} + +static void handle_FM_interrupt(struct fc_info *fi) +{ +u_int fm_status; +u_int tachyon_status; + + ENTER("handle_FM_interrupt"); + fm_status = readl(fi->t_r.ptr_fm_status_reg); + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + DPRINTK("FM_status = %x, Tachyon_status = %x", fm_status, tachyon_status); + if (fm_status & LINK_DOWN) { + T_MSG("Fibre Channel Link DOWN"); + fm_status = readl(fi->t_r.ptr_fm_status_reg); + + del_timer(&fi->explore_timer); + del_timer(&fi->nport_timer); + del_timer(&fi->lport_timer); + del_timer(&fi->display_cache_timer); + fi->g.link_up = FALSE; + if (fi->g.ptp_up == TRUE) + fi->g.n_port_try = FALSE; + fi->g.ptp_up = FALSE; + fi->g.port_discovery = FALSE; + fi->g.explore_fabric = FALSE; + fi->g.perform_adisc = FALSE; + + /* Logout will all nodes */ + if (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + while(temp_list) { + temp_list->login = LOGIN_ATTEMPTED; + temp_list = temp_list->next; + } + fi->num_nodes = 0; + } + + if ((fi->g.n_port_try == FALSE) && (fi->g.dont_init == FALSE)){ + take_tachyon_offline(fi); + /* write AL_TIME & E_D_TOV into the registers */ + writel(TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + + if ((fi->g.fabric_present == TRUE) && (fi->g.loop_up == TRUE)) { + u_int al_pa = fi->g.my_id & 0xFF; + writel((al_pa << 24) | LOOP_INIT_FABRIC_ADDRESS | LOOP_INIT_PREVIOUS_ADDRESS, fi->t_r.ptr_fm_config_reg); + } + else + if (fi->g.loop_up == TRUE) { + u_int al_pa = fi->g.my_id & 0xFF; + writel((al_pa << 24) | LOOP_INIT_PREVIOUS_ADDRESS, fi->t_r.ptr_fm_config_reg); + } + else + writel(LOOP_INIT_SOFT_ADDRESS, fi->t_r.ptr_fm_config_reg); + fi->g.loop_up = FALSE; + DPRINTK1("In LDWN TACHYON initializing as L_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + } + + if (fm_status & NON_PARTICIPATING) { + T_MSG("Did not acquire an AL_PA. I am not participating"); + } + else + if ((fm_status & LINK_UP) && ((fm_status & LINK_DOWN) == 0)) { + T_MSG("Fibre Channel Link UP"); + if ((fm_status & NON_PARTICIPATING) != TRUE) { + fi->g.link_up = TRUE; + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + } + init_timer(&fi->explore_timer); + init_timer(&fi->nport_timer); + init_timer(&fi->lport_timer); + init_timer(&fi->display_cache_timer); + if ((fm_status & OLD_PORT) == 0) { + fi->g.loop_up = TRUE; + fi->g.ptp_up = FALSE; + fi->g.my_id = readl(fi->t_r.ptr_fm_config_reg) >> 24; + DPRINTK1("My AL_PA = %x", fi->g.my_id); + fi->g.port_discovery = TRUE; + fi->g.explore_fabric = FALSE; + } + else + if (((fm_status & 0xF0) == OLD_PORT) && ((fm_status & 0x0F) == PORT_STATE_ACTIVE)) { + fi->g.loop_up = FALSE; + fi->g.my_id = 0x0; + /* In a point-to-point configuration, we expect to be + * connected to an F_Port. This driver does not yet support + * a configuration where it is connected to another N_Port + * directly. + */ + fi->g.explore_fabric = TRUE; + fi->g.port_discovery = FALSE; + if (fi->g.n_port_try == FALSE) { + take_tachyon_offline(fi); + /* write R_T_TOV & E_D_TOV into the registers */ + writel(PTP_TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + writel(BB_CREDIT | NPORT, fi->t_r.ptr_fm_config_reg); + fi->g.n_port_try = TRUE; + DPRINTK1("In LUP TACHYON initializing as N_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + else { + fi->g.ptp_up = TRUE; + tx_logi(fi, ELS_FLOGI, F_PORT); + } + } + fi->g.my_ddaa = 0x0; + fi->g.fabric_present = FALSE; + /* We havn't sent out any Name Server Reqs */ + fi->g.name_server = FALSE; + fi->g.alpa_list_index = 0; + fi->g.ox_id = NOT_SCSI_XID; + fi->g.my_mtu = FRAME_SIZE; + + /* Implicitly LOGO with all logged-in nodes. + */ + if (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + while(temp_list) { + temp_list->login = LOGIN_ATTEMPTED; + temp_list = temp_list->next; + } + fi->num_nodes = 0; + fi->g.perform_adisc = TRUE; + //fi->g.perform_adisc = FALSE; + fi->g.port_discovery = FALSE; + tx_logi(fi, ELS_FLOGI, F_PORT); + } + else { + /* If Link coming up for the _first_ time or no nodes + * were logged in before... + */ + fi->g.scsi_oxid = 0; + fi->g.seq_id = 0x00; + fi->g.perform_adisc = FALSE; + } + + /* reset OX_ID table */ + while (fi->ox_id_list) { + struct ox_id_els_map *temp = fi->ox_id_list; + fi->ox_id_list = fi->ox_id_list->next; + kfree(temp); + } + fi->ox_id_list = NULL; + } /* End of if partipating */ + } + + if (fm_status & ELASTIC_STORE_ERROR) { + /* Too much junk on the Link + */ + /* Trying to clear it up by Txing PLOGI to urself */ + if (fi->g.link_up == TRUE) + tx_logi(fi, ELS_PLOGI, fi->g.my_id); + } + + if (fm_status & LOOP_UP) { + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + } + } + + if (fm_status & NOS_OLS_RECEIVED){ + if (fi->g.nport_timer_set == FALSE) { + DPRINTK("NOS/OLS Received"); + DPRINTK("FM_status = %x", fm_status); + fi->nport_timer.function = nos_ols_timer; + fi->nport_timer.data = (unsigned long)fi; + fi->nport_timer.expires = RUN_AT((3*HZ)/100); /* 30 msec */ + init_timer(&fi->nport_timer); + add_timer(&fi->nport_timer); + fi->g.nport_timer_set = TRUE; + } + } + + if (((fm_status & 0xF0) == OLD_PORT) && (((fm_status & 0x0F) == PORT_STATE_LF1) || ((fm_status & 0x0F) == PORT_STATE_LF2))) { + DPRINTK1("Link Fail-I in OLD-PORT."); + take_tachyon_offline(fi); + reset_tachyon(fi, SOFTWARE_RESET); + } + + if (fm_status & LOOP_STATE_TIMEOUT){ + if ((fm_status & 0xF0) == ARBITRATING) + DPRINTK1("ED_TOV timesout.In ARBITRATING state..."); + if ((fm_status & 0xF0) == ARB_WON) + DPRINTK1("ED_TOV timesout.In ARBITRATION WON state..."); + if ((fm_status & 0xF0) == OPEN) + DPRINTK1("ED_TOV timesout.In OPEN state..."); + if ((fm_status & 0xF0) == OPENED) + DPRINTK1("ED_TOV timesout.In OPENED state..."); + if ((fm_status & 0xF0) == TX_CLS) + DPRINTK1("ED_TOV timesout.In XMITTED CLOSE state..."); + if ((fm_status & 0xF0) == RX_CLS) + DPRINTK1("ED_TOV timesout.In RECEIVED CLOSE state..."); + if ((fm_status & 0xF0) == INITIALIZING) + DPRINTK1("ED_TOV timesout.In INITIALIZING state..."); + DPRINTK1("Initializing Loop..."); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + + if ((fm_status & BAD_ALPA) && (fi->g.loop_up == TRUE)) { + u_char bad_alpa = (readl(fi->t_r.ptr_fm_rx_al_pa_reg) & 0xFF00) >> 8; + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + } + /* Fix for B34 */ + tx_logi(fi, ELS_PLOGI, fi->g.my_id); + + if (!fi->g.port_discovery && !fi->g.perform_adisc) { + if (bad_alpa != 0xFE) + DPRINTK("Bad AL_PA = %x", bad_alpa); + } + else { + if ((fi->g.perform_adisc == TRUE) && (bad_alpa == 0x00)) { + DPRINTK1("Performing ADISC..."); + fi->g.fabric_present = FALSE; + perform_adisc(fi); + } + } + } + + if (fm_status & LIPF_RECEIVED){ + DPRINTK("LIP(F8) Received"); + } + + if (fm_status & LINK_FAILURE) { + if (fm_status & LOSS_OF_SIGNAL) + DPRINTK1("Detected Loss of Signal."); + if (fm_status & OUT_OF_SYNC) + DPRINTK1("Detected Loss of Synchronization."); + } + + if (fm_status & TRANSMIT_PARITY_ERROR) { + /* Bad! Should not happen. Solution-> Hard Reset. + */ + T_MSG("Parity Error. Perform Hard Reset!"); + } + + if (fi->g.alpa_list_index >= MAX_NODES){ + if (fi->g.port_discovery == TRUE) { + fi->g.port_discovery = FALSE; + add_display_cache_timer(fi); + } + fi->g.alpa_list_index = MAX_NODES; + } + + if (fi->g.port_discovery == TRUE) + local_port_discovery(fi); + + LEAVE("handle_FM_interrupt"); + return; +} + +static void local_port_discovery(struct fc_info *fi) +{ + if (fi->g.loop_up == TRUE) { + /* If this is not here, some of the Bad AL_PAs are missed. + */ + udelay(20); + if ((fi->g.alpa_list_index == 0) && (fi->g.fabric_present == FALSE)){ + tx_logi(fi, ELS_FLOGI, F_PORT); + } + else { + int login_state = sid_logged_in(fi, fi->g.my_ddaa | alpa_list[fi->g.alpa_list_index]); + while ((fi->g.alpa_list_index == 0) || ((fi->g.alpa_list_index < MAX_NODES) && ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN) || (alpa_list[fi->g.alpa_list_index] == (fi->g.my_id & 0xFF))))) + fi->g.alpa_list_index++; + if (fi->g.alpa_list_index < MAX_NODES) + tx_logi(fi, ELS_PLOGI, alpa_list[fi->g.alpa_list_index]); + } + fi->g.alpa_list_index++; + if (fi->g.alpa_list_index >= MAX_NODES){ + if (fi->g.port_discovery == TRUE) { + fi->g.port_discovery = FALSE; + add_display_cache_timer(fi); + } + fi->g.alpa_list_index = MAX_NODES; + } + } +} + +static void nos_ols_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; +u_int fm_status; + fm_status = readl(fi->t_r.ptr_fm_status_reg); + DPRINTK1("FM_status in timer= %x", fm_status); + fi->g.nport_timer_set = FALSE; + del_timer(&fi->nport_timer); + if ((fi->g.ptp_up == TRUE) || (fi->g.loop_up == TRUE)) + return; + if (((fm_status & 0xF0) == OLD_PORT) && (((fm_status & 0x0F) == PORT_STATE_ACTIVE) || ((fm_status & 0x0F) == PORT_STATE_OFFLINE))) { + DPRINTK1("In OLD-PORT after E_D_TOV."); + take_tachyon_offline(fi); + /* write R_T_TOV & E_D_TOV into the registers */ + writel(PTP_TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + writel(BB_CREDIT | NPORT, fi->t_r.ptr_fm_config_reg); + fi->g.n_port_try = TRUE; + DPRINTK1("In timer, TACHYON initializing as N_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + else + if ((fi->g.lport_timer_set == FALSE) && ((fm_status & 0xF0) == LOOP_FAIL)) { + DPRINTK1("Loop Fail after E_D_TOV."); + fi->lport_timer.function = loop_timer; + fi->lport_timer.data = (unsigned long)fi; + fi->lport_timer.expires = RUN_AT((8*HZ)/100); + init_timer(&fi->lport_timer); + add_timer(&fi->lport_timer); + fi->g.lport_timer_set = TRUE; + take_tachyon_offline(fi); + reset_tachyon(fi, SOFTWARE_RESET); + } + else + if (((fm_status & 0xF0) == OLD_PORT) && (((fm_status & 0x0F) == PORT_STATE_LF1) || ((fm_status & 0x0F) == PORT_STATE_LF2))) { + DPRINTK1("Link Fail-II in OLD-PORT."); + take_tachyon_offline(fi); + reset_tachyon(fi, SOFTWARE_RESET); + } +} + +static void loop_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + fi->g.lport_timer_set = FALSE; + del_timer(&fi->lport_timer); + if ((fi->g.ptp_up == TRUE) || (fi->g.loop_up == TRUE)) + return; +} + +static void add_display_cache_timer(struct fc_info *fi) +{ + fi->display_cache_timer.function = display_cache_timer; + fi->display_cache_timer.data = (unsigned long)fi; + fi->display_cache_timer.expires = RUN_AT(fi->num_nodes * HZ); + init_timer(&fi->display_cache_timer); + add_timer(&fi->display_cache_timer); +} + +static void display_cache_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + del_timer(&fi->display_cache_timer); + display_cache(fi); + return; +} + +static void reset_tachyon(struct fc_info *fi, u_int value) +{ +u_int tachyon_status, reset_done = OCQ_RESET_STATUS | SCSI_FREEZE_STATUS; +int not_done = 1, i = 0; + writel(value, fi->t_r.ptr_tach_control_reg); + if (value == OCQ_RESET) + fi->q.ocq_prod_indx = 0; + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + + /* Software resets are immediately done, whereas other aren't. It + about 30 clocks to do the reset */ + if (value != SOFTWARE_RESET) { + while(not_done) { + if (i++ > 100000) { + T_MSG("Reset was unsuccessful! Tachyon Status = %x", tachyon_status); + break; + } + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + if ((tachyon_status & reset_done) == 0) + not_done = 0; + } + } + else { + write_to_tachyon_registers(fi); + } +} + +static void take_tachyon_offline(struct fc_info *fi) +{ +u_int fm_status = readl(fi->t_r.ptr_fm_status_reg); + + /* The first two conditions will never be true. The Manual and + * the errata say this. But the current implementation is + * decently stable. + */ + //if ((fm_status & 0xF0) == LOOP_FAIL) { + if (fm_status == LOOP_FAIL) { + // workaround as in P. 89 + writel(HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + if (fi->g.loop_up == TRUE) + writel(SOFTWARE_RESET, fi->t_r.ptr_tach_control_reg); + else { + writel(OFFLINE, fi->t_r.ptr_fm_control_reg); + writel(EXIT_HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + } + } + else + //if ((fm_status & LOOP_UP) == LOOP_UP) { + if (fm_status == LOOP_UP) { + writel(SOFTWARE_RESET, fi->t_r.ptr_tach_control_reg); + } + else + writel(OFFLINE, fi->t_r.ptr_fm_control_reg); +} + + +static void read_novram(struct fc_info *fi) +{ +int off = 0; + fi->n_r.ptr_novram_hw_control_reg = fi->i_r.ptr_ichip_hw_control_reg; + fi->n_r.ptr_novram_hw_status_reg = fi->i_r.ptr_ichip_hw_status_reg; + iph5526_nr_do_init(fi); + if (fi->clone_id == PCI_VENDOR_ID_INTERPHASE) + off = 32; + + fi->g.my_node_name_high = (fi->n_r.data[off] << 16) | fi->n_r.data[off+1]; + fi->g.my_node_name_low = (fi->n_r.data[off+2] << 16) | fi->n_r.data[off+3]; + fi->g.my_port_name_high = (fi->n_r.data[off+4] << 16) | fi->n_r.data[off+5]; + fi->g.my_port_name_low = (fi->n_r.data[off+6] << 16) | fi->n_r.data[off+7]; + DPRINTK("node_name = %x %x", fi->g.my_node_name_high, fi->g.my_node_name_low); + DPRINTK("port_name = %x %x", fi->g.my_port_name_high, fi->g.my_port_name_low); +} + +static void reset_ichip(struct fc_info *fi) +{ + /* (i)chip reset */ + writel(ICHIP_HCR_RESET, fi->i_r.ptr_ichip_hw_control_reg); + /*wait for chip to get reset */ + udelay(10000); + /*de-assert reset */ + writel(ICHIP_HCR_DERESET, fi->i_r.ptr_ichip_hw_control_reg); + + /* enable INT lines on the (i)chip */ + writel(ICHIP_HCR_ENABLE_INTA , fi->i_r.ptr_ichip_hw_control_reg); + /* enable byte swap */ + writel(ICHIP_HAMR_BYTE_SWAP_ADDR_TR, fi->i_r.ptr_ichip_hw_addr_mask_reg); +} + +static void tx_logi(struct fc_info *fi, u_int logi, u_int d_id) +{ +int int_required = 1; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int r_ctl = RCTL_ELS_UCTL; +u_int type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_logi"); + /* We dont want interrupted for our own logi. + * It screws up the port discovery process. + */ + if (d_id == fi->g.my_id) + int_required = 0; + fill_login_frame(fi, logi); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.login, sizeof(LOGIN)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),sizeof(LOGIN), r_ctl, type, d_id, my_mtu, int_required, ox_id, logi); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_logi"); + return; +} + +static void tx_logi_acc(struct fc_info *fi, u_int logi, u_int d_id, u_short received_ox_id) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_logi_acc"); + fill_login_frame(fi, logi); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.login, sizeof(LOGIN)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),sizeof(LOGIN), r_ctl, type, d_id, my_mtu, int_required, received_ox_id, logi); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_logi_acc"); + return; +} + +static void tx_prli(struct fc_info *fi, u_int command_code, u_int d_id, u_short received_ox_id) +{ +int int_required = 1; +u_int r_ctl = RCTL_ELS_UCTL; +u_int type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_prli"); + if (command_code == ELS_PRLI) + fi->g.prli.cmnd_code = htons((ELS_PRLI | PAGE_LEN) >> 16); + else { + fi->g.prli.cmnd_code = htons((ELS_ACC | PAGE_LEN) >> 16); + int_required = 0; + type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; + r_ctl = RCTL_ELS_SCTL; + } + fi->g.prli.payload_length = htons(PRLI_LEN); + fi->g.prli.type_code = htons(FCP_TYPE_CODE); + fi->g.prli.est_image_pair = htons(IMAGE_PAIR); + fi->g.prli.responder_pa = 0; + fi->g.prli.originator_pa = 0; + fi->g.prli.service_params = htonl(INITIATOR_FUNC | READ_XFER_RDY_DISABLED); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.prli, sizeof(PRLI)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]), sizeof(PRLI), r_ctl, type, d_id, my_mtu, int_required, received_ox_id, command_code); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_prli"); + return; +} + +static void tx_logo(struct fc_info *fi, u_int d_id, u_short received_ox_id) +{ +int int_required = 1; +u_int r_ctl = RCTL_ELS_UCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | SEQUENCE_RESPONDER | FIRST_SEQUENCE | END_SEQUENCE | SEQUENCE_INITIATIVE; +int size = sizeof(LOGO); +char fc_id[3]; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_logo"); + fi->g.logo.logo_cmnd = htonl(ELS_LOGO); + fi->g.logo.reserved = 0; + memcpy(fc_id, &(fi->g.my_id), 3); + fi->g.logo.n_port_id_0 = fc_id[0]; + fi->g.logo.n_port_id_1 = fc_id[1]; + fi->g.logo.n_port_id_2 = fc_id[2]; + fi->g.logo.port_name_up = htonl(N_PORT_NAME_HIGH); + fi->g.logo.port_name_low = htonl(N_PORT_NAME_LOW); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.logo, sizeof(LOGO)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, ELS_LOGO); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_logo"); +} + +static void tx_adisc(struct fc_info *fi, u_int cmnd_code, u_int d_id, u_short received_ox_id) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | SEQUENCE_RESPONDER | FIRST_SEQUENCE | END_SEQUENCE; +int size = sizeof(ADISC); +u_int my_mtu = fi->g.my_mtu; + fi->g.adisc.ls_cmnd_code = htonl(cmnd_code); + fi->g.adisc.hard_address = htonl(0); + fi->g.adisc.port_name_high = htonl(N_PORT_NAME_HIGH); + fi->g.adisc.port_name_low = htonl(N_PORT_NAME_LOW); + fi->g.adisc.node_name_high = htonl(NODE_NAME_HIGH); + fi->g.adisc.node_name_low = htonl(NODE_NAME_LOW); + fi->g.adisc.n_port_id = htonl(fi->g.my_id); + if (cmnd_code == ELS_ADISC) { + int_required = 1; + r_ctl = RCTL_ELS_UCTL; + type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; + } + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.adisc, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, cmnd_code); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; +} + +static void tx_ls_rjt(struct fc_info *fi, u_int d_id, u_short received_ox_id, u_short reason_code, u_short expln_code) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; +int size = sizeof(LS_RJT); +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_ls_rjt"); + fi->g.ls_rjt.cmnd_code = htonl(ELS_LS_RJT); + fi->g.ls_rjt.reason_code = htonl((reason_code << 16) | expln_code); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.ls_rjt, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, ELS_LS_RJT); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_ls_rjt"); +} + +static void tx_abts(struct fc_info *fi, u_int d_id, u_short ox_id) +{ +int int_required = 1; +u_int r_ctl = RCTL_BASIC_ABTS; +u_int type = TYPE_BLS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +int size = 0; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_abts"); + fi->g.type_of_frame = FC_BLS; + tx_exchange(fi, NULL, size, r_ctl, type, d_id, my_mtu, int_required, ox_id, RCTL_BASIC_ABTS); + LEAVE("tx_abts"); +} + +static u_int plogi_ok(struct fc_info *fi, u_int *buff_addr, int size) +{ +int ret_code = 0; +u_short mtu = ntohl(*(buff_addr + 10)) & 0x00000FFF; +u_short class3 = ntohl(*(buff_addr + 25)) >> 16; +u_short class3_conc_seq = ntohl(*(buff_addr + 27)) >> 16; +u_short open_seq = ntohl(*(buff_addr + 28)) >> 16; + DPRINTK1("mtu = %x class3 = %x conc_seq = %x open_seq = %x", mtu, class3, class3_conc_seq, open_seq); + size -= TACHYON_HEADER_LEN; + if (!(class3 & 0x8000)) { + DPRINTK1("Received PLOGI with class3 = %x", class3); + ret_code = (LOGICAL_ERR << 16) | NO_EXPLN; + return ret_code; + } + if (mtu < 256) { + DPRINTK1("Received PLOGI with MTU set to %x", mtu); + ret_code = (LOGICAL_ERR << 16) | RECV_FIELD_SIZE; + return ret_code; + } + if (size != PLOGI_LEN) { + DPRINTK1("Received PLOGI of size %x", size); + ret_code = (LOGICAL_ERR << 16) | INV_PAYLOAD_LEN; + return ret_code; + } + if (class3_conc_seq == 0) { + DPRINTK1("Received PLOGI with conc_seq == 0"); + ret_code = (LOGICAL_ERR << 16) | CONC_SEQ; + return ret_code; + } + if (open_seq == 0) { + DPRINTK1("Received PLOGI with open_seq == 0"); + ret_code = (LOGICAL_ERR << 16) | NO_EXPLN; + return ret_code; + } + + /* Could potentially check for more fields, but might end up + not talking to most of the devices. ;-) */ + /* Things that could get checked are: + common_features = 0x8800 + total_concurrent_seq = at least 1 + */ + return ret_code; +} + +static void tx_acc(struct fc_info *fi, u_int d_id, u_short received_ox_id) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; +int size = sizeof(ACC); +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_acc"); + fi->g.acc.cmnd_code = htonl(ELS_ACC); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.acc, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, ELS_ACC); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_acc"); +} + + +static void tx_name_server_req(struct fc_info *fi, u_int req) +{ +int int_required = 1, i, size = 0; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int type = TYPE_FC_SERVICES | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_CONTROL; +u_int my_mtu = fi->g.my_mtu, d_id = DIRECTORY_SERVER; +CT_HDR ct_hdr; + ENTER("tx_name_server_req"); + /* Fill up CT_Header */ + ct_hdr.rev_in_id = htonl(FC_CT_REV); + ct_hdr.fs_type = DIRECTORY_SERVER_APP; + ct_hdr.fs_subtype = NAME_SERVICE; + ct_hdr.options = 0; + ct_hdr.resv1 = 0; + ct_hdr.cmnd_resp_code = htons(req >> 16); + ct_hdr.max_res_size = 0; + ct_hdr.resv2 = 0; + ct_hdr.reason_code = 0; + ct_hdr.expln_code = 0; + ct_hdr.vendor_unique = 0; + + fi->g.type_of_frame = FC_ELS; + switch(req) { + case FCS_RFC_4: + memcpy(&(fi->g.rfc_4.ct_hdr), &ct_hdr, sizeof(CT_HDR)); + fi->g.rfc_4.s_id = htonl(fi->g.my_id); + for (i = 0; i < 32; i++) + fi->g.rfc_4.bit_map[i] = 0; + /* We support IP & SCSI */ + fi->g.rfc_4.bit_map[2] = 0x01; + fi->g.rfc_4.bit_map[3] = 0x20; + size = sizeof(RFC_4); + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.rfc_4, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, ox_id, req); + break; + case FCS_GP_ID4: + memcpy(&(fi->g.gp_id4.ct_hdr), &ct_hdr, sizeof(CT_HDR)); + fi->g.gp_id4.port_type = htonl(PORT_TYPE_NX_PORTS); + size = sizeof(GP_ID4); + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.gp_id4, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, ox_id, req); + break; + } + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_name_server_req"); +} + +static void tx_scr(struct fc_info *fi) +{ +int int_required = 1, size = sizeof(SCR); +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int r_ctl = RCTL_ELS_UCTL; +u_int my_mtu = fi->g.my_mtu, d_id = FABRIC_CONTROLLER; + ENTER("tx_scr"); + fi->g.scr.cmnd_code = htonl(ELS_SCR); + fi->g.scr.reg_function = htonl(FULL_REGISTRATION); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.scr, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, ox_id, ELS_SCR); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_scr"); +} + +static void perform_adisc(struct fc_info *fi) +{ +int count = 0; + /* Will be set to TRUE when timer expires in a PLDA environment. + */ + fi->g.port_discovery = FALSE; + + if (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + while(temp_list) { + /* Tx ADISC to all non-fabric based + * entities. + */ + if ((temp_list->d_id & 0xFF0000) != 0xFF0000) + tx_adisc(fi, ELS_ADISC, temp_list->d_id, OX_ID_FIRST_SEQUENCE); + temp_list = temp_list->next; + udelay(20); + count++; + } + } + /* Perform Port Discovery after timer expires. + * We are giving time for the ADISCed nodes to respond + * so that we dont have to perform PLOGI to those whose + * login are _still_ valid. + */ + fi->explore_timer.function = port_discovery_timer; + fi->explore_timer.data = (unsigned long)fi; + fi->explore_timer.expires = RUN_AT((count*3*HZ)/100); + init_timer(&fi->explore_timer); + add_timer(&fi->explore_timer); +} + +static void explore_fabric(struct fc_info *fi, u_int *buff_addr) +{ +u_int *addr = buff_addr + 12; /* index into payload */ +u_char control_code; +u_int d_id; +int count = 0; + ENTER("explore_fabric"); + DPRINTK1("entering explore_fabric"); + + /*fi->g.perform_adisc = TRUE; + fi->g.explore_fabric = TRUE; + perform_adisc(fi);*/ + + do { + d_id = ntohl(*addr) & 0x00FFFFFF; + if (d_id != fi->g.my_id) { + if (sid_logged_in(fi, d_id) == NODE_NOT_PRESENT) + tx_logi(fi, ELS_PLOGI, d_id); + else + if (sid_logged_in(fi, d_id) == NODE_LOGGED_OUT) + tx_adisc(fi, ELS_ADISC, d_id, OX_ID_FIRST_SEQUENCE); + count++; + } + control_code = (ntohl(*addr) & 0xFF000000) >> 24; + addr++; + DPRINTK1("cc = %x, d_id = %x", control_code, d_id); + } while (control_code != 0x80); + + fi->explore_timer.function = fabric_explore_timer; + fi->explore_timer.data = (unsigned long)fi; + /* We give 30 msec for each device to respond and then send out + * our SCSI enquiries. + */ + fi->explore_timer.expires = RUN_AT((count*3*HZ)/100); + init_timer(&fi->explore_timer); + add_timer(&fi->explore_timer); + + DPRINTK1("leaving explore_fabric"); + LEAVE("explore_fabric"); +} + +static void fabric_explore_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + del_timer(&fi->explore_timer); + + if ((fi->g.loop_up == TRUE) && (fi->g.ptp_up == FALSE)) { + /* Initiate Local Port Discovery on the Local Loop. + */ + fi->g.port_discovery = TRUE; + fi->g.alpa_list_index = 1; + local_port_discovery(fi); + } + fi->g.explore_fabric = FALSE; + return; +} + +static void port_discovery_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + del_timer(&fi->explore_timer); + + if ((fi->g.loop_up == TRUE) && (fi->g.explore_fabric != TRUE)) { + fi->g.port_discovery = TRUE; + fi->g.alpa_list_index = 1; + local_port_discovery(fi); + } + fi->g.perform_adisc = FALSE; + return; +} + +static void add_to_ox_id_list(struct fc_info *fi, u_int transaction_id, u_int cmnd_code) +{ +struct ox_id_els_map *p, *q = fi->ox_id_list, *r = NULL; +int size = sizeof(struct ox_id_els_map); + while (q != NULL) { + r = q; + q = q->next; + } + p = (struct ox_id_els_map *)kmalloc(size, GFP_ATOMIC); + if (p == NULL) { + T_MSG("kmalloc failed in add_to_ox_id_list()"); + return; + } + p->ox_id = transaction_id; + p->els = cmnd_code; + p->next = NULL; + if (fi->ox_id_list == NULL) + fi->ox_id_list = p; + else + r->next = p; + return; +} + +static u_int remove_from_ox_id_list(struct fc_info *fi, u_short received_ox_id) +{ +struct ox_id_els_map *p = fi->ox_id_list, *q = fi->ox_id_list; +u_int els_type; + while (q != NULL) { + if (q->ox_id == received_ox_id) { + + if (q == fi->ox_id_list) + fi->ox_id_list = fi->ox_id_list->next; + else + if (q->next == NULL) + p->next = NULL; + else + p->next = q->next; + + els_type = q->els; + kfree(q); + return els_type; + } + p = q; + q = q->next; + } + if (q == NULL) + DPRINTK2("Could not find ox_id %x in ox_id_els_map", received_ox_id); + return 0; +} + +static void build_tachyon_header(struct fc_info *fi, u_int my_id, u_int r_ctl, u_int d_id, u_int type, u_char seq_id, u_char df_ctl, u_short ox_id, u_short rx_id, char *data) +{ +u_char alpa = d_id & 0x0000FF; +u_int dest_ddaa = d_id &0xFFFF00; + + ENTER("build_tachyon_header"); + DPRINTK("d_id = %x, my_ddaa = %x", d_id, fi->g.my_ddaa); + /* Does it have to go to/thru a Fabric? */ + if ((dest_ddaa != 0) && ((d_id == F_PORT) || (fi->g.fabric_present && (dest_ddaa != fi->g.my_ddaa)))) + alpa = 0x00; + fi->g.tach_header.resv = 0x00000000; + fi->g.tach_header.sof_and_eof = SOFI3 | EOFN; + fi->g.tach_header.dest_alpa = alpa; + /* Set LCr properly to have enuff credit */ + if (alpa == REPLICATE) + fi->g.tach_header.lcr_and_time_stamp = htons(0xC00);/* LCr=3 */ + else + fi->g.tach_header.lcr_and_time_stamp = 0; + fi->g.tach_header.r_ctl_and_d_id = htonl(r_ctl | d_id); + fi->g.tach_header.vc_id_and_s_id = htonl(my_id); + fi->g.tach_header.type_and_f_cntl = htonl(type); + fi->g.tach_header.seq_id = seq_id; + fi->g.tach_header.df_cntl = df_ctl; + fi->g.tach_header.seq_cnt = 0; + fi->g.tach_header.ox_id = htons(ox_id); + fi->g.tach_header.rx_id = htons(rx_id); + fi->g.tach_header.ro = 0; + if (data) { + /* We use the Seq_Count to keep track of IP frames in the + * OCI_interrupt handler. Initial Seq_Count of IP frames is 1. + */ + if (fi->g.type_of_frame == FC_BROADCAST) + fi->g.tach_header.seq_cnt = htons(0x1); + else + fi->g.tach_header.seq_cnt = htons(0x2); + fi->g.tach_header.nw_header.d_naa = htons(0x1000); + fi->g.tach_header.nw_header.s_naa = htons(0x1000); + memcpy(&(fi->g.tach_header.nw_header.dest_high), data, 2); + memcpy(&(fi->g.tach_header.nw_header.dest_low), data + 2, 4); + memcpy(&(fi->g.tach_header.nw_header.source_high), data + 6, 2); + memcpy(&(fi->g.tach_header.nw_header.source_low), data + 8, 4); + } + LEAVE("build_tachyon_header"); +} + +static void build_EDB(struct fc_info *fi, char *data, u_short flags, u_short len) +{ + fi->g.edb.buf_addr = ntohl((u_int)virt_to_bus(data)); + fi->g.edb.ehf = ntohs(flags); + if (len % 4) + len += (4 - (len % 4)); + fi->g.edb.buf_len = ntohs(len); +} + +static void build_ODB(struct fc_info *fi, u_char seq_id, u_int d_id, u_int len, u_int cntl, u_short mtu, u_short ox_id, u_short rx_id, int NW_header, int int_required, u_int frame_class) +{ + fi->g.odb.seq_d_id = htonl(seq_id << 24 | d_id); + fi->g.odb.tot_len = len; + if (NW_header) + fi->g.odb.tot_len += NW_HEADER_LEN; + if (fi->g.odb.tot_len % 4) + fi->g.odb.tot_len += (4 - (fi->g.odb.tot_len % 4)); + fi->g.odb.tot_len = htonl(fi->g.odb.tot_len); + switch(int_required) { + case NO_COMP_AND_INT: + fi->g.odb.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | ODB_NO_INT | ODB_NO_COMP | cntl); + break; + case INT_AND_COMP_REQ: + fi->g.odb.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | cntl); + break; + case NO_INT_COMP_REQ: + fi->g.odb.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | ODB_NO_INT | cntl); + break; + } + fi->g.odb.rx_id = htons(rx_id); + fi->g.odb.cs_enable = 0; + fi->g.odb.cs_seed = htons(1); + + fi->g.odb.hdr_addr = htonl(virt_to_bus(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx])); + fi->g.odb.frame_len = htons(mtu); + + if (NW_header) { + /* The pointer to the sk_buff is in here. Freed up when the + * OCI_interrupt is received. + */ + fi->g.odb.trans_id = htonl(frame_class); + fi->g.odb.hdr_len = TACHYON_HEADER_LEN + NW_HEADER_LEN; + } + else { + /* helps in tracking transmitted OX_IDs */ + fi->g.odb.trans_id = htonl((frame_class & 0xFFFF0000) | ox_id); + fi->g.odb.hdr_len = TACHYON_HEADER_LEN; + } + fi->g.odb.hdr_len = htons(fi->g.odb.hdr_len); + + fi->g.odb.edb_addr = htonl(virt_to_bus(fi->q.ptr_edb[fi->q.edb_buffer_indx])); +} + +static void fill_login_frame(struct fc_info *fi, u_int logi) +{ +int i; + fi->g.login.ls_cmnd_code= htonl(logi); + fi->g.login.fc_ph_version = htons(PH_VERSION); + if (fi->g.loop_up) + fi->g.login.buff_to_buff_credit = htons(LOOP_BB_CREDIT); + else + if (fi->g.ptp_up) + fi->g.login.buff_to_buff_credit = htons(PT2PT_BB_CREDIT); + if ((logi != ELS_FLOGI) || (logi == ELS_ACC)) + fi->g.login.common_features = htons(PLOGI_C_F); + else + if (logi == ELS_FLOGI) + fi->g.login.common_features = htons(FLOGI_C_F); + fi->g.login.recv_data_field_size = htons(FRAME_SIZE); + fi->g.login.n_port_total_conc_seq = htons(CONCURRENT_SEQUENCES); + fi->g.login.rel_off_by_info_cat = htons(RO_INFO_CATEGORY); + fi->g.login.ED_TOV = htonl(E_D_TOV); + fi->g.login.n_port_name_high = htonl(N_PORT_NAME_HIGH); + fi->g.login.n_port_name_low = htonl(N_PORT_NAME_LOW); + fi->g.login.node_name_high = htonl(NODE_NAME_HIGH); + fi->g.login.node_name_low = htonl(NODE_NAME_LOW); + + /* Fill Class 1 parameters */ + fi->g.login.c_of_s[0].service_options = htons(0); + fi->g.login.c_of_s[0].initiator_ctl = htons(0); + fi->g.login.c_of_s[0].recipient_ctl = htons(0); + fi->g.login.c_of_s[0].recv_data_field_size = htons(0); + fi->g.login.c_of_s[0].concurrent_sequences = htons(0); + fi->g.login.c_of_s[0].n_port_end_to_end_credit = htons(0); + fi->g.login.c_of_s[0].open_seq_per_exchange = htons(0); + fi->g.login.c_of_s[0].resv = htons(0); + + /* Fill Class 2 parameters */ + fi->g.login.c_of_s[1].service_options = htons(0); + fi->g.login.c_of_s[1].initiator_ctl = htons(0); + fi->g.login.c_of_s[1].recipient_ctl = htons(0); + fi->g.login.c_of_s[1].recv_data_field_size = htons(0); + fi->g.login.c_of_s[1].concurrent_sequences = htons(0); + fi->g.login.c_of_s[1].n_port_end_to_end_credit = htons(0); + fi->g.login.c_of_s[1].open_seq_per_exchange = htons(0); + fi->g.login.c_of_s[1].resv = htons(0); + + /* Fill Class 3 parameters */ + if (logi == ELS_FLOGI) + fi->g.login.c_of_s[2].service_options = htons(SERVICE_VALID | SEQUENCE_DELIVERY); + else + fi->g.login.c_of_s[2].service_options = htons(SERVICE_VALID); + fi->g.login.c_of_s[2].initiator_ctl = htons(0); + fi->g.login.c_of_s[2].recipient_ctl = htons(0); + fi->g.login.c_of_s[2].recv_data_field_size = htons(FRAME_SIZE); + fi->g.login.c_of_s[2].concurrent_sequences = htons(CLASS3_CONCURRENT_SEQUENCE); + fi->g.login.c_of_s[2].n_port_end_to_end_credit = htons(0); + fi->g.login.c_of_s[2].open_seq_per_exchange = htons(CLASS3_OPEN_SEQUENCE); + fi->g.login.c_of_s[2].resv = htons(0); + + for(i = 0; i < 4; i++) { + fi->g.login.resv[i] = 0; + fi->g.login.vendor_version_level[i] = 0; + } +} + + +/* clear the Interrupt Latch on the (i)chip, so that you can receive + * Interrupts from Tachyon in future + */ +static void reset_latch(struct fc_info *fi) +{ + writel(readl(fi->i_r.ptr_ichip_hw_status_reg) | ICHIP_HSR_INT_LATCH, fi->i_r.ptr_ichip_hw_status_reg); +} + +static void update_OCQ_indx(struct fc_info *fi) +{ + fi->q.ocq_prod_indx++; + if (fi->q.ocq_prod_indx == OCQ_LENGTH) + fi->q.ocq_prod_indx = 0; + writel(fi->q.ocq_prod_indx, fi->t_r.ptr_ocq_prod_indx_reg); +} + +static void update_IMQ_indx(struct fc_info *fi, int count) +{ + fi->q.imq_cons_indx += count; + if (fi->q.imq_cons_indx >= IMQ_LENGTH) + fi->q.imq_cons_indx -= IMQ_LENGTH; + writel(fi->q.imq_cons_indx, fi->t_r.ptr_imq_cons_indx_reg); +} + +static void update_SFSBQ_indx(struct fc_info *fi) +{ + fi->q.sfsbq_prod_indx++; + if (fi->q.sfsbq_prod_indx == SFSBQ_LENGTH) + fi->q.sfsbq_prod_indx = 0; + writel(fi->q.sfsbq_prod_indx, fi->t_r.ptr_sfsbq_prod_reg); +} + +static void update_MFSBQ_indx(struct fc_info *fi, int count) +{ + fi->q.mfsbq_prod_indx += count; + if (fi->q.mfsbq_prod_indx >= MFSBQ_LENGTH) + fi->q.mfsbq_prod_indx -= MFSBQ_LENGTH; + writel(fi->q.mfsbq_prod_indx, fi->t_r.ptr_mfsbq_prod_reg); +} + + +static void update_tachyon_header_indx(struct fc_info *fi) +{ + fi->q.tachyon_header_indx++; + if (fi->q.tachyon_header_indx == NO_OF_TACH_HEADERS) + fi->q.tachyon_header_indx = 0; +} + +static void update_EDB_indx(struct fc_info *fi) +{ + fi->q.edb_buffer_indx++; + if (fi->q.edb_buffer_indx == EDB_LEN) + fi->q.edb_buffer_indx = 0; +} + +static int iph5526_open(struct device *dev) +{ + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int iph5526_close(struct device *dev) +{ + dev->tbusy = 1; + dev->start = 0; + MOD_DEC_USE_COUNT; + return 0; +} + +static int iph5526_send_packet(struct sk_buff *skb, struct device *dev) +{ +struct fc_info *fi = (struct fc_info*)dev->priv; +int status = 0; +short type = 0; +u_long flags; + ENTER("iph5526_send_packet"); + if (dev->tbusy) { + printk(KERN_WARNING "%s: DEVICE BUSY\n", dev->name); + dev->tbusy = 0; + fi->fc_stats.rx_dropped++; + dev->trans_start = jiffies; + return 0; + } + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk(KERN_WARNING "%s: Transmitter access conflict.\n", +dev->name); + fi->fc_stats.rx_dropped++; + return 1; + } + else { + struct fcllc *fcllc; + /* Strip off the pseudo header. + */ + skb->data = skb->data + 2*FC_ALEN; + skb->len = skb->len - 2*FC_ALEN; + fcllc = (struct fcllc *)skb->data; + type = ntohs(fcllc->ethertype); + + spin_lock_irqsave(&fi->fc_lock, flags); + switch(type) { + case ETH_P_IP: + status = tx_ip_packet(skb, skb->len, fi); + break; + case ETH_P_ARP: + status = tx_arp_packet(skb->data, skb->len, fi); + break; + default: + T_MSG("WARNING!!! Received Unknown Packet Type... Discarding..."); + fi->fc_stats.rx_dropped++; + break; + } + spin_unlock_irqrestore(&fi->fc_lock, flags); + } + + if (status) { + fi->fc_stats.tx_bytes += skb->len; + fi->fc_stats.tx_packets++; + } + else + fi->fc_stats.rx_dropped++; + dev->trans_start = jiffies; + dev->tbusy = 0; + /* We free up the IP buffers in the OCI_interrupt handler. + * status == 0 implies that the frame was not transmitted. So the + * skb is freed here. + */ + if ((type == ETH_P_ARP) || (status == 0)) + dev_kfree_skb(skb); + mark_bh(NET_BH); + LEAVE("iph5526_send_packet"); + return 0; +} + +static int iph5526_change_mtu(struct device *dev, int mtu) +{ + return 0; +} + +static int tx_ip_packet(struct sk_buff *skb, unsigned long len, struct fc_info *fi) +{ +u_int d_id; +int int_required = 1; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_DATA; +u_int type = TYPE_LLC_SNAP; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int mtu; +struct fc_node_info *q; + + ENTER("tx_ip_packet"); + q = look_up_cache(fi, skb->data - 2*FC_ALEN); + if (q != NULL) { + d_id = q->d_id; + DPRINTK("Look-Up Cache Succeeded for d_id = %x", d_id); + mtu = q->mtu; + if (q->login == LOGIN_COMPLETED){ + fi->g.type_of_frame = FC_IP; + return tx_exchange(fi, skb->data, len, r_ctl, type, d_id, mtu, int_required, ox_id, virt_to_bus(skb)); + } + + if (q->d_id == BROADCAST) { + struct fc_node_info *p = fi->node_info_list; + int return_value = FALSE; + fi->g.type_of_frame = FC_BROADCAST; + /* Do unicast to local nodes. + */ + int_required = 0; + while(p != NULL) { + d_id = p->d_id; + if ((d_id & 0xFFFF00) == fi->g.my_ddaa) + return_value |= tx_exchange(fi, skb->data, len, r_ctl, type, d_id, fi->g.my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + p = p->next; + } + kfree(q); + return return_value; + } + + if (q->login != LOGIN_COMPLETED) { + DPRINTK1("Node not logged in... Txing PLOGI to %x", d_id); + /* FIXME: we are dumping the frame here */ + tx_logi(fi, ELS_PLOGI, d_id); + } + } + DPRINTK2("Look-Up Cache Failed"); + LEAVE("tx_ip_packet"); + return 0; +} + +static int tx_arp_packet(char *data, unsigned long len, struct fc_info *fi) +{ +u_int opcode = data[ARP_OPCODE_0]; +u_int d_id; +int int_required = 0, return_value = FALSE; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_DATA; +u_int type = TYPE_LLC_SNAP; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_arp_packet"); + + opcode = opcode << 8 | data[ARP_OPCODE_1]; + fi->g.type_of_frame = FC_IP; + + if (opcode == ARPOP_REQUEST) { + struct fc_node_info *q = fi->node_info_list; + d_id = BROADCAST; + return_value |= tx_exchange(fi, data, len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + /* Some devices support HW_TYPE 0x01 */ + memcpy(fi->g.arp_buffer, data - 2*FC_ALEN, len + 2*FC_ALEN); + fi->g.arp_buffer[9 + 2*FC_ALEN] = 0x01; + return_value |= tx_exchange(fi, (char *)(fi->g.arp_buffer + 2*FC_ALEN), len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + + /* Do unicast to local nodes. + */ + while(q != NULL) { + fi->g.type_of_frame = FC_BROADCAST; + d_id = q->d_id; + if ((d_id & 0xFFFF00) == fi->g.my_ddaa) { + return_value |= tx_exchange(fi, data, len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + // Some devices support HW_TYPE 0x01 + memcpy(fi->g.arp_buffer, data - 2*FC_ALEN, len + 2*FC_ALEN); + fi->g.arp_buffer[9 + 2*FC_ALEN] = 0x01; + return_value |= tx_exchange(fi, (char *)(fi->g.arp_buffer + 2*FC_ALEN), len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + } + q = q->next; + } + return return_value; + } + else + if (opcode == ARPOP_REPLY) { + struct fc_node_info *q; u_int mtu; + DPRINTK("We are sending out an ARP reply"); + q = look_up_cache(fi, data - 2*FC_ALEN); + if (q != NULL) { + d_id = q->d_id; + DPRINTK("Look-Up Cache Succeeded for d_id = %x", d_id); + mtu = q->mtu; + if (q->login == LOGIN_COMPLETED){ + tx_exchange(fi, data, len, r_ctl, type, d_id, mtu, int_required, ox_id, TYPE_LLC_SNAP); + /* Some devices support HW_TYPE 0x01 */ + memcpy(fi->g.arp_buffer, data - 2*FC_ALEN, len + 2*FC_ALEN); + fi->g.arp_buffer[9 + 2*FC_ALEN] = 0x01; + return tx_exchange(fi, (char *)(fi->g.arp_buffer + 2*FC_ALEN), len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + } + else { + DPRINTK1("Node not logged in... Txing PLOGI to %x", d_id); + tx_logi(fi, ELS_PLOGI, d_id); /* FIXME: we are dumping the frame here */ + } + } + DPRINTK2("Look-Up Cache Failed"); + } + else { + T_MSG("Warning!!! Invalid Opcode in ARP Packet!"); + } + LEAVE("tx_arp_packet"); + return 0; +} + + +static void rx_net_packet(struct fc_info *fi, u_char *buff_addr, int payload_size) +{ +struct device *dev = fi->dev; +struct sk_buff *skb; +u_int skb_size = 0; +struct fch_hdr fch; + ENTER("rx_net_packet"); + skb_size = payload_size - TACHYON_HEADER_LEN; + DPRINTK("skb_size = %d", skb_size); + fi->fc_stats.rx_bytes += skb_size - 2; + skb = dev_alloc_skb(skb_size); + if (skb == NULL) { + printk(KERN_NOTICE "%s: In rx_net_packet() Memory squeeze, dropping packet.\n", dev->name); + fi->fc_stats.rx_dropped++; + return; + } + /* Skip over the Tachyon Frame Header. + */ + buff_addr += TACHYON_HEADER_LEN; + + memcpy(fch.daddr, buff_addr + 2, FC_ALEN); + memcpy(fch.saddr, buff_addr + 10, FC_ALEN); + buff_addr += 2; + memcpy(buff_addr, fch.daddr, FC_ALEN); + memcpy(buff_addr + 6, fch.saddr, FC_ALEN); + skb_reserve(skb, 2); + memcpy(skb_put(skb, skb_size - 2), buff_addr, skb_size - 2); + skb->dev = dev; + skb->protocol = fc_type_trans(skb, dev); + DPRINTK("protocol = %x", skb->protocol); + + /* Hmmm... to accept HW Type 0x01 as well... + */ + if (skb->protocol == ntohs(ETH_P_ARP)) + skb->data[1] = 0x06; + netif_rx(skb); + fi->fc_stats.rx_packets++; + LEAVE("rx_net_packet"); +} + + +static void rx_net_mfs_packet(struct fc_info *fi, struct sk_buff *skb) +{ +struct device *dev = fi->dev; +struct fch_hdr fch; + ENTER("rx_net_mfs_packet"); + /* Construct your Hard Header */ + memcpy(fch.daddr, skb->data + 2, FC_ALEN); + memcpy(fch.saddr, skb->data + 10, FC_ALEN); + skb_pull(skb, 2); + memcpy(skb->data, fch.daddr, FC_ALEN); + memcpy(skb->data + 6, fch.saddr, FC_ALEN); + skb->dev = dev; + skb->protocol = fc_type_trans(skb, dev); + DPRINTK("protocol = %x", skb->protocol); + netif_rx(skb); + LEAVE("rx_net_mfs_packet"); +} + +unsigned short fc_type_trans(struct sk_buff *skb, struct device *dev) +{ +struct fch_hdr *fch=(struct fch_hdr *)skb->data; +struct fcllc *fcllc; + skb->mac.raw = skb->data; + fcllc = (struct fcllc *)(skb->data + sizeof(struct fch_hdr) + 2); + skb_pull(skb,sizeof(struct fch_hdr) + 2); + + if(*fch->daddr & 1) { + if(!memcmp(fch->daddr,dev->broadcast,FC_ALEN)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } + else if(dev->flags & IFF_PROMISC) { + if(memcmp(fch->daddr, dev->dev_addr, FC_ALEN)) + skb->pkt_type=PACKET_OTHERHOST; + } + + /* Strip the SNAP header from ARP packets since we don't + * pass them through to the 802.2/SNAP layers. + */ + + if (fcllc->dsap == EXTENDED_SAP && + (fcllc->ethertype == ntohs(ETH_P_IP) || + fcllc->ethertype == ntohs(ETH_P_ARP))) { + skb_pull(skb, sizeof(struct fcllc)); + return fcllc->ethertype; + } + return ntohs(ETH_P_802_2); +} + +static int tx_exchange(struct fc_info *fi, char *data, u_int len, u_int r_ctl, u_int type, u_int d_id, u_int mtu, int int_required, u_short tx_ox_id, u_int frame_class) +{ +u_char df_ctl; +int NW_flag = 0, h_size, return_value; +u_short rx_id = RX_ID_FIRST_SEQUENCE; +u_int tachyon_status; +u_int my_id = fi->g.my_id; + ENTER("tx_exchange"); + + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + DPRINTK("Tachyon Status = %x len = %d MTU = %d", tachyon_status, len, mtu); + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + DPRINTK("Tachyon Status = %x len = %d MTU = %d", tachyon_status, len, mtu); + } + if (tx_ox_id == OX_ID_FIRST_SEQUENCE) { + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + tx_ox_id = fi->g.scsi_oxid | SCSI_READ_BIT; + break; + case FC_SCSI_WRITE: + tx_ox_id = fi->g.scsi_oxid; + break; + default: + tx_ox_id = fi->g.ox_id; + break; + } + } + else { + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + rx_id = fi->g.scsi_oxid | SCSI_READ_BIT; + break; + case FC_SCSI_WRITE: + rx_id = fi->g.scsi_oxid; + break; + case FC_BLS: + rx_id = RX_ID_FIRST_SEQUENCE; + break; + default: + rx_id = fi->g.ox_id; + break; + } + } + + if (type == TYPE_LLC_SNAP) { + df_ctl = 0x20; + NW_flag = 1; + /* Multi Frame Sequence ? If yes, set RO bit */ + if (len > mtu) + type |= RELATIVE_OFF_PRESENT; + build_tachyon_header(fi, my_id, r_ctl, d_id, type, fi->g.seq_id, df_ctl, tx_ox_id, rx_id, data - 2*FC_ALEN); + } + else { + df_ctl = 0; + /* Multi Frame Sequence ? If yes, set RO bit */ + if (len > mtu) + type |= RELATIVE_OFF_PRESENT; + build_tachyon_header(fi, my_id, r_ctl, d_id, type, fi->g.seq_id, df_ctl, tx_ox_id, rx_id, NULL); + } + + /* Get free Tachyon Headers and EDBs */ + if (get_free_header(fi) || get_free_EDB(fi)) + return 0; + + if ((type & 0xFF000000) == TYPE_LLC_SNAP) { + h_size = TACHYON_HEADER_LEN + NW_HEADER_LEN; + memcpy(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx], &(fi->g.tach_header), h_size); + } + else + memcpy(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx], &(fi->g.tach_header), TACHYON_HEADER_LEN); + + return_value = tx_sequence(fi, data, len, mtu, d_id, tx_ox_id, rx_id, fi->g.seq_id, NW_flag, int_required, frame_class); + + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + case FC_SCSI_WRITE: + update_scsi_oxid(fi); + break; + case FC_BLS: + break; + default: + fi->g.ox_id++; + if (fi->g.ox_id == 0xFFFF) + fi->g.ox_id = NOT_SCSI_XID; + break; + } + + if (fi->g.seq_id == MAX_SEQ_ID) + fi->g.seq_id = 0; + else + fi->g.seq_id++; + LEAVE("tx_exchange"); + return return_value; +} + +static int tx_sequence(struct fc_info *fi, char *data, u_int len, u_int mtu, u_int d_id, u_short ox_id, u_short rx_id, u_char seq_id, int NW_flag, int int_required, u_int frame_class) +{ +u_int cntl = 0; +int return_value; + ENTER("tx_sequence"); + build_EDB(fi, data, EDB_END, len); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + build_ODB(fi, seq_id, d_id, len, cntl, mtu, ox_id, rx_id, NW_flag, int_required, frame_class); + memcpy(fi->q.ptr_odb[fi->q.ocq_prod_indx], &(fi->g.odb), sizeof(ODB)); + if (fi->g.link_up != TRUE) { + DPRINTK2("Fibre Channel Link not up. Dropping Exchange!"); + return_value = FALSE; + } + else { + /* To be on the safe side, a check should be included + * at this point to check if we are overrunning + * Tachyon. + */ + update_OCQ_indx(fi); + return_value = TRUE; + } + update_EDB_indx(fi); + update_tachyon_header_indx(fi); + LEAVE("tx_sequence"); + return return_value; +} + +static int get_free_header(struct fc_info *fi) +{ +u_short temp_ox_id; +u_int *tach_header, initial_indx = fi->q.tachyon_header_indx; + /* Check if the header is in use. + * We could have an outstanding command. + * We should find a free slot as we can queue a + * maximum of 32 SCSI commands only. + */ + tach_header = fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx]; + temp_ox_id = ntohl(*(tach_header + 6)) >> 16; + /* We care about the SCSI writes only. Those are the wicked ones + * that need an additional set of buffers. + */ + while(temp_ox_id <= MAX_SCSI_XID) { + update_tachyon_header_indx(fi); + if (fi->q.tachyon_header_indx == initial_indx) { + /* Should never happen. + */ + T_MSG("No free Tachyon headers available"); + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + tach_header = fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx]; + temp_ox_id = ntohl(*(tach_header + 6)) >> 16; + } + return 0; +} + +static int get_free_EDB(struct fc_info *fi) +{ +unsigned int initial_indx = fi->q.edb_buffer_indx; + /* Check if the EDB is in use. + * We could have an outstanding SCSI Write command. + * We should find a free slot as we can queue a + * maximum of 32 SCSI commands only. + */ + while (fi->q.free_edb_list[fi->q.edb_buffer_indx] != EDB_FREE) { + update_EDB_indx(fi); + if (fi->q.edb_buffer_indx == initial_indx) { + T_MSG("No free EDB buffers avaliable") + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + } + return 0; +} + +static int validate_login(struct fc_info *fi, u_int *base_ptr) +{ +struct fc_node_info *q = fi->node_info_list; +char n_port_name[PORT_NAME_LEN]; +char node_name[NODE_NAME_LEN]; +u_int s_id; + ENTER("validate_login"); + /*index to Port Name in the payload. We need the 8 byte Port Name */ + memcpy(n_port_name, base_ptr + 10, PORT_NAME_LEN); + memcpy(node_name, base_ptr + 12, NODE_NAME_LEN); + s_id = ntohl(*(base_ptr + 3)) & 0x00FFFFFF; + + /* check if Fibre Channel IDs have changed */ + while(q != NULL) { + if (memcmp(n_port_name, q->hw_addr, PORT_NAME_LEN) == 0) { + if ((s_id != q->d_id) || (memcmp(node_name, q->node_name, NODE_NAME_LEN) != 0)) { + DPRINTK1("Fibre Channel ID of Node has changed. Txing LOGO."); + return 0; + } + q->login = LOGIN_COMPLETED; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return 1; + } + q = q->next; + } + DPRINTK1("Port Name does not match. Txing LOGO."); + return 0; + LEAVE("validate_login"); +} + +static void add_to_address_cache(struct fc_info *fi, u_int *base_ptr) +{ +int size = sizeof(struct fc_node_info); +struct fc_node_info *p, *q = fi->node_info_list, *r = NULL; +char n_port_name[PORT_NAME_LEN]; +u_int s_id; + ENTER("add_to_address_cache"); + /*index to Port Name in the payload. We need the 8 byte Port Name */ + memcpy(n_port_name, base_ptr + 13, PORT_NAME_LEN); + s_id = ntohl(*(base_ptr + 3)) & 0x00FFFFFF; + + /* check if info already exists */ + while(q != NULL) { + if (memcmp(n_port_name, q->hw_addr, PORT_NAME_LEN) == 0) { + if (s_id != q->d_id) { + memcpy(&(q->c_of_s[0]), base_ptr + 17, 3 * sizeof(CLASS_OF_SERVICE)); + q->mtu = ntohl(*(base_ptr + 10)) & 0x00000FFF; + q->d_id = s_id; + memcpy(q->node_name, base_ptr + 15, NODE_NAME_LEN); + } + q->login = LOGIN_COMPLETED; + q->scsi = FALSE; + fi->num_nodes++; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + r = q; + q = q->next; + } + p = (struct fc_node_info *)kmalloc(size, GFP_ATOMIC); + if (p == NULL) { + T_MSG("kmalloc failed in add_to_address_cache()"); + return; + } + memcpy(&(p->c_of_s[0]), base_ptr + 17, 3 * sizeof(CLASS_OF_SERVICE)); + p->mtu = ntohl(*(base_ptr + 10)) & 0x00000FFF; + p->d_id = s_id; + memcpy(p->hw_addr, base_ptr + 13, PORT_NAME_LEN); + memcpy(p->node_name, base_ptr + 15, NODE_NAME_LEN); + p->login = LOGIN_COMPLETED; + p->scsi = FALSE; + p->target_id = 0xFF; + p->next = NULL; + if (fi->node_info_list == NULL) + fi->node_info_list = p; + else + r->next = p; + fi->num_nodes++; +#if DEBUG_5526_2 + display_cache(fi); +#endif + LEAVE("add_to_address_cache"); + return; +} + +static void remove_from_address_cache(struct fc_info *fi, u_int *base_ptr, u_int cmnd_code) +{ +struct fc_node_info *q = fi->node_info_list; +u_int s_id; + ENTER("remove_from_address_cache"); + s_id = ntohl(*(base_ptr + 3)) & 0x00FFFFFF; + switch(cmnd_code) { + case ELS_LOGO: + /* check if info exists */ + while (q != NULL) { + if (s_id == q->d_id) { + if (q->login == LOGIN_COMPLETED) + q->login = LOGIN_ATTEMPTED; + if (fi->num_nodes > 0) + fi->num_nodes--; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + q = q->next; + } + DPRINTK1("ELS_LOGO received from node 0x%x which is not logged-in", s_id); + break; + case ELS_RSCN: + { + int payload_len = ntohl(*(base_ptr + 8)) & 0xFF; + int no_of_pages, i; + u_char address_format; + u_short received_ox_id = ntohl(*(base_ptr + 6)) >> 16; + u_int node_id, mask, *page_ptr = base_ptr + 9; + if ((payload_len < 4) || (payload_len > 256)) { + DPRINTK1("RSCN with invalid payload length received"); + tx_ls_rjt(fi, s_id, received_ox_id, LOGICAL_ERR, RECV_FIELD_SIZE); + return; + } + /* Page_size includes the Command Code */ + no_of_pages = (payload_len / 4) - 1; + for (i = 0; i < no_of_pages; i++) { + address_format = ntohl(*page_ptr) >> 24; + node_id = ntohl(*page_ptr) & 0x00FFFFFF; + switch(address_format) { + case PORT_ADDRESS_FORMAT: + rscn_handler(fi, node_id); + break; + case AREA_ADDRESS_FORMAT: + case DOMAIN_ADDRESS_FORMAT: + if (address_format == AREA_ADDRESS_FORMAT) + mask = 0xFFFF00; + else + mask = 0xFF0000; + while(q != NULL) { + if ((q->d_id & mask) == (node_id & mask)) + rscn_handler(fi, q->d_id); + q = q->next; + } + /* There might be some new nodes to be + * discovered. But, some of the earlier + * requests as a result of the RSCN might be + * in progress. We dont want to duplicate that + * effort. So letz call SCR after a lag. + */ + fi->explore_timer.function = scr_timer; + fi->explore_timer.data = (unsigned long)fi; + fi->explore_timer.expires = RUN_AT((no_of_pages*3*HZ)/100); + init_timer(&fi->explore_timer); + add_timer(&fi->explore_timer); + break; + default: + T_MSG("RSCN with invalid address format received"); + tx_ls_rjt(fi, s_id, received_ox_id, LOGICAL_ERR, NO_EXPLN); + } + page_ptr += 1; + } /* end of for loop */ + } /* end of case RSCN: */ + break; + } +#if DEBUG_5526_2 + display_cache(fi); +#endif + LEAVE("remove_from_address_cache"); +} + +static void rscn_handler(struct fc_info *fi, u_int node_id) +{ +struct fc_node_info *q = fi->node_info_list; +int login_state = sid_logged_in(fi, node_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) { + while(q != NULL) { + if (q->d_id == node_id) { + q->login = LOGIN_ATTEMPTED; + if (fi->num_nodes > 0) + fi->num_nodes--; + break; + } + else + q = q->next; + } + } + else + if (login_state == NODE_LOGGED_OUT) + tx_adisc(fi, ELS_ADISC, node_id, OX_ID_FIRST_SEQUENCE); + else + if (login_state == NODE_LOGGED_OUT) + tx_logi(fi, ELS_PLOGI, node_id); +} + +static void scr_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info *)data; + del_timer(&fi->explore_timer); + tx_name_server_req(fi, FCS_GP_ID4); +} + +static int sid_logged_in(struct fc_info *fi, u_int s_id) +{ +struct fc_node_info *temp = fi->node_info_list; + while(temp != NULL) + if ((temp->d_id == s_id) && (temp->login == LOGIN_COMPLETED)) { + if (temp->scsi != FALSE) + return NODE_PROCESS_LOGGED_IN; + else + return NODE_LOGGED_IN; + } + else + if ((temp->d_id == s_id) && (temp->login != LOGIN_COMPLETED)) + return NODE_LOGGED_OUT; + else + temp = temp->next; + return NODE_NOT_PRESENT; +} + +static void mark_scsi_sid(struct fc_info *fi, u_int *buff_addr, u_char action) +{ +struct fc_node_info *temp = fi->node_info_list; +u_int s_id; +u_int service_params; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + service_params = ntohl(*(buff_addr + 12)) & 0x000000F0; + while(temp != NULL) + if ((temp->d_id == s_id) && (temp->login == LOGIN_COMPLETED)) { + if (action == DELETE_ENTRY) { + temp->scsi = FALSE; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + /* Check if it is a SCSI Target */ + if (!(service_params & TARGET_FUNC)) { + temp->scsi = INITIATOR; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + temp->scsi = TARGET; + /* This helps to maintain the target_id no matter what your + * Fibre Channel ID is. + */ + if (temp->target_id == 0xFF) { + if (fi->g.no_of_targets <= MAX_SCSI_TARGETS) + temp->target_id = fi->g.no_of_targets++; + else + T_MSG("MAX TARGETS reached!"); + } + else + DPRINTK1("Target_id %d already present", temp->target_id); +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + else + temp = temp->next; + return; +} + +static int node_logged_in_prev(struct fc_info *fi, u_int *buff_addr) +{ +struct fc_node_info *temp; +u_char *data = (u_char *)buff_addr; +u_int s_id; +char node_name[NODE_NAME_LEN]; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + memcpy(node_name, buff_addr + 12, NODE_NAME_LEN); + /* point to port_name in the ADISC payload */ + data += 10 * 4; + /* point to last 6 bytes of port_name */ + data += 2; + temp = look_up_cache(fi, data); + if (temp != NULL) { + if ((temp->d_id == s_id) && (memcmp(node_name, temp->node_name, NODE_NAME_LEN) == 0)) { + temp->login = LOGIN_COMPLETED; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return TRUE; + } + } + return FALSE; +} + +static struct fc_node_info *look_up_cache(struct fc_info *fi, char *data) +{ +struct fc_node_info *temp_list = fi->node_info_list, *q; +u_char n_port_name[FC_ALEN], temp_addr[FC_ALEN]; + ENTER("look_up_cache"); + memcpy(n_port_name, data, FC_ALEN); + while(temp_list) { + if (memcmp(n_port_name, &(temp_list->hw_addr[2]), FC_ALEN) == 0) + return temp_list; + else + temp_list = temp_list->next; + } + + /* Broadcast IP ? + */ + temp_addr[0] = temp_addr[1] = temp_addr[2] = 0xFF; + temp_addr[3] = temp_addr[4] = temp_addr[5] = 0xFF; + if (memcmp(n_port_name, temp_addr, FC_ALEN) == 0) { + q = (struct fc_node_info *)kmalloc(sizeof(struct fc_node_info), GFP_ATOMIC); + if (q == NULL) { + T_MSG("kmalloc failed in look_up_cache()"); + return NULL; + } + q->d_id = BROADCAST; + return q; + } + LEAVE("look_up_cache"); + return NULL; +} + +static int display_cache(struct fc_info *fi) +{ +struct fc_node_info *q = fi->node_info_list; +#if DEBUG_5526_2 +struct ox_id_els_map *temp_ox_id_list = fi->ox_id_list; +#endif +int count = 0, j; + printk("\nFibre Channel Node Information for %s\n", fi->name); + printk("My FC_ID = %x, My WWN = %x %x, ", fi->g.my_id, fi->g.my_node_name_high, fi->g.my_node_name_low); + if (fi->g.ptp_up == TRUE) + printk("Port_Type = N_Port\n"); + if (fi->g.loop_up == TRUE) + printk("Port_Type = L_Port\n"); + while(q != NULL) { + printk("WWN = "); + for (j = 0; j < PORT_NAME_LEN; j++) + printk("%x ", q->hw_addr[j]); + printk("FC_ID = %x, ", q->d_id); + printk("Login = "); + if (q->login == LOGIN_COMPLETED) + printk("ON "); + else + printk("OFF "); + if (q->scsi == TARGET) + printk("Target_ID = %d ", q->target_id); + printk("\n"); + q = q->next; + count++; + } + +#if DEBUG_5526_2 + printk("OX_ID -> ELS Map\n"); + while(temp_ox_id_list) { + printk("ox_id = %x, ELS = %x\n", temp_ox_id_list->ox_id, temp_ox_id_list->els); + temp_ox_id_list = temp_ox_id_list->next; + } +#endif + + return 0; +} + +static struct net_device_stats * iph5526_get_stats(struct device *dev) +{ +struct fc_info *fi = (struct fc_info*)dev->priv; + return (struct net_device_stats *) &fi->fc_stats; +} + + +/* SCSI stuff starts here */ + +static struct proc_dir_entry proc_scsi_iph5526 = { + PROC_SCSI_IPH5526_FC, 7, "iph5526", S_IFDIR, S_IRUGO | S_IXUGO, 2 +}; + + +int iph5526_detect(Scsi_Host_Template *tmpt) +{ +struct Scsi_Host *host = NULL; +struct iph5526_hostdata *hostdata; +struct fc_info *fi = NULL; +int no_of_hosts = 0, timeout, i, j, count = 0; +u_int pci_maddr = 0; +struct pci_dev *pdev = NULL; + + tmpt->proc_dir = &proc_scsi_iph5526; + if (pci_present() == 0) { + printk("iph5526: PCI not present\n"); + return 0; + } + + for (i = 0; i <= MAX_FC_CARDS; i++) + fc[i] = NULL; + + for (i = 0; i < clone_list[i].vendor_id != 0; i++) + while ((pdev = pci_find_device(clone_list[i].vendor_id, clone_list[i].device_id, pdev))) { + unsigned short pci_command; + if (count < MAX_FC_CARDS) { + fc[count] = kmalloc(sizeof(struct fc_info), GFP_ATOMIC); + if (fc[count] == NULL) { + printk("iph5526.c: Unable to register card # %d\n", count + 1); + return no_of_hosts; + } + memset(fc[count], 0, sizeof(struct fc_info)); + } + else { + printk("iph5526.c: Maximum Number of cards reached.\n"); + return no_of_hosts; + } + + fi = fc[count]; + sprintf(fi->name, "fc%d", count); + + host = scsi_register(tmpt, sizeof(struct iph5526_hostdata)); + hostdata = (struct iph5526_hostdata *)host->hostdata; + memset(hostdata, 0 , sizeof(struct iph5526_hostdata)); + for (j = 0; j < MAX_SCSI_TARGETS; j++) + hostdata->tag_ages[j] = jiffies; + hostdata->fi = fi; + fi->host = host; + //host->max_id = MAX_SCSI_TARGETS; + host->max_id = 5; + host->hostt->use_new_eh_code = 1; + host->this_id = tmpt->this_id; + + pci_maddr = pdev->base_address[0]; + if ( (pci_maddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { + printk("iph5526.c : Cannot find proper PCI device base address.\n"); + scsi_unregister(host); + kfree(fc[count]); + fc[count] = NULL; + continue; + } + + pci_maddr &= PCI_BASE_ADDRESS_MEM_MASK; + DPRINTK("pci_maddr = %x", pci_maddr); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + + pci_irq_line = pdev->irq; + printk("iph5526.c: PCI BIOS reports %s at i/o %#x, irq %d.\n", clone_list[i].name, pci_maddr, pci_irq_line); + fi->g.mem_base = ioremap(pci_maddr & PAGE_MASK, 1024); + + /* We use Memory Mapped IO. The initial space contains the + * PCI Configuration registers followed by the (i) chip + * registers followed by the Tachyon registers. + */ + /* Thatz where (i)chip maps Tachyon Address Space. + */ + fi->g.tachyon_base = (u_long)fi->g.mem_base + TACHYON_OFFSET + ( pci_maddr & ~PAGE_MASK ); + DPRINTK("fi->g.tachyon_base = %x", (u_int)fi->g.tachyon_base); + if (fi->g.mem_base == NULL) { + printk("iph5526.c : ioremap failed!!!\n"); + scsi_unregister(host); + kfree(fc[count]); + fc[count] = NULL; + continue; + } + DPRINTK("IRQ1 = %d\n", pci_irq_line); + printk(version); + fi->base_addr = (long) pdev; + + if (pci_irq_line) { + int irqval = 0; + /* Found it, get IRQ. + */ + irqval = request_irq(pci_irq_line, &tachyon_interrupt, pci_irq_line ? SA_SHIRQ : 0, fi->name, host); + if (irqval) { + printk("iph5526.c : Unable to get IRQ %d (irqval = %d).\n", pci_irq_line, irqval); + scsi_unregister(host); + kfree(fc[count]); + fc[count] = NULL; + continue; + } + host->irq = fi->irq = pci_irq_line; + pci_irq_line = 0; + fi->clone_id = clone_list[i].vendor_id; + } + + if (!initialize_register_pointers(fi) || !tachyon_init(fi)) { + printk("iph5526.c: TACHYON initialization failed for card # %d!!!\n", count + 1); + free_irq(host->irq, host); + scsi_unregister(host); + if (fi) + clean_up_memory(fi); + kfree(fc[count]); + fc[count] = NULL; + break; + } + DPRINTK1("Fibre Channel card initialized"); + /* Wait for the Link to come up and the login process + * to complete. + */ + for(timeout = jiffies + 10*HZ; (timeout > jiffies) && ((fi->g.link_up == FALSE) || (fi->g.port_discovery == TRUE) || (fi->g.explore_fabric == TRUE) || (fi->g.perform_adisc == TRUE));) + barrier(); + + count++; + no_of_hosts++; + } + DPRINTK1("no_of_hosts = %d",no_of_hosts); + + /* This is to make sure that the ACC to the PRLI comes in + * for the last ALPA. + */ + udelay(1000000); /* Ugly! Let the Gods forgive me */ + + DPRINTK1("leaving iph5526_detect\n"); + return no_of_hosts; +} + + +int iph5526_biosparam(Disk * disk, kdev_t n, int ip[]) +{ +int size = disk->capacity; + ip[0] = 64; + ip[1] = 32; + ip[2] = size >> 11; + if (ip[2] > 1024) { + ip[0] = 255; + ip[1] = 63; + ip[2] = size / (ip[0] * ip[1]); + } + return 0; +} + +int iph5526_queuecommand(Scsi_Cmnd *Cmnd, void (*done) (Scsi_Cmnd *)) +{ +int int_required = 0; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_COMMAND; +u_int type = TYPE_FCP | SEQUENCE_INITIATIVE; +u_int frame_class = Cmnd->target; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +struct Scsi_Host *host = Cmnd->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata*)host->hostdata; +struct fc_info *fi = hostdata->fi; +struct fc_node_info *q; +u_long flags; + ENTER("iph5526_queuecommand"); + + spin_lock_irqsave(&fi->fc_lock, flags); + Cmnd->scsi_done = done; + + if (Cmnd->device->tagged_supported) { + switch(Cmnd->tag) { + case SIMPLE_QUEUE_TAG: + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_SIMPLE; + break; + case HEAD_OF_QUEUE_TAG: + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_HEAD_OF_Q; + break; + case ORDERED_QUEUE_TAG: + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_ORDERED; + break; + default: + if ((jiffies - hostdata->tag_ages[Cmnd->target]) > (5 * HZ)) { + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_ORDERED; + hostdata->tag_ages[Cmnd->target] = jiffies; + } + else + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_SIMPLE; + break; + } + } + /*else + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_UNTAGGED; + */ + + hostdata->cmnd.fcp_addr[3] = 0; + hostdata->cmnd.fcp_addr[2] = 0; + hostdata->cmnd.fcp_addr[1] = 0; + hostdata->cmnd.fcp_addr[0] = htons(Cmnd->lun); + + memcpy(&hostdata->cmnd.fcp_cdb, Cmnd->cmnd, Cmnd->cmd_len); + hostdata->cmnd.fcp_data_len = htonl(Cmnd->request_bufflen); + + /* Get an used OX_ID. We could have pending commands. + */ + if (get_scsi_oxid(fi)) + return 1; + fi->q.free_scsi_oxid[fi->g.scsi_oxid] = OXID_INUSE; + + /* Maintain a handler so that we can associate the done() function + * on completion of the SCSI command. + */ + hostdata->cmnd_handler[fi->g.scsi_oxid] = Cmnd; + + switch(Cmnd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_12: + fi->g.type_of_frame = FC_SCSI_WRITE; + hostdata->cmnd.fcp_cntl = htonl(FCP_CNTL_WRITE | hostdata->cmnd.fcp_cntl); + break; + default: + fi->g.type_of_frame = FC_SCSI_READ; + hostdata->cmnd.fcp_cntl = htonl(FCP_CNTL_READ | hostdata->cmnd.fcp_cntl); + } + + memcpy(fi->q.ptr_fcp_cmnd[fi->q.fcp_cmnd_indx], &(hostdata->cmnd), sizeof(fcp_cmd)); + + q = resolve_target(fi, Cmnd->target); + + if (q == NULL) { + u_int bad_id = fi->g.my_ddaa | 0xFE; + /* We transmit to an non-existant AL_PA so that the "done" + * function can be called while receiving the interrupt + * due to a Timeout for a bad AL_PA. In a PTP configuration, + * the int_required field is set, since there is no notion + * of AL_PAs. This approach sucks, but works alright! + */ + if (fi->g.ptp_up == TRUE) + int_required = 1; + tx_exchange(fi, (char *)(&(hostdata->cmnd)), sizeof(fcp_cmd), r_ctl, type, bad_id, fi->g.my_mtu, int_required, ox_id, FC_SCSI_BAD_TARGET); + spin_unlock_irqrestore(&fi->fc_lock, flags); + DPRINTK1("Target ID %x not present", Cmnd->target); + return 0; + } + if (q->login == LOGIN_COMPLETED) { + if (add_to_sest(fi, Cmnd, q)) { + DPRINTK1("add_to_sest() failed."); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return 0; + } + tx_exchange(fi, (char *)(fi->q.ptr_fcp_cmnd[fi->q.fcp_cmnd_indx]), sizeof(fcp_cmd), r_ctl, type, q->d_id, q->mtu, int_required, ox_id, frame_class << 16); + update_FCP_CMND_indx(fi); + } + spin_unlock_irqrestore(&fi->fc_lock, flags); + /* If q != NULL, then we have a SCSI Target. + * If q->login != LOGIN_COMPLETED, then that device could be + * offline temporarily. So we let the command to time-out. + */ + LEAVE("iph5526_queuecommand"); + return 0; +} + +int iph5526_abort(Scsi_Cmnd *Cmnd) +{ +struct Scsi_Host *host = Cmnd->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +struct fc_info *fi = hostdata->fi; +struct fc_node_info *q; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_COMMAND; +u_int type = TYPE_FCP | SEQUENCE_INITIATIVE; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +int int_required = 1, i, abort_status = FALSE; +u_long flags; + + ENTER("iph5526_abort"); + + spin_lock_irqsave(&fi->fc_lock, flags); + + q = resolve_target(fi, Cmnd->target); + if (q == NULL) { + u_int bad_id = fi->g.my_ddaa | 0xFE; + /* This should not happen as we should always be able to + * resolve a target id. But, jus in case... + * We transmit to an non-existant AL_PA so that the done + * function can be called while receiving the interrupt + * for a bad AL_PA. + */ + DPRINTK1("Unresolved Target ID!"); + tx_exchange(fi, (char *)(&(hostdata->cmnd)), sizeof(fcp_cmd), r_ctl, type, bad_id, fi->g.my_mtu, int_required, ox_id, FC_SCSI_BAD_TARGET); + DPRINTK1("Target ID %x not present", Cmnd->target); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return FAILED; + } + + /* If q != NULL, then we have a SCSI Target. If + * q->login != LOGIN_COMPLETED, then that device could + * be offline temporarily. So we let the command to time-out. + */ + + /* Get the OX_ID for the Command to be aborted. + */ + for (i = 0; i <= MAX_SCSI_XID; i++) { + if (hostdata->cmnd_handler[i] == Cmnd) { + hostdata->cmnd_handler[i] = NULL; + ox_id = i; + break; + } + } + if (i > MAX_SCSI_XID) { + T_MSG("Command could not be resolved to OX_ID"); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return FAILED; + } + + switch(Cmnd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_12: + break; + default: + ox_id |= SCSI_READ_BIT; + } + abort_status = abort_exchange(fi, ox_id); + + if ((q->login == LOGIN_COMPLETED) && (abort_status == TRUE)) { + /* Then, transmit an ABTS to the target. The rest + * is done when the BA_ACC is received for the ABTS. + */ + tx_abts(fi, q->d_id, ox_id); + } + else { + u_int STE_bit; + u_short x_id; + /* Invalidate resources for that Exchange. + */ + x_id = ox_id & MAX_SCSI_XID; + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + if (STE_bit & SEST_V) { + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + invalidate_SEST_entry(fi, ox_id); + } + } + + LEAVE("iph5526_abort"); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return SUCCESS; +} + +static int abort_exchange(struct fc_info *fi, u_short ox_id) +{ +u_short x_id; +volatile u_int flush_SEST, STE_bit; + x_id = ox_id & MAX_SCSI_XID; + DPRINTK1("Aborting Exchange %x", ox_id); + + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + /* Is the Exchange still active?. + */ + if (STE_bit & SEST_V) { + if (ox_id & SCSI_READ_BIT) { + /* If the Exchange to be aborted is Inbound, + * Flush the SEST Entry from Tachyon's Cache. + */ + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + flush_tachyon_cache(fi, ox_id); + flush_SEST = readl(fi->t_r.ptr_tach_flush_oxid_reg); + while ((flush_SEST & 0x80000000) != 0) + flush_SEST = readl(fi->t_r.ptr_tach_flush_oxid_reg); + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + while ((STE_bit & 0x80000000) != 0) + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + flush_SEST = readl(fi->t_r.ptr_tach_flush_oxid_reg); + invalidate_SEST_entry(fi, ox_id); + } + else { + int i; + u_int *ptr_edb; + /* For In-Order Reassembly, the following is done: + * First, write zero as the buffer length in the EDB. + */ + ptr_edb = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 7))); + for (i = 0; i < EDB_LEN; i++) + if (fi->q.ptr_edb[i] == ptr_edb) + break; + if (i < EDB_LEN) + *ptr_edb = *ptr_edb & 0x0000FFFF; + else + T_MSG("EDB not found while clearing in abort_exchange()"); + } + DPRINTK1("Exchange %x invalidated", ox_id); + return TRUE; + } + else { + DPRINTK1("SEST Entry for exchange %x not valid", ox_id); + return FALSE; + } +} + +static void flush_tachyon_cache(struct fc_info *fi, u_short ox_id) +{ +volatile u_int tachyon_status; + if (fi->g.loop_up == TRUE) { + writel(HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + /* Make sure that the Inbound FIFO is empty. + */ + do { + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + udelay(200); + }while ((tachyon_status & RECEIVE_FIFO_EMPTY) == 0); + /* Ok. Go ahead and flushhhhhhhhh! + */ + writel(0x80000000 | ox_id, fi->t_r.ptr_tach_flush_oxid_reg); + writel(EXIT_HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + return; + } + if (fi->g.ptp_up == TRUE) { + take_tachyon_offline(fi); + /* Make sure that the Inbound FIFO is empty. + */ + do { + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + udelay(200); + }while ((tachyon_status & RECEIVE_FIFO_EMPTY) == 0); + writel(0x80000000 | ox_id, fi->t_r.ptr_tach_flush_oxid_reg); + /* Write the Initialize command to the FM Control reg. + */ + fi->g.n_port_try = TRUE; + DPRINTK1("In abort_exchange, TACHYON initializing as N_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } +} + +static struct fc_node_info *resolve_target(struct fc_info *fi, u_char target) +{ +struct fc_node_info *temp = fi->node_info_list; + while(temp != NULL) + if (temp->target_id == target) { + if ((temp->scsi == TARGET) && (temp->login == LOGIN_COMPLETED)) + return temp; + else { + if (temp->login != LOGIN_COMPLETED) { + /* The Target is not currently logged in. + * It could be a Target on the Local Loop or + * on a Remote Loop connected through a switch. + * In either case, we will know whenever the Target + * comes On-Line again. We let the command to + * time-out so that it gets retried. + */ + T_MSG("Target %d not logged in.", temp->target_id); + tx_logi(fi, ELS_PLOGI, temp->d_id); + return temp; + } + else { + if (temp->scsi != TARGET) { + /* For some reason, we did not get a response to + * PRLI. Letz try it again... + */ + DPRINTK1("Node not PRLIied. Txing PRLI..."); + tx_prli(fi, ELS_PRLI, temp->d_id, OX_ID_FIRST_SEQUENCE); + } + } + return temp; + } + } + else + temp = temp->next; + return NULL; +} + +static int add_to_sest(struct fc_info *fi, Scsi_Cmnd *Cmnd, struct fc_node_info *ni) +{ +/* we have at least 1 buffer, the terminator */ +int no_of_sdb_buffers = 1, i; +int no_of_edb_buffers = 0; +u_int *req_buffer = (u_int *)Cmnd->request_buffer; +u_int *ptr_sdb = NULL; +struct scatterlist *sl1, *sl2 = NULL; +int no_of_sg = 0; + + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + fi->g.inb_sest_entry.flags_and_byte_offset = htonl(INB_SEST_VED); + fi->g.inb_sest_entry.byte_count = 0; + fi->g.inb_sest_entry.no_of_recvd_frames = 0; + fi->g.inb_sest_entry.no_of_expected_frames = 0; + fi->g.inb_sest_entry.last_fctl = 0; + + if (Cmnd->use_sg) { + no_of_sg = Cmnd->use_sg; + sl1 = sl2 = (struct scatterlist *)Cmnd->request_buffer; + for (i = 0; i < no_of_sg; i++) { + no_of_sdb_buffers += sl1->length / SEST_BUFFER_SIZE; + if (sl1->length % SEST_BUFFER_SIZE) + no_of_sdb_buffers++; + sl1++; + } + } + else { + no_of_sdb_buffers += Cmnd->request_bufflen / SEST_BUFFER_SIZE; + if (Cmnd->request_bufflen % SEST_BUFFER_SIZE) + no_of_sdb_buffers++; + } /* if !use_sg */ + + /* We are working with the premise that at the max we would + * get a scatter-gather buffer containing 63 buffers + * of size 1024 bytes each. Is it a _bad_ assumption? + */ + if (no_of_sdb_buffers > 512) { + T_MSG("Number of SDB buffers needed = %d", no_of_sdb_buffers); + T_MSG("Disable Scatter-Gather!!!"); + return 1; + } + + + /* Store it in the sdb_table so that we can retrieve that + * free up the memory when the Read Command completes. + */ + if (get_free_SDB(fi)) + return 1; + ptr_sdb = fi->q.ptr_sdb_slot[fi->q.sdb_indx]; + fi->q.sdb_slot_status[fi->q.sdb_indx] = SDB_BUSY; + fi->g.inb_sest_entry.sdb_address = htonl(virt_to_bus(ptr_sdb)); + + if (Cmnd->use_sg) { + int count = 0, j; + for(i = 0; i < no_of_sg; i++) { + char *addr_ptr = sl2->address; + count = sl2->length / SEST_BUFFER_SIZE; + if (sl2->length % SEST_BUFFER_SIZE) + count++; + for (j = 0; j < count; j++) { + *(ptr_sdb) = htonl(virt_to_bus(addr_ptr)); + addr_ptr += SEST_BUFFER_SIZE; + ptr_sdb++; + } + count = 0; + sl2++; + } + } + else { + for (i = 0; i < no_of_sdb_buffers - 1; i++) { + *(ptr_sdb) = htonl(virt_to_bus(req_buffer)); + req_buffer += SEST_BUFFER_SIZE/4; + ptr_sdb++; + } + } + *(ptr_sdb) = htonl(0x1); /* Terminator */ + + /* The scratch pad is used to hold the index into the SDB. + */ + fi->g.inb_sest_entry.scratch_pad = fi->q.sdb_indx; + fi->g.inb_sest_entry.expected_ro = 0; + fi->g.inb_sest_entry.buffer_index = 0; + fi->g.inb_sest_entry.buffer_offset = 0; + memcpy(fi->q.ptr_sest[fi->g.scsi_oxid], &fi->g.inb_sest_entry, sizeof(INB_SEST_ENTRY)); + break; + case FC_SCSI_WRITE: + fi->g.outb_sest_entry.flags_and_did = htonl(OUTB_SEST_VED | ni->d_id); + fi->g.outb_sest_entry.max_frame_len = htons(ni->mtu << 4); + fi->g.outb_sest_entry.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | ODB_NO_INT | ODB_NO_COMP); + fi->g.outb_sest_entry.total_seq_length = INV_SEQ_LEN; + fi->g.outb_sest_entry.link = htons(OUTB_SEST_LINK); + fi->g.outb_sest_entry.transaction_id = htonl(fi->g.scsi_oxid); + fi->g.outb_sest_entry.seq_id = fi->g.seq_id; + fi->g.outb_sest_entry.reserved = 0x0; + fi->g.outb_sest_entry.header_length = htons(TACHYON_HEADER_LEN); + + { + u_char df_ctl = 0; + u_short rx_id = RX_ID_FIRST_SEQUENCE; + u_int r_ctl = FC4_DEVICE_DATA | SOLICITED_DATA; + u_int type = TYPE_FCP | SEQUENCE_INITIATIVE; + /* Multi Frame Sequence ? If yes, set RO bit. + */ + if (Cmnd->request_bufflen > ni->mtu) + type |= RELATIVE_OFF_PRESENT; + build_tachyon_header(fi, fi->g.my_id, r_ctl, ni->d_id, type, fi->g.seq_id, df_ctl, fi->g.scsi_oxid, rx_id, NULL); + if (get_free_header(fi) || get_free_EDB(fi)) + return 1; + memcpy(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx], &(fi->g.tach_header), TACHYON_HEADER_LEN); + fi->g.outb_sest_entry.header_address = htonl(virt_to_bus(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx])); + update_tachyon_header_indx(fi); + } + + if (Cmnd->use_sg) { + no_of_sg = Cmnd->use_sg; + sl1 = sl2 = (struct scatterlist *)Cmnd->request_buffer; + for (i = 0; i < no_of_sg; i++) { + no_of_edb_buffers += sl1->length / SEST_BUFFER_SIZE; + if (sl1->length % SEST_BUFFER_SIZE) + no_of_edb_buffers++; + sl1++; + } + } + else { + no_of_edb_buffers += Cmnd->request_bufflen / SEST_BUFFER_SIZE; + if (Cmnd->request_bufflen % SEST_BUFFER_SIZE) + no_of_edb_buffers++; + } /* if !use_sg */ + + + /* We need "no_of_edb_buffers" _contiguous_ EDBs + * that are FREE. Check for that first. + */ + for (i = 0; i < no_of_edb_buffers; i++) { + int j; + if ((fi->q.edb_buffer_indx + no_of_edb_buffers) >= EDB_LEN) + fi->q.edb_buffer_indx = 0; + if (fi->q.free_edb_list[fi->q.edb_buffer_indx + i] != EDB_FREE) { + for (j = 0; j < i; j++) + update_EDB_indx(fi); + if (get_free_EDB(fi)) + return 1; + i = 0; + } + } + + /* We got enuff FREE EDBs. + */ + if (Cmnd->use_sg) { + fi->g.outb_sest_entry.edb_address = htonl(virt_to_bus(fi->q.ptr_edb[fi->q.edb_buffer_indx])); + sl1 = (struct scatterlist *)Cmnd->request_buffer; + for(i = 0; i < no_of_sg; i++) { + int count = 0, j; + count = sl1->length / SEST_BUFFER_SIZE; + for (j = 0; j < count; j++) { + build_EDB(fi, (char *)sl1->address, 0, SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + /* Mark this EDB as being in use */ + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + /* We have already made sure that we have enuff + * free EDBs that are contiguous. So this is + * safe. + */ + update_EDB_indx(fi); + sl1->address += SEST_BUFFER_SIZE; + } + /* Just in case itz not a multiple of + * SEST_BUFFER_SIZE bytes. + */ + if (sl1->length % SEST_BUFFER_SIZE) { + build_EDB(fi, (char *)sl1->address, 0, sl1->length % SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + update_EDB_indx(fi); + } + sl1++; + } + /* The last EDB is special. It needs the "end bit" to + * be set. + */ + *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) = *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) | ntohs(EDB_END); + } + else { + int count = 0, j; + fi->g.outb_sest_entry.edb_address = htonl(virt_to_bus(fi->q.ptr_edb[fi->q.edb_buffer_indx])); + count = Cmnd->request_bufflen / SEST_BUFFER_SIZE; + for (j = 0; j < count; j++) { + build_EDB(fi, (char *)req_buffer, 0, SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + /* Mark this EDB as being in use */ + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + /* We have already made sure that we have enuff + * free EDBs that are contiguous. So this is + * safe. + */ + update_EDB_indx(fi); + req_buffer += SEST_BUFFER_SIZE; + } + /* Just in case itz not a multiple of + * SEST_BUFFER_SIZE bytes. + */ + if (Cmnd->request_bufflen % SEST_BUFFER_SIZE) { + build_EDB(fi, (char *)req_buffer, EDB_END, Cmnd->request_bufflen % SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + update_EDB_indx(fi); + } + else { + /* Mark the last EDB as the "end edb". + */ + *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) = *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) | htons(EDB_END); + } + } + + /* Finally we have something to send!. + */ + memcpy(fi->q.ptr_sest[fi->g.scsi_oxid], &fi->g.outb_sest_entry, sizeof(OUTB_SEST_ENTRY)); + break; + } + return 0; +} + +static void update_FCP_CMND_indx(struct fc_info *fi) +{ + fi->q.fcp_cmnd_indx++; + if (fi->q.fcp_cmnd_indx == NO_OF_FCP_CMNDS) + fi->q.fcp_cmnd_indx = 0; +} + +static int get_scsi_oxid(struct fc_info *fi) +{ +u_short initial_oxid = fi->g.scsi_oxid; + /* Check if the OX_ID is in use. + * We could have an outstanding SCSI command. + */ + while (fi->q.free_scsi_oxid[fi->g.scsi_oxid] != OXID_AVAILABLE) { + update_scsi_oxid(fi); + if (fi->g.scsi_oxid == initial_oxid) { + T_MSG("No free OX_IDs avaliable") + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + } + return 0; +} + +static void update_scsi_oxid(struct fc_info *fi) +{ + fi->g.scsi_oxid++; + if (fi->g.scsi_oxid == (MAX_SCSI_XID + 1)) + fi->g.scsi_oxid = 0; +} + +static int get_free_SDB(struct fc_info *fi) +{ +unsigned int initial_indx = fi->q.sdb_indx; + /* Check if the SDB is in use. + * We could have an outstanding SCSI Read command. + * We should find a free slot as we can queue a + * maximum of 32 SCSI commands only. + */ + while (fi->q.sdb_slot_status[fi->q.sdb_indx] != SDB_FREE) { + update_SDB_indx(fi); + if (fi->q.sdb_indx == initial_indx) { + T_MSG("No free SDB buffers avaliable") + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + } + return 0; +} + +static void update_SDB_indx(struct fc_info *fi) +{ + fi->q.sdb_indx++; + if (fi->q.sdb_indx == NO_OF_SDB_ENTRIES) + fi->q.sdb_indx = 0; +} + +int iph5526_release(struct Scsi_Host *host) +{ +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata*)host->hostdata; +struct fc_info *fi = hostdata->fi; + free_irq(host->irq, host); + iounmap(fi->g.mem_base); + return 0; +} + +const char *iph5526_info(struct Scsi_Host *host) +{ +static char buf[80]; + sprintf(buf, "Interphase 5526 Fibre Channel PCI SCSI Adapter using IRQ %d\n", host->irq); + return buf; +} + +#ifdef MODULE + +#define NAMELEN 8 /* # of chars for storing dev->name */ + +static struct device *dev_fc[MAX_FC_CARDS]; + +static int io = 0; +static int irq = 0; +static int bad = 0; /* 0xbad = bad sig or no reset ack */ +static int scsi_registered; + + +int init_module(void) +{ +int i = 0; + + driver_template.module = &__this_module; + scsi_register_module(MODULE_SCSI_HA, &driver_template); + if (driver_template.present) + scsi_registered = TRUE; + else { + printk("iph5526: SCSI registeration failed!!!\n"); + scsi_registered = FALSE; + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + } + + while(fc[i] != NULL) { + dev_fc[i] = NULL; + dev_fc[i] = init_fcdev(dev_fc[i], 0); + if (dev_fc[i] == NULL) { + printk("iph5526.c: init_fcdev failed for card #%d\n", i+1); + break; + } + dev_fc[i]->irq = irq; + dev_fc[i]->mem_end = bad; + dev_fc[i]->base_addr = io; + dev_fc[i]->init = iph5526_probe; + dev_fc[i]->priv = fc[i]; + fc[i]->dev = dev_fc[i]; + if (register_fcdev(dev_fc[i]) != 0) { + kfree_s(dev_fc[i], sizeof(struct device)); + dev_fc[i] = NULL; + if (i == 0) { + printk("iph5526.c: IP registeration failed!!!\n"); + return -ENODEV; + } + } + i++; + } + if (i == 0) + return -ENODEV; + + return 0; +} + +void cleanup_module(void) +{ +int i = 0; + while(fc[i] != NULL) { + struct device *dev = fc[i]->dev; + void *priv = dev->priv; + fc[i]->g.dont_init = TRUE; + take_tachyon_offline(fc[i]); + unregister_fcdev(dev); + clean_up_memory(fc[i]); + if (dev->priv) + kfree(priv); + kfree(dev); + dev = NULL; + i++; + } + if (scsi_registered == TRUE) + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); +} +#endif /* MODULE */ + +void clean_up_memory(struct fc_info *fi) +{ +int i,j; + ENTER("clean_up_memory"); + if (fi->q.ptr_mfsbq_base) + free_pages((u_long)bus_to_virt(ntohl(*(fi->q.ptr_mfsbq_base))), 5); + DPRINTK("after kfree2"); + for (i = 0; i < SFSBQ_LENGTH; i++) + for (j = 0; j < NO_OF_ENTRIES; j++) + if (fi->q.ptr_sfs_buffers[i*NO_OF_ENTRIES + j]) + kfree(fi->q.ptr_sfs_buffers[i*NO_OF_ENTRIES + j]); + DPRINTK("after kfree1"); + if (fi->q.ptr_ocq_base) + free_page((u_long)fi->q.ptr_ocq_base); + if (fi->q.ptr_imq_base) + free_page((u_long)fi->q.ptr_imq_base); + if (fi->q.ptr_mfsbq_base) + free_page((u_long)fi->q.ptr_mfsbq_base); + if (fi->q.ptr_sfsbq_base) + free_page((u_long)fi->q.ptr_sfsbq_base); + if (fi->q.ptr_edb_base) + free_pages((u_long)fi->q.ptr_edb_base, 5); + if (fi->q.ptr_sest_base) + free_pages((u_long)fi->q.ptr_sest_base, 5); + if (fi->q.ptr_tachyon_header_base) + free_page((u_long)fi->q.ptr_tachyon_header_base); + if (fi->q.ptr_sdb_base) + free_pages((u_long)fi->q.ptr_sdb_base, 5); + if (fi->q.ptr_fcp_cmnd_base) + free_page((u_long)fi->q.ptr_fcp_cmnd_base); + DPRINTK("after free_pages"); + if (fi->q.ptr_host_ocq_cons_indx) + kfree(fi->q.ptr_host_ocq_cons_indx); + if (fi->q.ptr_host_hpcq_cons_indx) + kfree(fi->q.ptr_host_hpcq_cons_indx); + if (fi->q.ptr_host_imq_prod_indx) + kfree(fi->q.ptr_host_imq_prod_indx); + DPRINTK("after kfree3"); + while (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + fi->node_info_list = fi->node_info_list->next; + kfree(temp_list); + } + while (fi->ox_id_list) { + struct ox_id_els_map *temp = fi->ox_id_list; + fi->ox_id_list = fi->ox_id_list->next; + kfree(temp); + } + LEAVE("clean_up_memory"); +} + +static int initialize_register_pointers(struct fc_info *fi) +{ +ENTER("initialize_register_pointers"); +if(fi->g.tachyon_base == 0) + return -ENOMEM; + +fi->i_r.ptr_ichip_hw_control_reg = ICHIP_HW_CONTROL_REG_OFF + fi->g.tachyon_base; +fi->i_r.ptr_ichip_hw_status_reg = ICHIP_HW_STATUS_REG_OFF + fi->g.tachyon_base; +fi->i_r.ptr_ichip_hw_addr_mask_reg = ICHIP_HW_ADDR_MASK_REG_OFF + fi->g.tachyon_base; +fi->t_r.ptr_ocq_base_reg = OCQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_ocq_len_reg = OCQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_ocq_prod_indx_reg = OCQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_ocq_cons_indx_reg = OCQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_base_reg = IMQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_len_reg = IMQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_cons_indx_reg = IMQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_prod_indx_reg = IMQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_base_reg = MFSBQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_len_reg = MFSBQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_prod_reg = MFSBQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_cons_reg = MFSBQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbuff_len_reg = MFS_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_base_reg = SFSBQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_len_reg = SFSBQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_prod_reg = SFSBQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_cons_reg = SFSBQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbuff_len_reg = SFS_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sest_base_reg = SEST_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sest_len_reg = SEST_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_scsibuff_len_reg = SCSI_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_config_reg = TACHYON_CONFIG_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_control_reg = TACHYON_CONTROL_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_status_reg = TACHYON_STATUS_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_flush_oxid_reg = TACHYON_FLUSH_SEST_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_config_reg = FMGR_CONFIG_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_control_reg = FMGR_CONTROL_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_status_reg = FMGR_STATUS_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_tov_reg = FMGR_TIMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_wwn_hi_reg = FMGR_WWN_HI_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_wwn_low_reg = FMGR_WWN_LO_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_rx_al_pa_reg = FMGR_RCVD_ALPA_REGISTER_OFFSET + fi->g.tachyon_base; + +LEAVE("initialize_register_pointers"); +return 1; +} + + + +/* + * Local variables: + * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c iph5526.c" + * version-control: t + * kept-new-versions: 5 + * End: + */ diff --git a/drivers/net/fc/iph5526_ip.h b/drivers/net/fc/iph5526_ip.h new file mode 100644 index 000000000000..e304260e9f54 --- /dev/null +++ b/drivers/net/fc/iph5526_ip.h @@ -0,0 +1,25 @@ +#ifndef IPH5526_IP_H +#define IPH5526_IP_H + +#define LLC_SNAP_LEN 0x8 + +/* Offsets into the ARP frame */ +#define ARP_OPCODE_0 (0x6 + LLC_SNAP_LEN) +#define ARP_OPCODE_1 (0x7 + LLC_SNAP_LEN) + +int iph5526_probe(struct device *dev); +static int fcdev_init(struct device *dev); +static int iph5526_open(struct device *dev); +static int iph5526_close(struct device *dev); +static int iph5526_send_packet(struct sk_buff *skb, struct device *dev); +static struct net_device_stats * iph5526_get_stats(struct device *dev); +static int iph5526_change_mtu(struct device *dev, int mtu); + + +static void rx_net_packet(struct fc_info *fi, u_char *buff_addr, int payload_size); +static void rx_net_mfs_packet(struct fc_info *fi, struct sk_buff *skb); +unsigned short fc_type_trans(struct sk_buff *skb, struct device *dev); +static int tx_ip_packet(struct sk_buff *skb, unsigned long len, struct fc_info *fi); +static int tx_arp_packet(char *data, unsigned long len, struct fc_info *fi); +#endif + diff --git a/drivers/net/fc/iph5526_novram.c b/drivers/net/fc/iph5526_novram.c new file mode 100644 index 000000000000..d73587be0325 --- /dev/null +++ b/drivers/net/fc/iph5526_novram.c @@ -0,0 +1,278 @@ +/********************************************************************** + * Reading the NVRAM on the Interphase 5526 PCI Fibre Channel Card. + * All contents in this file : courtesy Interphase Corporation. + * Special thanks to Kevin Quick, kquick@iphase.com. + **********************************************************************/ + +#define FF_MAGIC 0x4646 +#define DB_MAGIC 0x4442 +#define DL_MAGIC 0x444d + + +#define CMD_LEN 9 + +/*********** + * + * Switches and defines for header files. + * + * The following defines are used to turn on and off + * various options in the header files. Primarily useful + * for debugging. + * + ***********/ + +static const unsigned short novram_default[4] = { + FF_MAGIC, + DB_MAGIC, + DL_MAGIC, + 0 }; + + +/* + * a list of the commands that can be sent to the NOVRAM + */ + +#define NR_EXTEND 0x100 +#define NR_WRITE 0x140 +#define NR_READ 0x180 +#define NR_ERASE 0x1c0 + +#define EWDS 0x00 +#define WRAL 0x10 +#define ERAL 0x20 +#define EWEN 0x30 + +/* + * Defines for the pins on the NOVRAM + */ + +#define BIT(x) (1 << (x)) + +#define NVDI_B 31 +#define NVDI BIT(NVDI_B) +#define NVDO BIT(9) +#define NVCE BIT(30) +#define NVSK BIT(29) +#define NV_MANUAL BIT(28) + +/*********** + * + * Include files. + * + ***********/ + +#define KeStallExecutionProcessor(x) {volatile int d, p;\ + for (d=0; dn_r.ptr_novram_hw_control_reg); \ + t &= (val); \ + writel(t, fi->n_r.ptr_novram_hw_control_reg); \ + } + +/*********************** + * + * This define ors the value and the current config register and puts + * the result in the config register + * + ***********************/ + +#define CFG_OR(val) { volatile int t; \ + t = readl(fi->n_r.ptr_novram_hw_control_reg); \ + t |= (val); \ + writel(t, fi->n_r.ptr_novram_hw_control_reg); \ + } + +/*********************** + * + * Send a command to the NOVRAM, the command is in cmd. + * + * clear CE and SK. Then assert CE. + * Clock each of the command bits out in the correct order with SK + * exit with CE still asserted + * + ***********************/ + +#define NVRAM_CMD(cmd) { int i; \ + int c = cmd; \ + CFG_AND(~(NVCE|NVSK)); \ + CFG_OR(NVCE); \ + for (i=0; in_r.ptr_novram_hw_status_reg) & NVDO) ? 1 : 0; \ + } + +/*********** + * + * Function Prototypes + * + ***********/ + +static int iph5526_nr_get(struct fc_info *fi, int addr); +static void iph5526_nr_do_init(struct fc_info *fi); +static void iph5526_nr_checksum(struct fc_info *fi); + + +/******************************************************************* + * + * Local routine: iph5526_nr_do_init + * Purpose: initialize novram server + * Description: + * + * iph5526_nr_do_init reads the novram into the temporary holding place. + * A checksum is done on the area and the Magic Cookies are checked. + * If any of them are bad, the NOVRAM is initialized with the + * default values and a warning message is displayed. + * + *******************************************************************/ + +static void iph5526_nr_do_init(struct fc_info *fi) +{ + int i; + unsigned short chksum = 0; + int bad = 0; + + for (i=0; in_r.data[i] = iph5526_nr_get(fi, i); + chksum += fi->n_r.data[i]; + } + + if (chksum) + bad = 1; + + if (fi->n_r.data[IPH5526_NOVRAM_SIZE - 4] != FF_MAGIC) + bad = 1; + if (fi->n_r.data[IPH5526_NOVRAM_SIZE - 3] != DB_MAGIC) + bad = 1; + if (fi->n_r.data[IPH5526_NOVRAM_SIZE - 2] != DL_MAGIC) + bad = 1; + + if (bad) { + for (i=0; in_r.data[i] = 0xffff; + } else { + fi->n_r.data[i] = novram_default[i - (IPH5526_NOVRAM_SIZE - 4)]; + } + } + iph5526_nr_checksum(fi); + } +} + + +/******************************************************************* + * + * Local routine: iph5526_nr_get + * Purpose: read a single word of NOVRAM + * Description: + * + * read the 16 bits that make up a word addr of the novram. + * The 16 bits of data that are read are returned as the return value + * + *******************************************************************/ + +static int iph5526_nr_get(struct fc_info *fi, int addr) +{ + int i; + int t; + int val = 0; + + CFG_OR(NV_MANUAL); + + /* + * read the first bit that was clocked with the falling edge of the + * the last command data clock + */ + + NVRAM_CMD(NR_READ + addr); + + /* + * Now read the rest of the bits, the next bit read is D1, then D2, + * and so on + */ + + val = 0; + for (i=0; i<16; i++) { + NVRAM_CLKIN(t); + val <<= 1; + val |= t; + } + NVRAM_CLR_CE; + + CFG_OR(NVDI); + CFG_AND(~NV_MANUAL); + + return(val); +} + + + + +/******************************************************************* + * + * Local routine: iph5526_nr_checksum + * Purpose: calculate novram checksum on fi->n_r.data + * Description: + * + * calculate a checksum for the novram on the image that is + * currently in fi->n_r.data + * + *******************************************************************/ + +static void iph5526_nr_checksum(struct fc_info *fi) +{ + int i; + unsigned short chksum = 0; + + for (i=0; i<(IPH5526_NOVRAM_SIZE - 1); i++) + chksum += fi->n_r.data[i]; + + fi->n_r.data[i] = -chksum; +} diff --git a/drivers/net/fc/iph5526_scsi.h b/drivers/net/fc/iph5526_scsi.h new file mode 100644 index 000000000000..18221157ba29 --- /dev/null +++ b/drivers/net/fc/iph5526_scsi.h @@ -0,0 +1,31 @@ +#ifndef IPH5526_SCSI_H +#define IPH5526_SCSI_H + +#define IPH5526_CAN_QUEUE 32 +#define IPH5526_SCSI_FC { \ + name: "Interphase 5526 Fibre Channel SCSI Adapter", \ + detect: iph5526_detect, \ + release: iph5526_release, \ + info: iph5526_info, \ + queuecommand: iph5526_queuecommand, \ + bios_param: iph5526_biosparam, \ + can_queue: IPH5526_CAN_QUEUE, \ + this_id: -1, \ + sg_tablesize: 255, \ + cmd_per_lun: 8, \ + use_clustering: DISABLE_CLUSTERING, \ + eh_abort_handler: iph5526_abort, \ + eh_device_reset_handler:NULL, \ + eh_bus_reset_handler: NULL, \ + eh_host_reset_handler: NULL, \ +} + +int iph5526_detect(Scsi_Host_Template *tmpt); +int iph5526_queuecommand(Scsi_Cmnd *Cmnd, void (*done) (Scsi_Cmnd *)); +int iph5526_release(struct Scsi_Host *host); +int iph5526_abort(Scsi_Cmnd *Cmnd); +const char *iph5526_info(struct Scsi_Host *host); +int iph5526_biosparam(Disk * disk, kdev_t n, int ip[]); + +#endif + diff --git a/drivers/net/fc/tach.h b/drivers/net/fc/tach.h new file mode 100644 index 000000000000..741812c5f554 --- /dev/null +++ b/drivers/net/fc/tach.h @@ -0,0 +1,475 @@ +/********************************************************************** + * Defines for the Tachyon Fibre Channel Controller and the Interphase + * (i)chip TPI. + *********************************************************************/ + +#ifndef _TACH_H +#define _TACH_H + +#define MY_PAGE_SIZE 4096 +#define REPLICATE 0xFF +#define MAX_NODES 127 +#define BROADCAST 0xFFFFFF +#define BROADCAST_ADDR 0xFFFFFFFFFFFF +#define LOGIN_COMPLETED 2 +#define LOGIN_ATTEMPTED 1 +#define LOGIN_NOT_ATTEMPTED 0 +#define TRUE 1 +#define FALSE 0 + +#define TACHYON_LIMIT 0x01EF +#define TACHYON_OFFSET 0x200 + +/* Offsets to the (i) chip */ +#define ICHIP_HW_CONTROL_REG_OFF (0x080 - TACHYON_OFFSET) +#define ICHIP_HW_STATUS_REG_OFF (0x084 - TACHYON_OFFSET) +#define ICHIP_HW_ADDR_MASK_REG_OFF (0x090 - TACHYON_OFFSET) + +/* (i)chip Hardware Control Register defines */ +#define ICHIP_HCR_RESET 0x01 +#define ICHIP_HCR_DERESET 0x0 +#define ICHIP_HCR_ENABLE_INTA 0x0000003E +#define ICHIP_HCR_ENABLE_INTB 0x003E0000 +#define ICHIP_HCR_IWDATA_FIFO 0x800000 + +/* (i)chip Hardware Status Register defines */ +#define ICHIP_HSR_INT_LATCH 0x02 + +/* (i)chip Hardware Address Mask Register defines */ +#define ICHIP_HAMR_BYTE_SWAP_ADDR_TR 0x08 +#define ICHIP_HAMR_BYTE_SWAP_NO_ADDR_TR 0x04 + +/* NOVRAM defines */ +#define IPH5526_NOVRAM_SIZE 64 + + +/* Offsets for the registers that correspond to the + * Qs on the Tachyon (As defined in the Tachyon Manual). + */ + +/* Outbound Command Queue (OCQ). + */ +#define OCQ_BASE_REGISTER_OFFSET 0x000 +#define OCQ_LENGTH_REGISTER_OFFSET 0x004 +#define OCQ_PRODUCER_REGISTER_OFFSET 0x008 +#define OCQ_CONSUMER_REGISTER_OFFSET 0x00C + +/* Inbound Message Queue (IMQ). + */ +#define IMQ_BASE_REGISTER_OFFSET 0x080 +#define IMQ_LENGTH_REGISTER_OFFSET 0x084 +#define IMQ_CONSUMER_REGISTER_OFFSET 0x088 +#define IMQ_PRODUCER_REGISTER_OFFSET 0x08C + +/* Multiframe Sequence Buffer Queue (MFSBQ) + */ +#define MFSBQ_BASE_REGISTER_OFFSET 0x0C0 +#define MFSBQ_LENGTH_REGISTER_OFFSET 0x0C4 +#define MFSBQ_PRODUCER_REGISTER_OFFSET 0x0C8 +#define MFSBQ_CONSUMER_REGISTER_OFFSET 0x0CC +#define MFS_LENGTH_REGISTER_OFFSET 0x0D0 + +/* Single Frame Sequence Buffer Queue (SFSBQ) + */ +#define SFSBQ_BASE_REGISTER_OFFSET 0x100 +#define SFSBQ_LENGTH_REGISTER_OFFSET 0x104 +#define SFSBQ_PRODUCER_REGISTER_OFFSET 0x108 +#define SFSBQ_CONSUMER_REGISTER_OFFSET 0x10C +#define SFS_LENGTH_REGISTER_OFFSET 0x110 + +/* SCSI Exchange State Table (SEST) + */ +#define SEST_BASE_REGISTER_OFFSET 0x140 +#define SEST_LENGTH_REGISTER_OFFSET 0x144 +#define SCSI_LENGTH_REGISTER_OFFSET 0x148 + +/* Length of the various Qs + */ +#define NO_OF_ENTRIES 8 +#define OCQ_LENGTH (MY_PAGE_SIZE/32) +#define IMQ_LENGTH (MY_PAGE_SIZE/32) +#define MFSBQ_LENGTH 8 +#define SFSBQ_LENGTH 8 +#define SEST_LENGTH MY_PAGE_SIZE + +/* Size of the various buffers. + */ +#define FRAME_SIZE 2048 +#define MFS_BUFFER_SIZE FRAME_SIZE +#define SFS_BUFFER_SIZE (FRAME_SIZE + TACHYON_HEADER_LEN) +#define SEST_BUFFER_SIZE 512 +#define TACH_HEADER_SIZE 64 +#define NO_OF_TACH_HEADERS ((MY_PAGE_SIZE)/TACH_HEADER_SIZE) + +#define NO_OF_FCP_CMNDS (MY_PAGE_SIZE/32) +#define SDB_SIZE 2048 +#define NO_OF_SDB_ENTRIES ((32*MY_PAGE_SIZE)/SDB_SIZE) + + +/* Offsets to the other Tachyon registers. + * (As defined in the Tachyon manual) + */ +#define TACHYON_CONFIG_REGISTER_OFFSET 0x184 +#define TACHYON_CONTROL_REGISTER_OFFSET 0x188 +#define TACHYON_STATUS_REGISTER_OFFSET 0x18C +#define TACHYON_FLUSH_SEST_REGISTER_OFFSET 0x190 + +/* Defines for the Tachyon Configuration register. + */ +#define SCSI_ENABLE 0x40000000 +#define WRITE_STREAM_SIZE 0x800 /* size = 16 */ +#define READ_STREAM_SIZE 0x300 /* size = 64 */ +#define PARITY_EVEN 0x2 +#define OOO_REASSEMBLY_DISABLE 0x40 + +/* Defines for the Tachyon Control register. + */ +#define SOFTWARE_RESET 0x80000000 +#define OCQ_RESET 0x4 +#define ERROR_RELEASE 0x2 + +/* Defines for the Tachyon Status register. + */ +#define RECEIVE_FIFO_EMPTY 0x10 +#define OSM_FROZEN 0x1 +#define OCQ_RESET_STATUS 0x20 +#define SCSI_FREEZE_STATUS 0x40 + + +/* Offsets to the Frame Manager registers. + */ +#define FMGR_CONFIG_REGISTER_OFFSET 0x1C0 +#define FMGR_CONTROL_REGISTER_OFFSET 0x1C4 +#define FMGR_STATUS_REGISTER_OFFSET 0x1C8 +#define FMGR_TIMER_REGISTER_OFFSET 0x1CC +#define FMGR_WWN_HI_REGISTER_OFFSET 0x1E0 +#define FMGR_WWN_LO_REGISTER_OFFSET 0x1E4 +#define FMGR_RCVD_ALPA_REGISTER_OFFSET 0x1E8 + +/* Defines for the Frame Manager Configuration register. + */ +#define BB_CREDIT 0x10000 +#define NPORT 0x8000 +#define LOOP_INIT_FABRIC_ADDRESS 0x400 +#define LOOP_INIT_PREVIOUS_ADDRESS 0x200 +#define LOOP_INIT_SOFT_ADDRESS 0x80 + +/* Defines for the Frame Manager Control register. + */ +#define HOST_CONTROL 0x02 +#define EXIT_HOST_CONTROL 0x03 +#define OFFLINE 0x05 +#define INITIALIZE 0x06 +#define CLEAR_LF 0x07 + +/* Defines for the Frame Manager Status register. + */ +#define LOOP_UP 0x80000000 +#define TRANSMIT_PARITY_ERROR 0x40000000 +#define NON_PARTICIPATING 0x20000000 +#define OUT_OF_SYNC 0x02000000 +#define LOSS_OF_SIGNAL 0x01000000 +#define NOS_OLS_RECEIVED 0x00080000 +#define LOOP_STATE_TIMEOUT 0x00040000 +#define LIPF_RECEIVED 0x00020000 +#define BAD_ALPA 0x00010000 +#define LINK_FAILURE 0x00001000 +#define ELASTIC_STORE_ERROR 0x00000400 +#define LINK_UP 0x00000200 +#define LINK_DOWN 0x00000100 +#define ARBITRATING 0x00000010 +#define ARB_WON 0x00000020 +#define OPEN 0x00000030 +#define OPENED 0x00000040 +#define TX_CLS 0x00000050 +#define RX_CLS 0x00000060 +#define TRANSFER 0x00000070 +#define INITIALIZING 0x00000080 +#define LOOP_FAIL 0x000000D0 +#define OLD_PORT 0x000000F0 +#define PORT_STATE_ACTIVE 0x0000000F +#define PORT_STATE_OFFLINE 0x00000000 +#define PORT_STATE_LF1 0x00000009 +#define PORT_STATE_LF2 0x0000000A + +/* Completion Message Types + * (defined in P.177 of the Tachyon manual) + */ +#define OUTBOUND_COMPLETION 0x000 +#define OUTBOUND_COMPLETION_I 0x100 +#define OUT_HI_PRI_COMPLETION 0x001 +#define OUT_HI_PRI_COMPLETION_I 0x101 +#define INBOUND_MFS_COMPLETION 0x102 +#define INBOUND_OOO_COMPLETION 0x003 +#define INBOUND_SFS_COMPLETION 0x104 +#define INBOUND_C1_TIMEOUT 0x105 +#define INBOUND_UNKNOWN_FRAME_I 0x106 +#define INBOUND_BUSIED_FRAME 0x006 +#define SFS_BUF_WARN 0x107 +#define MFS_BUF_WARN 0x108 +#define IMQ_BUF_WARN 0x109 +#define FRAME_MGR_INTERRUPT 0x10A +#define READ_STATUS 0x10B +#define INBOUND_SCSI_DATA_COMPLETION 0x10C +#define INBOUND_SCSI_COMMAND 0x10D +#define BAD_SCSI_FRAME 0x10E +#define INB_SCSI_STATUS_COMPLETION 0x10F + +/* One of the things that we care about when we receive an + * Outbound Completion Message (OCM). + */ +#define OCM_TIMEOUT_OR_BAD_ALPA 0x0800 + +/* Defines for the Tachyon Header structure. + */ +#define SOFI3 0x70 +#define SOFN3 0xB0 +#define EOFN 0x5 + +/* R_CTL */ +#define FC4_DEVICE_DATA 0 +#define EXTENDED_LINK_DATA 0x20000000 +#define FC4_LINK_DATA 0x30000000 +#define BASIC_LINK_DATA 0x80000000 +#define LINK_CONTROL 0xC0000000 +#define SOLICITED_DATA 0x1000000 +#define UNSOLICITED_CONTROL 0x2000000 +#define SOLICITED_CONTROL 0x3000000 +#define UNSOLICITED_DATA 0x4000000 +#define DATA_DESCRIPTOR 0x5000000 +#define UNSOLICITED_COMMAND 0x6000000 + +#define RCTL_ELS_UCTL 0x22000000 +#define RCTL_ELS_SCTL 0x23000000 +#define RCTL_BASIC_ABTS 0x81000000 +#define RCTL_BASIC_ACC 0x84000000 +#define RCTL_BASIC_RJT 0x85000000 + +/* TYPE */ +#define TYPE_BLS 0x00000000 +#define TYPE_ELS 0x01000000 +#define TYPE_FC_SERVICES 0x20000000 +#define TYPE_LLC_SNAP 0x05000000 +#define TYPE_FCP 0x08000000 + +/* F_CTL */ +#define EXCHANGE_RESPONDER 0x800000 +#define SEQUENCE_RESPONDER 0x400000 +#define FIRST_SEQUENCE 0x200000 +#define LAST_SEQUENCE 0x100000 +#define SEQUENCE_INITIATIVE 0x10000 +#define RELATIVE_OFF_PRESENT 0x8 +#define END_SEQUENCE 0x80000 + +#define TACHYON_HEADER_LEN 32 +#define NW_HEADER_LEN 16 +/* Defines for the Outbound Descriptor Block (ODB). + */ +#define ODB_CLASS_3 0xC000 +#define ODB_NO_COMP 0x400 +#define ODB_NO_INT 0x200 +#define ODB_EE_CREDIT 0xF + +/* Defines for the Extended Descriptor Block (EDB). + */ +#define EDB_LEN ((32*MY_PAGE_SIZE)/8) +#define EDB_END 0x8000 +#define EDB_FREE 0 +#define EDB_BUSY 1 + +/* Command Codes */ +#define ELS_LS_RJT 0x01000000 +#define ELS_ACC 0x02000000 +#define ELS_PLOGI 0x03000000 +#define ELS_FLOGI 0x04000000 +#define ELS_LOGO 0x05000000 +#define ELS_TPRLO 0x24000000 +#define ELS_ADISC 0x52000000 +#define ELS_PDISC 0x50000000 +#define ELS_PRLI 0x20000000 +#define ELS_PRLO 0x21000000 +#define ELS_SCR 0x62000000 +#define ELS_RSCN 0x61000000 +#define ELS_FARP_REQ 0x54000000 +#define ELS_ABTX 0x06000000 +#define ELS_ADVC 0x0D000000 +#define ELS_ECHO 0x10000000 +#define ELS_ESTC 0x0C000000 +#define ELS_ESTS 0x0B000000 +#define ELS_RCS 0x07000000 +#define ELS_RES 0x08000000 +#define ELS_RLS 0x0F000000 +#define ELS_RRQ 0x12000000 +#define ELS_RSS 0x09000000 +#define ELS_RTV 0x0E000000 +#define ELS_RSI 0x0A000000 +#define ELS_TEST 0x11000000 +#define ELS_RNC 0x53000000 +#define ELS_RVCS 0x41000000 +#define ELS_TPLS 0x23000000 +#define ELS_GAID 0x30000000 +#define ELS_FACT 0x31000000 +#define ELS_FAN 0x60000000 +#define ELS_FDACT 0x32000000 +#define ELS_NACT 0x33000000 +#define ELS_NDACT 0x34000000 +#define ELS_QoSR 0x40000000 +#define ELS_FDISC 0x51000000 + +#define ELS_NS_PLOGI 0x03FFFFFC + +/* LS_RJT reason codes. + */ +#define INV_LS_CMND_CODE 0x0001 +#define LOGICAL_ERR 0x0003 +#define LOGICAL_BUSY 0x0005 +#define PROTOCOL_ERR 0x0007 +#define UNABLE_TO_PERFORM 0x0009 +#define CMND_NOT_SUPP 0x000B + +/* LS_RJT explanation codes. + */ +#define NO_EXPLN 0x0000 +#define RECV_FIELD_SIZE 0x0700 +#define CONC_SEQ 0x0900 +#define REQ_NOT_SUPPORTED 0x2C00 +#define INV_PAYLOAD_LEN 0x2D00 + +/* Payload Length defines. + */ +#define PLOGI_LEN 116 + +#define CONCURRENT_SEQUENCES 0x01 +#define RO_INFO_CATEGORY 0xFE +#define E_D_TOV 0x07D0 /* 2 Secs */ +#define AL_TIME 0x0010 /* ~15 msec */ +#define TOV_VALUES (AL_TIME << 16) | E_D_TOV +#define RT_TOV 0x64 /* 100 msec */ +#define PTP_TOV_VALUES (RT_TOV << 16) | E_D_TOV +#define SERVICE_VALID 0x8000 +#define SEQUENCE_DELIVERY 0x0800 +#define CLASS3_CONCURRENT_SEQUENCE 0x01 +#define CLASS3_OPEN_SEQUENCE 0x01 + +/* These are retrieved from the NOVRAM. + */ +#define WORLD_WIDE_NAME_LOW fi->g.my_port_name_low +#define WORLD_WIDE_NAME_HIGH fi->g.my_port_name_high +#define N_PORT_NAME_HIGH fi->g.my_port_name_high +#define N_PORT_NAME_LOW fi->g.my_port_name_low +#define NODE_NAME_HIGH fi->g.my_node_name_high +#define NODE_NAME_LOW fi->g.my_node_name_low + +#define PORT_NAME_LEN 8 +#define NODE_NAME_LEN 8 + + +#define PH_VERSION 0x0909 + +#define LOOP_BB_CREDIT 0x00 +#define PT2PT_BB_CREDIT 0x01 +#define FLOGI_C_F 0x0800 /* Alternate BB_Credit Mgmnt */ +#define PLOGI_C_F 0x8800 /* Continuously Increasing + Alternate BB_Credit Management */ + +/* Fabric defines */ +#define DIRECTORY_SERVER 0xFFFFFC +#define FABRIC_CONTROLLER 0xFFFFFD +#define F_PORT 0xFFFFFE + +#define FLOGI_DID 0xFFFE +#define NS_PLOGI_DID 0xFFFC + +/* Fibre Channel Services defines */ +#define FCS_RFC_4 0x02170000 +#define FCS_GP_ID4 0x01A10000 +#define FCS_ACC 0x8002 +#define FCS_REJECT 0x8001 + +/* CT Header defines */ +#define FC_CT_REV 0x01000000 +#define DIRECTORY_SERVER_APP 0xFC +#define NAME_SERVICE 0x02 + +/* Port Type defines */ +#define PORT_TYPE_IP 0x05000000 +#define PORT_TYPE_NX_PORTS 0x7F000000 + +/* SCR defines */ +#define FABRIC_DETECTED_REG 0x00000001 +#define N_PORT_DETECTED_REG 0x00000002 +#define FULL_REGISTRATION 0x00000003 +#define CLEAR_REGISTRATION 0x000000FF + +/* Command structure has only one byte to address targets + */ +#define MAX_SCSI_TARGETS 0xFF + +#define FC_SCSI_READ 0x80 +#define FC_SCSI_WRITE 0x81 +#define FC_ELS 0x01 +#define FC_BLS 0x00 +#define FC_IP 0x05 +#define FC_BROADCAST 0xFF + +/* SEST defines. + */ +#define SEST_V 0x80000000 /* V = 1 */ +#define INB_SEST_VED 0xA0000000 /* V = 1, D = 1 */ +#define SEST_INV 0x7FFFFFFF +#define OUTB_SEST_VED 0x80000000 /* V = 1 */ +#define INV_SEQ_LEN 0xFFFFFFFF +#define OUTB_SEST_LINK 0xFFFF + +/* PRLI defines. + */ +#define PAGE_LEN 0x100000 /* 3rd byte - 0x10 */ +#define PRLI_LEN 0x0014 /* 20 bytes */ +#define FCP_TYPE_CODE 0x0800 /* FCP-SCSI */ +#define IMAGE_PAIR 0x2000 /* establish image pair */ +#define INITIATOR_FUNC 0x00000020 +#define TARGET_FUNC 0x00000010 +#define READ_XFER_RDY_DISABLED 0x00000002 + +#define NODE_PROCESS_LOGGED_IN 0x3 +#define NODE_NOT_PRESENT 0x2 +#define NODE_LOGGED_IN 0x1 +#define NODE_LOGGED_OUT 0x0 + +/* Defines to determine what should be returned when a SCSI frame + * times out. + */ +#define FC_SCSI_BAD_TARGET 0xFFFE0000 + +/* RSCN Address formats */ +#define PORT_ADDRESS_FORMAT 0x00 +#define AREA_ADDRESS_FORMAT 0x01 +#define DOMAIN_ADDRESS_FORMAT 0x02 + +/* Defines used to determine whether a frame transmission should + * be indicated by an interrupt or not. + */ +#define NO_COMP_AND_INT 0 +#define INT_AND_COMP_REQ 1 +#define NO_INT_COMP_REQ 2 + +/* Other junk... + */ +#define SDB_FREE 0 +#define SDB_BUSY 1 +#define MAX_PENDING_FRAMES 15 +#define RX_ID_FIRST_SEQUENCE 0xFFFF +#define OX_ID_FIRST_SEQUENCE 0xFFFF +#define NOT_SCSI_XID 0x8000 +#define MAX_SCSI_XID 0x0FFF /* X_IDs are from 0-4095 */ +#define SCSI_READ_BIT 0x4000 +#define MAX_SCSI_OXID 0x4FFF +#define OXID_AVAILABLE 0 +#define OXID_INUSE 1 +#define MAX_SEQ_ID 0xFF + +#define INITIATOR 2 +#define TARGET 1 +#define DELETE_ENTRY 1 +#define ADD_ENTRY 2 + +#endif /* _TACH_H */ diff --git a/drivers/net/fc/tach_structs.h b/drivers/net/fc/tach_structs.h new file mode 100644 index 000000000000..ca9f0f2c26de --- /dev/null +++ b/drivers/net/fc/tach_structs.h @@ -0,0 +1,428 @@ +/********************************************************************** + * iph5526.c: Structures for the Interphase 5526 PCI Fibre Channel + * IP/SCSI driver. + * Copyright (C) 1999 Vineet M Abraham + **********************************************************************/ + +#ifndef _TACH_STRUCT_H +#define _TACH_STRUCT_H + +typedef struct { + u_short cmnd_code; + u_short payload_length; + u_short type_code; + u_short est_image_pair; + u_int originator_pa; + u_int responder_pa; + u_int service_params; +} PRLI; + +typedef struct { + u_int flags_and_byte_offset; + u_int byte_count; + u_short no_of_recvd_frames; + u_short no_of_expected_frames; + u_int last_fctl; + u_int sdb_address; + u_int scratch_pad; + u_int expected_ro; + u_short buffer_index; + u_short buffer_offset; + } INB_SEST_ENTRY; + +typedef struct { + u_int flags_and_did; + u_short max_frame_len; + u_short cntl; + u_int total_seq_length; + u_short link; + u_short rx_id; + u_int transaction_id; + u_int header_address; + u_char seq_id; + u_char reserved; + u_short header_length; + u_int edb_address; + } OUTB_SEST_ENTRY; + +typedef struct { + u_short d_naa; + u_short dest_high; + u_int dest_low; + u_short s_naa; + u_short source_high; + u_int source_low; + } NW_HEADER; + +typedef struct { + u_int resv; + u_char sof_and_eof; + u_char dest_alpa; + u_short lcr_and_time_stamp; + u_int r_ctl_and_d_id; + u_int vc_id_and_s_id; + u_int type_and_f_cntl; + u_char seq_id; + u_char df_cntl; + u_short seq_cnt; + u_short ox_id; + u_short rx_id; + u_int ro; + NW_HEADER nw_header; + } TACHYON_HEADER; + +typedef struct { + u_short service_options; + u_short initiator_ctl; + u_short recipient_ctl; + u_short recv_data_field_size; + u_short concurrent_sequences; + u_short n_port_end_to_end_credit; + u_short open_seq_per_exchange; + u_short resv; + }CLASS_OF_SERVICE; + +typedef struct { + u_int logo_cmnd; + u_char reserved; + u_char n_port_id_2; + u_char n_port_id_1; + u_char n_port_id_0; + u_int port_name_up; + u_int port_name_low; + } LOGO; + +typedef struct { + u_int ls_cmnd_code; + u_int hard_address; + u_int port_name_high; + u_int port_name_low; + u_int node_name_high; + u_int node_name_low; + u_int n_port_id; + } ADISC; + +typedef struct { + u_int cmnd_code; + u_int reason_code; + } LS_RJT; + +typedef struct { + u_int cmnd_code; + } ACC; + +typedef struct { + u_int seq_d_id; + u_int tot_len; + u_short cntl; + u_short rx_id; + u_short cs_enable; + u_short cs_seed; + u_int trans_id; + u_int hdr_addr; + u_short frame_len; + u_short hdr_len; + u_int edb_addr; + }ODB; + +typedef struct { + u_int cmnd_code; + u_int reg_function; /* in the last byte */ + } SCR; + +typedef struct { + u_int rev_in_id; + u_char fs_type; + u_char fs_subtype; + u_char options; + u_char resv1; + u_short cmnd_resp_code; + u_short max_res_size; + u_char resv2; + u_char reason_code; + u_char expln_code; + u_char vendor_unique; + } CT_HDR; + +typedef struct { + CT_HDR ct_hdr; + u_int s_id; + u_char bit_map[32]; /* 32 byte bit map */ + } RFC_4; + +typedef struct { + u_int ls_cmnd_code; + u_short fc_ph_version; + u_short buff_to_buff_credit; + u_short common_features; + u_short recv_data_field_size; + u_short n_port_total_conc_seq; + u_short rel_off_by_info_cat; + u_int ED_TOV; + u_int n_port_name_high; + u_int n_port_name_low; + u_int node_name_high; + u_int node_name_low; + CLASS_OF_SERVICE c_of_s[3]; + u_int resv[4]; + u_int vendor_version_level[4]; + }LOGIN; + +typedef struct { + CT_HDR ct_hdr; + u_int port_type; /* in the first byte */ + } GP_ID4; + +typedef struct { + u_int buf_addr; + u_short ehf; + u_short buf_len; + }EDB; + +/* (i)chip Registers */ +struct i_chip_regs { + u_int ptr_ichip_hw_control_reg; + u_int ptr_ichip_hw_status_reg; + u_int ptr_ichip_hw_addr_mask_reg; +}; + +struct iph5526_novram { + u_int ptr_novram_hw_control_reg; + u_int ptr_novram_hw_status_reg; + u_short data[IPH5526_NOVRAM_SIZE]; +}; + +/* Tachyon Registers */ +struct tachyon_regs { + u_int ptr_ocq_base_reg; + u_int ptr_ocq_len_reg; + u_int ptr_ocq_prod_indx_reg; + u_int ptr_ocq_cons_indx_reg; + + u_int ptr_imq_base_reg; + u_int ptr_imq_len_reg; + u_int ptr_imq_cons_indx_reg; + u_int ptr_imq_prod_indx_reg; + + u_int ptr_mfsbq_base_reg; + u_int ptr_mfsbq_len_reg; + u_int ptr_mfsbq_prod_reg; + u_int ptr_mfsbq_cons_reg; + u_int ptr_mfsbuff_len_reg; + + u_int ptr_sfsbq_base_reg; + u_int ptr_sfsbq_len_reg; + u_int ptr_sfsbq_prod_reg; + u_int ptr_sfsbq_cons_reg; + u_int ptr_sfsbuff_len_reg; + + u_int ptr_sest_base_reg; + u_int ptr_sest_len_reg; + u_int ptr_scsibuff_len_reg; + + u_int ptr_tach_config_reg; + u_int ptr_tach_control_reg; + u_int ptr_tach_status_reg; + u_int ptr_tach_flush_oxid_reg; + + u_int ptr_fm_config_reg; + u_int ptr_fm_control_reg; + u_int ptr_fm_status_reg; + u_int ptr_fm_tov_reg; + u_int ptr_fm_wwn_hi_reg; + u_int ptr_fm_wwn_low_reg; + u_int ptr_fm_rx_al_pa_reg; +}; + +struct globals { + u_long tachyon_base; + u_int *mem_base; + u_short ox_id; /* OX_ID used for IP and ELS frames */ + u_short scsi_oxid; /* OX_ID for SEST entry */ + u_char seq_id; + u_int my_id; + u_int my_ddaa; /* my domain and area in a fabric */ + volatile u_char loop_up; + volatile u_char ptp_up; /* we have a point-to-point link */ + volatile u_char link_up; + volatile u_char n_port_try; + volatile u_char nport_timer_set; + volatile u_char lport_timer_set; + /* Hmmm... We dont want to Initialize while closing */ + u_char dont_init; + u_int my_node_name_high; + u_int my_node_name_low; + u_int my_port_name_high; + u_int my_port_name_low; + u_char fabric_present; + u_char explore_fabric; + u_char name_server; + u_int my_mtu; + u_int *els_buffer[MAX_PENDING_FRAMES]; /* temp space for ELS frames */ + char *arp_buffer; /* temp space for ARP frames */ + u_int mfs_buffer_count; /* keep track of MFS buffers used*/ + u_char scsi_registered; + /* variables for port discovery */ + volatile u_char port_discovery; + volatile u_char perform_adisc; + u_short alpa_list_index; + u_short type_of_frame; /* Could be IP/SCSI Read/SCSI Write*/ + u_char no_of_targets; /* used to assign target_ids */ + u_long sem; /* to synchronize between IP and SCSI */ + u_char e_i; + + /* the frames */ + TACHYON_HEADER tach_header; + LOGIN login; + PRLI prli; + LOGO logo; + ADISC adisc; + LS_RJT ls_rjt; + ODB odb; + INB_SEST_ENTRY inb_sest_entry; + OUTB_SEST_ENTRY outb_sest_entry; + ACC acc; + SCR scr; + EDB edb; + RFC_4 rfc_4; + GP_ID4 gp_id4; +}; + +struct queue_variables { + /* Indices maintained in host memory. + */ + u_int *host_ocq_cons_indx, *host_hpcq_cons_indx, *host_imq_prod_indx; + u_int *ptr_host_ocq_cons_indx, *ptr_host_hpcq_cons_indx, *ptr_host_imq_prod_indx; + + /* Variables for Outbound Command Queue (OCQ). + */ + u_int *ptr_ocq_base; + u_int ocq_len, ocq_end; + u_int ocq_prod_indx; + u_int *ptr_odb[OCQ_LENGTH]; + + /* Variables for Inbound Message Queue (IMQ). + */ + u_int *ptr_imq_base; + u_int imq_len, imq_end; + u_int imq_cons_indx; + u_int imq_prod_indx; + u_int *ptr_imqe[IMQ_LENGTH]; + + u_int *ptr_mfsbq_base; + u_int mfsbq_len, mfsbq_end; + u_int mfsbq_prod_indx; + u_int mfsbq_cons_indx; + u_int mfsbuff_len, mfsbuff_end; + + u_int *ptr_sfsbq_base; + u_int sfsbq_len, sfsbq_end; + u_int sfsbq_prod_indx; + u_int sfsbq_cons_indx; + u_int sfsbuff_len, sfsbuff_end; + u_int *ptr_sfs_buffers[SFSBQ_LENGTH * NO_OF_ENTRIES]; + + /* Tables for SCSI Transactions */ + u_int *ptr_sest_base; + u_int *ptr_sest[SEST_LENGTH]; + u_char free_scsi_oxid[SEST_LENGTH]; + u_int *ptr_sdb_base; + u_int *ptr_sdb_slot[NO_OF_SDB_ENTRIES]; + u_char sdb_slot_status[NO_OF_SDB_ENTRIES]; + u_int sdb_indx; + u_int *ptr_fcp_cmnd_base; + u_int *ptr_fcp_cmnd[NO_OF_FCP_CMNDS]; + u_int fcp_cmnd_indx; + + /* Table for data to be transmitted. + */ + u_int *ptr_edb_base; + u_int *ptr_edb[EDB_LEN]; + u_int edb_buffer_indx; + volatile u_char free_edb_list[EDB_LEN]; + + /* Table of Tachyon Headers. + */ + u_int *ptr_tachyon_header[NO_OF_TACH_HEADERS]; + u_int *ptr_tachyon_header_base; + u_int tachyon_header_indx; +}; + +/* Used to match incoming ACCs to ELS requests sent out */ +struct ox_id_els_map { + u_short ox_id; + u_int els; + struct ox_id_els_map *next; +}; + + +/* Carries info about individual nodes... stores the info got at login + * time. Also maintains mapping between MAC->FC addresses + */ +struct fc_node_info { + /* Itz the WWN (8 bytes), the last 6 bytes is the MAC address */ + u_char hw_addr[PORT_NAME_LEN]; + u_char node_name[NODE_NAME_LEN]; + u_int d_id; /*real FC address, 3 bytes */ + int mtu; + /* login = 1 if login attempted + * login = 2 if login completed + */ + int login; + u_char scsi; /* = 1 if device is a SCSI Target */ + u_char target_id; + CLASS_OF_SERVICE c_of_s[3]; + struct fc_node_info *next; +}; + +struct fc_info { + char name[8]; + u_long base_addr; + int irq; + struct net_device_stats fc_stats; + struct fc_node_info *node_info_list; + int num_nodes; + struct ox_id_els_map *ox_id_list; + struct i_chip_regs i_r; + struct tachyon_regs t_r; + struct queue_variables q; + struct globals g; + struct iph5526_novram n_r; + u_short clone_id; + struct timer_list nport_timer; + struct timer_list lport_timer; + struct timer_list explore_timer; + struct timer_list display_cache_timer; + struct device *dev; + struct Scsi_Host *host; + spinlock_t fc_lock; +}; + +struct iph5526_hostdata { + struct fc_info *fi; + fcp_cmd cmnd; + Scsi_Cmnd *cmnd_handler[SEST_LENGTH]; + u_int tag_ages[MAX_SCSI_TARGETS]; +}; + +/* List of valid AL_PAs */ +u_char alpa_list[127] = { + 0x00, 0x01, 0x02, 0x04, 0x08, 0x0F, 0x10, 0x17, + 0x18, 0x1B, 0x1D, 0x1E, 0x1F, 0x23, 0x25, 0x26, + 0x27, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x39, 0x3A, 0x3C, + 0x43, 0x45, 0x46, 0x47, 0x49, 0x4A, 0x4B, 0x4C, + 0x4D, 0x4E, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, + 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x67, 0x69, + 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x79, 0x7A, 0x7C, 0x80, 0x81, + 0x82, 0x84, 0x88, 0x8F, 0x90, 0x97, 0x98, 0x9B, + 0x9D, 0x9E, 0x9F, 0xA3, 0xA5, 0xA6, 0xA7, 0xA9, + 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xB1, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB9, 0xBA, 0xBC, 0xC3, 0xC5, + 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, + 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD9, 0xDA, + 0xDC, 0xE0, 0xE1, 0xE2, 0xE4, 0xE8, 0xEF +}; + +#endif /* _TACH_STRUCT_H */ diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c index be7006d5cc72..9d3d4e48dbb6 100644 --- a/drivers/net/net_init.c +++ b/drivers/net/net_init.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -645,7 +646,130 @@ void unregister_trdev(struct device *dev) } #endif - + +#ifdef CONFIG_NET_FC + +#define MAX_FC_CARDS 2 +static struct device *fcdev_index[MAX_FC_CARDS]; + +void fc_setup(struct device *dev) +{ +int i; + + /* register boot-defined "fc" devices */ + if (dev->name && (strncmp(dev->name, "fc", 2) == 0)) { + i = simple_strtoul(dev->name + 2, NULL, 0); + if (fcdev_index[i] == NULL) { + fcdev_index[i] = dev; + } + else if (dev != fcdev_index[i]) { + /* Really shouldn't happen! */ + printk("fc_setup: Ouch! Someone else took %s\n", + dev->name); + } + } + + dev->hard_header = fc_header; + dev->rebuild_header = fc_rebuild_header; + + dev->type = ARPHRD_IEEE802; + dev->hard_header_len = FC_HLEN; + dev->mtu = 2024; + dev->addr_len = FC_ALEN; + dev->tx_queue_len = 100; /* Long queues on fc */ + + memset(dev->broadcast,0xFF, FC_ALEN); + + /* New-style flags. */ + dev->flags = IFF_BROADCAST; + dev_init_buffers(dev); + return; +} + + +struct device *init_fcdev(struct device *dev, int sizeof_priv) +{ +int new_device = 0; +int i; + /* Use an existing correctly named device in Space.c:dev_base. */ + if (dev == NULL) { + int alloc_size = sizeof(struct device) + sizeof("fc%d ") + sizeof_priv + 3; + struct device *cur_dev; + char pname[8]; /* Putative name for the device. */ + + for (i = 0; i < MAX_FC_CARDS; ++i) + if (fcdev_index[i] == NULL) { + sprintf(pname, "fc%d", i); + for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) + if (strcmp(pname, cur_dev->name) == 0) { + dev = cur_dev; + dev->init = NULL; + sizeof_priv = (sizeof_priv + 3) &~3; + dev->priv = sizeof_priv + ? kmalloc(sizeof_priv, GFP_KERNEL) + : NULL; + if (dev->priv) memset(dev->priv, 0, sizeof_priv); + goto fcfound; + } + } + + alloc_size &= ~3; /* Round to dword boundary. */ + dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL); + memset(dev, 0, alloc_size); + if (sizeof_priv) + dev->priv = (void *) (dev + 1); + dev->name = sizeof_priv + (char *)(dev + 1); + new_device = 1; + } + +fcfound: /* From the double loop */ + + for (i = 0; i < MAX_FC_CARDS; ++i) + if (fcdev_index[i] == NULL) { + sprintf(dev->name, "fc%d", i); + fcdev_index[i] = dev; + break; + } + + fc_setup(dev); + if (new_device) + register_netdevice(dev); + + return dev; +} + +void fc_freedev(struct device *dev) +{ +int i; + for (i = 0; i < MAX_FC_CARDS; ++i) { + if (fcdev_index[i] == dev) { + fcdev_index[i] = NULL; + break; + } + } +} + + +int register_fcdev(struct device *dev) +{ + dev_init_buffers(dev); + if (dev->init && dev->init(dev) != 0) { + unregister_fcdev(dev); + return -EIO; + } + return 0; +} + +void unregister_fcdev(struct device *dev) +{ + rtnl_lock(); + unregister_netdevice(dev); + rtnl_unlock(); + fc_freedev(dev); +} + +#endif /* CONFIG_NET_FC */ + /* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c" diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c index dea2d1e9952c..588bd1a48a90 100644 --- a/drivers/net/rtl8139.c +++ b/drivers/net/rtl8139.c @@ -1042,9 +1042,9 @@ static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs) rtl8129_rx(dev); if (status & (TxOK | TxErr)) { - unsigned int dirty_tx = tp->dirty_tx; + unsigned int dirty_tx; - while (tp->cur_tx - dirty_tx > 0) { + for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) { int entry = dirty_tx % NUM_TX_DESC; int txstatus = inl(ioaddr + TxStatus0 + entry*4); @@ -1091,7 +1091,6 @@ static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs) clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } - dirty_tx++; } #ifndef final_version diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index 280f9ad27b5f..3ed54576478a 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -273,7 +273,7 @@ sb1000_probe(struct device *dev) dev->type = ARPHRD_ETHER; dev->hard_header_len = 0; - dev->mtu = 0; + dev->mtu = 1500; dev->addr_len = ETH_ALEN; /* hardware address is 0:0:serial_number */ dev->dev_addr[0] = 0; diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index eddfd47d97cb..2fff7e42b059 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -33,6 +33,8 @@ static int sis900_debug = 0; static int multicast_filter_limit = 128; #define MAX_UNITS 8 /* More are supported, limit only on options */ +static int speeds[MAX_UNITS] = {100, 100, 100, 100, 100, 100, 100, 100}; +static int full_duplex[MAX_UNITS] = {1, 1, 1, 1, 1, 1, 1, 1}; #define TX_BUF_SIZE 1536 #define RX_BUF_SIZE 1536 @@ -41,9 +43,9 @@ static int multicast_filter_limit = 128; #define RX_DMA_BURST 0 #define TX_FIFO_THRESH 16 #define TxDRNT_100 (1536>>5) -#define TxDRNT_10 16 //(1536>>5) +#define TxDRNT_10 16 #define RxDRNT_100 8 -#define RxDRNT_10 8 //(1536>>5) +#define RxDRNT_10 8 #define TRUE 1 #define FALSE 0 @@ -387,21 +389,23 @@ struct sis900_private { EuphLiteDesc rx_buf[NUM_RX_DESC]; unsigned char *rx_bufs; unsigned char *tx_bufs; /* Tx bounce buffer region. */ - char phys[4]; /* MII device addresses. */ - int phy_idx; + char phys[4]; /* MII device addresses. */ + int phy_idx; /* Support Max 4 PHY */ u16 pmd_status; - unsigned int tx_full; /* The Tx queue is full. */ - u16 full_duplex; /* FullHalf-duplex. */ - u16 hunmbps; /* 10010 Mbps. */ + unsigned int tx_full; /* The Tx queue is full. */ + int MediaSpeed; /* user force speed */ + int MediaDuplex; /* user force duplex */ + int full_duplex; /* Full/Half-duplex. */ + int speeds; /* 100/10 Mbps. */ u16 LinkOn; u16 LinkChange; }; #ifdef MODULE #if LINUX_VERSION_CODE > 0x20115 -MODULE_AUTHOR("Silicon Integrated Systems Corporation"); +MODULE_AUTHOR("Jim Huang "); MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(speeds, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(multicast_filter_limit, "i"); MODULE_PARM(max_interrupt_work, "i"); @@ -425,6 +429,7 @@ static struct enet_statistics *sis900_get_stats(struct device *dev); static void set_rx_mode(struct device *dev); static void sis900_reset(struct device *dev); static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed); +static void elSetCapability(struct device *dev, int phy_id, int duplex, int speed); static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex); static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value); static void elSetMediaType(struct device *dev, int speed, int duplex); @@ -544,9 +549,10 @@ static struct device * sis900_probe1( int pci_bus, { static int did_version = 0; /* Already printed version info. */ struct sis900_private *tp; - int duplex=0; - int speed=0; - int i; + u16 status; + int duplex = found_cnt < MAX_UNITS ? full_duplex[found_cnt] : 0 ; + int speed = found_cnt < MAX_UNITS ? speeds[found_cnt] : 0 ; + int phy=0, phy_idx=0, i; if (did_version++ == 0) printk(KERN_INFO "%s", version); @@ -588,25 +594,11 @@ static struct device * sis900_probe1( int pci_bus, Doing this in open() would allow detecting external xcvrs later, but takes too much time. */ if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) { - int phy, phy_idx; for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(dev, phy, MII_STATUS); - /* - { - int p; - int l; - for (p = 0 ; p < 4 ; p ++) { - for (l=0 ; l<16 ; l++) { - int status = mdio_read(dev, phy, l); - status = mdio_read(dev, phy, l); - printk(KERN_INFO "MII info addr[%d][%d]=%x\n", - p, l, status); - } - } - } - */ + int mii_status ; + mii_status = mdio_read(dev, phy, MII_STATUS); if (mii_status != 0xffff && mii_status != 0x0000) { tp->phy_idx = phy_idx; @@ -620,45 +612,85 @@ static struct device * sis900_probe1( int pci_bus, } if (phy_idx == 0) { - printk(KERN_INFO "%s: No MII transceivers found!", + printk(KERN_INFO "%s: No MII transceivers found!\n", dev->name); - printk(KERN_INFO "Assuming SYM transceiver.\n"); tp->phys[0] = -1; + tp->pmd_status = 0; } } else { - tp->phys[0] = 32; + tp->phys[0] = -1; + tp->pmd_status = 0; } - if (tp->pmd_status > 0) { - tp->pmd_status= - elAutoNegotiate(dev, tp->phys[tp->phy_idx], &duplex, &speed); - if (tp->pmd_status & MIISTAT_LINK) { - if (duplex == FDX_CAPABLE_FULL_SELECTED) - tp->full_duplex=1; - if (speed == HW_SPEED_100_MBPS) - tp->hunmbps=1; - tp->LinkOn = TRUE; - } else { + if ((tp->pmd_status > 0) && (phy_idx > 0)) { + if (sis900_debug > 1) { + printk(KERN_INFO "duplex=%d, speed=%d\n", + duplex, speed); + } + if (!duplex && !speed) { + // auto-config media type + // Set full capability + if (sis900_debug > 1) { + printk(KERN_INFO "Auto Config ...\n"); + } + elSetCapability(dev, tp->phys[tp->phy_idx], 1, 100); + tp->pmd_status=elAutoNegotiate(dev, + tp->phys[tp->phy_idx], + &tp->full_duplex, + &tp->speeds); + } else { + tp->MediaSpeed = speed; + tp->MediaDuplex = duplex; + elSetCapability(dev, tp->phys[tp->phy_idx], + duplex, speed); + elAutoNegotiate(dev, tp->phys[tp->phy_idx], + &tp->full_duplex, + &tp->speeds); + status = mdio_read(dev, phy, MII_ANLPAR); + if ( !(status & (MII_NWAY_T | MII_NWAY_T_FDX | + MII_NWAY_TX | MII_NWAY_TX_FDX ))) + { + u16 cmd=0; + cmd |= ( speed == 100 ? + MIICNTL_SPEED : 0 ); + cmd |= ( duplex ? MIICNTL_FDX : 0 ); + mdio_write(dev, phy, MII_CONTROL, cmd); + elSetMediaType(dev, speed==100 ? + HW_SPEED_100_MBPS : + HW_SPEED_10_MBPS, + duplex ? + FDX_CAPABLE_FULL_SELECTED: + FDX_CAPABLE_HALF_SELECTED); + elMIIpollBit(dev, phy, MII_STATUS, + MIISTAT_LINK, TRUE, &status); + } else { + status = mdio_read(dev, phy, MII_STATUS); + } + } + + if (tp->pmd_status & MIISTAT_LINK) + tp->LinkOn = TRUE; + else tp->LinkOn = FALSE; - } - tp->LinkChange = FALSE; - } - - /* - if (found_cnt < MAX_UNITS && full_duplex[found_cnt] > 0) - tp->full_duplex = full_duplex[found_cnt]; - if (tp->full_duplex) { - printk(KERN_INFO "%s: Media type is Full Duplex.\n", dev->name); - } else { - printk(KERN_INFO "%s: Media type is Half Duplex.\n", dev->name); + tp->LinkChange = FALSE; + } - if (tp->hunmbps) { - printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name); - } else { - printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name); - } - */ + + if (sis900_debug > 1) { + if (tp->full_duplex == FDX_CAPABLE_FULL_SELECTED) { + printk(KERN_INFO "%s: Media type is Full Duplex.\n", + dev->name); + } else { + printk(KERN_INFO "%s: Media type is Half Duplex.\n", + dev->name); + } + if (tp->speeds == HW_SPEED_100_MBPS) { + printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name); + } else { + printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name); + } + } /* The SiS900-specific entries in the device structure. */ dev->open = &sis900_open; @@ -857,16 +889,23 @@ sis900_open(struct device *dev) return -EAGAIN; } - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL); tp->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL); if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) { if (tp->tx_bufs) kfree(tp->tx_bufs); - if (sis900_debug > 0) - printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n", + if (tp->rx_bufs) + kfree(tp->rx_bufs); + if (!tp->tx_bufs) { + printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n", dev->name, TX_BUF_SIZE * NUM_TX_DESC); + } + if (!tp->rx_bufs) { + printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n", + dev->name, RX_BUF_SIZE * NUM_RX_DESC); + } return -ENOMEM; } @@ -886,15 +925,6 @@ sis900_open(struct device *dev) i, inl(ioaddr + rfdr)); } } - /* - for (i=0 ; i<3 ; i++) { - outl((((u32) i) << RFEP_shift), ioaddr + rfcr); - if (sis900_debug > 4) { - printk(KERN_INFO "Read Filter Addr[%d]=%x\n", - i, inl(ioaddr + rfdr)); - } - } - */ outl(rfcrSave, rfcr); } @@ -919,31 +949,28 @@ sis900_open(struct device *dev) /* Must enable Tx/Rx before setting transfer thresholds! */ /* - #define TX_DMA_BURST 0 - #define RX_DMA_BURST 0 - #define TX_FIFO_THRESH 16 - #define TxDRNT_100 (1536>>5) - #define TxDRNT_10 (1536>>5) - #define RxDRNT_100 (1536>>5) - #define RxDRNT_10 (1536>>5) - */ - //outl(RxENA | TxENA, ioaddr + cr); + * #define TX_DMA_BURST 0 + * #define RX_DMA_BURST 0 + * #define TX_FIFO_THRESH 16 + * #define TxDRNT_100 (1536>>5) + * #define TxDRNT_10 (1536>>5) + * #define RxDRNT_100 (1536>>5) + * #define RxDRNT_10 (1536>>5) + */ outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg); outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10, ioaddr + txcfg); - //tp->full_duplex = tp->duplex_lock; - if (tp->phys[tp->phy_idx] >= 0 || - (sis_cap_tbl[tp->chip_id] & HAS_MII_XCVR)) { - if (sis900_debug > 1) - if (tp->LinkOn) { - printk(KERN_INFO"%s: Setting %s%s-duplex.\n", + if (sis900_debug > 1) + if (tp->LinkOn) { + printk(KERN_INFO"%s: Media Type %s%s-duplex.\n", dev->name, - tp->hunmbps ? "100mbps " : "10mbps ", - tp->full_duplex ? "full" : "half"); - } else { - printk(KERN_INFO"%s: Media Link Off\n", dev->name); - } - } + tp->speeds==HW_SPEED_100_MBPS ? + "100mbps " : "10mbps ", + tp->full_duplex== FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + } else { + printk(KERN_INFO"%s: Media Link Off\n", dev->name); + } set_rx_mode(dev); dev->tbusy = 0; @@ -955,7 +982,7 @@ sis900_open(struct device *dev) outl(RxENA, ioaddr + cr); outl(IE, ioaddr + ier); - if (sis900_debug > 1) + if (sis900_debug > 3) printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n", dev->name, ioaddr, dev->irq); @@ -974,81 +1001,31 @@ static void sis900_timer(unsigned long data) { struct device *dev = (struct device *)data; struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; int next_tick = 0; - int duplex, full_duplex=0; - int speed, hunmbps=0; u16 status; -// printk(KERN_INFO "%s: SiS900 timer\n", dev->name); - elMIIpollBit(dev, tp->phys[tp->phy_idx], MII_STATUS, MIISTAT_LINK, TRUE, &status); - if (status & MIISTAT_LINK) { -// printk(KERN_INFO "%s: SiS900 timer link\n", dev->name); - /* - if (!tp->LinkOn) { - printk(KERN_INFO "%s: AutoNegotiate ...\n", dev->name); - tp->LinkChange=TRUE; - tp->pmd_status= - elAutoNegotiate(dev, tp->phys[tp->phy_idx], - &duplex, &speed); - } - else { - printk(KERN_INFO "%s: Link Still On.\n", dev->name); - elPMDreadMode(dev, tp->phys[tp->phy_idx], - &speed, &duplex); - } - */ - elPMDreadMode(dev, tp->phys[tp->phy_idx], - &speed, &duplex); - - - if (duplex == FDX_CAPABLE_FULL_SELECTED) full_duplex=1; - if (speed == HW_SPEED_100_MBPS) hunmbps=1; - if (full_duplex != tp->full_duplex || hunmbps != tp->hunmbps) - tp->LinkChange = TRUE; - if (tp->LinkChange) { - tp->full_duplex=full_duplex; - tp->hunmbps=hunmbps; - //elSetMediaType(dev, speed, duplex); - printk(KERN_INFO "%s: Setting %s%s-duplex based on MII " - "#%d link partner ability.\n", + if (!tp->LinkOn) { + status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); + if (status & MIISTAT_LINK) { + elPMDreadMode(dev, tp->phys[tp->phy_idx], + &tp->speeds, &tp->full_duplex); + tp->LinkOn = TRUE; + printk(KERN_INFO "%s: Media Link On %s%s-duplex ", dev->name, - tp->hunmbps ? "100mbps " : "10mbps ", - tp->full_duplex ? "full" : "half", - tp->phys[0]); - } - tp->LinkOn=TRUE; - tp->LinkChange = FALSE; - } else { - if (tp->LinkOn) { - tp->LinkChange = TRUE; - tp->pmd_status= - elAutoNegotiate(dev, tp->phys[tp->phy_idx], - &duplex, &speed); - if (tp->pmd_status & MIISTAT_LINK) { - if (duplex == FDX_CAPABLE_FULL_SELECTED) - tp->full_duplex=1; - if (speed == HW_SPEED_100_MBPS) - tp->hunmbps=1; - } else { - tp->LinkOn = FALSE; - printk(KERN_INFO "%s: Link Off\n", dev->name); - } - } -// printk(KERN_INFO "%s: Link Off\n", dev->name); + tp->speeds == HW_SPEED_100_MBPS ? + "100mbps " : "10mbps ", + tp->full_duplex==FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + } + } else { // previous link on + status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); + if (!(status & MIISTAT_LINK)) { + tp->LinkOn = FALSE; + printk(KERN_INFO "%s: Media Link Off\n", dev->name); + } } next_tick = 2*HZ; - if (sis900_debug > 3) { - printk(KERN_INFO "%s: Other registers are IntMask " - "%4.4x IntStatus %4.4x" - " RxStatus %4.4x.\n", - dev->name, - inw(ioaddr + imr), - inw(ioaddr + isr), - inl(ioaddr + rxcfg)); - } - if (next_tick) { tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); @@ -1067,24 +1044,28 @@ static void sis900_tx_timeout(struct device *dev) /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); + /* Emit info to figure out what went wrong. */ - printk(KERN_INFO "%s: Tx queue start entry %d dirty entry %d.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - for (i = 0; i < NUM_TX_DESC; i++) - printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n", - dev->name, i, (unsigned int)&tp->tx_buf[i], - i == tp->dirty_tx % NUM_TX_DESC ? - " (queue head)" : ""); - /* - printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); - for (mii_reg = 0; mii_reg < 8; mii_reg++) - printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg)); - printk(".\n"); - */ + if (sis900_debug > 1) { + printk(KERN_INFO "%s:Tx queue start entry %d dirty entry %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + for (i = 0; i < NUM_TX_DESC; i++) + printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n", + dev->name, i, (unsigned int)&tp->tx_buf[i], + i == tp->dirty_tx % NUM_TX_DESC ? + " (queue head)" : ""); + } /* Soft reset the chip. */ + //outb(RESET, ioaddr + cr); + /* Check that the chip has finished the reset. */ + /* + for (i = 1000; i > 0; i--) + if ((inb(ioaddr + cr) & RESET) == 0) + break; + */ - tp->cur_rx = 0; //?????? + tp->cur_rx = 0; /* Must enable Tx/Rx before setting transfer thresholds! */ /* set_rx_mode(dev); @@ -1145,10 +1126,6 @@ sis900_init_ring(struct device *dev) tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE]; tp->tx_buf[i].bufPhys = virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); - /* - printk(KERN_INFO "tp->tx_buf[%d].bufPhys=%8.8x\n", - i, tp->tx_buf[i].bufPhys); - */ } /* Tx Descriptor */ @@ -1161,11 +1138,6 @@ sis900_init_ring(struct device *dev) tp->tx_buf[i].physAddr= virt_to_bus(&(tp->tx_buf[i].plink)); tp->tx_buf[i].cmdsts=0; - - /* - printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n", - i, tp->tx_buf[i].physAddr); - */ } /* Rx Buffer */ @@ -1173,10 +1145,6 @@ sis900_init_ring(struct device *dev) tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE]; tp->rx_buf[i].bufPhys = virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]); - /* - printk(KERN_INFO "tp->rx_buf[%d].bufPhys=%8.8x\n", - i, tp->rx_buf[i].bufPhys); - */ } /* Rx Descriptor */ @@ -1189,10 +1157,6 @@ sis900_init_ring(struct device *dev) tp->rx_buf[i].physAddr= virt_to_bus(&(tp->rx_buf[i].plink)); tp->rx_buf[i].cmdsts=RX_BUF_SIZE; - /* - printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n", - i, tp->tx_buf[i].physAddr); - */ } } @@ -1234,6 +1198,7 @@ sis900_start_xmit(struct sk_buff *skb, struct device *dev) tp->tx_buf[entry].cmdsts=(OWN | skb->len); + //tp->tx_buf[entry].plink = 0; outl(TxENA, ioaddr + cr); if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ clear_bit(0, (void*)&dev->tbusy); @@ -1347,11 +1312,6 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) tp->stats.tx_errors++; if (txstatus & ABORT) { tp->stats.tx_aborted_errors++; - /* - outl((TX_DMA_BURST<<8)| - 0x03000001, - ioaddr + TxConfig); - */ } if (txstatus & NOCARRIER) tp->stats.tx_carrier_errors++; @@ -1366,27 +1326,21 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) /* No count for tp->stats.tx_deferred */ #endif if (txstatus & UNDERRUN) { - if (sis900_debug > 1) + if (sis900_debug > 2) printk(KERN_INFO "Tx UnderRun\n"); - /* Add 64 to the Tx FIFO threshold. */ - /* - if (tp->tx_flag < 0x00300000) - tp->tx_flag+=0x00020000; - tp->stats.tx_fifo_errors++; - */ } tp->stats.collisions += (txstatus >> 16) & 0xF; #if LINUX_VERSION_CODE > 0x20119 tp->stats.tx_bytes += txstatus & DSIZE; #endif - if (sis900_debug > 1) + if (sis900_debug > 2) printk(KERN_INFO "Tx Transmit OK\n"); tp->stats.tx_packets++; } /* Free the original skb. */ - if (sis900_debug > 1) + if (sis900_debug > 2) printk(KERN_INFO "Free original skb\n"); dev_free_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; @@ -1404,15 +1358,16 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) { /* The ring is no longer full, clear tbusy. */ - //printk(KERN_INFO "Tx Ring NO LONGER Full\n"); + if (sis900_debug > 3) + printk(KERN_INFO "Tx Ring NO LONGER Full\n"); tp->tx_full = 0; dev->tbusy = 0; mark_bh(NET_BH); } tp->dirty_tx = dirty_tx; - if (sis900_debug > 1) - printk(KERN_INFO "TxOK release, tp->cur_tx:%d, tp->dirty:%d\n", + if (sis900_debug > 2) + printk(KERN_INFO "TxOK,tp->cur_tx:%d,tp->dirty:%d\n", tp->cur_tx, tp->dirty_tx); } // if (TxOK | TxERR) @@ -1424,25 +1379,18 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) if (status == 0xffffffff) break; - if (status & (RxORN | RxERR)) tp->stats.rx_errors++; + if (status & RxORN) { tp->stats.rx_over_errors++; - /* - tp->cur_rx = - inw(ioaddr + RxBufAddr) % RX_BUF_LEN; - outw(tp->cur_rx - 16, ioaddr + RxBufPtr); - */ } } if (--boguscnt < 0) { printk(KERN_INFO "%s: Too much work at interrupt, " "IntrStatus=0x%4.4x.\n", dev->name, status); - /* Clear all interrupt sources. */ - //outw(0xffff, ioaddr + IntrStatus); break; } } while (1); @@ -1478,7 +1426,6 @@ static int sis900_rx(struct device *dev) int rx_size = rx_status & DSIZE; rx_size -= CRC_SIZE; - //printk(KERN_INFO "Rx OWN\n"); if (sis900_debug > 4) { int i; printk(KERN_INFO "%s: sis900_rx, rx status %8.8x," @@ -1510,21 +1457,11 @@ static int sis900_rx(struct device *dev) if (rx_status & (RUNT | TOOLONG)) tp->stats.rx_length_errors++; if (rx_status & CRCERR) tp->stats.rx_crc_errors++; - /* Reset the receiver, - based on RealTek recommendation. (Bug?) */ - /* - tp->cur_rx = 0; - outl(TxENA, ioaddr + cr); - outl(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); - outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | - (RX_DMA_BURST<<8), ioaddr + RxConfig); - */ } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ struct sk_buff *skb; - //printk(KERN_INFO "Rx OK\n"); skb = dev_alloc_skb(rx_size + 2); if (skb == NULL) { printk(KERN_INFO "%s: Memory squeeze," @@ -1556,7 +1493,6 @@ static int sis900_rx(struct device *dev) memset(rx_bufs, 0xcc, 16); } */ - //printk(KERN_INFO "Frame Wrap....\n"); } else { #if 0 /* USE_IP_COPYSUM */ eth_copy_and_sum(skb, @@ -1578,7 +1514,6 @@ static int sis900_rx(struct device *dev) cur_rx = ((cur_rx+1) % NUM_RX_DESC); rx_status = tp->rx_buf[cur_rx].cmdsts; - //outw(cur_rx - 16, ioaddr + RxBufPtr); } // while if (sis900_debug > 4) printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x " @@ -1635,7 +1570,7 @@ static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - data[0] = tp->phys[0] & 0x3f; + data[0] = tp->phys[tp->phy_idx]; /* Fall Through */ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); @@ -1654,6 +1589,7 @@ static struct enet_statistics * sis900_get_stats(struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; + return &tp->stats; } @@ -1709,16 +1645,26 @@ static u16 elPMDreadMode(struct device *dev, int *speed, int *duplex) { - u16 status; + u16 status, OurCap; *speed = HW_SPEED_10_MBPS; *duplex = FDX_CAPABLE_HALF_SELECTED; status = mdio_read(dev, phy_id, MII_ANLPAR); + OurCap = mdio_read(dev, phy_id, MII_ANAR); + if (sis900_debug > 1) { + printk(KERN_INFO "Link Part Status %4X\n", status); + printk(KERN_INFO "Our Status %4X\n", OurCap); + printk(KERN_INFO "Status Reg %4X\n", + mdio_read(dev, phy_id, MII_STATUS)); + } + status &= OurCap; if ( !( status & (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) { -// printk(KERN_INFO "%s: Link Partner not detected.\n", dev->name); + if (sis900_debug > 1) { + printk(KERN_INFO "The other end NOT support NWAY...\n"); + } while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ; while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ; if (status & 0x80) @@ -1734,7 +1680,10 @@ static u16 elPMDreadMode(struct device *dev, "full" : "half"); } } else { -// printk(KERN_INFO "%s: Link Partner detected\n", dev->name); + if (sis900_debug > 1) { + printk(KERN_INFO "The other end support NWAY...\n"); + } + if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) { *duplex = FDX_CAPABLE_FULL_SELECTED; } @@ -1756,14 +1705,28 @@ static u16 elPMDreadMode(struct device *dev, static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed) { - u16 status; + u16 status, retnVal; + if (sis900_debug > 1) { + printk(KERN_INFO "AutoNegotiate...\n"); + } mdio_write(dev, phy_id, MII_CONTROL, 0); - mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | - MIICNTL_RST_AUTO); - elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, FALSE,&status); - elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, TRUE, &status); - elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, TRUE, &status); + mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | MIICNTL_RST_AUTO); + retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, + FALSE,&status); + if (!retnVal) { + printk(KERN_INFO "Not wait for Reset Complete\n"); + } + retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, + TRUE, &status); + if (!retnVal) { + printk(KERN_INFO "Not wait for AutoNego Complete\n"); + } + retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, + TRUE, &status); + if (!retnVal) { + printk(KERN_INFO "Not wait for Link Complete\n"); + } if (status & MIISTAT_LINK) { elPMDreadMode(dev, phy_id, speed, duplex); elSetMediaType(dev, *speed, *duplex); @@ -1771,6 +1734,29 @@ static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *spe return(status); } +static void elSetCapability(struct device *dev, int phy_id, + int duplex, int speed) +{ + u16 cap = ( MII_NWAY_T | MII_NWAY_T_FDX | + MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_CSMA_CD ); + + if (speed != 100) { + cap &= ~( MII_NWAY_TX | MII_NWAY_TX_FDX ); + if (sis900_debug > 1) { + printk(KERN_INFO "UNSET 100Mbps\n"); + } + } + + if (!duplex) { + cap &= ~( MII_NWAY_T_FDX | MII_NWAY_TX_FDX ); + if (sis900_debug > 1) { + printk(KERN_INFO "UNSET full-duplex\n"); + } + } + + mdio_write(dev, phy_id, MII_ANAR, cap); +} + static void elSetMediaType(struct device *dev, int speed, int duplex) { long ioaddr = dev->base_addr; diff --git a/drivers/pci/oldproc.c b/drivers/pci/oldproc.c index 0839b153b302..ff4f987ff9a6 100644 --- a/drivers/pci/oldproc.c +++ b/drivers/pci/oldproc.c @@ -535,6 +535,7 @@ struct pci_dev_info dev_info[] = { DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4 USB"), DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 ACPI"), DEVICE( INTEL, INTEL_82443LX_0,"440LX - 82443LX PAC Host"), + DEVICE( COMPUTONE, COMPUTONE_IP2EX, "Computone IntelliPort Plus"), DEVICE( INTEL, INTEL_82443LX_1,"440LX - 82443LX PAC AGP"), DEVICE( INTEL, INTEL_82443BX_0,"440BX - 82443BX Host"), DEVICE( INTEL, INTEL_82443BX_1,"440BX - 82443BX AGP"), @@ -830,6 +831,7 @@ static const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_NVIDIA_SGS: return "NVidia/SGS Thomson"; case PCI_VENDOR_ID_CBOARDS: return "ComputerBoards"; case PCI_VENDOR_ID_SYMPHONY: return "Symphony"; + case PCI_VENDOR_ID_COMPUTONE: return "Computone Corporation"; case PCI_VENDOR_ID_TEKRAM: return "Tekram"; case PCI_VENDOR_ID_3DLABS: return "3Dlabs"; case PCI_VENDOR_ID_AVANCE: return "Avance"; diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 55082434cf1f..8c3845f88253 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -327,6 +327,10 @@ #include "jazz_esp.h" #endif +#ifdef CONFIG_IPHASE5526 +#include "../net/fc/iph5526_scsi.h" +#endif + /* * Moved ppa driver to the end of the probe list * since it is a removable host adapter. @@ -589,6 +593,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] = POWERTECSCSI, #endif #endif +#ifdef CONFIG_IPHASE5526 + IPH5526_SCSI_FC, +#endif /* "Removable host adapters" below this line (Parallel Port/USB/other) */ #ifdef CONFIG_SCSI_PPA PPA, diff --git a/drivers/scsi/ini9100u.c b/drivers/scsi/ini9100u.c index e23fc28765c0..9554dcdc08bd 100644 --- a/drivers/scsi/ini9100u.c +++ b/drivers/scsi/ini9100u.c @@ -118,19 +118,13 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) #include -#include #include -#include #include -#include -#include #if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92) #include #endif #include -#include #include -#include #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23) #include #endif @@ -138,39 +132,28 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) #include #endif -#include "sd.h" -#include "scsi.h" -#include "hosts.h" -#include "ini9100u.h" #include -#include #include #else -#include #include #include +#include +#include "../block/blk.h" +#endif + +#include #include #include - #include #include -#include #include -#include "../block/blk.h" #include "scsi.h" #include "sd.h" #include "hosts.h" #include #include "ini9100u.h" -#endif - -#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93) -#ifdef CONFIG_PCI -#include -#endif -#endif #ifdef DEBUG_i91u unsigned int i91u_debug = DEBUG_DEFAULT; diff --git a/drivers/scsi/inia100.c b/drivers/scsi/inia100.c index b6f4eb34f8f1..870b24e6c97c 100644 --- a/drivers/scsi/inia100.c +++ b/drivers/scsi/inia100.c @@ -73,19 +73,13 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) #include -#include #include -#include #include -#include -#include #include -#include #if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92) #include #endif #include -#include #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23) #include #endif @@ -93,33 +87,27 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) #include #endif -#include "sd.h" -#include "scsi.h" -#include "hosts.h" -#include "inia100.h" #include -#include - #else -#include #include #include +#include +#include "../block/blk.h" +#endif + +#include #include #include - #include #include -#include #include -#include "../block/blk.h" #include "scsi.h" #include "sd.h" #include "hosts.h" #include #include "inia100.h" -#endif #ifdef MODULE Scsi_Host_Template driver_template = INIA100; diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c index ca17a5b0e753..6d630e1ce82e 100644 --- a/drivers/scsi/mac53c94.c +++ b/drivers/scsi/mac53c94.c @@ -85,7 +85,9 @@ mac53c94_detect(Scsi_Host_Template *tp) if (host == 0) panic("couldn't register 53c94 host"); host->unique_id = nfscs; +#ifndef MODULE note_scsi_host(node, host); +#endif state = (struct fsc_state *) host->hostdata; if (state == 0) diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index 316d661b5c82..71a50d624054 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -252,8 +252,10 @@ mesh_detect(Scsi_Host_Template *tp) continue; } mesh_host->unique_id = nmeshes; +#ifndef MODULE note_scsi_host(mesh, mesh_host); - +#endif + ms = (struct mesh_state *) mesh_host->hostdata; if (ms == 0) panic("no mesh state"); @@ -685,16 +687,15 @@ finish_cmds(void *data) unsigned long flags; for (;;) { - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); cmd = ms->completed_q; if (cmd == NULL) { - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); break; } ms->completed_q = (Scsi_Cmnd *) cmd->host_scribble; - restore_flags(flags); (*cmd->scsi_done)(cmd); + spin_unlock_irqrestore(&io_request_lock, flags); } } @@ -762,8 +763,8 @@ start_phase(struct mesh_state *ms) Scsi_Cmnd *cmd = ms->current_req; struct mesh_target *tp = &ms->tgts[ms->conn_tgt]; - dlog(ms, "start_phase err/exc/fc/seq = %.8x", - MKWORD(mr->error, mr->exception, mr->fifo_count, mr->sequence)); + dlog(ms, "start_phase nmo/exc/fc/seq = %.8x", + MKWORD(ms->n_msgout, mr->exception, mr->fifo_count, mr->sequence)); out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0); switch (ms->msgphase) { @@ -1057,6 +1058,7 @@ cmd_complete(struct mesh_state *ms) t = 230; /* wait up to 230us */ while ((mr->bus_status0 & BS0_REQ) == 0) { if (--t < 0) { + dlog(ms, "impatient for req", ms->n_msgout); ms->msgphase = msg_none; break; } @@ -1643,7 +1645,7 @@ mesh_done(struct mesh_state *ms, int start_next) static void mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd) { -#if 0 +#if 1 if (ms->completed_q == NULL) ms->completed_q = cmd; else diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c index 0414f19ffc74..5684d83292ff 100644 --- a/drivers/scsi/pas16.c +++ b/drivers/scsi/pas16.c @@ -73,7 +73,6 @@ * software after reset using the default_irq for the * current board number. * - * * 2. With command line overrides - pas16=port,irq may be * used on the LILO command line to override the defaults. * @@ -83,6 +82,11 @@ * -DPAS16_OVERRIDE={{0x388, 10}} * NOTE: Untested. * + * 4. When included as a module, with arguments passed on the command line: + * pas16_irq=xx the interrupt + * pas16_addr=xx the port + * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5" + * * Note that if the override methods are used, place holders must * be specified for other boards in the system. * @@ -99,7 +103,11 @@ * interrupts. Ie, for a board at the default 0x388 base port, * boot: linux pas16=0x388,255 * - * (255 is the IRQ_NONE constant in NCR5380.h) + * IRQ_NONE (255) should be specified for no interrupt, + * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden + * on the command line. + * + * (IRQ_AUTO == 254, IRQ_NONE == 255 in NCR5380.h) */ #ifdef MODULE @@ -129,6 +137,8 @@ struct proc_dir_entry proc_scsi_pas16 = { }; static int pas_maxi = 0; static int pas_wmaxi = 0; +static unsigned short pas16_addr = 0; +static int pas16_irq = 0; int scsi_irq_translate[] = @@ -383,6 +393,22 @@ __initfunc(int pas16_detect(Scsi_Host_Template * tpnt)) { tpnt->proc_dir = &proc_scsi_pas16; tpnt->proc_info = &pas16_proc_info; + if (pas16_addr != 0) { + overrides[0].io_port = pas16_addr; + /* + * This is how we avoid seeing more than + * one host adapter at the same I/O port. + * Cribbed shamelessly from pas16_setup(). + */ + for (count = 0; count < NO_BASES; ++count) + if (bases[count].io_port == pas16_addr) { + bases[count].noauto = 1; + break; + } + } + if (pas16_irq != 0) + overrides[0].irq = pas16_irq; + for (count = 0; current_override < NO_OVERRIDES; ++current_override) { io_port = 0; @@ -576,7 +602,11 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src #ifdef MODULE /* Eventually this will go into an include file, but this will be later */ Scsi_Host_Template driver_template = MV_PAS16; - + #include + +MODULE_PARM(pas16_addr, "h"); +MODULE_PARM(pas16_irq, "i"); + #include "scsi_module.c" #endif diff --git a/drivers/scsi/sym53c8xx_defs.h b/drivers/scsi/sym53c8xx_defs.h index 027668898ef1..01b6c82c9255 100644 --- a/drivers/scsi/sym53c8xx_defs.h +++ b/drivers/scsi/sym53c8xx_defs.h @@ -473,6 +473,9 @@ typedef struct { {PCI_DEVICE_ID_NCR_53C875, 0x2f, "875E", 6, 16, 5, \ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ , \ + {PCI_DEVICE_ID_NCR_53C875, 0xff, "876", 6, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + , \ {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5, \ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ , \ diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h index 0a49ce982a80..d559f05f3078 100644 --- a/drivers/scsi/wd33c93.h +++ b/drivers/scsi/wd33c93.h @@ -23,6 +23,7 @@ #ifndef WD33C93_H #define WD33C93_H +#include #define PROC_INTERFACE /* add code for /proc/scsi/wd33c93/xxx interface */ #ifdef PROC_INTERFACE diff --git a/drivers/sound/mad16.c b/drivers/sound/mad16.c index d4acccb23251..6b1b2d13fcb8 100644 --- a/drivers/sound/mad16.c +++ b/drivers/sound/mad16.c @@ -889,7 +889,7 @@ int irq = -1; int cdtype = 0; int cdirq = 0; int cdport = 0x340; -int cddma = 3; +int cddma = -1; int opl4 = 0; int joystick = 0; @@ -949,23 +949,28 @@ int init_module(void) break; case 0x02: printk("Sony CDU31A"); - dmatype = 2; + dmatype = 1; + if(cddma == -1) cddma = 3; break; case 0x04: printk("Mitsumi"); - dmatype = 1; + dmatype = 0; + if(cddma == -1) cddma = 5; break; case 0x06: printk("Panasonic Lasermate"); - dmatype = 2; + dmatype = 1; + if(cddma == -1) cddma = 3; break; case 0x08: printk("Secondary IDE"); - dmatype = 1; + dmatype = 0; + if(cddma == -1) cddma = 5; break; case 0x0A: printk("Primary IDE"); - dmatype = 1; + dmatype = 0; + if(cddma == -1) cddma = 5; break; default: printk("\n"); @@ -973,8 +978,16 @@ int init_module(void) return -EINVAL; } - if (dmatype) - { + /* + * Build the config words + */ + + mad16_conf = (joystick ^ 1) | cdtype; + mad16_cdsel = 0; + if (opl4) + mad16_cdsel |= 0x20; + + if(cdtype){ if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) { printk("\n"); @@ -985,58 +998,51 @@ int init_module(void) printk(", DMA %d", cddma); else printk(", no DMA"); - } - if (cdtype && !cdirq) - printk(", no IRQ"); - else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) - { - printk(", invalid IRQ (disabling)"); - cdirq = 0; - } - else printk(", IRQ %d", cdirq); - printk(".\n"); - printk(KERN_INFO "Joystick port "); - if (joystick == 1) - printk("enabled.\n"); - else - { - joystick = 0; - printk("disabled.\n"); - } - - /* - * Build the config words - */ + if (!cdirq) + printk(", no IRQ"); + else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) + { + printk(", invalid IRQ (disabling)"); + cdirq = 0; + } + else printk(", IRQ %d", cdirq); - mad16_conf = (joystick ^ 1) | cdtype; - mad16_cdsel = 0; - if (opl4) - mad16_cdsel |= 0x20; - mad16_cdsel |= dma_map[dmatype][cddma]; + mad16_cdsel |= dma_map[dmatype][cddma]; - if (cdtype < 0x08) - { - switch (cdport) + if (cdtype < 0x08) { - case 0x340: - mad16_cdsel |= 0x00; - break; - case 0x330: - mad16_cdsel |= 0x40; - break; - case 0x360: - mad16_cdsel |= 0x80; - break; - case 0x320: - mad16_cdsel |= 0xC0; - break; - default: - printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); - return -EINVAL; + switch (cdport) + { + case 0x340: + mad16_cdsel |= 0x00; + break; + case 0x330: + mad16_cdsel |= 0x40; + break; + case 0x360: + mad16_cdsel |= 0x80; + break; + case 0x320: + mad16_cdsel |= 0xC0; + break; + default: + printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); + return -EINVAL; + } } + mad16_cdsel |= irq_map[cdirq]; } - mad16_cdsel |= irq_map[cdirq]; + + printk(".\n"); + printk(KERN_INFO "Joystick port "); + if (joystick == 1) + printk("enabled.\n"); + else + { + joystick = 0; + printk("disabled.\n"); + } config.io_base = io; config.irq = irq; diff --git a/drivers/usb/CREDITS b/drivers/usb/CREDITS index 6cd211b2270c..7ad890ee2e48 100644 --- a/drivers/usb/CREDITS +++ b/drivers/usb/CREDITS @@ -8,6 +8,7 @@ difficult to maintain, add yourself with a patch if desired. Johannes Erdfelt ham Bradley M Keryan + Paul Mackerras Vojtech Pavlik Gregory P. Smith Linus Torvalds @@ -28,7 +29,11 @@ of hardware, support, time and development (this is from the original THANKS file in Inaky's driver): The following corporations have helped us in the development -of Linux USB / UUSBD: + of Linux USB / UUSBD: + + - 3Com GmbH for donating a ISDN Pro TA and supporting me + in technical questions and with test equipment. I'd never + expect such a great help. - USAR Systems provided us with one of their excellent USB Evaluation Kits. It allows us to test the Linux-USB driver diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 632e3d08a5a8..7eff290a0d7c 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -5,25 +5,39 @@ # Right now hubs, mice and keyboards work - at least with UHCI. # But that may be more a lucky coincidence than anything else.. # -# This was all developed modularly, but I've been lazy in cleaning -# it up, so right now they are all bools. -# mainmenu_option next_comment comment 'USB drivers - not for the faint of heart' -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB - if [ ! "$CONFIG_USB" = "n" ]; then - bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI - bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI - bool 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support?' CONFIG_USB_OHCI_HCD - if [ "$CONFIG_USB_OHCI_HCD" = "y" ]; then - bool 'OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB - fi +tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB +if [ ! "$CONFIG_USB" = "n" ]; then + dep_tristate 'UHCI (intel PIIX4 and others) support' CONFIG_USB_UHCI \ + $CONFIG_USB + dep_tristate 'OHCI (mac, compaq and others) support' CONFIG_USB_OHCI \ + $CONFIG_USB - bool 'USB mouse support' CONFIG_USB_MOUSE - bool 'USB keyboard support' CONFIG_USB_KBD - bool 'USB audio parsing support' CONFIG_USB_AUDIO + if [ "$CONFIG_USB_OHCI" != "n" ]; then + bool ' Enable tons of OHCI debugging output' CONFIG_USB_OHCI_DEBUG + fi + dep_tristate 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support' \ + CONFIG_USB_OHCI_HCD $CONFIG_USB + if [ "$CONFIG_USB_OHCI_HCD" != "n" ]; then +# bool ' OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB + define_bool CONFIG_USB_OHCI_VROOTHUB y + fi + + dep_tristate 'USB hub support' CONFIG_USB_HUB $CONFIG_USB + dep_tristate 'USB mouse support' CONFIG_USB_MOUSE $CONFIG_USB + dep_tristate 'USB keyboard support' CONFIG_USB_KBD $CONFIG_USB + dep_tristate 'USB audio parsing support' CONFIG_USB_AUDIO $CONFIG_USB + dep_tristate 'USB Abstract Control Model support' CONFIG_USB_ACM $CONFIG_USB + dep_tristate 'USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB + dep_tristate 'USB SCSI Support' CONFIG_USB_SCSI $CONFIG_USB + if [ "$CONFIG_USB_SCSI" != "n" ]; then + dep_tristate ' USB SCSI verbose debug' CONFIG_USB_SCSI_DEBUG $CONFIG_USB_SCSI + fi + dep_tristate 'EZUSB Firmware downloader' CONFIG_USB_EZUSB $CONFIG_USB + if [ "$CONFIG_PROC_FS" != "n" ]; then + bool 'Preliminary /proc/bus/usb support' CONFIG_USB_PROC fi fi diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 6c6e7b091cc0..10c8bfa1605e 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -7,8 +7,6 @@ # # Note 2! The CFLAGS definitions are now inherited from the # parent makes.. -# -# This isn't actually supported yet. Don't try to use it. SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) @@ -17,59 +15,127 @@ ALL_SUB_DIRS := $(SUB_DIRS) L_TARGET := usb.a M_OBJS := L_OBJS := -LX_OBJS := -USBX_OBJS := usb.o hub.o usb-debug.o + +ifeq ($(CONFIG_USB),y) + L_OBJS +=usbcore.o +endif +ifeq ($(CONFIG_USB),m) + M_OBJS +=usbcore.o + MIX_OBJS +=usb.o usb-debug.o usb-core.o + ifeq ($(CONFIG_USB_PROC),y) + MIX_OBJS += proc_usb.o + endif +endif + +ifeq ($(CONFIG_USB_UHCI),y) + L_OBJS += uhci.o uhci-debug.o +endif + +ifeq ($(CONFIG_USB_UHCI),m) + M_OBJS += usb-uhci.o + MIX_OBJS += uhci.o uhci-debug.o +endif + +ifeq ($(CONFIG_USB_OHCI),y) + L_OBJS += ohci.o ohci-debug.o +endif +ifeq ($(CONFIG_USB_OHCI),m) + M_OBJS += usb-ohci.o + MIX_OBJS += ohci.o ohci-debug.o +endif + +ifeq ($(CONFIG_USB_OHCI_HCD),y) + L_OBJS += ohci-hcd.o ohci-root-hub.o +endif +ifeq ($(CONFIG_USB_OHCI_HCD),m) + M_OBJS += usb-ohci-hcd.o + MIX_OBJS += ohci-hcd.o ohci-root-hub.o +endif ifeq ($(CONFIG_USB_MOUSE),y) - USBX_OBJS += mouse.o + L_OBJS += mouse.o +endif +ifeq ($(CONFIG_USB_MOUSE),m) + M_OBJS +=mouse.o + MIX_OBJS +=mouse.o endif -ifeq ($(CONFIG_USB_KBD),y) - USBX_OBJS += keyboard.o keymap.o +ifeq ($(CONFIG_USB_HUB),y) + L_OBJS += hub.o +endif +ifeq ($(CONFIG_USB_HUB),m) + M_OBJS +=hub.o + MIX_OBJS +=hub.o endif -ifeq ($(CONFIG_USB_AUDIO),y) - USBX_OBJS += audio.o +ifeq ($(CONFIG_USB_ACM),y) + L_OBJS += acm.o +endif +ifeq ($(CONFIG_USB_ACM),m) + M_OBJS += acm.o + MIX_OBJS +=acm.o endif -ifeq ($(CONFIG_USB), y) - L_OBJS += $(USBX_OBJS) +ifeq ($(CONFIG_USB_PRINTER),y) + L_OBJS += printer.o endif -ifeq ($(CONFIG_USB_UHCI),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += uhci.o uhci-debug.o +ifeq ($(CONFIG_USB_PRINTER),m) + M_OBJS += printer.o + MIX_OBJS += printer.o +endif + +ifeq ($(CONFIG_USB_KBD),y) + ifneq ($(CONFIG_MAC_KEYBOARD),y) + L_OBJS += keyboard.o keymap.o else - ifeq ($(CONFIG_USB),m) - M_OBJS += usb-uhci.o - MIX_OBJS += $(USBX_OBJS) - endif + L_OBJS += keyboard.o keymap-mac.o endif endif -ifeq ($(CONFIG_USB_OHCI),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += ohci.o ohci-debug.o +ifeq ($(CONFIG_USB_KBD),m) + M_OBJS += usb-keyboard.o + ifneq ($(CONFIG_MAC_KEYBOARD),y) + MIX_OBJS += keyboard.o keymap.o else - ifeq ($(CONFIG_USB),m) - USBO_OBJS += ohci.o ohci-debug.o - M_OBJS += usb-ohci.o - MIX_OBJS += $(USBX_OBJS) - endif + MIX_OBJS += keyboard.o keymap-mac.o endif endif -ifeq ($(CONFIG_USB_OHCI_HCD),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += ohci-hcd.o ohci-root-hub.o - else - ifeq ($(CONFIG_USB),m) - USBO_OBJS += ohci-hcd.o ohci-root-hub.o - M_OBJS += usb-ohci-hcd.o - MIX_OBJS += $(USBX_OBJS) - endif +ifeq ($(CONFIG_USB_AUDIO),y) + L_OBJS += audio.o +endif + +ifeq ($(CONFIG_USB_AUDIO),m) + M_OBJS += audio.o + MIX_OBJS += audio.o +endif + +ifeq ($(CONFIG_USB_CPIA),y) + L_OBJS += cpia.o +endif + +ifeq ($(CONFIG_USB_CPIA),m) + M_OBJS += cpia.o + MIX_OBJS += cpia.o +endif + +ifeq ($(CONFIG_USB_SCSI),y) + L_OBJS += usb_scsi.o + ifeq ($(CONFIG_USB_SCSI_DEBUG),y) + L_OBJS += usb_scsi_debug.o endif endif + +ifeq ($(CONFIG_USB_EZUSB),y) + L_OBJS += ezusb.o +endif + +ifeq ($(CONFIG_USB_EZUSB),m) + M_OBJS += ezusb.o + MIX_OBJS += ezusb.o +endif + include $(TOPDIR)/Rules.make keymap.o: keymap.c @@ -77,12 +143,31 @@ keymap.o: keymap.c keymap.c: maps/serial.map maps/usb.map maps/fixup.map ./mkmap > $@ -usb-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ uhci.o uhci-debug.o $(USBX_OBJS) +keymap-mac.o: keymap-mac.c +keymap-mac.c: maps/mac.map maps/usb.map + ./mkmap.adb > $@ -usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o $(USBX_OBJS) +ifneq ($(CONFIG_MAC_KEYBOARD),y) +usb-keyboard.o: keymap.o keyboard.o + $(LD) $(LD_RFLAG) -r -o $@ keymap.o keyboard.o +else +usb-keyboard.o: keymap-mac.o keyboard.o + $(LD) $(LD_RFLAG) -r -o $@ keymap-mac.o keyboard.o +endif -usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) +usb-uhci.o: uhci.o uhci-debug.o + $(LD) $(LD_RFLAG) -r -o $@ uhci.o uhci-debug.o + +usb-ohci.o: ohci.o ohci-debug.o + $(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o + +usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o + $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o +ifeq ($(CONFIG_USB_PROC),y) +usbcore.o: usb.o usb-debug.o usb-core.o proc_usb.o + $(LD) $(LD_RFLAG) -r -o $@ usb.o usb-debug.o usb-core.o proc_usb.o +else +usbcore.o: usb.o usb-debug.o usb-core.o + $(LD) $(LD_RFLAG) -r -o $@ usb.o usb-debug.o usb-core.o +endif diff --git a/drivers/usb/README.ohci b/drivers/usb/README.ohci index 7a544eb3353a..921d9a9a36a4 100644 --- a/drivers/usb/README.ohci +++ b/drivers/usb/README.ohci @@ -1,4 +1,50 @@ -May 09, 1999 16:25:58 +[This is the readme for ohci.c, ohci-debug.c and ohci.h] + +June 23, 1999 00:31:20 PST + +I now have bulk support in a reasonably working state. The only +device I have tested it with at the moment is my Epson Stylus 740 +printer. I can print both small and large files. + +I have included code to support transfers of large amounts of data in +either control or bulk transfers. Check out the build_td_chain() and +add_td_chain_to_ed() functions. + +TODO: + +~ Get Michael Gee's mass storage driver working with my donated + YE-Data floppy drive over OHCI. +~ Drool on the Epson printer because its the new toy around the house. + +June 08, 1999 01:23:34 + +Paul Mackerras went through the OHCI (& USB code) to fix most of the +endianness issues so that the code now works on Linux-PPC. He also +simplified add_td_to_ed to be simpler & atomic to the hardware. + +May 16, 1999 16:20:54 + +EDs are now allocated dynamically from their device's pool. Root hub +status changes should stop the infinite "no device connected" messages +that occurred after removing a device. + +TODO: + +~ Add Isochronous transfer support. These have their own special + format TDs to allow for several DMA data pointers. Kinda neat, but + likely harder to use through a generic interface in practice. +~ Support dynamic allocation & growth of the TD/ED pools. Merge them + into global pools rather than a today's static per device allocation. + +KNOWN BUGS: + +~ Unplugging a hub causes khubd to Oops. I don't think this is + directly related to OHCI, but due to the fact that the interrupt TD + for the hub is never stopped. We need a usb_release_irq() that gets + called using the "IRQ handle" that should be returned by + usb_request_irq(). + +May 09, 1999 16:25:58 PST Cool, things are working "well" now. (I'm not getting oops's from the OHCI code anyways.. ;). I can attach a usb hub and mouse in any @@ -9,18 +55,5 @@ acknowledged because /proc/interrupts usb-ohci goes up accordingly with mouse movements/events. That means the TD at least returns some data and requeues itself. -Device attach/detach from the root hub is not working well. Currently -every interrupt checks for root hub status changes and frame number -overflow interrupts are enabled. This means you shouldn't have to -wait more than 32-33 seconds for the change to occur, less if there is -other activity. (due to checking in the WDH caused interrupts) -My OHCI controller [SiS 5598 motherboard] doesn't seem to play well -with the RHSC interrupt so it has been disabled. The ohci_timer -should be polling but it not currently working, I haven't had time to -look into that problem. - -However, when I tried telling X to use /dev/psaux for the mouse my -machine locked up... - - greg@electricrain.com diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c new file mode 100644 index 000000000000..4521bd701ba1 --- /dev/null +++ b/drivers/usb/acm.c @@ -0,0 +1,295 @@ +/* + * USB Abstract Control Model based on Brad Keryan's USB busmouse driver + * + * Armin Fuerst 5/8/1999 + * + * version 0.2: Improved Bulk transfer. TX led now flashes every time data is + * sent. Send Encapsulated Data is not needed, nor does it do anything. + * Why's that ?!? Thanks to Thomas Sailer for his close look at the bulk code. + * He told me about some importand bugs. (5/21/99) + * + * version 0.1: Bulk transfer for uhci seems to work now, no dangling tds any + * more. TX led of the ISDN TA flashed the first time. Does this mean it works? + * The interrupt of the ctrl endpoint crashes the kernel => no read possible + * (5/19/99) + * + * version 0.0: Driver sets up configuration, sets up data pipes, opens misc + * device. No actual data transfer is done, since we don't have bulk transfer, + * yet. Purely skeleton for now. (5/8/99) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usb.h" + +#define USB_ACM_MINOR 32 + +struct acm_state { + int present; /* this acm is plugged in */ + int active; /* someone is has this acm's device open */ + int serstate; /* Status of the serial port (rate, handshakelines,...) */ + struct usb_device *dev; + unsigned ctrlbuffer; /*buffer for control messages*/ + unsigned int readendp,writeendp,ctrlendp; + unsigned int readpipe,writepipe,ctrlpipe; + char buffer; +}; + +static struct acm_state static_acm_state; + +spinlock_t usb_acm_lock = SPIN_LOCK_UNLOCKED; + +static int acm_irq(int state, void *__buffer, int len, void *dev_id) +{ +// unsigned char *data = __buffer; + struct acm_state *acm = &static_acm_state; + devrequest *dr; + + dr=__buffer; + printk("ACM_USB_IRQ\n"); + printk("reqtype: %02X\n",dr->requesttype); + printk("request: %02X\n",dr->request); + printk("wValue: %02X\n",dr->value); + printk("wIndex: %02X\n",dr->index); + printk("wLength: %02X\n",dr->length); + + switch(dr->request) { + //Network connection + case 0x00: + printk("Network connection: "); + if (dr->request==0) printk("disconnected\n"); + if (dr->request==1) printk("connected\n"); + break; + + //Response available + case 0x01: + printk("Response available\n"); + acm->buffer=1; + break; + + //Set serial line state + case 0x20: + if ((dr->index==1)&&(dr->length==2)) { + acm->serstate=acm->ctrlbuffer; + printk("Serstate: %02X\n",acm->ctrlbuffer); + } + break; + } +/* + if(!acm->active) + return 1; +*/ + return 1; +} + +static int release_acm(struct inode * inode, struct file * file) +{ + struct acm_state *acm = &static_acm_state; + printk("ACM_FILE_RELEASE\n"); + +// fasync_acm(-1, file, 0); + if (--acm->active) + return 0; + return 0; +} + +static int open_acm(struct inode * inode, struct file * file) +{ + struct acm_state *acm = &static_acm_state; + printk("USB_FILE_OPEN\n"); + + if (!acm->present) + return -EINVAL; + if (acm->active++) + return 0; + return 0; +} + +static ssize_t write_acm(struct file * file, + const char * buffer, size_t count, loff_t *ppos) +{ + devrequest dr; + struct acm_state *acm = &static_acm_state; + unsigned long retval; + + printk("USB_FILE_WRITE\n"); +//Huh, i seem to got that wrong, we don't need this ?!? +/* + dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT; + dr.request = 0; + dr.value = 0; + dr.index = acm->writeendp; + dr.length = count; + acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0); +*/ + + acm->dev->bus->op->bulk_msg(acm->dev,&acm->writepipe,buffer, count, &retval); + return -EINVAL; +} + + +static ssize_t read_acm(struct file * file, const char * buffer, size_t count, loff_t *ppos) +{ + devrequest dr; + struct acm_state *acm = &static_acm_state; + unsigned long retval; + printk("USB_FILE_READ\n"); +// if (!acm->buffer) return -1; + acm->buffer=0; +//We don't need this +/* + printk("writing control msg\n"); + dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT | 0x80; + dr.request = 1; + dr.value = 0; + dr.index = acm->readendp; + dr.length = 0; + acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0); +*/ + printk("reading:>%s<\n",buffer); + acm->dev->bus->op->bulk_msg(acm->dev,&acm->readpipe,buffer, 1,&retval); + printk("done:>%s<\n",buffer); + return 1; +} + +struct file_operations usb_acm_fops = { + NULL, /* acm_seek */ + read_acm, + write_acm, + NULL, /* acm_readdir */ + NULL, /* acm_poll */ + NULL, /* acm_ioctl */ + NULL, /* acm_mmap */ + open_acm, + NULL, /* flush */ + release_acm, + NULL, + NULL, /*fasync*/ +}; + +static struct miscdevice usb_acm = { + USB_ACM_MINOR, "USB ACM", &usb_acm_fops +}; + +static int acm_probe(struct usb_device *dev) +{ + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct acm_state *acm = &static_acm_state; + int cfgnum; + + /* Only use CDC */ + if (dev->descriptor.bDeviceClass != 2 || + dev->descriptor.bDeviceSubClass != 0 || + dev->descriptor.bDeviceProtocol != 0) + return -1; + + /*Now scan all configs for a ACM configuration*/ + for (cfgnum=0;cfgnumdescriptor.bNumConfigurations;cfgnum++) { + /* The first one should be Communications interface? */ + interface = &dev->config[cfgnum].altsetting[0].interface[0]; + if (interface->bInterfaceClass != 2 || + interface->bInterfaceSubClass != 2 || + interface->bInterfaceProtocol != 1 || + interface->bNumEndpoints != 1) + continue; + + /*Which uses an interrupt input */ + endpoint = &interface->endpoint[0]; + if ((endpoint->bEndpointAddress & 0x80) != 0x80 || + (endpoint->bmAttributes & 3) != 3) + continue; + + /* The second one should be a Data interface? */ + interface = &dev->config[cfgnum].altsetting[0].interface[1]; + if (interface->bInterfaceClass != 10 || + interface->bInterfaceSubClass != 0 || + interface->bInterfaceProtocol != 0 || + interface->bNumEndpoints != 2) + continue; + + /*With a bulk input */ + endpoint = &interface->endpoint[0]; + if ((endpoint->bEndpointAddress & 0x80) != 0x80 || + (endpoint->bmAttributes & 3) != 2) + continue; + + /*And a bulk output */ + endpoint = &interface->endpoint[1]; + if ((endpoint->bEndpointAddress & 0x80) == 0x80 || + (endpoint->bmAttributes & 3) != 2) + continue; + + printk("USB ACM found\n"); + if (usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue)) { + printk (KERN_INFO " Failed usb_set_configuration: ACM\n"); + continue; + } + acm->dev=dev; + acm->readendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[0].bEndpointAddress; + acm->writeendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[1].bEndpointAddress; + acm->ctrlendp=dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bEndpointAddress; + acm->readpipe=usb_rcvbulkpipe(dev,acm->readendp); + acm->writepipe=usb_sndbulkpipe(dev,acm->writeendp); + usb_request_irq(dev,acm->ctrlpipe=usb_rcvctrlpipe(dev,acm->ctrlendp), acm_irq, dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bInterval, &acm->ctrlbuffer); + acm->present = 1; + acm->buffer=0; + return 0; + } + + return -1; +} + +static void acm_disconnect(struct usb_device *dev) +{ + struct acm_state *acm = &static_acm_state; + + /* this might need work */ + acm->present = 0; +} + +static struct usb_driver acm_driver = { + "acm", + acm_probe, + acm_disconnect, + { NULL, NULL } +}; + +int usb_acm_init(void) +{ + struct acm_state *acm = &static_acm_state; + + misc_register(&usb_acm); + + acm->present = acm->active = 0; + + usb_register(&acm_driver); + printk(KERN_INFO "USB ACM registered.\n"); + return 0; +} + +#ifdef MODULE + +int init_module(void) +{ + return usb_acm_init(); +} + +void cleanup_module(void) +{ + /* this, too, probably needs work */ + usb_deregister(&acm_driver); + misc_deregister(&usb_acm); +} + +#endif diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 8b0c9d15c748..fc503f3cb7ec 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -3,6 +3,8 @@ #include #include #include +#include + #include "usb.h" static int usb_audio_probe(struct usb_device *dev); @@ -24,7 +26,7 @@ static struct usb_driver usb_audio_driver = }; -static int usb_audio_irq(int state, void *buffer, void *dev_id) +static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) { struct usb_audio *aud = (struct usb_audio*) dev_id; return 1; @@ -39,7 +41,7 @@ static int usb_audio_probe(struct usb_device *dev) int i; int na=0; - interface = &dev->config[0].interface[0]; + interface = &dev->config[0].altsetting[0].interface[0]; for(i=0;iconfig[0].bNumInterfaces;i++) { @@ -79,7 +81,10 @@ static int usb_audio_probe(struct usb_device *dev) endpoint = &interface->endpoint[0]; -// usb_set_configuration(dev, dev->config[0].bConfigurationValue); +// if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { +// printk (KERN_INFO " Failed usb_set_configuration: Audio\n"); +// break; +// } // usb_set_protocol(dev, 0); // usb_set_idle(dev, 0, 0); @@ -90,8 +95,13 @@ static int usb_audio_probe(struct usb_device *dev) aud); list_add(&aud->list, &usb_audio_list); + + return 0; } - return 0; + + if (aud) + kfree (aud); + return -1; } static void usb_audio_disconnect(struct usb_device *dev) @@ -124,3 +134,15 @@ void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) { } +#ifdef MODULE +int init_module(void) +{ + return usb_audio_init(); +} + +void cleanup_module(void) +{ + usb_deregister(&usb_audio_driver); +} + +#endif diff --git a/drivers/usb/cpia.c b/drivers/usb/cpia.c new file mode 100644 index 000000000000..abfd1b11be7a --- /dev/null +++ b/drivers/usb/cpia.c @@ -0,0 +1,1277 @@ +/* + * USB CPiA Video Camera driver + * + * Supports CPiA based Video Camera's. Many manufacturers use this chipset. + * There's a good chance, if you have a USB video camera, it's a CPiA based + * one + * + * (C) Copyright 1999 Johannes Erdfelt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "usb.h" +#include "cpia.h" + +#define MAX_FRAME_SIZE (384 * 288 * 3) + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +/* convert virtual user memory address to physical address */ +/* (virt_to_phys only works for kmalloced kernel memory) */ + +static inline unsigned long uvirt_to_phys(unsigned long adr) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + + pgd = pgd_offset(current->mm, adr); + if (pgd_none(*pgd)) + return 0; + pmd = pmd_offset(pgd, adr); + if (pmd_none(*pmd)) + return 0; + ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); + pte = *ptep; + if(pte_present(pte)) + return + virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); + return 0; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + return virt_to_bus(phys_to_virt(uvirt_to_phys(adr))); +} + +/* convert virtual kernel memory address to physical address */ +/* (virt_to_phys only works for kmalloced kernel memory) */ + +static inline unsigned long kvirt_to_phys(unsigned long adr) +{ + return uvirt_to_phys(VMALLOC_VMADDR(adr)); +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + return uvirt_to_bus(VMALLOC_VMADDR(adr)); +} + + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem=vmalloc(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_phys(adr); + mem_map_reserve(MAP_NR(phys_to_virt(page))); + adr+=PAGE_SIZE; + if (size > PAGE_SIZE) + size-=PAGE_SIZE; + else + size=0; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_phys(adr); + mem_map_unreserve(MAP_NR(phys_to_virt(page))); + adr+=PAGE_SIZE; + if (size > PAGE_SIZE) + size-=PAGE_SIZE; + else + size=0; + } + vfree(mem); + } +} + +int usb_cpia_get_version(struct usb_device *dev, void *buf) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80; + dr.request = USB_REQ_CPIA_GET_VERSION; + dr.value = 0; + dr.index = 0; + dr.length = 4; + + return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 4); +} + +int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80; + dr.request = USB_REQ_CPIA_GET_PNP_ID; + dr.value = 0; + dr.index = 0; + dr.length = 6; + + return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 6); +} + +int usb_cpia_get_camera_status(struct usb_device *dev, void *buf) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80; + dr.request = USB_REQ_CPIA_GET_CAMERA_STATUS; + dr.value = 0; + dr.index = 0; + dr.length = 8; + + return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 8); +} + +int usb_cpia_goto_hi_power(struct usb_device *dev) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_GOTO_HI_POWER; + dr.value = 0; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_get_vp_version(struct usb_device *dev, void *buf) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_GET_VP_VERSION; + dr.value = 0; + dr.index = 0; + dr.length = 4; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, buf, 4); +} + +int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_SET_SENSOR_FPS; + dr.value = (sensorclkdivisor << 8) + sensorbaserate; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_GRAB_FRAME; + dr.value = streamstartline << 8; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_upload_frame(struct usb_device *dev, int forceupload) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_UPLOAD_FRAME; + dr.value = forceupload; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_set_grab_mode(struct usb_device *dev, int continuousgrab) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_SET_GRAB_MODE; + dr.value = continuousgrab; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_set_format(struct usb_device *dev, int size, int subsample, int order) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_SET_FORMAT; + dr.value = (subsample << 8) + size; + dr.index = order; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_set_compression(struct usb_device *dev, int compmode, int decimation) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_SET_COMPRESSION; + dr.value = (decimation << 8) + compmode; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_initstreamcap(struct usb_device *dev, int skipframes, int streamstartline) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_INIT_STREAM_CAP; + dr.value = (streamstartline << 8) + skipframes; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_finistreamcap(struct usb_device *dev) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_FINI_STREAM_CAP; + dr.value = 0; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_startstreamcap(struct usb_device *dev) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_START_STREAM_CAP; + dr.value = 0; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +int usb_cpia_endstreamcap(struct usb_device *dev) +{ + devrequest dr; + + dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); + dr.request = USB_REQ_CPIA_END_STREAM_CAP; + dr.value = 0; + dr.index = 0; + dr.length = 0; + + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +#define scratch_left(x) (cpia->scratchlen - (int)((char *)x - (char *)cpia->scratch)) + +static void cpia_parse_data(struct usb_cpia *cpia) +{ + unsigned char *data = cpia->scratch; + int done; + + done = 0; + while (!done && scratch_left(data)) { + switch (cpia->state) { + case STATE_SCANNING: + { + unsigned char *begin = data; + + /* We need atleast 2 bytes for the magic value */ + if (scratch_left(data) < 2) { + done = 1; + break; + } + + printk("header: %X\n", (*data << 8) + *(data + 1)); + if ((*data == 0x19) && (*(data + 1) == 0x68)) { + cpia->state = STATE_HEADER; + printk("moving to header\n"); + break; + } + + if (scratch_left(data) < 4) { + done = 1; + break; + } + + printk("Scanning for end of frame\n"); + while (scratch_left(data) >= 4) { + if ((*data == 0xFF) && + (*(data + 1) == 0xFF) && + (*(data + 2) == 0xFF) && + (*(data + 3) == 0xFF)) { + data += 4; + break; + } + data++; + } +printk("scan: scanned %d bytes\n", data-begin); + break; + } + case STATE_HEADER: + /* We need atleast 64 bytes for the header */ + if (scratch_left(data) < 64) { + done = 1; + break; + } + +printk("header: framerate %d\n", data[41]); + + data += 64; + + cpia->state = STATE_LINES; + + break; + case STATE_LINES: + { + unsigned char *begin = data; + int found = 0; + + while (scratch_left(data)) { + if (*data == 0xFD) { + data++; + found = 1; + break; + } else if ((*data == 0xFF) && + (scratch_left(data) >= 3) && + (*(data + 1) == 0xFF) && + (*(data + 2) == 0xFF) && + (*(data + 3) == 0xFF)) { + data+=4; + cpia->curline = 144; + found = 1; + break; + } + + data++; + } +#if 0 +printk("line %d: scanned %d bytes\n", cpia->curline, data-begin); +#endif +if (data-begin == 355 && cpia->frame[cpia->curframe].width != 64) { + int i; + char *f = cpia->frame[cpia->curframe].data, *b = begin; + +#if 0 +printk("copying\n"); +#endif + + b+=2; + f+=(cpia->frame[cpia->curframe].width*3)*cpia->curline; + for (i = 0; i < 176; i++) + f[(i * 3) + 0] = + f[(i * 3) + 1] = + f[(i * 3) + 2] = + b[(i * 2)]; +} + if (found) { + cpia->curline++; + if (cpia->curline >= 144) { + wake_up(&cpia->wq); + cpia->state = STATE_SCANNING; + cpia->curline = 0; + cpia->curframe = -1; + done = 1; + } + } else { + data = begin; + done = 1; + } + + break; + } + } + } + + { + int l; + + l = scratch_left(data); + memmove(cpia->scratch, data, l); + cpia->scratchlen = l; + } +} + +static int cpia_isoc_irq(int status, void *__buffer, int len, void *dev_id) +{ + struct usb_cpia *cpia = dev_id; + struct usb_device *dev = cpia->dev; + struct cpia_sbuf *sbuf; + int i; + char *p; + + if (!cpia->streaming) { + printk("oops, not streaming, but interrupt\n"); + return 0; + } + + if (cpia->curframe < 0) { + if (cpia->frame[0].state == FRAME_READY) { + cpia->curframe = 0; + cpia->frame[0].state = FRAME_GRABBING; +printk("capturing to frame 0\n"); + } else if (cpia->frame[1].state == FRAME_READY) { + cpia->curframe = 1; + cpia->frame[1].state = FRAME_GRABBING; +printk("capturing to frame 1\n"); + } else +printk("no frame available\n"); + } + + sbuf = &cpia->sbuf[cpia->receivesbuf]; + + usb_unschedule_isochronous(dev, sbuf->isodesc); + + /* Do something to it now */ + sbuf->len = usb_compress_isochronous(dev, sbuf->isodesc); + + if (sbuf->len) + printk("%d bytes received\n", sbuf->len); + + if (sbuf->len && cpia->curframe >= 0) { + if (sbuf->len > (SCRATCH_BUF_SIZE - cpia->scratchlen)) { + printk("overflow!\n"); + return 0; + } + memcpy(cpia->scratch + cpia->scratchlen, sbuf->data, sbuf->len); + cpia->scratchlen += sbuf->len; + + cpia_parse_data(cpia); + } + + /* Reschedule this block of Isochronous desc */ + usb_schedule_isochronous(dev, sbuf->isodesc, cpia->sbuf[(cpia->receivesbuf + 2) % 3].isodesc); + + /* Move to the next one */ + cpia->receivesbuf = (cpia->receivesbuf + 1) % 3; + + return 1; +} + +int cpia_init_isoc(struct usb_cpia *cpia) +{ + struct usb_device *dev = cpia->dev; + + cpia->receivesbuf = 0; + +#if 0 + cpia->parsesbuf = 0; + cpia->parsepos = 0; +#endif + cpia->scratchlen = 0; + cpia->curline = 0; + cpia->state = STATE_SCANNING; + + /* Allocate all of the memory necessary */ + cpia->sbuf[0].isodesc = usb_allocate_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[0].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia); + cpia->sbuf[1].isodesc = usb_allocate_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[1].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia); + cpia->sbuf[2].isodesc = usb_allocate_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[2].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia); + + printk("isodesc[0] @ %p\n", cpia->sbuf[0].isodesc); + printk("isodesc[1] @ %p\n", cpia->sbuf[1].isodesc); + printk("isodesc[2] @ %p\n", cpia->sbuf[2].isodesc); + + /* Schedule the queues */ + usb_schedule_isochronous(dev, cpia->sbuf[0].isodesc, NULL); + usb_schedule_isochronous(dev, cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc); + usb_schedule_isochronous(dev, cpia->sbuf[2].isodesc, cpia->sbuf[1].isodesc); + + if (usb_set_interface(cpia->dev, 1, 3)) { + printk("cpia_set_interface error\n"); + return -EINVAL; + } + + usb_cpia_startstreamcap(cpia->dev); + + cpia->streaming = 1; + + return 0; +} + +void cpia_stop_isoc(struct usb_cpia *cpia) +{ + struct usb_device *dev = cpia->dev; + + if (!cpia->streaming) + return; + + cpia->streaming = 0; + + /* Stop the streaming */ + usb_cpia_endstreamcap(cpia->dev); + + /* Set packet size to 0 */ + if (usb_set_interface(cpia->dev, 1, 0)) { + printk("cpia_set_interface error\n"); + return /* -EINVAL */; + } + + /* Unschedule all of the iso td's */ + usb_unschedule_isochronous(dev, cpia->sbuf[2].isodesc); + usb_unschedule_isochronous(dev, cpia->sbuf[1].isodesc); + usb_unschedule_isochronous(dev, cpia->sbuf[0].isodesc); + + /* Delete them all */ + usb_delete_isochronous(dev, cpia->sbuf[2].isodesc); + usb_delete_isochronous(dev, cpia->sbuf[1].isodesc); + usb_delete_isochronous(dev, cpia->sbuf[0].isodesc); +} + +/* Video 4 Linux API */ +static int cpia_open(struct video_device *dev, int flags) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + +printk("cpia_open\n"); + + cpia->fbuf = rvmalloc(2 * MAX_FRAME_SIZE); + if (!cpia->fbuf) + goto open_err_ret; + + cpia->frame[0].state = FRAME_DONE; + cpia->frame[1].state = FRAME_DONE; + + cpia->frame[0].data = cpia->fbuf; + cpia->frame[1].data = cpia->fbuf + MAX_FRAME_SIZE; + printk("frame [0] @ %p\n", cpia->frame[0].data); + printk("frame [1] @ %p\n", cpia->frame[1].data); + + cpia->sbuf[0].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL); + if (!cpia->sbuf[0].data) + goto open_err_on0; + + cpia->sbuf[1].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL); + if (!cpia->sbuf[1].data) + goto open_err_on1; + + cpia->sbuf[2].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL); + if (!cpia->sbuf[2].data) + goto open_err_on2; + + printk("sbuf[0] @ %p\n", cpia->sbuf[0].data); + printk("sbuf[1] @ %p\n", cpia->sbuf[1].data); + printk("sbuf[2] @ %p\n", cpia->sbuf[2].data); + + cpia->curframe = -1; + cpia->receivesbuf = 0; + + usb_cpia_initstreamcap(cpia->dev, 0, 60); + + cpia_init_isoc(cpia); + + return 0; + +open_err_on2: + kfree (cpia->sbuf[1].data); +open_err_on1: + kfree (cpia->sbuf[0].data); +open_err_on0: + rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE); +open_err_ret: + return -ENOMEM; +} + +static void cpia_close(struct video_device *dev) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + +printk("cpia_close\n"); + + cpia_stop_isoc(cpia); + + usb_cpia_finistreamcap(cpia->dev); + + rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE); + + kfree(cpia->sbuf[2].data); + kfree(cpia->sbuf[1].data); + kfree(cpia->sbuf[0].data); +} + +static int cpia_init_done(struct video_device *dev) +{ + return 0; +} + +static long cpia_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) +{ + return -EINVAL; +} + +#if 0 + if (usb_set_interface(dev, 1, 3)) { + printk("cpia_set_interface error\n"); + return -EINVAL; + } + + if (usb_cpia_grab_frame(dev, 0)) { + printk("cpia_grab_frame error\n"); + return -EINVAL; + } + + if (usb_cpia_upload_frame(dev, 0)) { + printk("cpia_upload_frame error\n"); + return -EINVAL; + } + + buf = cpia->ibuf; + uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), buf, 176*144*4); + + { + printk("header magic: %X\n", (buf[0] << 8) + buf[1]); + + while ((buf[0] != 0x19) || (buf[1] != 0x68)) { + int i; + + printk("resync'ing\n"); + for (i=0;i<(176*144*4)-4;i++, buf++) + if ( + (buf[0] == 0xFF) && + (buf[1] == 0xFF) && + (buf[2] == 0xFF) && + (buf[3] == 0xFF)) { + buf+=4; + i+=4; + break; + } + + memmove(cpia->ibuf, buf, (176*144*4) - i); + uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->ibuf + (176*144*4) - i, i); + buf = cpia->ibuf; + +#if 0 + printk("header magic: %X\n", (buf[0] << 8) + buf[1]); +#endif + } + + printk("size: %d, sample: %d, order: %d\n", buf[16], buf[17], buf[18]); + printk("comp: %d, decimation: %d\n", buf[28], buf[29]); + printk("roi: top left: %d, %d bottom right: %d, %d\n", + buf[26] * 4, buf[24] * 8, + buf[27] * 4, buf[25] * 8); + + printk("vm->frame: %d\n", vm->frame); + + { + int i, i1; + char *b = buf + 64, *fbuf = &cpia->fbuffer[MAX_FRAME_SIZE * (vm->frame & 1)]; + for (i=0;i<144;i++) { +#if 0 + printk("line len: %d\n", (b[1] << 8) + b[0]); +#endif + b += 2; + for (i1=0;i1<176;i1++) { + fbuf[(i * vm->width * 3) + (i1 * 3)] = + fbuf[(i * vm->width * 3) + (i1 * 3) + 1] = + fbuf[(i * vm->width * 3) + (i1 * 3) + 2] = + b[i1 * 2]; +#if 0 + *((short *)&fbuf[(i * vm->width * 2) + (i1 * 2)]) = + ((b[i1 * 2] >> 3) << 11) + ((b[i1 * 2] >> 2) << 6) + (b[i1 * 2] >> 3); +#endif + } + b += (176 * 2) + 1; + } + } + + } + + if (usb_set_interface(dev, 1, 0)) { + printk("cpia_set_interface error\n"); + return -EINVAL; + } +#endif + +static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + + strcpy(b.name, "CPiA USB Camera"); + b.type = VID_TYPE_CAPTURE /* | VID_TYPE_SUBCAPTURE */; + b.channels = 1; + b.audios = 0; + b.maxwidth = 176 /* 352 */; + b.maxheight = 144 /* 240 */; + b.minwidth = 176 /* (Something small?) */; + b.minheight = 144 /* " " */; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.channel != 0) + return -EINVAL; + + v.flags = 0; + v.tuners = 0; + v.type = VIDEO_TYPE_CAMERA; + strcpy(v.name, "Camera"); + + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v != 0) + return -EINVAL; + + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v.tuner) + return -EINVAL; + + strcpy(v.name, "Format"); + + v.rangelow = 0; + v.rangehigh = 0; + v.flags = 0; + v.mode = VIDEO_MODE_AUTO; + + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v.tuner) + return -EINVAL; + + if (v.mode != VIDEO_MODE_AUTO) + return -EINVAL; + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p; + + p.colour = 0x8000; /* Damn British people :) */ + p.hue = 0x8000; + p.brightness = 180 << 8; /* XXX */ + p.contrast = 192 << 8; /* XXX */ + p.whiteness = 105 << 8; /* XXX */ +#if 0 + p.depth = 24; +#endif + p.depth = 16; + p.palette = VIDEO_PALETTE_YUYV; + + if (copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + +printk("Attempting to set palette %d, depth %d\n", p.palette, p.depth); + +#if 0 + if (p.palette != VIDEO_PALETTE_YUYV) + return -EINVAL; + if (p.depth != 16) + return -EINVAL; +#endif + + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + +printk("VIDIOCSWIN\n"); + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + if (vw.flags) + return -EINVAL; + if (vw.clipcount) + return -EINVAL; + if (vw.height != 176) + return -EINVAL; + if (vw.width != 144) + return -EINVAL; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + +printk("VIDIOCGWIN\n"); + vw.x = 0; + vw.y = 0; + vw.width = 176; + vw.height = 144; + vw.chromakey = 0; + vw.flags = 0; + + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + + memset(&vm, 0, sizeof(vm)); + vm.size = MAX_FRAME_SIZE * 2; + vm.frames = 2; + vm.offsets[0] = 0; + vm.offsets[1] = MAX_FRAME_SIZE; + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) + return -EFAULT; + +printk("MCAPTURE\n"); +printk("frame: %d, size: %dx%d, format: %d\n", vm.frame, vm.width, vm.height, vm.format); + + if (vm.format != VIDEO_PALETTE_RGB24) + return -EINVAL; + + if ((vm.frame != 0) && (vm.frame != 1)) + return -EINVAL; + + cpia->frame[vm.frame].width = vm.width; + cpia->frame[vm.frame].height = vm.height; + + /* Mark it as free */ + cpia->frame[vm.frame].state = FRAME_READY; + + return 0; + } + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) + return -EFAULT; + + printk("syncing to frame %d\n", frame); + switch (cpia->frame[frame].state) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + interruptible_sleep_on(&cpia->wq); + case FRAME_DONE: + cpia->frame[frame].state = FRAME_UNUSED; + break; + } + printk("synced to frame %d\n", frame); + return 0; + } + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCGFBUF: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCKEY: + return 0; + case VIDIOCGFREQ: + return -EINVAL; + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + return -EINVAL; + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long cpia_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + int len; + + printk("cpia_read: %ld bytes\n", count); +#if 0 + len = cpia_capture(cpia, buf, count); + + return len; +#endif + return 0; +} + +static int cpia_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + printk("mmap: %ld (%lX) bytes\n", size, size); + if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + +#if 0 + if (!cpia->fbuffer) { + if ((cpia->fbuffer = rvmalloc(2 * MAX_FRAME_SIZE)) == NULL) + return -EINVAL; + } +#endif + + pos = (unsigned long)cpia->fbuf; + while (size > 0) + { + page = kvirt_to_phys(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start+=PAGE_SIZE; + pos+=PAGE_SIZE; + if (size > PAGE_SIZE) + size-=PAGE_SIZE; + else + size=0; + } + + return 0; +} + +static struct video_device cpia_template = { + "CPiA USB Camera", + VID_TYPE_CAPTURE, + VID_HARDWARE_CPIA, + cpia_open, + cpia_close, + cpia_read, + cpia_write, + NULL, + cpia_ioctl, + cpia_mmap, + cpia_init_done, + NULL, + 0, + 0 +}; + +static void usb_cpia_configure(struct usb_cpia *cpia) +{ + struct usb_device *dev = cpia->dev; + unsigned char version[4]; + unsigned char pnpid[6]; + unsigned char camerastat[8]; + unsigned char *buf; + + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + printk (KERN_INFO " Failed usb_set_configuration: CPIA\n"); + return; + } + + if (usb_cpia_get_version(dev, version)) { + printk("cpia_get_version error\n"); + return; + } + + printk("cpia: Firmware v%d.%d, VC Hardware v%d.%d\n", + version[0], version[1], version[2], version[3]); + + if (usb_cpia_get_pnp_id(dev, pnpid)) { + printk("cpia_get_pnp_id error\n"); + return; + } + + printk("cpia: PnP Id: Vendor: %X, Product: %X, Revision: %X\n", + (pnpid[1] << 8) + pnpid[0], (pnpid[3] << 8) + pnpid[2], + (pnpid[5] << 8) + pnpid[4]); + + memcpy(&cpia->vdev, &cpia_template, sizeof(cpia_template)); + + init_waitqueue_head(&cpia->wq); + + if (video_register_device(&cpia->vdev, VFL_TYPE_GRABBER) == -1) { + printk("video_register_device failed\n"); + return; + } + + if (usb_cpia_goto_hi_power(dev)) { + printk("cpia_goto_hi_power error\n"); + return; + } + + if (usb_cpia_get_vp_version(dev, version)) { + printk("cpia_get_vp_version error\n"); + return; + } + + printk("cpia: VP v%d rev %d\n", version[0], version[1]); + printk("cpia: Camera Head ID %04X\n", (version[3] << 8) + version[2]); + + /* Turn off continuous grab */ + if (usb_cpia_set_grab_mode(dev, 1)) { + printk("cpia_set_grab_mode error\n"); + return; + } + + /* Set up the sensor to be 30fps */ + if (usb_cpia_set_sensor_fps(dev, 1, 0)) { + printk("cpia_set_sensor_fps error\n"); + return; + } + + /* Set video into QCIF mode, and order into YUYV mode */ + if (usb_cpia_set_format(dev, CPIA_QCIF, 1, CPIA_YUYV)) { + printk("cpia_set_format error\n"); + return; + } + + /* Turn off compression */ + if (usb_cpia_set_compression(dev, 0, 0)) { + printk("cpia_set_compression error\n"); + return; + } + +#if 0 + if (usb_cpia_grab_frame(dev, 0)) { + printk("cpia_grab_frame error\n"); + return; + } + + if (usb_cpia_upload_frame(dev, 1)) { + printk("cpia_upload_frame error\n"); + return; + } + + buf = (void *)__get_free_page(GFP_KERNEL); + + { + int i; + for (i=0;i<448;i++) + buf[i]=0; + } + uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), buf, 448); + + { + int i; + for (i=0;i<448;i++) { + printk("%02X ", buf[i]); + if ((i % 16) == 15) + printk("\n"); + } + printk("\n"); + } + + free_page((unsigned long)buf); +#endif +} + +static int cpia_probe(struct usb_device *dev) +{ + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_cpia *cpia; + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -1; + +#if 0 + /* We don't handle multi-interface hubs */ + if (dev->config[0].bNumInterfaces != 1) + return -1; +#endif + + interface = &dev->config[0].altsetting[0].interface[0]; + + /* Is it a CPiA? */ +/* +Apr 24 17:49:04 bjorn kernel: Vendor: 0545 +Apr 24 17:49:04 bjorn kernel: Product: 8080 +*/ +/* + if (dev->descriptor.idVendor != 0x0545) + return -1; + if (dev->descriptor.idProduct != 0x8080) + return -1; + if (interface->bInterfaceClass != 0xFF) + return -1; + if (interface->bInterfaceSubClass != 0xFF) + return -1; +*/ + if (dev->descriptor.idVendor != 0x0553) + return -1; + if (dev->descriptor.idProduct != 0x0002) + return -1; + if (interface->bInterfaceClass != 0xFF) + return -1; + if (interface->bInterfaceSubClass != 0x00) + return -1; + +#if 0 + /* Multiple endpoints? What kind of mutant ninja-hub is this? */ + if (interface->bNumEndpoints != 1) + return -1; + + endpoint = &interface->endpoint[0]; + + /* Output endpoint? Curiousier and curiousier.. */ + if (!(endpoint->bEndpointAddress & 0x80)) + return -1; + + /* If it's not an interrupt endpoint, we'd better punt! */ + if ((endpoint->bmAttributes & 3) != 3) + return -1; +#endif + + /* We found a CPiA */ + printk("USB CPiA camera found\n"); + + if ((cpia = kmalloc(sizeof(*cpia), GFP_KERNEL)) == NULL) { + printk("couldn't kmalloc cpia struct\n"); + return -1; + } + + memset(cpia, 0, sizeof(*cpia)); + + dev->private = cpia; + cpia->dev = dev; + + usb_cpia_configure(cpia); + +#if 0 + usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), pport_irq, endpoint->bInterval, pport); +#endif + + return 0; +} + +static void cpia_disconnect(struct usb_device *dev) +{ + struct usb_cpia *cpia = dev->private; + + video_unregister_device(&cpia->vdev); + + /* Free the memory */ + kfree(cpia); +} + +static struct usb_driver cpia_driver = { + "cpia", + cpia_probe, + cpia_disconnect, + { NULL, NULL } +}; + +/* + * This should be a separate module. + */ +int usb_cpia_init(void) +{ + usb_register(&cpia_driver); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return usb_cpia_init(); +} +void cleanup_module(void) +{ +} +#endif + diff --git a/drivers/usb/cpia.h b/drivers/usb/cpia.h new file mode 100644 index 000000000000..51ff43e79404 --- /dev/null +++ b/drivers/usb/cpia.h @@ -0,0 +1,139 @@ +#ifndef __LINUX_CPIA_H +#define __LINUX_CPIA_H + +#include + +#define USB_REQ_CPIA_GET_VERSION 0x01 +#define USB_REQ_CPIA_GET_PNP_ID 0x02 +#define USB_REQ_CPIA_GET_CAMERA_STATUS 0x03 +#define USB_REQ_CPIA_GOTO_HI_POWER 0x04 +#define USB_REQ_CPIA_GOTO_LO_POWER 0x05 +/* No 0x06 */ +#define USB_REQ_CPIA_GOTO_SUSPEND 0x07 +#define USB_REQ_CPIA_GOTO_PASS_THROUGH 0x08 +/* No 0x09 */ +#define USB_REQ_CPIA_MODIFY_CAMERA_STATUS 0x0A + +#define USB_REQ_CPIA_READ_VC_REGS 0x21 +#define USB_REQ_CPIA_WRITE_BC_REG 0x22 +#define USB_REQ_CPIA_READ_MC_PORTS 0x23 +#define USB_REQ_CPIA_WRITE_MC_PORT 0x24 +#define USB_REQ_CPIA_SET_BAUD_RATE 0x25 +#define USB_REQ_CPIA_SET_ECP_TIMING 0x26 +#define USB_REQ_CPIA_READ_IDATA 0x27 +#define USB_REQ_CPIA_WRITE_IDATA 0x28 +#define USB_REQ_CPIA_GENERIC_CALL 0x29 +#define USB_REQ_CPIA_I2CSTART 0x2A +#define USB_REQ_CPIA_I2CSTOP 0x2B +#define USB_REQ_CPIA_I2CWRITE 0x2C +#define USB_REQ_CPIA_I2CREAD 0x2D + +#define USB_REQ_CPIA_GET_VP_VERSION 0xA1 +#define USB_REQ_CPIA_SET_COLOUR_PARAMS 0xA3 +#define USB_REQ_CPIA_SET_EXPOSURE 0xA4 +/* No 0xA5 */ +#define USB_REQ_CPIA_SET_COLOUR_BALANCE 0xA6 +#define USB_REQ_CPIA_SET_SENSOR_FPS 0xA7 +#define USB_REQ_CPIA_SET_VP_DEFAULTS 0xA8 +#define USB_REQ_CPIA_SET_APCOR 0xA9 +#define USB_REQ_CPIA_SET_FLICKER_CTRL 0xAA +#define USB_REQ_CPIA_SET_VL_OFFSET 0xAB + +#define USB_REQ_CPIA_GET_COLOUR_PARAMETERS 0xB0 +#define USB_REQ_CPIA_GET_COLOUR_BALANCE 0xB1 +#define USB_REQ_CPIA_GET_EXPOSURE 0xB2 +#define USB_REQ_CPIA_SET_SENSOR_MATRIX 0xB3 + +#define USB_REQ_CPIA_COLOUR_BARS 0xBD +#define USB_REQ_CPIA_READ_VP_REGS 0xBE +#define USB_REQ_CPIA_WRITE_VP_REGS 0xBF + +#define USB_REQ_CPIA_GRAB_FRAME 0xC1 +#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 +#define USB_REQ_CPIA_SET_GRAB_MODE 0xC3 +#define USB_REQ_CPIA_INIT_STREAM_CAP 0xC4 +#define USB_REQ_CPIA_FINI_STREAM_CAP 0xC5 +#define USB_REQ_CPIA_START_STREAM_CAP 0xC6 +#define USB_REQ_CPIA_END_STREAM_CAP 0xC7 +#define USB_REQ_CPIA_SET_FORMAT 0xC8 +#define USB_REQ_CPIA_SET_ROI 0xC9 +#define USB_REQ_CPIA_SET_COMPRESSION 0xCA +#define USB_REQ_CPIA_SET_COMPRESSION_TARGET 0xCB +#define USB_REQ_CPIA_SET_YUV_THRESH 0xCC +#define USB_REQ_CPIA_SET_COMPRESSION_PARAMS 0xCD +#define USB_REQ_CPIA_DISCARD_FRAME 0xCE + +#define USB_REQ_CPIA_OUTPUT_RS232 0xE1 +#define USB_REQ_CPIA_ABORT_PROCESS 0xE4 +#define USB_REQ_CPIA_SET_DRAM_PAGE 0xE5 +#define USB_REQ_CPIA_START_DRAM_UPLOAD 0xE6 +#define USB_REQ_CPIA_START_DUMMY_STREAM 0xE8 +#define USB_REQ_CPIA_ABORT_STREAM 0xE9 +#define USB_REQ_CPIA_DOWNLOAD_DRAM 0xEA +/* #define USB_REQ_CPIA_NULL_CMD 0x?? */ + +#define CPIA_QCIF 0 +#define CPIA_CIF 1 + +#define CPIA_YUYV 0 +#define CPIA_UYVY 1 + +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) + +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +enum { + STATE_SCANNING, /* Scanning for start */ + STATE_HEADER, /* Parsing header */ + STATE_LINES, /* Parsing lines */ +}; + +struct usb_device; + +struct cpia_sbuf { + char *data; + int len; + void *isodesc; +}; + +enum { + FRAME_READY, /* Ready to grab into */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_UNUSED, /* Unused (no MCAPTURE) */ +}; + +struct cpia_frame { + char *data; + int width; + int height; + int state; +}; + +struct usb_cpia { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + int streaming; + + char *fbuf; /* Videodev buffer area */ + + int curframe; + struct cpia_frame frame[2]; /* Double buffering */ + + int receivesbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[3]; /* Triple buffering */ + + int state; /* Current scanning state */ + int curline; + + char scratch[SCRATCH_BUF_SIZE]; + int scratchlen; + + wait_queue_head_t wq; +}; + +#endif + diff --git a/drivers/usb/ezusb.c b/drivers/usb/ezusb.c new file mode 100644 index 000000000000..90e45b54e5b2 --- /dev/null +++ b/drivers/usb/ezusb.c @@ -0,0 +1,472 @@ +/*****************************************************************************/ + +/* + * ezusb.c -- Firmware download miscdevice for Anchorchips EZUSB microcontrollers. + * + * Copyright (C) 1999 + * 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. + * + * History: + * 0.1 26.05.99 Created + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include + +#include "usb.h" +#include "ezusb.h" + +/* --------------------------------------------------------------------- */ + +#define NREZUSB 1 + +static struct ezusb { + struct semaphore mutex; + struct usb_device *usbdev; + unsigned int irqep; + unsigned int intlen; + unsigned char intdata[64]; +} ezusb[NREZUSB]; + +/* --------------------------------------------------------------------- */ + +static int ezusb_irq(int state, void *__buffer, int len, void *dev_id) +{ + struct ezusb *ez = (struct ezusb *)dev_id; + + if (len > sizeof(ez->intdata)) + len = sizeof(ez->intdata); + ez->intlen = len; + memcpy(ez->intdata, __buffer, len); + return 1; +} + +/* --------------------------------------------------------------------- */ + +static loff_t ezusb_llseek(struct file *file, loff_t offset, int origin) +{ + struct ezusb *ez = (struct ezusb *)file->private_data; + + switch(origin) { + case 1: + offset += file->f_pos; + break; + case 2: + offset += 0x10000; + break; + } + if (offset < 0 || offset >= 0x10000) + return -EINVAL; + return (file->f_pos = offset); +} + +static ssize_t ezusb_read(struct file *file, char *buf, size_t sz, loff_t *ppos) +{ + struct ezusb *ez = (struct ezusb *)file->private_data; + unsigned pos = *ppos; + unsigned ret = 0; + unsigned len; + unsigned char b[64]; + devrequest dr; + int i; + + if (*ppos < 0 || *ppos >= 0x10000) + return -EINVAL; + down(&ez->mutex); + if (!ez->usbdev) { + up(&ez->mutex); + return -EIO; + } + while (sz > 0 && pos < 0x10000) { + len = sz; + if (len > sizeof(b)) + len = sizeof(b); + if (pos + len > 0x10000) + len = 0x10000 - pos; + dr.requesttype = 0xc0; + dr.request = 0xa0; + dr.value = pos; + dr.index = 0; + dr.length = len; + i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, 0), &dr, b, len); + if (i) { + up(&ez->mutex); + printk(KERN_WARNING "ezusb: upload failed pos %u len %u ret %d\n", dr.value, dr.length, i); + *ppos = pos; + if (ret) + return ret; + return -ENXIO; + } + if (copy_to_user(buf, b, len)) { + up(&ez->mutex); + *ppos = pos; + if (ret) + return ret; + return -EFAULT; + } + pos += len; + buf += len; + sz -= len; + ret += len; + } + up(&ez->mutex); + *ppos = pos; + return ret; +} + +static ssize_t ezusb_write(struct file *file, const char *buf, size_t sz, loff_t *ppos) +{ + struct ezusb *ez = (struct ezusb *)file->private_data; + unsigned pos = *ppos; + unsigned ret = 0; + unsigned len; + unsigned char b[64]; + devrequest dr; + int i; + + if (*ppos < 0 || *ppos >= 0x10000) + return -EINVAL; + down(&ez->mutex); + if (!ez->usbdev) { + up(&ez->mutex); + return -EIO; + } + while (sz > 0 && pos < 0x10000) { + len = sz; + if (len > sizeof(b)) + len = sizeof(b); + if (pos + len > 0x10000) + len = 0x10000 - pos; + if (copy_from_user(b, buf, len)) { + up(&ez->mutex); + *ppos = pos; + if (ret) + return ret; + return -EFAULT; + } + dr.requesttype = 0x40; + dr.request = 0xa0; + dr.value = pos; + dr.index = 0; + dr.length = len; + i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), &dr, b, len); + if (i) { + up(&ez->mutex); + printk(KERN_WARNING "ezusb: download failed pos %u len %u ret %d\n", dr.value, dr.length, i); + *ppos = pos; + if (ret) + return ret; + return -ENXIO; + } + pos += len; + buf += len; + sz -= len; + ret += len; + } + up(&ez->mutex); + *ppos = pos; + return ret; +} + +static int ezusb_open(struct inode *inode, struct file *file) +{ + struct ezusb *ez = &ezusb[0]; + + down(&ez->mutex); + while (!ez->usbdev) { + up(&ez->mutex); + if (!(file->f_flags & O_NONBLOCK)) { + return -EIO; + } + schedule_timeout(HZ/2); + if (signal_pending(current)) + return -EAGAIN; + down(&ez->mutex); + } + up(&ez->mutex); + file->f_pos = 0; + file->private_data = ez; + return 0; +} + +static int ezusb_release(struct inode *inode, struct file *file) +{ + struct ezusb *ez = (struct ezusb *)file->private_data; + return 0; +} + +static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ezusb *ez = (struct ezusb *)file->private_data; + struct ezusb_ctrltransfer ctrl; + struct ezusb_bulktransfer bulk; + unsigned int len1, ep; + unsigned long len2; + unsigned int irqep; + unsigned char tbuf[1024]; + int i; + + switch (cmd) { + case EZUSB_CONTROL: + copy_from_user_ret(&ctrl, (void *)arg, sizeof(ctrl), -EFAULT); + if (ctrl.dlen > sizeof(tbuf) || ctrl.dlen > 1024) + return -EINVAL; + if (ctrl.requesttype & 0x80) { + if (ctrl.dlen && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.dlen)) + return -EINVAL; + down(&ez->mutex); + if (!ez->usbdev) { + up(&ez->mutex); + return -EIO; + } + i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, 0), + (devrequest *)&ctrl, tbuf, ctrl.dlen); + up(&ez->mutex); + if (!i && ctrl.dlen) { + copy_to_user_ret(ctrl.data, tbuf, ctrl.dlen, -EFAULT); + } + } else { + if (ctrl.dlen) { + copy_from_user_ret(tbuf, ctrl.data, ctrl.dlen, -EFAULT); + } + down(&ez->mutex); + if (!ez->usbdev) { + up(&ez->mutex); + return -EIO; + } + i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), + (devrequest *)&ctrl, tbuf, ctrl.dlen); + up(&ez->mutex); + } + if (i) { + printk(KERN_WARNING "ezusb: EZUSB_CONTROL failed rqt %u rq %u len %u ret %d\n", + ctrl.requesttype, ctrl.request, ctrl.length, i); + return -ENXIO; + } + return 0; + + case EZUSB_INTERRUPT: + get_user_ret(irqep, (unsigned int *)arg, -EFAULT); + if (irqep != ez->irqep) { + if (ez->irqep) + return -EIO; + ez->irqep = irqep; + usb_request_irq(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, ez->irqep), + ezusb_irq, 2 /* interval */, ez); + ez->intlen = 0; + return -EAGAIN; + } + copy_to_user_ret((&((struct ezusb_interrupttransfer *)0)->data) + arg, + ez->intdata, 64, -EFAULT); + return ez->intlen; + + case EZUSB_BULK: + copy_from_user_ret(&bulk, (void *)arg, sizeof(bulk), -EFAULT); + len1 = bulk.len; + if (len1 > sizeof(tbuf)) + len1 = sizeof(tbuf); + if (bulk.ep & 0x80) { + if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) + return -EINVAL; + down(&ez->mutex); + if (!ez->usbdev) { + up(&ez->mutex); + return -EIO; + } + i = ez->usbdev->bus->op->bulk_msg(ez->usbdev, usb_rcvbulkpipe(ez->usbdev, bulk.ep & 0x7f), + tbuf, len1, &len2); + up(&ez->mutex); + if (!i && len2) { + copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT); + } + } else { + if (len1) { + copy_from_user_ret(tbuf, bulk.data, len1, -EFAULT); + } + down(&ez->mutex); + if (!ez->usbdev) { + up(&ez->mutex); + return -EIO; + } + i = ez->usbdev->bus->op->bulk_msg(ez->usbdev, usb_sndbulkpipe(ez->usbdev, bulk.ep & 0x7f), + tbuf, len1, &len2); + up(&ez->mutex); + } + if (i) { + printk(KERN_WARNING "ezusb: EZUSB_BULK failed ep 0x%x len %u ret %d\n", + bulk.ep, bulk.len, i); + return -ENXIO; + } + return len2; + + case EZUSB_RESETEP: + get_user_ret(ep, (unsigned int *)arg, -EFAULT); + if ((ep & ~0x80) >= 16) + return -EINVAL; + usb_settoggle(ez->usbdev, ep & 0xf, !(ep & 0x80), 0); + return 0; + } + return -ENOIOCTLCMD; +} + +static struct file_operations ezusb_fops = { + ezusb_llseek, + ezusb_read, + ezusb_write, + NULL, /* readdir */ + NULL, /* poll */ + ezusb_ioctl, + NULL, /* mmap */ + ezusb_open, + NULL, /* flush */ + ezusb_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +static struct miscdevice ezusb_misc = { + 192, "ezusb", &ezusb_fops +}; + +/* --------------------------------------------------------------------- */ + +static int ezusb_probe(struct usb_device *usbdev) +{ + struct ezusb *ez = &ezusb[0]; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + +#undef KERN_DEBUG +#define KERN_DEBUG "" + printk(KERN_DEBUG "ezusb: probe: vendor id 0x%x, device id 0x%x\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); + + /* the 1234:5678 is just a self assigned test ID */ + if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) && + (usbdev->descriptor.idVendor != 0x1234 || usbdev->descriptor.idProduct != 0x5678)) + return -1; + + /* We don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return -1; + + /* We don't handle multiple interfaces */ + if (usbdev->config[0].bNumInterfaces != 1) + return -1; + + down(&ez->mutex); + if (ez->usbdev) { + up(&ez->mutex); + printk(KERN_INFO "ezusb: device already used\n"); + return -1; + } + ez->usbdev = usbdev; + usbdev->private = ez; + if (usb_set_configuration(usbdev, usbdev->config[0].bConfigurationValue)) { + printk(KERN_ERR "ezusb: set_configuration failed\n"); + goto err; + } + interface = &usbdev->config[0].altsetting[1].interface[0]; + if (usb_set_interface(usbdev, 0, 1)) { + printk(KERN_ERR "ezusb: set_interface failed\n"); + goto err; + } + up(&ez->mutex); + MOD_INC_USE_COUNT; + return 0; + + err: + up(&ez->mutex); + ez->usbdev = NULL; + usbdev->private = NULL; + return -1; +} + +static void ezusb_disconnect(struct usb_device *usbdev) +{ + struct ezusb *ez = (struct ezusb *)usbdev->private; + + down(&ez->mutex); + ez->usbdev = NULL; + up(&ez->mutex); + usbdev->private = NULL; + MOD_DEC_USE_COUNT; +} + +static struct usb_driver ezusb_driver = { + "ezusb", + ezusb_probe, + ezusb_disconnect, + { NULL, NULL } +}; + +/* --------------------------------------------------------------------- */ + +int ezusb_init(void) +{ + unsigned u; + + /* initialize struct */ + for (u = 0; u < NREZUSB; u++) { + ezusb[u].mutex = MUTEX; + ezusb[u].usbdev = NULL; + ezusb[u].irqep = 0; + } + /* register misc device */ + if (misc_register(&ezusb_misc)) { + printk(KERN_WARNING "ezusb: cannot register minor %d\n", ezusb_misc.minor); + return -1; + } + usb_register(&ezusb_driver); + printk(KERN_INFO "ezusb: Anchorchip firmware download driver registered\n"); + return 0; +} + +void ezusb_cleanup(void) +{ + usb_deregister(&ezusb_driver); + misc_deregister(&ezusb_misc); +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +int minor = 192; + +int init_module(void) +{ + ezusb_misc.minor = minor; + return ezusb_init(); +} + +void cleanup_module(void) +{ + ezusb_cleanup(); +} + +#endif + +/* --------------------------------------------------------------------- */ diff --git a/drivers/usb/ezusb.h b/drivers/usb/ezusb.h new file mode 100644 index 000000000000..8ddaad1272b0 --- /dev/null +++ b/drivers/usb/ezusb.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ + +/* + * ezusb.h -- Firmware download miscdevice for Anchorchips EZUSB microcontrollers. + * + * Copyright (C) 1999 + * 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. + * + */ + +/*****************************************************************************/ + +#ifndef _LINUX_EZUSB_H +#define _LINUX_EZUSB_H + +#include + +/* --------------------------------------------------------------------- */ + +struct ezusb_ctrltransfer { + /* keep in sync with usb.h:devrequest */ + unsigned char requesttype; + unsigned char request; + unsigned short value; + unsigned short index; + unsigned short length; + /* pointer to data */ + unsigned dlen; + void *data; +}; + +#define EZUSB_CONTROL _IOWR('E', 0, struct ezusb_ctrltransfer) + +struct ezusb_interrupttransfer { + unsigned int ep; + unsigned char data[64]; +}; + +#define EZUSB_INTERRUPT _IOWR('E', 1, struct ezusb_interrupttransfer) + +struct ezusb_bulktransfer { + unsigned int ep; + unsigned int len; + void *data; +}; + +#define EZUSB_BULK _IOWR('E', 2, struct ezusb_bulktransfer) + +#define EZUSB_RESETEP _IOR('E', 3, unsigned int) + + +/* --------------------------------------------------------------------- */ +#endif /* _LINUX_EZUSB_H */ diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 16789b9445c0..a0874889fad3 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -1,12 +1,9 @@ /* * USB hub driver. * - * This is horrible, it knows about the UHCI driver - * internals, but it's just meant as a rough example, - * let's do the virtualization later when this works. - * * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Gregory P. Smith */ #include @@ -14,22 +11,24 @@ #include #include #include +#include #include #include "usb.h" -#include "uhci.h" #include "hub.h" -extern struct usb_operations uhci_device_operations; - /* Wakes up khubd */ -static struct wait_queue *usb_hub_wait = NULL; +static DECLARE_WAIT_QUEUE_HEAD(usb_hub_wait); static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t hub_list_lock = SPIN_LOCK_UNLOCKED; /* List of hubs needing servicing */ static struct list_head hub_event_list; +/* List containing all of the hubs (for cleanup) */ +static struct list_head all_hubs_list; + /* PID of khubd */ static int khubd_pid = 0; @@ -38,7 +37,7 @@ static int khubd_pid = 0; * the low-level driver that it wants to be re-activated, * or zero to say "I'm done". */ -static int hub_irq(int status, void *__buffer, void *dev_id) +static int hub_irq(int status, void *__buffer, int len, void *dev_id) { struct usb_hub *hub = dev_id; unsigned long flags; @@ -69,59 +68,59 @@ static void usb_hub_configure(struct usb_hub *hub) return; hub->nports = dev->maxchild = hubdescriptor[2]; - printk("hub: %d-port%s detected\n", hub->nports, + printk(KERN_DEBUG "hub: %d-port%s detected\n", hub->nports, (hub->nports == 1) ? "" : "s"); charac = (hubdescriptor[4] << 8) + hubdescriptor[3]; switch (charac & HUB_CHAR_LPSM) { case 0x00: - printk("hub: ganged power switching\n"); + printk(KERN_DEBUG "hub: ganged power switching\n"); break; case 0x01: - printk("hub: individual port power switching\n"); + printk(KERN_DEBUG "hub: individual port power switching\n"); break; case 0x02: case 0x03: - printk("hub: unknown reserved power switching mode\n"); + printk(KERN_DEBUG "hub: unknown reserved power switching mode\n"); break; } if (charac & HUB_CHAR_COMPOUND) - printk("hub: part of a compound device\n"); + printk(KERN_DEBUG "hub: part of a compound device\n"); else - printk("hub: standalone hub\n"); + printk(KERN_DEBUG "hub: standalone hub\n"); switch (charac & HUB_CHAR_OCPM) { case 0x00: - printk("hub: global over current protection\n"); + printk(KERN_DEBUG "hub: global over current protection\n"); break; case 0x08: - printk("hub: individual port over current protection\n"); + printk(KERN_DEBUG "hub: individual port over current protection\n"); break; case 0x10: case 0x18: - printk("hub: no over current protection\n"); + printk(KERN_DEBUG "hub: no over current protection\n"); break; } - printk("hub: power on to power good time: %dms\n", + printk(KERN_DEBUG "hub: power on to power good time: %dms\n", hubdescriptor[5] * 2); - printk("hub: hub controller current requirement: %dmA\n", + printk(KERN_DEBUG "hub: hub controller current requirement: %dmA\n", hubdescriptor[6]); for (i = 0; i < dev->maxchild; i++) - printk("hub: port %d is%s removable\n", i + 1, + printk(KERN_DEBUG "hub: port %d is%s removable\n", i + 1, hubdescriptor[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8)) ? " not" : ""); if (usb_get_hub_status(dev, buf)) return; - printk("hub: local power source is %s\n", + printk(KERN_DEBUG "hub: local power source is %s\n", (buf[0] & 1) ? "lost (inactive)" : "good"); - printk("hub: %sover current condition exists\n", + printk(KERN_DEBUG "hub: %sover current condition exists\n", (buf[0] & 2) ? "" : "no "); #if 0 @@ -134,18 +133,19 @@ static void usb_hub_configure(struct usb_hub *hub) portstat = (portstatus[1] << 8) + portstatus[0]; portchange = (portstatus[3] << 8) + portstatus[2]; - printk("hub: port %d status\n", i + 1); - printk("hub: %sdevice present\n", (portstat & 1) ? "" : "no "); - printk("hub: %s\n", (portstat & 2) ? "enabled" : "disabled"); - printk("hub: %ssuspended\n", (portstat & 4) ? "" : "not "); - printk("hub: %sover current\n", (portstat & 8) ? "" : "not "); - printk("hub: has %spower\n", (portstat & 0x100) ? "" : "no "); - printk("hub: %s speed\n", (portstat & 0x200) ? "low" : "full"); + printk(KERN_DEBUG "hub: port %d status\n", i + 1); + printk(KERN_DEBUG "hub: %sdevice present\n", (portstat & 1) ? "" : "no "); + printk(KERN_DEBUG "hub: %s\n", (portstat & 2) ? "enabled" : "disabled"); + printk(KERN_DEBUG "hub: %ssuspended\n", (portstat & 4) ? "" : "not "); + printk(KERN_DEBUG "hub: %sover current\n", (portstat & 8) ? "" : "not "); + printk(KERN_DEBUG "hub: has %spower\n", (portstat & 0x100) ? "" : "no "); + printk(KERN_DEBUG "hub: %s speed\n", (portstat & 0x200) ? "low" : "full"); } #endif /* Enable power to the ports */ - printk("enabling power on all ports\n"); + printk(KERN_INFO "USB hub %d: enabling power on all ports\n", + dev->devnum); for (i = 0; i < hub->nports; i++) usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); } @@ -155,6 +155,7 @@ static int hub_probe(struct usb_device *dev) struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_hub *hub; + unsigned long flags; /* We don't handle multi-config hubs */ if (dev->descriptor.bNumConfigurations != 1) @@ -164,7 +165,7 @@ static int hub_probe(struct usb_device *dev) if (dev->config[0].bNumInterfaces != 1) return -1; - interface = &dev->config[0].interface[0]; + interface = &dev->config[0].altsetting[0].interface[0]; /* Is it a hub? */ if (interface->bInterfaceClass != 9) @@ -188,10 +189,10 @@ static int hub_probe(struct usb_device *dev) return -1; /* We found a hub */ - printk("USB hub found\n"); + printk(KERN_INFO "USB hub found\n"); if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) { - printk("couldn't kmalloc hub struct\n"); + printk(KERN_ERR "couldn't kmalloc hub struct\n"); return -1; } @@ -202,9 +203,15 @@ static int hub_probe(struct usb_device *dev) INIT_LIST_HEAD(&hub->event_list); hub->dev = dev; + /* Record the new hub's existence */ + spin_lock_irqsave(&hub_list_lock, flags); + INIT_LIST_HEAD(&hub->hub_list); + list_add(&hub->hub_list, &all_hubs_list); + spin_unlock_irqrestore(&hub_list_lock, flags); + usb_hub_configure(hub); - usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), hub_irq, endpoint->bInterval, hub); + hub->irq_handle = usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), hub_irq, endpoint->bInterval, hub); /* Wake up khubd */ wake_up(&usb_hub_wait); @@ -222,9 +229,14 @@ static void hub_disconnect(struct usb_device *dev) /* Delete it and then reset it */ list_del(&hub->event_list); INIT_LIST_HEAD(&hub->event_list); + list_del(&hub->hub_list); + INIT_LIST_HEAD(&hub->hub_list); spin_unlock_irqrestore(&hub_event_lock, flags); + usb_release_irq(hub->dev, hub->irq_handle); + hub->irq_handle = NULL; + /* Free the memory */ kfree(hub); } @@ -242,12 +254,12 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) wait_ms(50); /* FIXME: This is from the *BSD stack, thanks! :) */ if (usb_get_port_status(hub, port + 1, buf)) { - printk("get_port_status failed\n"); + printk(KERN_ERR "get_port_status failed\n"); return; } - portstatus = *((unsigned short *)buf + 0); - portchange = *((unsigned short *)buf + 1); + portstatus = le16_to_cpup((unsigned short *)buf + 0); + portchange = le16_to_cpup((unsigned short *)buf + 1); if ((!(portstatus & USB_PORT_STAT_CONNECTION)) && (!(portstatus & USB_PORT_STAT_ENABLE))) { @@ -258,7 +270,7 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) usb = hub->bus->op->allocate(hub); if (!usb) { - printk("couldn't allocate usb_device\n"); + printk(KERN_ERR "couldn't allocate usb_device\n"); return; } @@ -295,15 +307,15 @@ static void usb_hub_events(void) for (i = 0; i < hub->nports; i++) { if (usb_get_port_status(dev, i + 1, buf)) { - printk("get_port_status failed\n"); + printk(KERN_ERR "get_port_status failed\n"); continue; } - portstatus = *((unsigned short *)buf + 0); - portchange = *((unsigned short *)buf + 1); + portstatus = le16_to_cpup((unsigned short *)buf + 0); + portchange = le16_to_cpup((unsigned short *)buf + 1); if (portchange & USB_PORT_STAT_C_CONNECTION) { - printk("hub: port %d connection change\n", i + 1); + printk(KERN_DEBUG "hub: port %d connection change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_CONNECTION); @@ -312,19 +324,19 @@ static void usb_hub_events(void) } if (portchange & USB_PORT_STAT_C_ENABLE) { - printk("hub: port %d enable change\n", i + 1); + printk(KERN_DEBUG "hub: port %d enable change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); } if (portchange & USB_PORT_STAT_C_SUSPEND) - printk("hub: port %d suspend change\n", i + 1); + printk(KERN_DEBUG "hub: port %d suspend change\n", i + 1); if (portchange & USB_PORT_STAT_C_OVERCURRENT) - printk("hub: port %d over-current change\n", i + 1); + printk(KERN_DEBUG "hub: port %d over-current change\n", i + 1); if (portchange & USB_PORT_STAT_C_RESET) { - printk("hub: port %d reset change\n", i + 1); + printk(KERN_DEBUG "hub: port %d reset change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } @@ -339,13 +351,13 @@ static void usb_hub_events(void) portstatus = (buf[1] << 8) + buf[0]; portchange = (buf[3] << 8) + buf[2]; - printk("hub: port %d status\n", i + 1); - printk("hub: %sdevice present\n", (portstatus & 1) ? "" : "no "); - printk("hub: %s\n", (portstatus & 2) ? "enabled" : "disabled"); - printk("hub: %ssuspended\n", (portstatus & 4) ? "" : "not "); - printk("hub: %sover current\n", (portstatus & 8) ? "" : "not "); - printk("hub: has %spower\n", (portstatus & 0x100) ? "" : "no "); - printk("hub: %s speed\n", (portstatus & 0x200) ? "low" : "full"); + printk(KERN_DEBUG "hub: port %d status\n", i + 1); + printk(KERN_DEBUG "hub: %sdevice present\n", (portstatus & 1) ? "" : "no "); + printk(KERN_DEBUG "hub: %s\n", (portstatus & 2) ? "enabled" : "disabled"); + printk(KERN_DEBUG "hub: %ssuspended\n", (portstatus & 4) ? "" : "not "); + printk(KERN_DEBUG "hub: %sover current\n", (portstatus & 8) ? "" : "not "); + printk(KERN_DEBUG "hub: has %spower\n", (portstatus & 0x100) ? "" : "no "); + printk(KERN_DEBUG "hub: %s speed\n", (portstatus & 0x200) ? "low" : "full"); #endif } tmp = next; @@ -359,16 +371,19 @@ static void usb_hub_events(void) static int usb_hub_thread(void *__hub) { + MOD_INC_USE_COUNT; + + printk(KERN_INFO "USB hub driver registered\n"); + lock_kernel(); /* * This thread doesn't need any user-level access, * so get rid of all our resources */ - printk("usb_hub_thread at %p\n", &usb_hub_thread); exit_mm(current); exit_files(current); - exit_fs(current); + /* exit_fs(current); */ /* Setup a nice name */ strcpy(current->comm, "khubd"); @@ -379,7 +394,9 @@ static int usb_hub_thread(void *__hub) usb_hub_events(); } while (!signal_pending(current)); - printk("usb_hub_thread exiting\n"); + MOD_DEC_USE_COUNT; + + printk(KERN_DEBUG "usb_hub_thread exiting\n"); return 0; } @@ -394,11 +411,12 @@ static struct usb_driver hub_driver = { /* * This should be a separate module. */ -int hub_init(void) +int usb_hub_init(void) { int pid; INIT_LIST_HEAD(&hub_event_list); + INIT_LIST_HEAD(&all_hubs_list); usb_register(&hub_driver); pid = kernel_thread(usb_hub_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); @@ -413,10 +431,24 @@ int hub_init(void) return 0; } -void hub_cleanup(void) +void usb_hub_cleanup(void) { - if (khubd_pid >= 0) - kill_proc(khubd_pid, SIGINT, 1); - + /* + * Hub resources are freed for us by usb_deregister. It + * usb_driver_purge on every device which in turn calls that + * devices disconnect function if it is using this driver. + * The hub_disconnect function takes care of releasing the + * individual hub resources. -greg + */ usb_deregister(&hub_driver); +} /* usb_hub_cleanup() */ + +#ifdef MODULE +int init_module(void){ + return usb_hub_init(); } + +void cleanup_module(void){ + usb_hub_cleanup(); +} +#endif diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h index d015c5a33063..62f4c0ef93d2 100644 --- a/drivers/usb/hub.h +++ b/drivers/usb/hub.h @@ -12,6 +12,7 @@ /* * Port feature numbers */ +#define USB_PORT_FEAT_CONNECTION 0 #define USB_PORT_FEAT_ENABLE 1 #define USB_PORT_FEAT_SUSPEND 2 #define USB_PORT_FEAT_OVER_CURRENT 3 @@ -40,7 +41,7 @@ #define USB_PORT_STAT_C_OVERCURRENT 0x0008 #define USB_PORT_STAT_C_RESET 0x0010 -/* Characteristics */ +/* wHubCharacteristics (masks) */ #define HUB_CHAR_LPSM 0x0003 #define HUB_CHAR_COMPOUND 0x0004 #define HUB_CHAR_OCPM 0x0018 @@ -67,6 +68,12 @@ struct usb_hub { /* Device structure */ struct usb_device *dev; + /* Reference to the hub's polling IRQ */ + void* irq_handle; + + /* List of hubs */ + struct list_head hub_list; + /* Temporary event list */ struct list_head event_list; diff --git a/drivers/usb/inits.h b/drivers/usb/inits.h index 81979ce399fa..28636769904a 100644 --- a/drivers/usb/inits.h +++ b/drivers/usb/inits.h @@ -1,6 +1,9 @@ -int bp_mouse_init(void); int usb_kbd_init(void); int usb_audio_init(void); -int hub_init(void); -void hub_cleanup(void); +int usb_hub_init(void); +int usb_acm_init(void); +int usb_printer_init(void); +void usb_hub_cleanup(void); void usb_mouse_cleanup(void); +int proc_usb_init (void); +void proc_usb_cleanup (void); diff --git a/drivers/usb/keyboard.c b/drivers/usb/keyboard.c index c60c812a587e..47c0780e51b6 100644 --- a/drivers/usb/keyboard.c +++ b/drivers/usb/keyboard.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include + #include #include "usb.h" @@ -53,10 +56,12 @@ usb_kbd_handle_key(unsigned char key, int down) int scancode = (int) usb_kbd_map[key]; if(scancode) { +#ifndef CONFIG_MAC_KEYBOARD if(scancode & PCKBD_NEEDS_E0) { handle_scancode(0xe0, 1); } +#endif /* CONFIG_MAC_KEYBOARD */ handle_scancode((scancode & ~PCKBD_NEEDS_E0), down); } } @@ -87,7 +92,7 @@ usb_kbd_repeat(unsigned long dev_id) } static int -usb_kbd_irq(int state, void *buffer, void *dev_id) +usb_kbd_irq(int state, void *buffer, int len, void *dev_id) { struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; unsigned long *down = (unsigned long*) buffer; @@ -168,7 +173,10 @@ usb_kbd_probe(struct usb_device *dev) struct usb_endpoint_descriptor *endpoint; struct usb_keyboard *kbd; - interface = &dev->config[0].interface[0]; + if (dev->descriptor.bNumConfigurations < 1) + return -1; + + interface = &dev->config[0].altsetting[0].interface[0]; endpoint = &interface->endpoint[0]; if(interface->bInterfaceClass != 3 @@ -187,7 +195,10 @@ usb_kbd_probe(struct usb_device *dev) kbd->dev = dev; dev->private = kbd; - usb_set_configuration(dev, dev->config[0].bConfigurationValue); + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + printk (KERN_INFO " Failed usb_set_configuration: kbd\n"); + goto probe_err; + } usb_set_protocol(dev, 0); usb_set_idle(dev, 0, 0); @@ -198,9 +209,14 @@ usb_kbd_probe(struct usb_device *dev) kbd); list_add(&kbd->list, &usb_kbd_list); + + return 0; } - return 0; +probe_err: + if (kbd) + kfree (kbd); + return -1; } static void @@ -218,9 +234,21 @@ usb_kbd_disconnect(struct usb_device *dev) printk(KERN_INFO "USB HID boot protocol keyboard removed.\n"); } -int -usb_kbd_init(void) +int usb_kbd_init(void) { usb_register(&usb_kbd_driver); return 0; } + +#ifdef MODULE +int init_module(void) +{ + return usb_kbd_init(); +} + +void cleanup_module(void) +{ + usb_deregister(&usb_kbd_driver); +} +#endif + diff --git a/drivers/usb/keymap.c b/drivers/usb/keymap.c index 16c7b28f3b5e..478de504b085 100644 --- a/drivers/usb/keymap.c +++ b/drivers/usb/keymap.c @@ -7,13 +7,13 @@ unsigned char usb_kbd_map[256] = 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x1c, 0x01, 0xd3, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, + 0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, 0x1b, 0x2b, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34, 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46, - 0x00, 0xd2, 0xc7, 0xc9, 0x63, 0xcf, 0xd1, 0xcd, + 0x00, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd, 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, diff --git a/drivers/usb/maps/fixup.map b/drivers/usb/maps/fixup.map index fc5d1ed7f227..6fbec256240d 100644 --- a/drivers/usb/maps/fixup.map +++ b/drivers/usb/maps/fixup.map @@ -1,7 +1,6 @@ # misc fixes keycode 0 = Pause keycode 29 = Control -keycode 99 = Remove keycode 42 = Shift keycode 54 = Shift_R keycode 109 = Application @@ -25,7 +24,7 @@ keycode 207 = End keycode 208 = Down keycode 209 = Next keycode 210 = Insert -keycode 211 = Delete +keycode 211 = Remove keycode 219 = Window keycode 220 = Window_R keycode 221 = Menu diff --git a/drivers/usb/maps/mac.map b/drivers/usb/maps/mac.map new file mode 100644 index 000000000000..dd601f76d225 --- /dev/null +++ b/drivers/usb/maps/mac.map @@ -0,0 +1,350 @@ +# Kernel keymap for Macintoshes. This uses 7 modifier combinations. +keymaps 0-2,4-5,8,12 +# +# Fixups: +keycode 0x69 = Print_Screen +keycode 0x6b = F14 +keycode 0x37 = Window_R +# +#keycode 0x00 = a +# hack! +keycode 0x80 = a + altgr keycode 0x00 = Hex_A +keycode 0x01 = s +keycode 0x02 = d + altgr keycode 0x02 = Hex_D +keycode 0x03 = f + altgr keycode 0x03 = Hex_F +keycode 0x04 = h +keycode 0x05 = g +keycode 0x06 = z +keycode 0x07 = x +keycode 0x08 = c + altgr keycode 0x08 = Hex_C +keycode 0x09 = v +keycode 0x0a = +keycode 0x0b = b + altgr keycode 0x0b = Hex_B +keycode 0x0c = q +keycode 0x0d = w +keycode 0x0e = e + altgr keycode 0x0e = Hex_E +keycode 0x0f = r +keycode 0x10 = y +keycode 0x11 = t +keycode 0x12 = one exclam + alt keycode 0x12 = Meta_one +keycode 0x13 = two at at + control keycode 0x13 = nul + shift control keycode 0x13 = nul + alt keycode 0x13 = Meta_two +keycode 0x14 = three numbersign + control keycode 0x14 = Escape + alt keycode 0x14 = Meta_three +keycode 0x15 = four dollar dollar + control keycode 0x15 = Control_backslash + alt keycode 0x15 = Meta_four +keycode 0x16 = six asciicircum + control keycode 0x16 = Control_asciicircum + alt keycode 0x16 = Meta_six +keycode 0x17 = five percent + control keycode 0x17 = Control_bracketright + alt keycode 0x17 = Meta_five +keycode 0x18 = equal plus + alt keycode 0x18 = Meta_equal +keycode 0x19 = nine parenleft bracketright + alt keycode 0x19 = Meta_nine +keycode 0x1a = seven ampersand braceleft + control keycode 0x1a = Control_underscore + alt keycode 0x1a = Meta_seven +keycode 0x1b = minus underscore backslash + control keycode 0x1b = Control_underscore + shift control keycode 0x1b = Control_underscore + alt keycode 0x1b = Meta_minus +keycode 0x1c = eight asterisk bracketleft + control keycode 0x1c = Delete + alt keycode 0x1c = Meta_eight +keycode 0x1d = zero parenright braceright + alt keycode 0x1d = Meta_zero +keycode 0x1e = bracketright braceright asciitilde + control keycode 0x1e = Control_bracketright + alt keycode 0x1e = Meta_bracketright +keycode 0x1f = o +keycode 0x20 = u +keycode 0x21 = bracketleft braceleft + control keycode 0x21 = Escape + alt keycode 0x21 = Meta_bracketleft +keycode 0x22 = i +keycode 0x23 = p +keycode 0x24 = Return + alt keycode 0x24 = Meta_Control_m +keycode 0x25 = l +keycode 0x26 = j +keycode 0x27 = apostrophe quotedbl + control keycode 0x27 = Control_g + alt keycode 0x27 = Meta_apostrophe +keycode 0x28 = k +keycode 0x29 = semicolon colon + alt keycode 0x29 = Meta_semicolon +keycode 0x2a = backslash bar + control keycode 0x2a = Control_backslash + alt keycode 0x2a = Meta_backslash +keycode 0x2b = comma less + alt keycode 0x2b = Meta_comma +keycode 0x2c = slash question + control keycode 0x2c = Delete + alt keycode 0x2c = Meta_slash +keycode 0x2d = n +keycode 0x2e = m +keycode 0x2f = period greater + control keycode 0x2f = Compose + alt keycode 0x2f = Meta_period +keycode 0x30 = Tab Tab + alt keycode 0x30 = Meta_Tab +keycode 0x31 = space space + control keycode 0x31 = nul + alt keycode 0x31 = Meta_space +keycode 0x32 = grave asciitilde + control keycode 0x32 = nul + alt keycode 0x32 = Meta_grave +keycode 0x33 = Delete Delete + control keycode 0x33 = BackSpace + alt keycode 0x33 = Meta_Delete +keycode 0x34 = +keycode 0x35 = Escape Escape + alt keycode 0x35 = Meta_Escape +keycode 0x36 = Control +keycode 0x37 = Window +keycode 0x38 = Shift +keycode 0x39 = Caps_Lock +keycode 0x3a = Alt +keycode 0x3b = Left + alt keycode 0x3b = Decr_Console +keycode 0x3c = Right + alt keycode 0x3c = Incr_Console +keycode 0x3d = Down +keycode 0x3e = Up +keycode 0x3f = +keycode 0x40 = +keycode 0x41 = KP_Period +keycode 0x42 = +keycode 0x43 = KP_Multiply +keycode 0x44 = +keycode 0x45 = KP_Add +keycode 0x46 = +keycode 0x47 = Num_Lock +# shift keycode 0x47 = Bare_Num_Lock +keycode 0x48 = +keycode 0x49 = +keycode 0x4a = +keycode 0x4b = KP_Divide +keycode 0x4c = KP_Enter +keycode 0x4d = +keycode 0x4e = KP_Subtract +keycode 0x4f = +keycode 0x50 = +keycode 0x51 = +#keycode 0x51 = KP_Equals +keycode 0x52 = KP_0 + alt keycode 0x52 = Ascii_0 + altgr keycode 0x52 = Hex_0 +keycode 0x53 = KP_1 + alt keycode 0x53 = Ascii_1 + altgr keycode 0x53 = Hex_1 +keycode 0x54 = KP_2 + alt keycode 0x54 = Ascii_2 + altgr keycode 0x54 = Hex_2 +keycode 0x55 = KP_3 + alt keycode 0x55 = Ascii_3 + altgr keycode 0x55 = Hex_3 +keycode 0x56 = KP_4 + alt keycode 0x56 = Ascii_4 + altgr keycode 0x56 = Hex_4 +keycode 0x57 = KP_5 + alt keycode 0x57 = Ascii_5 + altgr keycode 0x57 = Hex_5 +keycode 0x58 = KP_6 + alt keycode 0x58 = Ascii_6 + altgr keycode 0x58 = Hex_6 +keycode 0x59 = KP_7 + alt keycode 0x59 = Ascii_7 + altgr keycode 0x59 = Hex_7 +keycode 0x5b = KP_8 + alt keycode 0x5b = Ascii_8 + altgr keycode 0x5b = Hex_8 +keycode 0x5c = KP_9 + alt keycode 0x5c = Ascii_9 + altgr keycode 0x5c = Hex_9 +keycode 0x5d = +keycode 0x5e = +keycode 0x5f = +keycode 0x60 = F5 F15 Console_17 + control keycode 0x60 = F5 + alt keycode 0x60 = Console_5 + control alt keycode 0x60 = Console_5 +keycode 0x61 = F6 F16 Console_18 + control keycode 0x61 = F6 + alt keycode 0x61 = Console_6 + control alt keycode 0x61 = Console_6 +keycode 0x62 = F7 F17 Console_19 + control keycode 0x62 = F7 + alt keycode 0x62 = Console_7 + control alt keycode 0x62 = Console_7 +keycode 0x63 = F3 F13 Console_15 + control keycode 0x63 = F3 + alt keycode 0x63 = Console_3 + control alt keycode 0x63 = Console_3 +keycode 0x64 = F8 F18 Console_20 + control keycode 0x64 = F8 + alt keycode 0x64 = Console_8 + control alt keycode 0x64 = Console_8 +keycode 0x65 = F9 F19 Console_21 + control keycode 0x65 = F9 + alt keycode 0x65 = Console_9 + control alt keycode 0x65 = Console_9 +keycode 0x66 = +keycode 0x67 = F11 F11 Console_23 + control keycode 0x67 = F11 + alt keycode 0x67 = Console_11 + control alt keycode 0x67 = Console_11 +keycode 0x68 = +keycode 0x69 = F13 +keycode 0x6a = +keycode 0x6b = Scroll_Lock Show_Memory Show_Registers + control keycode 0x6b = Show_State + alt keycode 0x6b = Scroll_Lock +keycode 0x6c = +keycode 0x6d = F10 F20 Console_22 + control keycode 0x6d = F10 + alt keycode 0x6d = Console_10 + control alt keycode 0x6d = Console_10 +keycode 0x6e = +keycode 0x6f = F12 F12 Console_24 + control keycode 0x6f = F12 + alt keycode 0x6f = Console_12 + control alt keycode 0x6f = Console_12 +keycode 0x70 = +keycode 0x71 = Pause +keycode 0x72 = Insert +keycode 0x73 = Home +keycode 0x74 = Prior + shift keycode 0x74 = Scroll_Backward +keycode 0x75 = Remove +keycode 0x76 = F4 F14 Console_16 + control keycode 0x76 = F4 + alt keycode 0x76 = Console_4 + control alt keycode 0x76 = Console_4 +keycode 0x77 = End +keycode 0x78 = F2 F12 Console_14 + control keycode 0x78 = F2 + alt keycode 0x78 = Console_2 + control alt keycode 0x78 = Console_2 +keycode 0x79 = Next + shift keycode 0x79 = Scroll_Forward +keycode 0x7a = F1 F11 Console_13 + control keycode 0x7a = F1 + alt keycode 0x7a = Console_1 + control alt keycode 0x7a = Console_1 +keycode 0x7b = Shift_R +keycode 0x7c = Alt_R +keycode 0x7d = Control_R +keycode 0x7e = +keycode 0x7f = +#keycode 0x7f = Power + control shift keycode 0x7f = Boot +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff --git a/drivers/usb/mkmap.adb b/drivers/usb/mkmap.adb new file mode 100644 index 000000000000..45020d5ccdfc --- /dev/null +++ b/drivers/usb/mkmap.adb @@ -0,0 +1,88 @@ +#!/usr/bin/perl + +($ME = $0) =~ s|.*/||; + +$file = "maps/mac.map"; +$line = 1; +open(PC, $file) || die("$!"); +while() +{ + if(/^\s*keycode\s+(\d+|0x[0-9a-fA-F]+)\s*=\s*(\S+)/) + { + my($idx) = $1; + my($sym) = $2; + if ($idx =~ "0x.*") { + $idx = hex($idx); + } else { + $idx = int($idx); + } + if(defined($map{uc($sym)})) + { + # print STDERR "$file:$line: warning: `$sym' redefined\n"; + } + $map{uc($sym)} = $idx; + } + $line++; +} +close(PC); + +# $file = "maps/fixup.map"; +# $line = 1; +# open(FIXUP, $file) || die("$!"); +# while() +# { +# if(/^\s*keycode\s+(\d+)\s*=\s*/) +# { +# my($idx) = int($1); +# for $sym (split(/\s+/, $')) +# { +# $map{uc($sym)} = $idx; +# } +# } +# $line++; +# } +# close(FIXUP); + +$file = "maps/usb.map"; +$line = 1; +open(USB, $file) || die("$!"); +while() +{ + if(/^\s*keycode\s+(\d+)\s*=\s*/) + { + my($idx) = int($1); + for $sym (split(/\s+/, $')) + { + my($val) = $map{uc($sym)}; + $map[$idx] = $val; + if(!defined($val)) + { + print STDERR "$file:$line: warning: `$sym' undefined\n"; + } + else + { + last; + } + } + } + $line++; +} +close(USB); + +print "unsigned char usb_kbd_map[256] = \n{\n"; +for($x = 0; $x < 32; $x++) +{ + if($x && !($x % 2)) + { + print "\n"; + } + print " "; + for($y = 0; $y < 8; $y++) + { + my($idx) = $x * 8 + $y; + print sprintf(" 0x%02x,", + int(defined($map[$idx]) ? $map[$idx]:0)); + } + print "\n"; +} +print "};\n"; diff --git a/drivers/usb/mouse.c b/drivers/usb/mouse.c index 4d346c41ac98..906e455c12c1 100644 --- a/drivers/usb/mouse.c +++ b/drivers/usb/mouse.c @@ -2,6 +2,8 @@ * USB HID boot protocol mouse support based on MS BusMouse driver, psaux * driver, and Linus's skeleton USB mouse driver. Fixed up a lot by Linus. * + * N.B. this driver can't cope if more than one mouse is connected! - paulus + * * Brad Keryan 4/3/1999 * * version 0.20: Linus rewrote read_mouse() to do PS/2 and do it @@ -33,6 +35,7 @@ #include #include #include +#include #include @@ -48,17 +51,23 @@ struct mouse_state { int present; /* this mouse is plugged in */ int active; /* someone is has this mouse's device open */ int ready; /* the mouse has changed state since the last read */ - struct wait_queue *wait; /* for polling */ + wait_queue_head_t wait; /* for polling */ struct fasync_struct *fasync; /* later, add a list here to support multiple mice */ /* but we will also need a list of file pointers to identify it */ + + /* FIXME: move these to a per-mouse structure */ + struct usb_device *dev; /* host controller this mouse is on */ + void* irq_handle; /* host controller's IRQ transfer handle */ + __u8 bEndpointAddress; /* these are from the endpoint descriptor */ + __u8 bInterval; /* ... used when calling usb_request_irq */ }; static struct mouse_state static_mouse_state; spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED; -static int mouse_irq(int state, void *__buffer, void *dev_id) +static int mouse_irq(int state, void *__buffer, int len, void *dev_id) { signed char *data = __buffer; /* finding the mouse is easy when there's only one */ @@ -73,7 +82,7 @@ static int mouse_irq(int state, void *__buffer, void *dev_id) mouse->buttons = data[0] & 0x07; mouse->dx += data[1]; /* data[] is signed, so this works */ mouse->dy -= data[2]; /* y-axis is reversed */ - mouse->dz += data[3]; + mouse->dz -= data[3]; mouse->ready = 1; add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) + @@ -102,8 +111,16 @@ static int release_mouse(struct inode * inode, struct file * file) struct mouse_state *mouse = &static_mouse_state; fasync_mouse(-1, file, 0); - if (--mouse->active) - return 0; + + MOD_DEC_USE_COUNT; + + if (--mouse->active == 0) { + /* stop polling the mouse while its not in use */ + usb_release_irq(mouse->dev, mouse->irq_handle); + /* never keep a reference to a released IRQ! */ + mouse->irq_handle = NULL; + } + return 0; } @@ -117,6 +134,13 @@ static int open_mouse(struct inode * inode, struct file * file) return 0; /* flush state */ mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0; + + /* prevent the driver from being unloaded while its in use */ + MOD_INC_USE_COUNT; + + /* start the usb controller's polling of the mouse */ + mouse->irq_handle = usb_request_irq(mouse->dev, usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress), mouse_irq, mouse->bInterval, NULL); + return 0; } @@ -172,6 +196,30 @@ static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_ buffer++; retval++; state = 0; + if (!--count) + break; + } + + /* + * SUBTLE: + * + * The only way to get here is to do a read() of + * more than 3 bytes: if you read a byte at a time + * you will just ever see states 0-2, for backwards + * compatibility. + * + * So you can think of this as a packet interface, + * where you have arbitrary-sized packets, and you + * only ever see the first three bytes when you read + * them in small chunks. + */ + { /* fallthrough - dz */ + int dz = mouse->dz; + mouse->dz = 0; + put_user(dz, buffer); + buffer++; + retval++; + state = 0; } break; } @@ -223,7 +271,7 @@ static int mouse_probe(struct usb_device *dev) return -1; /* Is it a mouse interface? */ - interface = &dev->config[0].interface[0]; + interface = &dev->config[0].altsetting[0].interface[0]; if (interface->bInterfaceClass != 3) return -1; if (interface->bInterfaceSubClass != 1) @@ -245,11 +293,17 @@ static int mouse_probe(struct usb_device *dev) if ((endpoint->bmAttributes & 3) != 3) return -1; - printk("USB mouse found\n"); + printk(KERN_INFO "USB mouse found\n"); - usb_set_configuration(dev, dev->config[0].bConfigurationValue); + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + printk (KERN_INFO " Failed usb_set_configuration: mouse\n"); + return -1; + } - usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), mouse_irq, endpoint->bInterval, NULL); + /* these are used to request the irq when the mouse is opened */ + mouse->dev = dev; + mouse->bEndpointAddress = endpoint->bEndpointAddress; + mouse->bInterval = endpoint->bInterval; mouse->present = 1; return 0; @@ -259,8 +313,18 @@ static void mouse_disconnect(struct usb_device *dev) { struct mouse_state *mouse = &static_mouse_state; + /* stop the usb interrupt transfer */ + if (mouse->present) { + usb_release_irq(mouse->dev, mouse->irq_handle); + /* never keep a reference to a released IRQ! */ + mouse->irq_handle = NULL; + } + + mouse->irq_handle = NULL; + /* this might need work */ mouse->present = 0; + printk(KERN_INFO "Mouse disconnected\n"); } static struct usb_driver mouse_driver = { @@ -274,20 +338,42 @@ int usb_mouse_init(void) { struct mouse_state *mouse = &static_mouse_state; - misc_register(&usb_mouse); - mouse->present = mouse->active = 0; - mouse->wait = NULL; + mouse->irq_handle = NULL; + init_waitqueue_head(&mouse->wait); mouse->fasync = NULL; + misc_register(&usb_mouse); + usb_register(&mouse_driver); - printk(KERN_INFO "USB HID boot protocol mouse registered.\n"); + printk(KERN_INFO "USB HID boot protocol mouse driver registered.\n"); return 0; } void usb_mouse_cleanup(void) { + struct mouse_state *mouse = &static_mouse_state; + + /* stop the usb interrupt transfer */ + if (mouse->present) { + usb_release_irq(mouse->dev, mouse->irq_handle); + /* never keep a reference to a released IRQ! */ + mouse->irq_handle = NULL; + } + /* this, too, probably needs work */ usb_deregister(&mouse_driver); misc_deregister(&usb_mouse); } + +#ifdef MODULE +int init_module(void) +{ + return usb_mouse_init(); +} + +void cleanup_module(void) +{ + usb_mouse_cleanup(); +} +#endif diff --git a/drivers/usb/ohci-debug.c b/drivers/usb/ohci-debug.c index 1510bb9da441..339652d210e5 100644 --- a/drivers/usb/ohci-debug.c +++ b/drivers/usb/ohci-debug.c @@ -67,7 +67,7 @@ void show_ohci_status(struct ohci *ohci) void show_ohci_ed(struct ohci_ed *ed) { - int stat = ed->status; + int stat = le32_to_cpup(&ed->status); int skip = (stat & OHCI_ED_SKIP); int mps = (stat & OHCI_ED_MPS) >> 16; int isoc = (stat & OHCI_ED_F_ISOC); @@ -75,58 +75,74 @@ void show_ohci_ed(struct ohci_ed *ed) int dir = (stat & OHCI_ED_D); int endnum = (stat & OHCI_ED_EN) >> 7; int funcaddr = (stat & OHCI_ED_FA); - int halted = (ed->_head_td & 1); - int toggle = (ed->_head_td & 2) >> 1; + int halted = (le32_to_cpup(&ed->_head_td) & 1); + int toggle = (le32_to_cpup(&ed->_head_td) & 2) >> 1; printk(KERN_DEBUG " ohci ED:\n"); printk(KERN_DEBUG " status = 0x%x\n", stat); - printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d\n", + printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d%s\n", skip ? "Skip " : "", mps, - isoc ? "Isoc. " : "", + isoc ? " Isoc." : "", low_speed ? " LowSpd" : "", (dir == OHCI_ED_D_IN) ? " Input" : (dir == OHCI_ED_D_OUT) ? " Output" : "", halted ? " Halted" : "", toggle, endnum, - funcaddr); - printk(KERN_DEBUG " tail_td = 0x%x\n", ed->tail_td); + funcaddr, + (stat & ED_ALLOCATED) ? " Allocated" : ""); + printk(KERN_DEBUG " tail_td = 0x%x\n", ed_tail_td(ed)); printk(KERN_DEBUG " head_td = 0x%x\n", ed_head_td(ed)); - printk(KERN_DEBUG " next_ed = 0x%x\n", ed->next_ed); + printk(KERN_DEBUG " next_ed = 0x%x\n", le32_to_cpup(&ed->next_ed)); } /* show_ohci_ed() */ void show_ohci_td(struct ohci_td *td) { - int td_round = td->info & OHCI_TD_ROUND; - int td_dir = td->info & OHCI_TD_D; - int td_int_delay = (td->info & OHCI_TD_IOC_DELAY) >> 21; - int td_toggle = (td->info & OHCI_TD_DT) >> 24; + int info = le32_to_cpup(&td->info); + int td_round = info & OHCI_TD_ROUND; + int td_dir = info & OHCI_TD_D; + int td_int_delay = (info & OHCI_TD_IOC_DELAY) >> 21; + int td_toggle = (info & OHCI_TD_DT) >> 24; int td_errcnt = td_errorcount(*td); - int td_cc = OHCI_TD_CC_GET(td->info); + int td_cc = OHCI_TD_CC_GET(info); printk(KERN_DEBUG " ohci TD hardware fields:\n"); - printk(KERN_DEBUG " info = 0x%x\n", td->info); - printk(KERN_DEBUG " %s%s%s%d %s\n", + printk(KERN_DEBUG " info = 0x%x\n", info); + printk(KERN_DEBUG " %s%s%s%d %s %s%d\n", td_round ? "Rounding " : "", (td_dir == OHCI_TD_D_IN) ? "Input " : (td_dir == OHCI_TD_D_OUT) ? "Output " : (td_dir == OHCI_TD_D_SETUP) ? "Setup " : "", "IntDelay ", td_int_delay, (td_toggle < 2) ? " " : - (td_toggle & 1) ? "Data1 " : "Data0 "); - printk(KERN_DEBUG " %s%d %s0x%x, %sAccessed, %sActive\n", - "ErrorCnt ", td_errcnt, - "ComplCode ", td_cc, - td_cc_accessed(*td) ? "" : "Not ", - td_active(*td) ? "" : "Not "); - - printk(KERN_DEBUG " cur_buf = 0x%x\n", td->cur_buf); - printk(KERN_DEBUG " next_td = 0x%x\n", td->next_td); - printk(KERN_DEBUG " buf_end = 0x%x\n", td->buf_end); + (td_toggle & 1) ? "Data1" : "Data0", + "ErrorCnt ", td_errcnt); + printk(KERN_DEBUG " ComplCode 0x%x, %sAccessed\n", + td_cc, + td_cc_accessed(*td) ? "" : "Not "); + + printk(KERN_DEBUG " %s%s\n", + td_allocated(*td) ? "Allocated" : "Free", + td_dummy(*td) ? " DUMMY" : ""); + + printk(KERN_DEBUG " cur_buf = 0x%x\n", le32_to_cpup(&td->cur_buf)); + printk(KERN_DEBUG " next_td = 0x%x\n", le32_to_cpup(&td->next_td)); + printk(KERN_DEBUG " buf_end = 0x%x\n", le32_to_cpup(&td->buf_end)); printk(KERN_DEBUG " ohci TD driver fields:\n"); + printk(KERN_DEBUG " flags = %x {", td->hcd_flags); + if (td_allocated(*td)) + printk(" alloc"); + if (td_dummy(*td)) + printk(" dummy"); + if (td_endofchain(*td)) + printk(" endofchain"); + if (!can_auto_free(*td)) + printk(" noautofree"); + printk("}\n"); printk(KERN_DEBUG " data = %p\n", td->data); + printk(KERN_DEBUG " cmpltd = %p\n", td->completed); printk(KERN_DEBUG " dev_id = %p\n", td->dev_id); printk(KERN_DEBUG " ed = %p\n", td->ed); if (td->data != NULL) { @@ -137,6 +153,26 @@ void show_ohci_td(struct ohci_td *td) } /* show_ohci_td() */ +void show_ohci_td_chain(struct ohci_td *td) +{ + struct ohci_td *cur_td; + if (td == NULL) return; + + printk(KERN_DEBUG "+++ OHCI TD Chain %lx: +++\n", virt_to_bus(td)); + + cur_td = td; + for (;;) { + show_ohci_td(cur_td); + if (!cur_td->next_td) break; + cur_td = bus_to_virt(le32_to_cpup(&cur_td->next_td)); + /* we can't trust -anything- we find inside of a dummy TD */ + if (td_dummy(*cur_td)) break; + } + + printk(KERN_DEBUG "--- End TD Chain %lx. ---\n", virt_to_bus(td)); +} /* show_ohci_td_chain () */ + + void show_ohci_device(struct ohci_device *dev) { int idx; @@ -166,11 +202,14 @@ void show_ohci_hcca(struct ohci_hcca *hcca) printk(KERN_DEBUG " ohci_hcca\n"); for (idx=0; idxint_table +idx); + printk(KERN_DEBUG " int_table[%2d] == %x\n", idx, + le32_to_cpup(hcca->int_table + idx)); } - printk(KERN_DEBUG " frame_no == %d\n", hcca->frame_no); - printk(KERN_DEBUG " donehead == 0x%08x\n", hcca->donehead); + printk(KERN_DEBUG " frame_no == %d\n", + le16_to_cpup(&hcca->frame_no)); + printk(KERN_DEBUG " donehead == 0x%08x\n", + le32_to_cpup(&hcca->donehead)); } /* show_ohci_hcca() */ diff --git a/drivers/usb/ohci-hcd.c b/drivers/usb/ohci-hcd.c index da3ef6657dc0..b839ba884138 100644 --- a/drivers/usb/ohci-hcd.c +++ b/drivers/usb/ohci-hcd.c @@ -3,12 +3,14 @@ * * (C) Copyright 1999 Roman Weissgaerber * - * The OHCI HCD layer is a simple but nearly complete implementation of what the - * USB people would call a HCD for the OHCI. + * The OHCI HCD layer is a simple but nearly complete implementation of what + * the USB people would call a HCD for the OHCI. * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) - * The layer on top of it, is for interfacing to the alternate-usb device-drivers. + * The layer on top of it, is for interfacing to the alternate-usb + * device-drivers. * - * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ] + * [ This is based on Linus' UHCI code and gregs OHCI fragments + * (0.03c source tree). ] * [ Open Host Controller Interface driver for USB. ] * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] * [ (C) Copyright 1999 Gregory P. Smith ] @@ -51,9 +53,6 @@ #include "usb.h" #include "ohci-hcd.h" -#include "inits.h" - - #ifdef CONFIG_APM #include @@ -62,9 +61,9 @@ static int apm_resume = 0; #endif - -static struct wait_queue *control_wakeup; -static struct wait_queue *root_hub = NULL; +static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); +static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); +static DECLARE_WAIT_QUEUE_HEAD(root_hub); static __u8 cc_to_status[16] = { /* mapping of the OHCI CC to the UHCI status codes; first guess */ /* Activ, Stalled, Data Buffer Err, Babble Detected : NAK recvd, CRC/Timeout, Bitstuff, reservd */ @@ -103,7 +102,7 @@ static int sohci_int_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - ret = handler(cc_to_status[status & 0xf], data, dev_id); + ret = handler(cc_to_status[status & 0xf], data, data_len, dev_id); if(ret == 0) return 0; /* 0 .. do not requeue */ if(status > 0) return -1; /* error occured do not requeue ? */ ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */ @@ -122,7 +121,19 @@ static int sohci_ctrl_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len OHCI_DEBUG( printk(" ret_status: %x\n", status); }) return 0; } - + +static int sohci_bulk_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw) +{ + *(int * )lw0 = status; + wake_up(&bulk_wakeup); + + OHCI_DEBUG( { int i; printk("USB HC BULK<<<: %x:", ep_addr, ctrl_len);) + OHCI_DEBUG( printk(" data(%d):", data_len);) + OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) + OHCI_DEBUG( printk(" ret_status: %x\n", status); }) + return 0; +} + static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) { struct ohci * ohci = usb_dev->bus->hcpriv; @@ -145,7 +156,7 @@ static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_ static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); struct ohci * ohci = usb_dev->bus->hcpriv; int status; union ep_addr_ ep_addr; @@ -179,6 +190,40 @@ static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void return cc_to_status[status & 0x0f]; } +static int sohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len) +{ + DECLARE_WAITQUEUE(wait, current); + struct ohci * ohci = usb_dev->bus->hcpriv; + int status; + union ep_addr_ ep_addr; + + ep_addr.iep = 0; + ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */ + | (pipe & 0x80) /* direction */ + | (11 << 5); /* type = bulk*/ + ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */ + + status = 0xf; /* CC not Accessed */ + OHCI_DEBUG( { int i; printk("USB HC BULK>>>: %x:", ep_addr.iep);) + OHCI_DEBUG( printk(" data(%d):", len);) + OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) + OHCI_DEBUG( printk("\n"); }) + + usb_ohci_add_ep(ohci, ep_addr.iep, 0, 1, sohci_bulk_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01); + + current->state = TASK_UNINTERRUPTIBLE; + add_wait_queue(&bulk_wakeup, &wait); + + ohci_trans_req(ohci, ep_addr.iep, 0, NULL, data, len, (__OHCI_BAG) &status, 0); + + schedule_timeout(HZ/10); + + remove_wait_queue(&bulk_wakeup, &wait); + + OHCI_DEBUG(printk("USB HC status::: %x\n", cc_to_status[status & 0x0f]);) + + return cc_to_status[status & 0x0f]; +} static int sohci_usb_deallocate(struct usb_device *usb_dev) { struct ohci_device *dev = usb_to_ohci(usb_dev); @@ -234,11 +279,45 @@ static struct usb_device *sohci_usb_allocate(struct usb_device *parent) { return usb_dev; } + +static void *sohci_alloc_isochronous (struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id) +{ + return NULL; +} + +static void sohci_delete_isochronous (struct usb_device *dev, void *_isodesc) +{ + return; +} + +static int sohci_sched_isochronous (struct usb_device *usb_dev, void *_isodesc, void *_pisodesc) +{ + return USB_ST_NOTSUPPORTED; +} + +static int sohci_unsched_isochronous (struct usb_device *usb_dev, void *_isodesc) +{ + return USB_ST_NOTSUPPORTED; +} + +static int sohci_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) +{ + return USB_ST_NOTSUPPORTED; +} + + struct usb_operations sohci_device_operations = { sohci_usb_allocate, sohci_usb_deallocate, sohci_control_msg, + sohci_bulk_msg, sohci_request_irq, + NULL, /* FIXME: should be release_irq/remove_irq */ + sohci_alloc_isochronous, + sohci_delete_isochronous, + sohci_sched_isochronous, + sohci_unsched_isochronous, + sohci_compress_isochronous }; @@ -563,7 +642,7 @@ static int ohci_rm_eds(struct ohci * ohci) { } writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */ writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */ - writel_set((0x01<<4), &ohci->regs->control); /* start CTRL u. (BULK list) */ + writel_set((0x03<<4), &ohci->regs->control); /* start CTRL u. (BULK list) */ spin_unlock_irqrestore(&usb_ed_lock, flags); @@ -911,6 +990,7 @@ void start_hc(struct ohci *ohci) { /* int fminterval; */ unsigned int mask; + int port_nr; /* fminterval = readl(&ohci->regs->fminterval) & 0x3fff; reset_hc(ohci); */ @@ -922,16 +1002,19 @@ void start_hc(struct ohci *ohci) /* | OHCI_INTR_SO | OHCI_INTR_UE |OHCI_INTR_RHSC |OHCI_INTR_SF| OHCI_INTR_FNO */ - + if(readl(&ohci->regs->roothub.a) & 0x100) /* global power on */ + writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */ + else { /* port power on */ + for(port_nr=0; port_nr < (ohci->regs->roothub.a & 0xff); port_nr++) + writel(0x100, &ohci->regs->roothub.portstatus[port_nr]); + } + wait_ms(50); writel((0x00), &ohci->regs->control); /* USB Reset BUS */ wait_ms(10); - writel((0x97), &ohci->regs->control); /* USB Operational */ - - writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */ - wait_ms(50); - + writel((0xB7), &ohci->regs->control); /* USB Operational */ + OHCI_DEBUG(printk("USB HC rstart_hc_operational: %x\n", readl(&ohci->regs->control)); ) OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); ) OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); ) @@ -948,15 +1031,15 @@ void start_hc(struct ohci *ohci) struct usb_device * usb_dev; struct ohci_device *dev; - - usb_dev = sohci_usb_allocate(ohci->root_hub->usb); + struct ohci_device *tmp_root_hub= usb_to_ohci(ohci->bus->root_hub); + usb_dev = sohci_usb_allocate(tmp_root_hub->usb); dev = usb_dev->hcpriv; dev->ohci = ohci; usb_connect(usb_dev); - ohci->root_hub->usb->children[0] = usb_dev; + tmp_root_hub->usb->children[0] = usb_dev; usb_new_device(usb_dev); } @@ -1033,6 +1116,7 @@ static void ohci_connect_change(struct ohci *ohci, unsigned int port_nr) { struct usb_device *usb_dev; struct ohci_device *dev; + struct ohci_device *tmp_root_hub=usb_to_ohci(ohci->bus->root_hub); OHCI_DEBUG(printk("uhci_connect_change: called for %d stat %x\n", port_nr,readl(&ohci->regs->roothub.portstatus[port_nr]) );) /* @@ -1042,7 +1126,7 @@ static void ohci_connect_change(struct ohci *ohci, unsigned int port_nr) * * So start off by getting rid of any old devices.. */ - usb_disconnect(&ohci->root_hub->usb->children[port_nr]); + usb_disconnect(&tmp_root_hub->usb->children[port_nr]); if(!(readl(&ohci->regs->roothub.portstatus[port_nr]) & RH_PS_CCS)) { writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[port_nr]); @@ -1052,11 +1136,11 @@ static void ohci_connect_change(struct ohci *ohci, unsigned int port_nr) * Ok, we got a new connection. Allocate a device to it, * and find out what it wants to do.. */ - usb_dev = sohci_usb_allocate(ohci->root_hub->usb); + usb_dev = sohci_usb_allocate(tmp_root_hub->usb); dev = usb_dev->hcpriv; dev->ohci = ohci; usb_connect(dev->usb); - ohci->root_hub->usb->children[port_nr] = usb_dev; + tmp_root_hub->usb->children[port_nr] = usb_dev; wait_ms(200); /* wait for powerup */ /* reset port/device */ writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[port_nr]); /* reset port */ @@ -1201,10 +1285,9 @@ static struct ohci *alloc_ohci(void* mem_base) if (!usb) return NULL; - dev = ohci->root_hub = usb_to_ohci(usb); - + dev = usb_to_ohci(usb); usb->bus = bus; - /* bus->root_hub = ohci_to_usb(ohci->root_hub); */ + bus->root_hub = usb; dev->ohci = ohci; /* Initialize the root hub */ @@ -1223,10 +1306,11 @@ static struct ohci *alloc_ohci(void* mem_base) static void release_ohci(struct ohci *ohci) { int i; + struct ohci_device *tmp_root_hub=usb_to_ohci(ohci->bus->root_hub); union ep_addr_ ep_addr; ep_addr.iep = 0; - OHCI_DEBUG(printk("USB HC release ohci \n");) + OHCI_DEBUG(printk("USB HC release ohci \n");); if (ohci->irq >= 0) { free_irq(ohci->irq, ohci); @@ -1244,25 +1328,22 @@ static void release_ohci(struct ohci *ohci) ohci_rm_eds(ohci); /* remove eds */ /* disconnect all devices */ - if(ohci->root_hub) - for(i = 0; i < ohci->root_hub->usb->maxchild; i++) - usb_disconnect(ohci->root_hub->usb->children + i); + if(ohci->bus->root_hub) + for(i = 0; i < tmp_root_hub->usb->maxchild; i++) + usb_disconnect(tmp_root_hub->usb->children + i); - USB_FREE(ohci->root_hub->usb); - USB_FREE(ohci->root_hub); + usb_deregister_bus(ohci->bus); + USB_FREE(tmp_root_hub->usb); + USB_FREE(tmp_root_hub); USB_FREE(ohci->bus); /* unmap the IO address space */ iounmap(ohci->regs); - free_pages((unsigned int) ohci->hc_area, 1); } - -void cleanup_drivers(void); - static int ohci_roothub_thread(void * __ohci) { struct ohci *ohci = (struct ohci *)__ohci; @@ -1284,6 +1365,7 @@ static int ohci_roothub_thread(void * __ohci) start_hc(ohci); writel( 0x10000, &ohci->regs->roothub.status); wait_ms(50); /* root hub power on */ + usb_register_bus(ohci->bus); do { #ifdef CONFIG_APM if (apm_resume) { @@ -1293,7 +1375,7 @@ static int ohci_roothub_thread(void * __ohci) } #endif - OHCI_DEBUG(printk("USB RH tasks: int: %x\n", ohci->intrstatus); ) + OHCI_DEBUG(printk("USB RH tasks: int: %x\n",ohci->intrstatus);); #ifndef VROOTHUB /* if (ohci->intrstatus & OHCI_INTR_RHSC) */ { @@ -1328,9 +1410,6 @@ static int ohci_roothub_thread(void * __ohci) return 0; } - - - /* * Increment the module usage count, start the control thread and * return success. @@ -1426,21 +1505,6 @@ static int handle_apm_event(apm_event_t event) } return 0; } -#endif - - - int usb_mouse_init(void); -#ifdef MODULE - -void cleanup_module(void) -{ -#ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -#endif -} - -#define ohci_hcd_init init_module - #endif #define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310 @@ -1460,16 +1524,6 @@ int ohci_hcd_init(void) if (retval < 0) break; -#ifdef CONFIG_USB_MOUSE - usb_mouse_init(); -#endif -#ifdef CONFIG_USB_KBD - usb_kbd_init(); -#endif - hub_init(); -#ifdef CONFIG_USB_AUDIO - usb_audio_init(); -#endif #ifdef CONFIG_APM apm_register_callback(&handle_apm_event); #endif @@ -1479,11 +1533,16 @@ int ohci_hcd_init(void) return retval; } -void cleanup_drivers(void) +#ifdef MODULE +int init_module(void){ + return ohci_hcd_init(); +} + +void cleanup_module(void) { - hub_cleanup(); -#ifdef CONFIG_USB_MOUSE - usb_mouse_cleanup(); -#endif +# ifdef CONFIG_APM + apm_unregister_callback(&handle_apm_event); +# endif } +#endif //MODULE diff --git a/drivers/usb/ohci-hcd.h b/drivers/usb/ohci-hcd.h index aa782bb1df7f..27d07c93291c 100644 --- a/drivers/usb/ohci-hcd.h +++ b/drivers/usb/ohci-hcd.h @@ -307,7 +307,6 @@ struct ohci { struct usb_ohci_ed * ed_bulktail; /* last endpoint of bulk list */ struct usb_ohci_ed * ed_controltail; /* last endpoint of control list */ struct usb_ohci_ed * ed_isotail; /* last endpoint of iso list */ - struct ohci_device * root_hub; struct usb_ohci_ed ed_rh_ep0; struct usb_ohci_ed ed_rh_epi; struct ohci_rep_td *td_rh_epi; diff --git a/drivers/usb/ohci.c b/drivers/usb/ohci.c index b12d0114c690..6cb8f518d15a 100644 --- a/drivers/usb/ohci.c +++ b/drivers/usb/ohci.c @@ -2,19 +2,14 @@ * Open Host Controller Interface driver for USB. * * (C) Copyright 1999 Gregory P. Smith + * Significant code from the following individuals has also been used: + * (C) Copyright 1999 Roman Weissgaerber [ohci-hcd.c] + * (C) Copyright 1999 Linus Torvalds [uhci.c] * * This is the "other" host controller interface for USB. You will * find this on many non-Intel based motherboards, and of course the - * Mac. As Linus hacked his UHCI driver together first, I modeled - * this after his.. (it should be obvious) - * - * From the programming standpoint the OHCI interface seems a little - * prettier and potentially less CPU intensive. This remains to be - * proven. In reality, I don't believe it'll make one darn bit of - * difference. USB v1.1 is a slow bus by today's standards. - * - * OHCI hardware takes care of most of the scheduling of different - * transfer types with the correct prioritization for us. + * Mac. As Linus hacked his UHCI driver together first, I originally + * modeled this after his.. (it should be obvious) * * To get started in USB, I used the "Universal Serial Bus System * Architecture" book by Mindshare, Inc. It was a reasonable introduction @@ -29,7 +24,7 @@ * * No filesystems were harmed in the development of this code. * - * $Id: ohci.c,v 1.26 1999/05/11 07:34:47 greg Exp $ + * $Id: ohci.c,v 1.43 1999/05/16 22:35:24 greg Exp $ */ #include @@ -49,7 +44,6 @@ #include #include "ohci.h" -#include "inits.h" #ifdef CONFIG_APM #include @@ -57,94 +51,128 @@ static int handle_apm_event(apm_event_t event); static int apm_resume = 0; #endif -static struct wait_queue *ohci_configure = NULL; +static DECLARE_WAIT_QUEUE_HEAD(ohci_configure); -#ifdef OHCI_TIMER -static struct timer_list ohci_timer; /* timer for root hub polling */ +#ifdef CONFIG_USB_OHCI_DEBUG +#define OHCI_DEBUG /* to make typing it easier.. */ #endif +int MegaDebug = 0; /* SIGUSR2 to the control thread toggles this */ -static int ohci_td_result(struct ohci_device *dev, struct ohci_td *td) -{ - unsigned int status; - - status = td->info & OHCI_TD_CC; - - /* TODO Debugging code for TD failures goes here */ - - return status; -} /* ohci_td_result() */ +#ifdef OHCI_TIMER +static struct timer_list ohci_timer; /* timer for root hub polling */ +#endif static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED; +#define FIELDS_OF_ED(e) le32_to_cpup(&e->status), le32_to_cpup(&e->tail_td), \ + le32_to_cpup(&e->_head_td), le32_to_cpup(&e->next_ed) +#define FIELDS_OF_TD(t) le32_to_cpup(&t->info), le32_to_cpup(&t->cur_buf), \ + le32_to_cpup(&t->next_td), le32_to_cpup(&t->buf_end) + +#ifdef OHCI_DEBUG +static const char *cc_names[16] = { + "no error", + "CRC error", + "bit stuff error", + "data toggle mismatch", + "stall", + "device not responding", + "PID check failed", + "unexpected PID", + "data overrun", + "data underrun", + "reserved (10)", + "reserved (11)", + "buffer overrun", + "buffer underrun", + "not accessed (14)", + "not accessed" +}; +#endif + /* - * Add a TD to the end of the TD list on a given ED. If td->next_td - * points to any more TDs, they will be added as well (naturally). - * Otherwise td->next_td must be 0. - * - * The SKIP flag will be cleared after this function. + * Add a chain of TDs to the end of the TD list on a given ED. + * + * This function uses the first TD of the chain as the new dummy TD + * for the ED, and uses the old dummy TD instead of the first TD + * of the chain. The reason for this is that this makes it possible + * to update the TD chain without needing any locking between the + * CPU and the OHCI controller. + * + * The return value is the pointer to the new first TD (the old + * dummy TD). * - * Important! This function needs locking and atomicity as it works - * in parallel with the HC's DMA. Locking ohci_edtd_lock while using - * the function is a must. + * Important! This function is not re-entrant w.r.t. each ED. + * Locking ohci_edtd_lock while using the function is a must + * if there is any possibility of another CPU or an interrupt routine + * calling this function with the same ED. * * This function can be called by the interrupt handler. */ -static void ohci_add_td_to_ed(struct ohci_td *td, struct ohci_ed *ed) +static struct ohci_td *ohci_add_tds_to_ed(struct ohci_td *td, + struct ohci_ed *ed) { - /* don't let the HC pull anything from underneath us */ - ed->status |= OHCI_ED_SKIP; + struct ohci_td *t, *dummy_td; + u32 new_dummy; - if (ed_head_td(ed) == 0) { /* empty list, put it on the head */ - set_ed_head_td(ed, virt_to_bus(td)); - ed->tail_td = 0; - } else { - struct ohci_td *tail, *head; - head = (ed_head_td(ed) == 0) ? NULL : bus_to_virt(ed_head_td(ed)); - tail = (ed->tail_td == 0) ? NULL : bus_to_virt(ed->tail_td); - if (!tail) { /* no tail, single element list */ - td->next_td = head->next_td; - head->next_td = virt_to_bus(td); - ed->tail_td = virt_to_bus(td); - } else { /* append to the list */ - td->next_td = tail->next_td; - tail->next_td = virt_to_bus(td); - ed->tail_td = virt_to_bus(td); - } + if (ed->tail_td == 0) { + printk(KERN_ERR "eek! an ED without a dummy_td\n"); + return td; } - /* save the ED link in each of the TDs added */ - td->ed = ed; - while (td->next_td != 0) { - td = bus_to_virt(td->next_td); - td->ed = ed; + /* Get a pointer to the current dummy TD. */ + dummy_td = bus_to_virt(ed_tail_td(ed)); + + for (t = td; ; t = bus_to_virt(le32_to_cpup(&t->next_td))) { + t->ed = ed; + if (t->next_td == 0) + break; } - /* turn off the SKIP flag */ - ed->status &= ~OHCI_ED_SKIP; -} /* ohci_add_td_to_ed() */ + /* Make the last TD point back to the first, since it + * will become the new dummy TD. */ + new_dummy = cpu_to_le32(virt_to_bus(td)); + t->next_td = new_dummy; + + /* Copy the contents of the first TD into the dummy */ + *dummy_td = *td; + + /* Turn the first TD into a dummy */ + make_dumb_td(td); + + /* Set the HC's tail pointer to the new dummy */ + ed->tail_td = new_dummy; + + return dummy_td; /* replacement head of chain */ +} /* ohci_add_tds_to_ed() */ + +/* .......... */ -inline void ohci_start_control(struct ohci *ohci) + +void ohci_start_control(struct ohci *ohci) { /* tell the HC to start processing the control list */ - writel(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus); + writel_set(OHCI_USB_CLE, &ohci->regs->control); + writel_set(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus); } -inline void ohci_start_bulk(struct ohci *ohci) +void ohci_start_bulk(struct ohci *ohci) { /* tell the HC to start processing the bulk list */ - writel(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus); + writel_set(OHCI_USB_BLE, &ohci->regs->control); + writel_set(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus); } -inline void ohci_start_periodic(struct ohci *ohci) +void ohci_start_periodic(struct ohci *ohci) { - /* enable processing periodc transfers starting next frame */ + /* enable processing periodic (intr) transfers starting next frame */ writel_set(OHCI_USB_PLE, &ohci->regs->control); } -inline void ohci_start_isoc(struct ohci *ohci) +void ohci_start_isoc(struct ohci *ohci) { /* enable processing isoc. transfers starting next frame */ writel_set(OHCI_USB_IE, &ohci->regs->control); @@ -152,6 +180,7 @@ inline void ohci_start_isoc(struct ohci *ohci) /* * Add an ED to the hardware register ED list pointed to by hw_listhead_p + * This function only makes sense for Control and Bulk EDs. */ static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p) { @@ -164,19 +193,17 @@ static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p) /* if the list is not empty, insert this ED at the front */ /* XXX should they go on the end? */ - if (listhead) { - ed->next_ed = listhead; - } + ed->next_ed = cpu_to_le32(listhead); /* update the hardware listhead pointer */ writel(virt_to_bus(ed), hw_listhead_p); spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_add_ed() */ +} /* ohci_add_ed_to_hw() */ /* - * Put another control ED on the controller's list + * Put a control ED on the controller's list */ void ohci_add_control_ed(struct ohci *ohci, struct ohci_ed *ed) { @@ -184,37 +211,187 @@ void ohci_add_control_ed(struct ohci *ohci, struct ohci_ed *ed) ohci_start_control(ohci); } /* ohci_add_control_ed() */ +/* + * Put a bulk ED on the controller's list + */ +void ohci_add_bulk_ed(struct ohci *ohci, struct ohci_ed *ed) +{ + ohci_add_ed_to_hw(ed, &ohci->regs->ed_bulkhead); + ohci_start_bulk(ohci); +} /* ohci_add_bulk_ed() */ -#if 0 /* - * Put another control ED on the controller's list + * Put a periodic ED on the appropriate list given the period. */ void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period) { - ohci_add_ed_to_hw(ed, /* XXX */); + struct ohci_ed *int_ed; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); + unsigned long flags; + + /* + * Pick a good frequency endpoint based on the requested period + */ + int_ed = &root_hub->ed[ms_to_ed_int(period)]; +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "usb-ohci: Using INT ED queue %d for %dms period\n", + ms_to_ed_int(period), period); +#endif + + spin_lock_irqsave(&ohci_edtd_lock, flags); + /* + * Insert this ED at the front of the list. + */ + ed->next_ed = int_ed->next_ed; + int_ed->next_ed = cpu_to_le32(virt_to_bus(ed)); + + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + ohci_start_periodic(ohci); -} /* ohci_add_control_ed() */ +} /* ohci_add_periodic_ed() */ + +/* + * Locate the periodic ED for a given interrupt endpoint. + */ +struct ohci_ed *ohci_get_periodic_ed(struct ohci_device *dev, int period, + unsigned int pipe, int isoc) +{ + struct ohci_device *root_hub = usb_to_ohci(dev->ohci->bus->root_hub); + unsigned long flags; + struct ohci_ed *int_ed; + unsigned int status, req_status; + + /* get the dummy ED before the EDs for this period */ + int_ed = &root_hub->ed[ms_to_ed_int(period)]; + + /* decide on what the status field should look like */ + req_status = ed_set_maxpacket(usb_maxpacket(ohci_to_usb(dev), pipe)) + | ed_set_speed(usb_pipeslow(pipe)) + | (usb_pipe_endpdev(pipe) & 0x7ff) + | ed_set_type_isoc(isoc); + + spin_lock_irqsave(&ohci_edtd_lock, flags); + for (;;) { + int_ed = bus_to_virt(le32_to_cpup(&int_ed->next_ed)); + /* stop if we get to the end or to another dummy ED. */ + if (int_ed == 0) + break; + status = le32_to_cpup(&int_ed->status); + if ((status & OHCI_ED_FA) == 0) { + int_ed = 0; + break; + } + /* check whether all the appropriate fields match */ + if ((status & 0x7ffa7ff) == req_status) + break; + } + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + return int_ed; +} + +/* + * Put an isochronous ED on the controller's list + */ +inline void ohci_add_isoc_ed(struct ohci *ohci, struct ohci_ed *ed) +{ + ohci_add_periodic_ed(ohci, ed, 1); +} + + +/* + * This will be used for the interrupt to wake us up on the next SOF + */ +DECLARE_WAIT_QUEUE_HEAD(start_of_frame_wakeup); + +static void ohci_wait_sof(struct ohci_regs *regs) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&start_of_frame_wakeup, &wait); + + /* clear the SOF interrupt status and enable it */ + writel(OHCI_INTR_SF, ®s->intrstatus); + writel(OHCI_INTR_SF, ®s->intrenable); + + schedule_timeout(HZ/10); + + remove_wait_queue(&start_of_frame_wakeup, &wait); +} + +/* + * Guarantee that an ED is safe to be modified by the HCD (us). + * + * This function can NOT be called from an interrupt. + */ +void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed, int ed_type) +{ + __u32 *hw_listcurrent; + + /* tell the controller to skip this ED */ + ed->status |= cpu_to_le32(OHCI_ED_SKIP); + + switch (ed_type) { + case HCD_ED_CONTROL: + hw_listcurrent = ®s->ed_controlcurrent; + break; + case HCD_ED_BULK: + hw_listcurrent = ®s->ed_bulkcurrent; + break; + case HCD_ED_ISOC: + case HCD_ED_INT: + hw_listcurrent = ®s->ed_periodcurrent; + break; + default: + return; + } + + /* + * If the HC is processing this ED we need to wait until the + * at least the next frame. + */ + if (virt_to_bus(ed) == readl(hw_listcurrent)) { +#ifdef OHCI_DEBUG + printk(KERN_INFO "Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed)); #endif + ohci_wait_sof(regs); + + } + + return; /* The ED is now safe */ +} /* ohci_wait_for_ed_safe() */ /* - * Remove an ED from the HC list whos bus headpointer is pointed to - * by hw_listhead_p + * Remove an ED from the HC's list. + * This function can ONLY be used for Control or Bulk EDs. * * Note that the SKIP bit is left on in the removed ED. */ -void ohci_remove_ed_from_hw(struct ohci_ed *ed, __u32* hw_listhead_p) +void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_type) { unsigned long flags; + struct ohci_regs *regs = ohci->regs; struct ohci_ed *cur; __u32 bus_ed = virt_to_bus(ed); __u32 bus_cur; + __u32 *hw_listhead_p; if (ed == NULL || !bus_ed) return; + ed->status |= cpu_to_le32(OHCI_ED_SKIP); - /* tell the controller this skip ED */ - ed->status |= OHCI_ED_SKIP; + switch (ed_type) { + case HCD_ED_CONTROL: + hw_listhead_p = ®s->ed_controlhead; + break; + case HCD_ED_BULK: + hw_listhead_p = ®s->ed_bulkhead; + break; + default: + printk(KERN_ERR "Unknown HCD ED type %d.\n", ed_type); + return; + } bus_cur = readl(hw_listhead_p); @@ -227,31 +404,33 @@ void ohci_remove_ed_from_hw(struct ohci_ed *ed, __u32* hw_listhead_p) /* if its the head ED, move the head */ if (bus_cur == bus_ed) { - writel(cur->next_ed, hw_listhead_p); + writel(le32_to_cpup(&cur->next_ed), hw_listhead_p); } else if (cur->next_ed != 0) { struct ohci_ed *prev; /* walk the list and unlink the ED if found */ - for (;;) { + do { prev = cur; - cur = bus_to_virt(cur->next_ed); + cur = bus_to_virt(le32_to_cpup(&cur->next_ed)); - if (virt_to_bus(cur) == bus_ed) { + if (cur == ed) { /* unlink from the list */ prev->next_ed = cur->next_ed; break; } - - if (cur->next_ed == 0) - break; - } + } while (cur->next_ed != 0); } + /* + * Make sure this ED is not being accessed by the HC as we speak. + */ + ohci_wait_for_ed_safe(regs, ed, ed_type); + /* clear any links from the ED for safety */ ed->next_ed = 0; spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_remove_ed_from_hw() */ +} /* ohci_remove_norm_ed_from_hw() */ /* * Remove an ED from the controller's control list. Note that the SKIP bit @@ -259,7 +438,7 @@ void ohci_remove_ed_from_hw(struct ohci_ed *ed, __u32* hw_listhead_p) */ inline void ohci_remove_control_ed(struct ohci *ohci, struct ohci_ed *ed) { - ohci_remove_ed_from_hw(ed, &ohci->regs->ed_controlhead); + ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_CONTROL); } /* @@ -268,12 +447,128 @@ inline void ohci_remove_control_ed(struct ohci *ohci, struct ohci_ed *ed) */ inline void ohci_remove_bulk_ed(struct ohci *ohci, struct ohci_ed *ed) { - ohci_remove_ed_from_hw(ed, &ohci->regs->ed_bulkhead); + ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_BULK); } /* - * Remove a TD from the given EDs TD list. + * Remove a periodic ED from the host controller + */ +void ohci_remove_periodic_ed(struct ohci *ohci, struct ohci_ed *ed) +{ + struct ohci_device *root_hub = usb_to_ohci(ohci->bus->root_hub); + struct ohci_ed *cur_ed = NULL, *prev_ed; + unsigned long flags; + + /* FIXME: this will need to up fixed when add_periodic_ed() + * is updated to spread similar polling rate EDs out over + * multiple periodic queues. Currently this assumes that the + * 32ms (slowest) polling queue links to all others... */ + + /* search the periodic EDs, skipping the first one which is + * only a placeholder. */ + prev_ed = &root_hub->ed[ED_INT_32]; + if (prev_ed->next_ed) + cur_ed = bus_to_virt(le32_to_cpup(&prev_ed->next_ed)); + + while (cur_ed) { + if (ed == cur_ed) { /* remove the ED */ + /* set its SKIP bit and be sure its not in use */ + ohci_wait_for_ed_safe(ohci->regs, ed, HCD_ED_INT); + + /* unlink it */ + spin_lock_irqsave(&ohci_edtd_lock, flags); + prev_ed->next_ed = ed->next_ed; + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + ed->next_ed = 0; + + break; + } + + spin_lock_irqsave(&ohci_edtd_lock, flags); + if (cur_ed->next_ed) { + prev_ed = cur_ed; + cur_ed = bus_to_virt(le32_to_cpup(&cur_ed->next_ed)); + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + } else { + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + + /* if multiple polling queues need to be checked, + * here is where you'd advance to the next one */ + + printk(KERN_ERR "usb-ohci: ed %p not found on periodic queue\n", ed); + break; + } + } +} /* ohci_remove_periodic_ed() */ + + +/* + * Remove all the EDs which have a given device address from a list. + * Used when the device is unplugged. + * Returns 1 if anything was changed. + */ +static int ohci_remove_device_list(__u32 *headp, int devnum) +{ + struct ohci_ed *ed; + __u32 *prevp = headp; + int removed = 0; + + while (*prevp != 0) { + ed = bus_to_virt(le32_to_cpup(prevp)); + if ((le32_to_cpup(&ed->status) & OHCI_ED_FA) == devnum) { + /* set the controller to skip this one + and remove it from the list */ + ed->status |= cpu_to_le32(OHCI_ED_SKIP); + /* XXX should call td->completed for each td */ + *prevp = ed->next_ed; + removed = 1; + } else { + prevp = &ed->next_ed; + } + } + wmb(); + + return removed; +} + +/* + * Remove all the EDs for a given device from all lists. + */ +void ohci_remove_device(struct ohci *ohci, int devnum) +{ + unsigned long flags; + __u32 head; + struct ohci_regs *regs = ohci->regs; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); + + spin_lock_irqsave(&ohci_edtd_lock, flags); + + /* Control list */ + head = cpu_to_le32(readl(®s->ed_controlhead)); + if (ohci_remove_device_list(&head, devnum)) + writel(le32_to_cpup(&head), ®s->ed_controlhead); + + /* Bulk list */ + head = cpu_to_le32(readl(®s->ed_bulkhead)); + if (ohci_remove_device_list(&head, devnum)) + writel(le32_to_cpup(&head), ®s->ed_bulkhead); + + /* Interrupt/iso list */ + head = cpu_to_le32(virt_to_bus(&root_hub->ed[ED_INT_32])); + ohci_remove_device_list(&head, devnum); + + /* + * Wait until the start of the next frame to ensure + * that the HC has seen any changes. + */ + ohci_wait_sof(ohci->regs); + + spin_unlock_irqrestore(&ohci_edtd_lock, flags); +} + +/* + * Remove a TD from the given EDs TD list. The TD is freed as well. */ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) { @@ -283,13 +578,13 @@ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) if ((td == NULL) || (ed == NULL)) return; - spin_lock_irqsave(&ohci_edtd_lock, flags); - if (ed_head_td(ed) == 0) return; + spin_lock_irqsave(&ohci_edtd_lock, flags); + /* set the "skip me bit" in this ED */ - ed->status |= OHCI_ED_SKIP; + ed->status |= cpu_to_le32(OHCI_ED_SKIP); /* XXX Assuming this list will never be circular */ @@ -303,7 +598,7 @@ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) /* FIXME: collapse this into a nice simple loop :) */ if (head_td->next_td != 0) { prev_td = head_td; - cur_td = bus_to_virt(head_td->next_td); + cur_td = bus_to_virt(le32_to_cpup(&head_td->next_td)); for (;;) { if (td == cur_td) { /* remove it */ @@ -313,7 +608,7 @@ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) if (cur_td->next_td == 0) break; prev_td = cur_td; - cur_td = bus_to_virt(cur_td->next_td); + cur_td = bus_to_virt(le32_to_cpup(&cur_td->next_td)); } } } @@ -321,10 +616,11 @@ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) td->next_td = 0; /* remove the TDs links */ td->ed = NULL; - /* TODO return this TD to the pool of free TDs */ + /* return this TD to the pool of free TDs */ + ohci_free_td(td); /* unset the "skip me bit" in this ED */ - ed->status &= ~OHCI_ED_SKIP; + ed->status &= cpu_to_le32(~OHCI_ED_SKIP); spin_unlock_irqrestore(&ohci_edtd_lock, flags); } /* ohci_remove_td_from_ed() */ @@ -332,47 +628,109 @@ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) /* * Get a pointer (virtual) to an available TD from the given device's - * pool. - * - * Return NULL if none are left. + * pool. Return NULL if none are left. */ static struct ohci_td *ohci_get_free_td(struct ohci_device *dev) { int idx; + /* FIXME: this is horribly inefficient */ for (idx=0; idx < NUM_TDS; idx++) { if (!td_allocated(dev->td[idx])) { struct ohci_td *new_td = &dev->td[idx]; /* zero out the TD */ memset(new_td, 0, sizeof(*new_td)); /* mark the new TDs as unaccessed */ - new_td->info = OHCI_TD_CC_NEW; + new_td->info = cpu_to_le32(OHCI_TD_CC_NEW); /* mark it as allocated */ allocate_td(new_td); + /* record the device that its on */ + new_td->usb_dev = ohci_to_usb(dev); return new_td; } } - printk("usb-ohci error: unable to allocate a TD\n"); + printk(KERN_ERR "usb-ohci: unable to allocate a TD\n"); return NULL; } /* ohci_get_free_td() */ +/* + * Get a pointer (virtual) to an available TD from the given device's + * pool. Return NULL if none are left. + * + * NOTE: This function does not allocate and attach the dummy_td. + * That is done in ohci_fill_ed(). FIXME: it should probably be moved + * into here. + */ +static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev) +{ + int idx; + + /* FIXME: this is horribly inefficient */ + for (idx=0; idx < NUM_EDS; idx++) { + if (!ed_allocated(dev->ed[idx])) { + struct ohci_ed *new_ed = &dev->ed[idx]; + /* zero out the ED */ + memset(new_ed, 0, sizeof(*new_ed)); + /* all new EDs start with the SKIP bit set */ + new_ed->status |= cpu_to_le32(OHCI_ED_SKIP); + /* mark it as allocated */ + allocate_ed(new_ed); + new_ed->ohci_dev = dev; + return new_ed; + } + } + + printk(KERN_ERR "usb-ohci: unable to allocate an ED\n"); + return NULL; +} /* ohci_get_free_ed() */ + + +/* + * Free an OHCI ED and all of the TDs on its list. It is assumed that + * this ED is not active. You should call ohci_wait_for_ed_safe() + * beforehand if you can't guarantee that. + */ +void ohci_free_ed(struct ohci_ed *ed) +{ + if (!ed) + return; + + if (ed_head_td(ed) != 0) { + struct ohci_td *td, *tail_td, *next_td; + + td = bus_to_virt(ed_head_td(ed)); + tail_td = bus_to_virt(ed_tail_td(ed)); + for (;;) { + next_td = bus_to_virt(le32_to_cpup(&td->next_td)); + ohci_free_td(td); + if (td == tail_td) + break; + td = next_td; + } + } + + ed->status &= cpu_to_le32(~(__u32)ED_ALLOCATED); +} /* ohci_free_ed() */ + + /* * Initialize a TD * * dir = OHCI_TD_D_IN, OHCI_TD_D_OUT, or OHCI_TD_D_SETUP * toggle = TOGGLE_AUTO, TOGGLE_DATA0, TOGGLE_DATA1 */ -inline struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 flags, void *data, __u32 len, void *dev_id, usb_device_irq completed) +struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 flags, void *data, __u32 len, void *dev_id, usb_device_irq completed) { /* hardware fields */ - td->info = OHCI_TD_CC_NEW | - (dir & OHCI_TD_D) | - (toggle & OHCI_TD_DT) | - flags; - td->cur_buf = (data == NULL) ? 0 : virt_to_bus(data); - td->buf_end = (len == 0) ? 0 : td->cur_buf + len - 1; + td->info = cpu_to_le32(OHCI_TD_CC_NEW | + (dir & OHCI_TD_D) | + (toggle & OHCI_TD_DT) | + flags); + td->cur_buf = (data == NULL) ? 0 : cpu_to_le32(virt_to_bus(data)); + td->buf_end = (len == 0) ? 0 : + cpu_to_le32(virt_to_bus((char *)data + len - 1)); /* driver fields */ td->data = data; @@ -383,6 +741,177 @@ inline struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, } /* ohci_fill_new_td() */ +/* + * Initialize a new ED on device dev, including allocating and putting the + * dummy tail_td on its queue if it doesn't already have one. Any + * TDs on this ED other than the dummy will be lost (so there better + * not be any!). This assumes that the ED is Allocated and will + * force the Allocated bit on. + */ +struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, + int maxpacketsize, int lowspeed, int endp_id, + int isoc_tds) +{ + struct ohci_td *dummy_td; + + if (ed_head_td(ed) != ed_tail_td(ed)) + printk(KERN_ERR "Reusing a non-empty ED %p!\n", ed); + + if (!ed->tail_td) { + dummy_td = ohci_get_free_td(dev); + if (dummy_td == NULL) { + printk(KERN_ERR "Error allocating dummy TD for ED %p\n", ed); + return NULL; /* no dummy available! */ + } + make_dumb_td(dummy_td); /* flag it as a dummy */ + ed->tail_td = cpu_to_le32(virt_to_bus(dummy_td)); + } else { + dummy_td = bus_to_virt(ed_tail_td(ed)); + if (!td_dummy(*dummy_td)) + printk(KERN_ERR "ED %p's dummy %p is screwy\n", ed, dummy_td); + } + + /* set the head TD to the dummy and clear the Carry & Halted bits */ + ed->_head_td = ed->tail_td; + + ed->status = cpu_to_le32( + ed_set_maxpacket(maxpacketsize) | + ed_set_speed(lowspeed) | + (endp_id & 0x7ff) | + ((isoc_tds == 0) ? OHCI_ED_F_NORM : OHCI_ED_F_ISOC)); + allocate_ed(ed); + ed->next_ed = 0; + + return ed; +} /* ohci_fill_ed() */ + + +/* + * Create a chain of Normal TDs to be used for a large data transfer + * (bulk or control). + * + * Returns the head TD in the chain. + */ +struct ohci_td *ohci_build_td_chain(struct ohci_device *dev, + void *data, unsigned int len, int dir, __u32 toggle, + int round, int auto_free, void* dev_id, + usb_device_irq handler, __u32 next_td) +{ + struct ohci_td *head, *cur_td; + unsigned max_len; + + if (!data || (len == 0)) + return NULL; + + /* Get the first TD */ + head = ohci_get_free_td(dev); + if (head == NULL) { + printk(KERN_ERR "usb-ohci: out of TDs\n"); + return NULL; + } + + cur_td = head; + + /* AFICT, that the OHCI controller takes care of the innards of + * bulk & control data transfers by sending zero length + * packets as necessary if the transfer falls on an even packet + * size boundary, we don't need a special TD for that. */ + + /* check the 4096 byte alignment of the start of the data */ + max_len = 0x2000 - ((unsigned long)data & 0xfff); + + /* check if the remaining data occupies more than two pages */ + while (len > max_len) { + struct ohci_td *new_td; + + /* TODO lookup effect of rounding bit on + * individual TDs vs. whole TD chain transfers; + * disable cur_td's rounding bit here if needed. */ + + ohci_fill_new_td(cur_td, + td_set_dir_out(dir), + toggle & OHCI_TD_DT, + (round ? OHCI_TD_ROUND : 0), + data, max_len - 1, + dev_id, handler); + if (!auto_free) + noauto_free_td(head); + + /* adjust the data pointer & remaining length */ + data += max_len; + len -= max_len; + + /* allocate another td */ + new_td = ohci_get_free_td(dev); + if (new_td == NULL) { + printk(KERN_ERR "usb-ohci: out of TDs\n"); + /* FIXME: free any allocated TDs */ + return NULL; + } + + /* Link the new TD to the chain & advance */ + cur_td->next_td = cpu_to_le32(virt_to_bus(new_td)); + cur_td = new_td; + + /* address is page-aligned now */ + max_len = 0x2000; + toggle = TOGGLE_AUTO; /* toggle Data0/1 via the ED */ + } + + ohci_fill_new_td(cur_td, + td_set_dir_out(dir), + toggle & OHCI_TD_DT, + (round ? OHCI_TD_ROUND : 0), + data, len, + dev_id, handler); + if (!auto_free) + noauto_free_td(head); + + /* link the given next_td to the end of this chain */ + cur_td->next_td = cpu_to_le32(next_td); + if (next_td == 0) + set_td_endofchain(cur_td); + + return head; +} /* ohci_build_td_chain() */ + + +/* + * Compute the number of bytes that have been transferred on a given + * TD. Do not call this on TDs that are active on the host + * controller. + */ +static __u16 ohci_td_bytes_done(struct ohci_td *td) +{ + __u16 result; + __u32 bus_data_start, bus_data_end; + + bus_data_start = virt_to_bus(td->data); + if (!td->data || !bus_data_start) + return 0; + + /* if cur_buf is 0, all data has been transferred */ + if (!td->cur_buf) { + /* XXX this could be wrong for transfers > 1 page */ + return le32_to_cpup(&td->buf_end) - bus_data_start + 1; + } + + bus_data_end = le32_to_cpup(&td->cur_buf); + + /* is it on the same page? */ + if ((bus_data_start & ~0xfff) == (bus_data_end & ~0xfff)) { + result = bus_data_end - bus_data_start; + } else { + /* compute the amount transferred on the first page */ + result = 0x1000 - (bus_data_start & 0xfff); + /* add the amount done in the second page */ + result += (bus_data_end & 0xfff); + } + + return result; +} /* ohci_td_bytes_done() */ + + /********************************** * OHCI interrupt list operations * **********************************/ @@ -396,276 +925,631 @@ inline struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, * * Period is desired polling interval in ms. The closest, shorter * match will be used. Powers of two from 1-32 are supported by OHCI. + * + * Returns: a "handle pointer" that release_irq can use to stop this + * interrupt. (It's really a pointer to the TD). NULL = error. */ -static int ohci_request_irq(struct usb_device *usb, unsigned int pipe, +static void* ohci_request_irq(struct usb_device *usb, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) { struct ohci_device *dev = usb_to_ohci(usb); struct ohci_td *td; struct ohci_ed *interrupt_ed; /* endpoint descriptor for this irq */ + int maxps = usb_maxpacket(usb, pipe); + unsigned long flags; + + /* Get an ED and TD */ + interrupt_ed = ohci_get_periodic_ed(dev, period, pipe, 0); + if (interrupt_ed == 0) { + interrupt_ed = ohci_get_free_ed(dev); + if (!interrupt_ed) { + printk(KERN_ERR "Out of EDs on device %p in ohci_request_irq\n", dev); + return NULL; + } + + /* + * Set the max packet size, device speed, endpoint number, usb + * device number (function address), and type of TD. + */ + ohci_fill_ed(dev, interrupt_ed, maxps, usb_pipeslow(pipe), + usb_pipe_endpdev(pipe), 0 /* normal TDs */); + interrupt_ed->status &= cpu_to_le32(~OHCI_ED_SKIP); + + /* Assimilate the new ED into the collective */ + ohci_add_periodic_ed(dev->ohci, interrupt_ed, period); + } +#ifdef OHCI_DEBUG + if (MegaDebug) { + printk(KERN_DEBUG "ohci_request irq: using ED %p [%x %x %x %x]\n", + interrupt_ed, FIELDS_OF_ED(interrupt_ed)); + printk(KERN_DEBUG " for dev %d pipe %x period %d\n", usb->devnum, + pipe, period); + } +#endif + + td = ohci_get_free_td(dev); + if (!td) { + printk(KERN_ERR "Out of TDs in ohci_request_irq\n"); + ohci_free_ed(interrupt_ed); + return NULL; + } + + /* Fill in the TD */ + if (maxps > sizeof(dev->data)) + maxps = sizeof(dev->data); + ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)), + TOGGLE_AUTO, + OHCI_TD_ROUND, + dev->data, maxps, + dev_id, handler); + set_td_endofchain(td); /* - * Pick a good frequency endpoint based on the requested period + * Put the TD onto our ED and make sure its ready to run */ - interrupt_ed = &dev->ohci->root_hub->ed[ms_to_ed_int(period)]; + td->next_td = 0; + spin_lock_irqsave(&ohci_edtd_lock, flags); + td = ohci_add_tds_to_ed(td, interrupt_ed); + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + + return (void*)td; +} /* ohci_request_irq() */ + +/* + * Release an interrupt handler previously allocated using + * ohci_request_irq. This function does no validity checking, so make + * sure you're not releasing an already released handle as it may be + * in use by something else.. + * + * This function can NOT be called from an interrupt. + */ +int ohci_release_irq(void* handle) +{ + struct ohci_device *dev; + struct ohci_td *int_td; + struct ohci_ed *int_ed; + +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "usb-ohci: Releasing irq handle %p\n", handle); +#endif + + int_td = (struct ohci_td*)handle; + if (int_td == NULL) + return USB_ST_INTERNALERROR; + + dev = usb_to_ohci(int_td->usb_dev); + int_ed = int_td->ed; + + ohci_remove_periodic_ed(dev->ohci, int_ed); + + /* Tell the driver that the IRQ has been killed. */ + /* Passing NULL in the "buffer" void* along with the + * USB_ST_REMOVED status is the signal. */ + if (int_td->completed != NULL) + int_td->completed(USB_ST_REMOVED, NULL, 0, int_td->dev_id); + + /* Free the ED (& TD) */ + ohci_free_ed(int_ed); + + return USB_ST_NOERROR; +} /* ohci_release_irq() */ + + +/************************************ + * OHCI control transfer operations * + ************************************/ + +static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); + +/* + * This is the handler that gets called when a control transaction + * completes. + * + * This function is called from the interrupt handler. + */ +static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id) +{ + /* pass the TDs completion status back to control_msg */ + if (dev_id) { + int *completion_status = (int *)dev_id; + *completion_status = stats; + } + + wake_up(&control_wakeup); + return 0; +} /* ohci_control_completed() */ + + +/* + * Send or receive a control message on a "pipe" + * + * The cmd parameter is a pointer to the 8 byte setup command to be + * sent. + * + * A control message contains: + * - The command itself + * - An optional data phase (if len > 0) + * - Status complete phase + * + * This function can NOT be called from an interrupt. + */ +static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, + devrequest *cmd, void *data, int len) +{ + struct ohci_device *dev = usb_to_ohci(usb); + struct ohci_ed *control_ed = ohci_get_free_ed(dev); + struct ohci_td *setup_td, *data_td, *status_td; + DECLARE_WAITQUEUE(wait, current); + int completion_status = -1; + devrequest our_cmd; + + /* byte-swap fields of cmd if necessary */ + our_cmd = *cmd; + cpu_to_le16s(&our_cmd.value); + cpu_to_le16s(&our_cmd.index); + cpu_to_le16s(&our_cmd.length); + +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "ohci_control_msg %p (ohci_dev: %p) pipe %x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len); +#endif + if (!control_ed) { + printk(KERN_ERR "usb-ohci: couldn't get ED for dev %p\n", dev); + return -1; + } + + /* get a TD to send this control message with */ + setup_td = ohci_get_free_td(dev); + if (!setup_td) { + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev); + ohci_free_ed(control_ed); + return -1; + } /* * Set the max packet size, device speed, endpoint number, usb * device number (function address), and type of TD. * - * FIXME: Isochronous transfers need a pool of special 32 byte - * TDs (32 byte aligned) in order to be supported. */ - interrupt_ed->status = \ - ed_set_maxpacket(usb_maxpacket(pipe)) | - ed_set_speed(usb_pipeslow(pipe)) | - usb_pipe_endpdev(pipe) | - OHCI_ED_F_NORM; + ohci_fill_ed(dev, control_ed, usb_maxpacket(usb,pipe), usb_pipeslow(pipe), + usb_pipe_endpdev(pipe), 0 /* normal TDs */); - td = ohci_get_free_td(dev); - /* FIXME: check for NULL */ + /* + * Build the control TD + */ - /* Fill in the TD */ - ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)), - TOGGLE_AUTO, - OHCI_TD_ROUND, - dev->data, DATA_BUF_LEN, - dev_id, handler); /* - * TODO: be aware that OHCI won't advance out of the 4kb - * page cur_buf started in. It'll wrap around to the start - * of the page... annoying or useful? you decide. + * Set the not accessed condition code, allow odd sized data, + * and set the data transfer type to SETUP. Setup DATA always + * uses a DATA0 packet. * - * We should make sure dev->data doesn't cross a page... + * The setup packet contains a devrequest (usb.h) which + * will always be 8 bytes long. */ + ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0, + OHCI_TD_IOC_OFF, + &our_cmd, 8, /* cmd is always 8 bytes long */ + &completion_status, NULL); + + /* Allocate a TD for the control xfer status */ + status_td = ohci_get_free_td(dev); + if (!status_td) { + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); + ohci_free_td(setup_td); + ohci_free_ed(control_ed); + return -1; + } - /* FIXME: this just guarantees that its the end of the list */ - td->next_td = 0; + /* The control status packet always uses a DATA1 + * Give "dev_id" the address of completion_status so that the + * TDs status can be passed back to us from the IRQ. */ + ohci_fill_new_td(status_td, + td_set_dir_in(usb_pipeout(pipe) | (len == 0)), + TOGGLE_DATA1, + 0 /* flags */, + NULL /* data */, 0 /* data len */, + &completion_status, ohci_control_completed); + set_td_endofchain(status_td); + status_td->next_td = 0; /* end of TDs */ - /* Linus did this. see asm/system.h; scary concept... I don't - * know if its needed here or not but it won't hurt. */ - wmb(); + /* If there is data to transfer, create the chain of data TDs + * followed by the status TD. */ + if (len > 0) { + data_td = ohci_build_td_chain( dev, data, len, + usb_pipeout(pipe), TOGGLE_DATA1, + 1 /* round */, 1 /* autofree */, + &completion_status, NULL /* no handler here */, + virt_to_bus(status_td) ); + if (!data_td) { + printk(KERN_ERR "usb-ohci: couldn't allocate control data TDs for dev %p\n", dev); + ohci_free_td(setup_td); + ohci_free_td(status_td); + ohci_free_ed(control_ed); + return -1; + } + + /* link the to the data & status TDs */ + setup_td->next_td = cpu_to_le32(virt_to_bus(data_td)); + } else { + /* no data TDs, link to the status TD */ + setup_td->next_td = cpu_to_le32(virt_to_bus(status_td)); + } /* - * Put the TD onto our ED + * Add the control TDs to the control ED (setup_td is the first) */ - { - unsigned long flags; - spin_lock_irqsave(&ohci_edtd_lock, flags); - ohci_add_td_to_ed(td, interrupt_ed); - spin_unlock_irqrestore(&ohci_edtd_lock, flags); + setup_td = ohci_add_tds_to_ed(setup_td, control_ed); + control_ed->status &= cpu_to_le32(~OHCI_ED_SKIP); + /* ohci_unhalt_ed(control_ed); */ + +#ifdef OHCI_DEBUG + if (MegaDebug > 1) { + /* complete transaction debugging output (before) */ + printk(KERN_DEBUG " Control ED %lx:\n", virt_to_bus(control_ed)); + show_ohci_ed(control_ed); + printk(KERN_DEBUG " Control TD chain:\n"); + show_ohci_td_chain(setup_td); + printk(KERN_DEBUG " OHCI Controller Status:\n"); + show_ohci_status(dev->ohci); } +#endif -#if 0 - /* Assimilate the new ED into the collective */ /* - * When dynamic ED allocation is done, this call will be - * useful. For now, the correct ED already on the - * controller's proper periodic ED lists was chosen above. + * Start the control transaction.. */ - ohci_add_periodic_ed(dev->ohci, interrupt_ed, period); -#else - /* enable periodic (interrupt) transfers on the HC */ - ohci_start_periodic(dev->ohci); + current->state = TASK_UNINTERRUPTIBLE; + add_wait_queue(&control_wakeup, &wait); + + /* Give the ED to the HC */ + ohci_add_control_ed(dev->ohci, control_ed); + + schedule_timeout(HZ); + + remove_wait_queue(&control_wakeup, &wait); + +#ifdef OHCI_DEBUG + if (completion_status != 0) { + const char *what = (completion_status < 0)? "timed out": + cc_names[completion_status & 0xf]; + printk(KERN_DEBUG "ohci_control_msg: %s on pipe %x\n", + what, pipe); + } + if (MegaDebug) { + printk(KERN_DEBUG "ctrl msg %x %x %x %x %x on pipe %x", + cmd->requesttype, cmd->request, cmd->value, cmd->index, + cmd->length, pipe); + if (usb_pipeout(pipe)) + printk(" data (%d)", len); + else + printk(" returned (%d)", len); + if (len > 0) { + int i; + unsigned char *q = data; + for (i = 0; i < len; ++i) { + if (i % 16 == 0) + printk("\n" KERN_DEBUG); + printk(" %x", q[i]); + } + } + printk("\n"); + } + if (MegaDebug && completion_status < 0) { + printk(KERN_DEBUG "control_ed at %p:\n", control_ed); + show_ohci_ed(control_ed); + if (ed_head_td(control_ed) != ed_tail_td(control_ed)) + show_ohci_td_chain(bus_to_virt(ed_head_td(control_ed))); + printk(KERN_DEBUG "setup TD at %p:\n", setup_td); + show_ohci_td(setup_td); + } + + if (MegaDebug > 1) { + /* complete transaction debugging output (after) */ + printk(KERN_DEBUG " *after* Control ED %lx:\n", virt_to_bus(control_ed)); + show_ohci_ed(control_ed); + printk(KERN_DEBUG " *after* Control TD chain:\n"); + show_ohci_td_chain(setup_td); + printk(KERN_DEBUG " *after* OHCI Controller Status:\n"); + show_ohci_status(dev->ohci); + } #endif - return 0; -} /* ohci_request_irq() */ + /* no TD cleanup, the TDs were auto-freed as they finished */ + + /* remove the control ED from the HC */ + ohci_remove_control_ed(dev->ohci, control_ed); + ohci_free_ed(control_ed); /* return it to the pool */ + + if (completion_status < 0) + completion_status = USB_ST_TIMEOUT; + return completion_status; +} /* ohci_control_msg() */ + +/********************************************************************** + * Bulk transfer processing + **********************************************************************/ /* - * Control thread operations: + * Internal state for an ohci_bulk_request */ -static struct wait_queue *control_wakeup; +struct ohci_bulk_request_state { + struct usb_device *usb_dev; + unsigned int pipe; /* usb "pipe" */ + void *data; /* ptr to data */ + int length; /* length to transfer */ + int _bytes_done; /* bytes transferred so far */ + unsigned long *bytes_transferred_p; /* where to increment */ + void *dev_id; /* pass to the completion handler */ + usb_device_irq completion; /* completion handler */ +}; /* - * This is the handler that gets called when a control transaction - * completes. - * - * This function is called from the interrupt handler. + * this handles the individual TDs of a (possibly) larger bulk + * request. It keeps track of the total bytes transferred, calls the + * final completion handler, etc. */ -static int ohci_control_completed(int stats, void *buffer, void *dev_id) +static int ohci_bulk_td_handler(int stats, void *buffer, int len, void *dev_id) { - wake_up(&control_wakeup); - return 0; -} /* ohci_control_completed() */ + struct ohci_bulk_request_state *req; + + req = (struct ohci_bulk_request_state *) dev_id; + +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "ohci_bulk_td_handler stats %x, buffer %p, len %d, req %p\n", stats, buffer, len, req); +#endif + + /* only count TDs that were completed successfully */ + if (stats == USB_ST_NOERROR) + req->_bytes_done += len; + +#ifdef OHCI_DEBUG + if (MegaDebug && req->_bytes_done) { + int i; + printk(KERN_DEBUG " %d bytes, bulk data:", req->_bytes_done); + for (i = 0; i < 16 && i < req->_bytes_done; ++i) + printk(" %.2x", ((unsigned char *)buffer)[i]); + if (i < req->_bytes_done) + printk(" ..."); + printk("\n"); + } +#endif + + /* call the real completion handler when done or on an error */ + if ((stats != USB_ST_NOERROR) || + (req->_bytes_done >= req->length && req->completion != NULL)) { + *req->bytes_transferred_p += req->_bytes_done; +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "usb-ohci: bulk request %p ending\n", req); +#endif + req->completion(stats, buffer, req->_bytes_done, req->dev_id); + } + + return 0; /* do not re-queue the TD */ +} /* ohci_bulk_td_handler() */ /* - * Send or receive a control message on a "pipe" + * Request to send or receive bulk data. The completion() function + * will be called when the transfer has completed or been aborted due + * to an error. * - * The cmd parameter is a pointer to the 8 byte setup command to be - * sent. FIXME: This is a devrequest in usb.h. The function - * should be updated to accept a devrequest* instead of void*.. + * bytes_transferred_p is a pointer to an integer that will be + * set to the number of bytes that have been successfully + * transferred. The interrupt handler will update it after each + * internal TD completes successfully. * - * A control message contains: - * - The command itself - * - An optional data phase (if len > 0) - * - Status complete phase + * This function can NOT be called from an interrupt (?) + * (TODO: verify & fix this if needed). + * + * Returns: a pointer to the ED being used for this request. At the + * moment, removing & freeing it is the responsibilty of the caller. */ -static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, void *cmd, void *data, int len) +static struct ohci_ed* ohci_request_bulk(struct ohci_bulk_request_state *bulk_request) { - struct ohci_device *dev = usb_to_ohci(usb); - /* - * ideally dev->ed should be linked into the root hub's - * control_ed list and used instead of just using it directly. - * This could present a problem as is with more than one - * device. (but who wants to use a keyboard AND a mouse - * anyways? ;) - */ - struct ohci_ed *control_ed = &dev->ohci->root_hub->ed[ED_CONTROL]; - struct ohci_td *setup_td, *data_td, *status_td; - struct wait_queue wait = { current, NULL }; + /* local names for the readonly fields */ + struct usb_device *usb_dev = bulk_request->usb_dev; + unsigned int pipe = bulk_request->pipe; + void *data = bulk_request->data; + int len = bulk_request->length; + + struct ohci_device *dev = usb_to_ohci(usb_dev); + struct ohci_ed *bulk_ed; + struct ohci_td *head_td; + unsigned long flags; -#if 0 - printk(KERN_DEBUG "entering ohci_control_msg %p (ohci_dev: %p) pipe 0x%x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len); +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "ohci_request_bulk(%p) ohci_dev %p, completion %p, pipe %x, data %p, len %d\n", bulk_request, dev, bulk_request->completion, pipe, data, len); #endif - /* - * Set the max packet size, device speed, endpoint number, usb - * device number (function address), and type of TD. - * - */ - control_ed->status = \ - ed_set_maxpacket(usb_maxpacket(pipe)) | - ed_set_speed(usb_pipeslow(pipe)) | - usb_pipe_endpdev(pipe) | - OHCI_ED_F_NORM; + bulk_ed = ohci_get_free_ed(dev); + if (!bulk_ed) { + printk(KERN_ERR "usb-ohci: couldn't get ED for dev %p\n", dev); + return NULL; + } - /* - * Build the control TD - */ + /* allocate & fill in the TDs for this request */ + head_td = ohci_build_td_chain(dev, data, len, usb_pipeout(pipe), + TOGGLE_AUTO, + 0 /* round not required */, 1 /* autofree */, + bulk_request, /* dev_id: the bulk_request */ + ohci_bulk_td_handler, + 0 /* no additional TDs */); + if (!head_td) { + printk(KERN_ERR "usb-ohci: couldn't get TDs for dev %p\n", dev); + ohci_free_ed(bulk_ed); + return NULL; + } - /* get a TD to send this control message with */ - setup_td = ohci_get_free_td(dev); - /* TODO check for NULL */ + /* Set the max packet size, device speed, endpoint number, usb + * device number (function address), and type of TD. */ + ohci_fill_ed(dev, bulk_ed, + usb_maxpacket(usb_dev, pipe), + usb_pipeslow(pipe), + usb_pipe_endpdev(pipe), 0 /* bulk uses normal TDs */); + + /* initialize the toggle carry */ + if (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) + ohci_ed_set_carry(bulk_ed); + + /* initialize the internal counter */ + bulk_request->_bytes_done = 0; /* - * Set the not accessed condition code, allow odd sized data, - * and set the data transfer type to SETUP. Setup DATA always - * uses a DATA0 packet. - * - * The setup packet contains a devrequest (usb.h) which - * will always be 8 bytes long. FIXME: the cmd parameter - * should be a pointer to one of these instead of a void* !!! + * Add the TDs to the ED */ - ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0, - OHCI_TD_IOC_OFF, - cmd, 8, /* cmd is always 8 bytes long */ - NULL, NULL); + spin_lock_irqsave(&ohci_edtd_lock, flags); + head_td = ohci_add_tds_to_ed(head_td, bulk_ed); + bulk_ed->status &= cpu_to_le32(~OHCI_ED_SKIP); + /* ohci_unhalt_ed(bulk_ed); */ + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + +#ifdef OHCI_DEBUG + if (MegaDebug > 1) { + /* complete request debugging output (before) */ + printk(KERN_DEBUG " Bulk ED %lx:\n", virt_to_bus(bulk_ed)); + show_ohci_ed(bulk_ed); + printk(KERN_DEBUG " Bulk TDs %lx:\n", virt_to_bus(head_td)); + show_ohci_td_chain(head_td); + } +#endif - /* allocate the next TD */ - data_td = ohci_get_free_td(dev); /* TODO check for NULL */ + /* Give the ED to the HC */ + ohci_add_bulk_ed(dev->ohci, bulk_ed); - /* link to the next TD */ - setup_td->next_td = virt_to_bus(data_td); + return bulk_ed; +} /* ohci_request_bulk() */ - if (len > 0) { - /* build the Control DATA TD, it starts with a DATA1. */ - ohci_fill_new_td(data_td, td_set_dir_out(usb_pipeout(pipe)), - TOGGLE_DATA1, - OHCI_TD_ROUND | OHCI_TD_IOC_OFF, - data, len, - NULL, NULL); +static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); - /* - * XXX we should check that the data buffer doesn't - * cross a 4096 byte boundary. If so, it needs to be - * copied into a single 4096 byte aligned area for the - * OHCI's TD logic to see it all, or multiple TDs need - * to be made for each page. - * - * It's not likely a control transfer will run into - * this problem.. (famous last words) - */ - status_td = ohci_get_free_td(dev); /* TODO check for NULL */ - data_td->next_td = virt_to_bus(status_td); - } else { - status_td = data_td; /* no data_td, use it for status */ +static int ohci_bulk_msg_completed(int stats, void *buffer, int len, void *dev_id) +{ +#ifdef OHCI_DEBUG + if (MegaDebug) + printk("ohci_bulk_msg_completed %x, %p, %d, %p\n", stats, buffer, len, dev_id); +#endif + if (dev_id != NULL) { + int *completion_status = (int *)dev_id; + *completion_status = stats; } - /* The control status packet always uses a DATA1 */ - ohci_fill_new_td(status_td, - td_set_dir_in(usb_pipeout(pipe) | (len == 0)), - TOGGLE_DATA1, - 0, - NULL, 0, - NULL, ohci_control_completed); - status_td->next_td = 0; /* end of TDs */ + wake_up(&bulk_wakeup); + return 0; /* don't requeue the TD */ +} /* ohci_bulk_msg_completed() */ + + +static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, unsigned long *bytes_transferred_p) +{ + DECLARE_WAITQUEUE(wait, current); + int completion_status = USB_ST_INTERNALERROR; + struct ohci_bulk_request_state req; + struct ohci_ed *req_ed; + +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "ohci_bulk_msg %p pipe %x, data %p, len %d, bytes_transferred %p\n", + usb_dev, pipe, data, len, bytes_transferred_p); +#endif + + /* initialize bytes transferred to nothing */ + *bytes_transferred_p = 0; + + /* Hopefully this is similar to the "URP" (USB Request Packet) code + * that michael gee is working on... */ + req.usb_dev = usb_dev; + req.pipe = pipe; + req.data = data; + req.length = len; + req.bytes_transferred_p = bytes_transferred_p; + req.dev_id = &completion_status; + req.completion = ohci_bulk_msg_completed; + if (bytes_transferred_p) + *bytes_transferred_p = 0; + + if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe)) + && usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80))) + return USB_ST_STALL; /* - * Start the control transaction.. + * Start the transaction.. */ current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&control_wakeup, &wait); + add_wait_queue(&bulk_wakeup, &wait); - /* - * Add the chain of 2-3 control TDs to the control ED's TD list - */ - { - unsigned long flags; - spin_lock_irqsave(&ohci_edtd_lock, flags); - ohci_add_td_to_ed(setup_td, control_ed); - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - } + req_ed = ohci_request_bulk(&req); -#if 0 - /* complete transaction debugging output (before) */ - printk(KERN_DEBUG " Control ED %lx:\n", virt_to_bus(control_ed)); - show_ohci_ed(control_ed); - printk(KERN_DEBUG " Setup TD %lx:\n", virt_to_bus(setup_td)); - show_ohci_td(setup_td); - if (data_td != status_td) { - printk(KERN_DEBUG " Data TD %lx:\n", virt_to_bus(data_td)); - show_ohci_td(data_td); - } - printk(KERN_DEBUG " Status TD %lx:\n", virt_to_bus(status_td)); - show_ohci_td(status_td); + /* FIXME this should to wait for a caller specified time... */ + schedule_timeout(HZ*5); + + /* completion_status will only stay in this state of the + * request never finished */ + if (completion_status == USB_ST_INTERNALERROR) { + struct ohci_device *dev = usb_to_ohci(usb_dev); + struct ohci_regs *regs = dev->ohci->regs; + +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_bulk_msg timing out\n"); #endif + /* XXX This code should go into a function used to stop + * a previously requested bulk transfer. -greg */ + + /* stop the transfer & collect the number of bytes */ + ohci_wait_for_ed_safe(regs, req_ed, HCD_ED_BULK); + + /* Get the number of bytes transferred out of the head TD + * on the ED if it didn't finish while we were waiting. */ + if ( ed_head_td(req_ed) && + (ed_head_td(req_ed) != ed_tail_td(req_ed)) ) { + struct ohci_td *partial_td; + partial_td = bus_to_virt(ed_head_td(req_ed)); + +#ifdef OHCI_DEBUG + if (MegaDebug) { + show_ohci_td(partial_td); + } +#endif + /* Record the bytes as transferred */ + *bytes_transferred_p += ohci_td_bytes_done(partial_td); + + /* If there was an unreported error, return it. + * Otherwise return a timeout */ + completion_status = OHCI_TD_CC_GET(partial_td->info); + if (completion_status == USB_ST_NOERROR) { + completion_status = USB_ST_TIMEOUT; + } + } - /* Give the ED to the HC */ - ohci_add_control_ed(dev->ohci, control_ed); + } - /* FIXME: - * this should really check to see that the transaction completed. - */ - schedule_timeout(HZ/10); + remove_wait_queue(&bulk_wakeup, &wait); - remove_wait_queue(&control_wakeup, &wait); + /* remove the ED from the HC */ + ohci_remove_bulk_ed(usb_to_ohci(usb_dev)->ohci, req_ed); -#if 0 - /* complete transaction debugging output (after) */ - printk(KERN_DEBUG " (after) Control ED:\n"); - show_ohci_ed(control_ed); - printk(KERN_DEBUG " (after) Setup TD:\n"); - show_ohci_td(setup_td); - if (data_td != status_td) { - printk(KERN_DEBUG " (after) Data TD:\n"); - show_ohci_td(data_td); - } - printk(KERN_DEBUG " (after) Status TD:\n"); - show_ohci_td(status_td); + /* save the toggle value back into the usb_dev */ + usb_settoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), + ohci_ed_carry(req_ed)); + + ohci_free_ed(req_ed); /* return it to the pool */ + +#ifdef OHCI_DEBUG + if (completion_status != 0 || MegaDebug) + printk(KERN_DEBUG "ohci_bulk_msg done, status %x (bytes_transferred = %ld).\n", + completion_status, *bytes_transferred_p); #endif - /* clean up incase it failed */ - /* XXX only do this if their ed pointer still points to control_ed - * incase they've been reclaimed and used by something else - * already. -greg */ - ohci_remove_td_from_ed(setup_td, control_ed); - ohci_remove_td_from_ed(data_td, control_ed); - ohci_remove_td_from_ed(status_td, control_ed); + return completion_status; +} /* ohci_bulk_msg() */ - /* remove the control ED */ - ohci_remove_control_ed(dev->ohci, control_ed); -#if 0 - printk(KERN_DEBUG "leaving ohci_control_msg\n"); -#endif +/* .......... */ - return ohci_td_result(dev, status_td); -} /* ohci_control_msg() */ /* @@ -675,6 +1559,7 @@ static struct usb_device *ohci_usb_allocate(struct usb_device *parent) { struct usb_device *usb_dev; struct ohci_device *dev; + int idx; /* * Allocate the generic USB device @@ -696,6 +1581,12 @@ static struct usb_device *ohci_usb_allocate(struct usb_device *parent) memset(dev, 0, sizeof(*dev)); + /* Initialize all EDs in a new device with the skip flag so that + * they are ignored by the controller until set otherwise. */ + for (idx = 0; idx < NUM_EDS; ++idx) { + dev->ed[idx].status = cpu_to_le32(OHCI_ED_SKIP); + } + /* * Link them together */ @@ -723,12 +1614,42 @@ static struct usb_device *ohci_usb_allocate(struct usb_device *parent) */ static int ohci_usb_deallocate(struct usb_device *usb_dev) { - kfree(usb_to_ohci(usb_dev)); - kfree(usb_dev); + struct ohci_device *dev = usb_to_ohci(usb_dev); + + ohci_remove_device(dev->ohci, usb_dev->devnum); + + /* kfree(usb_to_ohci(usb_dev)); */ + /* kfree(usb_dev); */ return 0; } +static void *ohci_alloc_isochronous (struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id) +{ + return NULL; +} + +static void ohci_delete_isochronous (struct usb_device *dev, void *_isodesc) +{ + return; +} + +static int ohci_sched_isochronous (struct usb_device *usb_dev, void *_isodesc, void *_pisodesc) +{ + return USB_ST_NOTSUPPORTED; +} + +static int ohci_unsched_isochronous (struct usb_device *usb_dev, void *_isodesc) +{ + return USB_ST_NOTSUPPORTED; +} + +static int ohci_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) +{ + return USB_ST_NOTSUPPORTED; +} + + /* * functions for the generic USB driver */ @@ -736,7 +1657,14 @@ struct usb_operations ohci_device_operations = { ohci_usb_allocate, ohci_usb_deallocate, ohci_control_msg, + ohci_bulk_msg, ohci_request_irq, + ohci_release_irq, + ohci_alloc_isochronous, + ohci_delete_isochronous, + ohci_sched_isochronous, + ohci_unsched_isochronous, + ohci_compress_isochronous }; @@ -749,24 +1677,25 @@ struct usb_operations ohci_device_operations = { */ static int reset_hc(struct ohci *ohci) { - int timeout = 1000; /* prevent an infinite loop */ - -#if 0 - printk(KERN_DEBUG "usb-ohci: resetting HC %p\n", ohci); -#endif + int timeout = 10000; /* prevent an infinite loop */ writel(~0x0, &ohci->regs->intrdisable); /* Disable HC interrupts */ + /* this seems to be needed for the lucent controller on powerbooks... */ + writel(0, &ohci->regs->control); /* Move to reset state */ writel(1, &ohci->regs->cmdstatus); /* HC Reset */ - writel_mask(0x3f, &ohci->regs->control); /* move to UsbReset state */ + udelay(100); while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) { if (!--timeout) { - printk("usb-ohci: USB HC reset timed out!\n"); + printk(KERN_ERR "usb-ohci: USB HC reset timed out!\n"); return -1; } udelay(1); } + writel_mask(0x3f, &ohci->regs->control); /* move to UsbReset state */ + wait_ms(10); /* give the bus time to be reset */ + printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci); return 0; @@ -779,29 +1708,34 @@ static int reset_hc(struct ohci *ohci) static int start_hc(struct ohci *ohci) { int ret = 0; - int fminterval; + int fminterval, pot; + __u32 what_to_enable; - fminterval = readl(&ohci->regs->fminterval) & 0x3fff; -#if 0 - printk(KERN_DEBUG "entering start_hc %p\n", ohci); -#endif + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); if (reset_hc(ohci) < 0) return -1; /* restore registers cleared by the reset */ - writel(virt_to_bus(ohci->root_hub->hcca), &ohci->regs->hcca); + writel(virt_to_bus(root_hub->hcca), &ohci->regs->hcca); /* - * XXX Should fminterval also be set here? - * The spec suggests 0x2edf [11,999]. (FIXME: make this a constant) + * fminterval has to be 11999 (it can be adjusted +/- 1 + * to sync with other things if necessary). */ - fminterval |= (0x2edf << 16); - writel(fminterval, &ohci->regs->fminterval); + fminterval = 11999; + /* Start periodic transfers at 90% of fminterval (fmremaining * counts down; this will put them in the first 10% of the * frame). */ - writel((0x2edf*9)/10, &ohci->regs->periodicstart); + writel((fminterval * 9) / 10, &ohci->regs->periodicstart); + + /* Set largest data packet counter and frame interval. */ + fminterval |= ((fminterval - 210) * 6 / 7) << 16; + writel(fminterval, &ohci->regs->fminterval); + + /* Set low-speed threshold (value from MacOS) */ + writel(1576, &ohci->regs->lsthresh); /* * FNO (frame number overflow) could be enabled... they @@ -809,20 +1743,40 @@ static int start_hc(struct ohci *ohci) * useful for debugging and as a bus heartbeat. -greg */ /* Choose the interrupts we care about */ - writel( OHCI_INTR_MIE | /* OHCI_INTR_RHSC | */ - OHCI_INTR_WDH | OHCI_INTR_FNO, - &ohci->regs->intrenable); + what_to_enable = OHCI_INTR_MIE | +#ifdef OHCI_RHSC_INT + OHCI_INTR_RHSC | +#endif + /* | OHCI_INTR_FNO */ + OHCI_INTR_WDH; + writel( what_to_enable, &ohci->regs->intrenable); /* Enter the USB Operational state & start the frames a flowing.. */ - writel_set(OHCI_USB_OPER, &ohci->regs->control); + writel(OHCI_USB_OPER, &ohci->regs->control); /* Enable control lists */ writel_set(OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, &ohci->regs->control); + /* Force global power enable -gal@cs.uni-magdeburg.de */ + /* + * This turns on global power switching for all the ports + * and tells the HC that all of the ports should be powered on + * all of the time. + * + * TODO: This could be battery draining for laptops.. We + * should implement power switching. + */ + writel_set( OHCI_ROOT_A_NPS, &ohci->regs->roothub.a ); + writel_mask( ~(OHCI_ROOT_A_PSM), &ohci->regs->roothub.a ); + /* Turn on power to the root hub ports (thanks Roman!) */ writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status ); - printk("usb-ohci: host controller operational\n"); + /* wait for power-on to power-good time */ + pot = readl(&ohci->regs->roothub.a) >> 24; /* power-on time (* 2ms) */ + wait_ms(pot * 2); + + printk(KERN_INFO "usb-ohci: host controller operational\n"); return ret; } /* start_hc() */ @@ -831,14 +1785,14 @@ static int start_hc(struct ohci *ohci) /* * Reset a root hub port */ -static void ohci_reset_port(struct ohci *ohci, unsigned int port) +static int ohci_reset_port(struct ohci *ohci, unsigned int port) { int status; /* Don't allow overflows. */ if (port >= MAX_ROOT_PORTS) { - printk("usb-ohci: bad port #%d in ohci_reset_port\n", port); - port = MAX_ROOT_PORTS-1; + printk(KERN_ERR "usb-ohci: bad port #%d in ohci_reset_port\n", port); + return -1; } writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); /* Reset */ @@ -846,20 +1800,21 @@ static void ohci_reset_port(struct ohci *ohci, unsigned int port) /* * Wait for the reset to complete. */ - wait_ms(10); + wait_ms(20); /* check port status to see that the reset completed */ status = readl(&ohci->regs->roothub.portstatus[port]); if (status & PORT_PRS) { /* reset failed, try harder? */ - printk("usb-ohci: port %d reset failed, retrying\n", port); - writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); - wait_ms(50); + printk(KERN_ERR "usb-ohci: port %d reset failed\n", port); + show_ohci_status(ohci); + return -1; } /* TODO we might need to re-enable the port here or is that * done elsewhere? */ + return 0; } /* ohci_reset_port */ @@ -870,31 +1825,47 @@ static void ohci_connect_change(struct ohci * ohci, int port) { struct usb_device *usb_dev; struct ohci_device *dev; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); /* memory I/O address of the port status register */ - void *portaddr = &ohci->regs->roothub.portstatus[port]; + __u32 *portaddr = &ohci->regs->roothub.portstatus[port]; int portstatus; - printk(KERN_DEBUG "ohci_connect_change(%p, %d)\n", ohci, port); - /* * Because of the status change we have to forget * everything we think we know about the device * on this root hub port. It may have changed. */ - usb_disconnect(ohci->root_hub->usb->children + port); + usb_disconnect(root_hub->usb->children + port); portstatus = readl(portaddr); +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_connect_change on port %d, status=%x\n", + port, portstatus); +#endif + /* disable the port if nothing is connected */ if (!(portstatus & PORT_CCS)) { writel(PORT_CCS, portaddr); + /* We need to reset the CSC bit -after- disabling the + * port because it causes the CSC bit to come on + * again... */ + wait_ms(20); + writel(PORT_CSC, portaddr); +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci port %d disabled, nothing connected.\n", port); +#endif return; } + if (ohci_reset_port(ohci, port) < 0) + return; + wait_ms(10); /* give the device a while to get reset */ + /* * Allocate a device for the new thingy that's been attached */ - usb_dev = ohci_usb_allocate(ohci->root_hub->usb); + usb_dev = ohci_usb_allocate(root_hub->usb); dev = usb_dev->hcpriv; dev->ohci = ohci; @@ -902,10 +1873,7 @@ static void ohci_connect_change(struct ohci * ohci, int port) usb_connect(dev->usb); /* link it into the bus's device tree */ - ohci->root_hub->usb->children[port] = usb_dev; - - wait_ms(200); /* wait for powerup; XXX is this needed? */ - ohci_reset_port(ohci, port); + root_hub->usb->children[port] = usb_dev; /* Get information on speed by using LSD */ usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0; @@ -928,23 +1896,24 @@ static void ohci_check_configuration(struct ohci *ohci) struct ohci_regs *regs = ohci->regs; int num = 0; int maxport = readl(&ohci->regs->roothub) & 0xff; + __u32 rh_change_flags = PORT_CSC | PORT_PESC; /* root hub status changes */ -#if 1 - printk(KERN_DEBUG "entering ohci_check_configuration %p\n", ohci); +#ifdef OHCI_DEBUG + if (MegaDebug) + printk(KERN_DEBUG "entering ohci_check_configuration %p\n", ohci); #endif do { - if (readl(®s->roothub.portstatus[num]) & PORT_CSC) { - /* reset the connect status change bit */ - writel(PORT_CSC, ®s->roothub.portstatus[num]); + __u32 *portstatus_p = ®s->roothub.portstatus[num]; + if (readl(portstatus_p) & rh_change_flags) { + /* acknowledge the root hub status changes */ + writel_set(rh_change_flags, portstatus_p); + /* disable the port if nothing is on it */ /* check the port for a nifty device */ ohci_connect_change(ohci, num); } } while (++num < maxport); -#if 0 - printk(KERN_DEBUG "leaving ohci_check_configuration %p\n", ohci); -#endif } /* ohci_check_configuration() */ @@ -957,19 +1926,21 @@ static void ohci_check_configuration(struct ohci *ohci) */ static void ohci_root_hub_events(struct ohci *ohci) { - if (waitqueue_active(&ohci_configure)) { - int num = 0; - int maxport = ohci->root_hub->usb->maxchild; + int num = 0; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); + int maxport = root_hub->usb->maxchild; - do { - if (readl(&ohci->regs->roothub.portstatus[num]) & - PORT_CSC) { - if (waitqueue_active(&ohci_configure)) - wake_up(&ohci_configure); - return; - } - } while (++num < maxport); - } + if (!waitqueue_active(&ohci_configure)) + return; + do { + __u32 *portstatus_p = &ohci->regs->roothub.portstatus[num]; + if (readl(portstatus_p) & PORT_CSC) { + if (waitqueue_active(&ohci_configure)) + wake_up(&ohci_configure); + return; + } + } while (++num < maxport); + } /* ohci_root_hub_events() */ @@ -985,19 +1956,19 @@ static void ohci_root_hub_events(struct ohci *ohci) static struct ohci_td * ohci_reverse_donelist(struct ohci * ohci) { __u32 td_list_hc; - struct ohci_hcca *hcca = ohci->root_hub->hcca; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); + struct ohci_hcca *hcca = root_hub->hcca; struct ohci_td *td_list = NULL; struct ohci_td *td_rev = NULL; - - td_list_hc = hcca->donehead & 0xfffffff0; + + td_list_hc = le32_to_cpup(&hcca->donehead) & 0xfffffff0; hcca->donehead = 0; while(td_list_hc) { td_list = (struct ohci_td *) bus_to_virt(td_list_hc); td_list->next_dl_td = td_rev; - td_rev = td_list; - td_list_hc = td_list->next_td & 0xfffffff0; + td_list_hc = le32_to_cpup(&td_list->next_td) & 0xfffffff0; } return td_list; @@ -1014,6 +1985,8 @@ static void ohci_reap_donelist(struct ohci *ohci) { struct ohci_td *td; /* used for walking the list */ + /* um... isn't this dangerous to do in an interrupt handler? -greg */ + /* nope. -paulus */ spin_lock(&ohci_edtd_lock); /* create the FIFO ordered donelist */ @@ -1021,27 +1994,85 @@ static void ohci_reap_donelist(struct ohci *ohci) while (td != NULL) { struct ohci_td *next_td = td->next_dl_td; + int cc = OHCI_TD_CC_GET(le32_to_cpup(&td->info)); + struct ohci_ed *ed = td->ed; + + if (td_dummy(*td)) + printk(KERN_ERR "yikes! reaping a dummy TD\n"); + +#ifdef OHCI_DEBUG + if (cc != 0 && MegaDebug) { + printk(KERN_DEBUG "cc=%s on td %p (ed %p)\n", cc_names[cc], td, ed); + show_ohci_td(td); + show_ohci_ed(ed); + if (ed_head_td(ed) != ed_tail_td(ed)) + show_ohci_td_chain(bus_to_virt(ed_head_td(ed))); + } +#endif + + if (cc == USB_ST_STALL) { + /* mark endpoint as halted */ + usb_endpoint_halt(ed->ohci_dev->usb, ed_get_en(ed)); + } + + if (cc != 0 && ohci_ed_halted(ed) && !td_endofchain(*td)) { + /* + * There was an error on this TD and the ED + * is halted, and this was not the last TD + * of the transaction, so there will be TDs + * to clean off the ED. + */ + struct ohci_td *tail_td = bus_to_virt(ed_tail_td(ed)); + struct ohci_td *ntd; + + ohci_free_td(td); + td = ntd = bus_to_virt(ed_head_td(ed)); + while (td != tail_td) { + ntd = bus_to_virt(le32_to_cpup(&td->next_td)); + if (td_endofchain(*td)) + break; + + if (MegaDebug) + printk(KERN_DEBUG "skipping TD %p\n", td); + ohci_free_td(td); + + td = ntd; + } + /* Set the ED head past the ones we cleaned + off, and clear the halted flag */ + if (MegaDebug) + printk(KERN_DEBUG "restarting ED %p at TD %p\n", ed, ntd); + set_ed_head_td(ed, virt_to_bus(ntd)); + ohci_unhalt_ed(ed); + /* If we didn't find an endofchain TD, give up */ + if (td == tail_td) { + td = next_td; + continue; + } + } - /* FIXME: munge td->info into a future standard status format */ /* Check if TD should be re-queued */ if ((td->completed != NULL) && - (td->completed(OHCI_TD_CC_GET(td->info), td->data, td->dev_id))) - { + (td->completed(cc, td->data, ohci_td_bytes_done(td), td->dev_id))) { /* Mark the TD as active again: * Set the not accessed condition code - * FIXME: should this reset OHCI_TD_ERRCNT? + * Reset the Error count */ - td->info |= OHCI_TD_CC_NEW; + td->info |= cpu_to_le32(OHCI_TD_CC_NEW); + clear_td_errorcount(td); + /* reset the toggle field to TOGGLE_AUTO (0) */ + td->info &= cpu_to_le32(~OHCI_TD_DT); /* point it back to the start of the data buffer */ - td->cur_buf = virt_to_bus(td->data); + td->cur_buf = cpu_to_le32(virt_to_bus(td->data)); - /* XXX disabled for debugging reasons right now.. */ /* insert it back on its ED */ - ohci_add_td_to_ed(td, td->ed); + td->next_td = 0; + ohci_add_tds_to_ed(td, ed); } else { /* return it to the pool of free TDs */ - ohci_free_td(td); + if (can_auto_free(*td)) + ohci_free_td(td); } td = next_td; @@ -1051,9 +2082,6 @@ static void ohci_reap_donelist(struct ohci *ohci) } /* ohci_reap_donelist() */ -#if 0 -static int in_int = 0; -#endif /* * Get annoyed at the controller for bothering us. * This pretty much follows the OHCI v1.0a spec, section 5.3. @@ -1062,26 +2090,18 @@ static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) { struct ohci *ohci = __ohci; struct ohci_regs *regs = ohci->regs; - struct ohci_hcca *hcca = ohci->root_hub->hcca; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); + struct ohci_hcca *hcca = root_hub->hcca; __u32 status, context; -#if 0 - /* for debugging to keep IRQs from running away. */ - if (in_int >= 2) - return; - ++in_int; - return; -#endif - /* Save the status of the interrupts that are enabled */ status = readl(®s->intrstatus); status &= readl(®s->intrenable); - /* make context = the interrupt status bits that we care about */ if (hcca->donehead != 0) { context = OHCI_INTR_WDH; /* hcca donehead needs processing */ - if (hcca->donehead & 1) { + if (hcca->donehead & cpu_to_le32(1)) { context |= status; /* other status change to check */ } } else { @@ -1092,21 +2112,32 @@ static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) } } - /* Disable HC interrupts */ + /* Disable HC interrupts */ /* why? - paulus */ writel(OHCI_INTR_MIE, ®s->intrdisable); +#if 0 + /* Only do this for SERIOUS debugging, be sure kern.debug logs + * are not going to the console as this can cause your + * machine to lock up if so... -greg */ + show_ohci_status(ohci); +#endif + /* Process the done list */ if (context & OHCI_INTR_WDH) { /* See which TD's completed.. */ ohci_reap_donelist(ohci); /* reset the done queue and tell the controller */ - hcca->donehead = 0; + hcca->donehead = 0; /* XXX already done in ohci_reverse_donelist */ writel(OHCI_INTR_WDH, ®s->intrstatus); context &= ~OHCI_INTR_WDH; /* mark this as checked */ } +#ifdef OHCI_RHSC_INT + /* NOTE: this is very funky on some USB controllers (ie: it + * doesn't work right). Using the ohci_timer instead to poll + * the root hub is a much better choice. */ /* Process any root hub status changes */ if (context & OHCI_INTR_RHSC) { /* Wake the thread to process root hub events */ @@ -1119,10 +2150,17 @@ static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) * The control thread will re-enable it after it has * checked the root hub status. */ - } else { - /* check the root hub status anyways. Some controllers - * might not generate the interrupt properly. (?) */ - ohci_root_hub_events(ohci); + } +#endif + + /* Start of Frame interrupts, used during safe ED removal */ + if (context & (OHCI_INTR_SF)) { + writel(OHCI_INTR_SF, ®s->intrstatus); + if (waitqueue_active(&start_of_frame_wakeup)) + wake_up(&start_of_frame_wakeup); + /* Do NOT mark the frame start interrupt as checked + * as we don't want to receive any more of them until + * asked. */ } /* Check those "other" pesky bits */ @@ -1151,8 +2189,8 @@ static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) context &= ~OHCI_INTR_OC; /* mark this as checked */ } - /* Mask out any remaining unprocessed interrupts so we don't - * get any more of them. */ + /* Mask out any remaining unprocessed or unmasked interrupts + * so that we don't get any more of them. */ if (context & ~OHCI_INTR_MIE) { writel(context, ®s->intrdisable); } @@ -1179,10 +2217,6 @@ static struct ohci *alloc_ohci(void* mem_base) struct ohci_device *dev; struct usb_device *usb; -#if 0 - printk(KERN_DEBUG "entering alloc_ohci %p\n", mem_base); -#endif - ohci = kmalloc(sizeof(*ohci), GFP_KERNEL); if (!ohci) return NULL; @@ -1215,8 +2249,8 @@ static struct ohci *alloc_ohci(void* mem_base) if (!usb) return NULL; - dev = ohci->root_hub = usb_to_ohci(usb); - + dev = usb_to_ohci(usb); + ohci->bus->root_hub= ohci_to_usb(dev); usb->bus = bus; /* Initialize the root hub */ @@ -1228,7 +2262,8 @@ static struct ohci *alloc_ohci(void* mem_base) * page as that's guaranteed to have a nice boundary. */ dev->hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL); - + memset(dev->hcca, 0, sizeof(struct ohci_hcca)); + /* Tell the controller where the HCCA is */ writel(virt_to_bus(dev->hcca), &ohci->regs->hcca); @@ -1239,66 +2274,62 @@ static struct ohci *alloc_ohci(void* mem_base) /* Get the number of ports on the root hub */ usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; if (usb->maxchild > MAX_ROOT_PORTS) { - printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); + printk(KERN_INFO "usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); usb->maxchild = MAX_ROOT_PORTS; } if (usb->maxchild < 1) { - printk("usb-ohci: Less than one root hub port? Impossible!\n"); + printk(KERN_ERR "usb-ohci: Less than one root hub port? Impossible!\n"); usb->maxchild = 1; } - printk("usb-ohci: %d root hub ports found\n", usb->maxchild); + printk(KERN_DEBUG "usb-ohci: %d root hub ports found\n", usb->maxchild); /* * Initialize the ED polling "tree" (for simplicity's sake in * this driver many nodes in the tree will be identical) */ - dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]); - dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]); - dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]); - dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]); - dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]); + dev->ed[ED_INT_32].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_16])); + dev->ed[ED_INT_16].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_8])); + dev->ed[ED_INT_8].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_4])); + dev->ed[ED_INT_4].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_2])); + dev->ed[ED_INT_2].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_1])); /* * Initialize the polling table to call interrupts at the - * intended intervals. + * intended intervals. Note that these EDs are just + * placeholders. They have their SKIP bit set and are used as + * list heads to insert real EDs onto. */ - dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_32]); + dev->hcca->int_table[0] = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_1])); for (i = 1; i < NUM_INTS; i++) { - if (i & 1) + if (i & 16) dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_16]); - else if (i & 2) + cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_32])); + if (i & 8) dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_8]); - else if (i & 4) + cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_16])); + if (i & 4) dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_4]); - else if (i & 8) + cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_8])); + if (i & 2) dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_2]); - else if (i & 16) + cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_4])); + if (i & 1) dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_1]); + cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_2])); } /* - * Tell the controller where the control and bulk lists are + * Tell the controller where the control and bulk lists are. * The lists start out empty. */ writel(0, &ohci->regs->ed_controlhead); writel(0, &ohci->regs->ed_bulkhead); - /* - writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead); - writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead); - */ - -#if 0 - printk(KERN_DEBUG "alloc_ohci(): controller\n"); - show_ohci_status(ohci); -#endif -#if 0 - printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci); +#ifdef OHCI_DEBUG + if (MegaDebug) { + printk(KERN_DEBUG "alloc_ohci(): controller\n"); + show_ohci_status(ohci); + } #endif return ohci; @@ -1310,7 +2341,7 @@ static struct ohci *alloc_ohci(void* mem_base) */ static void release_ohci(struct ohci *ohci) { - printk(KERN_DEBUG "entering release_ohci %p\n", ohci); + printk(KERN_DEBUG "Releasing OHCI controller 0x%p\n", ohci); #ifdef OHCI_TIMER /* stop our timer */ @@ -1324,13 +2355,14 @@ static void release_ohci(struct ohci *ohci) /* stop all OHCI interrupts */ writel(~0x0, &ohci->regs->intrdisable); - if (ohci->root_hub) { + if (ohci->bus->root_hub) { + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); /* ensure that HC is stopped before releasing the HCCA */ writel(OHCI_USB_SUSPEND, &ohci->regs->control); - free_page((unsigned long) ohci->root_hub->hcca); - kfree(ohci->root_hub); - ohci->root_hub->hcca = NULL; - ohci->root_hub = NULL; + free_page((unsigned long) root_hub->hcca); + kfree(ohci->bus->root_hub); + root_hub->hcca = NULL; + ohci->bus->root_hub = NULL; } /* unmap the IO address space */ @@ -1363,20 +2395,23 @@ static int ohci_control_thread(void * __ohci) * This thread doesn't need any user-level access, * so get rid of all of our resources.. */ - printk("ohci_control_thread code at %p\n", &ohci_control_thread); + printk(KERN_DEBUG "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread); exit_mm(current); exit_files(current); - exit_fs(current); + /*exit_fs(current);*/ /* can't do kernel_thread if we do this */ strcpy(current->comm, "ohci-control"); + usb_register_bus(ohci->bus); + /* * Damn the torpedoes, full speed ahead */ if (start_hc(ohci) < 0) { - printk("usb-ohci: failed to start the controller\n"); + printk(KERN_ERR "usb-ohci: failed to start the controller\n"); release_ohci(ohci); - printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci); + usb_deregister_bus(ohci->bus); + printk(KERN_INFO "leaving ohci_control_thread %p\n", __ohci); return 0; } @@ -1390,11 +2425,12 @@ static int ohci_control_thread(void * __ohci) ohci_check_configuration(ohci); /* re-enable root hub status change interrupts. */ -#if 0 +#ifdef OHCI_RHSC_INT writel(OHCI_INTR_RHSC, &ohci->regs->intrenable); #endif - printk(KERN_DEBUG "ohci-control thread sleeping\n"); + if (MegaDebug) + printk(KERN_DEBUG "ohci-control thread sleeping\n"); interruptible_sleep_on(&ohci_configure); #ifdef CONFIG_APM if (apm_resume) { @@ -1416,11 +2452,19 @@ static int ohci_control_thread(void * __ohci) spin_unlock_irq(¤t->sigmask_lock); if(signr == SIGUSR1) { - /* FIXME: have it do a full ed/td queue dump */ + /* TODO: have it do a full ed/td queue dump? */ printk(KERN_DEBUG "OHCI status dump:\n"); show_ohci_status(ohci); + } else if (signr == SIGUSR2) { + /* toggle mega TD/ED debugging output */ +#ifdef OHCI_DEBUG + MegaDebug = (MegaDebug + 1) & 3; + printk(KERN_DEBUG "usb-ohci: Mega debugging level %d.\n", + MegaDebug); +#endif } else { /* unknown signal, exit the thread */ + printk(KERN_DEBUG "usb-ohci: control thread for %p exiting on signal %ld\n", __ohci, signr); break; } } @@ -1428,8 +2472,7 @@ static int ohci_control_thread(void * __ohci) reset_hc(ohci); release_ohci(ohci); - - printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci); + usb_deregister_bus(ohci->bus); return 0; } /* ohci_control_thread() */ @@ -1470,9 +2513,12 @@ static int handle_apm_event(apm_event_t event) #ifdef OHCI_TIMER /* * Inspired by Iñaky's driver. This function is a timer routine that - * is called OHCI_TIMER_FREQ times per second. It polls the root hub - * for status changes as on my system things are acting a bit odd at - * the moment.. + * is called every OHCI_TIMER_FREQ ms. It polls the root hub for + * status changes as on my system the RHSC interrupt just doesn't + * play well with others.. (so RHSC is turned off by default in this + * driver) + * [my controller is a "SiS 7001 USB (rev 16)"] + * -greg */ static void ohci_timer_func (unsigned long ohci_ptr) { @@ -1480,8 +2526,9 @@ static void ohci_timer_func (unsigned long ohci_ptr) ohci_root_hub_events(ohci); - /* press the snooze button... */ - mod_timer(&ohci_timer, jiffies + (OHCI_TIMER_FREQ*HZ)); + /* set the next timer */ + mod_timer(&ohci_timer, jiffies + ((OHCI_TIMER_FREQ*HZ)/1000)); + } /* ohci_timer_func() */ #endif @@ -1495,10 +2542,6 @@ static int found_ohci(int irq, void* mem_base) int retval; struct ohci *ohci; -#if 0 - printk(KERN_DEBUG "entering found_ohci %d %p\n", irq, mem_base); -#endif - /* Allocate the running OHCI structures */ ohci = alloc_ohci(mem_base); if (!ohci) { @@ -1507,9 +2550,10 @@ static int found_ohci(int irq, void* mem_base) #ifdef OHCI_TIMER init_timer(&ohci_timer); - ohci_timer.expires = jiffies + (OHCI_TIMER_FREQ*HZ); + ohci_timer.expires = jiffies + ((OHCI_TIMER_FREQ*HZ)/1000); ohci_timer.data = (unsigned long)ohci; ohci_timer.function = ohci_timer_func; + add_timer(&ohci_timer); #endif retval = -EBUSY; @@ -1518,8 +2562,8 @@ static int found_ohci(int irq, void* mem_base) ohci->irq = irq; -#if 0 - printk(KERN_DEBUG "usb-ohci: starting ohci-control thread\n"); +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "usb-ohci: forking ohci-control thread for 0x%p\n", ohci); #endif /* fork off the handler */ @@ -1531,14 +2575,10 @@ static int found_ohci(int irq, void* mem_base) retval = pid; } else { - printk("usb-ohci: Couldn't allocate interrupt %d\n", irq); + printk(KERN_ERR "usb-ohci: Couldn't allocate interrupt %d\n", irq); } release_ohci(ohci); -#if 0 - printk(KERN_DEBUG "leaving found_ohci %d %p\n", irq, mem_base); -#endif - return retval; } /* found_ohci() */ @@ -1559,7 +2599,7 @@ static int init_ohci(struct pci_dev *dev) /* no interrupt won't work... */ if (dev->irq == 0) { - printk("usb-ohci: no irq assigned? check your BIOS settings.\n"); + printk(KERN_ERR "usb-ohci: no irq assigned? check your BIOS settings.\n"); return -ENODEV; } @@ -1572,11 +2612,16 @@ static int init_ohci(struct pci_dev *dev) mem_base = (unsigned long) ioremap_nocache(mem_base, 4096); if (!mem_base) { - printk("Error mapping OHCI memory\n"); + printk(KERN_ERR "Error mapping OHCI memory\n"); return -EFAULT; } MOD_INC_USE_COUNT; +#ifdef OHCI_DEBUG + printk(KERN_INFO "usb-ohci: Warning! Gobs of debugging output has been enabled.\n"); + printk(KERN_INFO " Check your kern.debug logs for the bulk of it.\n"); +#endif + if (found_ohci(dev->irq, (void *) mem_base) < 0) { MOD_DEC_USE_COUNT; return -1; @@ -1585,26 +2630,6 @@ static int init_ohci(struct pci_dev *dev) return 0; } /* init_ohci() */ -#ifdef MODULE -/* - * Clean up when unloading the module - */ -void cleanup_module(void) -{ -#ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -#endif -#ifdef CONFIG_USB_MOUSE - usb_mouse_cleanup(); -#endif - printk("usb-ohci: module unloaded\n"); -} - -#define ohci_init init_module - -#endif - - /* TODO this should be named following Linux convention and go in pci.h */ #define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010) @@ -1621,11 +2646,11 @@ int ohci_init(void) /*u8 type;*/ if (sizeof(struct ohci_device) > 4096) { - printk("usb-ohci: struct ohci_device to large\n"); + printk(KERN_ERR "usb-ohci: struct ohci_device to large\n"); return -ENODEV; } - printk("OHCI USB Driver loading\n"); + printk(KERN_INFO "OHCI USB Driver loading\n"); retval = -ENODEV; for (;;) { @@ -1644,18 +2669,6 @@ int ohci_init(void) if (retval < 0) continue; - /* TODO check module params here to determine what to load */ - -#ifdef CONFIG_USB_MOUSE - usb_mouse_init(); -#endif -#ifdef CONFIG_USB_KBD - usb_kbd_init(); -#endif - hub_init(); -#ifdef CONFIG_USB_AUDIO - usb_audio_init(); -#endif #ifdef CONFIG_APM apm_register_callback(&handle_apm_event); #endif @@ -1665,5 +2678,22 @@ int ohci_init(void) return retval; } /* ohci_init */ + +#ifdef MODULE +/* + * Clean up when unloading the module + */ +void cleanup_module(void){ +# ifdef CONFIG_APM + apm_unregister_callback(&handle_apm_event); +# endif + printk(KERN_ERR "usb-ohci: module unloaded\n"); +} + +int init_module(void){ + return ohci_init(); +} +#endif //MODULE + /* vim:sw=8 */ diff --git a/drivers/usb/ohci.h b/drivers/usb/ohci.h index 8714cd2e833b..4bb71c55ce2e 100644 --- a/drivers/usb/ohci.h +++ b/drivers/usb/ohci.h @@ -6,7 +6,7 @@ * * (C) Copyright 1999 Gregory P. Smith * - * $Id: ohci.h,v 1.15 1999/05/09 23:25:49 greg Exp $ + * $Id: ohci.h,v 1.24 1999/05/16 10:18:26 greg Exp $ */ #include @@ -37,11 +37,16 @@ struct ohci_td { struct ohci_td *next_dl_td; /* used during donelist processing */ void *data; /* virt. address of the the buffer */ usb_device_irq completed; /* Completion handler routine */ - int allocated; /* boolean: is this TD allocated? */ + int hcd_flags; /* Flags for the HCD: */ + /* bit0: Is this TD allocated? */ + /* bit1: Is this a dummy (end of list) TD? */ + /* bit2: do NOT automatically free this TD on completion */ + /* bit3: this is the last TD in a contiguious TD chain */ + + struct usb_device *usb_dev; /* the owning device */ - /* User or Device class driver specific fields */ void *dev_id; /* user defined pointer passed to irq handler */ -} __attribute((aligned(16))); +} __attribute((aligned(32))); #define OHCI_TD_ROUND (1 << 18) /* buffer rounding bit */ #define OHCI_TD_D (3 << 19) /* direction of xfer: */ @@ -52,25 +57,46 @@ struct ohci_td { #define td_set_dir_out(d) ((d) ? OHCI_TD_D_OUT : OHCI_TD_D_IN ) #define OHCI_TD_IOC_DELAY (7 << 21) /* frame delay allowed before int. */ #define OHCI_TD_IOC_OFF (OHCI_TD_IOC_DELAY) /* no interrupt on complete */ +#define td_set_ioc_delay(frames) (((frames) & 7) << 21) #define OHCI_TD_DT (3 << 24) /* data toggle bits */ #define TOGGLE_AUTO (0 << 24) /* automatic (from the ED) */ #define TOGGLE_DATA0 (2 << 24) /* force Data0 */ #define TOGGLE_DATA1 (3 << 24) /* force Data1 */ #define td_force_toggle(b) (((b) | 2) << 24) #define OHCI_TD_ERRCNT (3 << 26) /* error count */ -#define td_errorcount(td) (((td).info >> 26) & 3) +#define td_errorcount(td) ((le32_to_cpup(&(td).info) >> 26) & 3) +#define clear_td_errorcount(td) ((td)->info &= cpu_to_le32(~(__u32)OHCI_TD_ERRCNT)) #define OHCI_TD_CC (0xf << 28) /* condition code */ #define OHCI_TD_CC_GET(td_i) (((td_i) >> 28) & 0xf) #define OHCI_TD_CC_NEW (OHCI_TD_CC) /* set this on all unaccessed TDs! */ -#define td_cc_notaccessed(td) (((td).info >> 29) == 7) -#define td_cc_accessed(td) (((td).info >> 29) != 7) -#define td_cc_noerror(td) ((((td).info) & OHCI_TD_CC) == 0) -#define td_active(td) (!td_cc_noerror((td)) && (td_errorcount((td)) < 3)) +#define td_cc_notaccessed(td) ((le32_to_cpup(&(td).info) >> 29) == 7) +#define td_cc_accessed(td) ((le32_to_cpup(&(td).info) >> 29) != 7) +#define td_cc_noerror(td) (((le32_to_cpup(&(td).info)) & OHCI_TD_CC) == 0) #define td_done(td) (td_cc_noerror((td)) || (td_errorcount((td)) == 3)) -#define td_allocated(td) ((td).allocated) -#define allocate_td(td) ((td)->allocated = 1) -#define ohci_free_td(td) ((td)->allocated = 0) +/* + * Macros to use the td->hcd_flags field. + */ +#define td_allocated(td) ((td).hcd_flags & 1) +#define allocate_td(td) ((td)->hcd_flags |= 1) +#define ohci_free_td(td) ((td)->hcd_flags &= ~(__u32)1) + +#define td_dummy(td) ((td).hcd_flags & 2) +#define make_dumb_td(td) ((td)->hcd_flags |= 2) +#define clear_dumb_td(td) ((td)->hcd_flags &= ~(__u32)2) + +#define td_endofchain(td) ((td).hcd_flags & (1 << 3)) +#define clear_td_endofchain(td) ((td)->hcd_flags &= ~(1 << 3)) +#define set_td_endofchain(td) ((td)->hcd_flags |= (1 << 3)) + +/* + * These control if the IRQ will call ohci_free_td after taking the TDs + * off of the donelist (assuming the completion function does not ask + * for the TD to be requeued). + */ +#define can_auto_free(td) (!((td).hcd_flags & 4)) +#define noauto_free_td(td) ((td)->hcd_flags |= 4) +#define auto_free_td(td) ((td)->hcd_flags &= ~(__u32)4) /* @@ -82,13 +108,27 @@ struct ohci_ed { __u32 tail_td; /* TD Queue tail pointer */ __u32 _head_td; /* TD Queue head pointer, toggle carry & halted bits */ __u32 next_ed; /* Next ED */ + + /* driver fields */ + struct ohci_device *ohci_dev; + struct ohci_ed *ed_chain; } __attribute((aligned(16))); /* get the head_td */ -#define ed_head_td(ed) ((ed)->_head_td & 0xfffffff0) +#define ed_head_td(ed) (le32_to_cpup(&(ed)->_head_td) & 0xfffffff0) +#define ed_tail_td(ed) (le32_to_cpup(&(ed)->tail_td)) -/* save the carry flag while setting the head_td */ -#define set_ed_head_td(ed, td) ((ed)->_head_td = (td) | ((ed)->_head_td & 3)) +/* save the carry & halted flag while setting the head_td */ +#define set_ed_head_td(ed, td) ((ed)->_head_td = cpu_to_le32((td)) \ + | ((ed)->_head_td & cpu_to_le32(3))) + +/* Control the ED's halted and carry flags */ +#define ohci_halt_ed(ed) ((ed)->_head_td |= cpu_to_le32(1)) +#define ohci_unhalt_ed(ed) ((ed)->_head_td &= cpu_to_le32(~(__u32)1)) +#define ohci_ed_halted(ed) ((ed)->_head_td & cpu_to_le32(1)) +#define ohci_ed_set_carry(ed) ((ed)->_head_td |= cpu_to_le32(2)) +#define ohci_ed_clr_carry(ed) ((ed)->_head_td &= ~cpu_to_le32(2)) +#define ohci_ed_carry(ed) ((le32_to_cpup(&(ed)->_head_td) >> 1) & 1) #define OHCI_ED_SKIP (1 << 14) #define OHCI_ED_MPS (0x7ff << 16) @@ -108,15 +148,17 @@ struct ohci_ed { #define OHCI_ED_EN (0xf << 7) #define OHCI_ED_FA (0x7f) +#define ed_get_en(ed) ((le32_to_cpup(&(ed)->status) & OHCI_ED_EN) >> 7) +#define ed_get_fa(ed) (le32_to_cpup(&(ed)->status) & OHCI_ED_FA) -/* NOTE: bits 27-31 of the status dword are reserved for the driver */ +/* NOTE: bits 27-31 of the status dword are reserved for the HCD */ /* * We'll use this status flag for to mark if an ED is in use by the - * driver or not. If the bit is set, it is used. - * - * FIXME: implement this! + * driver or not. If the bit is set, it is being used. */ -#define ED_USED (1 << 31) +#define ED_ALLOCATED (1 << 31) +#define ed_allocated(ed) (le32_to_cpup(&(ed).status) & ED_ALLOCATED) +#define allocate_ed(ed) ((ed)->status |= cpu_to_le32(ED_ALLOCATED)) /* * The HCCA (Host Controller Communications Area) is a 256 byte @@ -178,20 +220,21 @@ struct ohci_device { struct ohci_td td[NUM_TDS]; /* Transfer Descriptors */ unsigned long data[DATA_BUF_LEN]; -}; +} __attribute((aligned(32))); /* .... */ +/* + * These are the index of the placeholder EDs for the root hub to + * build the interrupt transfer ED tree out of. + */ #define ED_INT_1 0 #define ED_INT_2 1 #define ED_INT_4 2 #define ED_INT_8 3 #define ED_INT_16 4 #define ED_INT_32 5 -#define ED_CONTROL 6 -#define ED_BULK 7 #define ED_ISO ED_INT_1 /* same as 1ms interrupt queue */ -#define ED_FIRST_AVAIL 8 /* first non-reserved ED */ /* * Given a period p in ms, convert it to the closest endpoint @@ -210,7 +253,9 @@ struct ohci_device { * This is the maximum number of root hub ports. I don't think we'll * ever see more than two as that's the space available on an ATX * motherboard's case, but it could happen. The OHCI spec allows for - * up to 15... (which is insane!) + * up to 15... (which is insane given that they each need to supply up + * to 500ma; that would be 7.5 amps!). I have seen a PCI card with 4 + * downstream ports on it. * * Although I suppose several "ports" could be connected directly to * internal laptop devices such as a keyboard, mouse, camera and @@ -254,15 +299,24 @@ struct ohci_regs { } roothub; } __attribute((aligned(32))); +/* + * These are used by internal ED managing functions as a + * parameter to state the type of ED to deal with (when it matters). + */ +#define HCD_ED_ISOC (0) +#define HCD_ED_INT (1) +#define HCD_ED_CONTROL (2) +#define HCD_ED_BULK (3) + /* * Read a MMIO register and re-write it after ANDing with (m) */ -#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) ) +#define writel_mask(m, a) writel( (readl((unsigned long)(a))) & (__u32)(m), (unsigned long)(a) ) /* * Read a MMIO register and re-write it after ORing with (b) */ -#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) ) +#define writel_set(b, a) writel( (readl((unsigned long)(a))) | (__u32)(b), (unsigned long)(a) ) #define PORT_CCS (1) /* port current connect status */ @@ -288,6 +342,16 @@ struct ohci_regs { #define OHCI_ROOT_OCIC (1 << 17) /* Overcurrent indicator change */ #define OHCI_ROOT_CRWE (1 << 31) /* Clear RemoteWakeupEnable */ +/* + * Root hub A register masks + */ +#define OHCI_ROOT_A_NPS (1 << 9) +#define OHCI_ROOT_A_PSM (1 << 8) + +/* + * Root hub B register masks + */ + /* * Interrupt register masks */ @@ -334,12 +398,15 @@ struct ohci { struct list_head interrupt_list; /* List of interrupt active TDs for this OHCI */ }; -#define OHCI_TIMER -#define OHCI_TIMER_FREQ (1) /* frequency of OHCI status checks */ +#define OHCI_TIMER /* enable the OHCI timer */ +#define OHCI_TIMER_FREQ (234) /* ms between each root hub status check */ + +#undef OHCI_RHSC_INT /* Don't use root hub status interrupts! */ -/* Debugging code */ +/* Debugging code [ohci-debug.c] */ void show_ohci_ed(struct ohci_ed *ed); void show_ohci_td(struct ohci_td *td); +void show_ohci_td_chain(struct ohci_td *td); void show_ohci_status(struct ohci *ohci); void show_ohci_device(struct ohci_device *dev); void show_ohci_hcca(struct ohci_hcca *hcca); diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c new file mode 100644 index 000000000000..84083ce4654b --- /dev/null +++ b/drivers/usb/printer.c @@ -0,0 +1,420 @@ + +/* Driver for USB Printers + * + * (C) Michael Gee (michael@linuxspecific.com) 1999 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usb.h" + +#define NAK_TIMEOUT (HZ) /* stall wait for printer */ +#define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */ + +#ifndef USB_PRINTER_MAJOR +#define USB_PRINTER_MAJOR 63 +#endif + +static int mymajor = USB_PRINTER_MAJOR; + +#define MAX_PRINTERS 8 + +struct pp_usb_data { + struct usb_device *pusb_dev; + __u8 isopen; /* nz if open */ + __u8 noinput; /* nz if no input stream */ + __u8 minor; /* minor number of device */ + __u8 status; /* last status from device */ + int maxin, maxout; /* max transfer size in and out */ + char *obuf; /* transfer buffer (out only) */ + wait_queue_head_t wait_q; /* for timeouts */ + unsigned int last_error; /* save for checking */ +}; + +static struct pp_usb_data *minor_data[MAX_PRINTERS]; + +#define PPDATA(x) ((struct pp_usb_data *)(x)) + +unsigned char printer_read_status(struct pp_usb_data *p) +{ + __u8 status; + devrequest dr; + struct usb_device *dev = p->pusb_dev; + + dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80; + dr.request = 1; + dr.value = 0; + dr.index = 0; + dr.length = 1; + if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1)) { + return 0; + } + return status; +} + +static int printer_check_status(struct pp_usb_data *p) +{ + unsigned int last = p->last_error; + unsigned char status = printer_read_status(p); + + if (status & LP_PERRORP) + /* No error. */ + last = 0; + else if ((status & LP_POUTPA)) { + if (last != LP_POUTPA) { + last = LP_POUTPA; + printk(KERN_INFO "usblp%d out of paper\n", p->minor); + } + } else if (!(status & LP_PSELECD)) { + if (last != LP_PSELECD) { + last = LP_PSELECD; + printk(KERN_INFO "usblp%d off-line\n", p->minor); + } + } else { + if (last != LP_PERRORP) { + last = LP_PERRORP; + printk(KERN_INFO "usblp%d on fire\n", p->minor); + } + } + + p->last_error = last; + + return status; +} + +void printer_reset(struct pp_usb_data *p) +{ + devrequest dr; + struct usb_device *dev = p->pusb_dev; + + dr.requesttype = USB_TYPE_CLASS | USB_RECIP_OTHER; + dr.request = 2; + dr.value = 0; + dr.index = 0; + dr.length = 0; + dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +} + +static int open_printer(struct inode * inode, struct file * file) +{ + struct pp_usb_data *p; + + if(MINOR(inode->i_rdev) >= MAX_PRINTERS || + !minor_data[MINOR(inode->i_rdev)]) { + return -ENODEV; + } + + p = minor_data[MINOR(inode->i_rdev)]; + p->minor = MINOR(inode->i_rdev); + + if (p->isopen++) { + return -EBUSY; + } + if (!(p->obuf = (char *)__get_free_page(GFP_KERNEL))) { + p->isopen = 0; + return -ENOMEM; + } + + printer_check_status(p); + + + file->private_data = p; +// printer_reset(p); + init_waitqueue_head(&p->wait_q); + return 0; +} + +static int close_printer(struct inode * inode, struct file * file) +{ + struct pp_usb_data *p = file->private_data; + + free_page((unsigned long)p->obuf); + p->isopen = 0; + file->private_data = NULL; + if(!p->pusb_dev) { + minor_data[p->minor] = NULL; + kfree(p); + + MOD_DEC_USE_COUNT; + + } + return 0; +} + +static ssize_t write_printer(struct file * file, + const char * buffer, size_t count, loff_t *ppos) +{ + struct pp_usb_data *p = file->private_data; + unsigned long copy_size; + unsigned long bytes_written = 0; + unsigned long partial; + int result; + int maxretry; + + do { + char *obuf = p->obuf; + unsigned long thistime; + + thistime = copy_size = (count > p->maxout) ? p->maxout : count; + if (copy_from_user(p->obuf, buffer, copy_size)) + return -EFAULT; + maxretry = MAX_RETRY_COUNT; + while (thistime) { + if (!p->pusb_dev) + return -ENODEV; + if (signal_pending(current)) { + return bytes_written ? bytes_written : -EINTR; + } + result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev, + usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial); + if (partial) { + obuf += partial; + thistime -= partial; + maxretry = MAX_RETRY_COUNT; + } + if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */ + if(!maxretry--) + return -ETIME; + interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT); + continue; + } else if (!result && !partial) { + break; + } + }; + if (result) { + /* whoops - let's reset and fail the request */ +// printk("Whoops - %x\n", result); + printer_reset(p); + interruptible_sleep_on_timeout(&p->wait_q, 5*HZ); /* let reset do its stuff */ + return -EIO; + } + bytes_written += copy_size; + count -= copy_size; + buffer += copy_size; + } while ( count > 0 ); + + return bytes_written ? bytes_written : -EIO; +} + +static ssize_t read_printer(struct file * file, + char * buffer, size_t count, loff_t *ppos) +{ + struct pp_usb_data *p = file->private_data; + int read_count; + int this_read; + char buf[64]; + unsigned long partial; + int result; + + if (p->noinput) + return -EINVAL; + + read_count = 0; + while (count) { + if (signal_pending(current)) { + return read_count ? read_count : -EINTR; + } + if (!p->pusb_dev) + return -ENODEV; + this_read = (count > sizeof(buf)) ? sizeof(buf) : count; + + result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev, + usb_rcvbulkpipe(p->pusb_dev, 2), buf, this_read, &partial); + + /* unlike writes, we don't retry a NAK, just stop now */ + if (!result & partial) + count = this_read = partial; + else if (result) + return -EIO; + + if (this_read) { + if (copy_to_user(buffer, p->obuf, this_read)) + return -EFAULT; + count -= this_read; + read_count += this_read; + buffer += this_read; + } + } + return read_count; +} + +static int printer_probe(struct usb_device *dev) +{ + struct usb_interface_descriptor *interface; + int i; + + /* + * FIXME - this will not cope with combined printer/scanners + */ + if ((dev->descriptor.bDeviceClass != 7 && + dev->descriptor.bDeviceClass != 0) || + dev->descriptor.bNumConfigurations != 1 || + dev->config[0].bNumInterfaces != 1) { + return -1; + } + + interface = dev->config->altsetting->interface; + + /* Lets be paranoid (for the moment)*/ + if (interface->bInterfaceClass != 7 || + interface->bInterfaceSubClass != 1 || + (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1)|| + interface->bNumEndpoints > 2) { + return -1; + } + + if (interface->endpoint[0].bEndpointAddress != 0x01 || + interface->endpoint[0].bmAttributes != 0x02 || + (interface->bNumEndpoints > 1 && ( + interface->endpoint[1].bEndpointAddress != 0x82 || + interface->endpoint[1].bmAttributes != 0x02))) { + return -1; + } + + for (i=0; i= MAX_PRINTERS) { + return -1; + } + + printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum); + + if (!(dev->private = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) { + printk( KERN_DEBUG "usb_printer: no memory!\n"); + return -1; + } + + memset(dev->private, 0, sizeof(struct pp_usb_data)); + minor_data[i] = PPDATA(dev->private); + minor_data[i]->minor = i; + minor_data[i]->pusb_dev = dev; + /* The max packet size can't be more than 64 (& will be 64 for + * any decent bulk device); this calculation was silly. -greg + * minor_data[i]->maxout = interface->endpoint[0].wMaxPacketSize * 16; + */ + minor_data[i]->maxout = 8192; + if (minor_data[i]->maxout > PAGE_SIZE) { + minor_data[i]->maxout = PAGE_SIZE; + } + if (interface->bInterfaceProtocol != 2) + minor_data[i]->noinput = 1; + else { + minor_data[i]->maxin = interface->endpoint[1].wMaxPacketSize; + } + + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + printk(KERN_INFO " Failed usb_set_configuration: printer\n"); + return -1; + } +#if 0 + { + __u8 status; + __u8 ieee_id[64]; + devrequest dr; + + /* Lets get the device id if possible */ + dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80; + dr.request = 0; + dr.value = 0; + dr.index = 0; + dr.length = sizeof(ieee_id) - 1; + if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1) == 0) { + if (ieee_id[1] < sizeof(ieee_id) - 1) + ieee_id[ieee_id[1]+2] = '\0'; + else + ieee_id[sizeof(ieee_id)-1] = '\0'; + printk(KERN_INFO " Printer ID is %s\n", &ieee_id[2]); + } + status = printer_read_status(PPDATA(dev->private)); + printk(KERN_INFO " Status is %s,%s,%s\n", + (status & 0x10) ? "Selected" : "Not Selected", + (status & 0x20) ? "No Paper" : "Paper", + (status & 0x08) ? "No Error" : "Error"); + } +#endif + return 0; +} + +static void printer_disconnect(struct usb_device *dev) +{ + struct pp_usb_data *pp = dev->private; + + if (pp->isopen) { + /* better let it finish - the release will do whats needed */ + pp->pusb_dev = NULL; + return; + } + minor_data[pp->minor] = NULL; + kfree(pp); + dev->private = NULL; /* just in case */ + MOD_DEC_USE_COUNT; +} + +static struct usb_driver printer_driver = { + "printer", + printer_probe, + printer_disconnect, + { NULL, NULL } +}; + +static struct file_operations usb_printer_fops = { + NULL, /* seek */ + read_printer, + write_printer, + NULL, /* readdir */ + NULL, /* poll - out for the moment */ + NULL, /* ioctl */ + NULL, /* mmap */ + open_printer, + NULL, /* flush ? */ + close_printer, + NULL, + NULL +}; + +int usb_printer_init(void) +{ + int result; + + MOD_INC_USE_COUNT; + + if ((result = register_chrdev(USB_PRINTER_MAJOR, "usblp", &usb_printer_fops)) < 0) { + printk(KERN_WARNING "usbprinter: Cannot register device\n"); + return result; + } + if (mymajor == 0) { + mymajor = result; + } + usb_register(&printer_driver); + printk(KERN_INFO "USB Printer support registered.\n"); + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + + return usb_printer_init(); +} + +void cleanup_module(void) +{ + usb_deregister(&printer_driver); + unregister_chrdev(mymajor, "usblp"); +} +#endif diff --git a/drivers/usb/proc_usb.c b/drivers/usb/proc_usb.c new file mode 100644 index 000000000000..20cb728ba8a6 --- /dev/null +++ b/drivers/usb/proc_usb.c @@ -0,0 +1,487 @@ +/* + * drivers/usb/proc_usb.c + * (C) Copyright 1999 Randy Dunlap. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ************************************************************* + * + * This is a /proc/bus/usb filesystem output module for USB. + * It creates /proc/bus/usb/drivers and /proc/bus/usb/devices. + * + * /proc/bus/usb/devices contains USB topology, device, config, class, + * interface, & endpoint data. + * + * I considered using /proc/bus/usb/devices/device# for each device + * as it is attached or detached, but I didn't like this for some + * reason -- maybe it's just too deep of a directory structure. + * I also don't like looking in multiple places to gather and view + * the data. Having only one file for ./devices also prevents race + * conditions that could arise if a program was reading device info + * for devices that are being removed (unplugged). (That is, the + * program may find a directory for devnum_12 then try to open it, + * but it was just unplugged, so the directory is now deleted. + * But programs would just have to be prepared for situations like + * this in any plug-and-play environment.) + */ + +#define __KERNEL__ 1 + +#include +#include +#include +/* #include */ +#include +#include +#include +#include +#include + +#include "usb.h" + +#define DUMP_LIMIT (PAGE_SIZE - 100) + /* limit to only one memory page of output */ + +#define MAX_TOPO_LEVEL 6 + + +static char *format_topo = +/* T: Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd If#=ddd MxCh=dd Driver=%s */ + "T: Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s If#=%3d MxCh=%2d Driver=%s\n"; + +static char *format_device1 = +/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ + "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n"; + +static char *format_device2 = +/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ + "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n"; + +static char *format_config = +/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ + "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; + +static char *format_iface = +/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx */ + "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n"; + +static char *format_endpt = +/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms */ + "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%3dms\n"; + + +/* + * Need access to the driver and USB bus lists. + * extern struct list_head usb_driver_list; + * extern struct list_head usb_bus_list; + * However, these will come from functions that return ptrs to each of them. + */ + +extern struct list_head *usb_driver_get_list (void); +extern struct list_head *usb_bus_get_list (void); + +extern struct proc_dir_entry *proc_bus; + +static struct proc_dir_entry *usbdir = NULL, *driversdir = NULL; +static struct proc_dir_entry *devicesdir = NULL; + +struct class_info { + int class; + char *class_name; +}; + +struct class_info clas_info [] = +{ /* max. 5 chars. per name string */ + {USB_CLASS_PER_INTERFACE, ">ifc"}, + {USB_CLASS_AUDIO, "audio"}, + {USB_CLASS_COMM, "comm."}, + {USB_CLASS_HID, "HID"}, + {USB_CLASS_HUB, "hub"}, + {USB_CLASS_PRINTER, "print"}, + {USB_CLASS_MASS_STORAGE, "stor."}, + {USB_CLASS_VENDOR_SPEC, "vend."}, + {-1, "unk."} /* leave as last */ +}; + +/*****************************************************************/ + +static char *class_decode (const int class) +{ + int ix; + + for (ix = 0; clas_info [ix].class != -1; ix++) + if (clas_info [ix].class == class) + break; + + return (clas_info [ix].class_name); +} +static int usb_dump_endpoint_descriptor (const struct usb_endpoint_descriptor *desc, + char *buf, int *len) +{ + char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."}; + + *len += sprintf (buf + *len, format_endpt, + desc->bEndpointAddress, + (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O', + desc->bmAttributes, + EndpointType[desc->bmAttributes & 3], + desc->wMaxPacketSize, + desc->bInterval + ); + + return (*len >= DUMP_LIMIT) ? -1 : 0; +} + +static int usb_dump_endpoint (const struct usb_endpoint_descriptor *endpoint, + char *buf, int *len) +{ + if (usb_dump_endpoint_descriptor (endpoint, buf, len) < 0) + return -1; + + return 0; +} + +static int usb_dump_interface_descriptor (const struct usb_interface_descriptor *desc, + char *buf, int *len) +{ + *len += sprintf (buf + *len, format_iface, + desc->bInterfaceNumber, + desc->bAlternateSetting, + desc->bNumEndpoints, + desc->bInterfaceClass, + class_decode (desc->bInterfaceClass), + desc->bInterfaceSubClass, + desc->bInterfaceProtocol + ); + + return (*len >= DUMP_LIMIT) ? -1 : 0; +} + +static int usb_dump_interface (const struct usb_interface_descriptor *interface, + char *buf, int *len) +{ + int i; + + if (usb_dump_interface_descriptor (interface, buf, len) < 0) + return -1; + + for (i = 0; i < interface->bNumEndpoints; i++) { + if (usb_dump_endpoint (interface->endpoint + i, buf, len) < 0) + return -1; + } + + return 0; +} + +/* TBD: + * 0. TBDs + * 1. marking active config and ifaces (code lists all, but should mark + * which ones are active, if any) + * 2. Add proc_usb_init() call from usb-core.c. + * 3. proc_usb as a MODULE ? + * 4. use __initfunc() ? + * 5. add status to each endpoint line + */ + +static int usb_dump_config_descriptor (const struct usb_config_descriptor *desc, + const int active, char *buf, int *len) +{ + *len += sprintf (buf + *len, format_config, + active ? '*' : ' ', /* mark active/actual/current cfg. */ + desc->bNumInterfaces, + desc->bConfigurationValue, + desc->bmAttributes, + desc->MaxPower * 2 + ); + + return (*len >= DUMP_LIMIT) ? -1 : 0; +} + +static int usb_dump_config (const struct usb_config_descriptor *config, + const int active, char *buf, int *len) +{ + int i, j; + struct usb_alternate_setting *as; + + if (!config) { /* getting these some in 2.3.7; none in 2.3.6 */ + *len += sprintf (buf + *len, "(null Cfg. desc.)\n"); + return 0; + } + + if (usb_dump_config_descriptor (config, active, buf, len) < 0) + return -1; + + for (i = 0; i < config->num_altsetting; i++) { + as = config->altsetting + i; + if ((as) == NULL) + break; + + for (j = 0; j < config->bNumInterfaces; j++) + if (usb_dump_interface (as->interface + j, buf, len) < 0) + return -1; + } + + return 0; +} + +/* + * Dump the different USB descriptors. + */ +static int usb_dump_device_descriptor (const struct usb_device_descriptor *desc, + char *buf, int *len) +{ + *len += sprintf (buf + *len, format_device1, + desc->bcdUSB >> 8, desc->bcdUSB & 0xff, + desc->bDeviceClass, + class_decode (desc->bDeviceClass), + desc->bDeviceSubClass, + desc->bDeviceProtocol, + desc->bMaxPacketSize0, + desc->bNumConfigurations + ); + if (*len >= DUMP_LIMIT) return -1; + + *len += sprintf (buf + *len, format_device2, + desc->idVendor, desc->idProduct, + desc->bcdDevice >> 8, desc->bcdDevice & 0xff + ); + + return (*len >= DUMP_LIMIT) ? -1 : 0; +} + +static int usb_dump_desc (const struct usb_device *dev, char *buf, int *len) +{ + int i; + + if (usb_dump_device_descriptor (&dev->descriptor, buf, len) < 0) + return -1; + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (usb_dump_config (dev->config + i, + (dev->config + i) == dev->actconfig, /* active ? */ + buf, len) < 0) + return -1; + } + + return 0; +} + +#ifdef PROC_EXTRA /* TBD: may want to add this code later */ + +static int usb_dump_hub_descriptor (const struct usb_hub_descriptor * desc, + char *buf, int *len) +{ + int leng = USB_DT_HUB_NONVAR_SIZE; + unsigned char *ptr = (unsigned char *) desc; + + *len += sprintf (buf + *len, "Interface:"); + + while (leng) { + *len += sprintf (buf + *len, " %02x", *ptr); + ptr++; leng--; + } + *len += sprintf (buf + *len, "\n"); + + return (*len >= DUMP_LIMIT) ? -1 : 0; +} + +static int usb_dump_string (const struct usb_device *dev, char *id, int index, + char *buf, int *len) +{ + if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index]) + *len += sprintf (buf + *len, "%s: %s ", id, dev->stringindex[index]); + + return (*len >= DUMP_LIMIT) ? -1 : 0; +} + +#endif /* PROC_EXTRA */ + +/*****************************************************************/ + +static int usb_device_dump (char *buf, int *len, + const struct usb_device *usbdev, + int level, int index, int count) +{ + int chix; + int cnt = 0; + int parent_devnum; + + if (level > MAX_TOPO_LEVEL) return -1; + + parent_devnum = usbdev->parent ? (usbdev->parent->devnum == -1) ? 0 + : usbdev->parent->devnum : 0; + /* + * So the root hub's parent is 0 and any device that is + * plugged into the root hub has a parent of 0. + */ + *len += sprintf (buf + *len, format_topo, + level, parent_devnum, index, count, + usbdev->devnum, + usbdev->slow ? "1.5" : "12 ", + usbdev->ifnum, usbdev->maxchild, + usbdev->driver ? usbdev->driver->name : + (level == 0) ? "(root hub)" : "(none)" + ); + /* + * level = topology-tier level; + * parent_devnum = parent device number; + * index = parent's connector number; + * count = device count at this level + */ + + if (*len >= DUMP_LIMIT) + return -1; + + if (usbdev->devnum > 0) { /* for any except root hub */ + if (usb_dump_desc (usbdev, buf, len) < 0) + return -1; + } + + /* Now look at all of this device's children. */ + for (chix = 0; chix < usbdev->maxchild; chix++) { + if (usbdev->children [chix]) { + if (usb_device_dump (buf, len, + usbdev->children [chix], + level + 1, chix, ++cnt) < 0) + return -1; + } + } + + return 0; +} + +static int usb_bus_list_dump (char *buf, int len) +{ + struct list_head *usb_bus_list = usb_bus_get_list (); + struct list_head *list = usb_bus_list->next; + + len = 0; + + /* + * Go thru each usb_bus. Within each usb_bus: each usb_device. + * Within each usb_device: all of its device & config. descriptors, + * marking the currently active ones. + */ + + + while (list != usb_bus_list) { + struct usb_bus *bus = list_entry (list, struct usb_bus, bus_list); + + if (usb_device_dump (buf, &len, bus->root_hub, 0, 0, 0) + < 0) + break; + + list = list->next; + + if (len >= DUMP_LIMIT) { + len += sprintf (buf + len, "(truncated)\n"); + break; + } + } + + return (len); +} + +static int usb_bus_list_dump_devices (char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + return usb_bus_list_dump (buf, len); +} + +/* + * Dump usb_driver_list. + * + * We now walk the list of registered USB drivers. + */ +static int usb_driver_list_dump (char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct list_head *usb_driver_list = usb_driver_get_list (); + struct list_head *tmp = usb_driver_list->next; + int cnt = 0; + + len = 0; + + while (tmp != usb_driver_list) { + struct usb_driver *driver = list_entry (tmp, struct usb_driver, + driver_list); + len += sprintf (buf + len, "%s\n", driver->name); + cnt++; + tmp = tmp->next; + + if (len >= DUMP_LIMIT) + { + len += sprintf (buf + len, "(truncated)\n"); + return (len); + } + } + + if (!cnt) + len += sprintf (buf + len, "(none)\n"); + return (len); +} + +void proc_usb_cleanup (void) +{ + if (driversdir) + remove_proc_entry ("drivers", usbdir); + if (devicesdir) + remove_proc_entry ("devices", usbdir); + if (usbdir) + remove_proc_entry ("usb", proc_bus); +} + +int proc_usb_init (void) +{ + usbdir = create_proc_entry ("usb", S_IFDIR, proc_bus); + if (!usbdir) { + printk ("proc_usb: cannot create /proc/bus/usb entry\n"); + return -1; + } + + driversdir = create_proc_entry ("drivers", 0, usbdir); + if (!driversdir) { + printk ("proc_usb: cannot create /proc/bus/usb/drivers entry\n"); + proc_usb_cleanup (); + return -1; + } + driversdir->read_proc = usb_driver_list_dump; + + devicesdir = create_proc_entry ("devices", 0, usbdir); + if (!devicesdir) { + printk ("proc_usb: cannot create /proc/bus/usb/devices entry\n"); + proc_usb_cleanup (); + return -1; + } + devicesdir->read_proc = usb_bus_list_dump_devices; + + return 0; +} + +#ifdef PROCFS_MODULE /* TBD: support proc_fs MODULE ??? */ + +int init_module (void) +{ + return proc_usb_init (); +} + +void cleanup_module (void) +{ + proc_usb_cleanup (); +} + +#endif /* PROCFS_MODULE */ + +/* end proc_usb.c */ diff --git a/drivers/usb/procusb b/drivers/usb/procusb new file mode 100644 index 000000000000..6c956f7f8509 --- /dev/null +++ b/drivers/usb/procusb @@ -0,0 +1,41 @@ +#!/usr/bin/perl + +# Reads /proc/bus/usb/devices and selectively lists and/or +# interprets it. + +$DEVFILENAME = "/proc/bus/usb/devices"; +$PROGNAME = $0; + +$TAGS = $ARGV[0]; # save user TAGS +if (length ($TAGS) == 0) +{ + print "usage: $PROGNAME tags\n"; + print " where 'tags' can be any number of 'TDPCIE' or 'A(LL)'\n"; + exit 1; +} + +$ALL = ($TAGS =~ /all/i) || ($TAGS =~ /a/i); + +# TBD: Check that $TAGS is valid. +if (! $ALL) +{ +} + +if (! open (DEVNUM, "<$DEVFILENAME")) +{ + print "$PROGNAME: cannot open '$DEVFILENAME'\n"; + exit 1; +} + +while ($line = ) # read a text line from DEVNUM +{ + if (($ALL) || ($line =~ /^[$TAGS]:/i)) # any of TAGS at beg. of line? + { + print "$line"; # still has newline char on it + # TBD: add more/paging functionality. + } +} # end while DEVNUM + +close (DEVNUM); + +# END. diff --git a/drivers/usb/stopusb b/drivers/usb/stopusb index 1dc46980b537..7226738d4dde 100644 --- a/drivers/usb/stopusb +++ b/drivers/usb/stopusb @@ -1,7 +1,11 @@ #!/bin/sh +killall khubd killall ohci-control +killall uhci-control sleep 2 +rmmod hub rmmod usb-ohci +rmmod usb-uhci diff --git a/drivers/usb/uhci-debug.c b/drivers/usb/uhci-debug.c index fd2aba6da44a..8a1d3032c533 100644 --- a/drivers/usb/uhci-debug.c +++ b/drivers/usb/uhci-debug.c @@ -12,6 +12,8 @@ void show_td(struct uhci_td * td) { + char *spid; + printk("%08x ", td->link); printk("%se%d %s%s%s%s%s%s%s%s%s%sLength=%x ", ((td->status >> 29) & 1) ? "SPD " : "", @@ -27,12 +29,27 @@ void show_td(struct uhci_td * td) ((td->status >> 18) & 1) ? "CRC/Timeo " : "", ((td->status >> 17) & 1) ? "BitStuff " : "", td->status & 0x7ff); - printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x ", + switch (td->info & 0xff) { + case 0x2d: + spid = "SETUP"; + break; + case 0xe1: + spid = "OUT"; + break; + case 0x69: + spid = "IN"; + break; + default: + spid = "?"; + break; + } + printk("MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", td->info >> 21, - ((td->info >> 19) & 1) ? "DT " : "", + ((td->info >> 19) & 1), (td->info >> 15) & 15, (td->info >> 8) & 127, - td->info & 0xff); + (td->info & 0xff), + spid); printk("(buf=%08x)\n", td->buffer); } @@ -114,7 +131,7 @@ void show_queue(struct uhci_qh *qh) #if 0 printk(" link = %p, element = %p\n", qh->link, qh->element); #endif - if(!qh->element) { + if(!(qh->element & ~0xF)) { printk(" td 0 = NULL\n"); return; } @@ -130,9 +147,9 @@ void show_queue(struct uhci_qh *qh) int is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) { int j; - + struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub); for (j = 0; j < UHCI_MAXQH; j++) - if (qh == uhci->root_hub->qh + j) + if (qh == root_hub->qh + j) return 1; return 0; @@ -148,15 +165,16 @@ void show_queues(struct uhci *uhci) { int i; struct uhci_qh *qh; + struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub); for (i = 0; i < UHCI_MAXQH; ++i) { printk(" %s:\n", qh_names[i]); #if 0 - printk(" qh #%d, %p\n", i, virt_to_bus(uhci->root_hub->qh + i)); + printk(" qh #%d, %p\n", i, virt_to_bus(root_hub->qh + i)); show_queue(uhci->root_hub->qh + i); #endif - qh = uhci_link_to_qh(uhci->root_hub->qh[i].link); + qh = uhci_link_to_qh(root_hub->qh[i].link); for (; qh; qh = uhci_link_to_qh(qh->link)) { if (is_skeleton_qh(uhci, qh)) break; @@ -165,4 +183,3 @@ void show_queues(struct uhci *uhci) } } } - diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 3d8ccdb7d9d9..b57b93eb5e8c 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -19,6 +19,8 @@ */ /* 4/4/1999 added data toggle for interrupt pipes -keryan */ +/* 5/16/1999 added global toggles for bulk and control */ +/* 6/25/1999 added fix for data toggles on bidirectional bulk endpoints */ #include #include @@ -30,14 +32,15 @@ #include #include #include +#include +#include #include #include #include #include #include "uhci.h" -#include "inits.h" #ifdef CONFIG_APM #include @@ -45,33 +48,105 @@ static int handle_apm_event(apm_event_t event); static int apm_resume = 0; #endif +static int uhci_debug = 1; + #define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0) -static struct wait_queue *uhci_configure = NULL; +static DECLARE_WAIT_QUEUE_HEAD(uhci_configure); +/* + * Map status to standard result codes + */ +static int uhci_map_status(int status, int dir_out) +{ + if (!status) + return USB_ST_NOERROR; + if (status & 0x02) /* Bitstuff error*/ + return USB_ST_BITSTUFF; + if (status & 0x04) { /* CRC/Timeout */ + if (dir_out) + return USB_ST_NORESPONSE; + else + return USB_ST_CRC; + } + if (status & 0x08) /* NAK */ + return USB_ST_TIMEOUT; + if (status & 0x10) /* Babble */ + return USB_ST_STALL; + if (status & 0x20) /* Buffer error */ + return USB_ST_BUFFERUNDERRUN; + if (status & 0x40) /* Stalled */ + return USB_ST_STALL; + if (status & 0x80) /* Active */ + return USB_ST_NOERROR; + return USB_ST_INTERNALERROR; +} /* * Return the result of a TD.. */ -static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td) +static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned long *rval) { unsigned int status; + struct uhci_td *tmp = td->first; + + if(rval) + *rval = 0; + + /* locate the first failing td, if any */ + + do { + status = (tmp->status >> 16) & 0xff; + if (status) { + /* must reset the toggle on first error */ + if (uhci_debug) { + printk("Set toggle from %x rval %ld\n", (unsigned int)tmp, rval ? *rval : 0); + } + usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), usb_pipeout(tmp->info), (tmp->info >> 19) & 1); + break; + } else { + if(rval) + *rval += (tmp->status & 0x3ff) + 1; + } + if ((tmp->link & 1) || (tmp->link & 2)) + break; + tmp = bus_to_virt(tmp->link & ~0xF); + } while (1); - status = (td->status >> 16) & 0xff; + + if (!status) + return USB_ST_NOERROR; /* Some debugging code */ - if (status) { + if (uhci_debug /* && (!usb_pipeendpoint(tmp->info) || !(status & 0x08))*/ ) { int i = 10; - struct uhci_td *tmp = dev->control_td; - printk("uhci_td_result() failed with status %d\n", status); - show_status(dev->uhci); + + tmp = td->first; + printk("uhci_td_result() failed with status %x\n", status); + //show_status(dev->uhci); do { show_td(tmp); - tmp++; + if ((tmp->link & 1) || (tmp->link & 2)) + break; + tmp = bus_to_virt(tmp->link & ~0xF); if (!--i) break; - } while (tmp <= td); + } while (1); + } + + if (status & 0x40) { + /* endpoint has stalled - mark it halted */ + + usb_endpoint_halt(dev->usb, usb_pipeendpoint(tmp->info)); + return USB_ST_STALL; + } - return status; + + if (status == 0x80) { + /* still active */ + if (!rval) + return USB_ST_DATAUNDERRUN; + } + return uhci_map_status(status, usb_pipeout(tmp->info)); } /* @@ -205,8 +280,8 @@ static struct uhci_qh *uhci_qh_allocate(struct uhci_device *dev) static void uhci_qh_deallocate(struct uhci_qh *qh) { - if (qh->element != 1) - printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element); +// if (qh->element != 1) +// printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element); qh->element = 1; qh->link = 1; @@ -223,8 +298,10 @@ static struct uhci_td *uhci_td_allocate(struct uhci_device *dev) for (; (inuse = test_and_set_bit(0, &td->inuse)) != 0 && td < &dev->td[UHCI_MAXTD]; td++) ; - if (!inuse) + if (!inuse) { + td->inuse = 1; return(td); + } printk("ran out of td's for dev %p\n", dev); return(NULL); @@ -267,19 +344,24 @@ static void uhci_remove_irq_list(struct uhci_td *td) spin_unlock_irqrestore(&irqlist_lock, flags); } + /* * Request a interrupt handler.. + * + * Returns: a "handle pointer" that release_irq can use to stop this + * interrupt. (It's really a pointer to the TD). */ -static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) +static void* uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) { struct uhci_device *dev = usb_to_uhci(usb_dev); + struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub); struct uhci_td *td = uhci_td_allocate(dev); struct uhci_qh *interrupt_qh = uhci_qh_allocate(dev); unsigned int destination, status; /* Destination: pipe destination with INPUT */ - destination = (pipe & 0x0007ff00) | 0x69; + destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); /* Status: slow/fast, Interrupt, Active, Short Packet Detect Infinite Errors */ status = (pipe & (1 << 26)) | (1 << 24) | (1 << 23) | (1 << 29) | (0 << 27); @@ -289,21 +371,315 @@ static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_d interrupt_qh->element); td->link = 1; - td->status = status; /* In */ - td->info = destination | (7 << 21); /* 8 bytes of data */ + td->status = status; + td->info = destination | ((usb_maxpacket(usb_dev, pipe) - 1) << 21) | + (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); td->buffer = virt_to_bus(dev->data); + td->first = td; td->qh = interrupt_qh; - interrupt_qh->skel = &dev->uhci->root_hub->skel_int8_qh; + td->dev = usb_dev; + + /* if period 0, insert into fast q */ + + if (period == 0) { + td->inuse |= 2; + interrupt_qh->skel = &root_hub->skel_int2_qh; + } else + interrupt_qh->skel = &root_hub->skel_int8_qh; uhci_add_irq_list(dev->uhci, td, handler, dev_id); uhci_insert_td_in_qh(interrupt_qh, td); /* Add it into the skeleton */ - uhci_insert_qh(&dev->uhci->root_hub->skel_int8_qh, interrupt_qh); + uhci_insert_qh(interrupt_qh->skel, interrupt_qh); + + return (void*)td; +} + +/* + * Remove running irq td from queues + * + * This function is not used anymore. + */ +#if 0 +static int uhci_remove_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) +{ + struct uhci_device *dev = usb_to_uhci(usb_dev); + struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub); + struct uhci_td *td; + struct uhci_qh *interrupt_qh; + unsigned long flags; + struct list_head *head = &dev->uhci->interrupt_list; + struct list_head *tmp; + + spin_lock_irqsave(&irqlist_lock, flags); + + /* find the TD in the interrupt list */ + + tmp = head->next; + while (tmp != head) { + td = list_entry(tmp, struct uhci_td, irq_list); + if (td->dev_id == dev_id && td->completed == handler) { + + /* found the right one - let's remove it */ + + /* notify removal */ + + td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id); + + /* this is DANGEROUS - not sure whether this is right */ + + list_del(&td->irq_list); + uhci_remove_td(td); + interrupt_qh = td->qh; + uhci_remove_qh(interrupt_qh->skel, interrupt_qh); + uhci_td_deallocate(td); + uhci_qh_deallocate(interrupt_qh); + spin_unlock_irqrestore(&irqlist_lock, flags); + return USB_ST_NOERROR; + } + } + spin_unlock_irqrestore(&irqlist_lock, flags); + return USB_ST_INTERNALERROR; +} +#endif + +/* + * Release an interrupt handler previously allocated using + * uhci_request_irq. This function does no validity checking, so make + * sure you're not releasing an already released handle as it may be + * in use by something else.. + * + * This function can NOT be called from an interrupt. + */ +int uhci_release_irq(void* handle) +{ + struct uhci_td *td; + struct uhci_qh *interrupt_qh; + unsigned long flags; + +#ifdef UHCI_DEBUG + printk("usb-uhci: Releasing irq handle %p\n", handle); +#endif + + td = (struct uhci_td*)handle; + if (td == NULL) + return USB_ST_INTERNALERROR; + + /* Remove it from the internal irq_list */ + spin_lock_irqsave(&irqlist_lock, flags); + list_del(&td->irq_list); + spin_unlock_irqrestore(&irqlist_lock, flags); + + /* Remove the interrupt TD and QH */ + uhci_remove_td(td); + interrupt_qh = td->qh; + uhci_remove_qh(interrupt_qh->skel, interrupt_qh); + + if (td->completed != NULL) + td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id); + + /* Free the TD and QH */ + uhci_td_deallocate(td); + uhci_qh_deallocate(interrupt_qh); + + return USB_ST_NOERROR; +} /* uhci_release_irq() */ + + +/* + * Isochronous thread operations + */ + +static int uhci_compress_isochronous(struct usb_device *usb_dev, void *_isodesc) +{ + struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc; + char *data = isodesc->data; + int i, totlen = 0; + + for (i = 0; i < isodesc->num; i++) { + char *cdata = bus_to_virt(isodesc->td[i].buffer & ~0xF); + int n = (isodesc->td[i].status + 1) & 0x7FF; + + if ((cdata != data) && (n)) + memmove(data, cdata, n); + +#if 0 +if (n && n != 960) + printk("underrun: %d %d\n", i, n); +#endif +if ((isodesc->td[i].status >> 16) & 0xFF) + printk("error: %d %X\n", i, (isodesc->td[i].status >> 16)); + + data += n; + totlen += n; + } + + return totlen; +} + +static int uhci_unschedule_isochronous(struct usb_device *usb_dev, void *_isodesc) +{ + struct uhci_device *dev = usb_to_uhci(usb_dev); + struct uhci *uhci = dev->uhci; + struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc; + int i; + + if ((isodesc->frame < 0) || (isodesc->frame > 1023)) + return 1; + + /* Remove from previous frames */ + for (i = 0; i < isodesc->num; i++) { + /* Turn off Active and IOC bits */ + isodesc->td[i].status &= ~(3 << 23); + uhci->fl->frame[(isodesc->frame + i) % 1024] = isodesc->td[i].link; + } + + isodesc->frame = -1; + + return 0; +} + +/* td points to the one td we allocated for isochronous transfers */ +static int uhci_schedule_isochronous(struct usb_device *usb_dev, void *_isodesc, void *_pisodesc) +{ + struct uhci_device *dev = usb_to_uhci(usb_dev); + struct uhci *uhci = dev->uhci; + struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc; + struct uhci_iso_td *pisodesc = (struct uhci_iso_td *)_pisodesc; + int frame, i; + + if (isodesc->frame != -1) { + printk("isoc queue not removed\n"); + uhci_unschedule_isochronous(usb_dev, isodesc); + } + + /* Insert TD into list */ + if (!pisodesc) { + frame = inw(uhci->io_addr + USBFRNUM) % 1024; + /* HACK: Start 2 frames from now */ + frame = (frame + 2) % 1024; + } else + frame = (pisodesc->endframe + 1) % 1024; + +#if 0 +printk("scheduling first at frame %d\n", frame); +#endif + + for (i = 0; i < isodesc->num; i++) { + /* Active */ + isodesc->td[i].status |= (1 << 23); + isodesc->td[i].backptr = &uhci->fl->frame[(frame + i) % 1024]; + isodesc->td[i].link = uhci->fl->frame[(frame + i) % 1024]; + uhci->fl->frame[(frame + i) % 1024] = virt_to_bus(&isodesc->td[i]); + } + +#if 0 +printk("last at frame %d\n", (frame + i - 1) % 1024); +#endif + + /* Interrupt */ + isodesc->td[i - 1].status |= (1 << 24); + + isodesc->frame = frame; + isodesc->endframe = (frame + isodesc->num - 1) % 1024; + +#if 0 + return uhci_td_result(dev, td[num - 1]); +#endif return 0; } +/* + * Initialize isochronous queue + */ +static void *uhci_allocate_isochronous(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id) +{ + struct uhci_device *dev = usb_to_uhci(usb_dev); + unsigned long destination, status; + struct uhci_td *td; + struct uhci_iso_td *isodesc; + int i; + + isodesc = kmalloc(sizeof(*isodesc), GFP_KERNEL); + if (!isodesc) { + printk("Couldn't allocate isodesc!\n"); + return NULL; + } + memset(isodesc, 0, sizeof(*isodesc)); + + /* Carefully work around the non contiguous pages */ + isodesc->num = (len / PAGE_SIZE) * (PAGE_SIZE / maxsze); + isodesc->td = kmalloc(sizeof(struct uhci_td) * isodesc->num, GFP_KERNEL); + isodesc->frame = isodesc->endframe = -1; + isodesc->data = data; + isodesc->maxsze = maxsze; + + if (!isodesc->td) { + printk("Couldn't allocate td's\n"); + kfree(isodesc); + return NULL; + } + + isodesc->frame = isodesc->endframe = -1; + + /* + * Build the DATA TD's + */ + i = 0; + do { + /* Build the TD for control status */ + td = &isodesc->td[i]; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (pipe & PIPE_DEVEP_MASK) + | usb_packetid (pipe); /* add IN or OUT */ + + /* Status: slow/fast, Active, Isochronous */ + status = (pipe & (1 << 26)) | (1 << 23) | (1 << 25); + + /* + * Build the TD for the control request + */ + td->status = status; + td->info = destination | ((maxsze - 1) << 21); + td->buffer = virt_to_bus(data); + td->first = td; + td->backptr = NULL; + + i++; + + data += maxsze; + + if (((int)data % PAGE_SIZE) + maxsze >= PAGE_SIZE) + data = (char *)(((int)data + maxsze) & ~(PAGE_SIZE - 1)); + + len -= maxsze; + } while (i < isodesc->num); + + /* IOC on the last TD */ + td->status |= (1 << 24); + uhci_add_irq_list(dev->uhci, td, completed, dev_id); + + return isodesc; +} + +static void uhci_delete_isochronous(struct usb_device *usb_dev, void *_isodesc) +{ + struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc; + + /* If it's still scheduled, unschedule them */ + if (isodesc->frame) + uhci_unschedule_isochronous(usb_dev, isodesc); + + /* Remove it from the IRQ list */ + uhci_remove_irq_list(&isodesc->td[isodesc->num - 1]); + + kfree(isodesc->td); + kfree(isodesc); +} + /* * Control thread operations: we just mark the last TD * in a control thread as an interrupt TD, and wake up @@ -312,9 +688,9 @@ static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_d * We need to remove the TD from the lists (both interrupt * list and TD lists) by hand if something bad happens! */ -static struct wait_queue *control_wakeup; +static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); -static int uhci_control_completed(int status, void *buffer, void *dev_id) +static int uhci_control_completed(int status, void *buffer, int len, void *dev_id) { wake_up(&control_wakeup); return 0; /* Don't re-instate */ @@ -323,10 +699,10 @@ static int uhci_control_completed(int status, void *buffer, void *dev_id) /* td points to the last td in the list, which interrupts on completion */ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); struct uhci_qh *ctrl_qh = uhci_qh_allocate(dev); struct uhci_td *curtd; - + struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub); current->state = TASK_UNINTERRUPTIBLE; add_wait_queue(&control_wakeup, &wait); @@ -354,9 +730,18 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru uhci_insert_tds_in_qh(ctrl_qh, first, last); /* Add it into the skeleton */ - uhci_insert_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh); + uhci_insert_qh(&root_hub->skel_control_qh, ctrl_qh); + +// control should be full here... +// printk("control\n"); +// show_status(dev->uhci); +// show_queues(dev->uhci); - schedule_timeout(HZ/10); + schedule_timeout(HZ*5); + +// control should be empty here... +// show_status(dev->uhci); +// show_queues(dev->uhci); remove_wait_queue(&control_wakeup, &wait); @@ -368,11 +753,11 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru #endif /* Remove it from the skeleton */ - uhci_remove_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh); + uhci_remove_qh(&root_hub->skel_control_qh, ctrl_qh); uhci_qh_deallocate(ctrl_qh); - return uhci_td_result(dev, last); + return uhci_td_result(dev, last, NULL); } /* @@ -396,20 +781,21 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru * information, that's just ridiculously high. Most * control messages have just a few bytes of data. */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len) +static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len) { struct uhci_device *dev = usb_to_uhci(usb_dev); struct uhci_td *first, *td, *prevtd; unsigned long destination, status; int ret; + int maxsze = usb_maxpacket(usb_dev, pipe); - if (len > usb_maxpacket(usb_dev->maxpacketsize) * 29) + if (len > maxsze * 29) printk("Warning, too much data for a control packet, crashing\n"); first = td = uhci_td_allocate(dev); /* The "pipe" thing contains the destination in bits 8--18, 0x2D is SETUP */ - destination = (pipe & 0x0007ff00) | 0x2D; + destination = (pipe & PIPE_DEVEP_MASK) | 0x2D; /* Status: slow/fast, Active, Short Packet Detect Three Errors */ status = (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | (3 << 27); @@ -420,6 +806,7 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void td->status = status; /* Try forever */ td->info = destination | (7 << 21); /* 8 bytes of data */ td->buffer = virt_to_bus(cmd); + td->first = td; /* * If direction is "send", change the frame from SETUP (0x2D) @@ -439,7 +826,6 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void while (len > 0) { /* Build the TD for control status */ int pktsze = len; - int maxsze = usb_maxpacket(pipe); if (pktsze > maxsze) pktsze = maxsze; @@ -450,27 +836,31 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void td->status = status; /* Status */ td->info = destination | ((pktsze-1) << 21); /* pktsze bytes of data */ td->buffer = virt_to_bus(data); + td->first = first; td->backptr = &prevtd->link; + data += pktsze; + len -= pktsze; + prevtd = td; td = uhci_td_allocate(dev); prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */ - data += maxsze; - len -= maxsze; } /* - * Build the final TD for control status + * Build the final TD for control status */ destination ^= (0xE1 ^ 0x69); /* OUT -> IN */ destination |= 1 << 19; /* End in Data1 */ - td->link = 1; /* Terminate */ - td->status = status | (1 << 24); /* IOC */ - td->info = destination | (0x7ff << 21); /* 0 bytes of data */ - td->buffer = 0; td->backptr = &prevtd->link; + td->status = (status /* & ~(3 << 27) */) | (1 << 24); /* no limit on final packet */ + td->info = destination | (UHCI_NULL_DATA_SIZE << 21); /* 0 bytes of data */ + td->buffer = 0; + td->first = first; + td->link = 1; /* Terminate */ + /* Start it up.. */ ret = uhci_run_control(dev, first, td); @@ -495,6 +885,192 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void } while (1); } + if (uhci_debug && ret) { + __u8 *p = (__u8 *)cmd; + + printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + } + return ret; +} + + +/* + * Bulk thread operations: we just mark the last TD + * in a bulk thread as an interrupt TD, and wake up + * the front-end on completion. + * + * We need to remove the TD from the lists (both interrupt + * list and TD lists) by hand if something bad happens! + */ +static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); + +static int uhci_bulk_completed(int status, void *buffer, int len, void *dev_id) +{ + wake_up(&bulk_wakeup); + return 0; /* Don't re-instate */ +} + +/* td points to the last td in the list, which interrupts on completion */ +static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, unsigned long *rval) +{ + DECLARE_WAITQUEUE(wait, current); + struct uhci_qh *bulk_qh = uhci_qh_allocate(dev); + struct uhci_td *curtd; + struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub); + + current->state = TASK_UNINTERRUPTIBLE; + add_wait_queue(&bulk_wakeup, &wait); + + uhci_add_irq_list(dev->uhci, last, uhci_bulk_completed, NULL); + + /* FIXME: This is kinda kludged */ + /* Walk the TD list and update the QH pointer */ + { + int maxcount = 100; + + curtd = first; + do { + curtd->qh = bulk_qh; + if (curtd->link & 1) + break; + + curtd = bus_to_virt(curtd->link & ~0xF); + if (!--maxcount) { + printk("runaway tds!\n"); + break; + } + } while (1); + } + + uhci_insert_tds_in_qh(bulk_qh, first, last); + + /* Add it into the skeleton */ + uhci_insert_qh(&root_hub->skel_bulk0_qh, bulk_qh); + +// now we're in the queue... but don't ask WHAT is in there ;-( +// printk("bulk\n"); +// show_status(dev->uhci); +// show_queues(dev->uhci); + + schedule_timeout(HZ*5); +// show_status(dev->uhci); +// show_queues(dev->uhci); + + //show_queue(first->qh); + remove_wait_queue(&bulk_wakeup, &wait); + + /* Clean up in case it failed.. */ + uhci_remove_irq_list(last); + +#if 0 + printk("Looking for tds [%p, %p]\n", dev->control_td, td); +#endif + + /* Remove it from the skeleton */ + uhci_remove_qh(&root_hub->skel_bulk0_qh, bulk_qh); + + uhci_qh_deallocate(bulk_qh); + + return uhci_td_result(dev, last, rval); +} + +/* + * Send or receive a bulk message on a pipe. + * + * Note that the "pipe" structure is set up to map + * easily to the uhci destination fields. + * + * A bulk message is only built up from + * the data phase + * + * The data phase can be an arbitrary number of TD's + * although we currently had better not have more than + * 31 TD's here. + * + * 31 TD's is a minimum of 248 bytes worth of bulk + * information. + */ +static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval) +{ + struct uhci_device *dev = usb_to_uhci(usb_dev); + struct uhci_td *first, *td, *prevtd; + unsigned long destination, status; + int ret; + int maxsze = usb_maxpacket(usb_dev, pipe); + + if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe)) && + usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80))) + return USB_ST_STALL; + + if (len > maxsze * 31) + printk("Warning, too much data for a bulk packet, crashing (%d/%d)\n", len, maxsze); + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); + + /* Status: slow/fast, Active, Short Packet Detect Three Errors */ + status = (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | (3 << 27); + + /* + * Build the TDs for the bulk request + */ + first = td = uhci_td_allocate(dev); + prevtd = first; //This is fake, but at least it's not NULL + while (len > 0) { + /* Build the TD for control status */ + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td->status = status; /* Status */ + td->info = destination | ((pktsze-1) << 21) | + (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); /* pktsze bytes of data */ + td->buffer = virt_to_bus(data); + td->backptr = &prevtd->link; + td->first = first; + + data += maxsze; + len -= maxsze; + + if (len > 0) { + prevtd = td; + td = uhci_td_allocate(dev); + prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */ + } + + /* Alternate Data0/1 (start with Data0) */ + usb_dotoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + } + td->link = 1; /* Terminate */ + td->status |= (1 << 24); /* IOC */ + + /* CHANGE DIRECTION HERE! SAVE IT SOMEWHERE IN THE ENDPOINT!!! */ + + /* Start it up.. */ + ret = uhci_run_bulk(dev, first, td, rval); + + { + int maxcount = 100; + struct uhci_td *curtd = first; + unsigned int nextlink; + + do { + nextlink = curtd->link; + uhci_remove_td(curtd); + uhci_td_deallocate(curtd); + if (nextlink & 1) /* Tail? */ + break; + + curtd = bus_to_virt(nextlink & ~0xF); + if (!--maxcount) { + printk("runaway td's!?\n"); + break; + } + } while (1); + } + return ret; } @@ -554,19 +1130,23 @@ static int uhci_usb_deallocate(struct usb_device *usb_dev) for (i = 0; i < UHCI_MAXTD; ++i) { struct uhci_td *td = dev->td + i; - /* And remove it from the irq list, if it's active */ - if (td->status & (1 << 23)) - uhci_remove_irq_list(td); - - if (td->inuse) + if (td->inuse & 1) { uhci_remove_td(td); + + /* And remove it from the irq list, if it's active */ + if (td->status & (1 << 23)) + td->status &= ~(1 << 23); +#if 0 + uhci_remove_irq_list(td); +#endif + } } /* Remove the td from any queues */ for (i = 0; i < UHCI_MAXQH; ++i) { struct uhci_qh *qh = dev->qh + i; - if (qh->inuse) + if (qh->inuse & 1) uhci_remove_qh(qh->skel, qh); } @@ -581,7 +1161,14 @@ struct usb_operations uhci_device_operations = { uhci_usb_allocate, uhci_usb_deallocate, uhci_control_msg, + uhci_bulk_msg, uhci_request_irq, + uhci_release_irq, + uhci_allocate_isochronous, + uhci_delete_isochronous, + uhci_schedule_isochronous, + uhci_unschedule_isochronous, + uhci_compress_isochronous }; /* @@ -623,7 +1210,7 @@ static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned i struct usb_device *usb_dev; struct uhci_device *dev; unsigned short status; - + struct uhci_device *root_hub=usb_to_uhci(uhci->bus->root_hub); printk("uhci_connect_change: called for %d\n", nr); /* @@ -633,7 +1220,7 @@ static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned i * * So start off by getting rid of any old devices.. */ - usb_disconnect(&uhci->root_hub->usb->children[nr]); + usb_disconnect(&root_hub->usb->children[nr]); status = inw(port); @@ -648,14 +1235,17 @@ static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned i * Ok, we got a new connection. Allocate a device to it, * and find out what it wants to do.. */ - usb_dev = uhci_usb_allocate(uhci->root_hub->usb); + usb_dev = uhci_usb_allocate(root_hub->usb); + if (!usb_dev) + return; + dev = usb_dev->hcpriv; dev->uhci = uhci; usb_connect(usb_dev); - uhci->root_hub->usb->children[nr] = usb_dev; + root_hub->usb->children[nr] = usb_dev; wait_ms(200); /* wait for powerup */ uhci_reset_port(port); @@ -678,8 +1268,9 @@ static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned i */ static void uhci_check_configuration(struct uhci *uhci) { + struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub); unsigned int io_addr = uhci->io_addr + USBPORTSC1; - int maxchild = uhci->root_hub->usb->maxchild; + int maxchild = root_hub->usb->maxchild; int nr = 0; do { @@ -696,6 +1287,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) { struct list_head *head = &uhci->interrupt_list; struct list_head *tmp; + int status; spin_lock(&irqlist_lock); tmp = head->next; @@ -705,25 +1297,41 @@ static void uhci_interrupt_notify(struct uhci *uhci) next = tmp->next; - if (!(td->status & (1 << 23))) { /* No longer active? */ + if (!((status = td->status) & (1 << 23)) || /* No longer active? */ + ((td->qh->element & ~15) && + !((status = uhci_link_to_td(td->qh->element)->status) & (1 <<23)) && + (status & 0x760000) /* is in error state (Stall, db, babble, timeout, bitstuff) */)) { /* remove from IRQ list */ __list_del(tmp->prev, next); INIT_LIST_HEAD(tmp); - if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) { - struct uhci_qh *interrupt_qh = td->qh; - + if (td->completed(uhci_map_status(status, 0), bus_to_virt(td->buffer), -1, td->dev_id)) { list_add(&td->irq_list, &uhci->interrupt_list); - td->info ^= 1 << 19; /* toggle between data0 and data1 */ - td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */ - /* Remove then readd? Is that necessary */ - uhci_remove_td(td); - uhci_insert_td_in_qh(interrupt_qh, td); + if (!(td->status & (1 << 25))) { + struct uhci_qh *interrupt_qh = td->qh; + + usb_dotoggle(td->dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)); + td->info &= ~(1 << 19); /* clear data toggle */ + td->info |= usb_gettoggle(td->dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)) << 19; /* toggle between data0 and data1 */ + td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */ + + /* Remove then readd? Is that necessary */ + uhci_remove_td(td); + uhci_insert_td_in_qh(interrupt_qh, td); + } + } else if (td->inuse & 2) { + struct uhci_qh *interrupt_qh = td->qh; + /* marked for removal */ + td->inuse &= ~2; + usb_dotoggle(td->dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)); + uhci_remove_qh(interrupt_qh->skel, interrupt_qh); + uhci_qh_deallocate(interrupt_qh); + uhci_td_deallocate(td); } /* If completed wants to not reactivate, then it's */ /* responsible for free'ing the TD's and QH's */ /* or another function (such as run_control) */ - } + } tmp = next; } spin_unlock(&irqlist_lock); @@ -740,7 +1348,8 @@ static void uhci_interrupt_notify(struct uhci *uhci) static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr) { if (waitqueue_active(&uhci_configure)) { - int ports = uhci->root_hub->usb->maxchild; + struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub); + int ports = root_hub->usb->maxchild; io_addr += USBPORTSC1; do { if (inw(io_addr) & USBPORTSC_CSC) { @@ -764,6 +1373,9 @@ static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) status = inw(io_addr + USBSTS); outw(status, io_addr + USBSTS); +// if ((status & ~0x21) != 0) +// printk("interrupt: %X\n", status); + /* Walk the list of pending TD's to see which ones completed.. */ uhci_interrupt_notify(uhci); @@ -780,13 +1392,14 @@ static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) */ static void uhci_init_ticktd(struct uhci *uhci) { - struct uhci_device *dev = uhci->root_hub; + struct uhci_device *dev = usb_to_uhci(uhci->bus->root_hub); struct uhci_td *td = uhci_td_allocate(dev); td->link = 1; td->status = (1 << 24); /* interrupt on completion */ td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */ td->buffer = 0; + td->first = td; td->qh = NULL; uhci->fl->frame[0] = virt_to_bus(td); @@ -824,12 +1437,13 @@ static void start_hc(struct uhci *uhci) } } + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); outw(0, io_addr + USBFRNUM); outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD); /* Run and mark it configured with a 64-byte max packet */ - outw(USBCMD_RS | USBCMD_CF, io_addr + USBCMD); + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); } /* @@ -870,10 +1484,12 @@ static struct uhci *alloc_uhci(unsigned int io_addr) /* We need exactly one page (per UHCI specs), how convenient */ uhci->fl = (void *)__get_free_page(GFP_KERNEL); + if (!uhci->fl) + goto au_free_uhci; bus = kmalloc(sizeof(*bus), GFP_KERNEL); if (!bus) - return NULL; + goto au_free_fl; memset(bus, 0, sizeof(*bus)); @@ -893,12 +1509,11 @@ static struct uhci *alloc_uhci(unsigned int io_addr) */ usb = uhci_usb_allocate(NULL); if (!usb) - return NULL; - - dev = uhci->root_hub = usb_to_uhci(usb); + goto au_free_bus; usb->bus = bus; - + dev = usb_to_uhci(usb); + uhci->bus->root_hub=uhci_to_usb(dev); /* Initialize the root hub */ /* UHCI specs says devices must have 2 ports, but goes on to say */ /* they may have more but give no way to determine how many they */ @@ -965,6 +1580,18 @@ static struct uhci *alloc_uhci(unsigned int io_addr) } return uhci; + +/* + * error exits: + */ + +au_free_bus: + kfree (bus); +au_free_fl: + free_page ((unsigned long)uhci->fl); +au_free_uhci: + kfree (uhci); + return NULL; } @@ -979,9 +1606,9 @@ static void release_uhci(struct uhci *uhci) } #if 0 - if (uhci->root_hub) { - uhci_usb_deallocate(uhci_to_usb(uhci->root_hub)); - uhci->root_hub = NULL; + if (uhci->bus->root_hub) { + uhci_usb_deallocate(uhci_to_usb(uhci->bus->root_hub)); + uhci->bus->root_hub = NULL; } #endif @@ -994,11 +1621,10 @@ static void release_uhci(struct uhci *uhci) kfree(uhci); } -void cleanup_drivers(void); - static int uhci_control_thread(void * __uhci) { struct uhci *uhci = (struct uhci *)__uhci; + struct uhci_device * root_hub =usb_to_uhci(uhci->bus->root_hub); lock_kernel(); request_region(uhci->io_addr, 32, "usb-uhci"); @@ -1010,7 +1636,7 @@ static int uhci_control_thread(void * __uhci) printk("uhci_control_thread at %p\n", &uhci_control_thread); exit_mm(current); exit_files(current); - exit_fs(current); + //exit_fs(current); strcpy(current->comm, "uhci-control"); @@ -1018,6 +1644,7 @@ static int uhci_control_thread(void * __uhci) * Ok, all systems are go.. */ start_hc(uhci); + usb_register_bus(uhci->bus); for(;;) { siginfo_t info; int unsigned long signr; @@ -1041,19 +1668,23 @@ static int uhci_control_thread(void * __uhci) if(signr == SIGUSR1) { printk("UHCI queue dump:\n"); show_queues(uhci); + } else if (signr == SIGUSR2) { + uhci_debug = !uhci_debug; + printk("UHCI debug toggle = %x\n", uhci_debug); } else { break; } } } -#if 0 - if(uhci->root_hub) - for(i = 0; i < uhci->root_hub->usb->maxchild; i++) - usb_disconnect(uhci->root_hub->usb->children + i); -#endif + { + int i; + if(root_hub) + for(i = 0; i < root_hub->usb->maxchild; i++) + usb_disconnect(root_hub->usb->children + i); + } - cleanup_drivers(); + usb_deregister_bus(uhci->bus); reset_hc(uhci); release_region(uhci->io_addr, 32); @@ -1084,10 +1715,10 @@ static int found_uhci(int irq, unsigned int io_addr) retval = -EBUSY; if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) == 0) { int pid; - MOD_INC_USE_COUNT; uhci->irq = irq; - pid = kernel_thread(uhci_control_thread, uhci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + pid = kernel_thread(uhci_control_thread, uhci, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); if (pid >= 0) return 0; @@ -1152,18 +1783,6 @@ static int handle_apm_event(apm_event_t event) } #endif -#ifdef MODULE - -void cleanup_module(void) -{ -#ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -#endif -} - -#define uhci_init init_module - -#endif int uhci_init(void) { @@ -1185,29 +1804,24 @@ int uhci_init(void) if (retval < 0) continue; -#ifdef CONFIG_USB_MOUSE - usb_mouse_init(); -#endif -#ifdef CONFIG_USB_KBD - usb_kbd_init(); -#endif - hub_init(); -#ifdef CONFIG_USB_AUDIO - usb_audio_init(); -#endif #ifdef CONFIG_APM apm_register_callback(&handle_apm_event); #endif - return 0; } return retval; } -void cleanup_drivers(void) +#ifdef MODULE +int init_module(void) +{ + return uhci_init(); +} + +void cleanup_module(void) { - hub_cleanup(); -#ifdef CONFIG_USB_MOUSE - usb_mouse_cleanup(); +#ifdef CONFIG_APM + apm_unregister_callback(&handle_apm_event); #endif } +#endif //MODULE diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index f063356acc58..ea76df0cc1fa 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -53,6 +53,8 @@ #define USBPORTSC_PR 0x0200 /* Port Reset */ #define USBPORTSC_SUSP 0x1000 /* Suspend */ +#define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */ + struct uhci_qh { unsigned int link; /* Next queue */ unsigned int element; /* Queue element pointer */ @@ -89,10 +91,23 @@ struct uhci_td { usb_device_irq completed; /* Completion handler routine */ unsigned int *backptr; /* Where to remove this from.. */ void *dev_id; - int inuse; /* Inuse? */ + int inuse; /* Inuse? (b0) Remove (b1)*/ struct uhci_qh *qh; + struct uhci_td *first; + struct usb_device *dev; /* the owning device */ } __attribute__((aligned(32))); +struct uhci_iso_td { + int num; + char *data; + int maxsze; + + struct uhci_td *td; + + int frame; + int endframe; +}; + /* * Note the alignment requirements of the entries * @@ -102,7 +117,7 @@ struct uhci_td { */ struct uhci; -#define UHCI_MAXTD 64 +#define UHCI_MAXTD 64 #define UHCI_MAXQH 16 @@ -124,9 +139,11 @@ struct uhci_device { * The root hub pre-allocated QH's and TD's have * some special global uses.. */ +#if 0 #define control_td td /* Td's 0-30 */ /* This is only for the root hub's TD list */ #define tick_td td[31] +#endif /* * There are various standard queues. We set up several different @@ -152,12 +169,12 @@ struct uhci_device { * Linus: * * generic-iso-QH -> dev1-iso-QH -> generic-irq-QH -> dev1-irq-QH -> ... - * | | | | - * End dev1-iso-TD1 End dev1-irq-TD1 - * | - * dev1-iso-TD2 - * | - * .... + * | | | | + * End dev1-iso-TD1 End dev1-irq-TD1 + * | + * dev1-iso-TD2 + * | + * .... * * This may vary a bit (the UHCI docs don't explicitly say you can put iso * transfers in QH's and all of their pictures don't have that either) but @@ -211,8 +228,6 @@ struct uhci { /* These are "standard" QH's for the entire bus */ struct uhci_qh qh[UHCI_MAXQH]; #endif - struct uhci_device *root_hub; /* Root hub device descriptor.. */ - struct uhci_framelist *fl; /* Frame list */ struct list_head interrupt_list; /* List of interrupt-active TD's for this uhci */ }; diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c new file mode 100644 index 000000000000..2103789a8868 --- /dev/null +++ b/drivers/usb/usb-core.c @@ -0,0 +1,103 @@ +/* + * driver/usb/usb-core.c + * + * (C) Copyright David Waite 1999 + * based on code from usb.c, by Linus Torvolds + * + * The purpose of this file is to pull any and all generic modular code from + * usb.c and put it in a separate file. This way usb.c is kept as a generic + * library, while this file handles starting drivers, etc. + * + */ +#include +#include +#include + +#include "inits.h" +#include "usb.h" + +#ifndef CONFIG_USB_MODULE +# ifdef CONFIG_USB_UHCI + int uhci_init(void); +# endif +# ifdef CONFIG_USB_OHCI + int ohci_init(void); +# endif +# ifdef CONFIG_USB_OHCI_HCD + int ohci_hcd_init(void); +# endif +#endif + + +int usb_init(void) +{ +#ifndef CONFIG_USB_MODULE +# ifdef CONFIG_USB_UHCI + uhci_init(); +# endif +# ifdef CONFIG_USB_OHCI + ohci_init(); +# endif +# ifdef CONFIG_USB_OHCI_HCD + ohci_hcd_init(); +# endif +# ifdef CONFIG_USB_MOUSE + usb_mouse_init(); +# endif +# ifdef CONFIG_USB_KBD + usb_kbd_init(); +# endif +# ifdef CONFIG_USB_AUDIO + usb_audio_init(); +# endif +# ifdef CONFIG_USB_ACM + usb_acm_init(); +# endif +# ifdef CONFIG_USB_PRINTER + usb_printer_init(); +# endif +# ifdef CONFIG_USB_CPIA + usb_cpia_init(); +# endif +# ifdef CONFIG_USB_HUB + usb_hub_init(); +# endif +# ifdef CONFIG_USB_SCSI + usb_scsi_init(); +# endif +#endif +#ifdef CONFIG_USB_PROC + proc_usb_init (); +#endif + return 0; +} +/* + * Clean up when unloading the module + */ +void cleanup_drivers(void) +{ +#ifdef CONFIG_USB_PROC + proc_usb_cleanup (); +#endif +#ifndef MODULE +# ifdef CONFIG_USB_HUB + usb_hub_cleanup(); +# endif +# ifdef CONFIG_USB_MOUSE + usb_mouse_cleanup(); +# endif +#endif +} + +#ifdef MODULE +int init_module(void) +{ + return usb_init(); +} +void cleanup_module(void) +{ + cleanup_drivers(); +} +#endif + + diff --git a/drivers/usb/usb-debug.c b/drivers/usb/usb-debug.c index 86d08cd787f1..e656cc6c4c8e 100644 --- a/drivers/usb/usb-debug.c +++ b/drivers/usb/usb-debug.c @@ -24,11 +24,18 @@ static void usb_show_interface(struct usb_interface_descriptor *interface) static void usb_show_config(struct usb_config_descriptor *config) { - int i; - - usb_show_config_descriptor(config); - for (i = 0 ; i < config->bNumInterfaces; i++) - usb_show_interface(config->interface + i); + int i, j; + struct usb_alternate_setting *as; + + usb_show_config_descriptor(config); + for (i = 0; i < config->num_altsetting; i++) { + as = config->altsetting + i; + if ((as) == NULL) + break; + printk("\n Alternate Setting: %d\n", i); + for (j = 0 ; j < config->bNumInterfaces; j++) + usb_show_interface(as->interface + j); + } } void usb_show_device(struct usb_device *dev) @@ -46,12 +53,21 @@ void usb_show_device(struct usb_device *dev) */ void usb_show_device_descriptor(struct usb_device_descriptor *desc) { - printk(" USB version %x.%02x\n", desc->bcdUSB >> 8, desc->bcdUSB & 0xff); - printk(" Vendor: %04x\n", desc->idVendor); - printk(" Product: %04x\n", desc->idProduct); - printk(" Configurations: %d\n", desc->bNumConfigurations); - - printk(" Device Class: %d\n", desc->bDeviceClass); + printk(" Length = %2d%s\n", desc->bLength, + desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)"); + printk(" DescriptorType = %02x\n", desc->bDescriptorType); + + printk(" USB version = %x.%02x\n", + desc->bcdUSB >> 8, desc->bcdUSB & 0xff); + printk(" Vendor:Product = %04x:%04x\n", + desc->idVendor, desc->idProduct); + printk(" MaxPacketSize0 = %d\n", desc->bMaxPacketSize0); + printk(" NumConfigurations = %d\n", desc->bNumConfigurations); + printk(" Device version = %x.%02x\n", + desc->bcdDevice >> 8, desc->bcdDevice & 0xff); + + printk(" Device Class:SubClass:Protocol = %02x:%02x:%02x\n", + desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol); switch (desc->bDeviceClass) { case 0: printk(" Per-interface classes\n"); @@ -71,7 +87,7 @@ void usb_show_config_descriptor(struct usb_config_descriptor * desc) { printk("Configuration:\n"); printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == 9 ? "" : " (!!!)"); + desc->bLength == USB_DT_CONFIG_SIZE ? "" : " (!!!)"); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); printk(" wTotalLength = %04x\n", desc->wTotalLength); printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces); @@ -85,14 +101,13 @@ void usb_show_interface_descriptor(struct usb_interface_descriptor * desc) { printk(" Interface:\n"); printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == 9 ? "" : " (!!!)"); + desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)"); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber); printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting); printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints); - printk(" bInterfaceClass = %02x\n", desc->bInterfaceClass); - printk(" bInterfaceSubClass = %02x\n", desc->bInterfaceSubClass); - printk(" bInterfaceProtocol = %02x\n", desc->bInterfaceProtocol); + printk(" bInterface Class:SubClass:Protocol = %02x:%02x:%02x\n", + desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol); printk(" iInterface = %02x\n", desc->iInterface); } @@ -101,7 +116,7 @@ void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor * desc) char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" }; printk(" Endpoint:\n"); printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == 7 ? "" : " (!!!)"); + desc->bLength == USB_DT_ENDPOINT_SIZE ? "" : " (!!!)"); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress, (desc->bEndpointAddress & 0x80) ? "in" : "out"); @@ -124,4 +139,10 @@ void usb_show_hub_descriptor(struct usb_hub_descriptor * desc) printk("\n"); } +void usb_show_string(struct usb_device* dev, char *id, int index) +{ + char *p = usb_string(dev, index); + if (p != 0) + printk(KERN_INFO "%s: %s\n", id, p); +} diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index f5e89ea8986c..fdfe24f33f56 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1,5 +1,5 @@ /* - * driver/usb/usb.c + * drivers/usb/usb.c * * (C) Copyright Linus Torvalds 1999 * @@ -36,6 +36,7 @@ * 6 wLength 2 Count Bytes for data */ +#include #include #include #include @@ -46,25 +47,113 @@ * We have a per-interface "registered driver" list. */ static LIST_HEAD(usb_driver_list); +static LIST_HEAD(usb_bus_list); int usb_register(struct usb_driver *new_driver) { + struct list_head *tmp = usb_bus_list.next; /* Add it to the list of known drivers */ list_add(&new_driver->driver_list, &usb_driver_list); /* - * We should go through all existing devices, and see if any of - * them would be acceptable to the new driver.. Let's do that - * in version 2.0. + * We go through all existing devices, and see if any of them would + * be acceptable to the new driver.. This is done using a depth-first + * search for devices without a registered driver already, then + * running 'probe' with each of the drivers registered on every one + * of these. */ + while (tmp!= &usb_bus_list) { + struct usb_bus * bus = list_entry(tmp,struct + usb_bus,bus_list); + tmp=tmp->next; + usb_check_support(bus->root_hub); + } return 0; } void usb_deregister(struct usb_driver *driver) { + struct list_head *tmp = usb_bus_list.next; + /*first we remove the driver, to be sure it doesn't get used by + *another thread while we are stepping through removing entries + */ list_del(&driver->driver_list); + printk(KERN_INFO "usbcore: deregistering driver\n"); + while (tmp!= &usb_bus_list) { + struct usb_bus * bus = list_entry(tmp,struct + usb_bus,bus_list); + tmp=tmp->next; + usb_driver_purge(driver,bus->root_hub); + } +} + +/* This function is part of a depth-first search down the device tree, + * removing any instances of a device driver. + */ +void usb_driver_purge(struct usb_driver *driver,struct usb_device *dev) +{ + int i; + if (dev==NULL){ + printk(KERN_ERR "null device being passed in!!!\n"); + return; + } + for (i=0;ichildren[i]!=NULL) + usb_driver_purge(driver,dev->children[i]); + /*now we check this device*/ + if(dev->driver==driver) { + /* + * Note: this is not the correct way to do this, this + * uninitializes and reinitializes EVERY driver + */ + printk(KERN_ERR "disconnecting driverless device\n"); + dev->driver->disconnect(dev); + dev->driver=NULL; + /* This will go back through the list looking for a driver + * that can handle the device + */ + usb_device_descriptor(dev); + } +} + +/* + * New functions for (de)registering a controller + */ +void usb_register_bus(struct usb_bus *new_bus) +{ + /* Add it to the list of buses */ + list_add(&new_bus->bus_list, &usb_bus_list); + printk(KERN_DEBUG "New bus registered\n"); +} + +void usb_deregister_bus(struct usb_bus *bus) +{ + /* NOTE: make sure that all the devices are removed by the + * controller code, as well as having it call this when cleaning + * itself up + */ + list_del(&bus->bus_list); } +/* + * This function is for doing a depth-first search for devices which + * have support, for dynamic loading of driver modules. + */ +void usb_check_support(struct usb_device *dev) +{ + int i; + if (dev==NULL) + { + printk(KERN_ERR "null device being passed in!!!\n"); + return; + } + for (i=0;ichildren[i]!=NULL) + usb_check_support(dev->children[i]); + /*now we check this device*/ + if (dev->driver==NULL) + usb_device_descriptor(dev); +} /* * This entrypoint gets called for each new device. * @@ -72,25 +161,24 @@ void usb_deregister(struct usb_driver *driver) * looking for one that will accept this device as * his.. */ -void usb_device_descriptor(struct usb_device *dev) +int usb_device_descriptor(struct usb_device *dev) { struct list_head *tmp = usb_driver_list.next; while (tmp != &usb_driver_list) { - struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); + struct usb_driver *driver = list_entry(tmp, struct usb_driver, + driver_list); tmp = tmp->next; if (driver->probe(dev)) continue; dev->driver = driver; - return; + return 1; } - /* * Ok, no driver accepted the device, so show the info * for debugging.. */ - printk("Unknown new USB device:\n"); - usb_show_device(dev); + return 0; } /* @@ -109,8 +197,8 @@ static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desc if (len < descindex) return -1; - n_desc = *(unsigned short *)ptr; - n_len = n_desc & 0xff; + n_desc = le16_to_cpup((unsigned short *)ptr); + n_len = ptr[0]; if (n_desc == ((desctype << 8) + descindex)) break; @@ -118,28 +206,30 @@ static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desc if (((n_desc >> 8)&0xFF) == desctype && n_len > descindex) { - printk("bug: oversized descriptor.\n"); + printk(KERN_DEBUG "bug: oversized descriptor.\n"); break; } if (n_len < 2 || n_len > len) { - printk("Short descriptor.\n"); + printk(KERN_DEBUG "Short descriptor\n"); return -1; } - printk( - "Expected descriptor %02X/%02X, got %02X/%02X - skipping\n", - desctype, descindex, - (n_desc >> 8) & 0xFF, n_desc & 0xFF); - for (i = 0 ; i < n_len; i++) - printk(" %d %02x\n", i, ptr[i]); + printk(KERN_DEBUG + "Expected descriptor %02X/%02X, got %02X/%02X - skipping", + desctype, descindex, ptr[1], ptr[0]); + for (i = 0; i < n_len; i++) { + if (i % 16 == 0) + printk("\n" KERN_DEBUG); + printk(" %02x", ptr[i]); + } + printk("\n"); len -= n_len; ptr += n_len; parsed += n_len; } - printk("Found %02X:%02X\n", - desctype, descindex); + printk(KERN_DEBUG "Found %02X:%02X\n", desctype, descindex); return parsed; } @@ -152,9 +242,15 @@ static int usb_check_descriptor(unsigned char *ptr, int len, unsigned char desct { int n_len = ptr[0]; + if (len <= 0) + return -1; + if (n_len < 2 || n_len > len) { - printk("Short descriptor.\n"); + int i; + printk(KERN_DEBUG "Short descriptor. (%d, %d):\n", len, n_len); + for (i = 0; i < len; ++i) + printk(KERN_DEBUG " %d: %x\n", i, ptr[i]); return -1; } @@ -167,15 +263,16 @@ static int usb_check_descriptor(unsigned char *ptr, int len, unsigned char desct static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len) { - int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7); + int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE); int i; if (parsed < 0) return parsed; memcpy(endpoint, ptr + parsed, ptr[parsed]); + le16_to_cpus(&endpoint->wMaxPacketSize); parsed += ptr[parsed]; - len -= ptr[parsed]; + len -= parsed; while((i = usb_check_descriptor(ptr+parsed, len, 0x25))>=0) { @@ -190,7 +287,7 @@ static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descri static int usb_parse_interface(struct usb_device *dev, struct usb_interface_descriptor *interface, unsigned char *ptr, int len) { int i; - int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9); + int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE); int retval; if (parsed < 0) @@ -237,7 +334,9 @@ static int usb_parse_interface(struct usb_device *dev, struct usb_interface_desc static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len) { - int i; + int i, j; + int retval; + struct usb_alternate_setting *as; int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9); if (parsed < 0) @@ -246,6 +345,12 @@ static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor memcpy(config, ptr + parsed, *ptr); len -= *ptr; parsed += *ptr; + le16_to_cpus(&config->wTotalLength); + + if (config->MaxPower == 200) { + printk(KERN_ERR "bNumInterfaces kludge\n"); + config->bNumInterfaces += 3; + } if (config->bNumInterfaces > USB_MAXINTERFACES) { @@ -254,23 +359,59 @@ static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor } - config->interface = (struct usb_interface_descriptor *) + config->altsetting = (struct usb_alternate_setting *) + kmalloc(USB_MAXALTSETTING * sizeof(struct usb_alternate_setting), GFP_KERNEL); + if (config->altsetting == NULL) { + printk(KERN_WARNING "usb: out of memory.\n"); + return -1; + } + config->act_altsetting = 0; + config->num_altsetting = 1; + + config->altsetting->interface = (struct usb_interface_descriptor *) kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL); - if(config->interface==NULL) + if(config->altsetting->interface==NULL) { printk(KERN_WARNING "usb: out of memory.\n"); return -1; } - memset(config->interface, 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor)); + memset(config->altsetting->interface, + 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor)); for (i = 0; i < config->bNumInterfaces; i++) { - int retval = usb_parse_interface(dev, config->interface + i, ptr + parsed, len); + retval = usb_parse_interface(dev, config->altsetting->interface + i, ptr + parsed, len); if (retval < 0) return parsed; // HACK // return retval; parsed += retval; len -= retval; } + + printk(KERN_DEBUG "parsed = %d len = %d\n", parsed, len); + + // now parse for additional alternate settings + for (j = 1; j < USB_MAXALTSETTING; j++) { + retval = usb_expect_descriptor(ptr + parsed, len, USB_DT_INTERFACE, 9); + if (retval) + break; + config->num_altsetting++; + as = config->altsetting + j; + as->interface = (struct usb_interface_descriptor *) + kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL); + if (as->interface == NULL) { + printk(KERN_WARNING "usb: out of memory.\n"); + return -1; + } + memset(as->interface, 0, config->bNumInterfaces * sizeof(struct usb_interface_descriptor)); + for (i = 0; i < config->bNumInterfaces; i++) { + retval = usb_parse_interface(dev, as->interface + i, + ptr + parsed, len); + if (retval < 0) + return parsed; + parsed += retval; + len -= retval; + } + } return parsed; } @@ -298,34 +439,58 @@ int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes) if (retval < 0) return retval; ptr += retval; - bytes += retval; + bytes -= retval; } + if (bytes) + printk(KERN_WARNING "usb: %d bytes of extra configuration data left\n", bytes); return 0; } void usb_destroy_configuration(struct usb_device *dev) { - int c, i; + int c, a, i; struct usb_config_descriptor *cf; + struct usb_alternate_setting *as; struct usb_interface_descriptor *ifp; if(dev->config==NULL) return; - for(c=0;cdescriptor.bNumConfigurations;c++) + + for(c = 0; c < dev->descriptor.bNumConfigurations; c++) { - cf=&dev->config[c]; - if(cf->interface==NULL) - break; - for(i=0;ibNumInterfaces;i++) + cf = &dev->config[c]; + if (cf->altsetting == NULL) + break; + for (a = 0; a < cf->num_altsetting; a++) { - ifp=&cf->interface[i]; - if(ifp->endpoint==NULL) - break; - kfree(ifp->endpoint); + as = &cf->altsetting[a]; + if (as->interface == NULL) + break; + for(i=0;ibNumInterfaces;i++) + { + ifp = &as->interface[i]; + if(ifp->endpoint==NULL) + break; + kfree(ifp->endpoint); + } + kfree(as->interface); } - kfree(cf->interface); + kfree(cf->altsetting); } kfree(dev->config); + + for (i = 1; i < USB_MAXSTRINGS; ++i) { + if (dev->stringindex[i]) { + kfree(dev->stringindex[i]); + dev->stringindex[i] = 0; + } + } +#if 0 + if (dev->stringindex) + kfree(dev->stringindex); + if (dev->stringtable) + kfree(dev->stringtable); +#endif } void usb_init_root_hub(struct usb_device *dev) @@ -346,7 +511,7 @@ void usb_disconnect(struct usb_device **pdev) *pdev = NULL; - printk("USB disconnect on device %d\n", dev->devnum); + printk(KERN_INFO "USB disconnect on device %d\n", dev->devnum); if(dev->driver) dev->driver->disconnect(dev); @@ -403,6 +568,8 @@ int usb_set_address(struct usb_device *dev) int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) { devrequest dr; + int i = 5; + int result; dr.requesttype = 0x80; dr.request = USB_REQ_GET_DESCRIPTOR; @@ -410,12 +577,38 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char dr.index = 0; dr.length = size; + while (i--) { + if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size)) + || result == USB_ST_STALL) + break; + } + return result; +} + +int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size) +{ + devrequest dr; + + dr.requesttype = 0x80; + dr.request = USB_REQ_GET_DESCRIPTOR; + dr.value = (USB_DT_STRING << 8) + index; + dr.index = langid; + dr.length = size; + return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size); } int usb_get_device_descriptor(struct usb_device *dev) { - return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor)); + int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, + sizeof(dev->descriptor)); + if (ret == 0) { + le16_to_cpus(&dev->descriptor.bcdUSB); + le16_to_cpus(&dev->descriptor.idVendor); + le16_to_cpus(&dev->descriptor.idProduct); + le16_to_cpus(&dev->descriptor.bcdDevice); + } + return ret; } int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) @@ -457,6 +650,19 @@ int usb_set_port_feature(struct usb_device *dev, int port, int feature) return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); } +int usb_get_status (struct usb_device *dev, int type, int target, void *data) +{ + devrequest dr; + + dr.requesttype = USB_DIR_IN | type; /* USB_RECIP_DEVICE, _INTERFACE, or _ENDPOINT */ + dr.request = USB_REQ_GET_STATUS; + dr.value = 0; + dr.index = target; + dr.length = 2; + + return dev->bus->op->control_msg (dev, usb_rcvctrlpipe (dev,0), &dr, data, 2); +} + int usb_get_hub_status(struct usb_device *dev, void *data) { devrequest dr; @@ -534,19 +740,120 @@ int usb_set_idle(struct usb_device *dev, int duration, int report_id) return 0; } -int usb_set_configuration(struct usb_device *dev, int configuration) +static void usb_set_maxpacket(struct usb_device *dev) +{ + int i; + struct usb_endpoint_descriptor *ep; + int act_as = dev->actconfig->act_altsetting; + struct usb_alternate_setting *as = dev->actconfig->altsetting + act_as; + struct usb_interface_descriptor *ip = as->interface; + + for (i=0; iactconfig->bNumInterfaces; i++) { + if (as->interface[i].bInterfaceNumber == dev->ifnum) { + ip = &as->interface[i]; + break; + } + } + ep = ip->endpoint; + for (i=0; ibNumEndpoints; i++) { + dev->epmaxpacket[ep[i].bEndpointAddress & 0x0f] = ep[i].wMaxPacketSize; + } +} + +int usb_clear_halt(struct usb_device *dev, int endp) +{ + devrequest dr; + int result; + __u16 status; + + //if (!usb_endpoint_halted(dev, endp)) + // return 0; + + dr.requesttype = USB_RT_ENDPOINT; + dr.request = USB_REQ_CLEAR_FEATURE; + dr.value = 0; + dr.index = endp; + dr.length = 0; + + result = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + + /* dont clear if failed */ + if (result) { + return result; + } + +#if 1 /* lets be really tough */ + dr.requesttype = 0x80 | USB_RT_ENDPOINT; + dr.request = USB_REQ_GET_STATUS; + dr.length = 2; + status = 0xffff; + + result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 2); + + if (result) { + return result; + } + if (status & 1) { + return 1; /* still halted */ + } +#endif + usb_endpoint_running(dev, endp & 0x0f); + + /* toggle is reset on clear */ + + usb_settoggle(dev, endp & 0x0f, ((endp >> 7) & 1) ^ 1, 0); + + return 0; +} + +int usb_set_interface(struct usb_device *dev, int interface, int alternate) { devrequest dr; + dr.requesttype = 1; + dr.request = USB_REQ_SET_INTERFACE; + dr.value = alternate; + dr.index = interface; + dr.length = 0; + + if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) + return -1; + + dev->ifnum = interface; + dev->actconfig->act_altsetting = alternate; + usb_set_maxpacket(dev); + return 0; +} + +int usb_set_configuration(struct usb_device *dev, int configuration) +{ + devrequest dr; + int i; + struct usb_config_descriptor *cp = NULL; + dr.requesttype = 0; dr.request = USB_REQ_SET_CONFIGURATION; dr.value = configuration; dr.index = 0; dr.length = 0; + for (i=0; idescriptor.bNumConfigurations; i++) { + if (dev->config[i].bConfigurationValue == configuration) { + cp = &dev->config[i]; + break; + } + } + if (!cp) { + printk(KERN_INFO "usb: selecting invalid configuration %d\n", configuration); + return -1; + } if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) return -1; + dev->actconfig = cp; + dev->toggle[0] = 0; + dev->toggle[1] = 0; + usb_set_maxpacket(dev); return 0; } @@ -569,30 +876,139 @@ int usb_get_report(struct usb_device *dev) int usb_get_configuration(struct usb_device *dev) { - unsigned int cfgno,size; - unsigned char buffer[400]; + unsigned int cfgno; unsigned char * bufptr; - - bufptr=buffer; - for (cfgno=0;cfgnodescriptor.bNumConfigurations;cfgno++) { + unsigned char * buffer; + int parse; + + buffer = (unsigned char *) __get_free_page (GFP_KERNEL); + if (!buffer) + return -1; + + bufptr = buffer; + for (cfgno = 0 ; cfgno < dev->descriptor.bNumConfigurations ; cfgno++) { + unsigned int size; /* Get the first 8 bytes - guaranteed */ - if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8)) + if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8)) { + __free_page ((struct page *) buffer); return -1; + } /* Get the full buffer */ - size = *(unsigned short *)(bufptr+2); - if (bufptr+size > buffer+sizeof(buffer)) - { + size = le16_to_cpup((unsigned short *)(bufptr+2)); + if (bufptr+size > buffer+PAGE_SIZE) { printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size); - size = buffer+sizeof(buffer)-bufptr; + size = buffer+PAGE_SIZE-bufptr; } - if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size)) + if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size)) { + __free_page ((struct page *) buffer); return -1; + } /* Prepare for next configuration */ - bufptr+=size; + bufptr += size; } - return usb_parse_configuration(dev, buffer, size); + parse = usb_parse_configuration(dev, buffer, bufptr - buffer); + __free_page ((struct page *) buffer); + return parse; +} + +#if 0 +int usb_get_stringtable(struct usb_device *dev) +{ + int i; + int maxindex; + int langid; + unsigned char buffer[256]; + int totalchars; + struct usb_string_descriptor *sd = (struct usb_string_descriptor *)buffer; + char *string; + __u8 bLengths[USB_MAXSTRINGS+1]; + int j; + + dev->maxstring = 0; + if(usb_get_string(dev, 0, 0, buffer, 2) || + usb_get_string(dev, 0, 0, buffer, sd->bLength)) + return -1; + /* we are going to assume that the first ID is good */ + langid = le16_to_cpup(&sd->wData[0]); + + /* whip through and find total length and max index */ + for (maxindex = 1, totalchars = 0; maxindex<=USB_MAXSTRINGS; maxindex++) { + if(usb_get_string(dev, langid, maxindex, buffer, 2)) + break; + totalchars += (sd->bLength - 2)/2 + 1; + bLengths[maxindex] = sd->bLength; + } + if (--maxindex <= 0) + return -1; + + /* get space for strings and index */ + dev->stringindex = kmalloc(sizeof(char *) * (maxindex+1), GFP_KERNEL); + if (!dev->stringindex) + return -1; + dev->stringtable = kmalloc(totalchars, GFP_KERNEL); + if (!dev->stringtable) { + kfree(dev->stringindex); + dev->stringindex = NULL; + return -1; + } + + /* fill them in */ + memset(dev->stringindex, 0, sizeof(char *) * (maxindex+1)); + for (i=1, string = dev->stringtable; i <= maxindex; i++) { + if (usb_get_string(dev, langid, i, buffer, bLengths[i])) + continue; + dev->stringindex[i] = string; + for (j=0; j < (bLengths[i] - 2)/2; j++) { + *string++ = le16_to_cpup(&sd->wData[j]); + } + *string++ = '\0'; + } + dev->maxstring = maxindex; + return 0; +} +#endif + +char *usb_string(struct usb_device *dev, int index) +{ + int len, i; + char *ptr; + union { + unsigned char buffer[256]; + struct usb_string_descriptor desc; + } u; + + if (index <= 0 || index >= USB_MAXSTRINGS) + return 0; + if (dev->stringindex[index] != 0) + return dev->stringindex[index]; + + if (dev->string_langid == 0) { + /* read string descriptor 0 */ + if (usb_get_string(dev, 0, 0, u.buffer, 2) == 0 + && u.desc.bLength >= 4 + && usb_get_string(dev, 0, 0, u.buffer, 4) == 0) + dev->string_langid = le16_to_cpup(&u.desc.wData[0]); + dev->string_langid |= 0x10000; /* so it's non-zero */ + } + + if (usb_get_string(dev, dev->string_langid, index, u.buffer, 2) + || usb_get_string(dev, dev->string_langid, index, u.buffer, + u.desc.bLength)) + return 0; + + len = u.desc.bLength / 2; /* includes terminating null */ + ptr = kmalloc(len, GFP_KERNEL); + if (ptr == 0) + return 0; + + for (i = 0; i < len - 1; ++i) + ptr[i] = le16_to_cpup(&u.desc.wData[i]); + ptr[i] = 0; + + dev->stringindex[index] = ptr; + return ptr; } /* @@ -604,30 +1020,34 @@ void usb_new_device(struct usb_device *dev) { int addr, i; - printk("USB new device connect, assigned device number %d\n", + printk(KERN_INFO "USB new device connect, assigned device number %d\n", dev->devnum); dev->maxpacketsize = 0; /* Default to 8 byte max packet size */ + dev->epmaxpacket[0] = 8; addr = dev->devnum; dev->devnum = 0; +#if 1 /* Slow devices */ for (i = 0; i < 5; i++) { if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8)) break; - printk("get_descriptor failed, waiting\n"); + printk(KERN_ERR "get_descriptor failed, waiting\n"); wait_ms(200); } if (i == 5) { - printk("giving up\n"); + printk(KERN_ERR "giving up\n"); return; } +#endif #if 0 printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0); #endif + dev->epmaxpacket[0] = dev->descriptor.bMaxPacketSize0; switch (dev->descriptor.bMaxPacketSize0) { case 8: dev->maxpacketsize = 0; break; case 16: dev->maxpacketsize = 1; break; @@ -640,34 +1060,98 @@ void usb_new_device(struct usb_device *dev) dev->devnum = addr; +#if 1 if (usb_set_address(dev)) { - printk("Unable to set address\n"); + printk(KERN_ERR "Unable to set address\n"); /* FIXME: We should disable the port */ return; } +#else + usb_set_address(dev); +#endif wait_ms(10); /* Let the SET_ADDRESS settle */ if (usb_get_device_descriptor(dev)) { - printk("Unable to get device descriptor\n"); + printk(KERN_ERR "Unable to get device descriptor\n"); return; } if (usb_get_configuration(dev)) { - printk("Unable to get configuration\n"); + printk(KERN_ERR "Unable to get configuration\n"); return; } + /* usb_get_stringtable(dev); */ + + dev->actconfig = dev->config; + dev->ifnum = 0; + usb_set_maxpacket(dev); + + usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer); + usb_show_string(dev, "Product", dev->descriptor.iProduct); + usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber); + #if 0 printk("Vendor: %X\n", dev->descriptor.idVendor); printk("Product: %X\n", dev->descriptor.idProduct); #endif - usb_device_descriptor(dev); + if (usb_device_descriptor(dev)==0) + { + /* + * Ok, no driver accepted the device, so show the info for + * debugging + */ + printk (KERN_WARNING "Unknown new USB device:\n"); + usb_show_device(dev); + } } -int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) +void* usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) { return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id); } + +void *usb_allocate_isochronous (struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id) +{ + return usb_dev->bus->op->alloc_isoc (usb_dev, pipe, data, len, maxsze, completed, dev_id); +} + +void usb_delete_isochronous (struct usb_device *dev, void *_isodesc) +{ + return dev->bus->op->delete_isoc (dev, _isodesc); +} + +int usb_schedule_isochronous (struct usb_device *usb_dev, void *_isodesc, void *_pisodesc) +{ + return usb_dev->bus->op->sched_isoc (usb_dev, _isodesc, _pisodesc); +} + +int usb_unschedule_isochronous (struct usb_device *usb_dev, void *_isodesc) +{ + return usb_dev->bus->op->unsched_isoc (usb_dev, _isodesc); +} + +int usb_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) +{ + return usb_dev->bus->op->compress_isoc (usb_dev, _isodesc); +} + +int usb_release_irq(struct usb_device *dev, void* handle) +{ + return dev->bus->op->release_irq(handle); +} + +#ifdef CONFIG_PROC_FS +struct list_head * usb_driver_get_list (void) +{ + return &usb_driver_list; +} + +struct list_head * usb_bus_get_list (void) +{ + return &usb_bus_list; +} +#endif diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index 10379ef88da4..7414f6a51189 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -6,6 +6,24 @@ #include #include +#include +#if LINUX_VERSION_CODE < 0x020300 +#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL} +#define wait_queue_head_t struct wait_queue * +#define init_waitqueue_head(w) *(w) = 0 +#define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED +#endif + +extern int usb_hub_init(void); +extern int usb_kbd_init(void); +extern int usb_cpia_init(void); +extern int usb_mouse_init(void); +extern int usb_printer_init(void); + +extern void hub_cleanup(void); +extern void usb_mouse_cleanup(void); + static __inline__ void wait_ms(unsigned int ms) { current->state = TASK_UNINTERRUPTIBLE; @@ -22,9 +40,16 @@ typedef struct { } devrequest; /* - * Class codes + * Device and/or Interface Class codes */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 #define USB_CLASS_HUB 9 +#define USB_CLASS_VENDOR_SPEC 0xff /* * Descriptor types @@ -38,6 +63,28 @@ typedef struct { #define USB_DT_HUB 0x29 #define USB_DT_HID 0x21 +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_HUB_NONVAR_SIZE 7 + +/* + * USB Request Type and Endpoint Directions + */ +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SETUP 0x2d + /* * Standard requests */ @@ -87,6 +134,32 @@ typedef struct { #define USB_RT_HIDD (USB_TYPE_CLASS | USB_RECIP_INTERFACE) +/* + * Status codes (these follow an OHCI controllers condition codes) + */ +#define USB_ST_NOERROR 0x0 +#define USB_ST_CRC 0x1 +#define USB_ST_BITSTUFF 0x2 +#define USB_ST_DTMISMATCH 0x3 /* data toggle mismatch */ +#define USB_ST_STALL 0x4 +#define USB_ST_NORESPONSE 0x5 /* device not responding/handshaking */ +#define USB_ST_PIDCHECK 0x6 /* Check bits on PID failed */ +#define USB_ST_PIDUNDEF 0x7 /* PID unexpected/undefined */ +#define USB_ST_DATAOVERRUN 0x8 +#define USB_ST_DATAUNDERRUN 0x9 +#define USB_ST_RESERVED1 0xA +#define USB_ST_RESERVED2 0xB +#define USB_ST_BUFFEROVERRUN 0xC +#define USB_ST_BUFFERUNDERRUN 0xD +#define USB_ST_RESERVED3 0xE +#define USB_ST_RESERVED4 0xF + +/* internal errors */ +#define USB_ST_REMOVED 0x100 +#define USB_ST_TIMEOUT 0x110 +#define USB_ST_INTERNALERROR -1 +#define USB_ST_NOTSUPPORTED -2 + /* * USB device number allocation bitmap. There's one bitmap * per USB tree. @@ -103,8 +176,10 @@ struct usb_devmap { */ #define USB_MAXCONFIG 8 +#define USB_MAXALTSETTING 5 #define USB_MAXINTERFACES 32 #define USB_MAXENDPOINTS 32 +#define USB_MAXSTRINGS 32 struct usb_device_descriptor { __u8 bLength; @@ -150,6 +225,11 @@ struct usb_interface_descriptor { void *audio; }; +/* hack for alternate settings */ +struct usb_alternate_setting { + struct usb_interface_descriptor *interface; +}; + /* Configuration descriptor information.. */ struct usb_config_descriptor { __u8 bLength; @@ -160,14 +240,16 @@ struct usb_config_descriptor { __u8 iConfiguration; __u8 bmAttributes; __u8 MaxPower; - - struct usb_interface_descriptor *interface; + int act_altsetting; /* active alternate setting */ + int num_altsetting; /* number of alternate settings */ + struct usb_alternate_setting *altsetting; }; /* String descriptor */ struct usb_string_descriptor { __u8 bLength; __u8 bDescriptorType; + __u16 wData[1]; }; /* Hub descriptor */ @@ -175,7 +257,7 @@ struct usb_hub_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bNbrPorts; - __u16 wHubCharacteristics; + __u8 wHubCharacteristics[2]; /* __u16 but not aligned! */ __u8 bPwrOn2PwrGood; __u8 bHubContrCurrent; /* DeviceRemovable and PortPwrCtrlMask want to be variable-length @@ -200,16 +282,28 @@ struct usb_driver { * until we come up with a common meaning. * void *buffer - This is a pointer to the data used in this * USB transfer. + * int length - This is the number of bytes transferred in or out + * of the buffer by this transfer. (-1 = unknown/unsupported) * void *dev_id - This is a user defined pointer set when the IRQ * is requested that is passed back. + * + * Special Cases: + * if (status == USB_ST_REMOVED), don't trust buffer or len. */ -typedef int (*usb_device_irq)(int, void *, void *); +typedef int (*usb_device_irq)(int, void *, int, void *); struct usb_operations { struct usb_device *(*allocate)(struct usb_device *); int (*deallocate)(struct usb_device *); - int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int); - int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *); + int (*control_msg)(struct usb_device *, unsigned int, devrequest *, void *, int); + int (*bulk_msg)(struct usb_device *, unsigned int, void *, int,unsigned long *); + void* (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *); + int (*release_irq)(void* handle); + void *(*alloc_isoc)(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id); + void (*delete_isoc)(struct usb_device *dev, void *_isodesc); + int (*sched_isoc)(struct usb_device *usb_dev, void *_isodesc, void *_pisodesc); + int (*unsched_isoc)(struct usb_device *usb_dev, void *_isodesc); + int (*compress_isoc)(struct usb_device *usb_dev, void *_isodesc); }; /* @@ -219,6 +313,7 @@ struct usb_bus { struct usb_devmap devmap; /* Device map */ struct usb_operations *op; /* Operations (specific to the HC) */ struct usb_device *root_hub; /* Root hub */ + struct list_head bus_list; void *hcpriv; /* Host Controller private data */ }; @@ -226,22 +321,28 @@ struct usb_bus { #define USB_MAXCHILDREN (8) struct usb_device { - int devnum; /* Device number on USB bus */ - int slow; /* Slow device? */ - int maxpacketsize; /* Maximum packet size */ - - struct usb_bus *bus; /* Bus we're apart of */ - struct usb_driver *driver; /* Driver */ - struct usb_device_descriptor descriptor; /* Descriptor */ - struct usb_config_descriptor *config; /* All of the configs */ + int devnum; /* Device number on USB bus */ + int slow; /* Slow device? */ + int maxpacketsize; /* Maximum packet size; encoded as 0,1,2,3 = 8,16,32,64 */ + unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */ + unsigned int halted; /* endpoint halts */ + struct usb_config_descriptor *actconfig;/* the active configuration */ + int epmaxpacket[16]; /* endpoint specific maximums */ + int ifnum; /* active interface number */ + struct usb_bus *bus; /* Bus we're apart of */ + struct usb_driver *driver; /* Driver */ + struct usb_device_descriptor descriptor;/* Descriptor */ + struct usb_config_descriptor *config; /* All of the configs */ struct usb_device *parent; + char *stringindex[USB_MAXSTRINGS]; /* pointers to strings */ + int string_langid; /* language ID for strings */ /* * Child devices - these can be either new devices * (if this is a hub device), or different instances * of this same device. * - * Each instance needs its own set of data structuctures. + * Each instance needs its own set of data structures. */ int maxchild; /* Number of ports if hub */ @@ -254,14 +355,28 @@ struct usb_device { extern int usb_register(struct usb_driver *); extern void usb_deregister(struct usb_driver *); -extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *); +extern void usb_register_bus(struct usb_bus *); +extern void usb_deregister_bus(struct usb_bus *); + +extern void* usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *); +extern int usb_release_irq(struct usb_device *dev, void *handle); extern void usb_init_root_hub(struct usb_device *dev); extern void usb_connect(struct usb_device *dev); extern void usb_disconnect(struct usb_device **); -extern void usb_device_descriptor(struct usb_device *dev); +extern int usb_device_descriptor(struct usb_device *dev); +void usb_check_support(struct usb_device *); +void usb_driver_purge(struct usb_driver *,struct usb_device *); extern int usb_parse_configuration(struct usb_device *dev, void *buf, int len); +extern void usb_destroy_configuration(struct usb_device *dev); + +extern void *usb_allocate_isochronous (struct usb_device *usb_dev, unsigned int pipe, void *data, int len, + int maxsze, usb_device_irq completed, void *dev_id); +extern void usb_delete_isochronous (struct usb_device *dev, void *_isodesc); +extern int usb_schedule_isochronous (struct usb_device *usb_dev, void *_isodesc, void *_pisodesc); +extern int usb_unschedule_isochronous (struct usb_device *usb_dev, void *_isodesc); +extern int usb_compress_isochronous (struct usb_device *usb_dev, void *_isodesc); /* * Calling this entity a "pipe" is glorifying it. A USB pipe @@ -282,36 +397,47 @@ extern int usb_parse_configuration(struct usb_device *dev, void *buf, int len); * Let's not fall in that trap. We'll just encode it as a simple * unsigned int. The encoding is: * + * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) + * - direction: bit 7 (0 = Host-to-Device, 1 = Device-to-Host) * - device: bits 8-14 * - endpoint: bits 15-18 * - Data0/1: bit 19 - * - direction: bit 7 (0 = Host-to-Device, 1 = Device-to-Host) - * - speed: bit 26 (0 = High, 1 = Low Speed) - * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) + * - speed: bit 26 (0 = Full, 1 = Low Speed) * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk) * * Why? Because it's arbitrary, and whatever encoding we select is really - * up to us. This one happens to share a lot of bit positions with the UCHI + * up to us. This one happens to share a lot of bit positions with the UHCI * specification, so that much of the uhci driver can just mask the bits * appropriately. */ -#define usb_maxpacket(pipe) (8 << ((pipe) & 3)) +#define usb_maxpacket(dev,pipe) ((dev)->epmaxpacket[usb_pipeendpoint(pipe)]) #define usb_packetid(pipe) (((pipe) & 0x80) ? 0x69 : 0xE1) +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) +#define usb_pipein(pipe) (((pipe) >> 7) & 1) #define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) +#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) #define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) #define usb_pipedata(pipe) (((pipe) >> 19) & 1) -#define usb_pipeout(pipe) (((pipe) & 0x80) == 0) #define usb_pipeslow(pipe) (((pipe) >> 26) & 1) - #define usb_pipetype(pipe) (((pipe) >> 30) & 3) #define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == 0) #define usb_pipeint(pipe) (usb_pipetype((pipe)) == 1) #define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == 2) #define usb_pipebulk(pipe) (usb_pipetype((pipe)) == 3) -#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) +#define PIPE_DEVEP_MASK 0x0007ff00 + +/* The D0/D1 toggle bits */ +#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << ep)) +#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep)) + +/* Endpoint halt */ +#define usb_endpoint_halt(dev, ep) ((dev)->halted |= (1 << (ep))) +#define usb_endpoint_running(dev, ep) ((dev)->halted &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep) ((dev)->halted & (1 << (ep))) static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint) { @@ -323,14 +449,16 @@ static inline unsigned int __default_pipe(struct usb_device *dev) return (dev->slow << 26); } -/* Create control pipes.. */ +/* Create various pipes... */ #define usb_sndctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint)) #define usb_rcvctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint) | 0x80) +#define usb_sndisocpipe(dev,endpoint) ((0 << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvisocpipe(dev,endpoint) ((0 << 30) | __create_pipe(dev,endpoint) | 0x80) +#define usb_sndbulkpipe(dev,endpoint) ((3 << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvbulkpipe(dev,endpoint) ((3 << 30) | __create_pipe(dev,endpoint) | 0x80) #define usb_snddefctrl(dev) ((2 << 30) | __default_pipe(dev)) #define usb_rcvdefctrl(dev) ((2 << 30) | __default_pipe(dev) | 0x80) -/* Create .. */ - /* * Send and receive control messages.. */ @@ -342,13 +470,18 @@ int usb_get_device_descriptor(struct usb_device *dev); int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size); int usb_clear_port_feature(struct usb_device *dev, int port, int feature); int usb_set_port_feature(struct usb_device *dev, int port, int feature); +int usb_get_status (struct usb_device *dev, int type, int target, void *data); int usb_get_hub_status(struct usb_device *dev, void *data); int usb_get_port_status(struct usb_device *dev, int port, void *data); int usb_get_protocol(struct usb_device *dev); int usb_set_protocol(struct usb_device *dev, int protocol); +int usb_set_interface(struct usb_device *dev, int interface, int alternate); int usb_set_idle(struct usb_device *dev, int duration, int report_id); +int usb_set_interface(struct usb_device *dev, int interface, int alternate); int usb_set_configuration(struct usb_device *dev, int configuration); int usb_get_report(struct usb_device *dev); +char *usb_string(struct usb_device *dev, int index); +int usb_clear_halt(struct usb_device *dev, int endp); /* * Debugging helpers.. @@ -359,6 +492,7 @@ void usb_show_interface_descriptor(struct usb_interface_descriptor *); void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *); void usb_show_hub_descriptor(struct usb_hub_descriptor *); void usb_show_device(struct usb_device *); +void usb_show_string(struct usb_device* dev, char *id, int index); /* * Audio parsing helpers diff --git a/drivers/usb/usb_scsi.c b/drivers/usb/usb_scsi.c new file mode 100644 index 000000000000..e2e2d5bc593c --- /dev/null +++ b/drivers/usb/usb_scsi.c @@ -0,0 +1,1453 @@ + +/* Driver for USB scsi like devices + * + * (C) Michael Gee (michael@linuxspecific.com) 1999 + * + * This driver is scitzoid - it makes a USB device appear as both a SCSI device + * and a character device. The latter is only available if the device has an + * interrupt endpoint, and is used specifically to receive interrupt events. + * + * In order to support various 'strange' devices, this module supports plug in + * device specific filter modules, which can do their own thing when required. + * + * Further reference. + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the transformation of SCSI command blocks to the + * equivalent USB control and data transfer required. + * It is important to note that in a number of cases this class exhibits + * class-specific exemptions from the USB specification. Notably the + * usage of NAK, STALL and ACK differs from the norm, in that they are + * used to communicate wait, failed and OK on SCSI commands. + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Basically, this stuff is WIERD!! + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" + +#include "usb.h" +#include "usb_scsi.h" + +/* direction table (what a pain) */ + +unsigned char us_direction[256/8] = { + +#include "usb_scsi_dt.c" + +}; + +/* + * Per device data + */ + +static int my_host_number; + +int usbscsi_debug = 1; + +struct us_data { + struct us_data *next; /* next device */ + struct usb_device *pusb_dev; + struct usb_scsi_filter *filter; /* filter driver */ + void *fdata; /* filter data */ + unsigned int flags; /* from filter initially*/ + __u8 ep_in; /* in endpoint */ + __u8 ep_out; /* out ....... */ + __u8 ep_int; /* interrupt . */ + __u8 subclass; /* as in overview */ + __u8 protocol; /* .............. */ + __u8 attention_done; /* force attention on first command */ + int (*pop)(Scsi_Cmnd *); /* protocol specific do cmd */ + int (*pop_reset)(struct us_data *); /* ................. device reset */ + GUID(guid); /* unique dev id */ + struct Scsi_Host *host; /* our dummy host data */ + Scsi_Host_Template *htmplt; /* own host template */ + int host_number; /* to find us */ + int host_no; /* allocated by scsi */ + int fixedlength; /* expand commands */ + Scsi_Cmnd *srb; /* current srb */ + int action; /* what to do */ + wait_queue_head_t waitq; /* thread waits */ + wait_queue_head_t ip_waitq; /* for CBI interrupts */ + __u16 ip_data; /* interrupt data */ + int ip_wanted; /* needed */ + int pid; /* control thread */ + struct semaphore *notify; /* wait for thread to begin */ +}; + +/* + * kernel thread actions + */ + +#define US_ACT_COMMAND 1 +#define US_ACT_ABORT 2 +#define US_ACT_DEVICE_RESET 3 +#define US_ACT_BUS_RESET 4 +#define US_ACT_HOST_RESET 5 + +static struct proc_dir_entry proc_usb_scsi = +{ + PROC_SCSI_USB_SCSI, + 0, + NULL, + S_IFDIR | S_IRUGO | S_IXUGO, + 2 +}; + +static struct us_data *us_list; + +static struct usb_scsi_filter *filters; + +static int scsi_probe(struct usb_device *dev); +static void scsi_disconnect(struct usb_device *dev); +static struct usb_driver scsi_driver = { + "usb_scsi", + scsi_probe, + scsi_disconnect, + { NULL, NULL } +}; + +/* Data handling, using SG if required */ + +static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) +{ + int max_size = usb_maxpacket(us->pusb_dev, pipe) * 16; + int this_xfer; + int result; + unsigned long partial; + int maxtry = 100; + while (length) { + this_xfer = length > max_size ? max_size : length; + length -= this_xfer; + do { + /*US_DEBUGP("Bulk xfer %x(%d)\n", (unsigned int)buf, this_xfer);*/ + result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, pipe, buf, + this_xfer, &partial); + + if (result != 0 || partial != this_xfer) + US_DEBUGP("bulk_msg returned %d xferred %lu/%d\n", + result, partial, this_xfer); + + if (result == USB_ST_STALL) { + US_DEBUGP("clearing endpoing halt for pipe %x\n", pipe); + usb_clear_halt(us->pusb_dev, + usb_pipeendpoint(pipe) | (pipe & 0x80)); + } + + /* we want to retry if the device reported NAK */ + if (result == USB_ST_TIMEOUT) { + if (partial != this_xfer) { + return 0; /* I do not like this */ + } + if (!maxtry--) + break; + this_xfer -= partial; + buf += partial; + } else if (!result && partial != this_xfer) { + /* short data - assume end */ + result = USB_ST_DATAUNDERRUN; + break; + } else if (result == USB_ST_STALL && us->protocol == US_PR_CB) { + if (!maxtry--) + break; + this_xfer -= partial; + buf += partial; + } else + break; + } while ( this_xfer ); + if (result) + return result; + buf += this_xfer; + } + return 0; + +} + +static int us_transfer(Scsi_Cmnd *srb, int dir_in) +{ + struct us_data *us = (struct us_data *)srb->host_scribble; + int i; + int result = -1; + unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) : + usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + if (srb->use_sg) { + struct scatterlist *sg = (struct scatterlist *) srb->request_buffer; + + for (i = 0; i < srb->use_sg; i++) { + result = us_one_transfer(us, pipe, sg[i].address, sg[i].length); + if (result) + break; + } + } + else + result = us_one_transfer(us, pipe, + srb->request_buffer, srb->request_bufflen); + + if (result) + US_DEBUGP("us_transfer returning error %d\n", result); + return result; +} + +static unsigned int us_transfer_length(Scsi_Cmnd *srb) +{ + int i; + unsigned int total = 0; + + /* always zero for some commands */ + switch (srb->cmnd[0]) { + case SEEK_6: + case SEEK_10: + case REZERO_UNIT: + case ALLOW_MEDIUM_REMOVAL: + case START_STOP: + case TEST_UNIT_READY: + return 0; + + default: + break; + } + + if (srb->use_sg) { + struct scatterlist *sg = (struct scatterlist *) srb->request_buffer; + + for (i = 0; i < srb->use_sg; i++) { + total += sg[i].length; + } + return total; + } + else + return srb->request_bufflen; + +} + +static int pop_CBI_irq(int state, void *buffer, int len, void *dev_id) +{ + struct us_data *us = (struct us_data *)dev_id; + + if (state != USB_ST_REMOVED) { + us->ip_data = le16_to_cpup((__u16 *)buffer); + if (us->ip_data != 0) + US_DEBUGP("Interrupt Status %x\n", us->ip_data); + } + if (us->ip_wanted) { + us->ip_wanted = 0; + wake_up(&us->ip_waitq); + } + + /* we dont want another interrupt */ + + return 0; +} + +static int pop_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + devrequest dr; + + US_DEBUGP("pop_CB_reset\n"); + dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; + dr.request = US_CBI_ADSC; + dr.value = 0; + dr.index = us->pusb_dev->ifnum; + dr.length = 12; + memset(cmd, -1, sizeof(cmd)); + cmd[0] = SEND_DIAGNOSTIC; + cmd[1] = 4; + us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + &dr, cmd, 12); + + /* long wait for reset */ + + schedule_timeout(HZ*5); + + US_DEBUGP("pop_CB_reset: clearing endpoint halt\n"); + usb_clear_halt(us->pusb_dev, us->ep_in | 0x80); + usb_clear_halt(us->pusb_dev, us->ep_out); + + US_DEBUGP("pop_CB_reset done\n"); + return 0; +} + +static int pop_CB_command(Scsi_Cmnd *srb, unsigned char *cmnd, int cmd_len) +{ + struct us_data *us = (struct us_data *)srb->host_scribble; + devrequest dr; + int result; + int retry = 5; + int done_start = 0; + + while (retry--) { + dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; + dr.request = US_CBI_ADSC; + dr.value = 0; + dr.index = us->pusb_dev->ifnum; + dr.length = cmd_len; + + result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + &dr, cmnd, cmd_len); + + if (result && !done_start && (us->flags & US_FL_FIXED_COMMAND) + && cmnd[0] == TEST_UNIT_READY + && (us->subclass == US_SC_UFI /*|| us->subclass == US_SC_8070*/)) { + /* as per spec try a start command, wait and retry */ + + done_start++; + cmnd[0] = START_STOP; + cmnd[4] = 1; /* start */ + result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + &dr, cmnd, cmd_len); + wait_ms(100); + retry++; + cmnd[0] = TEST_UNIT_READY; + cmnd[4] = 0; + continue; + } + if (result != USB_ST_TIMEOUT) + break; + } + return result; +} + +/* + * Control/Bulk status handler + */ + +static int pop_CB_status(Scsi_Cmnd *srb) +{ + struct us_data *us = (struct us_data *)srb->host_scribble; + int result; + __u8 status[2]; + devrequest dr; + int retry = 5; + void *irq_handle; + + switch (us->protocol) { + case US_PR_CB: + /* get from control */ + + while (retry--) { + dr.requesttype = 0x80 | USB_TYPE_STANDARD | USB_RT_DEVICE; + dr.request = USB_REQ_GET_STATUS; + dr.index = 0; + dr.value = 0; + dr.length = 2; + result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_rcvctrlpipe(us->pusb_dev,0), + &dr, status, sizeof(status)); + if (result != USB_ST_TIMEOUT) + break; + } + if (result) { + US_DEBUGP("Bad AP status request %d\n", result); + return DID_ABORT << 16; + } + US_DEBUGP("Got AP status %x %x\n", status[0], status[1]); + if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY && + ( (status[0] & ~3) || status[1])) + return (DID_OK << 16) | 2; + else + return DID_OK << 16; + break; + + case US_PR_CBI: + /* get from interrupt pipe */ + + /* add interrupt transfer, marked for removal */ + us->ip_wanted = 1; + irq_handle = us->pusb_dev->bus->op->request_irq + (us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev, us->ep_int), + pop_CBI_irq, 0, (void *)us); + if (irq_handle == 0) { + US_DEBUGP("No interrupt for CBI %x\n", result); + return DID_ABORT << 16; + } + sleep_on(&us->ip_waitq); + if (us->ip_wanted) { + US_DEBUGP("Did not get interrupt on CBI\n"); + us->ip_wanted = 0; + return DID_ABORT << 16; + } + + if (us->ip_data != 0) + US_DEBUGP("Got interrupt data %x\n", us->ip_data); + + /* sort out what it means */ + + if (us->subclass == US_SC_UFI) { + /* gives us asc and ascq, as per request sense */ + + if (srb->cmnd[0] == REQUEST_SENSE || + srb->cmnd[0] == INQUIRY) + return DID_OK << 16; + else + return (DID_OK << 16) + ((us->ip_data & 0xff) ? 2 : 0); + } + if (us->ip_data & 0xff) { + US_DEBUGP("Bad CBI interrupt data %x\n", us->ip_data); + return DID_ABORT << 16; + } + return (DID_OK << 16) + ((us->ip_data & 0x300) ? 2 : 0); + } + return DID_ERROR << 16; +} + +/* Protocol command handlers */ + +static int pop_CBI(Scsi_Cmnd *srb) +{ + struct us_data *us = (struct us_data *)srb->host_scribble; + unsigned char cmd[16]; + unsigned char *cmdp; + int cmd_len, len; + int result; + unsigned char modebuf[260]; + unsigned char *savebuf = srb->request_buffer; + int savelen = srb->request_bufflen; + + /* first fix some commands */ + cmdp = srb->cmnd; + cmd_len = srb->cmd_len; + + if (us->flags & US_FL_FIXED_COMMAND) { + cmdp = cmd; + cmd_len = us->fixedlength; + memset(cmd, 0, cmd_len); + + switch (srb->cmnd[0]) { + case WRITE_6: + case READ_6: + cmd[0] = srb->cmnd[0] | 0x20; + cmd[1] = srb->cmnd[1] & 0xE0; + cmd[2] = 0; + cmd[3] = srb->cmnd[1] & 0x1F; + cmd[4] = srb->cmnd[2]; + cmd[5] = srb->cmnd[3]; + cmd[8] = srb->cmnd[4]; + break; + + case MODE_SENSE: + if (srb->use_sg > 0) + US_DEBUGP("MODE SENSE with use_sg=%d!!!\n", srb->use_sg); + len = srb->cmnd[4] + 4; + cmd[0] = srb->cmnd[0] | 0x40; /* op code */ + cmd[1] = srb->cmnd[1]; /* LUN, various bits */ + cmd[2] = srb->cmnd[2]; /* page code */ + cmd[8] = len; /* allocation length */ + cmd[7] = len >> 8; + srb->request_buffer = modebuf; + srb->request_bufflen = len; + break; + + case MODE_SELECT: + if (srb->use_sg > 0) + US_DEBUGP("MODE SELECT with use_sg=%d!!!\n", srb->use_sg); + len = srb->cmnd[4] + 4; + memset(modebuf, 0, 8); + modebuf[1] = savebuf[0]; + modebuf[2] = savebuf[1]; + modebuf[3] = savebuf[2]; + modebuf[7] = savebuf[3]; + if (len > 8) + memcpy(modebuf+8, savebuf+4, len-8); + srb->request_buffer = modebuf; + srb->request_bufflen = len; + cmd[0] = srb->cmnd[0] | 0x40; + cmd[1] = srb->cmnd[1]; + cmd[2] = srb->cmnd[2]; + cmd[8] = len; + cmd[7] = len >> 8; + break; + + default: + memcpy(cmd, srb->cmnd, srb->cmd_len); + break; + } + } + + /* run the command */ + if ((result = pop_CB_command(srb, cmdp, cmd_len))) { + US_DEBUGP("CBI command %x\n", result); + if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) { + return (DID_OK << 16) | 2; + } + return DID_ERROR << 16; + } + + /* transfer the data */ + + if (us_transfer_length(srb)) { + result = us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + if (result && result != USB_ST_DATAUNDERRUN && result != USB_ST_STALL) { + US_DEBUGP("CBI transfer %x\n", result); + return DID_ERROR << 16; + } +#if 0 + else if (result == USB_ST_DATAUNDERRUN) { + return DID_OK << 16; + } + } else { + if (!result) { + return DID_OK << 16; + } +#endif + } + + if (us->flags & US_FL_FIXED_COMMAND) { + switch (srb->cmnd[0]) { + case MODE_SENSE: + len = srb->cmnd[4]; + savebuf[0] = modebuf[1]; + savebuf[1] = modebuf[2]; + savebuf[2] = modebuf[3]; + savebuf[3] = modebuf[7]; + if (len > 4) + memcpy(savebuf+4, modebuf+8, len-4); + /* fall through */ + case MODE_SELECT: + srb->request_buffer = savebuf; + srb->request_bufflen = savelen; + break; + } + } + + /* get status */ + + return pop_CB_status(srb); +} + +static int pop_Bulk_reset(struct us_data *us) +{ + devrequest dr; + int result; + + dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; + dr.request = US_BULK_RESET; + dr.value = US_BULK_RESET_HARD; + dr.index = 0; + dr.length = 0; + + result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0); + if (result) + US_DEBUGP("Bulk hard reset failed %d\n", result); + usb_clear_halt(us->pusb_dev, us->ep_in | 0x80); + usb_clear_halt(us->pusb_dev, us->ep_out); + + /* long wait for reset */ + + schedule_timeout(HZ*5); + + return result; +} +/* + * The bulk only protocol handler. + * Uses the in and out endpoints to transfer commands and data (nasty) + */ +static int pop_Bulk(Scsi_Cmnd *srb) +{ + struct us_data *us = (struct us_data *)srb->host_scribble; + struct bulk_cb_wrap bcb; + struct bulk_cs_wrap bcs; + int result; + unsigned long partial; + int stall; + + /* set up the command wrapper */ + + bcb.Signature = US_BULK_CB_SIGN; + bcb.DataTransferLength = us_transfer_length(srb);; + bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7; + bcb.Tag = srb->serial_number; + bcb.Lun = 0; + memset(bcb.CDB, 0, sizeof(bcb.CDB)); + memcpy(bcb.CDB, srb->cmnd, srb->cmd_len); + if (us->flags & US_FL_FIXED_COMMAND) { + bcb.Length = us->fixedlength; + } else { + bcb.Length = srb->cmd_len; + } + + /* send it to out endpoint */ + + US_DEBUGP("Bulk command S %x T %x L %d F %d CL %d\n", bcb.Signature, + bcb.Tag, bcb.DataTransferLength, bcb.Flags, bcb.Length); + result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, + usb_sndbulkpipe(us->pusb_dev, us->ep_out), &bcb, + US_BULK_CB_WRAP_LEN, &partial); + if (result) { + US_DEBUGP("Bulk command result %x\n", result); + return DID_ABORT << 16; + } + + //return DID_BAD_TARGET << 16; + /* send/receive data */ + + if (bcb.DataTransferLength) { + result = us_transfer(srb, bcb.Flags); + if (result && result != USB_ST_DATAUNDERRUN && result != USB_ST_STALL) { + US_DEBUGP("Bulk transfer result %x\n", result); + return DID_ABORT << 16; + } + } + + /* get status */ + + + stall = 0; + do { + result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs, + US_BULK_CS_WRAP_LEN, &partial); + if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) + stall++; + else + break; + } while ( stall < 3); + if (result && result != USB_ST_DATAUNDERRUN) { + US_DEBUGP("Bulk status result = %x\n", result); + return DID_ABORT << 16; + } + + /* check bulk status */ + + US_DEBUGP("Bulk status S %x T %x R %d V %x\n", bcs.Signature, bcs.Tag, + bcs.Residue, bcs.Status); + if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag || + bcs.Status > US_BULK_STAT_PHASE) { + US_DEBUGP("Bulk logical error\n"); + return DID_ABORT << 16; + } + switch (bcs.Status) { + case US_BULK_STAT_OK: + return DID_OK << 16; + + case US_BULK_STAT_FAIL: + /* check for underrun - dont report */ + if (bcs.Residue) + return DID_OK << 16; + //pop_Bulk_reset(us); + break; + + case US_BULK_STAT_PHASE: + return DID_ERROR << 16; + } + return (DID_OK << 16) | 2; /* check sense required */ + +} + +/* Host functions */ + +/* detect adapter (always true ) */ +static int us_detect(struct SHT *sht) +{ + /* FIXME - not nice at all, but how else ? */ + struct us_data *us = (struct us_data *)sht->proc_dir; + char name[32]; + + sprintf(name, "usbscsi%d", us->host_number); + proc_usb_scsi.namelen = strlen(name); + proc_usb_scsi.name = kmalloc(proc_usb_scsi.namelen+1, GFP_KERNEL); + if (!proc_usb_scsi.name) + return 0; + strcpy((char *)proc_usb_scsi.name, name); + sht->proc_dir = kmalloc(sizeof(*sht->proc_dir), GFP_KERNEL); + if (!sht->proc_dir) { + kfree(proc_usb_scsi.name); + return 0; + } + *sht->proc_dir = proc_usb_scsi; + sht->name = proc_usb_scsi.name; + us->host = scsi_register(sht, sizeof(us)); + if (us->host) { + us->host->hostdata[0] = (unsigned long)us; + us->host_no = us->host->host_no; + return 1; + } + kfree(proc_usb_scsi.name); + kfree(sht->proc_dir); + return 0; +} + +/* release - must be here to stop scsi + * from trying to release IRQ etc. + * Kill off our data + */ +static int us_release(struct Scsi_Host *psh) +{ + struct us_data *us = (struct us_data *)psh->hostdata[0]; + struct us_data *prev = (struct us_data *)&us_list; + + if (us->filter) + us->filter->release(us->fdata); + if (us->pusb_dev) + usb_deregister(&scsi_driver); + + /* FIXME - leaves hanging host template copy */ + /* (bacause scsi layer uses it after removal !!!) */ + while(prev->next != us) + prev = prev->next; + prev->next = us->next; + return 0; +} + +/* run command */ +static int us_command( Scsi_Cmnd *srb ) +{ + US_DEBUGP("Bad use of us_command\n"); + + return DID_BAD_TARGET << 16; +} + +/* run command */ +static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) +{ + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + /* US_DEBUGP("Command wakeup\n"); */ + if (us->srb) { + /* busy */ + } + srb->host_scribble = (unsigned char *)us; + us->srb = srb; + srb->scsi_done = done; + us->action = US_ACT_COMMAND; + + /* wake up the process task */ + + wake_up_interruptible(&us->waitq); + + return 0; +} + +static int us_abort( Scsi_Cmnd *srb ) +{ + return 0; +} + +static int us_bus_reset( Scsi_Cmnd *srb ) +{ + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + us->pop_reset(us); + return SUCCESS; +} + +static int us_host_reset( Scsi_Cmnd *srb ) +{ + return 0; +} + + +#undef SPRINTF +#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } + +int usb_scsi_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout) +{ + struct us_data *us = us_list; + char *pos = buffer; + char *vendor; + char *product; + char *style = ""; + + /* find our data from hostno */ + + while (us) { + if (us->host_no == hostno) + break; + us = us->next; + } + + if (!us) + return -ESRCH; + + /* null on outward */ + + if (inout) + return length; + + if (!us->pusb_dev || !(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer))) + vendor = "?"; + if (!us->pusb_dev || !(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct))) + product = "?"; + + switch (us->protocol) { + case US_PR_CB: + style = "Control/Bulk"; + break; + + case US_PR_CBI: + style = "Control/Bulk/Interrupt"; + break; + + case US_PR_ZIP: + style = "Bulk only"; + break; + + } + SPRINTF ("Host scsi%d: usb-scsi\n", hostno); + SPRINTF ("Device: %s %s - GUID " GUID_FORMAT "\n", vendor, product, GUID_ARGS(us->guid) ); + SPRINTF ("Style: %s\n", style); + + /* + * Calculate start of next buffer, and return value. + */ + *start = buffer + offset; + + if ((pos - buffer) < offset) + return (0); + else if ((pos - buffer - offset) < length) + return (pos - buffer - offset); + else + return (length); +} + +/* + * this defines our 'host' + */ + +static Scsi_Host_Template my_host_template = { + NULL, /* next */ + NULL, /* module */ + NULL, /* proc_dir */ + usb_scsi_proc_info, + NULL, /* name - points to unique */ + us_detect, + us_release, + NULL, /* info */ + NULL, /* ioctl */ + us_command, + us_queuecommand, + NULL, /* eh_strategy */ + us_abort, + us_bus_reset, + us_bus_reset, + us_host_reset, + NULL, /* abort */ + NULL, /* reset */ + NULL, /* slave_attach */ + NULL, /* bios_param */ + 1, /* can_queue */ + -1, /* this_id */ + SG_ALL, /* sg_tablesize */ + 1, /* cmd_per_lun */ + 0, /* present */ + FALSE, /* unchecked_isa_dma */ + FALSE, /* use_clustering */ + TRUE, /* use_new_eh_code */ + TRUE /* emulated */ +}; + +static unsigned char sense_notready[] = { + 0x70, /* current error */ + 0x00, + 0x02, /* not ready */ + 0x00, + 0x00, + 10, /* additional length */ + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, /* not ready */ + 0x03, /* manual intervention */ + 0x00, + 0x00, + 0x00, + 0x00 +}; + +static int usbscsi_control_thread(void * __us) +{ + struct us_data *us = (struct us_data *)__us; + int action; + + lock_kernel(); + + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources.. + */ + exit_mm(current); + exit_files(current); + //exit_fs(current); + + sprintf(current->comm, "usbscsi%d", us->host_number); + + unlock_kernel(); + + up(us->notify); + + for(;;) { + siginfo_t info; + int unsigned long signr; + + interruptible_sleep_on(&us->waitq); + + action = us->action; + us->action = 0; + + switch (action) { + case US_ACT_COMMAND : + if (us->srb->target || us->srb->lun) { + /* bad device */ + US_DEBUGP( "Bad device number (%d/%d) or dev %x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev); + us->srb->result = DID_BAD_TARGET << 16; + } else if (!us->pusb_dev) { + + /* our device has gone - pretend not ready */ + + if (us->srb->cmnd[0] == REQUEST_SENSE) { + memcpy(us->srb->request_buffer, sense_notready, sizeof(sense_notready)); + us->srb->result = DID_OK << 16; + } else { + us->srb->result = (DID_OK << 16) | 2; + } + } else { + /* US_DEBUG(us_show_command(us->srb)); */ + + /* check for variable length - do properly if so */ + + if (us->filter && us->filter->command) + us->srb->result = us->filter->command(us->fdata, us->srb); + else if (us->srb->cmnd[0] == START_STOP && + us->pusb_dev->descriptor.idProduct == 0x0001 && + us->pusb_dev->descriptor.idVendor == 0x04e6) + us->srb->result = DID_OK << 16; + else { + unsigned int savelen = us->srb->request_bufflen; + unsigned int saveallocation; + + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + if (us->srb->request_bufflen > 18) + us->srb->request_bufflen = 18; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 18; + break; + + case INQUIRY: + if (us->srb->request_bufflen > 36) + us->srb->request_bufflen = 36; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 36; + break; + + case MODE_SENSE: + if (us->srb->request_bufflen > 4) + us->srb->request_bufflen = 4; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 4; + break; + + case LOG_SENSE: + case MODE_SENSE_10: + if (us->srb->request_bufflen > 8) + us->srb->request_bufflen = 8; + else + break; + saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8]; + us->srb->cmnd[7] = 0; + us->srb->cmnd[8] = 8; + break; + + default: + break; + } + us->srb->result = us->pop(us->srb); + + if (savelen != us->srb->request_bufflen && + us->srb->result == (DID_OK << 16)) { + unsigned char *p = (unsigned char *)us->srb->request_buffer; + unsigned int length; + + /* set correct length and retry */ + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + /* simply return 18 bytes */ + p[7] = 10; + length = us->srb->request_bufflen;; + break; + + case INQUIRY: + length = p[4] + 5 > savelen ? savelen : p[4] + 5; + us->srb->cmnd[4] = length; + break; + + case MODE_SENSE: + length = p[0] + 4 > savelen ? savelen : p[0] + 4; + us->srb->cmnd[4] = 4; + break; + + case LOG_SENSE: + length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4; + us->srb->cmnd[7] = length >> 8; + us->srb->cmnd[8] = length; + break; + + case MODE_SENSE_10: + length = ((p[0] << 8) + p[1]) + 8 > savelen ? savelen : ((p[0] << 8) + p[1]) + 8; + us->srb->cmnd[7] = length >> 8; + us->srb->cmnd[8] = length; + break; + } + + US_DEBUGP("Old/New length = %d/%d\n", savelen, length); + + if (us->srb->request_bufflen != length) { + us->srb->request_bufflen = length; + us->srb->result = us->pop(us->srb); + } + /* reset back to original values */ + + us->srb->request_bufflen = savelen; + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + case INQUIRY: + case MODE_SENSE: + us->srb->cmnd[4] = saveallocation; + break; + + case LOG_SENSE: + case MODE_SENSE_10: + us->srb->cmnd[7] = saveallocation >> 8; + us->srb->cmnd[8] = saveallocation; + break; + } + } + /* force attention on first command */ + if (!us->attention_done) { + US_DEBUGP("forcing unit attention\n"); + if (us->srb->cmnd[0] == REQUEST_SENSE) { + if (us->srb->result == (DID_OK << 16)) { + unsigned char *p = (unsigned char *)us->srb->request_buffer; + + us->attention_done = 1; + if ((p[2] & 0x0f) != UNIT_ATTENTION) { + p[2] = UNIT_ATTENTION; + p[12] = 0x29; /* power on, reset or bus-reset */ + p[13] = 0; + } + } + } else if (us->srb->cmnd[0] != INQUIRY && + us->srb->result == (DID_OK << 16)) { + us->srb->result |= 2; /* force check condition */ + } + } + } + } + if (us->srb->result) { + US_DEBUG(us_show_command(us->srb)); + US_DEBUGP("scsi cmd done, result=%x\n", us->srb->result); + } + us->srb->scsi_done(us->srb); + us->srb = NULL; + break; + + case US_ACT_ABORT : + break; + + case US_ACT_DEVICE_RESET : + break; + + case US_ACT_BUS_RESET : + break; + + case US_ACT_HOST_RESET : + break; + + } + + if(signal_pending(current)) { + /* sending SIGUSR1 makes us print out some info */ + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (signr == SIGUSR2) { + printk("USBSCSI debug toggle\n"); + usbscsi_debug = !usbscsi_debug; + } else { + break; + } + } + } + + MOD_DEC_USE_COUNT; + + printk("usbscsi_control_thread exiting\n"); + + return 0; +} + +static int scsi_probe(struct usb_device *dev) +{ + struct usb_interface_descriptor *interface; + int i; + char *mf; /* manufacturer */ + char *prod; /* product */ + char *serial; /* serial number */ + struct us_data *ss = NULL; + struct usb_scsi_filter *filter = filters; + void *fdata = NULL; + unsigned int flags = 0; + GUID(guid); + struct us_data *prev; + Scsi_Host_Template *htmplt; + int protocol = 0; + int subclass = 0; + + GUID_CLEAR(guid); + mf = usb_string(dev, dev->descriptor.iManufacturer); + prod = usb_string(dev, dev->descriptor.iProduct); + serial = usb_string(dev, dev->descriptor.iSerialNumber); + + /* probe with filters first */ + + if (mf && prod) { + while (filter) { + if ((fdata = filter->probe(dev, mf, prod, serial)) != NULL) { + flags = filter->flags; + printk(KERN_INFO "USB Scsi filter %s\n", filter->name); + break; + } + filter = filter->next; + } + } + + /* generic devices next */ + + if (fdata == NULL) { + + /* some exceptions */ + if (dev->descriptor.idVendor == 0x04e6 && + dev->descriptor.idProduct == 0x0001) { + /* shuttle E-USB */ + protocol = US_PR_CB; + subclass = US_SC_8070; /* an assumption */ + } else if (dev->descriptor.bDeviceClass != 0 || + dev->config->altsetting->interface->bInterfaceClass != 8 || + dev->config->altsetting->interface->bInterfaceSubClass < US_SC_MIN || + dev->config->altsetting->interface->bInterfaceSubClass > US_SC_MAX) { + return -1; + } + + /* now check if we have seen it before */ + + if (dev->descriptor.iSerialNumber && + usb_string(dev, dev->descriptor.iSerialNumber) ) { + make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, + usb_string(dev, dev->descriptor.iSerialNumber)); + } else { + make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, + "0"); + } + for (ss = us_list; ss; ss = ss->next) { + if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) { + US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + flags = ss->flags; + break; + } + } + } + + if (!ss) { + if ((ss = (struct us_data *)kmalloc(sizeof(*ss), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING USB_SCSI "Out of memory\n"); + if (filter) + filter->release(fdata); + return -1; + } + memset(ss, 0, sizeof(struct us_data)); + } + + interface = dev->config->altsetting->interface; + ss->filter = filter; + ss->fdata = fdata; + ss->flags = flags; + if (subclass) { + ss->subclass = subclass; + ss->protocol = protocol; + } else { + ss->subclass = interface->bInterfaceSubClass; + ss->protocol = interface->bInterfaceProtocol; + } + ss->attention_done = 0; + + /* set the protocol op */ + + US_DEBUGP("Protocol "); + switch (ss->protocol) { + case US_PR_CB: + US_DEBUGPX("Control/Bulk\n"); + ss->pop = pop_CBI; + ss->pop_reset = pop_CB_reset; + break; + + case US_PR_CBI: + US_DEBUGPX("Control/Bulk/Interrupt\n"); + ss->pop = pop_CBI; + ss->pop_reset = pop_CB_reset; + break; + + default: + US_DEBUGPX("Bulk\n"); + ss->pop = pop_Bulk; + ss->pop_reset = pop_Bulk_reset; + break; + } + + /* + * we are expecting a minimum of 2 endpoints - in and out (bulk) + * an optional interrupt is OK (necessary for CBI protocol) + * we will ignore any others + */ + + for (i = 0; i < interface->bNumEndpoints; i++) { + if (interface->endpoint[i].bmAttributes == 0x02) { + if (interface->endpoint[i].bEndpointAddress & 0x80) + ss->ep_in = interface->endpoint[i].bEndpointAddress & 0x0f; + else + ss->ep_out = interface->endpoint[i].bEndpointAddress & 0x0f; + } else if (interface->endpoint[i].bmAttributes == 0x03) { + ss->ep_int = interface->endpoint[i].bEndpointAddress & 0x0f; + } + } + US_DEBUGP("Endpoints In %d Out %d Int %d\n", ss->ep_in, ss->ep_out, ss->ep_int); + + /* exit if strange looking */ + + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) || + usb_set_interface(dev, interface->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { + US_DEBUGP("Problems with device\n"); + if (ss->host) { + scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt); + kfree(ss->htmplt->name); + kfree(ss->htmplt); + } + if (filter) + filter->release(fdata); + kfree(ss); + return -1; /* no endpoints */ + } + + if (dev->config[0].iConfiguration && usb_string(dev, dev->config[0].iConfiguration)) + US_DEBUGP("Configuration %s\n", usb_string(dev, dev->config[0].iConfiguration)); + if (interface->iInterface && usb_string(dev, interface->iInterface)) + US_DEBUGP("Interface %s\n", usb_string(dev, interface->iInterface)); + + ss->pusb_dev = dev; + + /* Now generate a scsi host definition, and register with scsi above us */ + + if (!ss->host) { + + /* make unique id if possible */ + + US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + memcpy(ss->guid, guid, sizeof(guid)); + + /* set class specific stuff */ + + US_DEBUGP("SubClass "); + switch (ss->subclass) { + case US_SC_RBC: + US_DEBUGPX("Reduced Block Commands\n"); + break; + case US_SC_8020: + US_DEBUGPX("8020\n"); + break; + case US_SC_QIC: + US_DEBUGPX("QIC157\n"); + break; + case US_SC_8070: + US_DEBUGPX("8070\n"); + ss->flags |= US_FL_FIXED_COMMAND; + ss->fixedlength = 12; + break; + case US_SC_SCSI: + US_DEBUGPX("Transparent SCSI\n"); + break; + case US_SC_UFI: + US_DEBUGPX(" UFF\n"); + ss->flags |= US_FL_FIXED_COMMAND; + ss->fixedlength = 12; + break; + + default: + break; + } + + /* create unique host template */ + + if ((htmplt = (Scsi_Host_Template *)kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) { + printk(KERN_WARNING USB_SCSI "Out of memory\n"); + if (filter) + filter->release(fdata); + kfree(ss); + return -1; + } + memcpy(htmplt, &my_host_template, sizeof(my_host_template)); + ss->host_number = my_host_number++; + + + (struct us_data *)htmplt->proc_dir = ss; + + if (dev->descriptor.idVendor == 0x04e6 && + dev->descriptor.idProduct == 0x0001) { + devrequest dr; + __u8 qstat[2]; + + /* shuttle E-USB */ + dr.requesttype = 0xC0; + dr.request = 1; + dr.index = 0; + dr.value = 0; + dr.length = 0; + ss->pusb_dev->bus->op->control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), &dr, qstat, 2); + US_DEBUGP("C0 status %x %x\n", qstat[0], qstat[1]); + init_waitqueue_head(&ss->ip_waitq); + ss->pusb_dev->bus->op->request_irq(ss->pusb_dev, + usb_rcvctrlpipe(ss->pusb_dev, ss->ep_int), + pop_CBI_irq, 0, (void *)ss); + interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*5); + + } else if (ss->protocol == US_PR_CBI) + init_waitqueue_head(&ss->ip_waitq); + + + /* start up our thread */ + + { + DECLARE_MUTEX_LOCKED(sem); + + init_waitqueue_head(&ss->waitq); + + ss->notify = &sem; + ss->pid = kernel_thread(usbscsi_control_thread, ss, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + if (ss->pid < 0) { + printk(KERN_WARNING USB_SCSI "Unable to start control thread\n"); + kfree(htmplt); + if (filter) + filter->release(fdata); + kfree(ss); + return -1; + } + + /* wait for it to start */ + + down(&sem); + } + + /* now register - our detect function will be called */ + + scsi_register_module(MODULE_SCSI_HA, htmplt); + + /* put us in the list */ + + prev = (struct us_data *)&us_list; + while (prev->next) + prev = prev->next; + prev->next = ss; + + } + + + printk(KERN_INFO "USB SCSI device found at address %d\n", dev->devnum); + + dev->private = ss; + return 0; +} + +static void scsi_disconnect(struct usb_device *dev) +{ + struct us_data *ss = dev->private; + + if (!ss) + return; + if (ss->filter) + ss->filter->release(ss->fdata); + ss->pusb_dev = NULL; + dev->private = NULL; /* just in case */ + MOD_DEC_USE_COUNT; +} + +int usb_scsi_init(void) +{ + + MOD_INC_USE_COUNT; +#ifdef CONFIG_USB_HP4100 + hp4100_init(); +#endif +#ifdef CONFIG_USB_ZIP + usb_zip_init(); +#endif + usb_register(&scsi_driver); + printk(KERN_INFO "USB SCSI support registered.\n"); + return 0; +} + + +int usb_scsi_register(struct usb_scsi_filter *filter) +{ + struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters; + + while (prev->next) + prev = prev->next; + prev->next = filter; + return 0; +} + +void usb_scsi_deregister(struct usb_scsi_filter *filter) +{ + struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters; + + while (prev->next && prev->next != filter) + prev = prev->next; + if (prev->next) + prev->next = filter->next; +} + +#ifdef MODULE +int init_module(void) +{ + + return usb_scsi_init(); +} + +void cleanup_module(void) +{ + unsigned int offset; + + usb_deregister(&scsi_driver); +} +#endif diff --git a/drivers/usb/usb_scsi.h b/drivers/usb/usb_scsi.h new file mode 100644 index 000000000000..3e6386f4f1e3 --- /dev/null +++ b/drivers/usb/usb_scsi.h @@ -0,0 +1,147 @@ +/* Driver for USB scsi - include file + * + * (C) Michael Gee (michael@linuxspecific.com) 1999 + * + * This driver is scitzoid - it make a USB scanner appear as both a SCSI device + * and a character device. The latter is only available if the device has an + * interrupt endpoint, and is used specifically to receive interrupt events. + * + * In order to support various 'strange' scanners, this module supports plug in + * device specific filter modules, which can do their own thing when required. + * + */ + +#include + +#define USB_SCSI "usbscsi: " + +extern int usbscsi_debug; + +#ifdef CONFIG_USB_SCSI_DEBUG +void us_show_command(Scsi_Cmnd *srb); +#define US_DEBUGP(x...) { if(usbscsi_debug) printk( KERN_DEBUG USB_SCSI ## x ); } +#define US_DEBUGPX(x...) { if(usbscsi_debug) printk( ## x ); } +#define US_DEBUG(x) { if(usbscsi_debug) x; } +#else +#define US_DEBUGP(x...) +#define US_DEBUGPX(x...) +#define US_DEBUG(x) +#endif + +/* bit set if input */ +extern unsigned char us_direction[256/8]; +#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1) + +/* Sub Classes */ + +#define US_SC_RBC 1 /* Typically, flash devices */ +#define US_SC_8020 2 /* CD-ROM */ +#define US_SC_QIC 3 /* QIC-157 Tapes */ +#define US_SC_UFI 4 /* Floppy */ +#define US_SC_8070 5 /* Removable media */ +#define US_SC_SCSI 6 /* Transparent */ +#define US_SC_MIN US_SC_RBC +#define US_SC_MAX US_SC_SCSI + +/* Protocols */ + +#define US_PR_CB 1 /* Control/Bulk w/o interrupt */ +#define US_PR_CBI 0 /* Control/Bulk/Interrupt */ +#define US_PR_ZIP 0x50 /* bulk only */ +/* #define US_PR_BULK ?? */ + +/* + * Bulk only data structures (Zip 100, for example) + */ + +struct bulk_cb_wrap { + __u32 Signature; /* contains 'USBC' */ + __u32 Tag; /* unique per command id */ + __u32 DataTransferLength; /* size of data */ + __u8 Flags; /* direction in bit 0 */ + __u8 Lun; /* LUN normally 0 */ + __u8 Length; /* of of the CDB */ + __u8 CDB[16]; /* max command */ +}; + +#define US_BULK_CB_WRAP_LEN 31 +#define US_BULK_CB_SIGN 0x43425355 +#define US_BULK_FLAG_IN 1 +#define US_BULK_FLAG_OUT 0 + +struct bulk_cs_wrap { + __u32 Signature; /* should = 'USBS' */ + __u32 Tag; /* same as original command */ + __u32 Residue; /* amount not transferred */ + __u8 Status; /* see below */ + __u8 Filler[18]; +}; + +#define US_BULK_CS_WRAP_LEN 31 +#define US_BULK_CS_SIGN 0x53425355 +#define US_BULK_STAT_OK 0 +#define US_BULK_STAT_FAIL 1 +#define US_BULK_STAT_PHASE 2 + +#define US_BULK_RESET 0xff +#define US_BULK_RESET_SOFT 1 +#define US_BULK_RESET_HARD 0 + +/* + * CBI style + */ + +#define US_CBI_ADSC 0 + +/* + * Filter device definitions + */ +struct usb_scsi_filter { + + struct usb_scsi_filter * next; /* usb_scsi driver only */ + char *name; /* not really required */ + + unsigned int flags; /* Filter flags */ + void * (* probe) (struct usb_device *, char *, char *, char *); /* probe device */ + void (* release)(void *); /* device gone */ + int (* command)(void *, Scsi_Cmnd *); /* all commands */ +}; + +#define GUID(x) __u32 x[3] +#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2]) +#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0; +#define GUID_NONE(x) (!x[0] && !x[1] && !x[2]) +#define GUID_FORMAT "%08x%08x%08x" +#define GUID_ARGS(x) x[0], x[1], x[2] + +static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial) +{ + pg[0] = (vendor << 16) | product; + pg[1] = pg[2] = 0; + while (*serial) { + pg[1] <<= 4; + pg[1] |= pg[2] >> 28; + pg[2] <<= 4; + if (*serial >= 'a') + *serial -= 'a' - 'A'; + pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0' + : *serial - 'A' + 10; + serial++; + } +} + +/* Flag definitions */ +#define US_FL_IP_STATUS 0x00000001 /* status uses interrupt */ +#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */ + +/* + * Called by filters to register/unregister the mini driver + * + * WARNING - the supplied probe function may be called before exiting this fn + */ +int usb_scsi_register(struct usb_scsi_filter *); +void usb_scsi_deregister(struct usb_scsi_filter *); + +#ifdef CONFIG_USB_HP4100 +int hp4100_init(void); +#endif diff --git a/drivers/usb/usb_scsi_debug.c b/drivers/usb/usb_scsi_debug.c new file mode 100644 index 000000000000..1a4273f94eb2 --- /dev/null +++ b/drivers/usb/usb_scsi_debug.c @@ -0,0 +1,116 @@ + +/* Driver for USB scsi like devices + * + * (C) Michael Gee (michael@linuxspecific.com) 1999 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" + +#include "usb.h" +#include "usb_scsi.h" + +void us_show_command(Scsi_Cmnd *srb) +{ + char *what; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case READ_TOC: what = "READ_TOC"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: break; + } + printk(KERN_DEBUG USB_SCSI "Command %s (%d bytes)\n", what, srb->cmd_len); + printk(KERN_DEBUG USB_SCSI " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], + srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]); + printk(KERN_DEBUG USB_SCSI " use_sg=%d buff=%p len=%d\n", + srb->use_sg, srb->request_buffer, srb->request_bufflen); + if (srb->use_sg) { + struct scatterlist *sg = (struct scatterlist *) srb->request_buffer; + int i; + printk(KERN_DEBUG USB_SCSI " "); + for (i = 0; i < srb->use_sg && i < 5; ++i) + printk(" [%p %d]", sg[i].address, sg[i].length); + if (i < srb->use_sg) + printk(" ..."); + printk("\n"); + } +} diff --git a/drivers/usb/usb_scsi_dt.c b/drivers/usb/usb_scsi_dt.c new file mode 100644 index 000000000000..5d615bdff923 --- /dev/null +++ b/drivers/usb/usb_scsi_dt.c @@ -0,0 +1,4 @@ +0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, +0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c index 12ab76c65baa..450ada8a6cd3 100644 --- a/drivers/video/fbcon.c +++ b/drivers/video/fbcon.c @@ -215,7 +215,7 @@ static int fbcon_show_logo(void); #ifdef CONFIG_MAC /* - * On the Macintoy, there may or may not be a working VBL int. We need to prob + * On the Macintoy, there may or may not be a working VBL int. We need to probe */ static int vbl_detected = 0; diff --git a/fs/Makefile b/fs/Makefile index 54c31189f7a1..2f24a94ea6eb 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -13,7 +13,7 @@ O_TARGET := fs.o O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \ - dcache.o inode.o attr.o bad_inode.o $(BINFMTS) + dcache.o inode.o attr.o bad_inode.o file.o $(BINFMTS) MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ diff --git a/fs/autofs/root.c b/fs/autofs/root.c index c1b57ec6e969..4f21959d079f 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -165,7 +165,7 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str * yet completely filled in, and revalidate has to delay such * lookups.. */ -static int autofs_revalidate(struct dentry * dentry, int flags) +static int autofs_do_revalidate(struct dentry * dentry, int flags) { struct inode * dir = dentry->d_parent->d_inode; struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); @@ -200,6 +200,15 @@ static int autofs_revalidate(struct dentry * dentry, int flags) return 1; } +static int autofs_revalidate(struct dentry * dentry, int flags) +{ + int r; + up(&dentry->d_parent->d_inode->i_sem); + r = autofs_do_revalidate(dentry, flags); + down(&dentry->d_parent->d_inode->i_sem); + return r; +} + static struct dentry_operations autofs_dentry_operations = { autofs_revalidate, /* d_revalidate */ NULL, /* d_hash */ @@ -237,9 +246,7 @@ static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentr dentry->d_flags |= DCACHE_AUTOFS_PENDING; d_add(dentry, NULL); - up(&dir->i_sem); autofs_revalidate(dentry, 0); - down(&dir->i_sem); /* * If we are still pending, check if we had to handle diff --git a/fs/block_dev.c b/fs/block_dev.c index 13b3f534debc..401d9f34dfd1 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -14,7 +14,8 @@ extern int *blk_size[]; extern int *blksize_size[]; #define MAX_BUF_PER_PAGE (PAGE_SIZE / 512) -#define NBUF 64 +#define NBUF 128 +#define READAHEAD_SECTORS (128 * 4 * 2) ssize_t block_write(struct file * filp, const char * buf, size_t count, loff_t *ppos) @@ -152,12 +153,13 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) size_t blocks, rblocks, left; int bhrequest, uptodate; struct buffer_head ** bhb, ** bhe; - struct buffer_head * buflist[NBUF]; - struct buffer_head * bhreq[NBUF]; + struct buffer_head ** buflist; + struct buffer_head ** bhreq; unsigned int chars; loff_t size; kdev_t dev; ssize_t read; + int nbuf; dev = inode->i_rdev; blocksize = BLOCK_SIZE; @@ -187,6 +189,18 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) left = count; if (left <= 0) return 0; + + if ((buflist = (struct buffer_head **) __get_free_page(GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((bhreq = (struct buffer_head **) __get_free_page(GFP_KERNEL)) == NULL) { + free_page((unsigned long) buflist); + return -ENOMEM; + } + + nbuf = READAHEAD_SECTORS / (blocksize >> 9); + if (nbuf > PAGE_SIZE / sizeof(struct buffer_head *)) + nbuf = PAGE_SIZE / sizeof(struct buffer_head *); + read = 0; block = offset >> blocksize_bits; offset &= blocksize-1; @@ -194,8 +208,12 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) rblocks = blocks = (left + offset + blocksize - 1) >> blocksize_bits; bhb = bhe = buflist; if (filp->f_reada) { +#if 0 if (blocks < read_ahead[MAJOR(dev)] / (blocksize >> 9)) blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9); +#else + blocks += read_ahead[MAJOR(dev)] / (blocksize >> 9); +#endif if (rblocks > blocks) blocks = rblocks; @@ -227,7 +245,7 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) bhreq[bhrequest++] = *bhb; } - if (++bhb == &buflist[NBUF]) + if (++bhb == &buflist[nbuf]) bhb = buflist; /* If the block we have on hand is uptodate, go ahead @@ -248,7 +266,7 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) wait_on_buffer(*bhe); if (!buffer_uptodate(*bhe)) { /* read error? */ brelse(*bhe); - if (++bhe == &buflist[NBUF]) + if (++bhe == &buflist[nbuf]) bhe = buflist; left = 0; break; @@ -270,7 +288,7 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) put_user(0,buf++); } offset = 0; - if (++bhe == &buflist[NBUF]) + if (++bhe == &buflist[nbuf]) bhe = buflist; } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe))); if (bhe == bhb && !blocks) @@ -280,9 +298,12 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) /* Release the read-ahead blocks */ while (bhe != bhb) { brelse(*bhe); - if (++bhe == &buflist[NBUF]) + if (++bhe == &buflist[nbuf]) bhe = buflist; }; + + free_page((unsigned long) buflist); + free_page((unsigned long) bhreq); if (!read) return -EIO; filp->f_reada = 1; diff --git a/fs/buffer.c b/fs/buffer.c index 1f76051878cf..5e08a9774f29 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -600,6 +600,7 @@ unsigned int get_hardblocksize(kdev_t dev) return 0; } +#if 0 void set_blocksize(kdev_t dev, int size) { extern int *blksize_size[]; @@ -645,10 +646,113 @@ void set_blocksize(kdev_t dev, int size) clear_bit(BH_Req, &bh->b_state); bh->b_flushtime = 0; } - remove_from_hash_queue(bh); + remove_from_queues(bh); + bh->b_dev=B_FREE; + insert_into_queues(bh); + } + } +} + +#else +void set_blocksize(kdev_t dev, int size) +{ + extern int *blksize_size[]; + int i, nlist; + struct buffer_head * bh, *bhnext; + + if (!blksize_size[MAJOR(dev)]) + return; + + /* Size must be a power of two, and between 512 and PAGE_SIZE */ + if (size > PAGE_SIZE || size < 512 || (size & (size-1))) + panic("Invalid blocksize passed to set_blocksize"); + + if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) { + blksize_size[MAJOR(dev)][MINOR(dev)] = size; + return; + } + if (blksize_size[MAJOR(dev)][MINOR(dev)] == size) + return; + sync_buffers(dev, 2); + blksize_size[MAJOR(dev)][MINOR(dev)] = size; + + /* We need to be quite careful how we do this - we are moving entries + * around on the free list, and we can get in a loop if we are not careful. + */ + for(nlist = 0; nlist < NR_LIST; nlist++) { + bh = lru_list[nlist]; + for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bhnext) { + if(!bh) + break; + + bhnext = bh->b_next_free; + if (bh->b_dev != dev) + continue; + if (bh->b_size == size) + continue; + if (bhnext) + bhnext->b_count++; + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_size != size) { + clear_bit(BH_Dirty, &bh->b_state); + clear_bit(BH_Uptodate, &bh->b_state); + clear_bit(BH_Req, &bh->b_state); + bh->b_flushtime = 0; + } + + /* + * lets be mega-conservative about what to free: + */ + if (!(bh->b_dev != dev) && + !(bh->b_size == size) && + !bh->b_count && + !buffer_protected(bh) && + !buffer_dirty(bh) && + !buffer_locked(bh) && + !waitqueue_active(&bh->b_wait)) { + remove_from_hash_queue(bh); + bh->b_dev = NODEV; + refile_buffer(bh); + try_to_free_buffers(buffer_page(bh)); + } else { + remove_from_queues(bh); + bh->b_dev=B_FREE; + insert_into_queues(bh); + } + if (bhnext) + bhnext->b_count--; } } } +#endif + +/* +* This function knows that we do a linear pass over the whole array, +* so we can drop all unused buffers. Careful, bforget alone is +* unsafe, we must be 100% sure that at the end of bforget() we will +* really have no (new) users of this buffer. +* +* this logic improves overall system performance greatly during array +* resync or reconstruction. Actually, the reconstruction is basically +* seemless. +*/ +void cache_drop_behind(struct buffer_head *bh) +{ + /* + * We are up to something dangerous ... rather be careful + */ + if ((bh->b_count != 1) || buffer_protected(bh) || + buffer_dirty(bh) || buffer_locked(bh) || + !buffer_lowprio(bh) || waitqueue_active(&bh->b_wait)) { + brelse(bh); + } else { + bh->b_count--; + remove_from_hash_queue(bh); + bh->b_dev = NODEV; + refile_buffer(bh); + try_to_free_buffers(buffer_page(bh)); + } +} /* * We used to try various strange things. Let's not. @@ -854,22 +958,21 @@ struct buffer_head * bread(kdev_t dev, int block, int size) * Ok, breada can be used as bread, but additionally to mark other * blocks for reading as well. End the argument list with a negative * number. + * + * __breada does the same but with block arguments. This is handy if a + * device is bigger than 2G on a 32-bit architecture. */ #define NBUF 16 -struct buffer_head * breada(kdev_t dev, int block, int bufsize, - unsigned int pos, unsigned int filesize) +struct buffer_head * breada_blocks(kdev_t dev, int block, + int bufsize, int blocks) { struct buffer_head * bhlist[NBUF]; - unsigned int blocks; struct buffer_head * bh; int index; int i, j; - if (pos >= filesize) - return NULL; - if (block < 0) return NULL; @@ -878,18 +981,14 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize, if (buffer_uptodate(bh)) return(bh); - else ll_rw_block(READ, 1, &bh); - - blocks = (filesize - pos) >> (9+index); + else + ll_rw_block(READ, 1, &bh); if (blocks < (read_ahead[MAJOR(dev)] >> index)) blocks = read_ahead[MAJOR(dev)] >> index; if (blocks > NBUF) blocks = NBUF; -/* if (blocks) printk("breada (new) %d blocks\n",blocks); */ - - bhlist[0] = bh; j = 1; for(i=1; i= filesize) + return NULL; + + index = BUFSIZE_INDEX(bufsize); + + blocks = (filesize - pos) >> (9+index); + + return (breada_blocks(dev,block,bufsize,blocks)); +} + /* * Note: the caller should wake up the buffer_wait list if needed. */ diff --git a/fs/exec.c b/fs/exec.c index 767be4f2d01e..6212a061038a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -482,10 +482,10 @@ static inline void flush_old_files(struct files_struct * files) unsigned long set, i; i = j * __NFDBITS; - if (i >= files->max_fds) + if (i >= files->max_fds || i >= files->max_fdset) break; - set = files->close_on_exec.fds_bits[j]; - files->close_on_exec.fds_bits[j] = 0; + set = files->close_on_exec->fds_bits[j]; + files->close_on_exec->fds_bits[j] = 0; j++; for ( ; set ; i++,set >>= 1) { if (set & 1) diff --git a/fs/fcntl.c b/fs/fcntl.c index 036c9eb1fe96..2749adcdfe76 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -12,14 +12,15 @@ extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); -static inline int dupfd(unsigned int fd, unsigned int arg) +static inline int dupfd(unsigned int fd, unsigned int start) { struct files_struct * files = current->files; struct file * file; + unsigned int newfd; int error; error = -EINVAL; - if (arg >= NR_OPEN) + if (start >= NR_OPEN) goto out; error = -EBADF; @@ -27,15 +28,39 @@ static inline int dupfd(unsigned int fd, unsigned int arg) if (!file) goto out; +repeat: error = -EMFILE; - arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg); - if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur) + if (start < files->next_fd) + start = files->next_fd; + /* At this point, start MUST be <= max_fdset */ +#if 1 + if (start > files->max_fdset) + printk (KERN_ERR "dupfd: fd %d, max %d\n", + start, files->max_fdset); +#endif + newfd = find_next_zero_bit(files->open_fds->fds_bits, + files->max_fdset, + start); + if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur) goto out_putf; - FD_SET(arg, &files->open_fds); - FD_CLR(arg, &files->close_on_exec); - fd_install(arg, file); - error = arg; + + error = expand_files(files, newfd); + if (error < 0) + goto out_putf; + if (error) /* If we might have blocked, try again. */ + goto repeat; + + FD_SET(newfd, files->open_fds); + FD_CLR(newfd, files->close_on_exec); + if (start <= files->next_fd) + files->next_fd = newfd + 1; + fd_install(newfd, file); + error = newfd; out: +#ifdef FDSET_DEBUG + if (error < 0) + printk (KERN_ERR __FUNCTION__ ": return %d\n", error); +#endif return error; out_putf: @@ -48,18 +73,30 @@ asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) int err = -EBADF; lock_kernel(); +#ifdef FDSET_DEBUG + printk (KERN_ERR __FUNCTION__ " 0: oldfd = %d, newfd = %d\n", + oldfd, newfd); +#endif if (!fcheck(oldfd)) goto out; + if (newfd >= NR_OPEN) + goto out; /* following POSIX.1 6.2.1 */ + err = newfd; if (newfd == oldfd) goto out; - err = -EBADF; - if (newfd >= NR_OPEN) - goto out; /* following POSIX.1 6.2.1 */ + /* We must be able to do the fd setting inside dupfd() without + blocking after the sys_close(). */ + if ((err = expand_files(current->files, newfd)) < 0) + goto out; + sys_close(newfd); err = dupfd(oldfd, newfd); out: +#ifdef FDSET_DEBUG + printk (KERN_ERR __FUNCTION__ ": return %d\n", err); +#endif unlock_kernel(); return err; } @@ -71,6 +108,10 @@ asmlinkage int sys_dup(unsigned int fildes) lock_kernel(); ret = dupfd(fildes, 0); unlock_kernel(); +#ifdef FDSET_DEBUG + if (ret < 0) + printk (KERN_ERR __FUNCTION__ ": return %d\n", ret); +#endif return ret; } @@ -111,19 +152,20 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) filp = fget(fd); if (!filp) goto out; + err = 0; switch (cmd) { case F_DUPFD: err = dupfd(fd, arg); break; case F_GETFD: - err = FD_ISSET(fd, ¤t->files->close_on_exec); + err = FD_ISSET(fd, current->files->close_on_exec); break; case F_SETFD: if (arg&1) - FD_SET(fd, ¤t->files->close_on_exec); + FD_SET(fd, current->files->close_on_exec); else - FD_CLR(fd, ¤t->files->close_on_exec); + FD_CLR(fd, current->files->close_on_exec); break; case F_GETFL: err = filp->f_flags; @@ -151,7 +193,6 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_owner.pid; break; case F_SETOWN: - err = 0; filp->f_owner.pid = arg; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; @@ -171,10 +212,9 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) break; default: /* sockets need a few special fcntls. */ + err = -EINVAL; if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, cmd, arg); - else - err = -EINVAL; break; } fput(filp); diff --git a/fs/file.c b/fs/file.c new file mode 100644 index 000000000000..fac340c6df6f --- /dev/null +++ b/fs/file.c @@ -0,0 +1,224 @@ +/* + * linux/fs/open.c + * + * Copyright (C) 1998, Stephen Tweedie and Bill Hawes + * + * Manage the dynamic fd arrays in the process files_struct. + */ + +#include +#include +#include +#include +#include + +#include + + +/* + * Allocate an fd array, using get_free_page() if possible. + * Note: the array isn't cleared at allocation time. + */ +struct file ** alloc_fd_array(int num) +{ + struct file **new_fds; + int size = num * sizeof(struct file *); + + if (size < PAGE_SIZE) + new_fds = (struct file **) kmalloc(size, GFP_KERNEL); + else if (size == PAGE_SIZE) + new_fds = (struct file **) __get_free_page(GFP_KERNEL); + else + new_fds = (struct file **) vmalloc(size); + return new_fds; +} + +void free_fd_array(struct file **array, int num) +{ + int size = num * sizeof(struct file *); + + if (!array) { + printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num); + return; + } + + if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */ + return; + else if (size < PAGE_SIZE) + kfree(array); + else if (size == PAGE_SIZE) + free_page((unsigned long) array); + else + vfree(array); +} + +/* + * Expand the fd array in the files_struct. + */ + +int expand_fd_array(struct files_struct *files, int nr) +{ + struct file **new_fds; + int error, nfds; + + + error = -EMFILE; + if (files->max_fds >= NR_OPEN || nr > NR_OPEN) + goto out; + + nfds = files->max_fds; + + /* + * Expand to the max in easy steps, and keep expanding it until + * we have enough for the requested fd array size. + */ + + do { +#if NR_OPEN_DEFAULT < 256 + if (nfds < 256) + nfds = 256; + else +#endif + if (nfds < (PAGE_SIZE / sizeof(struct file *))) + nfds = PAGE_SIZE / sizeof(struct file *); + else { + nfds = nfds * 2; + if (nfds > NR_OPEN) + nfds = NR_OPEN; + } + } while (nfds < nr); + + error = -ENOMEM; + new_fds = alloc_fd_array(nfds); + if (!new_fds) + goto out; + + /* Copy the existing array and install the new pointer */ + + if (nfds > files->max_fds) { + struct file **old_fds; + int i = files->max_fds; + + old_fds = files->fd; + files->fd = new_fds; + files->max_fds = nfds; + /* Don't copy/clear the array if we are creating a new + fd array for fork() */ + if (i) { + memcpy(new_fds, old_fds, i * sizeof(struct file *)); + /* clear the remainder of the array */ + memset(&new_fds[i], 0, + (nfds-i) * sizeof(struct file *)); + free_fd_array(old_fds, i); + } + } else { + /* Somebody expanded the array while we slept ... */ + free_fd_array(new_fds, nfds); + } + error = 0; +out: + return error; +} + +/* + * Allocate an fdset array, using get_free_page() if possible. + * Note: the array isn't cleared at allocation time. + */ +fd_set * alloc_fdset(int num) +{ + fd_set *new_fdset; + int size = num / 8; + + if (size < PAGE_SIZE) + new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL); + else if (size == PAGE_SIZE) + new_fdset = (fd_set *) __get_free_page(GFP_KERNEL); + else + new_fdset = (fd_set *) vmalloc(size); + return new_fdset; +} + +void free_fdset(fd_set *array, int num) +{ + int size = num / 8; + + if (!array) { + printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num); + return; + } + + if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */ + return; + else if (size < PAGE_SIZE) + kfree(array); + else if (size == PAGE_SIZE) + free_page((unsigned long) array); + else + vfree(array); +} + +/* + * Expand the fdset in the files_struct. + */ +int expand_fdset(struct files_struct *files, int nr) +{ + fd_set *new_openset = 0, *new_execset = 0; + int error, nfds = 0; + + error = -EMFILE; + if (files->max_fdset >= NR_OPEN || nr > NR_OPEN) + goto out; + + nfds = files->max_fdset; + /* Expand to the max in easy steps */ + do { + if (nfds < (PAGE_SIZE * 8)) + nfds = PAGE_SIZE * 8; + else { + nfds = nfds * 2; + if (nfds > NR_OPEN) + nfds = NR_OPEN; + } + } while (nfds < nr); + + error = -ENOMEM; + new_openset = alloc_fdset(nfds); + new_execset = alloc_fdset(nfds); + if (!new_openset || !new_execset) + goto out; + + error = 0; + + /* Copy the existing tables and install the new pointers */ + if (nfds > files->max_fdset) { + int i = files->max_fdset / (sizeof(unsigned long) * 8); + int count = (nfds - files->max_fdset) / 8; + + /* + * Don't copy the entire array if the current fdset is + * not yet initialised. + */ + if (i) { + memcpy (new_openset, files->open_fds, files->max_fdset/8); + memcpy (new_execset, files->close_on_exec, files->max_fdset/8); + memset (&new_openset->fds_bits[i], 0, count); + memset (&new_execset->fds_bits[i], 0, count); + } + + free_fdset (files->close_on_exec, files->max_fdset); + free_fdset (files->open_fds, files->max_fdset); + files->max_fdset = nfds; + files->open_fds = new_openset; + files->close_on_exec = new_execset; + return 0; + } + /* Somebody expanded the array while we slept ... */ + +out: + if (new_openset) + free_fdset(new_openset, nfds); + if (new_execset) + free_fdset(new_execset, nfds); + return error; +} + diff --git a/fs/ioctl.c b/fs/ioctl.c index 200a171de6c7..97a763cd9a05 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -54,11 +54,11 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) error = 0; switch (cmd) { case FIOCLEX: - FD_SET(fd, ¤t->files->close_on_exec); + FD_SET(fd, current->files->close_on_exec); break; case FIONCLEX: - FD_CLR(fd, ¤t->files->close_on_exec); + FD_CLR(fd, current->files->close_on_exec); break; case FIONBIO: diff --git a/fs/nls/Config.in b/fs/nls/Config.in index 7e591b82af08..7d86a4091bca 100644 --- a/fs/nls/Config.in +++ b/fs/nls/Config.in @@ -38,6 +38,7 @@ if [ "$CONFIG_NLS" = "y" ]; then tristate 'NLS ISO 8859-7 (Modern Greek)' CONFIG_NLS_ISO8859_7 tristate 'NLS ISO 8859-8 (Hebrew)' CONFIG_NLS_ISO8859_8 tristate 'NLS ISO 8859-9 (Latin 5; Turkish)' CONFIG_NLS_ISO8859_9 + tristate 'NLS ISO 8859-14 (Latin 8; Celtic)' CONFIG_NLS_ISO8859_14 tristate 'NLS ISO 8859-15 (Latin 9; Western European Languages with Euro)' CONFIG_NLS_ISO8859_15 tristate 'NLS KOI8-R (Russian)' CONFIG_NLS_KOI8_R endmenu diff --git a/fs/nls/Makefile b/fs/nls/Makefile index 879fef8ff6f0..3d7688f0d4cd 100644 --- a/fs/nls/Makefile +++ b/fs/nls/Makefile @@ -286,6 +286,14 @@ else endif endif +ifeq ($(CONFIG_NLS_ISO8859_14),y) +NLS += nls_iso8859-14.o +else + ifeq ($(CONFIG_NLS_ISO8859_14),m) + M_OBJS += nls_iso8859-14.o + endif +endif + ifeq ($(CONFIG_NLS_ISO8859_15),y) NLS += nls_iso8859-15.o else diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c index b6483a1d67ed..40d5d6c2cbd2 100644 --- a/fs/nls/nls_base.c +++ b/fs/nls/nls_base.c @@ -429,6 +429,9 @@ int init_nls(void) #ifdef CONFIG_NLS_ISO8859_9 init_nls_iso8859_9(); #endif +#ifdef CONFIG_NLS_ISO8859_14 + init_nls_iso8859_14(); +#endif #ifdef CONFIG_NLS_ISO8859_15 init_nls_iso8859_15(); #endif diff --git a/fs/nls/nls_iso8859-14.c b/fs/nls/nls_iso8859-14.c new file mode 100644 index 000000000000..b466a6560475 --- /dev/null +++ b/fs/nls/nls_iso8859-14.c @@ -0,0 +1,275 @@ +/* + * linux/fs/nls_iso8859-14.c + * + * Charset iso8859-14 translation tables. + * + * Generated automatically from the Unicode and charset table + * provided by the Unicode Organisation at + * http://www.unicode.org/ + * The Unicode to charset table has only exact mappings. + * + * Rhys Jones, Swansea University Computer Society + * rhys@sucs.swan.ac.uk + */ + +#include +#include +#include +#include + +static struct nls_unicode charset2uni[256] = { + /* 0x00*/ + {0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, + {0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00}, + {0x08, 0x00}, {0x09, 0x00}, {0x0a, 0x00}, {0x0b, 0x00}, + {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00}, + /* 0x10*/ + {0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00}, + {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, + {0x18, 0x00}, {0x19, 0x00}, {0x1a, 0x00}, {0x1b, 0x00}, + {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00}, + /* 0x20*/ + {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00}, + {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, + {0x28, 0x00}, {0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x2f, 0x00}, + /* 0x30*/ + {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, + {0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00}, + {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0x00}, {0x3b, 0x00}, + {0x3c, 0x00}, {0x3d, 0x00}, {0x3e, 0x00}, {0x3f, 0x00}, + /* 0x40*/ + {0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00}, + {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00}, + {0x48, 0x00}, {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00}, + {0x4c, 0x00}, {0x4d, 0x00}, {0x4e, 0x00}, {0x4f, 0x00}, + /* 0x50*/ + {0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00}, + {0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00}, + {0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00}, + {0x5c, 0x00}, {0x5d, 0x00}, {0x5e, 0x00}, {0x5f, 0x00}, + /* 0x60*/ + {0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00}, + {0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00}, + {0x68, 0x00}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00}, + {0x6c, 0x00}, {0x6d, 0x00}, {0x6e, 0x00}, {0x6f, 0x00}, + /* 0x70*/ + {0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00}, + {0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00}, + {0x78, 0x00}, {0x79, 0x00}, {0x7a, 0x00}, {0x7b, 0x00}, + {0x7c, 0x00}, {0x7d, 0x00}, {0x7e, 0x00}, {0x7f, 0x00}, + /* 0x80*/ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + /* 0x90*/ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + /* 0xa0*/ + {0xa0, 0x00}, {0x02, 0x1e}, {0x03, 0x1e}, {0xa3, 0x00}, + {0x0a, 0x01}, {0x0b, 0x01}, {0x0a, 0x1e}, {0xa7, 0x00}, + {0x80, 0x1e}, {0xa9, 0x00}, {0x82, 0x1e}, {0x0b, 0x1e}, + {0xf2, 0x1e}, {0xad, 0x00}, {0xae, 0x00}, {0x78, 0x01}, + /* 0xb0*/ + {0x1e, 0x1e}, {0x1f, 0x1e}, {0x20, 0x01}, {0x21, 0x01}, + {0x40, 0x1e}, {0x41, 0x1e}, {0xb6, 0x00}, {0x56, 0x1e}, + {0x81, 0x1e}, {0x57, 0x1e}, {0x83, 0x1e}, {0x60, 0x1e}, + {0xf3, 0x1e}, {0x84, 0x1e}, {0x85, 0x1e}, {0x61, 0x1e}, + /* 0xc0*/ + {0xc0, 0x00}, {0xc1, 0x00}, {0xc2, 0x00}, {0xc3, 0x00}, + {0xc4, 0x00}, {0xc5, 0x00}, {0xc6, 0x00}, {0xc7, 0x00}, + {0xc8, 0x00}, {0xc9, 0x00}, {0xca, 0x00}, {0xcb, 0x00}, + {0xcc, 0x00}, {0xcd, 0x00}, {0xce, 0x00}, {0xcf, 0x00}, + /* 0xd0*/ + {0x74, 0x01}, {0xd1, 0x00}, {0xd2, 0x00}, {0xd3, 0x00}, + {0xd4, 0x00}, {0xd5, 0x00}, {0xd6, 0x00}, {0x6a, 0x1e}, + {0xd8, 0x00}, {0xd9, 0x00}, {0xda, 0x00}, {0xdb, 0x00}, + {0xdc, 0x00}, {0xdd, 0x00}, {0x76, 0x01}, {0xdf, 0x00}, + /* 0xe0*/ + {0xe0, 0x00}, {0xe1, 0x00}, {0xe2, 0x00}, {0xe3, 0x00}, + {0xe4, 0x00}, {0xe5, 0x00}, {0xe6, 0x00}, {0xe7, 0x00}, + {0xe8, 0x00}, {0xe9, 0x00}, {0xea, 0x00}, {0xeb, 0x00}, + {0xec, 0x00}, {0xed, 0x00}, {0xee, 0x00}, {0xef, 0x00}, + /* 0xf0*/ + {0x75, 0x01}, {0xf1, 0x00}, {0xf2, 0x00}, {0xf3, 0x00}, + {0xf4, 0x00}, {0xf5, 0x00}, {0xf6, 0x00}, {0x6b, 0x1e}, + {0xf8, 0x00}, {0xf9, 0x00}, {0xfa, 0x00}, {0xfb, 0x00}, + {0xfc, 0x00}, {0xfd, 0x00}, {0x77, 0x01}, {0xff, 0x00}, +}; + +static unsigned char page00[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ + 0xa0, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xa7, /* 0xa0-0xa7 */ + 0x00, 0xa9, 0x00, 0x00, 0x00, 0xad, 0xae, 0x00, /* 0xa8-0xaf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, /* 0xb0-0xb7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */ + 0x00, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0x00, /* 0xd0-0xd7 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0x00, 0xdf, /* 0xd8-0xdf */ + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */ + 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0x00, /* 0xf0-0xf7 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x00, 0xff, /* 0xf8-0xff */ +}; + +static unsigned char page01[256] = { + 0x00, 0x00, 0xa1, 0xa2, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */ + 0x00, 0x00, 0xa6, 0xab, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xb1, /* 0x18-0x1f */ + 0xb2, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */ + 0xb4, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xb9, /* 0x50-0x57 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */ + 0xbb, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */ + 0x00, 0x00, 0xd7, 0xf7, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */ + 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf0, 0xde, 0xfe, /* 0x70-0x77 */ + 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */ + + 0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0x00, 0x00, /* 0x80-0x87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */ + 0x00, 0x00, 0xac, 0xbc, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */ +}; + +static unsigned char page1e[256] = { + 0x00, 0x00, 0xa1, 0xa2, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */ + 0x00, 0x00, 0xa6, 0xab, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xb1, /* 0x18-0x1f */ + 0xb2, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */ + 0xb4, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xb9, /* 0x50-0x57 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */ + 0xbb, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */ + 0x00, 0x00, 0xd7, 0xf7, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */ + 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf0, 0xde, 0xfe, /* 0x70-0x77 */ + 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */ + + 0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0x00, 0x00, /* 0x80-0x87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */ + 0x00, 0x00, 0xac, 0xbc, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */ +}; + +static unsigned char *page_uni2charset[256] = { + page00, page01, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, page1e, NULL, + + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +static void inc_use_count(void) +{ + MOD_INC_USE_COUNT; +} + +static void dec_use_count(void) +{ + MOD_DEC_USE_COUNT; +} + +static struct nls_table table = { + "iso8859-14", + page_uni2charset, + charset2uni, + inc_use_count, + dec_use_count, + NULL +}; + +int init_nls_iso8859_14(void) +{ + return register_nls(&table); +} + +#ifdef MODULE +int init_module(void) +{ + return init_nls_iso8859_14(); +} + + +void cleanup_module(void) +{ + unregister_nls(&table); + return; +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * End: + */ diff --git a/fs/open.c b/fs/open.c index 629008d4d9ea..c7d05f35d093 100644 --- a/fs/open.c +++ b/fs/open.c @@ -691,9 +691,13 @@ int get_unused_fd(void) { struct files_struct * files = current->files; int fd, error; - + +repeat: error = -EMFILE; - fd = find_first_zero_bit(&files->open_fds, NR_OPEN); + + fd = find_next_zero_bit(files->open_fds, + current->files->max_fdset, + files->next_fd); /* * N.B. For clone tasks sharing a files structure, this test * will limit the total number of files that can be opened. @@ -701,10 +705,27 @@ int get_unused_fd(void) if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur) goto out; - /* Check here for fd > files->max_fds to do dynamic expansion */ + /* Do we need to expand the fdset array? */ + if (fd >= current->files->max_fdset) { + error = expand_fdset(files, 0); + if (!error) + goto repeat; + goto out; + } + + /* + * Check whether we need to expand the fd array. + */ + if (fd >= files->max_fds) { + error = expand_fd_array(files, 0); + if (!error) + goto repeat; + goto out; + } - FD_SET(fd, &files->open_fds); - FD_CLR(fd, &files->close_on_exec); + FD_SET(fd, files->open_fds); + FD_CLR(fd, files->close_on_exec); + files->next_fd = fd + 1; #if 1 /* Sanity check */ if (files->fd[fd] != NULL) { @@ -715,12 +736,18 @@ int get_unused_fd(void) error = fd; out: +#ifdef FDSET_DEBUG + if (error < 0) + printk (KERN_ERR __FUNCTION__ ": return %d\n", error); +#endif return error; } inline void put_unused_fd(unsigned int fd) { - FD_CLR(fd, ¤t->files->open_fds); + FD_CLR(fd, current->files->open_fds); + if (fd < current->files->next_fd) + current->files->next_fd = fd; } asmlinkage int sys_open(const char * filename, int flags, int mode) @@ -820,8 +847,8 @@ asmlinkage int sys_close(unsigned int fd) struct files_struct * files = current->files; files->fd[fd] = NULL; put_unused_fd(fd); - FD_CLR(fd, &files->close_on_exec); - error = filp_close(filp, files); + FD_CLR(fd, files->close_on_exec); + error = filp_close(filp, files); } unlock_kernel(); return error; diff --git a/fs/proc/array.c b/fs/proc/array.c index 5d1daeb8d761..d02cea19e59c 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -76,6 +76,11 @@ int get_malloc(char * buffer); #endif +static int open_kcore(struct inode * inode, struct file * filp) +{ + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + static ssize_t read_core(struct file * file, char * buf, size_t count, loff_t *ppos) { @@ -140,6 +145,12 @@ static ssize_t read_core(struct file * file, char * buf, static struct file_operations proc_kcore_operations = { NULL, /* lseek */ read_core, + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + open_kcore }; struct inode_operations proc_kcore_inode_operations = { diff --git a/fs/select.c b/fs/select.c index 1ba9d9be440c..290676166018 100644 --- a/fs/select.c +++ b/fs/select.c @@ -107,7 +107,7 @@ static int max_select_fd(unsigned long n, fd_set_bits *fds) /* handle last in-complete long-word first */ set = ~(~0UL << (n & (__NFDBITS-1))); n /= __NFDBITS; - open_fds = current->files->open_fds.fds_bits+n; + open_fds = current->files->open_fds->fds_bits+n; max = 0; if (set) { set &= BITS(fds, n); @@ -264,21 +264,31 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) if ((unsigned long) sec < MAX_SELECT_SECONDS) { timeout = ROUND_UP(usec, 1000000/HZ); timeout += sec * (unsigned long) HZ; + + if (timeout < 0) { + ret = -EINVAL; + goto out_nofds; + } } } ret = -EINVAL; + + /* + * We ought to optimise the n=0 case - it is used enough.. + */ + if (n < 0) goto out_nofds; - - if (n > KFDS_NR) - n = KFDS_NR; - + if (n > current->files->max_fdset + 1) + n = current->files->max_fdset + 1; + /* * We need 6 bitmaps (in/out/ex for both incoming and outgoing), * since we used fdset we need to allocate memory in units of - * long-words. + * long-words. */ + ret = -ENOMEM; size = FDS_BYTES(n); bits = kmalloc(6 * size, GFP_KERNEL); @@ -383,13 +393,13 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) lock_kernel(); /* Do a sanity check on nfds ... */ err = -EINVAL; - if (nfds > NR_OPEN) + if (nfds > current->files->max_fds) goto out; if (timeout) { - /* Carefula about overflow in the intermediate values */ + /* Careful about overflow in the intermediate values */ if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ) - timeout = (unsigned long)(timeout*HZ+999)/1000+1; + timeout = (timeout*HZ+999)/1000+1; else /* Negative or overflow */ timeout = MAX_SCHEDULE_TIMEOUT; } diff --git a/include/asm-alpha/md.h b/include/asm-alpha/md.h deleted file mode 100644 index 6c9b8222a4f2..000000000000 --- a/include/asm-alpha/md.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: md.h,v 1.1 1997/12/15 15:11:48 jj Exp $ - * md.h: High speed xor_block operation for RAID4/5 - * - */ - -#ifndef __ASM_MD_H -#define __ASM_MD_H - -/* #define HAVE_ARCH_XORBLOCK */ - -#define MD_XORBLOCK_ALIGNMENT sizeof(long) - -#endif /* __ASM_MD_H */ diff --git a/include/asm-alpha/resource.h b/include/asm-alpha/resource.h index 96b338fc48be..c6b6314ee705 100644 --- a/include/asm-alpha/resource.h +++ b/include/asm-alpha/resource.h @@ -28,7 +28,7 @@ {_STK_LIM, _STK_LIM}, /* RLIMIT_STACK */ \ { 0, LONG_MAX}, /* RLIMIT_CORE */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_RSS */ \ - { NR_OPEN, NR_OPEN}, /* RLIMIT_NOFILE */ \ + {INR_OPEN, INR_OPEN}, /* RLIMIT_NOFILE */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_AS */ \ {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, /* RLIMIT_NPROC */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_MEMLOCK */ \ diff --git a/include/asm-i386/md.h b/include/asm-i386/md.h deleted file mode 100644 index 0a2c5dd01c0c..000000000000 --- a/include/asm-i386/md.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: md.h,v 1.1 1997/12/15 15:11:57 jj Exp $ - * md.h: High speed xor_block operation for RAID4/5 - * - */ - -#ifndef __ASM_MD_H -#define __ASM_MD_H - -/* #define HAVE_ARCH_XORBLOCK */ - -#define MD_XORBLOCK_ALIGNMENT sizeof(long) - -#endif /* __ASM_MD_H */ diff --git a/include/asm-i386/resource.h b/include/asm-i386/resource.h index e7e2d115996c..fcbe5c1b586c 100644 --- a/include/asm-i386/resource.h +++ b/include/asm-i386/resource.h @@ -29,7 +29,7 @@ { 0, LONG_MAX }, \ { LONG_MAX, LONG_MAX }, \ { MAX_TASKS_PER_USER, MAX_TASKS_PER_USER }, \ - { NR_OPEN, NR_OPEN }, \ + { INR_OPEN, INR_OPEN }, \ { LONG_MAX, LONG_MAX }, \ { LONG_MAX, LONG_MAX }, \ } diff --git a/include/asm-m68k/md.h b/include/asm-m68k/md.h deleted file mode 100644 index 1d15aae21c0b..000000000000 --- a/include/asm-m68k/md.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: md.h,v 1.1 1997/12/15 15:12:04 jj Exp $ - * md.h: High speed xor_block operation for RAID4/5 - * - */ - -#ifndef __ASM_MD_H -#define __ASM_MD_H - -/* #define HAVE_ARCH_XORBLOCK */ - -#define MD_XORBLOCK_ALIGNMENT sizeof(long) - -#endif /* __ASM_MD_H */ diff --git a/include/asm-m68k/resource.h b/include/asm-m68k/resource.h index 09ae4cfd25fb..3f981c7b68ed 100644 --- a/include/asm-m68k/resource.h +++ b/include/asm-m68k/resource.h @@ -29,7 +29,7 @@ { 0, LONG_MAX}, \ {LONG_MAX, LONG_MAX}, \ {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \ - {NR_OPEN, NR_OPEN}, \ + {INR_OPEN, INR_OPEN}, \ {LONG_MAX, LONG_MAX}, \ {LONG_MAX, LONG_MAX} \ } diff --git a/include/asm-mips/resource.h b/include/asm-mips/resource.h index e14c45c29097..fa6d68ee1389 100644 --- a/include/asm-mips/resource.h +++ b/include/asm-mips/resource.h @@ -34,7 +34,7 @@ {LONG_MAX, LONG_MAX}, \ {_STK_LIM, _STK_LIM}, \ { 0, LONG_MAX}, \ - {NR_OPEN, NR_OPEN}, \ + {INR_OPEN, INR_OPEN}, \ {LONG_MAX, LONG_MAX}, \ {LONG_MAX, LONG_MAX}, \ {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \ diff --git a/include/asm-ppc/keyboard.h b/include/asm-ppc/keyboard.h index a2895fa554b2..13ee32487a35 100644 --- a/include/asm-ppc/keyboard.h +++ b/include/asm-ppc/keyboard.h @@ -29,6 +29,10 @@ #define DISABLE_KBD_DURING_INTERRUPTS 0 #define INIT_KBD +#ifdef CONFIG_PREP +extern int prep_kbd_present; +#endif /* CONFIG_PREP */ + static inline int kbd_setkeycode(unsigned int scancode, unsigned int keycode) { return ppc_md.kbd_setkeycode(scancode, keycode); @@ -52,12 +56,18 @@ static inline int kbd_unexpected_up(unsigned char keycode) static inline void kbd_leds(unsigned char leds) { - ppc_md.kbd_leds(leds); +#ifdef CONFIG_PREP + if (prep_kbd_present) +#endif /* CONFIG_PREP */ + ppc_md.kbd_leds(leds); } static inline void kbd_init_hw(void) { - ppc_md.kbd_init_hw(); +#ifdef CONFIG_PREP + if (prep_kbd_present) +#endif /* CONFIG_PREP */ + ppc_md.kbd_init_hw(); } #define kbd_sysrq_xlate (ppc_md.kbd_sysrq_xlate) diff --git a/include/asm-ppc/md.h b/include/asm-ppc/md.h deleted file mode 100644 index 0ff3e7e92dcd..000000000000 --- a/include/asm-ppc/md.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: md.h,v 1.1 1997/12/15 15:12:15 jj Exp $ - * md.h: High speed xor_block operation for RAID4/5 - * - */ - -#ifndef __ASM_MD_H -#define __ASM_MD_H - -/* #define HAVE_ARCH_XORBLOCK */ - -#define MD_XORBLOCK_ALIGNMENT sizeof(long) - -#endif /* __ASM_MD_H */ diff --git a/include/asm-ppc/resource.h b/include/asm-ppc/resource.h index 674e6d6f1eb6..7fe812abb3f8 100644 --- a/include/asm-ppc/resource.h +++ b/include/asm-ppc/resource.h @@ -25,7 +25,7 @@ { 0, LONG_MAX}, /* RLIMIT_CORE */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_RSS */ \ {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, /* RLIMIT_NPROC */ \ - { NR_OPEN, NR_OPEN}, /* RLIMIT_NOFILE */ \ + {INR_OPEN, INR_OPEN}, /* RLIMIT_NOFILE */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_MEMLOCK */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_AS */ \ } diff --git a/include/asm-sparc/md.h b/include/asm-sparc/md.h deleted file mode 100644 index e0d0e85a5539..000000000000 --- a/include/asm-sparc/md.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: md.h,v 1.1 1997/12/15 15:12:39 jj Exp $ - * md.h: High speed xor_block operation for RAID4/5 - * - */ - -#ifndef __ASM_MD_H -#define __ASM_MD_H - -/* #define HAVE_ARCH_XORBLOCK */ - -#define MD_XORBLOCK_ALIGNMENT sizeof(long) - -#endif /* __ASM_MD_H */ diff --git a/include/asm-sparc/resource.h b/include/asm-sparc/resource.h index 7e4f49093f4c..ff73c8e50376 100644 --- a/include/asm-sparc/resource.h +++ b/include/asm-sparc/resource.h @@ -31,7 +31,7 @@ {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \ {LONG_MAX, LONG_MAX}, {_STK_LIM, LONG_MAX}, \ { 0, LONG_MAX}, {LONG_MAX, LONG_MAX}, \ - {NR_OPEN, NR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \ + {INR_OPEN, INR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \ {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX} \ } diff --git a/include/asm-sparc64/md.h b/include/asm-sparc64/md.h deleted file mode 100644 index 03879934e7d5..000000000000 --- a/include/asm-sparc64/md.h +++ /dev/null @@ -1,91 +0,0 @@ -/* $Id: md.h,v 1.2 1997/12/27 16:28:38 jj Exp $ - * md.h: High speed xor_block operation for RAID4/5 - * utilizing the UltraSparc Visual Instruction Set. - * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - */ - -#ifndef __ASM_MD_H -#define __ASM_MD_H - -#include -#include - -#define HAVE_ARCH_XORBLOCK - -#define MD_XORBLOCK_ALIGNMENT 64 - -/* void __xor_block (char *dest, char *src, long len) - * { - * while (len--) *dest++ ^= *src++; - * } - * - * Requirements: - * !(((long)dest | (long)src) & (MD_XORBLOCK_ALIGNMENT - 1)) && - * !(len & 127) && len >= 256 - */ - -static inline void __xor_block (char *dest, char *src, long len) -{ - __asm__ __volatile__ (" - wr %%g0, %3, %%fprs - wr %%g0, %4, %%asi - membar #LoadStore|#StoreLoad|#StoreStore - sub %2, 128, %2 - ldda [%0] %4, %%f0 - ldda [%1] %4, %%f16 -1: ldda [%0 + 64] %%asi, %%f32 - fxor %%f0, %%f16, %%f16 - fxor %%f2, %%f18, %%f18 - fxor %%f4, %%f20, %%f20 - fxor %%f6, %%f22, %%f22 - fxor %%f8, %%f24, %%f24 - fxor %%f10, %%f26, %%f26 - fxor %%f12, %%f28, %%f28 - fxor %%f14, %%f30, %%f30 - stda %%f16, [%0] %4 - ldda [%1 + 64] %%asi, %%f48 - ldda [%0 + 128] %%asi, %%f0 - fxor %%f32, %%f48, %%f48 - fxor %%f34, %%f50, %%f50 - add %0, 128, %0 - fxor %%f36, %%f52, %%f52 - add %1, 128, %1 - fxor %%f38, %%f54, %%f54 - subcc %2, 128, %2 - fxor %%f40, %%f56, %%f56 - fxor %%f42, %%f58, %%f58 - fxor %%f44, %%f60, %%f60 - fxor %%f46, %%f62, %%f62 - stda %%f48, [%0 - 64] %%asi - bne,pt %%xcc, 1b - ldda [%1] %4, %%f16 - ldda [%0 + 64] %%asi, %%f32 - fxor %%f0, %%f16, %%f16 - fxor %%f2, %%f18, %%f18 - fxor %%f4, %%f20, %%f20 - fxor %%f6, %%f22, %%f22 - fxor %%f8, %%f24, %%f24 - fxor %%f10, %%f26, %%f26 - fxor %%f12, %%f28, %%f28 - fxor %%f14, %%f30, %%f30 - stda %%f16, [%0] %4 - ldda [%1 + 64] %%asi, %%f48 - membar #Sync - fxor %%f32, %%f48, %%f48 - fxor %%f34, %%f50, %%f50 - fxor %%f36, %%f52, %%f52 - fxor %%f38, %%f54, %%f54 - fxor %%f40, %%f56, %%f56 - fxor %%f42, %%f58, %%f58 - fxor %%f44, %%f60, %%f60 - fxor %%f46, %%f62, %%f62 - stda %%f48, [%0 + 64] %%asi - membar #Sync|#StoreStore|#StoreLoad - wr %%g0, 0, %%fprs - " : : - "r" (dest), "r" (src), "r" (len), "i" (FPRS_FEF), "i" (ASI_BLK_P) : - "cc", "memory"); -} - -#endif /* __ASM_MD_H */ diff --git a/include/asm-sparc64/resource.h b/include/asm-sparc64/resource.h index 02ba40894d49..7e490c729c85 100644 --- a/include/asm-sparc64/resource.h +++ b/include/asm-sparc64/resource.h @@ -30,7 +30,7 @@ {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \ {LONG_MAX, LONG_MAX}, {_STK_LIM, LONG_MAX}, \ { 0, LONG_MAX}, {LONG_MAX, LONG_MAX}, \ - {NR_OPEN, NR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \ + {INR_OPEN, INR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \ {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX} \ } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 87a9092b219f..808f69323db9 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -62,8 +62,9 @@ extern void unplug_device(void * data); extern void make_request(int major,int rw, struct buffer_head * bh); /* md needs this function to remap requests */ -extern int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size); -extern int md_make_request (int minor, int rw, struct buffer_head * bh); +extern int md_map (kdev_t dev, kdev_t *rdev, + unsigned long *rsector, unsigned long size); +extern int md_make_request (struct buffer_head * bh, int rw); extern int md_error (kdev_t mddev, kdev_t rdev); extern int * blk_size[MAX_BLKDEV]; diff --git a/include/linux/fcdevice.h b/include/linux/fcdevice.h new file mode 100644 index 000000000000..22a1e4542a1a --- /dev/null +++ b/include/linux/fcdevice.h @@ -0,0 +1,40 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. NET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the Fibre Channel handlers. + * + * Version: @(#)fcdevice.h 1.0.0 09/26/98 + * + * Authors: Vineet Abraham + * + * Relocated to include/linux where it belongs by Alan Cox + * + * + * 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. + * + * WARNING: This move may well be temporary. This file will get merged with others RSN. + * + */ +#ifndef _LINUX_FCDEVICE_H +#define _LINUX_FCDEVICE_H + + +#include + +#ifdef __KERNEL__ +extern int fc_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, + void *saddr, unsigned len); +extern int fc_rebuild_header(struct sk_buff *skb); +//extern unsigned short fc_type_trans(struct sk_buff *skb, struct device *dev); + +extern struct device * init_fcdev(struct device *, int); + +#endif + +#endif /* _LINUX_FCDEVICE_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 1d55ef52d3b4..2fd11f7b4964 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -20,24 +20,26 @@ #include #include -#include +#include #include struct poll_table_struct; /* - * It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix - * that later. Anyway, now the file code is no longer dependent - * on bitmaps in unsigned longs, but uses the new fd_set structure.. + * It's silly to have NR_OPEN bigger than NR_FILE, but you can change + * the file limit at runtime and only root can increase the per-process + * nr_file rlimit, so it's safe to set up a ridiculously high absolute + * upper limit on files-per-process. * * Some programs (notably those using select()) may have to be - * recompiled to take full advantage of the new limits.. + * recompiled to take full advantage of the new limits.. */ /* Fixed constants first: */ #undef NR_OPEN -#define NR_OPEN 1024 +#define NR_OPEN (1024*1024) /* Absolute upper limit on fd num */ +#define INR_OPEN 1024 /* Initial setting for nfile rlimits */ #define BLOCK_SIZE_BITS 10 #define BLOCK_SIZE (1< - struct fasync_struct { int magic; int fa_fd; @@ -754,6 +755,7 @@ extern struct file *inuse_filps; extern void refile_buffer(struct buffer_head * buf); extern void set_writetime(struct buffer_head * buf, int flag); extern int try_to_free_buffers(struct page *); +extern void cache_drop_behind(struct buffer_head *bh); extern int nr_buffers; extern int buffermem; @@ -774,6 +776,25 @@ extern inline void mark_buffer_clean(struct buffer_head * bh) } } +extern inline void mark_buffer_highprio(struct buffer_head * bh) +{ + clear_bit(BH_LowPrio, &bh->b_state); +} + +extern inline void mark_buffer_lowprio(struct buffer_head * bh) +{ + /* + * dirty buffers cannot be marked lowprio. + */ + if (!buffer_dirty(bh)) + set_bit(BH_LowPrio, &bh->b_state); +} + +static inline int buffer_lowprio(struct buffer_head * bh) +{ + return test_bit(BH_LowPrio, &bh->b_state); +} + extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag) { if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { @@ -781,6 +802,23 @@ extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag) if (bh->b_list != BUF_DIRTY) refile_buffer(bh); } + /* + * if a buffer gets marked dirty then it has to lose + * it's lowprio state. + */ + mark_buffer_highprio(bh); +} + +extern inline void mark_buffer_dirty_lowprio(struct buffer_head * bh) +{ + if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { + if (bh->b_list != BUF_DIRTY) + refile_buffer(bh); + /* + * Mark it lowprio only if it was not dirty before! + */ + set_bit(BH_LowPrio, &bh->b_state); + } } extern int check_disk_change(kdev_t dev); @@ -851,6 +889,7 @@ extern struct buffer_head * getblk(kdev_t, int, int); extern struct buffer_head * find_buffer(kdev_t dev, int block, int size); extern void ll_rw_block(int, int, struct buffer_head * bh[]); extern int is_read_only(kdev_t); +extern int is_device_idle(kdev_t); extern void __brelse(struct buffer_head *); extern inline void brelse(struct buffer_head *buf) { @@ -866,8 +905,12 @@ extern inline void bforget(struct buffer_head *buf) extern void set_blocksize(kdev_t dev, int size); extern unsigned int get_hardblocksize(kdev_t dev); extern struct buffer_head * bread(kdev_t dev, int block, int size); +extern struct buffer_head * buffer_ready (kdev_t dev, int block, int size); +extern void bread_ahead (kdev_t dev, int block, int size); extern struct buffer_head * breada(kdev_t dev,int block, int size, unsigned int pos, unsigned int filesize); +extern struct buffer_head * breada_blocks(kdev_t dev,int block, + int size, int blocks); extern int brw_page(int, struct page *, kdev_t, int [], int, int); @@ -903,9 +946,7 @@ extern int file_fsync(struct file *, struct dentry *dir); extern int inode_change_ok(struct inode *, struct iattr *); extern void inode_setattr(struct inode *, struct iattr *); -/* kludge to get SCSI modules working */ -#include -#include +extern __u32 inode_generation_count; #endif /* __KERNEL__ */ diff --git a/include/linux/if_fc.h b/include/linux/if_fc.h new file mode 100644 index 000000000000..33330b458b95 --- /dev/null +++ b/include/linux/if_fc.h @@ -0,0 +1,50 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for Fibre Channel. + * + * Version: @(#)if_fc.h 0.0 11/20/98 + * + * Author: Fred N. van Kempen, + * Donald Becker, + * Peter De Schrijver, + * Vineet Abraham, + * + * 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. + */ +#ifndef _LINUX_IF_FC_H +#define _LINUX_IF_FC_H + + +#define FC_ALEN 6 /* Octets in one ethernet addr */ +#define FC_HLEN (sizeof(struct fch_hdr)+sizeof(struct fcllc)) +#define FC_ID_LEN 3 /* Octets in a Fibre Channel Address */ + +/* LLC and SNAP constants */ +#define EXTENDED_SAP 0xAA +#define UI_CMD 0x03 + +/* This is NOT the Fibre Channel frame header. The FC frame header is + * constructed in the driver as the Tachyon needs certain fields in + * certains positions. So, it can't be generalized here.*/ + +struct fch_hdr { + __u8 daddr[FC_ALEN]; /* destination address */ + __u8 saddr[FC_ALEN]; /* source address */ +}; + +/* This is a Fibre Channel LLC structure */ +struct fcllc { + __u8 dsap; /* destination SAP */ + __u8 ssap; /* source SAP */ + __u8 llc; /* LLC control field */ + __u8 protid[3]; /* protocol id */ + __u16 ethertype; /* ether type field */ +}; + +#endif /* _LINUX_IF_FC_H */ diff --git a/include/linux/ip_masq.h b/include/linux/ip_masq.h index ba8931383023..66d1a1d68be2 100644 --- a/include/linux/ip_masq.h +++ b/include/linux/ip_masq.h @@ -103,6 +103,26 @@ struct ip_mfw_user { #define IP_MASQ_MFW_SCHED 0x01 +/* + * VS & schedulers stuff + */ +struct ip_vs_user { + /* create the virtual service and attach the scheduler to it */ + u_int16_t protocol; + u_int32_t vaddr; /* virtual address */ + u_int16_t vport; + /* ... timeouts and other stuff */ + + /* scheduler specific options */ + u_int32_t daddr; /* real destination address */ + u_int16_t dport; + unsigned masq_flags; + unsigned sched_flags; + unsigned weight; + char data[0]; /* optional scheduler parameters */ +}; + + #define IP_FW_MASQCTL_MAX 256 #define IP_MASQ_TNAME_MAX 32 @@ -115,6 +135,7 @@ struct ip_masq_ctl { struct ip_autofw_user autofw_user; struct ip_mfw_user mfw_user; struct ip_masq_user user; + struct ip_vs_user vs_user; unsigned char m_raw[IP_FW_MASQCTL_MAX]; } u; }; @@ -124,7 +145,10 @@ struct ip_masq_ctl { #define IP_MASQ_TARGET_CORE 1 #define IP_MASQ_TARGET_MOD 2 /* masq_mod is selected by "name" */ #define IP_MASQ_TARGET_USER 3 -#define IP_MASQ_TARGET_LAST 4 +#define IP_MASQ_TARGET_VS 4 /* sched_mod is selected by "name" */ +/* #define IP_MASQ_TARGET_VS_SCHED 5 */ +#define IP_MASQ_TARGET_LAST 5 + #define IP_MASQ_CMD_NONE 0 /* just peek */ #define IP_MASQ_CMD_INSERT 1 @@ -136,5 +160,9 @@ struct ip_masq_ctl { #define IP_MASQ_CMD_LIST 7 /* actually fake: done via /proc */ #define IP_MASQ_CMD_ENABLE 8 #define IP_MASQ_CMD_DISABLE 9 +#define IP_MASQ_CMD_ADD_DEST 10 /* for adding dest in IPVS */ +#define IP_MASQ_CMD_DEL_DEST 11 /* for deleting dest in IPVS */ +#define IP_MASQ_CMD_SET_DEST 12 /* for setting dest in IPVS */ #endif /* _LINUX_IP_MASQ_H */ + diff --git a/include/linux/major.h b/include/linux/major.h index a3d244bfb675..571c9be0724c 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -64,7 +64,6 @@ #define AZTECH_CDROM_MAJOR 29 #define GRAPHDEV_MAJOR 29 /* SparcLinux & Linux/68k /dev/fb */ #define SHMIQ_MAJOR 85 /* Linux/MIPS, SGI /dev/shmiq */ -#define USEMA_MAJOR 86 /* Linux/MIPS, SGI /dev/usema */ #define CM206_CDROM_MAJOR 32 #define IDE2_MAJOR 33 #define IDE3_MAJOR 34 diff --git a/include/linux/md.h b/include/linux/md.h deleted file mode 100644 index f4f4f54866ec..000000000000 --- a/include/linux/md.h +++ /dev/null @@ -1,300 +0,0 @@ -/* - md.h : Multiple Devices driver for Linux - Copyright (C) 1994-96 Marc ZYNGIER - or - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - You should have received a copy of the GNU General Public License - (for example /usr/src/linux/COPYING); if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef _MD_H -#define _MD_H - -#include -#include -#include - -/* - * Different major versions are not compatible. - * Different minor versions are only downward compatible. - * Different patchlevel versions are downward and upward compatible. - */ -#define MD_MAJOR_VERSION 0 -#define MD_MINOR_VERSION 36 -#define MD_PATCHLEVEL_VERSION 6 - -#define MD_DEFAULT_DISK_READAHEAD (256 * 1024) - -/* ioctls */ -#define REGISTER_DEV _IO (MD_MAJOR, 1) -#define START_MD _IO (MD_MAJOR, 2) -#define STOP_MD _IO (MD_MAJOR, 3) -#define REGISTER_DEV_NEW _IO (MD_MAJOR, 4) - -/* - personalities : - Byte 0 : Chunk size factor - Byte 1 : Fault tolerance count for each physical device - ( 0 means no fault tolerance, - 0xFF means always tolerate faults), not used by now. - Byte 2 : Personality - Byte 3 : Reserved. - */ - -#define FAULT_SHIFT 8 -#define PERSONALITY_SHIFT 16 - -#define FACTOR_MASK 0x000000FFUL -#define FAULT_MASK 0x0000FF00UL -#define PERSONALITY_MASK 0x00FF0000UL - -#define MD_RESERVED 0 /* Not used by now */ -#define LINEAR (1UL << PERSONALITY_SHIFT) -#define STRIPED (2UL << PERSONALITY_SHIFT) -#define RAID0 STRIPED -#define RAID1 (3UL << PERSONALITY_SHIFT) -#define RAID5 (4UL << PERSONALITY_SHIFT) -#define MAX_PERSONALITY 5 - -/* - * MD superblock. - * - * The MD superblock maintains some statistics on each MD configuration. - * Each real device in the MD set contains it near the end of the device. - * Some of the ideas are copied from the ext2fs implementation. - * - * We currently use 4096 bytes as follows: - * - * word offset function - * - * 0 - 31 Constant generic MD device information. - * 32 - 63 Generic state information. - * 64 - 127 Personality specific information. - * 128 - 511 12 32-words descriptors of the disks in the raid set. - * 512 - 911 Reserved. - * 912 - 1023 Disk specific descriptor. - */ - -/* - * If x is the real device size in bytes, we return an apparent size of: - * - * y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES - * - * and place the 4kB superblock at offset y. - */ -#define MD_RESERVED_BYTES (64 * 1024) -#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) -#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) - -#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) -#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) - -#define MD_SB_BYTES 4096 -#define MD_SB_WORDS (MD_SB_BYTES / 4) -#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) -#define MD_SB_SECTORS (MD_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define MD_SB_GENERIC_OFFSET 0 -#define MD_SB_PERSONALITY_OFFSET 64 -#define MD_SB_DISKS_OFFSET 128 -#define MD_SB_DESCRIPTOR_OFFSET 992 - -#define MD_SB_GENERIC_CONSTANT_WORDS 32 -#define MD_SB_GENERIC_STATE_WORDS 32 -#define MD_SB_GENERIC_WORDS (MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS) -#define MD_SB_PERSONALITY_WORDS 64 -#define MD_SB_DISKS_WORDS 384 -#define MD_SB_DESCRIPTOR_WORDS 32 -#define MD_SB_RESERVED_WORDS (1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS) -#define MD_SB_EQUAL_WORDS (MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS) -#define MD_SB_DISKS (MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS) - -/* - * Device "operational" state bits - */ -#define MD_FAULTY_DEVICE 0 /* Device is faulty / operational */ -#define MD_ACTIVE_DEVICE 1 /* Device is a part or the raid set / spare disk */ -#define MD_SYNC_DEVICE 2 /* Device is in sync with the raid set */ - -typedef struct md_device_descriptor_s { - __u32 number; /* 0 Device number in the entire set */ - __u32 major; /* 1 Device major number */ - __u32 minor; /* 2 Device minor number */ - __u32 raid_disk; /* 3 The role of the device in the raid set */ - __u32 state; /* 4 Operational state */ - __u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5]; -} md_descriptor_t; - -#define MD_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define MD_SB_CLEAN 0 -#define MD_SB_ERRORS 1 - -typedef struct md_superblock_s { - - /* - * Constant generic information - */ - __u32 md_magic; /* 0 MD identifier */ - __u32 major_version; /* 1 major version to which the set conforms */ - __u32 minor_version; /* 2 minor version to which the set conforms */ - __u32 patch_version; /* 3 patchlevel version to which the set conforms */ - __u32 gvalid_words; /* 4 Number of non-reserved words in this section */ - __u32 set_magic; /* 5 Raid set identifier */ - __u32 ctime; /* 6 Creation time */ - __u32 level; /* 7 Raid personality (mirroring, raid5, ...) */ - __u32 size; /* 8 Apparent size of each individual disk, in kB */ - __u32 nr_disks; /* 9 Number of total disks in the raid set */ - __u32 raid_disks; /* 10 Number of disks in a fully functional raid set */ - __u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 11]; - - /* - * Generic state information - */ - __u32 utime; /* 0 Superblock update time */ - __u32 state; /* 1 State bits (clean, ...) */ - __u32 active_disks; /* 2 Number of currently active disks (some non-faulty disks might not be in sync) */ - __u32 working_disks; /* 3 Number of working disks */ - __u32 failed_disks; /* 4 Number of failed disks */ - __u32 spare_disks; /* 5 Number of spare disks */ - __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 6]; - - /* - * Personality information - */ - __u32 parity_algorithm; - __u32 chunk_size; - __u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 2]; - - /* - * Disks information - */ - md_descriptor_t disks[MD_SB_DISKS]; - - /* - * Reserved - */ - __u32 reserved[MD_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - md_descriptor_t descriptor; -} md_superblock_t; - -#ifdef __KERNEL__ - -#include -#include -#include -#include - -/* - * Kernel-based reconstruction is mostly working, but still requires - * some additional work. - */ -#define SUPPORT_RECONSTRUCTION 0 - -#define MAX_REAL 8 /* Max number of physical dev per md dev */ -#define MAX_MD_DEV 4 /* Max number of md dev */ - -#define FACTOR(a) ((a)->repartition & FACTOR_MASK) -#define MAX_FAULT(a) (((a)->repartition & FAULT_MASK)>>8) -#define PERSONALITY(a) ((a)->repartition & PERSONALITY_MASK) - -#define FACTOR_SHIFT(a) (PAGE_SHIFT + (a) - 10) - -struct real_dev -{ - kdev_t dev; /* Device number */ - int size; /* Device size (in blocks) */ - int offset; /* Real device offset (in blocks) in md dev - (only used in linear mode) */ - struct inode *inode; /* Lock inode */ - md_superblock_t *sb; - u32 sb_offset; -}; - -struct md_dev; - -#define SPARE_INACTIVE 0 -#define SPARE_WRITE 1 -#define SPARE_ACTIVE 2 - -struct md_personality -{ - char *name; - int (*map)(struct md_dev *mddev, kdev_t *rdev, - unsigned long *rsector, unsigned long size); - int (*make_request)(struct md_dev *mddev, int rw, struct buffer_head * bh); - void (*end_request)(struct buffer_head * bh, int uptodate); - int (*run)(int minor, struct md_dev *mddev); - int (*stop)(int minor, struct md_dev *mddev); - int (*status)(char *page, int minor, struct md_dev *mddev); - int (*ioctl)(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); - int max_invalid_dev; - int (*error_handler)(struct md_dev *mddev, kdev_t dev); - -/* - * Some personalities (RAID-1, RAID-5) can get disks hot-added and - * hot-removed. Hot removal is different from failure. (failure marks - * a disk inactive, but the disk is still part of the array) - */ - int (*hot_add_disk) (struct md_dev *mddev, kdev_t dev); - int (*hot_remove_disk) (struct md_dev *mddev, kdev_t dev); - int (*mark_spare) (struct md_dev *mddev, md_descriptor_t *descriptor, int state); -}; - -struct md_dev -{ - struct real_dev devices[MAX_REAL]; - struct md_personality *pers; - md_superblock_t *sb; - int sb_dirty; - int repartition; - int busy; - int nb_dev; - void *private; -}; - -struct md_thread { - void (*run) (void *data); - void *data; - struct wait_queue *wqueue; - unsigned long flags; - struct semaphore *sem; - struct task_struct *tsk; -}; - -#define THREAD_WAKEUP 0 - -extern struct md_dev md_dev[MAX_MD_DEV]; -extern int md_size[MAX_MD_DEV]; -extern int md_maxreadahead[MAX_MD_DEV]; - -extern char *partition_name (kdev_t dev); - -extern int register_md_personality (int p_num, struct md_personality *p); -extern int unregister_md_personality (int p_num); -extern struct md_thread *md_register_thread (void (*run) (void *data), void *data); -extern void md_unregister_thread (struct md_thread *thread); -extern void md_wakeup_thread(struct md_thread *thread); -extern int md_update_sb (int minor); -extern int md_do_sync(struct md_dev *mddev); - -#endif __KERNEL__ -#endif _MD_H diff --git a/include/linux/mm.h b/include/linux/mm.h index 3e45b3b8bb87..0dc98d55612b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -355,10 +355,11 @@ static inline int expand_stack(struct vm_area_struct * vma, unsigned long addres address &= PAGE_MASK; grow = vma->vm_start - address; - if (vma->vm_end - address - > (unsigned long) current->rlim[RLIMIT_STACK].rlim_cur || - (vma->vm_mm->total_vm << PAGE_SHIFT) + grow - > (unsigned long) current->rlim[RLIMIT_AS].rlim_cur) + if ((vma->vm_end - address + > current->rlim[RLIMIT_STACK].rlim_cur) || + ((current->rlim[RLIMIT_AS].rlim_cur < RLIM_INFINITY) && + ((vma->vm_mm->total_vm << PAGE_SHIFT) + grow + > current->rlim[RLIMIT_AS].rlim_cur))) return -ENOMEM; vma->vm_start = address; vma->vm_offset -= grow; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f2ef43b29349..6d43f98d7a0c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -420,13 +420,17 @@ extern __inline__ void dev_init_buffers(struct device *dev) extern void ether_setup(struct device *dev); extern void fddi_setup(struct device *dev); extern void tr_setup(struct device *dev); +extern void fc_setup(struct device *dev); extern void tr_freedev(struct device *dev); +extern void fc_freedev(struct device *dev); extern int ether_config(struct device *dev, struct ifmap *map); /* Support for loadable net-drivers */ extern int register_netdev(struct device *dev); extern void unregister_netdev(struct device *dev); extern int register_trdev(struct device *dev); extern void unregister_trdev(struct device *dev); +extern int register_fcdev(struct device *dev); +extern void unregister_fcdev(struct device *dev); /* Functions used for multicast support */ extern void dev_mc_upload(struct device *dev); extern int dev_mc_delete(struct device *dev, void *addr, int alen, int all); diff --git a/include/linux/pci.h b/include/linux/pci.h index c3cc8dd5d1dc..505bb16fe83b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1072,6 +1072,9 @@ #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 + #define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 #define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 #define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 @@ -1143,6 +1146,10 @@ #define PCI_DEVICE_ID_ARK_STINGARK 0xa099 #define PCI_DEVICE_ID_ARK_2000MT 0xa0a1 +#define PCI_VENDOR_ID_INTERPHASE 0x107e +#define PCI_DEVICE_ID_INTERPHASE_5526 0x0004 +#define PCI_DEVICE_ID_INTERPHASE_55x6 0x0005 + /* * The PCI interface treats multi-function devices as independent * devices. The slot/function address of each device is encoded diff --git a/include/linux/poll.h b/include/linux/poll.h index 7eb57334fd4a..972c0cb06fc6 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -59,9 +59,6 @@ typedef struct { unsigned long *res_in, *res_out, *res_ex; } fd_set_bits; -/* - * How many longwords for "nr" bits? - */ #define FDS_BITPERLONG (8*sizeof(long)) #define FDS_LONGS(nr) (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG) #define FDS_BYTES(nr) (FDS_LONGS(nr)*sizeof(long)) @@ -90,14 +87,17 @@ int get_fd_set(unsigned long nr, void *ufdset, unsigned long *fdset) static inline void set_fd_set(unsigned long nr, void *ufdset, unsigned long *fdset) { - if (ufdset) - __copy_to_user(ufdset, fdset, FDS_BYTES(nr)); + if (ufdset) { + nr = FDS_BYTES(nr); + __copy_to_user(ufdset, fdset, nr); + } } static inline void zero_fd_set(unsigned long nr, unsigned long *fdset) { - memset(fdset, 0, FDS_BYTES(nr)); + nr = FDS_BYTES(nr); + memset(fdset, 0, nr); } extern int do_select(int n, fd_set_bits *fds, long *timeout); diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index dc047ec47b5b..9d0d94e2e556 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -206,8 +206,10 @@ enum scsi_directory_inos { PROC_SCSI_PLUTO, PROC_SCSI_INI9100U, PROC_SCSI_INIA100, + PROC_SCSI_IPH5526_FC, PROC_SCSI_FCAL, PROC_SCSI_I2O, + PROC_SCSI_USB_SCSI, PROC_SCSI_SCSI_DEBUG, PROC_SCSI_NOT_PRESENT, PROC_SCSI_FILE, /* I'm assuming here that we */ diff --git a/include/linux/raid/hsm.h b/include/linux/raid/hsm.h new file mode 100644 index 000000000000..0438d27aec4b --- /dev/null +++ b/include/linux/raid/hsm.h @@ -0,0 +1,65 @@ +#ifndef _LVM_H +#define _LVM_H + +#include + +#if __alpha__ +#error fix cpu_addr on Alpha first +#endif + +#include + +#define index_pv(lv,index) ((lv)->vg->pv_array+(index)->data.phys_nr) +#define index_dev(lv,index) index_pv((lv),(index))->dev +#define index_block(lv,index) (index)->data.phys_block +#define index_child(index) ((lv_lptr_t *)((index)->cpu_addr)) + +#define ptr_to_cpuaddr(ptr) ((__u32) (ptr)) + + +typedef struct pv_bg_desc_s { + unsigned int free_blocks; + pv_block_group_t *bg; +} pv_bg_desc_t; + +typedef struct pv_s pv_t; +typedef struct vg_s vg_t; +typedef struct lv_s lv_t; + +struct pv_s +{ + int phys_nr; + kdev_t dev; + pv_sb_t *pv_sb; + pv_bg_desc_t *bg_array; +}; + +struct lv_s +{ + int log_id; + vg_t *vg; + + unsigned int max_indices; + unsigned int free_indices; + lv_lptr_t root_index; + + kdev_t dev; +}; + +struct vg_s +{ + int nr_pv; + pv_t pv_array [MD_SB_DISKS]; + + int nr_lv; + lv_t lv_array [LVM_MAX_LVS_PER_VG]; + + vg_sb_t *vg_sb; + mddev_t *mddev; +}; + +#define kdev_to_lv(dev) ((lv_t *) mddev_map[MINOR(dev)].data) +#define mddev_to_vg(mddev) ((vg_t *) mddev->private) + +#endif + diff --git a/include/linux/raid/hsm_p.h b/include/linux/raid/hsm_p.h new file mode 100644 index 000000000000..02674b323544 --- /dev/null +++ b/include/linux/raid/hsm_p.h @@ -0,0 +1,237 @@ +#ifndef _LVM_P_H +#define _LVM_P_H + +#define LVM_BLOCKSIZE 4096 +#define LVM_BLOCKSIZE_WORDS (LVM_BLOCKSIZE/4) +#define PACKED __attribute__ ((packed)) + +/* + * Identifies a block in physical space + */ +typedef struct phys_idx_s { + __u16 phys_nr; + __u32 phys_block; + +} PACKED phys_idx_t; + +/* + * Identifies a block in logical space + */ +typedef struct log_idx_s { + __u16 log_id; + __u32 log_index; + +} PACKED log_idx_t; + +/* + * Describes one PV + */ +#define LVM_PV_SB_MAGIC 0xf091ae9fU + +#define LVM_PV_SB_GENERIC_WORDS 32 +#define LVM_PV_SB_RESERVED_WORDS \ + (LVM_BLOCKSIZE_WORDS - LVM_PV_SB_GENERIC_WORDS) + +/* + * On-disk PV identification data, on block 0 in any PV. + */ +typedef struct pv_sb_s +{ + __u32 pv_magic; /* 0 */ + + __u32 pv_uuid0; /* 1 */ + __u32 pv_uuid1; /* 2 */ + __u32 pv_uuid2; /* 3 */ + __u32 pv_uuid3; /* 4 */ + + __u32 pv_major; /* 5 */ + __u32 pv_minor; /* 6 */ + __u32 pv_patch; /* 7 */ + + __u32 pv_ctime; /* 8 Creation time */ + + __u32 pv_total_size; /* 9 size of this PV, in blocks */ + __u32 pv_first_free; /* 10 first free block */ + __u32 pv_first_used; /* 11 first used block */ + __u32 pv_blocks_left; /* 12 unallocated blocks */ + __u32 pv_bg_size; /* 13 size of a block group, in blocks */ + __u32 pv_block_size; /* 14 size of blocks, in bytes */ + __u32 pv_pptr_size; /* 15 size of block descriptor, in bytes */ + __u32 pv_block_groups; /* 16 number of block groups */ + + __u32 __reserved1[LVM_PV_SB_GENERIC_WORDS - 17]; + + /* + * Reserved + */ + __u32 __reserved2[LVM_PV_SB_RESERVED_WORDS]; + +} PACKED pv_sb_t; + +/* + * this is pretty much arbitrary, but has to be less than ~64 + */ +#define LVM_MAX_LVS_PER_VG 32 + +#define LVM_VG_SB_GENERIC_WORDS 32 + +#define LV_DESCRIPTOR_WORDS 8 +#define LVM_VG_SB_RESERVED_WORDS (LVM_BLOCKSIZE_WORDS - \ + LV_DESCRIPTOR_WORDS*LVM_MAX_LVS_PER_VG - LVM_VG_SB_GENERIC_WORDS) + +#if (LVM_PV_SB_RESERVED_WORDS < 0) +#error you messed this one up dude ... +#endif + +typedef struct lv_descriptor_s +{ + __u32 lv_id; /* 0 */ + phys_idx_t lv_root_idx; /* 1 */ + __u16 __reserved; /* 2 */ + __u32 lv_max_indices; /* 3 */ + __u32 lv_free_indices; /* 4 */ + __u32 md_id; /* 5 */ + + __u32 reserved[LV_DESCRIPTOR_WORDS - 6]; + +} PACKED lv_descriptor_t; + +#define LVM_VG_SB_MAGIC 0x98320d7aU +/* + * On-disk VG identification data, in block 1 on all PVs + */ +typedef struct vg_sb_s +{ + __u32 vg_magic; /* 0 */ + __u32 nr_lvs; /* 1 */ + + __u32 __reserved1[LVM_VG_SB_GENERIC_WORDS - 2]; + + lv_descriptor_t lv_array [LVM_MAX_LVS_PER_VG]; + /* + * Reserved + */ + __u32 __reserved2[LVM_VG_SB_RESERVED_WORDS]; + +} PACKED vg_sb_t; + +/* + * Describes one LV + */ + +#define LVM_LV_SB_MAGIC 0xe182bd8aU + +/* do we need lv_sb_t? */ + +typedef struct lv_sb_s +{ + /* + * On-disk LV identifier + */ + __u32 lv_magic; /* 0 LV identifier */ + __u32 lv_uuid0; /* 1 */ + __u32 lv_uuid1; /* 2 */ + __u32 lv_uuid2; /* 3 */ + __u32 lv_uuid3; /* 4 */ + + __u32 lv_major; /* 5 PV identifier */ + __u32 lv_minor; /* 6 PV identifier */ + __u32 lv_patch; /* 7 PV identifier */ + + __u32 ctime; /* 8 Creation time */ + __u32 size; /* 9 size of this LV, in blocks */ + phys_idx_t start; /* 10 position of root index block */ + log_idx_t first_free; /* 11-12 first free index */ + + /* + * Reserved + */ + __u32 reserved[LVM_BLOCKSIZE_WORDS-13]; + +} PACKED lv_sb_t; + +/* + * Pointer pointing from the physical space, points to + * the LV owning this block. It also contains various + * statistics about the physical block. + */ +typedef struct pv_pptr_s +{ + union { + /* case 1 */ + struct { + log_idx_t owner; + log_idx_t predicted; + __u32 last_referenced; + } used; + /* case 2 */ + struct { + __u16 log_id; + __u16 __unused1; + __u32 next_free; + __u32 __unused2; + __u32 __unused3; + } free; + } u; +} PACKED pv_pptr_t; + +static __inline__ int pv_pptr_free (const pv_pptr_t * pptr) +{ + return !pptr->u.free.log_id; +} + + +#define DATA_BLOCKS_PER_BG ((LVM_BLOCKSIZE*8)/(8*sizeof(pv_pptr_t)+1)) + +#define TOTAL_BLOCKS_PER_BG (DATA_BLOCKS_PER_BG+1) +/* + * A table of pointers filling up a single block, managing + * the next DATA_BLOCKS_PER_BG physical blocks. Such block + * groups form the physical space of blocks. + */ +typedef struct pv_block_group_s +{ + __u8 used_bitmap[(DATA_BLOCKS_PER_BG+7)/8]; + + pv_pptr_t blocks[DATA_BLOCKS_PER_BG]; + +} PACKED pv_block_group_t; + +/* + * Pointer from the logical space, points to + * the (PV,block) containing this logical block + */ +typedef struct lv_lptr_s +{ + phys_idx_t data; + __u16 __reserved; + __u32 cpu_addr; + __u32 __reserved2; + +} PACKED lv_lptr_t; + +static __inline__ int index_free (const lv_lptr_t * index) +{ + return !index->data.phys_block; +} + +static __inline__ int index_present (const lv_lptr_t * index) +{ + return index->cpu_addr; +} + + +#define LVM_LPTRS_PER_BLOCK (LVM_BLOCKSIZE/sizeof(lv_lptr_t)) +/* + * A table of pointers filling up a single block, managing + * LVM_LPTRS_PER_BLOCK logical blocks. Such block groups form + * the logical space of blocks. + */ +typedef struct lv_index_block_s +{ + lv_lptr_t blocks[LVM_LPTRS_PER_BLOCK]; + +} PACKED lv_index_block_t; + +#endif + diff --git a/include/linux/raid/linear.h b/include/linux/raid/linear.h new file mode 100644 index 000000000000..55cfab78f12f --- /dev/null +++ b/include/linux/raid/linear.h @@ -0,0 +1,32 @@ +#ifndef _LINEAR_H +#define _LINEAR_H + +#include + +struct dev_info { + kdev_t dev; + int size; + unsigned int offset; +}; + +typedef struct dev_info dev_info_t; + +struct linear_hash +{ + dev_info_t *dev0, *dev1; +}; + +struct linear_private_data +{ + struct linear_hash *hash_table; + dev_info_t disks[MD_SB_DISKS]; + dev_info_t *smallest; + int nr_zones; +}; + + +typedef struct linear_private_data linear_conf_t; + +#define mddev_to_conf(mddev) ((linear_conf_t *) mddev->private) + +#endif diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h new file mode 100644 index 000000000000..1059949801a6 --- /dev/null +++ b/include/linux/raid/md.h @@ -0,0 +1,95 @@ +/* + md.h : Multiple Devices driver for Linux + Copyright (C) 1996-98 Ingo Molnar, Gadi Oxman + Copyright (C) 1994-96 Marc ZYNGIER + or + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MD_H +#define _MD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/* + * 'md_p.h' holds the 'physical' layout of RAID devices + * 'md_u.h' holds the user <=> kernel API + * + * 'md_k.h' holds kernel internal definitions + */ + +#include +#include +#include + +/* + * Different major versions are not compatible. + * Different minor versions are only downward compatible. + * Different patchlevel versions are downward and upward compatible. + */ +#define MD_MAJOR_VERSION 0 +#define MD_MINOR_VERSION 90 +#define MD_PATCHLEVEL_VERSION 0 + +extern int md_size[MAX_MD_DEVS]; +extern struct hd_struct md_hd_struct[MAX_MD_DEVS]; + +extern void add_mddev_mapping (mddev_t *mddev, kdev_t dev, void *data); +extern void del_mddev_mapping (mddev_t *mddev, kdev_t dev); +extern char * partition_name (kdev_t dev); +extern int register_md_personality (int p_num, mdk_personality_t *p); +extern int unregister_md_personality (int p_num); +extern mdk_thread_t * md_register_thread (void (*run) (void *data), + void *data, const char *name); +extern void md_unregister_thread (mdk_thread_t *thread); +extern void md_wakeup_thread(mdk_thread_t *thread); +extern void md_interrupt_thread (mdk_thread_t *thread); +extern int md_update_sb (mddev_t *mddev); +extern int md_do_sync(mddev_t *mddev, mdp_disk_t *spare); +extern void md_recover_arrays (void); +extern int md_check_ordering (mddev_t *mddev); +extern void autodetect_raid(void); +extern struct gendisk * find_gendisk (kdev_t dev); +extern int md_notify_reboot(struct notifier_block *this, + unsigned long code, void *x); +#if CONFIG_BLK_DEV_MD +extern void raid_setup(char *str,int *ints) md__init; +#endif +#ifdef CONFIG_MD_BOOT +extern void md_setup(char *str,int *ints) md__init; +#endif + +extern void md_print_devices (void); + +#define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); } + +#endif _MD_H + diff --git a/include/linux/raid/md_compatible.h b/include/linux/raid/md_compatible.h new file mode 100644 index 000000000000..d4119a0811c9 --- /dev/null +++ b/include/linux/raid/md_compatible.h @@ -0,0 +1,387 @@ + +/* + md.h : Multiple Devices driver compatibility layer for Linux 2.0/2.2 + Copyright (C) 1998 Ingo Molnar + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#ifndef _MD_COMPATIBLE_H +#define _MD_COMPATIBLE_H + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0) + +/* 000 */ +#define md__get_free_pages(x,y) __get_free_pages(x,y,GFP_KERNEL) + +#ifdef __i386__ +/* 001 */ +extern __inline__ int md_cpu_has_mmx(void) +{ + return x86_capability & 0x00800000; +} +#endif + +/* 002 */ +#define md_clear_page(page) memset((void *)(page), 0, PAGE_SIZE) + +/* 003 */ +/* + * someone please suggest a sane compatibility layer for modules + */ +#define MD_EXPORT_SYMBOL(x) + +/* 004 */ +static inline unsigned long +md_copy_from_user(void *to, const void *from, unsigned long n) +{ + int err; + + err = verify_area(VERIFY_READ,from,n); + if (!err) + memcpy_fromfs(to, from, n); + return err; +} + +/* 005 */ +extern inline unsigned long +md_copy_to_user(void *to, const void *from, unsigned long n) +{ + int err; + + err = verify_area(VERIFY_WRITE,to,n); + if (!err) + memcpy_tofs(to, from, n); + return err; +} + +/* 006 */ +#define md_put_user(x,ptr) \ +({ \ + int __err; \ + \ + __err = verify_area(VERIFY_WRITE,ptr,sizeof(*ptr)); \ + if (!__err) \ + put_user(x,ptr); \ + __err; \ +}) + +/* 007 */ +extern inline int md_capable_admin(void) +{ + return suser(); +} + +/* 008 */ +#define MD_FILE_TO_INODE(file) ((file)->f_inode) + +/* 009 */ +extern inline void md_flush_signals (void) +{ + current->signal = 0; +} + +/* 010 */ +#define __S(nr) (1<<((nr)-1)) +extern inline void md_init_signals (void) +{ + current->exit_signal = SIGCHLD; + current->blocked = ~(__S(SIGKILL)); +} +#undef __S + +/* 011 */ +extern inline unsigned long md_signal_pending (struct task_struct * tsk) +{ + return (tsk->signal & ~tsk->blocked); +} + +/* 012 */ +#define md_set_global_readahead(x) read_ahead[MD_MAJOR] = MD_READAHEAD + +/* 013 */ +#define md_mdelay(n) (\ + {unsigned long msec=(n); while (msec--) udelay(1000);}) + +/* 014 */ +#define MD_SYS_DOWN 0 +#define MD_SYS_HALT 0 +#define MD_SYS_POWER_OFF 0 + +/* 015 */ +#define md_register_reboot_notifier(x) + +/* 016 */ +extern __inline__ unsigned long +md_test_and_set_bit(int nr, void * addr) +{ + unsigned long flags; + unsigned long oldbit; + + save_flags(flags); + cli(); + oldbit = test_bit(nr,addr); + set_bit(nr,addr); + restore_flags(flags); + return oldbit; +} + +/* 017 */ +extern __inline__ unsigned long +md_test_and_clear_bit(int nr, void * addr) +{ + unsigned long flags; + unsigned long oldbit; + + save_flags(flags); + cli(); + oldbit = test_bit(nr,addr); + clear_bit(nr,addr); + restore_flags(flags); + return oldbit; +} + +/* 018 */ +#define md_atomic_read(x) (*(volatile int *)(x)) +#define md_atomic_set(x,y) (*(volatile int *)(x) = (y)) + +/* 019 */ +extern __inline__ void md_lock_kernel (void) +{ +#if __SMP__ + lock_kernel(); + syscall_count++; +#endif +} + +extern __inline__ void md_unlock_kernel (void) +{ +#if __SMP__ + syscall_count--; + unlock_kernel(); +#endif +} +/* 020 */ + +#define md__init +#define md__initdata +#define md__initfunc(__arginit) __arginit + +/* 021 */ + +/* 022 */ + +struct md_list_head { + struct md_list_head *next, *prev; +}; + +#define MD_LIST_HEAD(name) \ + struct md_list_head name = { &name, &name } + +#define MD_INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +static __inline__ void md__list_add(struct md_list_head * new, + struct md_list_head * prev, + struct md_list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static __inline__ void md_list_add(struct md_list_head *new, + struct md_list_head *head) +{ + md__list_add(new, head, head->next); +} + +static __inline__ void md__list_del(struct md_list_head * prev, + struct md_list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static __inline__ void md_list_del(struct md_list_head *entry) +{ + md__list_del(entry->prev, entry->next); +} + +static __inline__ int md_list_empty(struct md_list_head *head) +{ + return head->next == head; +} + +#define md_list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/* 023 */ + +static __inline__ signed long md_schedule_timeout(signed long timeout) +{ + current->timeout = jiffies + timeout; + schedule(); + return 0; +} + +/* 024 */ +#define md_need_resched(tsk) (need_resched) + +/* 025 */ +typedef struct { int gcc_is_buggy; } md_spinlock_t; +#define MD_SPIN_LOCK_UNLOCKED (md_spinlock_t) { 0 } + +#define md_spin_lock_irq cli +#define md_spin_unlock_irq sti +#define md_spin_unlock_irqrestore(x,flags) restore_flags(flags) +#define md_spin_lock_irqsave(x,flags) do { save_flags(flags); cli(); } while (0) + +/* END */ + +#else + +#include +#include + +/* 000 */ +#define md__get_free_pages(x,y) __get_free_pages(x,y) + +#ifdef __i386__ +/* 001 */ +extern __inline__ int md_cpu_has_mmx(void) +{ + return boot_cpu_data.x86_capability & X86_FEATURE_MMX; +} +#endif + +/* 002 */ +#define md_clear_page(page) clear_page(page) + +/* 003 */ +#define MD_EXPORT_SYMBOL(x) EXPORT_SYMBOL(x) + +/* 004 */ +#define md_copy_to_user(x,y,z) copy_to_user(x,y,z) + +/* 005 */ +#define md_copy_from_user(x,y,z) copy_from_user(x,y,z) + +/* 006 */ +#define md_put_user put_user + +/* 007 */ +extern inline int md_capable_admin(void) +{ + return capable(CAP_SYS_ADMIN); +} + +/* 008 */ +#define MD_FILE_TO_INODE(file) ((file)->f_dentry->d_inode) + +/* 009 */ +extern inline void md_flush_signals (void) +{ + spin_lock(¤t->sigmask_lock); + flush_signals(current); + spin_unlock(¤t->sigmask_lock); +} + +/* 010 */ +extern inline void md_init_signals (void) +{ + current->exit_signal = SIGCHLD; + siginitsetinv(¤t->blocked, sigmask(SIGKILL)); +} + +/* 011 */ +#define md_signal_pending signal_pending + +/* 012 */ +extern inline void md_set_global_readahead(int * table) +{ + max_readahead[MD_MAJOR] = table; +} + +/* 013 */ +#define md_mdelay(x) mdelay(x) + +/* 014 */ +#define MD_SYS_DOWN SYS_DOWN +#define MD_SYS_HALT SYS_HALT +#define MD_SYS_POWER_OFF SYS_POWER_OFF + +/* 015 */ +#define md_register_reboot_notifier register_reboot_notifier + +/* 016 */ +#define md_test_and_set_bit test_and_set_bit + +/* 017 */ +#define md_test_and_clear_bit test_and_clear_bit + +/* 018 */ +#define md_atomic_read atomic_read +#define md_atomic_set atomic_set + +/* 019 */ +#define md_lock_kernel lock_kernel +#define md_unlock_kernel unlock_kernel + +/* 020 */ + +#include + +#define md__init __init +#define md__initdata __initdata +#define md__initfunc(__arginit) __initfunc(__arginit) + +/* 021 */ + + +/* 022 */ + +#define md_list_head list_head +#define MD_LIST_HEAD(name) LIST_HEAD(name) +#define MD_INIT_LIST_HEAD(ptr) INIT_LIST_HEAD(ptr) +#define md_list_add list_add +#define md_list_del list_del +#define md_list_empty list_empty + +#define md_list_entry(ptr, type, member) list_entry(ptr, type, member) + +/* 023 */ + +#define md_schedule_timeout schedule_timeout + +/* 024 */ +#define md_need_resched(tsk) ((tsk)->need_resched) + +/* 025 */ +#define md_spinlock_t spinlock_t +#define MD_SPIN_LOCK_UNLOCKED SPIN_LOCK_UNLOCKED + +#define md_spin_lock_irq spin_lock_irq +#define md_spin_unlock_irq spin_unlock_irq +#define md_spin_unlock_irqrestore spin_unlock_irqrestore +#define md_spin_lock_irqsave spin_lock_irqsave + +/* END */ + +#endif + +#endif _MD_COMPATIBLE_H + diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h new file mode 100644 index 000000000000..c98b4ef34515 --- /dev/null +++ b/include/linux/raid/md_k.h @@ -0,0 +1,338 @@ +/* + md_k.h : kernel internal structure of the Linux MD driver + Copyright (C) 1996-98 Ingo Molnar, Gadi Oxman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MD_K_H +#define _MD_K_H + +#define MD_RESERVED 0UL +#define LINEAR 1UL +#define STRIPED 2UL +#define RAID0 STRIPED +#define RAID1 3UL +#define RAID5 4UL +#define TRANSLUCENT 5UL +#define LVM 6UL +#define MAX_PERSONALITY 7UL + +extern inline int pers_to_level (int pers) +{ + switch (pers) { + case LVM: return -3; + case TRANSLUCENT: return -2; + case LINEAR: return -1; + case RAID0: return 0; + case RAID1: return 1; + case RAID5: return 5; + } + panic("pers_to_level()"); +} + +extern inline int level_to_pers (int level) +{ + switch (level) { + case -3: return LVM; + case -2: return TRANSLUCENT; + case -1: return LINEAR; + case 0: return RAID0; + case 1: return RAID1; + case 4: + case 5: return RAID5; + } + return MD_RESERVED; +} + +typedef struct mddev_s mddev_t; +typedef struct mdk_rdev_s mdk_rdev_t; + +#if (MINORBITS != 8) +#error MD doesnt handle bigger kdev yet +#endif + +#define MAX_REAL 12 /* Max number of disks per md dev */ +#define MAX_MD_DEVS (1<state & (1 << MD_DISK_FAULTY); +} + +extern inline int disk_active(mdp_disk_t * d) +{ + return d->state & (1 << MD_DISK_ACTIVE); +} + +extern inline int disk_sync(mdp_disk_t * d) +{ + return d->state & (1 << MD_DISK_SYNC); +} + +extern inline int disk_spare(mdp_disk_t * d) +{ + return !disk_sync(d) && !disk_active(d) && !disk_faulty(d); +} + +extern inline int disk_removed(mdp_disk_t * d) +{ + return d->state & (1 << MD_DISK_REMOVED); +} + +extern inline void mark_disk_faulty(mdp_disk_t * d) +{ + d->state |= (1 << MD_DISK_FAULTY); +} + +extern inline void mark_disk_active(mdp_disk_t * d) +{ + d->state |= (1 << MD_DISK_ACTIVE); +} + +extern inline void mark_disk_sync(mdp_disk_t * d) +{ + d->state |= (1 << MD_DISK_SYNC); +} + +extern inline void mark_disk_spare(mdp_disk_t * d) +{ + d->state = 0; +} + +extern inline void mark_disk_removed(mdp_disk_t * d) +{ + d->state = (1 << MD_DISK_FAULTY) | (1 << MD_DISK_REMOVED); +} + +extern inline void mark_disk_inactive(mdp_disk_t * d) +{ + d->state &= ~(1 << MD_DISK_ACTIVE); +} + +extern inline void mark_disk_nonsync(mdp_disk_t * d) +{ + d->state &= ~(1 << MD_DISK_SYNC); +} + +/* + * MD's 'extended' device + */ +struct mdk_rdev_s +{ + struct md_list_head same_set; /* RAID devices within the same set */ + struct md_list_head all; /* all RAID devices */ + struct md_list_head pending; /* undetected RAID devices */ + + kdev_t dev; /* Device number */ + kdev_t old_dev; /* "" when it was last imported */ + int size; /* Device size (in blocks) */ + mddev_t *mddev; /* RAID array if running */ + unsigned long last_events; /* IO event timestamp */ + + struct inode *inode; /* Lock inode */ + struct file filp; /* Lock file */ + + mdp_super_t *sb; + int sb_offset; + + int faulty; /* if faulty do not issue IO requests */ + int desc_nr; /* descriptor index in the superblock */ +}; + + +/* + * disk operations in a working array: + */ +#define DISKOP_SPARE_INACTIVE 0 +#define DISKOP_SPARE_WRITE 1 +#define DISKOP_SPARE_ACTIVE 2 +#define DISKOP_HOT_REMOVE_DISK 3 +#define DISKOP_HOT_ADD_DISK 4 + +typedef struct mdk_personality_s mdk_personality_t; + +struct mddev_s +{ + void *private; + mdk_personality_t *pers; + int __minor; + mdp_super_t *sb; + int nb_dev; + struct md_list_head disks; + int sb_dirty; + mdu_param_t param; + int ro; + unsigned int curr_resync; + unsigned long resync_start; + char *name; + int recovery_running; + struct semaphore reconfig_sem; + struct semaphore recovery_sem; + struct semaphore resync_sem; + struct md_list_head all_mddevs; +}; + +struct mdk_personality_s +{ + char *name; + int (*map)(mddev_t *mddev, kdev_t dev, kdev_t *rdev, + unsigned long *rsector, unsigned long size); + int (*make_request)(mddev_t *mddev, int rw, struct buffer_head * bh); + void (*end_request)(struct buffer_head * bh, int uptodate); + int (*run)(mddev_t *mddev); + int (*stop)(mddev_t *mddev); + int (*status)(char *page, mddev_t *mddev); + int (*ioctl)(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + int max_invalid_dev; + int (*error_handler)(mddev_t *mddev, kdev_t dev); + +/* + * Some personalities (RAID-1, RAID-5) can have disks hot-added and + * hot-removed. Hot removal is different from failure. (failure marks + * a disk inactive, but the disk is still part of the array) The interface + * to such operations is the 'pers->diskop()' function, can be NULL. + * + * the diskop function can change the pointer pointing to the incoming + * descriptor, but must do so very carefully. (currently only + * SPARE_ACTIVE expects such a change) + */ + int (*diskop) (mddev_t *mddev, mdp_disk_t **descriptor, int state); + + int (*stop_resync)(mddev_t *mddev); + int (*restart_resync)(mddev_t *mddev); +}; + + +/* + * Currently we index md_array directly, based on the minor + * number. This will have to change to dynamic allocation + * once we start supporting partitioning of md devices. + */ +extern inline int mdidx (mddev_t * mddev) +{ + return mddev->__minor; +} + +extern inline kdev_t mddev_to_kdev(mddev_t * mddev) +{ + return MKDEV(MD_MAJOR, mdidx(mddev)); +} + +extern mdk_rdev_t * find_rdev(mddev_t * mddev, kdev_t dev); +extern mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr); + +/* + * iterates through some rdev ringlist. It's safe to remove the + * current 'rdev'. Dont touch 'tmp' though. + */ +#define ITERATE_RDEV_GENERIC(head,field,rdev,tmp) \ + \ + for (tmp = head.next; \ + rdev = md_list_entry(tmp, mdk_rdev_t, field), \ + tmp = tmp->next, tmp->prev != &head \ + ; ) +/* + * iterates through the 'same array disks' ringlist + */ +#define ITERATE_RDEV(mddev,rdev,tmp) \ + ITERATE_RDEV_GENERIC((mddev)->disks,same_set,rdev,tmp) + +/* + * Same as above, but assumes that the device has rdev->desc_nr numbered + * from 0 to mddev->nb_dev, and iterates through rdevs in ascending order. + */ +#define ITERATE_RDEV_ORDERED(mddev,rdev,i) \ + for (i = 0; rdev = find_rdev_nr(mddev, i), i < mddev->nb_dev; i++) + + +/* + * Iterates through all 'RAID managed disks' + */ +#define ITERATE_RDEV_ALL(rdev,tmp) \ + ITERATE_RDEV_GENERIC(all_raid_disks,all,rdev,tmp) + +/* + * Iterates through 'pending RAID disks' + */ +#define ITERATE_RDEV_PENDING(rdev,tmp) \ + ITERATE_RDEV_GENERIC(pending_raid_disks,pending,rdev,tmp) + +/* + * iterates through all used mddevs in the system. + */ +#define ITERATE_MDDEV(mddev,tmp) \ + \ + for (tmp = all_mddevs.next; \ + mddev = md_list_entry(tmp, mddev_t, all_mddevs), \ + tmp = tmp->next, tmp->prev != &all_mddevs \ + ; ) + +extern inline int lock_mddev (mddev_t * mddev) +{ + return down_interruptible(&mddev->reconfig_sem); +} + +extern inline void unlock_mddev (mddev_t * mddev) +{ + up(&mddev->reconfig_sem); +} + +#define xchg_values(x,y) do { __typeof__(x) __tmp = x; \ + x = y; y = __tmp; } while (0) + +typedef struct mdk_thread_s { + void (*run) (void *data); + void *data; + struct wait_queue *wqueue; + unsigned long flags; + struct semaphore *sem; + struct task_struct *tsk; + const char *name; +} mdk_thread_t; + +#define THREAD_WAKEUP 0 + +typedef struct dev_name_s { + struct md_list_head list; + kdev_t dev; + char name [MAX_DISKNAME_LEN]; +} dev_name_t; + +#endif _MD_K_H + diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h new file mode 100644 index 000000000000..83f8eb173e97 --- /dev/null +++ b/include/linux/raid/md_p.h @@ -0,0 +1,161 @@ +/* + md_p.h : physical layout of Linux RAID devices + Copyright (C) 1996-98 Ingo Molnar, Gadi Oxman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MD_P_H +#define _MD_P_H + +/* + * RAID superblock. + * + * The RAID superblock maintains some statistics on each RAID configuration. + * Each real device in the RAID set contains it near the end of the device. + * Some of the ideas are copied from the ext2fs implementation. + * + * We currently use 4096 bytes as follows: + * + * word offset function + * + * 0 - 31 Constant generic RAID device information. + * 32 - 63 Generic state information. + * 64 - 127 Personality specific information. + * 128 - 511 12 32-words descriptors of the disks in the raid set. + * 512 - 911 Reserved. + * 912 - 1023 Disk specific descriptor. + */ + +/* + * If x is the real device size in bytes, we return an apparent size of: + * + * y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES + * + * and place the 4kB superblock at offset y. + */ +#define MD_RESERVED_BYTES (64 * 1024) +#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) +#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) + +#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) +#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) + +#define MD_SB_BYTES 4096 +#define MD_SB_WORDS (MD_SB_BYTES / 4) +#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) +#define MD_SB_SECTORS (MD_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define MD_SB_GENERIC_OFFSET 0 +#define MD_SB_PERSONALITY_OFFSET 64 +#define MD_SB_DISKS_OFFSET 128 +#define MD_SB_DESCRIPTOR_OFFSET 992 + +#define MD_SB_GENERIC_CONSTANT_WORDS 32 +#define MD_SB_GENERIC_STATE_WORDS 32 +#define MD_SB_GENERIC_WORDS (MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS) +#define MD_SB_PERSONALITY_WORDS 64 +#define MD_SB_DISKS_WORDS 384 +#define MD_SB_DESCRIPTOR_WORDS 32 +#define MD_SB_RESERVED_WORDS (1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS) +#define MD_SB_EQUAL_WORDS (MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS) +#define MD_SB_DISKS (MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS) + +/* + * Device "operational" state bits + */ +#define MD_DISK_FAULTY 0 /* disk is faulty / operational */ +#define MD_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define MD_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define MD_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +typedef struct mdp_device_descriptor_s { + __u32 number; /* 0 Device number in the entire set */ + __u32 major; /* 1 Device major number */ + __u32 minor; /* 2 Device minor number */ + __u32 raid_disk; /* 3 The role of the device in the raid set */ + __u32 state; /* 4 Operational state */ + __u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5]; +} mdp_disk_t; + +#define MD_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define MD_SB_CLEAN 0 +#define MD_SB_ERRORS 1 + +typedef struct mdp_superblock_s { + /* + * Constant generic information + */ + __u32 md_magic; /* 0 MD identifier */ + __u32 major_version; /* 1 major version to which the set conforms */ + __u32 minor_version; /* 2 minor version ... */ + __u32 patch_version; /* 3 patchlevel version ... */ + __u32 gvalid_words; /* 4 Number of used words in this section */ + __u32 set_uuid0; /* 5 Raid set identifier */ + __u32 ctime; /* 6 Creation time */ + __u32 level; /* 7 Raid personality */ + __u32 size; /* 8 Apparent size of each individual disk */ + __u32 nr_disks; /* 9 total disks in the raid set */ + __u32 raid_disks; /* 10 disks in a fully functional raid set */ + __u32 md_minor; /* 11 preferred MD minor device number */ + __u32 not_persistent; /* 12 does it have a persistent superblock */ + __u32 set_uuid1; /* 13 Raid set identifier #2 */ + __u32 set_uuid2; /* 14 Raid set identifier #3 */ + __u32 set_uuid3; /* 14 Raid set identifier #4 */ + __u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + __u32 utime; /* 0 Superblock update time */ + __u32 state; /* 1 State bits (clean, ...) */ + __u32 active_disks; /* 2 Number of currently active disks */ + __u32 working_disks; /* 3 Number of working disks */ + __u32 failed_disks; /* 4 Number of failed disks */ + __u32 spare_disks; /* 5 Number of spare disks */ + __u32 sb_csum; /* 6 checksum of the whole superblock */ + __u64 events; /* 7 number of superblock updates (64-bit!) */ + __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 9]; + + /* + * Personality information + */ + __u32 layout; /* 0 the array's physical layout */ + __u32 chunk_size; /* 1 chunk size in bytes */ + __u32 root_pv; /* 2 LV root PV */ + __u32 root_block; /* 3 LV root block */ + __u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + mdp_disk_t disks[MD_SB_DISKS]; + + /* + * Reserved + */ + __u32 reserved[MD_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + mdp_disk_t this_disk; + +} mdp_super_t; + +#endif _MD_P_H + diff --git a/include/linux/raid/md_u.h b/include/linux/raid/md_u.h new file mode 100644 index 000000000000..18c029589989 --- /dev/null +++ b/include/linux/raid/md_u.h @@ -0,0 +1,114 @@ +/* + md_u.h : user <=> kernel API between Linux raidtools and RAID drivers + Copyright (C) 1998 Ingo Molnar + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MD_U_H +#define _MD_U_H + +/* ioctls */ + +/* status */ +#define RAID_VERSION _IOR (MD_MAJOR, 0x10, mdu_version_t) +#define GET_ARRAY_INFO _IOR (MD_MAJOR, 0x11, mdu_array_info_t) +#define GET_DISK_INFO _IOR (MD_MAJOR, 0x12, mdu_disk_info_t) +#define PRINT_RAID_DEBUG _IO (MD_MAJOR, 0x13) + +/* configuration */ +#define CLEAR_ARRAY _IO (MD_MAJOR, 0x20) +#define ADD_NEW_DISK _IOW (MD_MAJOR, 0x21, mdu_disk_info_t) +#define HOT_REMOVE_DISK _IO (MD_MAJOR, 0x22) +#define SET_ARRAY_INFO _IOW (MD_MAJOR, 0x23, mdu_array_info_t) +#define SET_DISK_INFO _IO (MD_MAJOR, 0x24) +#define WRITE_RAID_INFO _IO (MD_MAJOR, 0x25) +#define UNPROTECT_ARRAY _IO (MD_MAJOR, 0x26) +#define PROTECT_ARRAY _IO (MD_MAJOR, 0x27) +#define HOT_ADD_DISK _IO (MD_MAJOR, 0x28) + +/* usage */ +#define RUN_ARRAY _IOW (MD_MAJOR, 0x30, mdu_param_t) +#define START_ARRAY _IO (MD_MAJOR, 0x31) +#define STOP_ARRAY _IO (MD_MAJOR, 0x32) +#define STOP_ARRAY_RO _IO (MD_MAJOR, 0x33) +#define RESTART_ARRAY_RW _IO (MD_MAJOR, 0x34) + +typedef struct mdu_version_s { + int major; + int minor; + int patchlevel; +} mdu_version_t; + +typedef struct mdu_array_info_s { + /* + * Generic constant information + */ + int major_version; + int minor_version; + int patch_version; + int ctime; + int level; + int size; + int nr_disks; + int raid_disks; + int md_minor; + int not_persistent; + + /* + * Generic state information + */ + int utime; /* 0 Superblock update time */ + int state; /* 1 State bits (clean, ...) */ + int active_disks; /* 2 Number of currently active disks */ + int working_disks; /* 3 Number of working disks */ + int failed_disks; /* 4 Number of failed disks */ + int spare_disks; /* 5 Number of spare disks */ + + /* + * Personality information + */ + int layout; /* 0 the array's physical layout */ + int chunk_size; /* 1 chunk size in bytes */ + +} mdu_array_info_t; + +typedef struct mdu_disk_info_s { + /* + * configuration/status of one particular disk + */ + int number; + int major; + int minor; + int raid_disk; + int state; + +} mdu_disk_info_t; + +typedef struct mdu_start_info_s { + /* + * configuration/status of one particular disk + */ + int major; + int minor; + int raid_disk; + int state; + +} mdu_start_info_t; + +typedef struct mdu_param_s +{ + int personality; /* 1,2,3,4 */ + int chunk_size; /* in bytes */ + int max_fault; /* unused for now */ +} mdu_param_t; + +#endif _MD_U_H + diff --git a/include/linux/raid/raid0.h b/include/linux/raid/raid0.h new file mode 100644 index 000000000000..3ea74db60c69 --- /dev/null +++ b/include/linux/raid/raid0.h @@ -0,0 +1,33 @@ +#ifndef _RAID0_H +#define _RAID0_H + +#include + +struct strip_zone +{ + int zone_offset; /* Zone offset in md_dev */ + int dev_offset; /* Zone offset in real dev */ + int size; /* Zone size */ + int nb_dev; /* # of devices attached to the zone */ + mdk_rdev_t *dev[MAX_REAL]; /* Devices attached to the zone */ +}; + +struct raid0_hash +{ + struct strip_zone *zone0, *zone1; +}; + +struct raid0_private_data +{ + struct raid0_hash *hash_table; /* Dynamically allocated */ + struct strip_zone *strip_zone; /* This one too */ + int nr_strip_zones; + struct strip_zone *smallest; + int nr_zones; +}; + +typedef struct raid0_private_data raid0_conf_t; + +#define mddev_to_conf(mddev) ((raid0_conf_t *) mddev->private) + +#endif diff --git a/include/linux/raid/raid1.h b/include/linux/raid/raid1.h new file mode 100644 index 000000000000..a50ba2ff7be1 --- /dev/null +++ b/include/linux/raid/raid1.h @@ -0,0 +1,64 @@ +#ifndef _RAID1_H +#define _RAID1_H + +#include + +struct mirror_info { + int number; + int raid_disk; + kdev_t dev; + int next; + int sect_limit; + + /* + * State bits: + */ + int operational; + int write_only; + int spare; + + int used_slot; +}; + +struct raid1_private_data { + mddev_t *mddev; + struct mirror_info mirrors[MD_SB_DISKS]; + int nr_disks; + int raid_disks; + int working_disks; + int last_used; + unsigned long next_sect; + int sect_count; + mdk_thread_t *thread, *resync_thread; + int resync_mirrors; + struct mirror_info *spare; +}; + +typedef struct raid1_private_data raid1_conf_t; + +/* + * this is the only point in the RAID code where we violate + * C type safety. mddev->private is an 'opaque' pointer. + */ +#define mddev_to_conf(mddev) ((raid1_conf_t *) mddev->private) + +/* + * this is our 'private' 'collective' RAID1 buffer head. + * it contains information about what kind of IO operations were started + * for this RAID1 operation, and about their status: + */ + +struct raid1_bh { + atomic_t remaining; /* 'have we finished' count, + * used from IRQ handlers + */ + int cmd; + unsigned long state; + mddev_t *mddev; + struct buffer_head *master_bh; + struct buffer_head *mirror_bh [MD_SB_DISKS]; + struct buffer_head bh_req; + struct buffer_head *next_retry; +}; + +#endif diff --git a/include/linux/raid5.h b/include/linux/raid/raid5.h similarity index 86% rename from include/linux/raid5.h rename to include/linux/raid/raid5.h index 5efd211a2d75..471323cd3607 100644 --- a/include/linux/raid5.h +++ b/include/linux/raid/raid5.h @@ -1,9 +1,8 @@ #ifndef _RAID5_H #define _RAID5_H -#ifdef __KERNEL__ -#include -#include +#include +#include struct disk_info { kdev_t dev; @@ -12,14 +11,16 @@ struct disk_info { int raid_disk; int write_only; int spare; + int used_slot; }; struct stripe_head { + md_spinlock_t stripe_lock; struct stripe_head *hash_next, **hash_pprev; /* hash pointers */ struct stripe_head *free_next; /* pool of free sh's */ struct buffer_head *buffer_pool; /* pool of free buffers */ struct buffer_head *bh_pool; /* pool of free bh's */ - struct raid5_data *raid_conf; + struct raid5_private_data *raid_conf; struct buffer_head *bh_old[MD_SB_DISKS]; /* disk image */ struct buffer_head *bh_new[MD_SB_DISKS]; /* buffers of the MD device (present in buffer cache) */ struct buffer_head *bh_copy[MD_SB_DISKS]; /* copy on write of bh_new (bh_new can change from under us) */ @@ -29,7 +30,7 @@ struct stripe_head { unsigned long sector; /* sector of this row */ int size; /* buffers size */ int pd_idx; /* parity disk index */ - int nr_pending; /* nr of pending cmds */ + atomic_t nr_pending; /* nr of pending cmds */ unsigned long state; /* state flags */ int cmd; /* stripe cmd */ int count; /* nr of waiters */ @@ -67,10 +68,10 @@ struct stripe_head { #define STRIPE_WRITE 1 #define STRIPE_READ 2 -struct raid5_data { +struct raid5_private_data { struct stripe_head **stripe_hashtbl; - struct md_dev *mddev; - struct md_thread *thread, *resync_thread; + mddev_t *mddev; + mdk_thread_t *thread, *resync_thread; struct disk_info disks[MD_SB_DISKS]; struct disk_info *spare; int buffer_size; @@ -97,7 +98,9 @@ struct raid5_data { struct wait_queue *wait_for_stripe; }; -#endif +typedef struct raid5_private_data raid5_conf_t; + +#define mddev_to_conf(mddev) ((raid5_conf_t *) mddev->private) /* * Our supported algorithms diff --git a/include/linux/raid/translucent.h b/include/linux/raid/translucent.h new file mode 100644 index 000000000000..a1326db41778 --- /dev/null +++ b/include/linux/raid/translucent.h @@ -0,0 +1,23 @@ +#ifndef _TRANSLUCENT_H +#define _TRANSLUCENT_H + +#include + +typedef struct dev_info dev_info_t; + +struct dev_info { + kdev_t dev; + int size; +}; + +struct translucent_private_data +{ + dev_info_t disks[MD_SB_DISKS]; +}; + + +typedef struct translucent_private_data translucent_conf_t; + +#define mddev_to_conf(mddev) ((translucent_conf_t *) mddev->private) + +#endif diff --git a/include/linux/raid/xor.h b/include/linux/raid/xor.h new file mode 100644 index 000000000000..e345fe7e4dc7 --- /dev/null +++ b/include/linux/raid/xor.h @@ -0,0 +1,12 @@ +#ifndef _XOR_H +#define _XOR_H + +#include + +#define MAX_XOR_BLOCKS 5 + +extern void calibrate_xor_block(void); +extern void (*xor_block)(unsigned int count, + struct buffer_head **bh_ptr); + +#endif diff --git a/include/linux/raid0.h b/include/linux/raid0.h deleted file mode 100644 index e1ae51c028c5..000000000000 --- a/include/linux/raid0.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _RAID0_H -#define _RAID0_H - -struct strip_zone -{ - int zone_offset; /* Zone offset in md_dev */ - int dev_offset; /* Zone offset in real dev */ - int size; /* Zone size */ - int nb_dev; /* Number of devices attached to the zone */ - struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */ -}; - -struct raid0_hash -{ - struct strip_zone *zone0, *zone1; -}; - -struct raid0_data -{ - struct raid0_hash *hash_table; /* Dynamically allocated */ - struct strip_zone *strip_zone; /* This one too */ - int nr_strip_zones; - struct strip_zone *smallest; - int nr_zones; -}; - -#endif diff --git a/include/linux/raid1.h b/include/linux/raid1.h deleted file mode 100644 index 4b031e6825cf..000000000000 --- a/include/linux/raid1.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _RAID1_H -#define _RAID1_H - -#include - -struct mirror_info { - int number; - int raid_disk; - kdev_t dev; - int next; - int sect_limit; - - /* - * State bits: - */ - int operational; - int write_only; - int spare; -}; - -struct raid1_data { - struct md_dev *mddev; - struct mirror_info mirrors[MD_SB_DISKS]; /* RAID1 devices, 2 to MD_SB_DISKS */ - int raid_disks; - int working_disks; /* Number of working disks */ - int last_used; - unsigned long next_sect; - int sect_count; - int resync_running; -}; - -/* - * this is our 'private' 'collective' RAID1 buffer head. - * it contains information about what kind of IO operations were started - * for this RAID5 operation, and about their status: - */ - -struct raid1_bh { - unsigned int remaining; - int cmd; - unsigned long state; - struct md_dev *mddev; - struct buffer_head *master_bh; - struct buffer_head *mirror_bh [MD_SB_DISKS]; - struct buffer_head bh_req; - struct buffer_head *next_retry; -}; - -#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 53a180e305cf..5ef85c63c6ae 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -123,23 +123,39 @@ extern void trap_init(void); extern signed long FASTCALL(schedule_timeout(signed long timeout)); asmlinkage void schedule(void); +/* + * The default fd array needs to be at least BITS_PER_LONG, + * as this is the granularity returned by copy_fdset(). + */ +#define NR_OPEN_DEFAULT BITS_PER_LONG + /* * Open file table structure */ struct files_struct { atomic_t count; int max_fds; + int max_fdset; + int next_fd; struct file ** fd; /* current fd array */ - fd_set close_on_exec; - fd_set open_fds; + fd_set *close_on_exec; + fd_set *open_fds; + fd_set close_on_exec_init; + fd_set open_fds_init; + struct file * fd_array[NR_OPEN_DEFAULT]; }; #define INIT_FILES { \ ATOMIC_INIT(1), \ - NR_OPEN, \ - &init_fd_array[0], \ + NR_OPEN_DEFAULT, \ + __FD_SETSIZE, \ + 0, \ + &init_files.fd_array[0], \ + &init_files.close_on_exec_init, \ + &init_files.open_fds_init, \ + { { 0, } }, \ { { 0, } }, \ - { { 0, } } \ + { NULL, } \ } struct fs_struct { @@ -618,6 +634,45 @@ extern void mmput(struct mm_struct *); /* Remove the current tasks stale references to the old mm_struct */ extern void mm_release(void); +/* + * Routines for handling the fd arrays + */ +extern struct file ** alloc_fd_array(int); +extern int expand_fd_array(struct files_struct *, int nr); +extern void free_fd_array(struct file **, int); + +extern fd_set *alloc_fdset(int); +extern int expand_fdset(struct files_struct *, int nr); +extern void free_fdset(fd_set *, int); + +/* Expand files. Return <0 on error; 0 nothing done; 1 files expanded, + * we may have blocked. */ +static inline int expand_files(struct files_struct *files, int nr) +{ + int err, expand = 0; +#ifdef FDSET_DEBUG + printk (KERN_ERR __FUNCTION__ " %d: nr = %d\n", current->pid, nr); +#endif + + if (nr >= files->max_fdset) { + expand = 1; + if ((err = expand_fdset(files, nr))) + goto out; + } + if (nr >= files->max_fds) { + expand = 1; + if ((err = expand_fd_array(files, nr))) + goto out; + } + err = expand; + out: +#ifdef FDSET_DEBUG + if (err) + printk (KERN_ERR __FUNCTION__ " %d: return %d\n", current->pid, err); +#endif + return err; +} + extern int copy_thread(int, unsigned long, unsigned long, struct task_struct *, struct pt_regs *); extern void flush_thread(void); extern void exit_thread(void); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index aba1498a73de..b7359b45b780 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -425,7 +425,8 @@ enum /* CTL_DEV names: */ enum { DEV_CDROM=1, - DEV_HWMON=2 + DEV_HWMON=2, + DEV_MD=3 }; /* /proc/sys/dev/cdrom */ @@ -433,6 +434,11 @@ enum { DEV_CDROM_INFO=1 }; +/* /proc/sys/dev/md */ +enum { + DEV_MD_SPEED_LIMIT=1 +}; + #ifdef __KERNEL__ extern asmlinkage int sys_sysctl(struct __sysctl_args *); diff --git a/include/linux/tty.h b/include/linux/tty.h index d959c927e626..55aabe43dfea 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -346,6 +346,7 @@ extern int rs_init(void); extern int lp_init(void); extern int pty_init(void); extern int tty_init(void); +extern int ip2_init(void); extern int pcxe_init(void); extern int pc_init(void); extern int vcs_init(void); diff --git a/include/linux/video_decoder.h b/include/linux/video_decoder.h new file mode 100644 index 000000000000..1302c7f44ec5 --- /dev/null +++ b/include/linux/video_decoder.h @@ -0,0 +1,37 @@ +#ifndef _LINUX_VIDEO_DECODER_H +#define _LINUX_VIDEO_DECODER_H + +struct video_decoder_capability { /* this name is too long */ + __u32 flags; +#define VIDEO_DECODER_PAL 1 /* can decode PAL signal */ +#define VIDEO_DECODER_NTSC 2 /* can decode NTSC */ +#define VIDEO_DECODER_SECAM 4 /* can decode SECAM */ +#define VIDEO_DECODER_AUTO 8 /* can autosense norm */ +#define VIDEO_DECODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */ + int inputs; /* number of inputs */ + int outputs; /* number of outputs */ +}; + +/* +DECODER_GET_STATUS returns the following flags. The only one you need is +DECODER_STATUS_GOOD, the others are just nice things to know. +*/ +#define DECODER_STATUS_GOOD 1 /* receiving acceptable input */ +#define DECODER_STATUS_COLOR 2 /* receiving color information */ +#define DECODER_STATUS_PAL 4 /* auto detected */ +#define DECODER_STATUS_NTSC 8 /* auto detected */ +#define DECODER_STATUS_SECAM 16 /* auto detected */ + + +#define DECODER_GET_CAPABILITIES _IOR('d', 1, struct video_decoder_capability) +#define DECODER_GET_STATUS _IOR('d', 2, int) +#define DECODER_SET_NORM _IOW('d', 3, int) +#define DECODER_SET_INPUT _IOW('d', 4, int) /* 0 <= input < #inputs */ +#define DECODER_SET_OUTPUT _IOW('d', 5, int) /* 0 <= output < #outputs */ +#define DECODER_ENABLE_OUTPUT _IOW('d', 6, int) /* boolean output enable control */ +#define DECODER_SET_PICTURE _IOW('d', 7, struct video_picture) + +#define DECODER_DUMP _IO('d', 192) /* debug hook */ + + +#endif diff --git a/include/linux/video_encoder.h b/include/linux/video_encoder.h new file mode 100644 index 000000000000..4b0e6907a7b4 --- /dev/null +++ b/include/linux/video_encoder.h @@ -0,0 +1,21 @@ +#ifndef _LINUX_VIDEO_ENCODER_H +#define _LINUX_VIDEO_ENCODER_H + +struct video_encoder_capability { /* this name is too long */ + __u32 flags; +#define VIDEO_ENCODER_PAL 1 /* can encode PAL signal */ +#define VIDEO_ENCODER_NTSC 2 /* can encode NTSC */ +#define VIDEO_ENCODER_SECAM 4 /* can encode SECAM */ +#define VIDEO_ENCODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */ + int inputs; /* number of inputs */ + int outputs; /* number of outputs */ +}; + +#define ENCODER_GET_CAPABILITIES _IOR('e', 1, struct video_encoder_capability) +#define ENCODER_SET_NORM _IOW('e', 2, int) +#define ENCODER_SET_INPUT _IOW('e', 3, int) /* 0 <= input < #inputs */ +#define ENCODER_SET_OUTPUT _IOW('e', 4, int) /* 0 <= output < #outputs */ +#define ENCODER_ENABLE_OUTPUT _IOW('e', 5, int) /* boolean output enable control */ + + +#endif diff --git a/include/net/ip_masq.h b/include/net/ip_masq.h index 1e050359a25c..e3fcf6523112 100644 --- a/include/net/ip_masq.h +++ b/include/net/ip_masq.h @@ -14,6 +14,10 @@ #include #endif /* __KERNEL__ */ +#ifdef CONFIG_IP_MASQUERADE_VS +struct ip_vs_dest; +#endif + /* * This define affects the number of ports that can be handled * by each of the protocol helper modules. @@ -40,10 +44,6 @@ #define IP_MASQ_MOD_CTL 0x00 #define IP_MASQ_USER_CTL 0x01 -#ifdef __KERNEL__ - -#define IP_MASQ_TAB_SIZE 256 - #define IP_MASQ_F_NO_DADDR 0x0001 /* no daddr yet */ #define IP_MASQ_F_NO_DPORT 0x0002 /* no dport set yet */ #define IP_MASQ_F_NO_SADDR 0x0004 /* no sport set yet */ @@ -60,6 +60,22 @@ #define IP_MASQ_F_USER 0x2000 /* from uspace */ #define IP_MASQ_F_SIMPLE_HASH 0x8000 /* prevent s+d and m+d hashing */ +#ifdef CONFIG_IP_MASQUERADE_VS +#define IP_MASQ_F_VS 0x00010000 /* virtual server releated */ +#define IP_MASQ_F_VS_NO_OUTPUT 0x00020000 /* output packets avoid masq */ +#define IP_MASQ_F_VS_FIN 0x00040000 /* fin detected */ +#define IP_MASQ_F_VS_FWD_MASK 0x00700000 /* mask for the fdw method */ +#define IP_MASQ_F_VS_LOCALNODE 0x00100000 /* local node destination */ +#define IP_MASQ_F_VS_TUNNEL 0x00200000 /* packets will be tunneled */ +#define IP_MASQ_F_VS_DROUTE 0x00400000 /* direct routing */ + /* masquerading otherwise */ +#define IP_MASQ_VS_FWD(ms) (ms->flags & IP_MASQ_F_VS_FWD_MASK) +#endif /* CONFIG_IP_MASQUERADE_VS */ + +#ifdef __KERNEL__ + +#define IP_MASQ_TAB_SIZE 256 + /* * Delta seq. info structure * Each MASQ struct has 2 (output AND input seq. changes). @@ -91,6 +107,9 @@ struct ip_masq { unsigned timeout; /* timeout */ unsigned state; /* state info */ struct ip_masq_timeout_table *timeout_table; +#ifdef CONFIG_IP_MASQUERADE_VS + struct ip_vs_dest *dest; /* real server & service */ +#endif /* CONFIG_IP_MASQUERADE_VS */ }; /* diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h new file mode 100644 index 000000000000..af47b53217c1 --- /dev/null +++ b/include/net/ip_vs.h @@ -0,0 +1,154 @@ +/* + * Virtual server support for IP masquerading + * data structure and funcationality definitions + */ + +#ifndef _IP_VS_H +#define _IP_VS_H + +#include + +#ifdef CONFIG_IP_VS_DEBUG +#define IP_VS_DBG(msg...) printk(KERN_DEBUG "IP_VS: " ## msg ) +#else /* NO DEBUGGING at ALL */ +#define IP_VS_DBG(msg...) +#endif + +#define IP_VS_ERR(msg...) printk(KERN_ERR "IP_VS: " ## msg ) +#define IP_VS_INFO(msg...) printk(KERN_INFO "IP_VS: " ## msg ) +#define IP_VS_WARNING(msg...) \ + printk(KERN_WARNING "IP_VS: " ## msg) + +struct ip_vs_dest; +struct ip_vs_scheduler; + +/* + * The information about the virtual service offered to the net + * and the forwarding entries + */ +struct ip_vs_service { + struct ip_vs_service *next; + __u32 addr; /* IP address for virtual service */ + __u16 port; /* port number for the service */ + __u16 protocol; /* which protocol (TCP/UDP) */ + struct ip_vs_dest *destinations; /* real server list */ + struct ip_vs_scheduler *scheduler; /* bound scheduler object */ + void *sched_data; /* scheduler application data */ +}; + + +/* + * The real server destination forwarding entry + * with ip address, port + */ +struct ip_vs_dest { + struct ip_vs_dest *next; + __u32 addr; /* IP address of real server */ + __u16 port; /* port number of the service */ + unsigned masq_flags; /* flags to copy to masq */ + atomic_t connections; + atomic_t refcnt; + int weight; + struct ip_vs_service *service; /* service might be NULL */ +}; + + +/* + * The scheduler object + */ +struct ip_vs_scheduler { + struct ip_vs_scheduler *next; + char *name; + atomic_t refcnt; + + /* scheduler initializing service */ + int (*init_service)(struct ip_vs_service *svc); + /* scheduling service finish */ + int (*done_service)(struct ip_vs_service *svc); + + /* scheduling and creating a masquerading entry */ + struct ip_masq* (*schedule)(struct ip_vs_service *svc, + struct iphdr *iph); +}; + +/* + * IP Virtual Server hash table + */ +#define IP_VS_TAB_BITS CONFIG_IP_MASQUERADE_VS_TAB_BITS +#define IP_VS_TAB_SIZE (1 << IP_VS_TAB_BITS) +extern struct list_head ip_vs_table[IP_VS_TAB_SIZE]; + +/* + * Hash and unhash functions + */ +extern int ip_vs_hash(struct ip_masq *ms); +extern int ip_vs_unhash(struct ip_masq *ms); + +/* + * registering/unregistering scheduler functions + */ +extern int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); +extern int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); + +/* + * Lookup functions for the hash table + */ +extern struct ip_masq * ip_vs_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port); +extern struct ip_masq * ip_vs_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port); + +/* + * Creating a masquerading entry for IPVS + */ +extern struct ip_masq *ip_masq_new_vs(int proto, __u32 maddr, __u16 mport, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned flags); + +/* + * IPVS data and functions + */ +extern rwlock_t __ip_vs_lock; + +extern int ip_vs_ctl(int optname, struct ip_masq_ctl *mctl, int optlen); + +extern void ip_vs_fin_masq(struct ip_masq *ms); +extern void ip_vs_bind_masq(struct ip_masq *ms, struct ip_vs_dest *dest); +extern void ip_vs_unbind_masq(struct ip_masq *ms); + +struct ip_vs_service *ip_vs_lookup_service(__u32 vaddr, __u16 vport, + __u16 protocol); +extern struct ip_masq *ip_vs_schedule(__u32 vaddr, __u16 vport, + __u16 protocol, + struct iphdr *iph); + +extern int ip_vs_tunnel_xmit(struct sk_buff **skb_p, __u32 daddr); + +/* + * init function + */ +extern int ip_vs_init(void); + +/* + * init function prototypes for scheduling modules + * these function will be called when they are built in kernel + */ +extern int ip_vs_rr_init(void); +extern int ip_vs_wrr_init(void); +extern int ip_vs_wlc_init(void); +extern int ip_vs_pcc_init(void); + + +/* + * ip_vs_fwd_tag returns the forwarding tag of the masq + */ +static __inline__ char ip_vs_fwd_tag(struct ip_masq *ms) +{ + char fwd = 'M'; + + switch (IP_MASQ_VS_FWD(ms)) { + case IP_MASQ_F_VS_LOCALNODE: fwd = 'L'; break; + case IP_MASQ_F_VS_TUNNEL: fwd = 'T'; break; + case IP_MASQ_F_VS_DROUTE: fwd = 'R'; break; + } + return fwd; +} + + +#endif /* _IP_VS_H */ diff --git a/init/main.c b/init/main.c index f316a4746e1e..a71187f48762 100644 --- a/init/main.c +++ b/init/main.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -470,7 +471,7 @@ static struct dev_name_struct { #ifdef CONFIG_BLK_DEV_FD { "fd", 0x0200 }, #endif -#ifdef CONFIG_MD_BOOT +#if CONFIG_MD_BOOT || CONFIG_AUTODETECT_RAID { "md", 0x0900 }, #endif #ifdef CONFIG_BLK_DEV_XD @@ -882,6 +883,9 @@ static struct kernel_param cooked_params[] __initdata = { #ifdef CONFIG_MD_BOOT { "md=", md_setup}, #endif +#if CONFIG_BLK_DEV_MD + { "raid=", raid_setup}, +#endif #ifdef CONFIG_ADBMOUSE { "adb_buttons=", adb_mouse_setup }, #endif @@ -1367,6 +1371,9 @@ static void __init do_basic_setup(void) while (pid != wait(&i)); if (MAJOR(real_root_dev) != RAMDISK_MAJOR || MINOR(real_root_dev) != 0) { +#ifdef CONFIG_BLK_DEV_MD + autodetect_raid(); +#endif error = change_root(real_root_dev,"/initrd"); if (error) printk(KERN_ERR "Change root to /initrd: " diff --git a/ipc/shm.c b/ipc/shm.c index 6aaeb00848e1..3684d92858f7 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -401,8 +401,9 @@ static int shm_map (struct vm_area_struct *shmd) /* add new mapping */ tmp = shmd->vm_end - shmd->vm_start; - if((current->mm->total_vm << PAGE_SHIFT) + tmp - > (unsigned long) current->rlim[RLIMIT_AS].rlim_cur) + if ((current->rlim[RLIMIT_AS].rlim_cur < RLIM_INFINITY) && + ((current->mm->total_vm << PAGE_SHIFT) + tmp + > current->rlim[RLIMIT_AS].rlim_cur)) return -ENOMEM; current->mm->total_vm += tmp >> PAGE_SHIFT; insert_vm_struct(current->mm, shmd); diff --git a/kernel/exit.c b/kernel/exit.c index 2a22530243d6..4ec7403d8f69 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef CONFIG_BSD_PROCESS_ACCT #include #endif @@ -22,7 +23,7 @@ extern struct task_struct *child_reaper; int getrusage(struct task_struct *, int, struct rusage *); -static void release(struct task_struct * p) +void release(struct task_struct * p) { if (p != current) { #ifdef __SMP__ @@ -159,11 +160,11 @@ static inline void close_files(struct files_struct * files) j = 0; for (;;) { - unsigned long set = files->open_fds.fds_bits[j]; + unsigned long set; i = j * __NFDBITS; - j++; - if (i >= files->max_fds) + if (i >= files->max_fdset || i >= files->max_fds) break; + set = files->open_fds->fds_bits[j++]; while (set) { if (set & 1) { struct file * file = files->fd[i]; @@ -189,12 +190,14 @@ static inline void __exit_files(struct task_struct *tsk) if (atomic_dec_and_test(&files->count)) { close_files(files); /* - * Free the fd array as appropriate ... + * Free the fd and fdset arrays if we expanded them. */ - if (NR_OPEN * sizeof(struct file *) == PAGE_SIZE) - free_page((unsigned long) files->fd); - else - kfree(files->fd); + if (files->fd != &files->fd_array[0]) + free_fd_array(files->fd, files->max_fds); + if (files->max_fdset > __FD_SETSIZE) { + free_fdset(files->open_fds, files->max_fdset); + free_fdset(files->close_on_exec, files->max_fdset); + } kmem_cache_free(files_cachep, files); } } diff --git a/kernel/fork.c b/kernel/fork.c index e472d89f14f4..99b753d7bde8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -362,6 +362,9 @@ static inline int copy_mm(int nr, unsigned long clone_flags, struct task_struct if (clone_flags & CLONE_VM) { mmget(current->mm); + tsk->min_flt = tsk->maj_flt = 0; + tsk->cmin_flt = tsk->cmaj_flt = 0; + tsk->nswap = tsk->cnswap = 0; /* * Set up the LDT descriptor for the clone task. */ @@ -414,32 +417,11 @@ static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) return 0; } -/* - * Copy a fd_set and compute the maximum fd it contains. - */ -static inline int __copy_fdset(unsigned long *d, unsigned long *src) -{ - int i; - unsigned long *p = src; - unsigned long *max = src; - - for (i = __FDSET_LONGS; i; --i) { - if ((*d++ = *p++) != 0) - max = p; - } - return (max - src)*sizeof(long)*8; -} - -static inline int copy_fdset(fd_set *dst, fd_set *src) -{ - return __copy_fdset(dst->fds_bits, src->fds_bits); -} - static int copy_files(unsigned long clone_flags, struct task_struct * tsk) { struct files_struct *oldf, *newf; struct file **old_fds, **new_fds; - int size, i, error = 0; + int nfds, size, i, error = 0; /* * A background process may not have any files ... @@ -459,25 +441,74 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) if (!newf) goto out; - /* - * Allocate the fd array, using get_free_page() if possible. - * Eventually we want to make the array size variable ... - */ - size = NR_OPEN * sizeof(struct file *); - if (size == PAGE_SIZE) - new_fds = (struct file **) __get_free_page(GFP_KERNEL); - else - new_fds = (struct file **) kmalloc(size, GFP_KERNEL); - if (!new_fds) - goto out_release; + size = oldf->max_fdset; + nfds = NR_OPEN_DEFAULT; +#ifdef FDSET_DEBUG + printk (KERN_ERR __FUNCTION__ " size = %d/%d\n", + oldf->max_fds, oldf->max_fdset); +#endif atomic_set(&newf->count, 1); - newf->max_fds = NR_OPEN; - newf->fd = new_fds; - newf->close_on_exec = oldf->close_on_exec; - i = copy_fdset(&newf->open_fds, &oldf->open_fds); + newf->next_fd = 0; + newf->max_fds = NR_OPEN_DEFAULT; + newf->max_fdset = __FD_SETSIZE; + newf->close_on_exec = &newf->close_on_exec_init; + newf->open_fds = &newf->open_fds_init; + newf->fd = &newf->fd_array[0]; + + /* Even if the old fdset gets grown here, we'll only copy "size" fds */ + if (size > __FD_SETSIZE) { + newf->max_fdset = 0; + error = expand_fdset(newf, size); + if (error) + goto out_release; + } + memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, size/8); + memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, size/8); + if (newf->max_fdset > size) { + int left = (newf->max_fdset-size)/8; + int start = size / (8 * sizeof(unsigned long)); + + memset(&newf->open_fds->fds_bits[start], 0, left); + memset(&newf->close_on_exec->fds_bits[start], 0, left); + } + + /* Find the last open fd */ + for (i = size/(8*sizeof(long)); i > 0; ) { + if (newf->open_fds->fds_bits[--i]) + break; + } + i = (i+1) * 8 * sizeof(long); + +#ifdef FDSET_DEBUG + printk (KERN_ERR __FUNCTION__ " first-free = %d/%d\n", i, size); +#endif + + /* Do a sanity check ... */ + if (i > oldf->max_fds) + printk(KERN_ERR + "copy_files: pid %d, open files %d exceeds max %d!\n", + current->pid, i, oldf->max_fds); + + /* + * Check whether we need to allocate a larger fd array. + * Note: we're not a clone task, so the open count won't + * change. + */ + if (i > NR_OPEN_DEFAULT) { + newf->max_fds = 0; + error = expand_fd_array(newf, i); + if (error) + goto out_release; + nfds = newf->max_fds; + } + + /* compute the remainder to be cleared */ + size = (nfds - i) * sizeof(struct file *); old_fds = oldf->fd; + new_fds = newf->fd; + for (; i != 0; i--) { struct file *f = *old_fds++; *new_fds = f; @@ -486,14 +517,20 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) new_fds++; } /* This is long word aligned thus could use a optimized version */ - memset(new_fds, 0, (char *)newf->fd + size - (char *)new_fds); + memset(new_fds, 0, size); tsk->files = newf; error = 0; out: +#ifdef FDSET_DEBUG + if (error) + printk (KERN_ERR "copy_files: return %d\n", error); +#endif return error; out_release: + free_fdset (newf->close_on_exec, newf->max_fdset); + free_fdset (newf->open_fds, newf->max_fdset); kmem_cache_free(files_cachep, newf); goto out; } @@ -553,13 +590,14 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) if (p->user) { if (atomic_read(&p->user->count) >= p->rlim[RLIMIT_NPROC].rlim_cur) goto bad_fork_free; + atomic_inc(&p->user->count); } { struct task_struct **tslot; tslot = find_empty_process(); if (!tslot) - goto bad_fork_free; + goto bad_fork_cleanup_count; p->tarray_ptr = tslot; *tslot = p; nr = tslot - &task[0]; @@ -632,7 +670,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) goto bad_fork_cleanup_sighand; retval = copy_thread(nr, clone_flags, usp, p, regs); if (retval) - goto bad_fork_cleanup_sighand; + goto bad_fork_cleanup_mm; p->semundo = NULL; /* ok, now we should be set up.. */ @@ -663,8 +701,6 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) write_unlock_irq(&tasklist_lock); nr_tasks++; - if (p->user) - atomic_inc(&p->user->count); p->next_run = NULL; p->prev_run = NULL; @@ -679,6 +715,9 @@ fork_out: down(&sem); return retval; +bad_fork_cleanup_mm: + mmput(p->mm); + p->mm = NULL; bad_fork_cleanup_sighand: exit_sighand(p); bad_fork_cleanup_fs: @@ -692,6 +731,9 @@ bad_fork_cleanup: __MOD_DEC_USE_COUNT(p->binfmt->module); add_free_taskslot(p->tarray_ptr); +bad_fork_cleanup_count: + if (p->user) + free_uid(p); bad_fork_free: free_task_struct(p); goto bad_fork; diff --git a/mm/mlock.c b/mm/mlock.c index 1c90350954e1..ff911ed687cc 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -129,7 +129,7 @@ static int do_mlock(unsigned long start, size_t len, int on) struct vm_area_struct * vma, * next; int error; - if (!capable(CAP_IPC_LOCK)) + if (on && !capable(CAP_IPC_LOCK)) return -EPERM; len = (len + ~PAGE_MASK) & PAGE_MASK; end = start + len; @@ -186,11 +186,13 @@ asmlinkage int sys_mlock(unsigned long start, size_t len) locked += current->mm->locked_vm; lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur; - lock_limit >>= PAGE_SHIFT; + if (lock_limit < RLIM_INFINITY) { + lock_limit >>= PAGE_SHIFT; - /* check against resource limits */ - if (locked > lock_limit) - goto out; + /* check against resource limits */ + if (locked > lock_limit) + goto out; + } /* we may lock at most half of physical memory... */ /* (this check is pretty bogus, but doesn't hurt) */ @@ -257,12 +259,14 @@ asmlinkage int sys_mlockall(int flags) if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE))) goto out; + ret = -ENOMEM; lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur; - lock_limit >>= PAGE_SHIFT; + if (lock_limit < RLIM_INFINITY) { + lock_limit >>= PAGE_SHIFT; - ret = -ENOMEM; - if (current->mm->total_vm > lock_limit) - goto out; + if (current->mm->total_vm > lock_limit) + goto out; + } /* we may lock at most half of physical memory... */ /* (this check is pretty bogus, but doesn't hurt) */ diff --git a/mm/mmap.c b/mm/mmap.c index 55e773bf26ef..8106eae571b6 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -197,7 +197,8 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, if (mm->def_flags & VM_LOCKED) { unsigned long locked = mm->locked_vm << PAGE_SHIFT; locked += len; - if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur) + if ((current->rlim[RLIMIT_MEMLOCK].rlim_cur < RLIM_INFINITY) && + (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)) return -EAGAIN; } @@ -288,8 +289,9 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, goto free_vma; /* Check against address space limit. */ - if ((mm->total_vm << PAGE_SHIFT) + len - > current->rlim[RLIMIT_AS].rlim_cur) + if ((current->rlim[RLIMIT_AS].rlim_cur < RLIM_INFINITY) && + ((mm->total_vm << PAGE_SHIFT) + len + > current->rlim[RLIMIT_AS].rlim_cur)) goto free_vma; /* Private writable mapping? Check memory availability.. */ diff --git a/mm/mremap.c b/mm/mremap.c index 3e1bfb1a2b81..206010db8482 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -196,12 +196,14 @@ asmlinkage unsigned long sys_mremap(unsigned long addr, unsigned long locked = current->mm->locked_vm << PAGE_SHIFT; locked += new_len - old_len; ret = -EAGAIN; - if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur) + if ((current->rlim[RLIMIT_MEMLOCK].rlim_cur < RLIM_INFINITY) && + (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)) goto out; } ret = -ENOMEM; - if ((current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len) - > current->rlim[RLIMIT_AS].rlim_cur) + if ((current->rlim[RLIMIT_AS].rlim_cur < RLIM_INFINITY) && + ((current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len) + > current->rlim[RLIMIT_AS].rlim_cur)) goto out; /* Private writable mapping? Check memory availability.. */ if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE && diff --git a/net/802/Makefile b/net/802/Makefile index cdfafc34349a..66f8af658d64 100644 --- a/net/802/Makefile +++ b/net/802/Makefile @@ -26,6 +26,10 @@ O_OBJS += tr.o SNAP=y endif +ifdef CONFIG_NET_FC +O_OBJS += fc.o +endif + ifdef CONFIG_FDDI O_OBJS += fddi.o endif diff --git a/net/802/fc.c b/net/802/fc.c new file mode 100644 index 000000000000..eb0a0203f4d3 --- /dev/null +++ b/net/802/fc.c @@ -0,0 +1,96 @@ +/* + * NET3: Fibre Channel device handling subroutines + * + * 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. + * + * Vineet Abraham + * v 1.0 03/22/99 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Put the headers on a Fibre Channel packet. + */ + +int fc_header(struct sk_buff *skb, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len) +{ + struct fch_hdr *fch; + int hdr_len; + + /* + * Add the 802.2 SNAP header if IP as the IPv4 code calls + * dev->hard_header directly. + */ + if (type == ETH_P_IP || type == ETH_P_ARP) + { + struct fcllc *fcllc=(struct fcllc *)(fch+1); + + hdr_len = sizeof(struct fch_hdr) + sizeof(struct fcllc); + fch = (struct fch_hdr *)skb_push(skb, hdr_len); + fcllc = (struct fcllc *)(fch+1); + fcllc->dsap = fcllc->ssap = EXTENDED_SAP; + fcllc->llc = UI_CMD; + fcllc->protid[0] = fcllc->protid[1] = fcllc->protid[2] = 0x00; + fcllc->ethertype = htons(type); + } + else + { + hdr_len = sizeof(struct fch_hdr); + fch = (struct fch_hdr *)skb_push(skb, hdr_len); + } + + if(saddr) + memcpy(fch->saddr,saddr,dev->addr_len); + else + memcpy(fch->saddr,dev->dev_addr,dev->addr_len); + + if(daddr) + { + memcpy(fch->daddr,daddr,dev->addr_len); + return(hdr_len); + } + return -hdr_len; +} + +/* + * A neighbour discovery of some species (eg arp) has completed. We + * can now send the packet. + */ + +int fc_rebuild_header(struct sk_buff *skb) +{ + struct fch_hdr *fch=(struct fch_hdr *)skb->data; + struct fcllc *fcllc=(struct fcllc *)(skb->data+sizeof(struct fch_hdr)); + if(fcllc->ethertype != htons(ETH_P_IP)) { + printk("fc_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(fcllc->ethertype)); + return 0; + } + + return arp_find(fch->daddr, skb); +} + diff --git a/net/ipv4/Config.in b/net/ipv4/Config.in index 29786da5e96b..1f4b54dce048 100644 --- a/net/ipv4/Config.in +++ b/net/ipv4/Config.in @@ -52,6 +52,14 @@ if [ "$CONFIG_IP_FIREWALL" = "y" ]; then tristate 'IP: ipportfw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPPORTFW tristate 'IP: ip fwmark masq-forwarding support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_MFW fi + bool 'IP: masquerading virtual server support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_VS + if [ "$CONFIG_IP_MASQUERADE_VS" = "y" ]; then + int 'IP masquerading VS table size (the Nth power of 2)' CONFIG_IP_MASQUERADE_VS_TAB_BITS 12 + tristate 'IPVS: round-robin scheduling' CONFIG_IP_MASQUERADE_VS_RR + tristate 'IPVS: weighted round-robin scheduling' CONFIG_IP_MASQUERADE_VS_WRR + tristate 'IPVS: weighted least-connection scheduling' CONFIG_IP_MASQUERADE_VS_WLC + tristate 'IPVS: persistent client connection scheduling' CONFIG_IP_MASQUERADE_VS_PCC + fi fi fi fi diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 8ab280deba5c..45296ae25abb 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -91,6 +91,42 @@ ifeq ($(CONFIG_IP_MASQUERADE_MOD),y) endif +ifeq ($(CONFIG_IP_MASQUERADE_VS),y) + IPV4X_OBJS += ip_vs.o + + ifeq ($(CONFIG_IP_MASQUERADE_VS_RR),y) + IPV4_OBJS += ip_vs_rr.o + else + ifeq ($(CONFIG_IP_MASQUERADE_VS_RR),m) + M_OBJS += ip_vs_rr.o + endif + endif + + ifeq ($(CONFIG_IP_MASQUERADE_VS_WRR),y) + IPV4_OBJS += ip_vs_wrr.o + else + ifeq ($(CONFIG_IP_MASQUERADE_VS_WRR),m) + M_OBJS += ip_vs_wrr.o + endif + endif + + ifeq ($(CONFIG_IP_MASQUERADE_VS_WLC),y) + IPV4_OBJS += ip_vs_wlc.o + else + ifeq ($(CONFIG_IP_MASQUERADE_VS_WLC),m) + M_OBJS += ip_vs_wlc.o + endif + endif + + ifeq ($(CONFIG_IP_MASQUERADE_VS_PCC),y) + IPV4_OBJS += ip_vs_pcc.o + else + ifeq ($(CONFIG_IP_MASQUERADE_VS_PCC),m) + M_OBJS += ip_vs_pcc.o + endif + endif +endif + M_OBJS += ip_masq_user.o M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o M_OBJS += ip_masq_vdolive.o ip_masq_cuseeme.o diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 27d2f80214b6..29a127ccfddd 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -65,6 +65,8 @@ * clean up the APFDDI & gen. FDDI bits. * Alexey Kuznetsov: new arp state machine; * now it is in net/core/neighbour.c. + * Wensong Zhang : NOARP device (such as tunl) arp fix. + * Peter Kese : arp_solicit: saddr opt disabled for vs. */ /* RFC1122 Status: @@ -306,9 +308,15 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) u32 target = *(u32*)neigh->primary_key; int probes = neigh->probes; +#if !defined(CONFIG_IP_MASQUERADE_VS) /* Virtual server */ + /* use default interface address as source address in virtual + * server environment. Otherways the saddr might be the virtual + * address and gateway's arp cache might start routing packets + * to the real server */ if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) saddr = skb->nh.iph->saddr; else +#endif saddr = inet_select_addr(dev, target, RT_SCOPE_LINK); if ((probes -= neigh->parms->ucast_probes) < 0) { @@ -534,6 +542,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) struct rtable *rt; unsigned char *sha, *tha; u32 sip, tip; + struct device *tdev; u16 dev_type = dev->type; int addr_type; struct in_device *in_dev = dev->ip_ptr; @@ -629,6 +638,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) if (LOOPBACK(tip) || MULTICAST(tip)) goto out; +/* + * Check for the device flags for the target IP. If the IFF_NOARP + * is set, just delete it. No arp reply is sent. -- WZ + */ + if ((tdev = ip_dev_find(tip)) && (tdev->flags & IFF_NOARP)) + goto out; + /* * Process entry. The idea here is we want to send a reply if it is a * request for us or if it is a request for someone else that we hold diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 7a3e2618bd90..c65a691e2108 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -266,6 +266,15 @@ int ip_local_deliver(struct sk_buff *skb) } ret = ip_fw_demasquerade(&skb); +#ifdef CONFIG_IP_MASQUERADE_VS + if (ret == -3) { + /* packet had been tunneled */ + return(0); + } + if (ret == -2) { + return skb->dst->input(skb); + } +#endif if (ret < 0) { kfree_skb(skb); return 0; diff --git a/net/ipv4/ip_masq.c b/net/ipv4/ip_masq.c index 0187c58d7c5c..705c02088ea3 100644 --- a/net/ipv4/ip_masq.c +++ b/net/ipv4/ip_masq.c @@ -4,7 +4,7 @@ * * Copyright (c) 1994 Pauline Middelink * - * $Id: ip_masq.c,v 1.34.2.2 1999/08/07 10:56:28 davem Exp $ + * $Id: ip_masq.c,v 1.34.2.1 1999/07/02 10:10:00 davem Exp $ * * * See ip_fw.c for original log @@ -47,7 +47,8 @@ * Kai Bankett : do not toss other IP protos in proto_doff() * Dan Kegel : pointed correct NAT behavior for UDP streams * Julian Anastasov : use daddr and dport as hash keys - * + * Wensong Zhang : Added virtual server support + * Peter Kese : fixed TCP state handling for input-only */ #include @@ -81,6 +82,196 @@ #include #include +#ifdef CONFIG_IP_MASQUERADE_VS +#include +#endif /* CONFIG_IP_MASQUERADE_VS */ + +#ifdef CONFIG_IP_MASQUERADE_VS +/* + * The following block implements slow timers, most code is stolen + * from linux/kernel/sched.c + */ +#define SHIFT_BITS 7 +#define TVN_BITS 11 +#define TVR_BITS 7 +#define TVN_SIZE (1 << TVN_BITS) +#define TVR_SIZE (1 << TVR_BITS) +#define TVN_MASK (TVN_SIZE - 1) +#define TVR_MASK (TVR_SIZE - 1) + +struct sltimer_vec { + int index; + struct timer_list *vec[TVN_SIZE]; +}; + +struct sltimer_vec_root { + int index; + struct timer_list *vec[TVR_SIZE]; +}; + +static struct sltimer_vec sltv3 = { 0 }; +static struct sltimer_vec sltv2 = { 0 }; +static struct sltimer_vec_root sltv1 = { 0 }; + +static struct sltimer_vec * const sltvecs[] = { + (struct sltimer_vec *)&sltv1, &sltv2, &sltv3 +}; + +#define NOOF_SLTVECS (sizeof(sltvecs) / sizeof(sltvecs[0])) + +static unsigned long sltimer_jiffies = 0; + +static inline void insert_sltimer(struct timer_list *timer, + struct timer_list **vec, int idx) +{ + if ((timer->next = vec[idx])) + vec[idx]->prev = timer; + vec[idx] = timer; + timer->prev = (struct timer_list *)&vec[idx]; +} + +static inline void internal_add_sltimer(struct timer_list *timer) +{ + /* + * must be cli-ed when calling this + */ + unsigned long expires = timer->expires; + unsigned long idx = (expires - sltimer_jiffies) >> SHIFT_BITS; + + if (idx < TVR_SIZE) { + int i = (expires >> SHIFT_BITS) & TVR_MASK; + insert_sltimer(timer, sltv1.vec, i); + } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { + int i = (expires >> (SHIFT_BITS+TVR_BITS)) & TVN_MASK; + insert_sltimer(timer, sltv2.vec, i); + } else if ((signed long) idx < 0) { + /* can happen if you add a timer with expires == jiffies, + * or you set a timer to go off in the past + */ + insert_sltimer(timer, sltv1.vec, sltv1.index); + } else if (idx <= 0xffffffffUL) { + int i = (expires >> (SHIFT_BITS+TVR_BITS+TVN_BITS)) & TVN_MASK; + insert_sltimer(timer, sltv3.vec, i); + } else { + /* Can only get here on architectures with 64-bit jiffies */ + timer->next = timer->prev = timer; + } +} + +rwlock_t sltimerlist_lock = RW_LOCK_UNLOCKED; + +void add_sltimer(struct timer_list *timer) +{ + write_lock(&sltimerlist_lock); + if (timer->prev) + goto bug; + internal_add_sltimer(timer); +out: + write_unlock(&sltimerlist_lock); + return; + +bug: + printk("bug: kernel sltimer added twice at %p.\n", + __builtin_return_address(0)); + goto out; +} + +static inline int detach_sltimer(struct timer_list *timer) +{ + struct timer_list *prev = timer->prev; + if (prev) { + struct timer_list *next = timer->next; + prev->next = next; + if (next) + next->prev = prev; + return 1; + } + return 0; +} + +void mod_sltimer(struct timer_list *timer, unsigned long expires) +{ + write_lock(&sltimerlist_lock); + timer->expires = expires; + detach_sltimer(timer); + internal_add_sltimer(timer); + write_unlock(&sltimerlist_lock); +} + +int del_sltimer(struct timer_list * timer) +{ + int ret; + + write_lock(&sltimerlist_lock); + ret = detach_sltimer(timer); + timer->next = timer->prev = 0; + write_unlock(&sltimerlist_lock); + return ret; +} + + +static inline void cascade_sltimers(struct sltimer_vec *tv) +{ + /* cascade all the timers from tv up one level */ + struct timer_list *timer; + timer = tv->vec[tv->index]; + /* + * We are removing _all_ timers from the list, so we don't have to + * detach them individually, just clear the list afterwards. + */ + while (timer) { + struct timer_list *tmp = timer; + timer = timer->next; + internal_add_sltimer(tmp); + } + tv->vec[tv->index] = NULL; + tv->index = (tv->index + 1) & TVN_MASK; +} + +static inline void run_sltimer_list(void) +{ + write_lock(&sltimerlist_lock); + while ((long)(jiffies - sltimer_jiffies) >= 0) { + struct timer_list *timer; + if (!sltv1.index) { + int n = 1; + do { + cascade_sltimers(sltvecs[n]); + } while (sltvecs[n]->index == 1 && ++n < NOOF_SLTVECS); + } + while ((timer = sltv1.vec[sltv1.index])) { + void (*fn)(unsigned long) = timer->function; + unsigned long data = timer->data; + detach_sltimer(timer); + timer->next = timer->prev = NULL; + write_unlock(&sltimerlist_lock); + fn(data); + write_lock(&sltimerlist_lock); + } + sltimer_jiffies += 1<rst) - return output+3; + return state_off+3; if (th->syn) - return output+0; + return state_off+0; if (th->fin) - return output+1; + return state_off+1; if (th->ack) - return output+2; + return state_off+2; return -1; } - static int masq_set_state_timeout(struct ip_masq *ms, int state) { struct ip_masq_timeout_table *mstim = ms->timeout_table; @@ -216,14 +416,24 @@ static int masq_set_state_timeout(struct ip_masq *ms, int state) return state; } -static int masq_tcp_state(struct ip_masq *ms, int output, struct tcphdr *th) +static int masq_tcp_state(struct ip_masq *ms, int state_off, struct tcphdr *th) { int state_idx; int new_state = IP_MASQ_S_CLOSE; - if ((state_idx = masq_tcp_state_idx(th, output)) < 0) { +#ifdef CONFIG_IP_MASQUERADE_VS + /* update state offset to INPUT_ONLY if necessary */ + /* or delete NO_OUTPUT flag if output packet detected */ + if (ms->flags & IP_MASQ_F_VS_NO_OUTPUT) { + if (state_off == MASQ_STATE_OUTPUT) + ms->flags &= ~IP_MASQ_F_VS_NO_OUTPUT; + else state_off = MASQ_STATE_INPUT_ONLY; + } +#endif + + if ((state_idx = masq_tcp_state_idx(th, state_off)) < 0) { IP_MASQ_DEBUG(1, "masq_state_idx(%d)=%d!!!\n", - output, state_idx); + state_off, state_idx); goto tcp_state_out; } @@ -233,7 +443,7 @@ tcp_state_out: if (new_state!=ms->state) IP_MASQ_DEBUG(1, "%s %s [%c%c%c%c] %08lX:%04X-%08lX:%04X state: %s->%s\n", masq_proto_name(ms->protocol), - output? "output" : "input ", + (state_off==MASQ_STATE_OUTPUT) ? "output " : "input ", th->syn? 'S' : '.', th->fin? 'F' : '.', th->ack? 'A' : '.', @@ -242,6 +452,14 @@ tcp_state_out: ntohl(ms->daddr), ntohs(ms->dport), ip_masq_state_name(ms->state), ip_masq_state_name(new_state)); + +#ifdef CONFIG_IP_MASQUERADE_VS + if (th->fin && (ms->state == IP_MASQ_S_ESTABLISHED) + && (ms->flags & IP_MASQ_F_VS) && !(ms->flags & IP_MASQ_F_VS_FIN)) { + ip_vs_fin_masq(ms); + } +#endif /* CONFIG_IP_MASQUERADE_VS */ + return masq_set_state_timeout(ms, new_state); } @@ -249,7 +467,7 @@ tcp_state_out: /* * Handle state transitions */ -static int masq_set_state(struct ip_masq *ms, int output, struct iphdr *iph, void *tp) +static int masq_set_state(struct ip_masq *ms, int state_off, struct iphdr *iph, void *tp) { switch (iph->protocol) { case IPPROTO_ICMP: @@ -257,7 +475,7 @@ static int masq_set_state(struct ip_masq *ms, int output, struct iphdr *iph, voi case IPPROTO_UDP: return masq_set_state_timeout(ms, IP_MASQ_S_UDP); case IPPROTO_TCP: - return masq_tcp_state(ms, output, tp); + return masq_tcp_state(ms, state_off, tp); } return -1; } @@ -356,6 +574,9 @@ atomic_t mport_count = ATOMIC_INIT(0); EXPORT_SYMBOL(ip_masq_get_debug_level); EXPORT_SYMBOL(ip_masq_new); +#ifdef CONFIG_IP_MASQUERADE_VS +EXPORT_SYMBOL(ip_masq_new_vs); +#endif /* CONFIG_IP_MASQUERADE_VS */ EXPORT_SYMBOL(ip_masq_listen); EXPORT_SYMBOL(ip_masq_free_ports); EXPORT_SYMBOL(ip_masq_out_get); @@ -424,9 +645,17 @@ static void __ip_masq_set_expire(struct ip_masq *ms, unsigned long tout) { if (tout) { ms->timer.expires = jiffies+tout; +#ifdef CONFIG_IP_MASQUERADE_VS + add_sltimer(&ms->timer); +#else add_timer(&ms->timer); +#endif } else { +#ifdef CONFIG_IP_MASQUERADE_VS + del_sltimer(&ms->timer); +#else del_timer(&ms->timer); +#endif } } @@ -742,6 +971,10 @@ struct ip_masq * ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 struct ip_masq *ms; read_lock(&__ip_masq_lock); +#ifdef CONFIG_IP_MASQUERADE_VS + ms = ip_vs_out_get(protocol, s_addr, s_port, d_addr, d_port); + if (ms == NULL) +#endif /* CONFIG_IP_MASQUERADE_VS */ ms = __ip_masq_out_get(protocol, s_addr, s_port, d_addr, d_port); read_unlock(&__ip_masq_lock); @@ -755,6 +988,10 @@ struct ip_masq * ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 struct ip_masq *ms; read_lock(&__ip_masq_lock); +#ifdef CONFIG_IP_MASQUERADE_VS + ms = ip_vs_in_get(protocol, s_addr, s_port, d_addr, d_port); + if (ms == NULL) +#endif /* CONFIG_IP_MASQUERADE_VS */ ms = __ip_masq_in_get(protocol, s_addr, s_port, d_addr, d_port); read_unlock(&__ip_masq_lock); @@ -825,6 +1062,14 @@ static void masq_expire(unsigned long data) if (ms->control) ip_masq_control_del(ms); +#ifdef CONFIG_IP_MASQUERADE_VS + if (ms->flags & IP_MASQ_F_VS) { + if (ip_vs_unhash(ms)) { + ip_vs_unbind_masq(ms); + } + } + else +#endif /* CONFIG_IP_MASQUERADE_VS */ if (ip_masq_unhash(ms)) { if (ms->flags&IP_MASQ_F_MPORT) { atomic_dec(&mport_count); @@ -1061,6 +1306,73 @@ mport_nono: return NULL; } + +#ifdef CONFIG_IP_MASQUERADE_VS +/* + * Create a new masquerade entry for IPVS, all parameters {maddr, + * mport, saddr, sport, daddr, dport, mflags} are known. No need + * to allocate a free mport. And, hash it into the ip_vs_table. + * + * Be careful, it can be called from u-space + */ + +struct ip_masq * ip_masq_new_vs(int proto, __u32 maddr, __u16 mport, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags) +{ + struct ip_masq *ms; + static int n_fails = 0; + int prio; + + prio = (mflags&IP_MASQ_F_USER) ? GFP_KERNEL : GFP_ATOMIC; + + ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), prio); + if (ms == NULL) { + if (++n_fails < 5) + IP_VS_ERR("ip_masq_new_vs(proto=%s): no memory available.\n", + masq_proto_name(proto)); + return NULL; + } + MOD_INC_USE_COUNT; + memset(ms, 0, sizeof(*ms)); + init_timer(&ms->timer); + ms->timer.data = (unsigned long)ms; + ms->timer.function = masq_expire; + ms->protocol = proto; + ms->saddr = saddr; + ms->sport = sport; + ms->daddr = daddr; + ms->dport = dport; + ms->maddr = maddr; + ms->mport = mport; + ms->flags = mflags; + ms->app_data = NULL; + ms->control = NULL; + + atomic_set(&ms->n_control,0); + atomic_set(&ms->refcnt,0); + + if (mflags & IP_MASQ_F_USER) + write_lock_bh(&__ip_masq_lock); + else + write_lock(&__ip_masq_lock); + + /* + * Hash it in the ip_vs_table + */ + ip_vs_hash(ms); + + if (mflags & IP_MASQ_F_USER) + write_unlock_bh(&__ip_masq_lock); + else + write_unlock(&__ip_masq_lock); + + /* ip_masq_bind_app(ms); */ + atomic_inc(&ms->refcnt); + masq_set_state_timeout(ms, IP_MASQ_S_NONE); + return ms; +} +#endif /* CONFIG_IP_MASQUERADE_VS */ + + /* * Get transport protocol data offset, check against size * return: @@ -1103,6 +1415,7 @@ static __inline__ int proto_doff(unsigned proto, char *th, unsigned size) return ret; } + int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb = *skb_p; @@ -1357,11 +1670,11 @@ int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr) IP_MASQ_DEBUG(2, "O-routed from %08lX:%04X with masq.addr %08lX\n", ntohl(ms->maddr),ntohs(ms->mport),ntohl(maddr)); - masq_set_state(ms, 1, iph, h.portp); + masq_set_state(ms, MASQ_STATE_OUTPUT, iph, h.portp); ip_masq_put(ms); return 0; - } +} /* * Restore original addresses and ports in the original IP @@ -1520,7 +1833,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) ntohs(icmp_id(icmph)), icmph->type); - masq_set_state(ms, 1, iph, icmph); + masq_set_state(ms, MASQ_STATE_OUTPUT, iph, icmph); ip_masq_put(ms); return 1; @@ -1766,7 +2079,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) ntohs(icmp_id(icmph)), icmph->type); - masq_set_state(ms, 0, iph, icmph); + masq_set_state(ms, MASQ_STATE_INPUT, iph, icmph); ip_masq_put(ms); return 1; @@ -1984,13 +2297,19 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) return(ip_fw_demasq_icmp(skb_p)); case IPPROTO_TCP: case IPPROTO_UDP: - /* + /* * Make sure packet is in the masq range * ... or some mod-ule relaxes input range * ... or there is still some `special' mport opened */ +#ifdef CONFIG_IP_MASQUERADE_VS + ms = ip_masq_in_get_iph(iph); + if ((ms == NULL) + && (ip_vs_lookup_service(maddr, h.portp[1], iph->protocol) == NULL) +#else if ((ntohs(h.portp[1]) < PORT_MASQ_BEGIN || ntohs(h.portp[1]) > PORT_MASQ_END) +#endif /* CONFIG_IP_MASQUERADE_VS */ #ifdef CONFIG_IP_MASQUERADE_MOD && (ip_masq_mod_in_rule(skb, iph) != 1) #endif @@ -2032,8 +2351,6 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) return 0; } - - IP_MASQ_DEBUG(2, "Incoming %s %08lX:%04X -> %08lX:%04X\n", masq_proto_name(iph->protocol), ntohl(iph->saddr), ntohs(h.portp[0]), @@ -2042,8 +2359,9 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) /* * reroute to original host:port if found... */ - +#ifndef CONFIG_IP_MASQUERADE_VS ms = ip_masq_in_get_iph(iph); +#endif /* * Give additional modules a chance to create an entry @@ -2058,10 +2376,19 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) ip_masq_mod_in_update(skb, iph, ms); #endif +#ifdef CONFIG_IP_MASQUERADE_VS + if (!ms && (h.th->syn || (iph->protocol!=IPPROTO_TCP))) { + /* + * Let the virtual server select a real server + * for the incomming connection, and create a + * masquerading entry. + */ + ms = ip_vs_schedule(iph->daddr,h.portp[1],iph->protocol,iph); + } +#endif /* CONFIG_IP_MASQUERADE_VS */ if (ms != NULL) { - /* * got reply, so clear flag */ @@ -2110,13 +2437,65 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) } } + if ((skb=masq_skb_cow(skb_p, &iph, &h.raw)) == NULL) { ip_masq_put(ms); return -1; } + +#ifdef CONFIG_IP_MASQUERADE_VS + if (IP_MASQ_VS_FWD(ms) != 0) { + int ret = 0; + + /* + * Return values mean: + * -1 skb must be released + * -2 call skb->dst->input(skb) to release skb + * -3 skb has been released + */ + switch (IP_MASQ_VS_FWD(ms)) { + case IP_MASQ_F_VS_TUNNEL: + if (ip_vs_tunnel_xmit(skb_p, ms->saddr) == 0) { + IP_VS_DBG("tunneling error.\n"); + } else { + IP_VS_DBG("tunneling succeeded.\n"); + } + ret = -3; + break; + + case IP_MASQ_F_VS_DROUTE: + dst_release(skb->dst); + skb->dst = NULL; + ip_send_check(iph); + if (ip_route_input(skb, ms->saddr, iph->saddr, + iph->tos, skb->dev)) { + IP_VS_DBG("direct routing error.\n"); + ret = -1; + } else { + IP_VS_DBG("direct routing succeeded.\n"); + ret = -2; + } + break; + + case IP_MASQ_F_VS_LOCALNODE: + ret = 0; + } + + /* + * Set state of masq entry + */ + masq_set_state (ms, MASQ_STATE_INPUT, iph, h.portp); + ip_masq_put(ms); + + return ret; + } + + IP_VS_DBG("masquerading packet...\n"); +#endif /* CONFIG_IP_MASQUERADE_VS */ + iph->daddr = ms->saddr; h.portp[1] = ms->sport; - + /* * Invalidate csum saving if tunnel has masq helper */ @@ -2173,11 +2552,11 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) h.uh->check = 0xFFFF; break; } - ip_send_check(iph); + ip_send_check(iph); IP_MASQ_DEBUG(2, "I-routed to %08lX:%04X\n",ntohl(iph->daddr),ntohs(h.portp[1])); - masq_set_state (ms, 0, iph, h.portp); + masq_set_state (ms, MASQ_STATE_INPUT, iph, h.portp); ip_masq_put(ms); return 1; @@ -2292,7 +2671,6 @@ static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset, len += sprintf(buffer+len, "%-127s\n", temp); if(len >= length) { - read_unlock_bh(&__ip_masq_lock); goto done; } @@ -2300,9 +2678,52 @@ static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset, read_unlock_bh(&__ip_masq_lock); } -done: +#ifdef CONFIG_IP_MASQUERADE_VS + for(idx = 0; idx < IP_VS_TAB_SIZE; idx++) + { + /* + * Lock is actually only need in next loop + * we are called from uspace: must stop bh. + */ + read_lock_bh(&__ip_masq_lock); + l = &ip_vs_table[idx]; + for (e=l->next; e!=l; e=e->next) { + ms = list_entry(e, struct ip_masq, m_list); + pos += 128; + if (pos <= offset) { + len = 0; + continue; + } + + /* + * We have locked the tables, no need to del/add timers + * nor cli() 8) + */ + + sprintf(temp,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %7lu", + masq_proto_name(ms->protocol), + ntohl(ms->saddr), ntohs(ms->sport), + ntohl(ms->daddr), ntohs(ms->dport), + ntohs(ms->mport), + ms->out_seq.init_seq, + ms->out_seq.delta, + ms->out_seq.previous_delta, + ms->timer.expires-jiffies); + len += sprintf(buffer+len, "%-127s\n", temp); + + if(len >= length) { + read_unlock_bh(&__ip_masq_lock); + goto done; + } + } + read_unlock_bh(&__ip_masq_lock); + + } +#endif /* CONFIG_IP_MASQUERADE_VS */ + +done: begin = len - (pos - offset); *start = buffer + begin; len -= begin; @@ -2409,6 +2830,11 @@ int ip_masq_uctl(int optname, char * optval , int optlen) case IP_MASQ_TARGET_MOD: ret = ip_masq_mod_ctl(optname, &masq_ctl, optlen); break; +#endif +#ifdef CONFIG_IP_MASQUERADE_VS + case IP_MASQ_TARGET_VS: + ret = ip_vs_ctl(optname, &masq_ctl, optlen); + break; #endif } @@ -2529,7 +2955,7 @@ __initfunc(int ip_masq_init(void)) (char *) IPPROTO_ICMP, ip_masq_user_info }); -#endif +#endif /* CONFIG_PROC_FS */ #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW ip_autofw_init(); #endif @@ -2538,6 +2964,11 @@ __initfunc(int ip_masq_init(void)) #endif #ifdef CONFIG_IP_MASQUERADE_MFW ip_mfw_init(); +#endif +#ifdef CONFIG_IP_MASQUERADE_VS + ip_vs_init(); + slow_timer.expires = jiffies+SLTIMER_PERIOD; + add_timer(&slow_timer); #endif ip_masq_app_init(); diff --git a/net/ipv4/ip_masq_autofw.c b/net/ipv4/ip_masq_autofw.c index d2a1729c5386..dc99d14eacfc 100644 --- a/net/ipv4/ip_masq_autofw.c +++ b/net/ipv4/ip_masq_autofw.c @@ -179,13 +179,13 @@ static __inline__ int ip_autofw_add(struct ip_autofw_user * af) { struct ip_autofw * newaf; newaf = kmalloc( sizeof(struct ip_autofw), GFP_KERNEL ); - init_timer(&newaf->timer); if ( newaf == NULL ) { printk("ip_autofw_add: malloc said no\n"); return( ENOMEM ); } + init_timer(&newaf->timer); MOD_INC_USE_COUNT; memcpy(newaf, af, sizeof(struct ip_autofw_user)); diff --git a/net/ipv4/ip_masq_mfw.c b/net/ipv4/ip_masq_mfw.c index 60c7797065f9..f1cbb8853982 100644 --- a/net/ipv4/ip_masq_mfw.c +++ b/net/ipv4/ip_masq_mfw.c @@ -216,6 +216,7 @@ static int mfw_delhost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu) (!mu->rport || h->port == mu->rport)) { /* HIT */ atomic_dec(&mfw->nhosts); + e = h->list.prev; list_del(&h->list); kfree_s(h, sizeof(*h)); MOD_DEC_USE_COUNT; diff --git a/net/ipv4/ip_masq_portfw.c b/net/ipv4/ip_masq_portfw.c index 6c697a1029bc..898bc5f525a2 100644 --- a/net/ipv4/ip_masq_portfw.c +++ b/net/ipv4/ip_masq_portfw.c @@ -85,7 +85,8 @@ static __inline__ int ip_portfw_del(__u16 protocol, __u16 lport, __u32 laddr, __ (!laddr || n->laddr == laddr) && (!raddr || n->raddr == raddr) && (!rport || n->rport == rport)) { - list_del(entry); + entry = n->list.prev; + list_del(&n->list); ip_masq_mod_dec_nent(mmod_self); kfree_s(n, sizeof(struct ip_portfw)); MOD_DEC_USE_COUNT; @@ -422,8 +423,6 @@ static struct ip_masq * portfw_in_create(const struct sk_buff *skb, const struct raddr, rport, iph->saddr, portp[0], 0); - ip_masq_listen(ms); - if (!ms || atomic_read(&mmod_self->mmod_nent) <= 1 /* || ip_masq_nlocks(&portfw_lock) != 1 */ ) /* @@ -431,6 +430,8 @@ static struct ip_masq * portfw_in_create(const struct sk_buff *skb, const struct */ goto out; + ip_masq_listen(ms); + /* * Entry created, lock==1. * if pref_cnt == 0, move diff --git a/net/ipv4/ip_masq_user.c b/net/ipv4/ip_masq_user.c index 5129744195f5..134d385c5e4e 100644 --- a/net/ipv4/ip_masq_user.c +++ b/net/ipv4/ip_masq_user.c @@ -2,7 +2,7 @@ * IP_MASQ_USER user space control module * * - * $Id: ip_masq_user.c,v 1.1.2.1 1999/08/07 10:56:33 davem Exp $ + * $Id: ip_masq_user.c,v 1.1 1998/08/29 23:51:08 davem Exp $ */ #include diff --git a/net/ipv4/ip_vs.c b/net/ipv4/ip_vs.c new file mode 100644 index 000000000000..2df98e1f50eb --- /dev/null +++ b/net/ipv4/ip_vs.c @@ -0,0 +1,1296 @@ +/* + * IPVS An implementation of the IP virtual server support for the + * LINUX operating system. IPVS is now implemented as a part + * of IP masquerading code. IPVS can be used to build a + * high-performance and highly available server based on a + * cluster of servers. + * + * Version: $Id: ip_vs.c,v 1.2 1999/07/09 12:12:23 wensong Exp $ + * + * Authors: Wensong Zhang + * Peter Kese + * + * 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. + * + * Changes: + * Wensong Zhang : fixed the overflow bug in ip_vs_procinfo + * Wensong Zhang : added editing dest and service functions + * Wensong Zhang : changed name of some functions + * Wensong Zhang : fixed the unlocking bug in ip_vs_del_dest + * Wensong Zhang : added a separate hash table for IPVS + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_KMOD +#include +#endif + +EXPORT_SYMBOL(register_ip_vs_scheduler); +EXPORT_SYMBOL(unregister_ip_vs_scheduler); +EXPORT_SYMBOL(ip_vs_bind_masq); +EXPORT_SYMBOL(ip_vs_unbind_masq); + +/* + * Lock for IPVS + */ +rwlock_t __ip_vs_lock = RW_LOCK_UNLOCKED; + +/* + * Hash table: for input and output packets lookups of IPVS + */ +struct list_head ip_vs_table[IP_VS_TAB_SIZE]; + +/* + * virtual server list and schedulers + */ +static struct ip_vs_service *service_list[2] = {NULL,NULL}; +static struct ip_vs_scheduler *schedulers = NULL; + + +/* + * Register a scheduler in the scheduler list + */ +int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) +{ + if (!scheduler) { + IP_VS_ERR("register_ip_vs_scheduler(): NULL arg\n"); + return -EINVAL; + } + + if (!scheduler->name) { + IP_MASQ_ERR("register_ip_vs_scheduler(): NULL scheduler_name\n"); + return -EINVAL; + } + + if (scheduler->next) { + IP_VS_ERR("register_ip_vs_scheduler(): scheduler already linked\n"); + return -EINVAL; + } + + scheduler->next = schedulers; + schedulers = scheduler; + + return 0; +} + + +/* + * Unregister a scheduler in the scheduler list + */ +int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) +{ + struct ip_vs_scheduler **psched; + + if (!scheduler) { + IP_MASQ_ERR( "unregister_ip_vs_scheduler(): NULL arg\n"); + return -EINVAL; + } + + /* + * Only allow unregistration if it is not referenced + */ + if (atomic_read(&scheduler->refcnt)) { + IP_MASQ_ERR( "unregister_ip_vs_scheduler(): is in use by %d guys. failed\n", + atomic_read(&scheduler->refcnt)); + return -EINVAL; + } + + /* + * Must be already removed from the scheduler list + */ + for (psched = &schedulers; (*psched) && (*psched != scheduler); + psched = &((*psched)->next)); + + if (*psched != scheduler) { + IP_VS_ERR("unregister_ip_vs_scheduler(): scheduler is in the list. failed\n"); + return -EINVAL; + } + + *psched = scheduler->next; + scheduler->next = NULL; + + return 0; +} + + +/* + * Bind a service with a scheduler + */ +int ip_vs_bind_scheduler(struct ip_vs_service *svc, + struct ip_vs_scheduler *scheduler) +{ + if (svc == NULL) { + IP_VS_ERR("ip_vs_bind_scheduler(): svc arg NULL\n"); + return -EINVAL; + } + if (scheduler == NULL) { + IP_VS_ERR("ip_vs_bind_scheduler(): scheduler arg NULL\n"); + return -EINVAL; + } + + svc->scheduler = scheduler; + atomic_inc(&scheduler->refcnt); + + if(scheduler->init_service) + if(scheduler->init_service(svc) != 0) { + IP_VS_ERR("ip_vs_bind_scheduler(): init error\n"); + return -EINVAL; + } + + return 0; +} + + +/* + * Unbind a service with its scheduler + */ +int ip_vs_unbind_scheduler(struct ip_vs_service *svc) +{ + struct ip_vs_scheduler *sched; + + if (svc == NULL) { + IP_VS_ERR("ip_vs_unbind_scheduler(): svc arg NULL\n"); + return -EINVAL; + } + + sched = svc->scheduler; + if (sched == NULL) { + IP_VS_ERR("ip_vs_unbind_scheduler(): svc isn't bound\n"); + return -EINVAL; + } + + if(sched->done_service) + if(sched->done_service(svc) != 0) { + IP_VS_ERR("ip_vs_unbind_scheduler(): done error\n"); + return -EINVAL; + } + + atomic_dec(&sched->refcnt); + svc->scheduler = NULL; + + return 0; +} + + +/* + * Returns hash value for IPVS + */ + +static __inline__ unsigned +ip_vs_hash_key(unsigned proto, __u32 addr, __u16 port) +{ + unsigned addrh = ntohl(addr); + + return (proto^addrh^(addrh>>IP_VS_TAB_BITS)^ntohs(port)) + & (IP_VS_TAB_SIZE-1); +} + + +/* + * Hashes ip_masq in ip_vs_table by proto,addr,port. + * should be called with locked tables. + * returns bool success. + */ +int ip_vs_hash(struct ip_masq *ms) +{ + unsigned hash; + + if (ms->flags & IP_MASQ_F_HASHED) { + IP_VS_ERR("ip_vs_hash(): request for already hashed, called from %p\n", + __builtin_return_address(0)); + return 0; + } + /* + * Hash by proto,client{addr,port} + */ + hash = ip_vs_hash_key(ms->protocol, ms->daddr, ms->dport); + + /* + * Note: because ip_masq_put sets masq expire if its refcnt==2, + * we have to increase counter two times, otherwise the + * masq won't expire. + */ + atomic_inc(&ms->refcnt); + atomic_inc(&ms->refcnt); + list_add(&ms->m_list, &ip_vs_table[hash]); + + ms->flags |= IP_MASQ_F_HASHED; + return 1; +} + + +/* + * UNhashes ip_masq from ip_vs_table. + * should be called with locked tables. + * returns bool success. + */ +int ip_vs_unhash(struct ip_masq *ms) +{ + unsigned int hash; + struct ip_masq ** ms_p; + + if (!(ms->flags & IP_MASQ_F_HASHED)) { + IP_VS_ERR("ip_vs_unhash(): request for unhash flagged, called from %p\n", + __builtin_return_address(0)); + return 0; + } + /* + * UNhash by client{addr,port} + */ + hash = ip_vs_hash_key(ms->protocol, ms->daddr, ms->dport); + /* + * Note: since we increase refcnt twice while hashing, + * we have to decrease it twice while unhashing. + */ + atomic_dec(&ms->refcnt); + atomic_dec(&ms->refcnt); + list_del(&ms->m_list); + ms->flags &= ~IP_MASQ_F_HASHED; + return 1; +} + + +/* + * Gets ip_masq associated with supplied parameters in the ip_vs_table. + * Called for pkts coming from OUTside-to-INside the firewall. + * s_addr, s_port: pkt source address (foreign host) + * d_addr, d_port: pkt dest address (firewall) + * Caller must lock tables + */ + +struct ip_masq * ip_vs_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) +{ + unsigned hash; + struct ip_masq *ms = NULL; + struct list_head *l, *e; + + hash = ip_vs_hash_key(protocol, s_addr, s_port); + + l=&ip_vs_table[hash]; + for(e=l->next; e!=l; e=e->next) + { + ms = list_entry(e, struct ip_masq, m_list); + if (protocol==ms->protocol && + d_addr==ms->maddr && d_port==ms->mport && + s_addr==ms->daddr && s_port==ms->dport + ) { + atomic_inc(&ms->refcnt); + goto out; + } + } + + out: + return ms; +} + + +/* + * Gets ip_masq associated with supplied parameters in the ip_vs_table. + * Called for pkts coming from inside-to-OUTside the firewall. + * s_addr, s_port: pkt source address (inside host) + * d_addr, d_port: pkt dest address (foreigh host) + * Caller must lock tables + */ +struct ip_masq * ip_vs_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) +{ + unsigned hash; + struct ip_masq *ms = NULL; + struct list_head *l, *e; + + /* + * Check for "full" addressed entries + */ + hash = ip_vs_hash_key(protocol, d_addr, d_port); + l=&ip_vs_table[hash]; + + for(e=l->next; e!=l; e=e->next) + { + ms = list_entry(e, struct ip_masq, m_list); + if (protocol == ms->protocol && + s_addr == ms->saddr && s_port == ms->sport && + d_addr == ms->daddr && d_port == ms->dport + ) { + atomic_inc(&ms->refcnt); + goto out; + } + + } + + out: + return ms; +} + + +/* + * Create a destination + */ +struct ip_vs_dest *ip_vs_new_dest(struct ip_vs_service *svc, + struct ip_masq_ctl *mctl) +{ + struct ip_vs_dest *dest; + struct ip_vs_user *mm = &mctl->u.vs_user; + + IP_VS_DBG("enter ip_vs_new_dest()\n"); + + dest = (struct ip_vs_dest*) kmalloc(sizeof(struct ip_vs_dest), + GFP_ATOMIC); + if (dest == NULL) { + IP_VS_ERR("ip_vs_new_dest: kmalloc failed.\n"); + return NULL; + } + memset(dest, 0, sizeof(struct ip_vs_dest)); + + dest->service = svc; + dest->addr = mm->daddr; + dest->port = mm->dport; + dest->weight = mm->weight; + dest->masq_flags = mm->masq_flags; + + atomic_set(&dest->connections, 0); + atomic_set(&dest->refcnt, 0); + + /* + * Set the IP_MASQ_F_VS flag + */ + dest->masq_flags |= IP_MASQ_F_VS; + + /* check if local node and update the flags */ + if (inet_addr_type(mm->daddr) == RTN_LOCAL) { + dest->masq_flags = (dest->masq_flags & ~IP_MASQ_F_VS_FWD_MASK) + | IP_MASQ_F_VS_LOCALNODE; + } + + /* check if (fwd != masquerading) and update the port & flags */ + if ((dest->masq_flags & IP_MASQ_F_VS_FWD_MASK) != 0) { + dest->masq_flags |= IP_MASQ_F_VS_NO_OUTPUT; + } + + return dest; +} + + +/* + * Add a destination into an existing service + */ +int ip_vs_add_dest(struct ip_vs_service *svc, struct ip_masq_ctl *mctl) +{ + struct ip_vs_dest *dest; + struct ip_vs_user *mm = &mctl->u.vs_user; + __u32 daddr = mm->daddr; + __u16 dport = mm->dport; + + IP_VS_DBG("enter ip_vs_add_dest()\n"); + + if (mm->weight < 0) { + IP_VS_ERR("ip_vs_add_dest(): server weight less than zero\n"); + return -ERANGE; + } + + write_lock_bh(&__ip_vs_lock); + + /* check the existing dest list */ + for (dest=svc->destinations; dest; dest=dest->next) { + if ((dest->addr == daddr) && (dest->port == dport)) { + write_unlock_bh(&__ip_vs_lock); + IP_VS_ERR("ip_vs_add_dest(): dest exists\n"); + return -EEXIST; + } + } + + /* allocate and initialize the dest structure */ + dest = ip_vs_new_dest(svc, mctl); + if (dest == NULL) { + write_unlock_bh(&__ip_vs_lock); + IP_VS_ERR("ip_vs_add_dest(): out of memory\n"); + return -ENOMEM; + } + + /* put the dest entry into the list */ + dest->next = svc->destinations; + svc->destinations = dest; + + write_unlock_bh(&__ip_vs_lock); + + atomic_inc(&dest->refcnt); + + return 0; +} + + +/* + * Edit a destination in a service + */ +int ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_masq_ctl *mctl) +{ + struct ip_vs_dest *dest; + struct ip_vs_user *mm = &mctl->u.vs_user; + __u32 daddr = mm->daddr; + __u16 dport = mm->dport; + + IP_VS_DBG("enter ip_vs_edit_dest()\n"); + + if (mm->weight < 0) { + IP_VS_ERR("ip_vs_add_dest(): server weight less than zero\n"); + return -ERANGE; + } + + write_lock_bh(&__ip_vs_lock); + + /* lookup the destination list */ + for (dest=svc->destinations; dest; dest=dest->next) { + if ((dest->addr == daddr) && (dest->port == dport)) { + /* HIT */ + break; + } + } + + if (dest == NULL) { + write_unlock_bh(&__ip_vs_lock); + IP_VS_ERR("ip_vs_edit_dest(): dest doesn't exist\n"); + return -ENOENT; + } + + /* + * Set the weight and the flags + */ + dest->weight = mm->weight; + dest->masq_flags = mm->masq_flags; + + dest->masq_flags |= IP_MASQ_F_VS; + + /* check if local node and update the flags */ + if (inet_addr_type(mm->daddr) == RTN_LOCAL) { + dest->masq_flags = (dest->masq_flags & ~IP_MASQ_F_VS_FWD_MASK) + | IP_MASQ_F_VS_LOCALNODE; + } + + /* check if (fwd != masquerading) and update the port & flags */ + if ((dest->masq_flags & IP_MASQ_F_VS_FWD_MASK) != 0) { + dest->masq_flags |= IP_MASQ_F_VS_NO_OUTPUT; + } + + write_unlock_bh(&__ip_vs_lock); + + return 0; +} + + +/* + * Delete a destination from an existing service + */ +int ip_vs_del_dest(struct ip_vs_service *svc, struct ip_masq_ctl *mctl) +{ + struct ip_vs_dest *dest; + struct ip_vs_dest **pdest; + struct ip_vs_user *mm = &mctl->u.vs_user; + __u32 daddr = mm->daddr; + __u16 dport = mm->dport; + + IP_VS_DBG("enter ip_vs_del_dest()\n"); + + write_lock_bh(&__ip_vs_lock); + + /* remove dest from the destination list */ + pdest = &svc->destinations; + while (*pdest) { + dest = *pdest; + if ((dest->addr == daddr) && (dest->port == dport)) + /* HIT */ + break; + + pdest = &dest->next; + } + + if (*pdest == NULL) { + write_unlock_bh(&__ip_vs_lock); + IP_VS_ERR("ip_vs_del_dest(): destination not found!\n"); + return -ENOENT; + } + + *pdest = dest->next; + dest->service = NULL; + + write_unlock_bh(&__ip_vs_lock); + + /* + * Decrease the refcnt of the dest, and free the dest + * if nobody refers to it (refcnt=0). + */ + if (atomic_dec_and_test(&dest->refcnt)) + kfree_s(dest, sizeof(*dest)); + + return 0; +} + + +#if 0 +struct ip_vs_dest * ip_vs_lookup_dest(struct ip_vs_service *svc, + __u32 daddr, __u16 dport) +{ + struct ip_vs_dest *dest; + + read_lock_bh(&__ip_vs_lock); + + /* + * Find the destination for the given service + */ + for (dest=svc->destinations; dest; dest=dest->next) { + if ((dest->addr == daddr) && (dest->port == dport)) { + /* HIT */ + read_unlock_bh(&__ip_vs_lock); + return dest; + } + } + + read_unlock_bh(&__ip_vs_lock); + return NULL; +} +#endif + + +/* + * Add a service into the service list + */ +int ip_vs_add_service(__u32 vaddr, __u16 vport, + __u16 protocol, struct ip_vs_scheduler *scheduler) +{ + struct ip_vs_service *svc; + int proto_num = masq_proto_num(protocol); + int ret = 0; + + write_lock_bh(&__ip_vs_lock); + + /* check if the service already exists */ + for (svc = service_list[proto_num]; svc; svc = svc->next) { + if ((svc->port == vport) && (svc->addr == vaddr)) { + ret = -EEXIST; + goto out; + } + } + + svc = (struct ip_vs_service*) kmalloc(sizeof(struct ip_vs_service), + GFP_ATOMIC); + if (svc == NULL) { + IP_VS_ERR("vs_add_svc: kmalloc failed.\n"); + ret = -1; + goto out; + } + memset(svc,0,sizeof(struct ip_vs_service)); + + svc->addr = vaddr; + svc->port = vport; + svc->protocol = protocol; + + /* + * Bind the scheduler + */ + ip_vs_bind_scheduler(svc, scheduler); + + + /* put the service into the proper service list */ + if ((svc->port) || (!service_list[proto_num])) { + /* prepend to the beginning of the list */ + svc->next = service_list[proto_num]; + service_list[proto_num] = svc; + } else { + /* append to the end of the list if port==0 */ + struct ip_vs_service *lsvc = service_list[proto_num]; + while (lsvc->next) lsvc = lsvc->next; + svc->next = NULL; + lsvc->next = svc; + } + + out: + write_unlock_bh(&__ip_vs_lock); + return ret; +} + + +/* + * Edit s service + */ +int ip_vs_edit_service(struct ip_vs_service *svc, + struct ip_vs_scheduler *scheduler) +{ + write_lock_bh(&__ip_vs_lock); + + /* + * Unbind the old scheduler + */ + ip_vs_unbind_scheduler(svc); + + /* + * Bind the new scheduler + */ + ip_vs_bind_scheduler(svc, scheduler); + + write_unlock_bh(&__ip_vs_lock); + + return 0; +} + + +/* + * Delete a service from the service list + */ +int ip_vs_del_service(struct ip_vs_service *svc) +{ + struct ip_vs_service **psvc; + struct ip_vs_dest *dest, *dnext; + int ret = 0; + + write_lock_bh(&__ip_vs_lock); + + /* remove the service from the service_list */ + psvc = &service_list[masq_proto_num(svc->protocol)]; + for(; *psvc; psvc = &(*psvc)->next) { + if (*psvc == svc) { + break; + } + } + + if (*psvc == NULL) { + IP_VS_ERR("vs_del_svc: service not listed."); + ret = -1; + goto out; + } + + *psvc = svc->next; + + /* + * Unbind scheduler + */ + ip_vs_unbind_scheduler(svc); + + /* + * Unlink the destination list + */ + dest = svc->destinations; + svc->destinations = NULL; + for (; dest; dest=dnext) { + dnext = dest->next; + dest->service = NULL; + dest->next = NULL; + + /* + * Decrease the refcnt of the dest, and free the dest + * if nobody refers to it (refcnt=0). + */ + if (atomic_dec_and_test(&dest->refcnt)) + kfree_s(dest, sizeof(*dest)); + } + + /* + * Free the service + */ + kfree_s(svc, sizeof(struct ip_vs_service)); + + out: + write_unlock_bh(&__ip_vs_lock); + return ret; +} + + +/* + * Flush all the virtual services + */ +int ip_vs_flush(void) +{ + int proto_num; + struct ip_vs_service *svc, *snext; + struct ip_vs_dest *dest, *dnext; + int ret = 0; + + write_lock_bh(&__ip_vs_lock); + + for (proto_num=0; proto_num<2; proto_num++) { + svc = service_list[proto_num]; + service_list[proto_num] = NULL; + for (; svc; svc=snext) { + snext = svc->next; + + /* + * Unbind scheduler + */ + ip_vs_unbind_scheduler(svc); + + /* + * Unlink the destination list + */ + dest = svc->destinations; + svc->destinations = NULL; + for (; dest; dest=dnext) { + dnext = dest->next; + dest->service = NULL; + dest->next = NULL; + + /* + * Decrease the refcnt of the dest, and free + * the dest if nobody refers to it (refcnt=0). + */ + if (atomic_dec_and_test(&dest->refcnt)) + kfree_s(dest, sizeof(*dest)); + } + + /* + * Free the service + */ + kfree_s(svc, sizeof(*svc)); + } + } + + write_unlock_bh(&__ip_vs_lock); + return ret; +} + + +/* + * Called when a FIN packet of ms is received + */ +void ip_vs_fin_masq(struct ip_masq *ms) +{ + IP_VS_DBG("enter ip_vs_fin_masq()\n"); + + IP_VS_DBG("Masq fwd:%c s:%s c:%lX:%x v:%lX:%x d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(ms), ip_masq_state_name(ms->state), + ntohl(ms->daddr),ntohs(ms->dport), + ntohl(ms->maddr),ntohs(ms->mport), + ntohl(ms->saddr),ntohs(ms->sport), + ms->flags, atomic_read(&ms->refcnt)); + + if(ms->dest) + atomic_dec(&ms->dest->connections); + ms->flags |= IP_MASQ_F_VS_FIN; +} + + +/* + * Bind a masq entry with a VS destination + */ +void ip_vs_bind_masq(struct ip_masq *ms, struct ip_vs_dest *dest) +{ + IP_VS_DBG("enter ip_vs_bind_masq()\n"); + + IP_VS_DBG("Masq fwd:%c s:%s c:%lX:%x v:%lX:%x d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(ms), ip_masq_state_name(ms->state), + ntohl(ms->daddr),ntohs(ms->dport), + ntohl(ms->maddr),ntohs(ms->mport), + ntohl(ms->saddr),ntohs(ms->sport), + ms->flags, atomic_read(&ms->refcnt)); + + ms->flags |= dest->masq_flags; + ms->dest = dest; + + /* + * Increase the refcnt and connections couters of the dest. + */ + atomic_inc(&dest->refcnt); + atomic_inc(&dest->connections); +} + + +/* + * Unbind a masq entry with its VS destination + */ +void ip_vs_unbind_masq(struct ip_masq *ms) +{ + struct ip_vs_dest *dest = ms->dest; + + IP_VS_DBG("enter ip_vs_unbind_masq()\n"); + + IP_VS_DBG("Masq fwd:%c s:%s c:%lX:%x v:%lX:%x d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(ms), ip_masq_state_name(ms->state), + ntohl(ms->daddr),ntohs(ms->dport), + ntohl(ms->maddr),ntohs(ms->mport), + ntohl(ms->saddr),ntohs(ms->sport), + ms->flags, atomic_read(&ms->refcnt)); + + if (dest) { + if (!(ms->flags & IP_MASQ_F_VS_FIN)) { + /* + * Masq timeout, decrease the connection counter + */ + atomic_dec(&dest->connections); + } + + /* + * Decrease the refcnt of the dest, and free the dest + * if nobody refers to it (refcnt=0). + */ + if (atomic_dec_and_test(&dest->refcnt)) + kfree_s(dest, sizeof(*dest)); + } +} + + +/* + * Get scheduler in the scheduler list by name + */ +struct ip_vs_scheduler * ip_vs_sched_getbyname(const char *sched_name) +{ + struct ip_vs_scheduler *sched; + + IP_VS_DBG("ip_vs_sched_getbyname(): sched_name \"%s\"\n", sched_name); + + read_lock_bh(&__ip_vs_lock); + for (sched = schedulers; sched; sched = sched->next) { + if (strcmp(sched_name, sched->name)==0) { + /* HIT */ + read_unlock_bh(&__ip_vs_lock); + return sched; + } + } + + read_unlock_bh(&__ip_vs_lock); + return NULL; +} + + +/* + * Lookup scheduler and try to load it if it doesn't exist + */ +struct ip_vs_scheduler * ip_vs_lookup_scheduler(const char *sched_name) +{ + struct ip_vs_scheduler *sched; + + /* search for the scheduler by sched_name */ + sched = ip_vs_sched_getbyname(sched_name); + + /* if scheduler not found, load the module and search again */ + if (sched == NULL) { + char module_name[IP_MASQ_TNAME_MAX+8]; + sprintf(module_name,"ip_vs_%s",sched_name); +#ifdef CONFIG_KMOD + request_module(module_name); +#endif /* CONFIG_KMOD */ + sched = ip_vs_sched_getbyname(sched_name); + } + + return sched; +} + + +/* + * Lookup service by {proto,addr,port} in the service list + */ +struct ip_vs_service *ip_vs_lookup_service(__u32 vaddr, __u16 vport, + __u16 protocol) +{ + struct ip_vs_service *svc; + + read_lock(&__ip_vs_lock); + svc = service_list[masq_proto_num(protocol)]; + while (svc) { + if ((svc->addr == vaddr) && + (!svc->port || (svc->port == vport))) + break; + svc = svc->next; + } + read_unlock(&__ip_vs_lock); + return svc; +} + + +/* + * IPVS main scheduling function + * It selects a server according to the virtual service, and + * creates a masq entry. + */ +struct ip_masq *ip_vs_schedule(__u32 vaddr, __u16 vport, __u16 protocol, + struct iphdr *iph) +{ + struct ip_vs_service *svc; + struct ip_masq *ms = NULL; + int proto_num = masq_proto_num(protocol); + + read_lock(&__ip_vs_lock); + + /* + * Lookup the service + */ + for (svc = service_list[proto_num]; svc; svc = svc->next) { + if ((svc->addr == vaddr) && + (!svc->port || (svc->port == vport))) { + /* + * choose the destination and create ip_masq entry + */ + ms = svc->scheduler->schedule(svc, iph); + break; + } + } + + read_unlock(&__ip_vs_lock); + + return ms; +} + + +/* + * IPVS user control entry + */ +int ip_vs_ctl(int optname, struct ip_masq_ctl *mctl, int optlen) +{ + struct ip_vs_scheduler *sched = NULL; + struct ip_vs_service *svc = NULL; + struct ip_vs_user *mm = &mctl->u.vs_user; + __u32 vaddr = mm->vaddr; + __u16 vport = mm->vport; + int proto_num = masq_proto_num(mm->protocol); + + /* + * Check the size of mctl, no overflow... + */ + if (optlen != sizeof(*mctl)) + return EINVAL; + + /* + * Flush all the virtual service... + */ + if (mctl->m_cmd == IP_MASQ_CMD_FLUSH) + return ip_vs_flush(); + + /* + * Check for valid protocol: TCP or UDP + */ + if ((proto_num < 0) || (proto_num > 1)) { + IP_VS_INFO("vs_ctl: invalid protocol: %d" + "%d.%d.%d.%d:%d %s", + ntohs(mm->protocol), + NIPQUAD(vaddr), ntohs(vport), mctl->m_tname); + return -EFAULT; + } + + /* + * Lookup the service by (vaddr, vport, protocol) + */ + svc = ip_vs_lookup_service(vaddr, vport, mm->protocol); + + switch (mctl->m_cmd) { + case IP_MASQ_CMD_ADD: + if (svc != NULL) + return -EEXIST; + + /* lookup the scheduler, by 'mctl->m_tname' */ + sched = ip_vs_lookup_scheduler(mctl->m_tname); + if (sched == NULL) { + IP_VS_INFO("Scheduler module ip_vs_%s.o not found\n", + mctl->m_tname); + return -ENOENT; + } + + return ip_vs_add_service(vaddr, vport, + mm->protocol, sched); + + case IP_MASQ_CMD_SET: + if (svc == NULL) + return -ESRCH; + + /* lookup the scheduler, by 'mctl->m_tname' */ + sched = ip_vs_lookup_scheduler(mctl->m_tname); + if (sched == NULL) { + IP_VS_INFO("Scheduler module ip_vs_%s.o not found\n", + mctl->m_tname); + return -ENOENT; + } + + return ip_vs_edit_service(svc, sched); + + case IP_MASQ_CMD_DEL: + if (svc == NULL) + return -ESRCH; + else + return ip_vs_del_service(svc); + + case IP_MASQ_CMD_ADD_DEST: + if (svc == NULL) + return -ESRCH; + else + return ip_vs_add_dest(svc, mctl); + + case IP_MASQ_CMD_SET_DEST: + if (svc == NULL) + return -ESRCH; + else + return ip_vs_edit_dest(svc, mctl); + + case IP_MASQ_CMD_DEL_DEST: + if (svc == NULL) + return -ESRCH; + else + return ip_vs_del_dest(svc, mctl); + } + return -EINVAL; +} + + + +#ifdef CONFIG_PROC_FS +/* + * Write the contents of the VS rule table to a PROCfs file. + */ +static int ip_vs_procinfo(char *buf, char **start, off_t offset, + int length, int *eof, void *data) +{ + int ind; + int len=0; + off_t pos=0; + int size; + char str1[22]; + struct ip_vs_service *svc = NULL; + struct ip_vs_dest *dest; + __u16 protocol = 0; + + size = sprintf(buf+len, + "IP Virtual Server (Version 0.7)\n" + "Protocol Local Address:Port Scheduler\n" + " -> Remote Address:Port Forward Weight ActiveConn FinConn\n"); + pos += size; + len += size; + + read_lock_bh(&__ip_vs_lock); + + for (ind = 0; ind < 2; ind++) { + if (ind == 0) + protocol = IPPROTO_UDP; + else + protocol = IPPROTO_TCP; + + for (svc=service_list[masq_proto_num(protocol)]; svc; svc=svc->next) { + size = sprintf(buf+len, "%s %d.%d.%d.%d:%d %s\n", + masq_proto_name(protocol), + NIPQUAD(svc->addr), ntohs(svc->port), + svc->scheduler->name); + len += size; + pos += size; + + if (pos <= offset) + len=0; + if (pos >= offset+length) + goto done; + + for (dest = svc->destinations; dest; dest = dest->next) { + char *fwd; + + switch (dest->masq_flags & IP_MASQ_F_VS_FWD_MASK) { + case IP_MASQ_F_VS_LOCALNODE: + fwd = "Local"; + break; + case IP_MASQ_F_VS_TUNNEL: + fwd = "Tunnel"; + break; + case IP_MASQ_F_VS_DROUTE: + fwd = "Route"; + break; + default: + fwd = "Masq"; + } + + sprintf(str1, "%d.%d.%d.%d:%d", + NIPQUAD(dest->addr), ntohs(dest->port)); + size = sprintf(buf+len, + " -> %-21s %-7s %-6d %-10d %-10d\n", + str1, fwd, dest->weight, + atomic_read(&dest->connections), + atomic_read(&dest->refcnt) - atomic_read(&dest->connections) - 1); + len += size; + pos += size; + + if (pos <= offset) + len=0; + if (pos >= offset+length) + goto done; + } + } + } + + done: + read_unlock_bh(&__ip_vs_lock); + + *start = buf+len-(pos-offset); /* Start of wanted data */ + len = pos-offset; + if (len > length) + len = length; + if (len < 0) + len = 0; + + return len; +} + +struct proc_dir_entry ip_vs_proc_entry = { + 0, /* dynamic inode */ + 2, "vs", /* namelen and name */ + S_IFREG | S_IRUGO, /* mode */ + 1, 0, 0, 0, /* nlinks, owner, group, size */ + &proc_net_inode_operations, /* operations */ + NULL, /* get_info */ + NULL, /* fill_inode */ + NULL, NULL, NULL, /* next, parent, subdir */ + NULL, /* data */ + &ip_vs_procinfo, /* function to generate proc data */ +}; + +#endif + + +/* + * This function encapsulates the packet in a new IP header, its destination + * will be set to the daddr. Most code of this function is from ipip.c. + * Usage: + * It is called in the ip_fw_demasquerade() function. The load balancer + * selects a real server from a cluster based on a scheduling algorithm, + * encapsulates the packet and forwards it to the selected server. All real + * servers are configured with "ifconfig tunl0 up". + * When the server receives the encapsulated packet, it decapsulates the + * packet, processes the request and return the reply packets directly to + * the client without passing the load balancer. This can greatly + * increase the scalability of virtual server. + * Returns: + * if succeeded, return 1; otherwise, return 0. + */ + +int ip_vs_tunnel_xmit(struct sk_buff **skb_p, __u32 daddr) +{ + struct sk_buff *skb = *skb_p; + struct rtable *rt; /* Route to the other host */ + struct device *tdev; /* Device to other host */ + struct iphdr *old_iph = skb->nh.iph; + u8 tos = old_iph->tos; + u16 df = 0; + struct iphdr *iph; /* Our new IP header */ + int max_headroom; /* The extra header space needed */ + u32 dst = daddr; + u32 src = 0; + int mtu; + + if (skb->protocol != __constant_htons(ETH_P_IP)) { + IP_VS_ERR("ip_vs_tunnel_xmit(): protocol error, ETH_P_IP: %d, skb protocol: %d\n", + __constant_htons(ETH_P_IP),skb->protocol); + goto tx_error; + } + + if (ip_route_output(&rt, dst, src, RT_TOS(tos), 0)) { + IP_VS_ERR("ip_vs_tunnel_xmit(): route error, dst: %08X\n", dst); + goto tx_error_icmp; + } + tdev = rt->u.dst.dev; + + mtu = rt->u.dst.pmtu - sizeof(struct iphdr); + if (mtu < 68) { + ip_rt_put(rt); + IP_VS_ERR("ip_vs_tunnel_xmit(): mtu less than 68\n"); + goto tx_error; + } + if (skb->dst && mtu < skb->dst->pmtu) + skb->dst->pmtu = mtu; + + df |= (old_iph->frag_off&__constant_htons(IP_DF)); + + if ((old_iph->frag_off&__constant_htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); + ip_rt_put(rt); + IP_VS_ERR("ip_vs_tunnel_xmit(): frag needed\n"); + goto tx_error; + } + + skb->h.raw = skb->nh.raw; + + /* + * Okay, now see if we can stuff it in the buffer as-is. + */ + max_headroom = (((tdev->hard_header_len+15)&~15)+sizeof(struct iphdr)); + + if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) { + struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); + if (!new_skb) { + ip_rt_put(rt); + kfree_skb(skb); + IP_VS_ERR("ip_vs_tunnel_xmit(): no memory for new_skb\n"); + return 0; + } + kfree_skb(skb); + skb = new_skb; + } + + skb->nh.raw = skb_push(skb, sizeof(struct iphdr)); + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + dst_release(skb->dst); + skb->dst = &rt->u.dst; + + /* + * Push down and install the IPIP header. + */ + + iph = skb->nh.iph; + iph->version = 4; + iph->ihl = sizeof(struct iphdr)>>2; + iph->frag_off = df; + iph->protocol = IPPROTO_IPIP; + iph->tos = tos; + iph->daddr = rt->rt_dst; + iph->saddr = rt->rt_src; + iph->ttl = old_iph->ttl; + iph->tot_len = htons(skb->len); + iph->id = htons(ip_id_count++); + ip_send_check(iph); + + ip_send(skb); + return 1; + +tx_error_icmp: + dst_link_failure(skb); +tx_error: + kfree_skb(skb); + return 0; +} + + +/* + * Initialize IP virtual server + */ +__initfunc(int ip_vs_init(void)) +{ + int idx; + for(idx = 0; idx < IP_VS_TAB_SIZE; idx++) { + INIT_LIST_HEAD(&ip_vs_table[idx]); + } +#ifdef CONFIG_PROC_FS + ip_masq_proc_register(&ip_vs_proc_entry); +#endif + +#ifdef CONFIG_IP_MASQUERADE_VS_RR + ip_vs_rr_init(); +#endif +#ifdef CONFIG_IP_MASQUERADE_VS_WRR + ip_vs_wrr_init(); +#endif +#ifdef CONFIG_IP_MASQUERADE_VS_WLC + ip_vs_wlc_init(); +#endif +#ifdef CONFIG_IP_MASQUERADE_VS_WLC + ip_vs_pcc_init(); +#endif + return 0; +} diff --git a/net/ipv4/ip_vs_pcc.c b/net/ipv4/ip_vs_pcc.c new file mode 100644 index 000000000000..7975336c5130 --- /dev/null +++ b/net/ipv4/ip_vs_pcc.c @@ -0,0 +1,240 @@ +/* + * IPVS: Persistent Client Connection Scheduling module + * + * Version: $Id: ip_vs_pcc.c,v 1.2 1999/07/09 12:12:40 wensong Exp $ + * + * Authors: Wensong Zhang + * Peter Kese + * + * 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. + * + * Changes: + * + */ + +#include +#include +#ifdef CONFIG_KMOD +#include +#endif +#include +#include +#include +#include +#ifdef CONFIG_IP_MASQUERADE_MOD +#include +#endif +#include +#include +#include + +/* + * Note: + * It is not very good to make persistent connection client feature + * as a sperate scheduling module, because PCC is different from + * scheduling modules such as RR, WRR and WLC. In fact, it is good + * to let user specify which port is persistent. This will be fixed + * in the near future. + */ + +/* + * Define TEMPLATE_TIMEOUT a little larger than average connection time + * plus MASQUERADE_EXPIRE_TCP_FIN(2*60*HZ). Because the template won't + * be released until its last controlled masq entry gets expired. + * If TEMPLATE_TIMEOUT is too less, the template will soon expire and + * will be put in expire again and again, which requires additional + * overhead. If it is too large, the same will always visit the same + * server, which will make dynamic load imbalance worse. + */ +#define TEMPLATE_TIMEOUT 6*60*HZ + +static int ip_vs_pcc_init_svc(struct ip_vs_service *svc) +{ + MOD_INC_USE_COUNT; + return 0; +} + + +static int ip_vs_pcc_done_svc(struct ip_vs_service *svc) +{ + MOD_DEC_USE_COUNT; + return 0; +} + + +/* + * In fact, it is Weighted Least Connection scheduling + */ +static struct ip_vs_dest* ip_vs_pcc_select(struct ip_vs_service *svc) +{ + struct ip_vs_dest *dest, *least; + int loh, doh; + + IP_VS_DBG("ip_vs_pcc_select(): selecting a server...\n"); + + if (svc->destinations == NULL) return NULL; + + /* + * The number of connections in TCP_FIN state is + * dest->refcnt - dest->connections -1 + * We think the overhead of processing active connections is fifty + * times than that of conncetions in TCP_FIN in average. (This fifty + * times might be not accurate, we will change it later.) We use + * the following formula to estimate the overhead: + * dest->connections*49 + dest->refcnt + * and the load: + * (dest overhead) / dest->weight + * + * Remember -- no floats in kernel mode!!! + * The comparison of h1*w2 > h2*w1 is equivalent to that of + * h1/w1 > h2/w2 + * if every weight is larger than zero. + */ + + least = svc->destinations; + loh = atomic_read(&least->connections)*49 + atomic_read(&least->refcnt); + + /* + * Find the destination with the least load. + */ + for (dest = least->next; dest; dest = dest->next) { + doh = atomic_read(&dest->connections)*49 + atomic_read(&dest->refcnt); + if (loh*dest->weight > doh*least->weight) { + least = dest; + loh = doh; + } + } + + IP_VS_DBG("The selected server: connections %d refcnt %d weight %d" + "overhead %d\n", atomic_read(&least->connections), + atomic_read(&least->refcnt), least->weight, loh); + + return least; +} + + +static struct ip_masq* ip_vs_pcc_schedule(struct ip_vs_service *svc, + struct iphdr *iph) +{ + struct ip_masq *ms, *mst; + struct ip_vs_dest *dest; + const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]); + + /* check if the template exists */ + mst = ip_masq_in_get(0, iph->saddr, 0, svc->addr, svc->port); + if (mst) { + /* + * Template masq exists... + */ + dest = mst->dest; + IP_VS_DBG("Template masq fwd:%c s:%s c:%lX:%x v:%lX:%x d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(mst), ip_masq_state_name(mst->state), + ntohl(mst->daddr),ntohs(mst->dport), + ntohl(mst->maddr),ntohs(mst->mport), + ntohl(mst->saddr),ntohs(mst->sport), + mst->flags, atomic_read(&mst->refcnt)); + } else { + /* template does not exist, select the destination */ + dest = ip_vs_pcc_select(svc); + if (!dest) return NULL; + + /* create the template */ + mst = ip_masq_new_vs(0, svc->addr, svc->port, + dest->addr, dest->port, + iph->saddr, 0, 0); + if (!mst) { + IP_VS_ERR("ip_masq_new template failed\n"); + return NULL; + } + + /* + * Bind the template masq entry with the vs dest. + */ + ip_vs_bind_masq(mst, dest); + + IP_VS_DBG("Template masq created fwd:%c s:%s c:%lX:%x v:%lX:%x" + " d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(mst), ip_masq_state_name(mst->state), + ntohl(mst->daddr),ntohs(mst->dport), + ntohl(mst->maddr),ntohs(mst->mport), + ntohl(mst->saddr),ntohs(mst->sport), + mst->flags, atomic_read(&mst->refcnt)); + + } + + /* + * The destination is known, and create the masq entry + */ + ms = ip_masq_new_vs(iph->protocol, + iph->daddr, portp[1], + dest->addr, dest->port, + iph->saddr, portp[0], + 0); + if (ms == NULL) { + IP_VS_ERR("new_vs failed\n"); + return NULL; + } + + /* + * Bind the masq entry with the vs dest. + */ + ip_vs_bind_masq(ms, dest); + + /* + * Add its control + */ + ip_masq_control_add(ms, mst); + + /* + * Set the timeout, and put it in expire. + */ + mst->timeout = TEMPLATE_TIMEOUT; + ip_masq_put(mst); + + return ms; +} + + +static struct ip_vs_scheduler ip_vs_pcc_scheduler = { + NULL, /* next */ + "pcc", /* name */ + ATOMIC_INIT(0), /* refcnt */ + ip_vs_pcc_init_svc, /* service initializer */ + ip_vs_pcc_done_svc, /* service done */ + ip_vs_pcc_schedule, /* select a server and create new masq entry */ +}; + + +__initfunc(int ip_vs_pcc_init(void)) +{ + IP_VS_INFO("InitialzingPCC scheduling\n"); + return register_ip_vs_scheduler(&ip_vs_pcc_scheduler) ; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + /* module initialization by 'request_module' */ + if(register_ip_vs_scheduler(&ip_vs_pcc_scheduler) != 0) + return -EIO; + + IP_VS_INFO("PCC scheduling module loaded.\n"); + + return 0; +} + +void cleanup_module(void) +{ + /* module cleanup by 'release_module' */ + if(unregister_ip_vs_scheduler(&ip_vs_pcc_scheduler) != 0) + IP_VS_INFO("cannot remove PCC scheduling module\n"); + else + IP_VS_INFO("PCC scheduling module unloaded.\n"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/ip_vs_rr.c b/net/ipv4/ip_vs_rr.c new file mode 100644 index 000000000000..e1622c0bb85b --- /dev/null +++ b/net/ipv4/ip_vs_rr.c @@ -0,0 +1,138 @@ +/* + * IPVS: Round-Robin Scheduling module + * + * Version: $Id: ip_vs_rr.c,v 1.2 1999/07/09 12:13:40 wensong Exp $ + * + * Authors: Wensong Zhang + * Peter Kese + * + * 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. + * + * Fixes/Changes: + * + */ + +#include +#include +#ifdef CONFIG_KMOD +#include +#endif +#include +#include +#include +#include +#ifdef CONFIG_IP_MASQUERADE_MOD +#include +#endif +#include +#include +#include + + +static int ip_vs_rr_init_svc(struct ip_vs_service *svc) +{ + MOD_INC_USE_COUNT; + return 0; +} + + +static int ip_vs_rr_done_svc(struct ip_vs_service *svc) +{ + MOD_DEC_USE_COUNT; + return 0; +} + + +/* + * Round-Robin Scheduling + */ +static struct ip_masq* ip_vs_rr_schedule(struct ip_vs_service *svc, + struct iphdr *iph) +{ + struct ip_vs_dest *dest; + struct ip_masq *ms; + const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]); + + IP_VS_DBG("ip_vs_rr_schedule(): Scheduling...\n"); + + if (svc->sched_data != NULL) + svc->sched_data = ((struct ip_vs_dest*)svc->sched_data)->next; + if (svc->sched_data == NULL) + svc->sched_data = svc->destinations; + if (svc->sched_data == NULL) + return NULL; + + dest = svc->sched_data; + + /* + * Create a masquerading entry. + */ + ms = ip_masq_new_vs(iph->protocol, + iph->daddr, portp[1], + dest->addr, dest->port, + iph->saddr, portp[0], + 0); + if (ms == NULL) { + IP_VS_ERR("ip_masq_new failed\n"); + return NULL; + } + + /* + * Bind the masq entry with the vs dest. + */ + ip_vs_bind_masq(ms, dest); + + IP_VS_DBG("Masq fwd:%c s:%s c:%lX:%x v:%lX:%x d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(ms), ip_masq_state_name(ms->state), + ntohl(ms->daddr),ntohs(ms->dport), + ntohl(ms->maddr),ntohs(ms->mport), + ntohl(ms->saddr),ntohs(ms->sport), + ms->flags, atomic_read(&ms->refcnt)); + + return ms; +} + + +static struct ip_vs_scheduler ip_vs_rr_scheduler = { + NULL, /* next */ + "rr", /* name */ + ATOMIC_INIT(0), /* refcnt */ + ip_vs_rr_init_svc, /* service initializer */ + ip_vs_rr_done_svc, /* service done */ + ip_vs_rr_schedule, /* select a server and create new masq entry */ +}; + + +__initfunc(int ip_vs_rr_init(void)) +{ + IP_VS_INFO("Initializing RR scheduling\n"); + return register_ip_vs_scheduler(&ip_vs_rr_scheduler) ; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + /* module initialization by 'request_module' */ + if(register_ip_vs_scheduler(&ip_vs_rr_scheduler) != 0) + return -EIO; + + IP_VS_INFO("RR scheduling module loaded.\n"); + + return 0; +} + +void cleanup_module(void) +{ + /* module cleanup by 'release_module' */ + if(unregister_ip_vs_scheduler(&ip_vs_rr_scheduler) != 0) + IP_VS_INFO("cannot remove RR scheduling module\n"); + else + IP_VS_INFO("RR scheduling module unloaded.\n"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/ip_vs_wlc.c b/net/ipv4/ip_vs_wlc.c new file mode 100644 index 000000000000..6a1df60da195 --- /dev/null +++ b/net/ipv4/ip_vs_wlc.c @@ -0,0 +1,167 @@ +/* + * IPVS: Weighted Least-Connection Scheduling module + * + * Version: $Id: ip_vs_wlc.c,v 1.2 1999/07/09 12:10:57 wensong Exp $ + * + * Authors: Wensong Zhang + * Peter Kese + * + * 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. + * + * Changes: + * + */ + +#include +#include +#ifdef CONFIG_KMOD +#include +#endif +#include +#include +#include +#include +#ifdef CONFIG_IP_MASQUERADE_MOD +#include +#endif +#include +#include +#include + + +static int ip_vs_wlc_init_svc(struct ip_vs_service *svc) +{ + MOD_INC_USE_COUNT; + return 0; +} + + +static int ip_vs_wlc_done_svc(struct ip_vs_service *svc) +{ + MOD_DEC_USE_COUNT; + return 0; +} + + +/* + * Weighted Least Connection scheduling + */ +static struct ip_masq* ip_vs_wlc_schedule(struct ip_vs_service *svc, + struct iphdr *iph) +{ + struct ip_masq *ms; + struct ip_vs_dest *dest, *least; + int loh, doh; + const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]); + + IP_VS_DBG("ip_vs_wlc_schedule(): Scheduling...\n"); + + if (svc->destinations == NULL) return NULL; + + /* + * The number of connections in TCP_FIN state is + * dest->refcnt - dest->connections -1 + * We think the overhead of processing active connections is fifty + * times than that of conncetions in TCP_FIN in average. (This fifty + * times might be not accurate, we will change it later.) We use + * the following formula to estimate the overhead: + * dest->connections*49 + dest->refcnt + * and the load: + * (dest overhead) / dest->weight + * + * Remember -- no floats in kernel mode!!! + * The comparison of h1*w2 > h2*w1 is equivalent to that of + * h1/w1 > h2/w2 + * if every weight is larger than zero. + */ + + least = svc->destinations; + loh = atomic_read(&least->connections)*49 + atomic_read(&least->refcnt); + + /* + * Find the destination with the least load. + */ + for (dest = least->next; dest; dest = dest->next) { + doh = atomic_read(&dest->connections)*49 + atomic_read(&dest->refcnt); + if (loh*dest->weight > doh*least->weight) { + least = dest; + loh = doh; + } + } + + IP_VS_DBG("The selected server: connections %d refcnt %d weight %d " + "overhead %d\n", atomic_read(&least->connections), + atomic_read(&least->refcnt), least->weight, loh); + + /* + * Create a masquerading entry. + */ + ms = ip_masq_new_vs(iph->protocol, + iph->daddr, portp[1], + least->addr, least->port, + iph->saddr, portp[0], + 0); + if (ms == NULL) { + IP_VS_ERR("ip_masq_new failed\n"); + return NULL; + } + + /* + * Bind the masq entry with the vs dest. + */ + ip_vs_bind_masq(ms, least); + + IP_VS_DBG("Masq fwd:%c s:%s c:%lX:%x v:%lX:%x d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(ms), ip_masq_state_name(ms->state), + ntohl(ms->daddr),ntohs(ms->dport), + ntohl(ms->maddr),ntohs(ms->mport), + ntohl(ms->saddr),ntohs(ms->sport), + ms->flags, atomic_read(&ms->refcnt)); + + return ms; +} + + +static struct ip_vs_scheduler ip_vs_wlc_scheduler = { + NULL, /* next */ + "wlc", /* name */ + ATOMIC_INIT(0), /* refcnt */ + ip_vs_wlc_init_svc, /* service initializer */ + ip_vs_wlc_done_svc, /* service done */ + ip_vs_wlc_schedule, /* select a server and create new masq entry */ +}; + + +__initfunc(int ip_vs_wlc_init(void)) +{ + IP_VS_INFO("Initializing WLC scheduling\n"); + return register_ip_vs_scheduler(&ip_vs_wlc_scheduler) ; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + /* module initialization by 'request_module' */ + if(register_ip_vs_scheduler(&ip_vs_wlc_scheduler) != 0) + return -EIO; + + IP_VS_INFO("WLC scheduling module loaded.\n"); + + return 0; +} + +void cleanup_module(void) +{ + /* module cleanup by 'release_module' */ + if(unregister_ip_vs_scheduler(&ip_vs_wlc_scheduler) != 0) + IP_VS_INFO("cannot remove WLC scheduling module\n"); + else + IP_VS_INFO("WLC scheduling module unloaded.\n"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/ip_vs_wrr.c b/net/ipv4/ip_vs_wrr.c new file mode 100644 index 000000000000..f44f08ecbe71 --- /dev/null +++ b/net/ipv4/ip_vs_wrr.c @@ -0,0 +1,196 @@ +/* + * IPVS: Weighted Round-Robin Scheduling module + * + * Version: $Id: ip_vs_wrr.c,v 1.2 1999/07/09 12:13:16 wensong Exp $ + * + * Authors: Wensong Zhang + * + * 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. + * + * Changes: + * + */ + +#include +#include +#ifdef CONFIG_KMOD +#include +#endif +#include +#include +#include +#include +#ifdef CONFIG_IP_MASQUERADE_MOD +#include +#endif +#include +#include +#include + +/* + * current destination pointer for weighted round-robin scheduling + */ +struct ip_vs_wrr_mark { + struct ip_vs_dest *cdest; /* current destination pointer */ + int cw; /* current weight */ +}; + + +static int ip_vs_wrr_init_svc(struct ip_vs_service *svc) +{ + /* + * Allocate the mark variable for WRR scheduling + */ + svc->sched_data = kmalloc(sizeof(struct ip_vs_wrr_mark), GFP_ATOMIC); + + if (svc->sched_data == NULL) { + IP_VS_ERR("ip_vs_wrr_init_svc(): no memory\n"); + return ENOMEM; + } + memset(svc->sched_data, 0, sizeof(struct ip_vs_wrr_mark)); + + MOD_INC_USE_COUNT; + return 0; +} + + +static int ip_vs_wrr_done_svc(struct ip_vs_service *svc) +{ + /* + * Release the mark variable + */ + kfree_s(svc->sched_data, sizeof(struct ip_vs_wrr_mark)); + + MOD_DEC_USE_COUNT; + return 0; +} + + +int ip_vs_wrr_max_weight(struct ip_vs_dest *destinations) +{ + struct ip_vs_dest *dest; + int weight = 0; + + for (dest=destinations; dest; dest=dest->next) { + if (dest->weight > weight) + weight = dest->weight; + } + + return weight; +} + + +/* + * Weighted Round-Robin Scheduling + */ +static struct ip_masq* ip_vs_wrr_schedule(struct ip_vs_service *svc, + struct iphdr *iph) +{ + struct ip_masq *ms; + const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]); + struct ip_vs_wrr_mark *mark = svc->sched_data; + struct ip_vs_dest *dest; + + IP_VS_DBG("ip_vs_wrr_schedule(): Scheduling...\n"); + + if (svc->destinations == NULL) return NULL; + + /* + * This loop will always terminate, because 0cwcdest == NULL) { + mark->cdest = svc->destinations; + mark->cw--; + if (mark->cw <= 0) { + mark->cw = ip_vs_wrr_max_weight(svc->destinations); + /* + * Still zero, which means no availabe servers. + */ + if (mark->cw == 0) { + IP_VS_INFO("ip_vs_wrr_schedule(): no available servers\n"); + return NULL; + } + } + } + else mark->cdest = mark->cdest->next; + + if(mark->cdest && (mark->cdest->weight >= mark->cw)) + break; + } + + dest = mark->cdest; + + /* + * Create a masquerading entry. + */ + ms = ip_masq_new_vs(iph->protocol, + iph->daddr, portp[1], + dest->addr, dest->port, + iph->saddr, portp[0], + 0); + if (ms == NULL) { + IP_VS_ERR("ip_masq_new failed\n"); + return NULL; + } + + /* + * Bind the masq entry with the vs dest. + */ + ip_vs_bind_masq(ms, dest); + + IP_VS_DBG("Masq fwd:%c s:%s c:%lX:%x v:%lX:%x d:%lX:%x flg:%X cnt:%d\n", + ip_vs_fwd_tag(ms), ip_masq_state_name(ms->state), + ntohl(ms->daddr),ntohs(ms->dport), + ntohl(ms->maddr),ntohs(ms->mport), + ntohl(ms->saddr),ntohs(ms->sport), + ms->flags, atomic_read(&ms->refcnt)); + + return ms; +} + + +static struct ip_vs_scheduler ip_vs_wrr_scheduler = { + NULL, /* next */ + "wrr", /* name */ + ATOMIC_INIT(0), /* refcnt */ + ip_vs_wrr_init_svc, /* service initializer */ + ip_vs_wrr_done_svc, /* service done */ + ip_vs_wrr_schedule, /* select a server and create new masq entry */ +}; + + +__initfunc(int ip_vs_wrr_init(void)) +{ + IP_VS_INFO("Initializing WRR scheduling\n"); + return register_ip_vs_scheduler(&ip_vs_wrr_scheduler) ; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + /* module initialization by 'request_module' */ + if(register_ip_vs_scheduler(&ip_vs_wrr_scheduler) != 0) + return -EIO; + + IP_VS_INFO("WRR scheduling module loaded.\n"); + + return 0; +} + +void cleanup_module(void) +{ + /* module cleanup by 'release_module' */ + if(unregister_ip_vs_scheduler(&ip_vs_wrr_scheduler) != 0) + IP_VS_INFO("cannot remove WRR scheduling module\n"); + else + IP_VS_INFO("WRR scheduling module unloaded.\n"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b67bfd827713..935fcb08dd6d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.175.2.8 1999/08/08 08:43:20 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.175.2.9 1999/08/12 15:34:26 davem Exp $ * * IPv4 specific functions * @@ -1778,13 +1778,12 @@ do_time_wait: case TCP_TW_ACK: tcp_v4_send_ack(skb, ((struct tcp_tw_bucket *)sk)->snd_nxt, ((struct tcp_tw_bucket *)sk)->rcv_nxt); - break; + goto discard_it; case TCP_TW_RST: goto no_tcp_socket; default: goto discard_it; } - return 0; } static void __tcp_v4_rehash(struct sock *sk) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 834e8f28c65e..fc3f239d730d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.104.2.6 1999/08/08 08:43:23 davem Exp $ + * $Id: tcp_ipv6.c,v 1.104.2.7 1999/08/12 15:34:32 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -1465,14 +1465,12 @@ do_time_wait: case TCP_TW_ACK: tcp_v6_send_ack(skb, ((struct tcp_tw_bucket *)sk)->snd_nxt, ((struct tcp_tw_bucket *)sk)->rcv_nxt); - break; + goto discard_it; case TCP_TW_RST: goto no_tcp_socket; default: goto discard_it; } - - return 0; } static int tcp_v6_rebuild_header(struct sock *sk) diff --git a/net/netsyms.c b/net/netsyms.c index 6d63862b9e57..08ce05029985 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -426,6 +427,14 @@ EXPORT_SYMBOL(init_trdev); EXPORT_SYMBOL(tr_freedev); #endif +#ifdef CONFIG_NET_FC +EXPORT_SYMBOL(fc_setup); +EXPORT_SYMBOL(register_fcdev); +EXPORT_SYMBOL(unregister_fcdev); +EXPORT_SYMBOL(init_fcdev); +EXPORT_SYMBOL(fc_freedev); +#endif + /* Device callback registration */ EXPORT_SYMBOL(register_netdevice_notifier); EXPORT_SYMBOL(unregister_netdevice_notifier); -- 2.39.5