diff -u --recursive --new-file v2.1.124/linux/CREDITS linux/CREDITS --- v2.1.124/linux/CREDITS Mon Oct 5 13:13:34 1998 +++ linux/CREDITS Thu Oct 8 17:25:16 1998 @@ -93,7 +93,7 @@ S: Canada N: Ralf Baechle -E: ralf@gnu.ai.mit.edu +E: ralf@gnu.org P: 1024/AF7B30C1 CF 97 C2 CC 6D AE A7 FE C8 BA 9C FC 88 DE 32 C3 D: Linux/MIPS port D: Linux/68k hacker @@ -566,15 +566,11 @@ S: USA N: Jim Freeman -E: jfree@caldera.com +E: jfree@sovereign.org W: http://www.sovereign.org/ D: Initial GPL'd Frame Relay driver D: Dynamic PPP devices D: Sundry modularizations (PPP, IPX, ...) and fixes -S: Caldera, Inc. -S: 240 West Center Street -S: Orem, Utah 84059-1920 -S: USA N: Bob Frey E: bobf@advansys.com @@ -1383,7 +1379,7 @@ D: improved memory detection code. N: Vojtech Pavlik -E: vojtech@atrey.karlin.mff.cuni.cz +E: vojtech@ucw.cz D: Joystick driver D: arcnet-hardware readme D: Minor ARCnet hacking diff -u --recursive --new-file v2.1.124/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.124/linux/Documentation/Configure.help Mon Oct 5 13:13:35 1998 +++ linux/Documentation/Configure.help Thu Oct 8 17:25:16 1998 @@ -225,7 +225,8 @@ If you say Y here, you will use the full-featured IDE driver to control up to four IDE interfaces, each being able to serve a "master" and a "slave" device, for a total of up to eight IDE - disk/cdrom/tape/floppy drives. + disk/cdrom/tape/floppy drives. People with SCSI-only systems + should say 'N' here. Useful information about large (>540MB) IDE disks, sound card IDE ports, module support, and other topics, is contained in @@ -256,6 +257,8 @@ more detailed information, read the Disk-HOWTO, available via FTP (user: anonymous) from ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO. + People with SCSI-only systems can say 'N' here. + Use old disk-only driver on primary interface CONFIG_BLK_DEV_HD_IDE There are two drivers for MFM/RLL/IDE disks. Most people use just @@ -270,11 +273,13 @@ addresses. Normally, just say N here; you will then use the new driver for all 4 interfaces. + People with SCSI-only systems can say 'N' here. + Include IDE/ATA-2 DISK support CONFIG_BLK_DEV_IDEDISK This will include enhanced support for MFM/RLL/IDE hard disks. If you have a MFM/RLL/IDE disk, and there is no special reason to use the - old hard disk driver instead, say Y. + old hard disk driver instead, say Y. If all your disks are SCSI, say 'N'. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -313,7 +318,7 @@ CONFIG_BLK_DEV_IDETAPE If you have an IDE tape drive using the ATAPI protocol, say Y. ATAPI is a newer protocol used by IDE tape and CDROM drives, similar - to the SCSI protocol. + to the SCSI protocol (but say 'N' if your tape is a SCSI device). If you say Y here, the tape drive will be identified at boot time along with other IDE devices, as "hdb" or "hdc", or something @@ -363,7 +368,7 @@ If both this SCSI emulation and native ATAPI support are compiled into the kernel, the native support will be used. - If unsure, say N. + People with SCSI-only systems should say 'N' here. If unsure, say N. CMD640 chipset bugfix/support CONFIG_BLK_DEV_CMD640 @@ -387,7 +392,9 @@ The CMD640 chip is also used on add-in cards by Acculogic, and on the "CSA-6400E PCI to IDE controller" that some people have. For - details, read Documentation/ide.txt. If unsure, say Y. + details, read Documentation/ide.txt. + + People with SCSI-only systems should say 'N' here. If unsure, say Y. CMD640 enhanced support CONFIG_BLK_DEV_CMD640_ENHANCED @@ -405,14 +412,18 @@ severe data corruption under many conditions. Say Y here to include code which automatically detects and corrects the problem under Linux. This may slow disk throughput by a few percent, but at least - things will operate 100% reliably. If unsure, say Y. + things will operate 100% reliably. + + People with SCSI-only systems can say 'N' here. If unsure, say Y. Generic PCI IDE chipset support CONFIG_BLK_DEV_IDEPCI Enable this for PCI systems which use IDE drive(s). This option helps the IDE driver to automatically detect and configure all PCI-based IDE interfaces in your system. - It is safe to say Y to this question. + + People with SCSI-only systems should say 'N' here. It is safe to + say 'Y' to this question. Generic PCI bus-master DMA support CONFIG_BLK_DEV_IDEDMA @@ -468,6 +479,8 @@ chipsets. Most of these also require special kernel boot parameters to actually turn on the support at runtime. + People with SCSI-only systems should say 'N' here. + Generic 4 drives/port support CONFIG_BLK_DEV_4DRIVES Certain older chipsets, including the Tekram 690CD, use a @@ -1479,13 +1492,6 @@ display that complies with the generic VGA standard. Virtually everyone wants that. Say Y. -Use 64KB of VGA video RAM (aka: Maximum VGA Scrollback) -CONFIG_VGA_GET_64KB - Use 64K rather than 32K of video RAM. This doesn't actually work - on all "VGA" controllers. If your vga card can do it, then you - can say Y here to get more scrollback buffer (shift-pgup) on VGA - consoles. - Video mode selection support CONFIG_VIDEO_SELECT This enables support for text mode selection on kernel startup. If @@ -1507,20 +1513,26 @@ hardware. It represents the frame buffer of some video hardware and allows application software to access the graphics hardware through a well-defined interface, so the software doesn't need to know - anything about the low-level (hardware register) stuff. This works - across the different architectures supported by Linux and makes the - implementation of application programs easier and more portable; at - this point, an X server exists which uses the frame buffer device - exclusively. + anything about the low-level (hardware register) stuff. + Frame buffer devices work identically across the different + architectures supported by Linux and make the implementation of + application programs easier and more portable; at this point, an X + server exists which uses the frame buffer device exclusively. + On several non-X86 architectures, the frame buffer device is the + only way to use the graphics hardware. + The device is accessed through special device nodes, usually located in the /dev directory, i.e. /dev/fb*. - Please read the file Documentation/fb/framebuffer.txt for more - information. - - If you want to play with it, say Y here and to the driver for your - graphics board, below. If unsure, say N. + You need an utility program called fbset to make full use of frame + buffer devices. Please read the file + Documentation/fb/framebuffer.txt for more information. + + If you want to play with it, say Y here and also to the driver for + your graphics board, below. If unsure, say N, unless you are + compiling a kernel for a non-X86 architecture, in which case you + should say Y. Acorn VIDC support CONFIG_FB_ACORN @@ -1559,33 +1571,34 @@ Amiga CyberVision support CONFIG_FB_CYBER - This enables support for the Cybervision 64 graphics card from Phase5. - Please note that its use is not all that intuitive (i.e. if you have - any questions, be sure to ask!). Say N unless you have a Cybervision - 64 or plan to get one before you next recompile the kernel. - Please note that this driver DOES NOT support the Cybervision 64 3D - card, as they use incompatible video chips. + This enables support for the Cybervision 64 graphics card from + Phase5. Please note that its use is not all that intuitive (i.e. if + you have any questions, be sure to ask!). Say N unless you have a + Cybervision 64 or plan to get one before you next recompile the + kernel. Please note that this driver DOES NOT support the + Cybervision 64 3D card, as they use incompatible video chips. Amiga CyberVision3D support (EXPERIMENTAL) CONFIG_FB_VIRGE - This enables support for the Cybervision 64/3D graphics card from Phase5. - Please note that its use is not all that intuitive (i.e. if you have - any questions, be sure to ask!). Say N unless you have a Cybervision - 64/3D or plan to get one before you next recompile the kernel. - Please note that this driver DOES NOT support the older Cybervision 64 - card, as they use incompatible video chips. + This enables support for the Cybervision 64/3D graphics card from + Phase5. Please note that its use is not all that intuitive (i.e. if + you have any questions, be sure to ask!). Say N unless you have a + Cybervision 64/3D or plan to get one before you next recompile the + kernel. Please note that this driver DOES NOT support the older + Cybervision 64 card, as they use incompatible video chips. Amiga RetinaZ3 support (EXPERIMENTAL) CONFIG_FB_RETINAZ3 - This enables support for the Retina Z3 graphics card. Say N unless you - have a Retina Z3 or plan to get one before you next recompile the kernel. + This enables support for the Retina Z3 graphics card. Say N unless + you have a Retina Z3 or plan to get one before you next recompile + the kernel. Amiga CLgen driver (EXPERIMENTAL) CONFIG_FB_CLGEN - This enables support for Cirrus Logic GD542x/543x based boards on Amiga: - SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum. Say N - unless you have such a graphics board or plan to get one before you next - recompile the kernel. + This enables support for Cirrus Logic GD542x/543x based boards on + Amiga: SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum. + Say N unless you have such a graphics board or plan to get one + before you next recompile the kernel. Atari native chipset support CONFIG_FB_ATARI @@ -1686,13 +1699,14 @@ Virtual Frame Buffer support (ONLY FOR TESTING!) CONFIG_FB_VIRTUAL - This is a `virtual' frame buffer device. It operates on a chunk of - unswapable kernel memory instead of on the memory of a graphics board. - This means you cannot see any output sent to this frame buffer device, - while it does consume precious memory. The main use of this frame - buffer device is testing and debugging the frame buffer subsystem. Do - NOT enable it for normal systems! To protect the innocent, it has to - be enabled explicitly on boot time using the kernel option `video=vfb:'. + This is a `virtual' frame buffer device. It operates on a chunk of + unswapable kernel memory instead of on the memory of a graphics + board. This means you cannot see any output sent to this frame + buffer device, while it does consume precious memory. The main use + of this frame buffer device is testing and debugging the frame + buffer subsystem. Do NOT enable it for normal systems! To protect + the innocent, it has to be enabled explicitly at boot time using the + kernel option `video=vfb:'. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). @@ -1709,12 +1723,12 @@ drivers. Note that they are used for text console output only; they are NOT needed for graphical applications. - If you do not enable this option, the needed low level drivers are - automatically enabled, depending on what frame buffer devices you - selected. This is recommended for most users. + If you say N here, the needed low level drivers are automatically + enabled, depending on what frame buffer devices you selected above. + This is recommended for most users. - If you enable this option, you have more fine-grained control over which - low level drivers are enabled. You can e.g. leave out low level drivers + If you say Y here, you have more fine-grained control over which low + level drivers are enabled. You can e.g. leave out low level drivers for color depths you do not intend to use for text consoles. Low level frame buffer console drivers can be modules ( = code which @@ -1788,13 +1802,13 @@ Mac variable bpp packed pixels support CONFIG_FBCON_MAC This is the low level frame buffer console driver for 1/2/4/8/16/32 - bits per pixel packed pixels on Mac. It supports variable fontwidths + bits per pixel packed pixels on Mac. It supports variable font widths for low resolution screens. VGA characters/attributes support CONFIG_FBCON_VGA - This is the low level frame buffer console driver for VGA text mode, as - used by vgafb. + This is the low level frame buffer console driver for VGA text mode; + it is used if you said Y to "VGA chipset support (text only)" above. Parallel-port support CONFIG_PARPORT @@ -2182,7 +2196,7 @@ server". This makes the local computers think they are talking to the remote end, while in fact they are connected to the local proxy. Redirection is activated by defining special input firewall - rules (using the ipfwadm utility) and/or by doing an appropriate + rules (using the ipchains utility) and/or by doing an appropriate bind() system call. IP: masquerading @@ -2825,12 +2839,12 @@ Serial port KISS driver for AX.25 CONFIG_MKISS - KISS is a protocol used to send IP traffic over AX.25 radio - connections, somewhat similar to SLIP for telephone lines. Say Y - here if you intend to send Internet traffic over amateur radio, - using some device connected to your machine's serial port. In that - case, you also have to say Y to "Amateur Radio AX.25 Level 2" - support. + KISS is a protocol used for the exchange of data between a computer + and a Terminal Node Controller (a small embedded system commonly + used for networking over AX25 amateur radio connections). + Although KISS is less advanced than the 6pack protocol, it has + the advantage that it is already supported by most modern TNCs + without the need for a firmware upgrade. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -5549,6 +5563,19 @@ module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. +Novell/Eagle/Microdyne NE3210 EISA support +CONFIG_NE3210 + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in + ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO. Note that this driver + will NOT WORK for NE3200 cards as they are completely different. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called ne3210.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. + Apricot Xen-II on board Ethernet CONFIG_APRICOT If you have a network (Ethernet) controller of this type, say Y and @@ -5666,6 +5693,12 @@ module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. +VIA Rhine support +CONFIG_VIA_RHINE + If you have a VIA "rhine" based network card (Rhine-I (3043) or + Rhine-2 (VT86c100A)), say Y here. To build this driver as a module + say M. + Zenith Z-Note support CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network @@ -8028,21 +8061,94 @@ The module will be called nvram.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -PC joystick support +Joystick support CONFIG_JOYSTICK - If you have a PC compatible analog or digital joystick, you can say - Y here. If you then create a character special file under /dev with - major number 15 and minor number 0 or 1 (for the two joystick ports) - using mknod ("man mknod"), you can read the status of the buttons - and the x and y coordinates from that file. Please read the file + If you have a joystick, you can say Y here to enable generic + joystick support. You will also need to say Y or M to at least one + of the hardware specific joystick drivers. This will make the + joysticks available under /dev/jsX devices. Please read the file Documentation/joystick.txt which contains more information and the - location of the joystick package that you'll need. + location of the joystick package that you'll need. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called joystick.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +Classic PC analog joysticks +CONFIG_JOY_ANALOG + Say Y here if you have an analog joystick or gamepad that connects + to the PC gameport. This supports many different types, including + joysticks with throttle control, and with CHF / FCS / 6-button + extensions. For more information on how to use the driver please + read Documentation/joystick.txt + +FPGaming and MadCatz A3D controllers +CONFIG_JOY_ASSASIN + Say Y here if you have an FPGaming Assasin 3D, MadCatz Panther or + MadCatz Panther XL. For more information on how to use the driver + please read Documentation/joystick.txt + +Gravis GrIP gamepads +CONFIG_JOY_GRAVIS + Say Y here if you have a Gravis GamePad Pro. For more information + on how to use the driver please read Documentation/joystick.txt + +PDPI Lightning L4 gamecards +CONFIG_JOY_LIGHTNING + Say Y here if you have a PDPI Lightning L4 gamecard and an analog + joystick or gamepad connected to it. For more information on how to + use the driver please read Documentation/joystick.txt + +Logitech Digital joysticks +CONFIG_JOY_LOGITECH + Say Y here if you have a Logitech WingMan Extreme Digital, + Logitech ThunderPad Digital or Logitech CyberMan 2. For more + information on how to use the driver please read + Documentation/joystick.txt + +Microsoft SideWinder, Genius Digital joysticks +CONFIG_JOY_SIDEWINDER + Say Y here if you have a Microsoft SideWinder 3d Pro, Microsoft + SideWinder Precision Pro, Microsoft SideWinder Force Feedback Pro, + Microsoft Sidewinder GamePad or Genius Flight2000 F-23 Digital. For + more information on how to use the driver please read + Documentation/joystick.txt + +ThrustMaster DirectConnect (BSP) joysticks +CONFIG_JOY_THRUSTMASTER + Say Y here if you have a ThrustMaster Millenium 3D Inceptor, ThrustMaster + 3D Rage Pad, or ThrustMaster WCS III. For more information on how to use the + driver please read Documentation/joystick.txt + +NES, SNES, PSX, Multisystem gamepads +CONFIG_JOY_CONSOLE + Say Y here if you have a Nintendo Entertainment System gamepad, + Super Nintendo Entertainment System gamepad, Sony PlayStation + gamepad or a Multisystem - Atari, Amiga, Commodore, Amstrad CPC + joystick. For more information on how to use the driver please read + Documentation/joystick.txt + +Sega, Multisystem gamepads +CONFIG_JOY_DB9 + Say Y here if you have a Sega Master System gamepad, Sega Genesis + gamepad, Sega Saturn gamepad, or a Multisystem - Atari, Amiga, + Commodore, Amstrad CPC joystick. For more information on how to use + the driver please read Documentation/joystick.txt + +TurboGraFX Multisystem joystick interface +CONFIG_JOY_TURBOGRAFX + Say Y here if you have the TurboGraFX interface by Steffen Schwenke, and + want to use it with Multiststem - Atari, Amiga, Commodore, Amstrad CPC + joystick. For more information on how to use the driver please read + Documentation/joystick-parport.txt + +Amiga joysticks +CONFIG_JOY_AMIGA + Say Y here if you have an Amiga with a digital joystick connected + to it. For more information on how to use the driver please read + Documentation/joystick.txt + Atomwide Serial Support CONFIG_ATOMWIDE_SERIAL If you have an Atomwide Serial card for an Acorn system, say Y to @@ -8988,15 +9094,6 @@ and CD32. If you intend to run Linux on any of these systems, say Y; otherwise say N. -Amiga Cybervision support -CONFIG_FB_CYBER - This enables support for the Cybervision 64 graphics card from Phase5. - Please note that its use is not all that intuitive (i.e. if you have - any questions, be sure to ask!). Say N unless you have a Cybervision - 64 or plan to get one before you next recompile the kernel. - Please note that this driver DOES NOT support the Cybervision 64 3D - card at present, as they use incompatible video chips. - Amiga GSP (TMS340x0) support CONFIG_AMIGA_GSP Include support for Amiga graphics cards that use the Texas @@ -9124,6 +9221,11 @@ 1260 accelerator, and the optional SCSI module, say Y. Otherwise, say N. +Blizzard PowerUP 603e+ SCSI support +CONFIG_BLZ603EPLUS_SCSI + If you have an Amiga 1200 with a Phase5 Blizzard PowerUP 603e+ + accelerator, say Y. Otherwise, say N. + Fastlane SCSI support CONFIG_FASTLANE_SCSI If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use @@ -9165,6 +9267,16 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called ariadne.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + +Ariadne II support +CONFIG_ARIADNE2 + If you have a VillageTronics Ariadne II Ethernet adapter, say Y. + Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). The module is called ariadne2.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. A2065 support diff -u --recursive --new-file v2.1.124/linux/Documentation/fb/internals.txt linux/Documentation/fb/internals.txt --- v2.1.124/linux/Documentation/fb/internals.txt Wed Jun 24 22:54:01 1998 +++ linux/Documentation/fb/internals.txt Mon Oct 5 13:39:20 1998 @@ -2,7 +2,7 @@ This is a first start for some documentation about frame buffer device internals. -Geert Uytterhoeven , 10 June 1998 +Geert Uytterhoeven , 21 July 1998 -------------------------------------------------------------------------------- @@ -68,14 +68,13 @@ True color (FB_VISUAL_TRUECOLOR) -------------------------------- -The pixel value is broken up into red, green, and blue fields, each of which -are looked up in separate red, green, and blue lookup tables. +The pixel value is broken up into red, green, and blue fields. Direct color (FB_VISUAL_DIRECTCOLOR) ------------------------------------ -The pixel value is broken up into red, green, and blue fields. This is what -most people mean when then say `true color'. +The pixel value is broken up into red, green, and blue fields, each of which +are looked up in separate red, green, and blue lookup tables. Grayscale displays diff -u --recursive --new-file v2.1.124/linux/Documentation/fb/vesafb.txt linux/Documentation/fb/vesafb.txt --- v2.1.124/linux/Documentation/fb/vesafb.txt Mon Oct 5 13:13:35 1998 +++ linux/Documentation/fb/vesafb.txt Mon Oct 5 13:39:20 1998 @@ -79,20 +79,17 @@ by comma. Accepted options: invers - no comment... - -redraw - scroll by redrawing the affected part of the screen. - This is the default. +redraw - scroll by redrawing the affected part of the screen ypan - enable display panning using the VESA protected mode interface. This enables the Shift-PgUp scrollback thing and greatly speeds up fullscreen scrolling. It is slower than "redraw" when scrolling only a halve - screen. Seems not to work with some BIOSes. + screen. This is the default. ywrap - If your gfx board supports wrap-around, use this one instead of ypan. - -vgapal - Use the standard vga registers for palette changes. - This is the default. -pmipal - Use the protected mode interface for palette changes. +nopal - Don't use the protected mode interface for palette + changes. vesafb will try the standard vga registers + instead. Have fun! diff -u --recursive --new-file v2.1.124/linux/Documentation/i386/zero-page.txt linux/Documentation/i386/zero-page.txt --- v2.1.124/linux/Documentation/i386/zero-page.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i386/zero-page.txt Mon Oct 5 12:59:25 1998 @@ -0,0 +1,73 @@ +Summary of empty_zero_page layout (kernel point of view) + ( collected by Hans Lermen and Martin Mares ) + +The contents of empty_zero_page are used to pass parameters from the +16-bit realmode code of the kernel to the 32-bit part. References/settings +to it mainly are in: + + arch/i386/boot/setup.S + arch/i386/boot/video.S + arch/i386/kernel/head.S + arch/i386/kernel/setup.c + + +Offset Type Description +------ ---- ----------- + 0 32 bytes struct screen_info, SCREEN_INFO + ATTENTION, overlaps the following !!! + 2 unsigned short EXT_MEM_K, extended memory size in Kb (from int 0x15) + 0x20 unsigned short CL_MAGIC, commandline magic number (=0xA33F) + 0x22 unsigned short CL_OFFSET, commandline offset + Address of commandline is calculated: + 0x90000 + contents of CL_OFFSET + (only taken, when CL_MAGIC = 0xA33F) + 0x40 20 bytes struct apm_bios_info, APM_BIOS_INFO + 0x80 16 bytes hd0-disk-parameter from intvector 0x41 + 0x90 16 bytes hd1-disk-parameter from intvector 0x46 + + 0xa0 16 bytes System description table truncated to 16 bytes. + ( struct sys_desc_table_struct ) + 0xb0 - 0x1df Free. Add more parameters here if you really need them. + +0x1e0 unsigned long ALT_MEM_K, alternative mem check, in Kb +0x1f1 char size of setup.S, number of sectors +0x1f2 unsigned short MOUNT_ROOT_RDONLY (if !=0) +0x1f4 unsigned short size of compressed kernel-part in the + (b)zImage-file (in 16 byte units, rounded up) +0x1f6 unsigned short swap_dev (unused AFAIK) +0x1f8 unsigned short RAMDISK_FLAGS +0x1fa unsigned short VGA-Mode (old one) +0x1fc unsigned short ORIG_ROOT_DEV (high=Major, low=minor) +0x1ff char AUX_DEVICE_INFO + +0x200 short jump to start of setup code aka "reserved" field. +0x202 4 bytes Signature for SETUP-header, ="HdrS" +0x206 unsigned short Version number of header format + Current version is 0x0201... +0x208 8 bytes (used by setup.S for communication with boot loaders, + look there) +0x210 char LOADER_TYPE, = 0, old one + else it is set by the loader: + 0xTV: T=0 for LILO + 1 for Loadlin + 2 for bootsect-loader + 3 for SYSLINUX + 4 for ETHERBOOT + V = version +0x211 char loadflags: + bit0 = 1: kernel is loaded high (bzImage) + bit7 = 1: Heap and pointer (see below) set by boot + loader. +0x212 unsigned short (setup.S) +0x214 unsigned long KERNEL_START, where the loader started the kernel +0x218 unsigned long INITRD_START, address of loaded ramdisk image +0x21c unsigned long INITRD_SIZE, size in bytes of ramdisk image +0x220 4 bytes (setup.S) +0x224 unsigned short setup.S heap end pointer +0x226 - 0x7ff setup.S code. + +0x800 string, 2K max COMMAND_LINE, the kernel commandline as + copied using CL_OFFSET. + Note: this will be copied once more by setup.c + into a local buffer which is only 256 bytes long. + ( #define COMMAND_LINE_SIZE 256 ) diff -u --recursive --new-file v2.1.124/linux/Documentation/joystick-api.txt linux/Documentation/joystick-api.txt --- v2.1.124/linux/Documentation/joystick-api.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/joystick-api.txt Thu Oct 8 17:25:16 1998 @@ -0,0 +1,308 @@ + Joystick API Documentation -*-Text-*- + + Ragnar Hojland Espinosa + + + 7 Aug 1998 + + +1. Initialization +~~~~~~~~~~~~~~~~~ + +Open the joystick device following the usual semantics (that is, with open). +Since the driver now reports events instead of polling for changes, +immediately after the open it will issue a series of synthetic events +(JS_EVENT_INIT) that you can read to check the initial state of the +joystick. + +By default, the device is opened in blocking mode. + + int fd = open ("/dev/js0", O_RDONLY); + + +2. Event Reading +~~~~~~~~~~~~~~~~ + + struct js_event e; + read (fd, &e, sizeof(struct js_event)); + +where js_event is defined as + + struct js_event { + __u32 time; /* event timestamp in miliseconds */ + __s16 value; /* value */ + __u8 type; /* event type */ + __u8 number; /* axis/button number */ + }; + +If the read is successfull, it will return sizeof(struct js_event), unless +you wanted to read more than one event per read as described in section 3.1. + + +2.1 js_event.type +~~~~~~~~~~~~~~~~~ + +The possible values of ``type'' are + + #define JS_EVENT_BUTTON 0x01 /* button pressed/released */ + #define JS_EVENT_AXIS 0x02 /* joystick moved */ + #define JS_EVENT_INIT 0x80 /* initial state of device */ + +As mentioned above, the driver will issue synthetic JS_EVENT_INIT ORed +events on open. That is, if it's issuing a INIT BUTTON event, the +current type value will be + + int type = JS_EVENT_BUTTON | JS_EVENT_INIT; /* 0x81 */ + +If you choose not to differentiate between synthetic or real events +you can turn off the JS_EVENT_INIT bits + + type &= ~JS_EVENT_INIT; /* 0x01 */ + + +2.2 js_event.number +~~~~~~~~~~~~~~~~~~~ + +The values of ``number'' correspond to the axis or button that +generated the event. Note that they carry separate numeration (that +is, you have both an axis 0 and a button 0). Generally, + + number + 1st Axis X 0 + 1st Axis Y 1 + 2nd Axis X 2 + 2nd Axis Y 3 + ...and so on + +Hats vary from one joystick type to another. Some can be moved in 8 +directions, some only in 4, however, the driver always reports a hat as two +independent axis, even if the hardware doesn't allow independent movement. + + +2.3 js_event.value +~~~~~~~~~~~~~~~~~~ + +For an axis, ``value'' is a signed integer between -32767 and +32767 +representing the position of the joystick along that axis. If you +don't read a 0 when the joystick is `dead', or if it doesn't span the +full range, you should recalibrate (with, for example, jscal). + +For a button, ``value'' for a press button event is 1 and for a release +button event is 0. + +Though this + + if (js_event.type == JS_EVENT_BUTTON) { + buttons_state ^= (1 << js_event.number); + } + +may work well if you handle JS_EVENT_INIT events separately, + + if (js_event.type & ~JS_EVENT_INIT == JS_EVENT_BUTTON) { + if (js_event.value) + buttons_state |= (1 << js_event.number); + else + buttons_state &= ~(1 << js_event.number); + } + +is much safer since it can't lose sync with the driver. As you would +have to write a separate handler for JS_EVENT_INIT events in the first +snippet, this ends up being shorter. + + +2.4 js_event.time +~~~~~~~~~~~~~~~~~ +The time an event was generated is stored in ``js_event.time''. It's a time +in miliseconds since ... well, since sometime in the past. This eases the +task of detecting double clicks, figuring out if movement of axis and button +presses happened at the same time, and similar. + + +3. Reading +~~~~~~~~~~ + +If you open the device in blocking mode, a read will block (that is, +wait) forever until an event is generated and effectively read. There +are two alternatives if you can't afford to wait forever (which is, +admittedly, a long time;) + + a) use select to wait until there's data to be read on fd, or + until it timeouts. There's a good example on the select(2) + man page. + + b) open the device in non-blocking mode (O_NONBLOCK) + + +3.1 O_NONBLOCK +~~~~~~~~~~~~~~ + +If read returns -1 when reading in O_NONBLOCK mode, this isn't +necessarily a "real" error (check errno(3)); it can just mean there +are no events pending to be read on the driver queue. You should read +all events on the queue (that is, until you get a -1). + +For example, + + while (1) { + while (read (fd, &e, sizeof(struct js_event)) > 0) { + process_event (e); + } + /* EAGAIN is returned when the queue is empty */ + if (errno != EAGAIN) { + /* error */ + } + /* do something interesting with processed events */ + } + +One reason for emptying the queue is that if it gets full you'll start +missing events since the queue is finite, and older events will get +overwritten. + +The other reason is that you want to know all what happened, and not +delay the processing till later. + +Why can get the queue full? Because you don't empty the queue as +mentioned, or because too much time elapses from one read to another +and too many events to store in the queue get generated. Note that +high system load may contribute to space those reads even more. + +If time between reads is enough to fill the queue and loose an event, +the driver will switch to startup mode and next time you read it, +synthetic events (JS_EVENT_INIT) will be generated to inform you of +the actual state of the joystick. + +[As for version 1.2.8, the queue is circular and able to hold 64 + events. You can increment this size bumping up JS_BUFF_SIZE in + joystick.h and recompiling the driver.] + + +In the above code, you might as well want to read more than one event +at a time using the typical read(2) functionality. For that, you would +replace the read above with something like + + struct js_event mybuffer[0xff]; + int i = read (fd, mybuffer, sizeof(struct mybuffer)); + +In this case, read would return -1 if the queue was empty, or some +other value in which the number of events read would be i / +sizeof(js_event) Again, if the buffer was full, it's a good idea to +process the events and keep reading it until you empty the driver queue. + + +4. IOCTLs +~~~~~~~~~ + +The joystick driver defines the following ioctl(2) operations. + + /* function 3rd arg */ + #define JSIOCGAXES /* get number of axes char */ + #define JSIOCGBUTTONS /* get number of buttons char */ + #define JSIOCGVERSION /* get driver version int */ + #define JSIOCGNAME(len) /* get identifier string char */ + #define JSIOCSCORR /* set correction values &js_corr */ + #define JSIOCGCORR /* get correction values &js_corr */ + +For example, to read the number of axes + + char number_of_axes; + ioctl (fd, JSIOCGAXES, &number_of_axes); + + +4.1 JSIOGCVERSION +~~~~~~~~~~~~~~~~~ + +JSIOGCVERSION is a good way to check in run-time whether the running +driver is 1.0+ and supports the event interface. If it is not, the +IOCTL will fail. For a compile-time decision, you can test the +JS_VERSION symbol + + #ifdef JS_VERSION + #if JS_VERSION > 0xsomething + +4.2 JSIOCGNAME +~~~~~~~~~~~~~~ + +JSIOCGNAME(len) allows you to get the name string of the joystick - the same +as is being printed at boot time. The 'len' argument is the lenght of the +buffer provided by the application asking for the name. It is used to avoid +possible overrun should the name be too long. + + char name[128]; + if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) + strncpy(name, "Unknown", sizeof(name)); + printf("Name: %s\n", name); + +4.3 JSIOC[SG]CORR +~~~~~~~~~~~~~~~~~ + +For usage on JSIOC[SG]CORR I suggest you to look into jscal.c They are +not needed in a normal program, only in joystick calibration software +such as jscal or kcmjoy. These IOCTLs and data types aren't considered +to be in the stable part of the API, and therefore may change without +warning in following releases of the driver. + +Both JSIOCSCORR and JSIOCGCORR expect &js_corr to be able to hold +information for all axis. That is, struct js_corr corr[MAX_AXIS]; + +struct js_corr is defined as + + struct js_corr { + __s32 coef[8]; + __u16 prec; + __u16 type; + }; + +and ``type'' + + #define JS_CORR_NONE 0x00 /* returns raw values */ + #define JS_CORR_BROKEN 0x01 /* broken line */ + + +5. Backward compatibility +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The 0.x joystick driver API is quite limited and its usage is deprecated. +The driver offers backward compatibility, though. Here's a quick summary: + + struct JS_DATA_TYPE js; + while (1) { + if (read (fd, &js, JS_RETURN) != JS_RETURN) { + /* error */ + } + usleep (1000); + } + +As you can figure out from the example, the read returns immediately, +with the actual state of the joystick. + + struct JS_DATA_TYPE { + int buttons; /* immediate button state */ + int x; /* immediate x axis value */ + int y; /* immediate y axis value */ + }; + +and JS_RETURN is defined as + + #define JS_RETURN sizeof(struct JS_DATA_TYPE) + +To test the state of the buttons, + + first_button_state = js.buttons & 1; + second_button_state = js.buttons & 2; + +The axis values do not have a defined range in the original 0.x driver, +except for that the values are non-negative. The 1.2.8+ drivers use a +fixed range for reporting the values, 0 being the minimum, 128 the +center, and 255 maximum value. + + +6. Final Notes +~~~~~~~~~~~~~~ + +____/| Comments, additions, and specially corrections are welcome. +\ o.O| Documentation valid for at least version 1.2.8 of the joystick + =(_)= driver and as usual, the ultimate source for documentation is + U to "Use The Source Luke" or, at your convenience, Vojtech ;) + + - Ragnar +EOF diff -u --recursive --new-file v2.1.124/linux/Documentation/joystick-parport.txt linux/Documentation/joystick-parport.txt --- v2.1.124/linux/Documentation/joystick-parport.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/joystick-parport.txt Thu Oct 8 17:25:16 1998 @@ -0,0 +1,515 @@ + Linux Joystick parport drivers v1.2 BETA + (c) 1998 Vojtech Pavlik + (c) 1998 Andree Borrmann +---------------------------------------------------------------------------- + +0. Disclaimer +~~~~~~~~~~~~~ + Any information in this file is provided as-is, without any guarantee that +it will be true. So, use it at your own risk. The possible damages that can +happen include burning your parallel port, and/or the sticks and joystick +and maybe even more. Like when a lightning kills you it is not our problem. + +1. Intro +~~~~~~~~ + The joystick parport drivers are used for joysticks and gamepads not +originally designed for PCs and other computers Linux runs on. Because of +that, PCs usually lack the right ports to connect these devices to. Parallel +port, because of its ability to change single bits at will, and providing +both output and input bits is the most suitable port on the PC for +connecting such devices. + +2. Devices supported +~~~~~~~~~~~~~~~~~~~~ + Many console and 8-bit coputer gamepads and joysticks are supported. The +following subsections discuss usage of each. + +2.1 NES and SNES +~~~~~~~~~~~~~~~~ + The Nintendo Entertainment System and Super Nintendo Entertainment System +gamepads are widely available, and easy to get. Also, they are quite easy to +connect to a PC, and don't need much processing speed (108 us for NES and +165 us for SNES, compared to about 1000 us for PC gamepads) to communicate +with them. + + All NES and SNES use the same synchronous serial protocol, clocked from +the computer's side (and thus timing insensitive). To allow up to 5 NES +and/or SNES gamepads connected to the parallel port at once, the output +lines of the parallel port are shared, while one of 5 available input lines +is assigned to each gamepad. + + This protocol is handled by the joy-console.c driver, so that's the one +you'll use for NES and SNES gamepads. + + The main problem with PC parallel ports is that they don't have +5V power +source on any of their pins. So, if you want a reliable source of power +for your pads, use either keyboard or joystick port, and make a pass-through +cable. + + If you want to use the parallel port only, you can take the power is from +some data pin. For most gamepad and parport implementations only one pin is +needed, and I'd recommend pin 9 for that, the highest data bit. On the other +hand, if you are not planning to use anything else than NES / SNES on the +port, anything between and including pin 4 and pin 9 will work. + +(pin 9) -----> Power + + Unfortunately, there are pads that need a lot more of power, and parallel +ports that can't give much current through the data pins. If this is your +case, you'll need to use diodes (as a prevention of destroying your parallel +port), and combine the currents of two or more data bits together. + + Diodes +(pin 9) ----|>|-------+------> Power + | +(pin 8) ----|>|-------+ + | +(pin 7) ----|>|-------+ + | + : + | +(pin 4) ----|>|-------+ + + Ground is quite easy. On PC's parallel port the ground is on any of the +pins from pin 18 to pin 25. So use any pin of these you like for the ground. + +(pin 18) -----> Ground + + NES and SNES pads have two input bits, Clock and Latch, which drive the +serial transfer. These are connected to pins 2 and 3 of the parallel port, +respectively. + +(pin 2) -----> Clock +(pin 3) -----> Latch + + And the last thing is the NES / SNES data wire. Only that isn't shared and +each pad needs its own data pin. The parallel port pins are: + +(pin 10) -----> Pad 1 data +(pin 11) -----> Pad 2 data +(pin 12) -----> Pad 3 data +(pin 13) -----> Pad 4 data +(pin 15) -----> Pad 5 data + + Note that pin 14 is not used, since it is not an input pin on the parallel +port. + + This is everything you need on the PC's side of the connection, now on to +the gamepads side. The NES and SNES have different connectors. Also, there +are quite a lot of NES clones, and because Nintendo used proprietary +connectors for their machines, the cloners couldn't and used standard D-Cannon +connectors. Anyway, if you've got a gamepad, and it has buttons A, B, Turbo +A, Turbo B, Select and Start, and is connected through 5 wires, then it is +either a NES or NES clone and will work with this connection. SNES gamepads +also use 5 wires, but have more buttons. They will work as well, of course. + +Pinout for NES gamepads Pinout for SNES gamepads + + +----> Power +-----------------------\ + | 7 | o o o o | x x o | 1 + 5 +---------+ 7 +-----------------------/ + | x x o \ | | | | | + | o o o o | | | | | +-> Ground + 4 +------------+ 1 | | | +------------> Data + | | | | | | +---------------> Latch + | | | +-> Ground | +------------------> Clock + | | +----> Clock +---------------------> Power + | +-------> Latch + +----------> Data + +Pinout for NES clone (db9) gamepads Pinout for NES clone (db15) gamepads + + +---------> Clock +-----------------> Data + | +-------> Latch | +---> Ground + | | +-----> Data | | + | | | ___________________ + _____________ 8 \ o x x x x x x o / 1 + 5 \ x o o o x / 1 \ o x x o x x o / + \ x o x o / 15 `~~~~~~~~~~~~~' 9 + 9 `~~~~~~~' 6 | | | + | | | | +----> Clock + | +----> Power | +----------> Latch + +--------> Ground +----------------> Power + +2.2 Multisystem joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~ + In the era of 8-bit machines, there was something like de-facto standard +for joystick ports. They were all digital, and all used D-Cannon 9 pin +connectors (db9). Because of that, a single joystick could be used without +hassle on Atari (130, 800XE, 800XL, 2600, 7200), Amiga, Commodore C64, +Amstrad CPC, Sinclair ZX Spectrum and many other machines. That's why these +joysticks are called "Multisystem". + + Now their pinout: + + +---------> Right + | +-------> Left + | | +-----> Down + | | | +---> Up + | | | | + _____________ +5 \ x o o o o / 1 + \ x o x o / + 9 `~~~~~~~' 6 + | | + | +----> Button + +--------> Ground + + However, as time passed, extension to this standard developed, and these +were not compatible with each other: + + + Atari 130, 800(XL/XE) MSX + + +-----------> Power + +---------> Right | +---------> Right + | +-------> Left | | +-------> Left + | | +-----> Down | | | +-----> Down + | | | +---> Up | | | | +---> Up + | | | | | | | | | + _____________ _____________ +5 \ x o o o o / 1 5 \ o o o o o / 1 + \ x o o o / \ o o o o / + 9 `~~~~~~~' 6 9 `~~~~~~~' 6 + | | | | | | | + | | +----> Button | | | +----> Button 1 + | +------> Power | | +------> Button 2 + +--------> Ground | +--------> Output 3 + +----------> Ground + + Amstrad CPC Commodore C64 + + +-----------> Analog Y + +---------> Right | +---------> Right + | +-------> Left | | +-------> Left + | | +-----> Down | | | +-----> Down + | | | +---> Up | | | | +---> Up + | | | | | | | | | + _____________ _____________ +5 \ x o o o o / 1 5 \ o o o o o / 1 + \ x o o o / \ o o o o / + 9 `~~~~~~~' 6 9 `~~~~~~~' 6 + | | | | | | | + | | +----> Button 1 | | | +----> Button + | +------> Button 2 | | +------> Power + +--------> Ground | +--------> Ground + +----------> Analog X + + And there were many others. + +2.2.1 Multisystem joysticks using joy-db9.c +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + For the Multisystem joysticks, and their derivatives, the joy-db9.c driver +was written. It allows only one joystick / gamepad per parallel port, but +the interface is easy to build and works with almost anything. + + For the basic 1-button Multisystem joystick you connect its wires to the +parallel port like this: + +(pin 1) -----> Power +(pin 18) -----> Ground + +(pin 2) -----> Up +(pin 3) -----> Down +(pin 4) -----> Left +(pin 5) -----> Right +(pin 6) -----> Button 1 + + However, if the joystick is switch based (eg. clicks when you move it), +you might or might not, depending on your parallel port, need 10 kOhm pullup +resistors on each of the direction and button signals, like this: + +(pin 2) ------------+------> Up + Resistor | +(pin 1) --[10kOhm]--+ + + Try without, and if it doesn't work, add them. For TTL based joysticks / +gamepads the pullups are not needed. + + For joysticks with two buttons you connect the second button to pin 7 on +the parallel port. + +(pin 7) -----> Button 2 + +2.2.1 Multisystem joysticks using joy-console.c +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + For some people just one joystick per parallel port is not enough, and/or +want to use them on one parallel port together with NES/SNES/PSX pads. This +is possible using the joy-console.c. It supports up to 5 devices of the +above types, including 1 and 2 buttons Multisystem joysticks. + + However, there is nothing for free. To allow more sticks to be used at +once, you need the sticks to be purely switch based (that is non-TTL), and +not to need power. Just a plain simple six switches inside. If your +joystick can do more (eg. turbofire) you'll need to disable it totally first +if you want to use joy-console.c. + + Also, the connection is a bit more complex. You'll need a bunch of diodes, +and one pullup resistor. First, you connect the Directions and the button +the same as for joy-db9, however with the diodes inbetween. + + Diodes +(pin 2) -----|<|----> Up +(pin 3) -----|<|----> Down +(pin 4) -----|<|----> Left +(pin 5) -----|<|----> Right +(pin 6) -----|<|----> Button 1 + + For two button sticks you also connect the other button. + +(pin 7) -----|<|----> Button 2 + + And finally, you connect the Ground wire of the joystick, like done in +this little schematic to Power and Data on the parallel port, as described +for the NES / SNES pads in section 2.1 of this file - that is, one data pin +for each joystick. The power source is shared. + +Data ------------+-----> Ground + Resistor | +Power --[10kOhm]--+ + + And that's all, here we go! + +2.2.3 Multisystem joysticks using joy-turbografx.c +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The TurboGraFX interface, designed by + + Steffen Schwenke + + allows up to 7 Multisystem joysticks connected to the parallel port. In +Steffen's version, there is support for up to 5 buttons per joystick. +However, since this doesn't work reliably on all parallel ports, the +joy-turbografx.c driver supports only one button per joystick. For more +information on how to build the interface, see + + http://www2.burg-halle.de/~schwenke/parport.html + +2.3 Sony Playstation +~~~~~~~~~~~~~~~~~~~~ + + WARNING: PSX support is experimental, and at the moment doesn't seem to +work for most people. If you like adventure, you can try yourself. + + The PSX controller is supported by the joy-console.c. + +Pinout of the PSX controller: + + +---------+---------+---------+ +9 | o o o | o o o | o o o | 1 parallel + \________|_________|________/ port pins + | | | | | | + | | | | | +--------> Clock --- (1) + | | | | +------------> Select --- (17) + | | | +---------------> Power --- (16) + | | +------------------> Ground --- (18-25) + | +-------------------------> Command --- (14) + +----------------------------> Data --- (10,11,12,13,15) one only... + + You may have to add pull up/down resistors. Maybe your pad also won't like +the 5V (PSX uses 3.7V). + + Currently the driver supports only ONE psx pad and only one type of +controller: The normal PSX controller. NEGCON support is planned for the +next release. ANALOG controller may be too (I do not recommend to connect +the "force feedback"/"rumble pack" version... it (may) use too much +power...) + +2.4 Sega +~~~~~~~~ + All the Sega controllers are more or less based on the standard 2-button +Multisystem joystick. However, since they don't use switches and use TTL +logic, the only driver useable with them is the joy-db9.c driver. + +2.4.1 Sega Master System +~~~~~~~~~~~~~~~~~~~~~~~~ + The SMS gamepads are almost exactly the same as normal 2-button +Multisystem joysticks. Set the driver to Multi2 mode, use the corresponding +parallel port pins, and the following schematic: + + +-----------> Power + | +---------> Right + | | +-------> Left + | | | +-----> Down + | | | | +---> Up + | | | | | + _____________ +5 \ o o o o o / 1 + \ o o x o / + 9 `~~~~~~~' 6 + | | | + | | +----> Button 1 + | +--------> Ground + +----------> Button 2 + +2.4.2 Sega Genesis aka MegaDrive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The Sega Genesis (in Europe sold as Sega MegaDrive) pads are an extension +to the Sega Master System pads. They use more buttons (3+1, 5+1, 6+1). Use +the following schematic: + + +-----------> Power + | +---------> Right + | | +-------> Left + | | | +-----> Down + | | | | +---> Up + | | | | | + _____________ +5 \ o o o o o / 1 + \ o o o o / + 9 `~~~~~~~' 6 + | | | | + | | | +----> Button 1 + | | +------> Select + | +--------> Ground + +----------> Button 2 + + The Select pin goes to pin 14 on the parallel port. + +(pin 14) -----> Select + + The rest is the same as for Multi2 joysticks using joy-db9.c + +2.4.3 Sega Saturn +~~~~~~~~~~~~~~~~~ + Sega Saturn has eight buttons, and to transfer that, without hacks like +Genesis 6 pads use, it needs one more select pin. Anyway, it is still +handled by the joy-db9.c driver. Its pinout is very different from anything +else. Use this schematic: + + +-----------> Select 1 + | +---------> Power + | | +-------> Up + | | | +-----> Down + | | | | +---> Ground + | | | | | + _____________ +5 \ o o o o o / 1 + \ o o o o / + 9 `~~~~~~~' 6 + | | | | + | | | +----> Select 2 + | | +------> Right + | +--------> Left + +----------> Power + + Select 1 is pin 14 on the parallel port, Select 2 is pin 16 on the +parallel port. + +(pin 14) -----> Select 1 +(pin 16) -----> Select 2 + + The other pins (Up, Down, Right, Left, Power, Ground) are the same as for +Multi joysticks using joy-db9.c + +3. The drivers +~~~~~~~~~~~~~~ + There are three drivers for the parallel port interfaces. Each, as +described above, allows to connect a different group of joysticks and pads. +Here are described their command lines: + +3.1 joy-console.c +~~~~~~~~~~~~~~~~~ + Using joy-console.c you can connect up to five devices to one parallel +port. It uses the following kernel/module command line: + + js_console=port,pad1,pad2,pad3,pad4,pad5 + + Where 'port' is either the address of the parallel port the joystick/pad +is connected to (eg. 0x378), or, if you are using the parport driver of 2.1+ +Linux kernels, the number of the parport interface (eg. 0 for parport0). + + And 'pad1' to 'pad5' are pad types connected to different data input pins +(10,11,12,13,15), as described in section 2.1 of this file. + + The types are: + + Type | Joystick/Pad + -------------------- + 0 | None + 1 | SNES pad + 2 | NES pad + 4 | Multisystem 1-button joystick + 5 | Multisystem 2-button joystick + 6 | Sony PSX controller + + The exact type of the PSX controller type is autoprobed, so you must have +your controller plugged in before initializing. + + Should you want to use more than one of parallel ports at once, you can +use js_console_2 and js_db9_3 as additional command line parameters for two +more parallel ports. + + Changes: + v0.1 : First version (SNES only) + v0.2 : X/Y directions were exchanged... + v0.3 : Adaptation for kernel 2.1 + v0.4 : Adaptation for joystick-1.2.6 + - added open/close callbacks + v0.5 : Renamed to "joy-console" because I have added + PSX controller support. + v0.6 : NES support + v0.7V: Added "multi system" support + v0.8 : Bugfixed PSX driver... + v0.9V: Changed multi system support + Added Multi2 support + Fixed parport handling + Cleaned up + v0.10: Fixed PSX buttons 7 and 8 + +3.2 joy-db9.c +~~~~~~~~~~~~~ + Apart from making an interface, there is nothing difficult on using the +joy-db9.c driver. It uses the following kernel/module command line: + + js_db9=port,type + + Where 'port' is either the address of the parallel port the joystick/pad +is connected to (eg. 0x378), or, if you are using the parport driver of 2.1+ +Linux kernels, the number of the parport interface (eg. 0 for parport0). + + 'Type' is the type of joystick or pad attached: + + Type | Joystick/Pad + -------------------- + 0 | None + 1 | Multisystem 1-button joystick + 2 | Multisystem 2-button joystick + 3 | Genesis pad (3+1 buttons) + 5 | Genesis pad (5+1 buttons) + 6 | Genesis pad (6+1 buttons) + 7 | Saturn pad + + Should you want to use more than one of these joysticks/pads at once, you +can use js_db9_2 and js_db9_3 as additional command line parameters for two +more joysticks/pads. + + Changes: + v0.1 : First version + v0.2 : Changed kernel parameter format + v0.3V: Added Sega Saturn support + Fixed parport and PS/2 mode handling + Cleaned up + +3.3 joy-turbografx.c +~~~~~~~~~~~~~~~~~~~~ + The joy-turbografx.c driver uses a very simple kernel/module command line: + + js_tg=port,count + + Where 'port' is either the address of the parallel port the interface is +connected to (eg. 0x378), or, if you are using the parport driver of 2.1+ +Linux kernels, the number of the parport interface (eg. 0 for parport0). + + 'Count' is the number of Multisystem, 1-button joysticks connected to the +interface. + + Should you want to use more than one of these interfaces at once, you can +use js_tg_2 and js_tg_3 as additional command line parameters for two more +interfaces. + + Changes: + v0.1V: First version + +3.4 End +~~~~~~~ + That's all, folks! Have fun! diff -u --recursive --new-file v2.1.124/linux/Documentation/joystick.txt linux/Documentation/joystick.txt --- v2.1.124/linux/Documentation/joystick.txt Thu May 14 19:47:37 1998 +++ linux/Documentation/joystick.txt Thu Oct 8 17:25:16 1998 @@ -1,213 +1,566 @@ - PC Joystick driver v1.0.9 - (c) 1997 Vojtech Pavlik + Linux Joystick driver v1.2.11 BETA + (c) 1996-1998 Vojtech Pavlik ---------------------------------------------------------------------------- +0. Disclaimer +~~~~~~~~~~~~~ + This is a BETA release. It probably will work well for you, but it doesn't +have to. + + 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 + + Should you need to contact me, the author, you can do so either by e-mail +- mail your message to , or by paper mail: Vojtech Pavlik, +Ucitelska 1576, Prague 8, 182 00 Czech Republic + + For your convenience, the GNU General Public Licens version 2 is included +in the package: See the file COPYING. + 1. Intro ~~~~~~~~ - The PC Joystick driver for Linux provides support for analog (variable -resistor based) and digital (switch based) joysticks connected via the PC -game port. It can support up to 2 joysticks. + The joystick driver for Linux provides support for a variety of joysticks +and similar devices. + + These currently include various analog joysticks (both variable resistor +based and microswitch+resistor based), following IBM PC joystick standard, +with extensions like additional hats and buttons compatible with CH +Flightstick Pro, ThrustMaster FCS or PC 6 or 8 button gamepads. + + In addition to these it also supports some of the new PC joysticks that +use porprietary digital protocols to communicate over the gameport, +currently by FPGaming, Genius, Gravis, Logitech, MadCatz and Microsoft. +ThrustMaster and Creative Labs protocols are in the works. + + The driver also includes support for many gamepads and joysticks that were +used by various non-PC computers and game consoles. These include Multi +system joysticks (Atari, Amiga, Commodore, Amstrad), Sega gamepads (Master +System, Genesis, Saturn), Nintendo gamepads (NES, SNES), Sony gamepads (PSX). +Support for N64, Atari Jaguar, Atari 2600, NES FourScore, SNES MultiTap, +Pegasus, PSX NegCon and others might be added later. + + Last, but not least there is also native Amiga joystick support for the +Amiga linux port. Should you encounter any problems while using the driver, or joysticks this driver can't make complete use of, I'm very interested in hearing about them. Bug reports and success stories are also welcome. -2. Usage -~~~~~~~~ - If you enable the joystick driver in the kernel configuration, all -connected joysticks should be found automatically. The driver can detect -standard two-axis two-button, and three-axis four-button joysticks only. If -that isn't your case, you can pass the joystick driver the following kernel -command line arguments: - -js=0xXX,0xYY - - Where XX and YY are bit masks for the two joysticks, with the bits -representing which buttons and axes of the joystick are present: - -Bit | Explanation ------------------ - 0 | Axis 0 - 1 | Axis 1 - 2 | Axis 2 - 3 | Axis 3 - 4 | Button 0 - 5 | Button 1 - 6 | Button 2 - 7 | Button 3 - - Another method of using the driver is loading it as a module. For that, -select `M' for this driver in the kernel configuration and insert the -module: - -insmod joystick.o js=0xXX,0xYY - - To enable autoloading/-unloading of the joystick module, you have to add -these lines to /etc/conf.modules: - -alias char-major-15 joystick -options joystick js=0xXX,0xYY + The joystick package is available at the following FTP site: - To enable the user space programs to read the joystick device, you have to -create the device files using mknod (man mknod for more info): + ftp://atrey.karlin.mff.cuni.cz/pub/linux/joystick/joystick-1.2.x.tar.gz -mknod /dev/js0 c 15 0 -mknod /dev/js1 c 15 1 + And a homepage of the driver is at: -3. Calibration -~~~~~~~~~~~~~~ - As of version 1.0 the calibration routines used in the joystick driver are -worth using. The idea of calibration is that you have to calibrate the -joystick only once, and then set the calibration at boot-time, thus removing -the need of re-calibrating it in each program that uses it. + http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/ - For calibration, use the jscal program, contained in the joystick package -which is available at: + There is also a mailing list for the driver at: -ftp://atrey.karlin.mff.cuni.cz/pub/linux/joystick/joystick-1.0.9.tar.gz + listproc@atrey.karlin.mff.cuni.cz -4. Programming Interface +send "subscribe linux-joystick Your Name" to subscribe to it. + +2. Usage +~~~~~~~~ + To use the driver as a standalone module, without patching the kernel, you +first need to edit the Makefile to meet your needs (namely whether you use +MODVERSIONS). Then you compile the driver by typing + + make + + And after compiling you first + + insmod joystick.o + + and then + + insmod joy-something.o + + where 'something' is the type of your joystick. See below for more precise +explanation. Of course, you can insmod more than one hardware specific +module at once, if you have more than one joystick installed. + + If you want to have the driver compiled into your kernel, you first need +to patch the kernel. For that, use either kernel-2.0.35.patch, or +kernel-2.1.115.patch files, that are included in the joystick package, and +using the command + + patch -Esp1 < /usr/src/joystick-1.2.x/kernel-2.x.y.patch + +in + + /usr/src/linux + +apply the patch to your kernel. To compile joystick support into the kernel, +use the kernel configuration scripts, and answer 'Y' to Joystick support and +also to at least one of the hardware specific options. + + After patching the kernel you can also compile it as a module, answering +'M' to all joystick support you want to have modules for. It is possible to +have the main joystick driver compiled into the kernel and the hardware +dependent drivers as modules. + + After you're done with installation of the driver itself, you'll need to +create the joystick device files in /dev so that applications can use them. +This is done by typing + + make devs + +in the joystick driver's directory. For manual creation of the joystick +devices, check the Documentation/devices.txt file in the Linux source tree. + +2.1 Analog joysticks +~~~~~~~~~~~~~~~~~~~~ + The joy-analog.c uses the standard analog inputs of the gameport, and +thus supports all standard joysticks. However the only types that can be +autodetected are: + +* 2-axis, 4-button joystick +* 3-axis, 4-button joystick +* Two 2-axis, 2-buttons joysticks on an Y-cable + + For other joystick types (more/less axes, hats, and buttons) support +you'll need to specify the types either on the kernel command line or on the +module command line, when inserting joy-analog.o into the kernel. The +parameters are: + + js_an=p0,m0,n0,p1,m1,n1 ... + + Where 'p' is the port number, eg. 0x201, which is the standard address. +'m' and 'n' are joystick 0 and joystick 1 bitmasks for the specified +joystick port. The bits in the bitmasks mean: + + Bit | 2^n | Meaning + ---------------------------------- + 0 | 1 | Axis X1 + 1 | 2 | Axis Y1 + 2 | 4 | Axis X2 + 3 | 8 | Axis Y2 + 4 | 16 | Button A + 5 | 32 | Button B + 6 | 64 | Button C + 7 | 128 | Button D + 8 | 256 | CHF Buttons X and Y + 9 | 512 | CHF Hat 1 + 10 | 1024 | CHF Hat 2 + 11 | 2048 | FCS Hat + 12 | 4096 | PXY Button X + 13 | 8192 | PXY Button Y + 14 | 16384 | PXY Button U + 15 | 32768 | PXY Button V + +(CHF = CH Flightstick Pro, FCS = ThrustMaster FCS, PXY - Pad with 6 or 8 buttons) + + Following is a table of joysticks for which the 'm' values are known. If +you have any additions/corrections to it, e-mail me. + + Joystick | 'm' value + ---------------------------------------------------- + Genius Flight2000 F-12 | 0x00f3 + Genius Flight2000 F-21 | 0x08f7 + Genius Flight2000 F-22 | 0x02ff + Genius GameHunter G-06 | 0xf0f3 + Genius MaxFire G-07 | 0xf0f3 + Genius PowerStation | 0xf0f3 + Laing #1 PC SuperPad | 0xf0f3 + Microsoft SideWinder Standard | 0x003b + QuickShot QS-201 SuperWarrior | 0x00fb + + In case you have one of the joystick in the table below, and it doesn't +work with a specific driver in digital mode for some reason, you can use +them in analog mode with the joy-analog driver as well. However, digital +operation is always better. + + Joystick | 'm' value + ---------------------------------------------------- + Gravis GamePad Pro - analog mode | 0x00f3 + Genius Flight2000 F-23 - CHF mode | 0x02ff + Genius Flight2000 F-23 - FCS mode | 0x08f7 + Microsoft SideWinder 3D Pro - CHF mode | 0x02ff + Microsoft SideWinder 3D Pro - FCS mode | 0x08f7 + + An example that would configure the driver to use two two axis, two button +joysticks connected to port 0x201, a single four button four axis joystick +connected to port 0x202, a four axis, six button and two hat CHF compatible +joystick on 0x203, and a two axis four button FCS compatible joystick with a +single hat on 0x207: + + js_an=0x201,0x33,0xcc,0x202,0xff,0,0x203,0x7ff,0,0x207,0x8f3,0 + + If you can't sum bits into hex numbers in your head easily, you can simply +sum the values in the 2^n column decimally and use that number instead. +Using this method you'd get a command line: + + js_an=0x201,51,204,0x202,255,0,0x203,2047,0,0x207,2291,0 + + And it would do the same as the above explained command line. Use +whichever way you like best. + +2.2 Microsoft SideWinder and Genius Digital joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SideWinder and Genius Digital joysticks are supported by the +joy-sidewinder.c module. All currently supported joysticks: + +* SideWinder 3d Pro +* SideWinder Precision Pro +* SideWinder Force Feedback Pro +* SideWinder Game Pad (up to four, chained together) +* Genius Flight2000 Digital F-23 + + are autodetected, and thus no module parameters are needed. + + The SideWinder Standard is not a digital joystick, and thus is supported +by the analog driver described above. SideWinder FreeStyle Pro and +SideWinder Force Feedback Wheel are not supported yet. + +2.3 Logitech Digital joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Logitech Digital joysticks are supported by the joy-logitech.c module. It +currently supports these devices: + +* Logitech Wingman Extreme Digital +* Logitech CyberMan 2 +* Logitech ThunderPad Digital + + All three are autodetected, and thus no parameters to the module are +needed. + + Logitech Wingman is not a digital joystick and is handled by the analog +driver described above. Logitech Wingman Warrior communicates through a +serial port and is not supported yet. Logitech Wingman Force, Wingman +Formula, Wingman Formula Force, Wingman Gamepad, Wingman Interceptor are USB +joysticks, with optional serial port connection, and are not supported yet. + +2.4 Gravis GrIP +~~~~~~~~~~~~~~~ + Gravis GrIP gamepads are supported by the joy-gravis.c module. It +currently supports only: + +* Gravis GamePad Pro +* Gravis Xterminator + + Both the pads are be autodetected, and you can even use any combination of +up to two these pads either chained together or using an Y-cable on a single +gameport. + + Gravis Blackhawk Digital, and GrIP MultiPort pads are not supported yet. +GrIP MultiPort support is in the works. Gravis Xcalibur, ArcadeXtreme, +GamePad Pro/M are joysticks/pads that probably never reached mass +production. + +2.5 FPGaming A3D and MadCatz A3D +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The Assasin 3D protocol created by FPGaming, is used both by FPGaming +themselves and is licensed to MadCatz. A3D devices are supported by the +joy-assasin.c module. It currently supports: + +* FPGaming Assasin 3D +* MadCatz Panther +* MadCatz Panther XL + + All these devices are autodetected. Because the Assasin 3D and the Panther +allow connecting analog joysticks to them, these are supported in this +driver, too. The driver uses the js_as parameter for the analog joysticks, +which has the same syntax as js_an for the analog driver. + + The trackball support is far from perfect at this stage of development, +but should be well usable. + +2.6 ThrustMaster DirectConnect (BSP) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The TM DirectConnect (BSP) protocol is supported by the joy-thrustmaster.c +module. It currently supports: + +* ThrustMaster Millenium 3D Inceptor +* ThrustMaster 3D Rage Pad +* ThrustMaster Weapon Control System III + + All these drvices are autodetected, and thus no parameters to the module +are needed. + + The only tested device was the Millenium. Rage Pad might work, and +WCS III won't, because important parts of code for that are missing. + +2.7 PDPI Lightning 4 gamecards +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + PDPI Lightning 4 gamecards are supported by the joy-lightning.c module. +This driver is only for analog joysticks connected to the card - if you want +to use some of the digital devices, you need to use its specific driver. The +card will work in legacy mode with them, though. + + Since all features of analog joysticks can't be detected, this driver +needs a command line: + + js_l4=p0,m0,n0,p1,m1,n1,.... + + As you can see, it's very similar to the analog drivers command line. +Actually it is the same except for the meaning of p0. p0 in this case is the +port the joystick is attached to: + + p | Port + ---------------------------- + 0 | Primary card, port 1 + 1 | Primary card, port 2 + 2 | Primary card, port 3 + 3 | Primary card, port 4 + 4 | Secondary card, port 1 + 5 | Secondary card, port 2 + 6 | Secondary card, port 3 + 7 | Secondary card, port 4 + + Two cards maximum are allowed in one system, because of the card's design. + + See the description of analog joystick driver for explanations of m0 and +n0 values. + +2.8 Amiga +~~~~~~~~~ + Amiga joysticks, connected to an Amiga, are supported by the joy-amiga.c +driver. Since they can't be autodetected, the driver has a command line. + + js_am=a,b + + a and b define the joysticks connected to the JOY0DAT and JOY1DAT ports of +the Amiga. + + Value | Joystick type + --------------------- + 0 | None + 1 | 1-button digital joystick + + No more joystick types are supported now, but that should change in the +future if I get an Amiga in the reach of my fingers. + +2.9 Game console and 8-bit pads and joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +See joystick-parport.txt for more info. + +3. Troubleshooting +~~~~~~~~~~~~~~~~~~ + There is quite a high probability that you run into some problems. For +testing whether the driver works, if in doubt, use the jstest utility in +some of its modes. The most useful modes are "normal" - for the 1.x +interface, and "old" for the "0.x" interface. You run it by typing: + + jstest --normal /dev/js0 + jstest --old /dev/js0 + + If your trouble stems from the fact the drivers can't detect the attached +joystick, and/or you decide you need my help (which I will gladly provide), +please use the joydump utility first. It's created just by typing + + make joydump.o + + in the driver's directory. It is run then by typing + + insmod joydump.o + + in the same directory. It will return a 'device busy' or 'initialization +failed' error. This is perfectly okay. It has already done it's job. The +results can be found in the system log. Please send me the results along +with your problem report. + + Oh, and read the FAQ! :) + +4. FAQ +~~~~~~ + Q: The driver doesn't find any joysticks connected to my soundcard with the +message "joy-something: no joysticks found" and "joy-something.o: +init_module: Device or resource busy." or "Initialization of joy-something +failed" What could be the cause? + A: The most common cause is that the joystick port on your soundcard is +not enabled. If it is an ISA PnP card, you'll need isapnptools to configure +the gameport. Non-PnP cards usually use some option to the sound driver - +see the sound driver docs and source and enable the port. + + Q: Any access to the joystick devices gives me "Operation not supported by +device". What am I doing wrong? + A: You're running a 2.0 kernel and you forgot to insmod the hardware +specific module. You not only need the joystick.o, but also one of the other +joy-*.o files (most usually joy-analog.o), as described in this document, +section 2. If you are not using modules, then you didn't say 'Y' to any of +the hardware-specific questions. Again, see section 2. If you did select +the specific support, and you still get this message, check that you +selected the right one, and if it still doesn't work, go to the previous +FAQ. + + Q: Everything is fine, except I get "No such device" error when I try to +do anything with /dev/js0. What's the cause? + A: You're running a 2.1 kernel and you want to read the previous FAQ. + + Q: Upon 'insmod joystick.o' I get a LOT of unresolved symbols, including +printk and others. Why? + A: You either don't have your kernel compiled with module support. If +that's the cause, re-compile your kernel with module support switched on. +Or, you use versioned symbols, and don't have -DMODVERSIONS in the joystick +driver Makefile, or vice versa. Correct the situation by either removing or +adding -DMODVERSIONS to the Makefile. + + Q: Running 'jstest 1' or 'jscal 1' doesn't work, and returns with "File +not found" error. What is the problem? + A: The command line interface for these tools is different from what +version 0.8.0 used. You have to specify the whole device name, eg. 'jstest +/dev/js0'. + + Q: Running 'jstest /dev/js0' results in "File not found" error. What's the +cause? + A: The device files don't exist. Run 'make devs'. + + Q: Is it possible to connect my old Atari/Commodore/Amiga/console joystick +or pad that uses a 9-pin D-type cannon connector to the serial port of my +PC? + A: Yes, it is possible, but it'll burn your serial port or the pad. It +won't work, of course. + +5. Calibration +~~~~~~~~~~~~~~ + For most joysticks you won't need any manual calibration, since the +joystick should be autocalibrated by the driver automagically. However, with +some analog joysticks, that either do not use linear resistors, or if you +want better precision, you can use the jscal program included in the +joystick package to set better correction coefficients than what the driver +would choose itself. + +6. Programming Interface ~~~~~~~~~~~~~~~~~~~~~~~~ The 1.0 driver uses a new, event based approach to the joystick driver. Instead of the user program polling for the joystick values, the joystick -driver now reports only any changes of its state. See joystick.h and -jstest.c included in the joystick package for more information. The joystick -device can be used in either blocking or nonblocking mode and supports -select() calls. - - For backward compatibility the old interface is still included, but will -be dropped in the future. +driver now reports only any changes of its state. See joystick-api.txt, +joystick.h and jstest.c included in the joystick package for more +information. The joystick device can be used in either blocking or +nonblocking mode and supports select() calls. + + For backward compatibility the old (v0.x) interface is still included. +Any call to the joystick driver using the old interface will return values +that are compatible to the old interface. This interface is still limited +to 2 axes, and applications using it usually decode only 2 buttons, although +the driver provides up to 32. -5. Credits +7. Credits ~~~~~~~~~~ - Thanks to the following authors that contributed to the joystick driver -development: + Thanks to the following people who contributed code, ideas or specifications +to the joystick driver development: - 0.1-0.5 Arthur C. Smith - 0.5 Eyal Lebedinsky - 0.6 Jeff Tranter - 0.7 Carlos Puchol - 0.7.1-0.8 Matt Rhoten - 0.7.3 Dan Fandrich - 0.7.3 Sverker Wilberg - 0.8 Hal Maney - 0.8 Bernd Schmidt - 0.9 Alan Cox - 0.9.0-1.0.9 Vojtech Pavlik - -6. Change Log -~~~~~~~~~~~~~ - The current (1.0.x) version was originally based on the 0.7.3 version of -the joystick driver, which caused some inconsistencies in version numbering. -The following log documents all changes done to the driver by the above -contributors: - -Version 0.1 Original version - Works but lacks multi-joystick support -Version 0.2 Added multi-joystick support (minor 0 and 1) - Added delay between measuring joystick axis - Added scaling ioctl -Version 0.3 Modified scaling to use ints to prevent kernel - panics 8-) -Version 0.4 Linux 0.99.6 and fixed race condition in js_read. - After looking at a schematic of a joystick card - it became apparent that any write to the joystick - port started ALL the joystick one shots. If the - one that we are reading is short enough and the - first one to be read, the second one will return - bad data if its one shot has not expired when - the joystick port is written for the second time. - Thus solves the mystery delay problem in 0.2! -Version 0.5 Upgraded the driver to the 0.99.9 kernel, added - joystick support to the make config options, - updated the driver to return the buttons as - positive logic, and read both axis at once - and added some new ioctls. -Version 0.6 Made necessary changes to work with 0.99.15 - kernel (and hopefully 1.0). Also did some - cleanup: indented code, fixed some typos, wrote - man page, etc ... -Version 0.7 Support for modules -Version 0.7.1 Fix bug in reading button state of js1 - Add include so module compiles under recent kernels -Version 0.7.3 Include directives changed for joystick.h - Separated out joystick detection/counting, cleanup - Fix for detection of 3-axis joysticks - Better detection announcement - Added I/O port registration, cleaned up code -Version 0.8 New read loop - Cleaned up #includes to allow #include of joystick.h with - gcc -Wall and from g++ - Made js_init fail if it finds zero joysticks - General source/comment cleanup - Use of MOD_(INC|DEC)_USE_COUNT - Changes to compile correctly under 1.3 in kernel or as module -Version 0.9 Ported to 2.1.x - Reformatted to resemble Linux coding standard - Removed semaphore bug (we can dump the lot I think) - Fixed xntp timer adjust during joystick timer0 bug - Changed variable names to lower case. Kept binary compatibility. - Better ioctl names. Kept binary compatibility. - Removed 'save_busy'. Just set busy to 1. -Version 0.9.0 Based on 0.7.3 - New read function that allows two axes to have the same value - New joystick calibration code - Real support for 3-axis joysticks - CPU speed independent timeouts - Reads may happen even for unwhole record size => cat /dev/js0 works - Correct error for lseek - /dev/js? can be read simultaneously by several processes -Version 0.9.1 IOCTLs now obey general Linux IOCTL rules ('j' letter assigned) - Use of verify_area result codes - Fuzz correction added - Semaphore and many cli()'s removed - Fix for TurboFire joysticks - read buttons always - Fix for broken joysticks - return with -ENODEV only if joystick - completely disconnected - Fix in read function to allow zero results - Broken line correction added for broken joysticks (eg. JB-500) - Timeouts back separated for easier setting - Some fixes and cleanups in read function -Version 0.9.2 Fixed a typo causing nothing to be working -Version 1.0.0 Event approach started -Version 1.0.1 Complete rewrite - Compiles but doesn't work -Version 1.0.2 Works, many bugs fixed, more yet to come -Version 1.0.3 Tail cutting logic changes & fixes - Fix in js_do_bh - no more zero values for axes - Lost event changes & fixes -Version 1.0.4 Kernel command line & module configuration support - Better cli()/sti() handling - Linux 2.1.25 select => poll changes -Version 1.0.5 Fixes in calibration routines - Better jscal -Version 1.0.6 Backward compatibility with old js driver added - Init value after recalibration bug fixed - Using KERN_* printk() codes - Finally leaving ALPHA and going beta - Cosmetic changes -Version 1.0.7 Readme update - Linux 2.1.89 poll update - Compatibility mode fix - refresh data after open - Cosmetic changes & cleanup -Version 1.0.8 Changing the __u16 data to __s16 in event packet - The range is now -32727..0..32767 - Broken line correction needs 2 coefs less - Fix to report number of buttons correctly - Removed various trailing spaces -Version 1.0.9 Fix for compatibility mode - don't do correction - Fix max number of module parameters + 0.1-0.5 Arthur C. Smith + 0.5 Eyal Lebedinsky + 0.6 Jeff Tranter + 0.7 Carlos Puchol + 0.7.1-0.8 Matt Rhoten + 0.7.3 Dan Fandrich + 0.7.3 Sverker Wilberg + 0.8 Hal Maney + 0.8 Bernd Schmidt + 0.9 Alan Cox + 1.1.0 John Markus Bjorndalen + 1.1.0 Boris Muehmer + 1.1.0 Robert W. Grubbs + 1.1.0 Pete Chown + 1.1.0 Benji York + 1.1.3 Leslie F. Donaldson + 1.2.0 Eng-Jon Ong + 1.2.8 Ragnar Hojland Espinosa + 1.2.3-1.2.9 Andree Borrmann + 1.1.0-1.2.9 Brian Gerst + 0.9.0-1.2.11 Vojtech Pavlik + + If you think you should be in this list and are not, it's possible that +I forgot to include you - contact me and I'll correct the error. :) + + Thanks to KYE Systems Europe, who provided me with driver sources for the +Genius Flight2000 Digital F-23, which happens to be compatible with +Microsoft SideWinder 3d Pro. + + Thanks to ThrustMaster Inc. who provided me with docs for their digital +protocol, and to Trystan A Larey-williams , who wrote an +attempt of a driver for them. + + Thanks to Creative Labs Europe, and Ifor Powell , +who provided me with docs for their first generation gamepad. + + Special thanks go to FP-Gaming, Inc. and James C Barnes , +who provided me with help and detailed information about the Assasin 3D +protocol and devices, and even sent me a Panther and Panther XL for testing, +along with cool T-shirts. + + Special thanks to PDPI, Mike Pelkey and Brand Kvavle +, for providing me with documentation and example +code for their L4 gamecard, and sending me the card to test my driver with +it. + + Thanks to everyone else who helped me develop this package of drivers! + + No thanks to Microsoft, Logitech, and Gravis, who don't release a word +about their hardware .... :( + +8. ChangeLog +~~~~~~~~~~~~ + See the ChangeLog file for the log of changes. -7. To do +9. To do ~~~~~~~~ - Sooner or later I'll get to these: + Sooner or later I'll do these: - Make an optional read routine using Pentium timers for better precision (*) - Support for hats and more buttons ala CH Flightstick (*) - Support for hats ala TM FCS (*) - Include support for MS SideWinder digital mode (*) - Create patches for most common programs using joystick - Support for cards with HW speed compensation - Support for more than one joystick port + Fix possible races at open/close + Fix possible read/timer races + Make the driver SMP-friendly + Support joysticks on Linux/m68k/Atari + Support joysticks on Linux/FM-TOWNS (?) + Support even more joystick types + +10. Current driver status +~~~~~~~~~~~~~~~~~~~~~~~~~ + OK means tested and not touched till this driver revision, unknown means +that the driver was changed since last test, broken means doesn't work, +incomplete means can't work, because vital parts of support are missing. + +joystick.c: 2.1.x kernel interface - OK +joy-amiga.c: Multi1 stick - unknown +joy-analog.c: standard joysticks - OK + FCS hats - OK + CHF hats & buttons - OK + XY buttons - OK + UV buttons - OK +joy-assasin.c: MadCatz Panther XL - OK + MadCatz PXL rudder - OK + MadCatz Panther - OK + FPG Assasin 3D - OK +joy-console.c: NES pad - OK + SNES pad - OK + Multi1 stick - OK + Multi2 stick - OK + PSX - SW OK, HW unreliable +joy-db9.c: Multi1 stick - OK + Multi2 stick - OK + Sega Genesis pad - OK + Sega Genesis 5 pad - OK + Sega Genesis 6 pad - OK + Sega Saturn pad - unknown +joy-gravis.c Gravis GamePad Pro - OK + Gravis Xterminator - OK +joy-lightning.c PDPI Lightning 4 - OK +joy-logitech.c WingMan Extreme Digital - OK + CyberMan 2 - OK + Thunder Pad Digital - unknown +joy-sidewinder.c SW 3D Pro - OK + Genius F-23 - OK + SW GP - OK + SW PP - OK + SW FFP - OK +joy-thrustmaster.c Millenium 3D Inceptor - OK + 3D-Rage pad - unknown + WCS III - incomplete +joy-turbografx.c Multi1 stick - OK - (*) - Already in the development 1.1 version. +Please help me and send me success / failure reports for the drivers, +I need to know what works, and what needs to be debugged. Thank you. diff -u --recursive --new-file v2.1.124/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.124/linux/MAINTAINERS Mon Sep 28 10:51:32 1998 +++ linux/MAINTAINERS Thu Oct 8 16:40:05 1998 @@ -57,8 +57,7 @@ W: Web-page with status/info S: Status, one of the following: - Supported: Someone is actually paid to look after this (wildly - improbable). + Supported: Someone is actually paid to look after this. Maintained: Someone actually looks after it. Odd Fixes: It has a maintainer but they don't have time to do much other than throw the odd patch in. See below.. @@ -142,8 +141,8 @@ S: Maintained AX.25 NETWORK LAYER -P: Jon Naylor -M: jsn@cs.nott.ac.uk +P: Matthias Welwarsky +M: dg2fef@afthd.tu-darmstadt.de L: linux-hams@vger.rutgers.edu S: Maintained @@ -358,6 +357,12 @@ L: autofs@linux.kernel.org S: Maintained +LAPB module +P: Henner Eisen +M: eis@baty.hanse.de +L: linux-x25@vger.rutgers.edu +S: Maintained + LINUX FOR POWERPC (PREP) P: Cort Dougan M: cort@cs.nmt.edu @@ -384,6 +389,12 @@ L: linux-mac68k@wave.lm.com S: As time permits [Michael confess, you are the mac68k maintainer 8)] +M68K ON HP9000/300 +P: Philip Blundell +M: philb@gnu.org +W: http://www.tazenda.demon.co.uk/phil/linux-hp +S: Maintained + MENUCONFIG: P: Michael Elizabeth Chastain M: mec@shout.net @@ -423,8 +434,8 @@ S: Maintained NETROM NETWORK LAYER -P: Jon Naylor -M: jsn@cs.nott.ac.uk +P: Tomi Manninen +M: Tomi.Manninen@hut.fi L: linux-hams@vger.rutgers.edu S: Maintained @@ -529,6 +540,12 @@ L: linux-kernel@vger.rutgers.edu S: Maintained +ROSE NETWORK LAYER +P: Frederic Rible +M: frible@teaser.fr +L: linux-hams@vger.rutgers.edu +S: Maintained + RISCOM8 DRIVER: P: Dmitry Gorodchanin M: pgmdsg@ibi.com @@ -677,6 +694,12 @@ P: Miroslav Zagorac M: zaga@fly.cc.fer.hr L: linux-scsi@vger.rutgers.edu +S: Maintained + +X.25 NETWORK LAYER +P: Henner Eisen +M: eis@baty.hanse.de +L: linux-x25@vger.rutgers.edu S: Maintained Z8530 DRIVER FOR AX.25 diff -u --recursive --new-file v2.1.124/linux/Makefile linux/Makefile --- v2.1.124/linux/Makefile Mon Oct 5 13:13:35 1998 +++ linux/Makefile Tue Oct 6 08:53:46 1998 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 124 +SUBLEVEL = 125 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -35,6 +35,7 @@ AR =$(CROSS_COMPILE)ar NM =$(CROSS_COMPILE)nm STRIP =$(CROSS_COMPILE)strip +OBJDUMP =$(CROSS_COMPILE)objdump MAKE =make GENKSYMS=/sbin/genksyms @@ -155,6 +156,10 @@ DRIVERS := $(DRIVERS) drivers/zorro/zorro.a endif +ifeq ($(CONFIG_FC4),y) +DRIVERS := $(DRIVERS) drivers/fc4/fc4.a +endif + ifdef CONFIG_PPC DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a endif @@ -316,11 +321,10 @@ if [ -f VIDEO_MODULES ]; then inst_mod VIDEO_MODULES video; fi; \ if [ -f FC4_MODULES ]; then inst_mod FC4_MODULES fc4; fi; \ \ - rm -f /tmp/.misc.$$$$ /tmp/.allmods.$$$$; \ - ls *.o > /tmp/.allmods.$$$$; \ - echo $$MODULES | tr ' ' '\n' | sort | comm -23 /tmp/.allmods.$$$$ - > /tmp/.misc.$$$$; \ - if [ -s /tmp/.misc.$$$$ ]; then inst_mod /tmp/.misc.$$$$ misc; fi; \ - rm -f /tmp/.misc.$$$$ /tmp/.allmods.$$$$; \ + ls *.o > $$MODLIB/.allmods; \ + echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \ + if [ -s $$MODLIB/.misc ]; then inst_mod $$MODLIB/.misc misc; fi; \ + rm -f $$MODLIB/.misc $$MODLIB/.allmods; \ ) # modules disabled.... diff -u --recursive --new-file v2.1.124/linux/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c --- v2.1.124/linux/arch/arm/kernel/signal.c Wed Sep 9 14:51:04 1998 +++ linux/arch/arm/kernel/signal.c Thu Oct 8 09:32:22 1998 @@ -21,17 +21,6 @@ #include #include -void checksignals(void) -{ - sigset_t *blocked = ¤t->blocked; - unsigned long mask = blocked->sig[0] | sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT); - mask &= blocked->sig[1]; - if (~mask) { - printk("Bad signal mask\n"); - __backtrace(); - } -} - #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)) diff -u --recursive --new-file v2.1.124/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S --- v2.1.124/linux/arch/i386/boot/setup.S Sat Sep 5 16:46:40 1998 +++ linux/arch/i386/boot/setup.S Mon Oct 5 12:59:25 1998 @@ -145,7 +145,7 @@ jne bad_sig jmp good_sig1 -! Routine to print ASCII string at DS:SI +! Routine to print ASCIIz string at DS:SI prtstr: lodsb and al,al @@ -345,7 +345,7 @@ mov ds,ax mov ds,ax xor ax,ax - mov [0x220], ax ! set table length to 0 + mov [0xa0], ax ! set table length to 0 mov ah, #0xc0 stc int 0x15 ! puts feature table at es:bx @@ -357,9 +357,13 @@ sub ax, #DELTA_INITSEG ! aka #INITSEG mov es,ax mov si,bx - mov di,#0x220 + mov di,#0xa0 mov cx,(si) add cx,#2 ! table length is a short + cmp cx,#0x10 + jc sysdesc_ok + mov cx,#0x10 ! we keep only first 16 bytes +sysdesc_ok: rep movsb pop ds diff -u --recursive --new-file v2.1.124/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.1.124/linux/arch/i386/defconfig Mon Oct 5 13:13:35 1998 +++ linux/arch/i386/defconfig Thu Oct 8 09:19:26 1998 @@ -193,6 +193,7 @@ CONFIG_EEXPRESS_PRO100=y # CONFIG_NE2K_PCI is not set # CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c --- v2.1.124/linux/arch/i386/kernel/apm.c Thu Sep 17 17:53:34 1998 +++ linux/arch/i386/kernel/apm.c Wed Oct 7 18:52:05 1998 @@ -2,6 +2,8 @@ * APM BIOS driver for Linux * Copyright 1994-1998 Stephen Rothwell * (Stephen.Rothwell@canb.auug.org.au) + * Development of this driver was funded by NEC Australia P/L + * and NEC Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -27,6 +29,7 @@ * Feb 1998, Version 1.3 * Feb 1998, Version 1.4 * Aug 1998, Version 1.5 + * Sep 1998, Version 1.6 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -51,7 +54,12 @@ * C. Scott Ananian Linux 2.1.87 * 1.5: Fix segment register reloading (in case of bad segments saved * across BIOS call). - * Stephen ROthwell + * Stephen Rothwell + * 1.6: Cope with complier/assembler differences. + * Only try to turn off the first display device. + * Fix OOPS at power off with no APM BIOS by Jan Echternach + * + * Stephen Rothwell * * APM 1.1 Reference: * @@ -214,7 +222,8 @@ /* * Save a segment register away */ -#define savesegment(seg, where) __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where)) +#define savesegment(seg, where) \ + __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where)) /* * Forward declarations @@ -264,7 +273,7 @@ static struct timer_list apm_timer; -static char driver_version[] = "1.5"; /* no spaces */ +static char driver_version[] = "1.6"; /* no spaces */ #ifdef APM_DEBUG static char * apm_event_name[] = { @@ -350,105 +359,95 @@ * can be zeroed before the call. Unfortunately, we can't do anything * about the stack segment/pointer. Also, we tell the compiler that * everything could change. + * + * Also, we KNOW that for the non error case of apm_bios_call, there + * is no useful data returned in the low order 8 bits of eax. */ - -static inline int apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, - u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi) -{ - unsigned int old_fs, old_gs; - int error; - -#ifdef APM_ZERO_SEGS - savesegment(fs, old_fs); - savesegment(gs, old_gs); -#endif - __asm__ __volatile__( - "pushfl\n\t" #ifdef APM_NOINTS - "cli\n\t" +# define APM_DO_CLI __cli() +#else +# define APM_DO_CLI #endif #ifdef APM_ZERO_SEGS - "pushl %%ds\n\t" - "pushl %%es\n\t" - "movl %w9,%%ds\n\t" - "movl %w9,%%es\n\t" - "movl %w9,%%fs\n\t" - "movl %w9,%%gs\n\t" +# define APM_DO_SAVE_SEGS \ + savesegment(fs, saved_fs); \ + savesegment(gs, saved_gs) +# define APM_DO_ZERO_SEGS \ + "pushl %%ds\n\t" \ + "pushl %%es\n\t" \ + "xorl %%edx, %%edx\n\t" \ + "mov %%dx, %%ds\n\t" \ + "mov %%dx, %%es\n\t" \ + "mov %%dx, %%fs\n\t" \ + "mov %%dx, %%gs\n\t" +# define APM_DO_POP_SEGS \ + "popl %%es\n\t" \ + "popl %%ds\n\t" +# define APM_DO_RESTORE_SEGS \ + loadsegment(fs, saved_fs); \ + loadsegment(gs, saved_gs) +#else +# define APM_DO_SAVE_SEGS +# define APM_DO_ZERO_SEGS +# define APM_DO_POP_SEGS +# define APM_DO_RESTORE_SEGS #endif + +static inline u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, + u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi) +{ + unsigned int saved_fs; + unsigned int saved_gs; + unsigned long flags; + + __save_flags(flags); + APM_DO_CLI; + APM_DO_SAVE_SEGS; + __asm__ __volatile__(APM_DO_ZERO_SEGS "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" - "movl $0, %%edi\n\t" - "jnc 1f\n\t" - "movl $1, %%edi\n" - "1:\tpopl %%es\n\t" - "popl %%ds\n\t" - "popfl\n\t" + "setc %%al\n\t" + APM_DO_POP_SEGS : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx), - "=S" (*esi), "=D" (error) + "=S" (*esi) : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) -#ifdef APM_ZERO_SEGS - , "r" (0) -#endif - : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory"); -#ifdef APM_ZERO_SEGS - loadsegment(fs, old_fs); - loadsegment(gs, old_gs); -#endif - return error; + : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory", "cc"); + APM_DO_RESTORE_SEGS; + __restore_flags(flags); + return *eax & 0xff; } /* * This version only returns one value (usually an error code) */ -static inline int apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, u32 *eax) +static inline u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, + u32 *eax) { - unsigned int old_fs, old_gs; - int error; + u8 error; + unsigned int saved_fs; + unsigned int saved_gs; + unsigned long flags; -#ifdef APM_ZERO_SEGS - savesegment(fs, old_fs); - savesegment(gs, old_gs); -#endif - __asm__ __volatile__( - "pushfl\n\t" -#ifdef APM_NOINTS - "cli\n\t" -#endif -#ifdef APM_ZERO_SEGS - "pushl %%ds\n\t" - "pushl %%es\n\t" - "movl %w5,%%ds\n\t" - "movl %w5,%%es\n\t" - "movl %w5,%%fs\n\t" - "movl %w5,%%gs\n\t" -#endif + __save_flags(flags); + APM_DO_CLI; + APM_DO_SAVE_SEGS; + __asm__ __volatile__(APM_DO_ZERO_SEGS "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" - "movl $0, %%edi\n\t" - "jnc 1f\n\t" - "movl $1, %%edi\n" - "1:\tpopl %%es\n\t" - "popl %%ds\n\t" - "popfl\n\t" - : "=a" (*eax), "=D" (error) + "setc %%bl\n\t" + APM_DO_POP_SEGS + : "=a" (*eax), "=b" (error) : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) -#ifdef APM_ZERO_SEGS - , "r" (0) -#endif - : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory"); -#ifdef APM_ZERO_SEGS - loadsegment(fs, old_fs); - loadsegment(gs, old_gs); -#endif + : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory", "cc"); + APM_DO_RESTORE_SEGS; + __restore_flags(flags); return error; } static int apm_driver_version(u_short *val) { - int error; u32 eax; - error = apm_bios_call_simple(0x530e, 0, *val, &eax); - if (error) + if (apm_bios_call_simple(0x530e, 0, *val, &eax)) return (eax >> 8) & 0xff; *val = eax; return APM_SUCCESS; @@ -456,14 +455,12 @@ static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info) { - int error; u32 eax; u32 ebx; u32 ecx; u32 dummy; - error = apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy); - if (error) + if (apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy)) return (eax >> 8) & 0xff; *event = ebx; if (apm_bios_info.version < 0x0102) @@ -473,27 +470,31 @@ return APM_SUCCESS; } -static int set_power_state(u_short what, u_short state) +static inline int set_power_state(u_short what, u_short state) { - int error; u32 eax; - error = apm_bios_call_simple(0x5307, what, state, &eax); - if (error) + if (apm_bios_call_simple(0x5307, what, state, &eax)) return (eax >> 8) & 0xff; return APM_SUCCESS; } -int apm_set_power_state(u_short state) +static int apm_set_power_state(u_short state) { return set_power_state(0x0001, state); } +void apm_power_off(void) +{ + if (apm_enabled) + (void) apm_set_power_state(APM_STATE_OFF); +} + #ifdef CONFIG_APM_DISPLAY_BLANK /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ static int apm_set_display_power_state(u_short state) { - return set_power_state(0x01ff, state); + return set_power_state(0x0100, state); } #endif @@ -501,12 +502,11 @@ /* Called by apm_setup if apm_enabled will be true. */ static int apm_enable_power_management(void) { - int error; u32 eax; - error = apm_bios_call_simple(0x5308, - (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, 1, &eax); - if (error) + if (apm_bios_call_simple(0x5308, + (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, + 1, &eax)) return (eax >> 8) & 0xff; return APM_SUCCESS; } @@ -514,15 +514,13 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) { - int error; u32 eax; u32 ebx; u32 ecx; u32 edx; u32 dummy; - error = apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy); - if (error) + if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy)) return (eax >> 8) & 0xff; *status = ebx; *bat = ecx; @@ -536,7 +534,6 @@ u_short *bat, u_short *life, u_short *nbat) { u_short status; - int error; u32 eax; u32 ebx; u32 ecx; @@ -551,8 +548,8 @@ return apm_get_power_status(&status, bat, life); } - error = apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi); - if (error) + if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, + &ebx, &ecx, &edx, &esi)) return (eax >> 8) & 0xff; *bat = ecx; *life = edx; @@ -563,11 +560,9 @@ static int apm_engage_power_management(u_short device) { - int error; u32 eax; - error = apm_bios_call_simple(0x530f, device, 1, &eax); - if (error) + if (apm_bios_call_simple(0x530f, device, 1, &eax)) return (eax >> 8) & 0xff; return APM_SUCCESS; } @@ -875,14 +870,12 @@ int apm_do_idle(void) { #ifdef CONFIG_APM_CPU_IDLE - int error; u32 dummy; if (!apm_enabled) return 0; - error = apm_bios_call_simple(0x5305, 0, 0, &dummy); - if (error) + if (apm_bios_call_simple(0x5305, 0, 0, &dummy)) return 0; clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0; diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c --- v2.1.124/linux/arch/i386/kernel/io_apic.c Mon Oct 5 13:13:35 1998 +++ linux/arch/i386/kernel/io_apic.c Tue Oct 6 16:44:00 1998 @@ -103,8 +103,10 @@ /* * This is performance-critical, we want to do it O(1) + * + * the indexing order of this array favors 1:1 mappings + * between pins and IRQs. */ -static int irq_2_pin[NR_IRQS]; static inline unsigned int io_apic_read(unsigned int reg) { @@ -119,6 +121,15 @@ } /* + * Re-write a value: to be used for read-modify-write + * cycles where the read already set up the index register. + */ +static inline void io_apic_modify(unsigned int value) +{ + *(IO_APIC_BASE+4) = value; +} + +/* * Synchronize the IO-APIC and the CPU by doing * a dummy read from the IO-APIC */ @@ -128,58 +139,68 @@ } /* - * We disable IO-APIC IRQs by setting their 'destination CPU mask' to - * zero. Trick by Ramesh Nalluri. + * Rough estimation of how many shared IRQs there are, can + * be changed anytime. */ -static inline void disable_IO_APIC_irq(unsigned int irq) -{ - int pin = irq_2_pin[irq]; - struct IO_APIC_route_entry entry; +#define MAX_PLUS_SHARED_IRQS NR_IRQS +#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS) - if (pin != -1) { - *(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2); - entry.dest.logical.logical_dest = 0x0; - io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1)); - io_apic_sync(); - } -} +static struct irq_pin_list { + int pin, next; +} irq_2_pin[PIN_MAP_SIZE]; -static inline void enable_IO_APIC_irq(unsigned int irq) +/* + * The common case is 1:1 IRQ<->pin mappings. Sometimes there are + * shared ISA-space IRQs, so we have to support them. We are super + * fast in the common case, and fast for shared ISA-space IRQs. + */ +static void add_pin_to_irq(unsigned int irq, int pin) { - int pin = irq_2_pin[irq]; - struct IO_APIC_route_entry entry; + static int first_free_entry = NR_IRQS; + struct irq_pin_list *entry = irq_2_pin + irq; - if (pin != -1) { - *(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2); - entry.dest.logical.logical_dest = 0xff; - io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1)); - } -} + while (entry->next) + entry = irq_2_pin + entry->next; -static inline void mask_IO_APIC_irq(unsigned int irq) -{ - int pin = irq_2_pin[irq]; - struct IO_APIC_route_entry entry; - - if (pin != -1) { - *(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2); - entry.mask = 1; - io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0)); - io_apic_sync(); - } + if (entry->pin != -1) { + entry->next = first_free_entry; + entry = irq_2_pin + entry->next; + if (++first_free_entry >= PIN_MAP_SIZE) + panic("io_apic.c: whoops"); + } + entry->pin = pin; +} + +#define DO_ACTION(name,R,ACTION, FINAL) \ + \ +static void name##_IO_APIC_irq(unsigned int irq) \ +{ \ + int pin; \ + struct irq_pin_list *entry = irq_2_pin + irq; \ + \ + for (;;) { \ + unsigned int reg; \ + pin = entry->pin; \ + if (pin == -1) \ + break; \ + reg = io_apic_read(0x10 + R + pin*2); \ + reg ACTION; \ + io_apic_modify(reg); \ + if (!entry->next) \ + break; \ + entry = irq_2_pin + entry->next; \ + } \ + FINAL; \ } -static inline void unmask_IO_APIC_irq(unsigned int irq) -{ - int pin = irq_2_pin[irq]; - struct IO_APIC_route_entry entry; - - if (pin != -1) { - *(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2); - entry.mask = 0; - io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0)); - } -} +/* + * We disable IO-APIC IRQs by setting their 'destination CPU mask' to + * zero. Trick by Ramesh Nalluri. + */ +DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync()) /* destination = 0x00 */ +DO_ACTION( enable, 1, |= 0xff000000, ) /* destination = 0xff */ +DO_ACTION( mask, 0, |= 0x00010000, io_apic_sync()) /* mask = 1 */ +DO_ACTION( unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ static void __init clear_IO_APIC_pin(unsigned int pin) { @@ -577,7 +598,7 @@ } irq = pin_2_irq(idx,pin); - irq_2_pin[irq] = pin; + add_pin_to_irq(irq, pin); if (!IO_APIC_IRQ(irq)) continue; @@ -702,10 +723,20 @@ ); } - printk("IRQ to pin mappings:\n"); - for (i = 0; i < NR_IRQS; i++) - printk("%d->%d ", i, irq_2_pin[i]); - printk("\n"); + printk(KERN_DEBUG "IRQ to pin mappings:\n"); + for (i = 0; i < NR_IRQS; i++) { + struct irq_pin_list *entry = irq_2_pin + i; + if (entry->pin < 0) + continue; + printk(KERN_DEBUG "IRQ%d ", i); + for (;;) { + printk("-> %d", entry->pin); + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } + printk("\n"); + } printk(".................................... done.\n"); @@ -716,8 +747,10 @@ { int i, pin; - for (i = 0; i < NR_IRQS; i++) - irq_2_pin[i] = -1; + for (i = 0; i < PIN_MAP_SIZE; i++) { + irq_2_pin[i].pin = -1; + irq_2_pin[i].next = 0; + } if (!pirqs_enabled) for (i = 0; i < MAX_PIRQS; i++) pirq_entries[i] =- 1; diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v2.1.124/linux/arch/i386/kernel/process.c Mon Oct 5 13:13:35 1998 +++ linux/arch/i386/kernel/process.c Wed Oct 7 18:52:06 1998 @@ -398,7 +398,7 @@ void machine_power_off(void) { #if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) - apm_set_power_state(APM_STATE_OFF); + apm_power_off(); #endif } diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.1.124/linux/arch/i386/kernel/setup.c Thu Sep 17 17:53:34 1998 +++ linux/arch/i386/kernel/setup.c Mon Oct 5 12:59:26 1998 @@ -86,13 +86,12 @@ * This is set up by the setup-routine at boot-time */ #define PARAM ((unsigned char *)empty_zero_page) +#define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) #define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) -#ifdef CONFIG_APM -#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64)) -#endif +#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40)) #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) -#define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) +#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0)) #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) #define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) #define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) @@ -101,7 +100,6 @@ #define KERNEL_START (*(unsigned long *) (PARAM+0x214)) #define INITRD_START (*(unsigned long *) (PARAM+0x218)) #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) -#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0x220)) #define COMMAND_LINE ((char *) (PARAM+2048)) #define COMMAND_LINE_SIZE 256 diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.1.124/linux/arch/i386/kernel/smp.c Mon Oct 5 13:13:35 1998 +++ linux/arch/i386/kernel/smp.c Mon Oct 5 13:19:44 1998 @@ -736,7 +736,6 @@ /* Must be done before calibration delay is computed */ mtrr_init_secondary_cpu (); #endif - stts(); smp_callin(); while (!smp_commenced) barrier(); @@ -753,6 +752,13 @@ struct thread_struct * p = ¤t->tss; /* + * Load up the LDT and the task register. + */ + asm volatile("lldt %%ax": :"a" (p->ldt)); + asm volatile("ltr %%ax": :"a" (p->tr)); + stts(); + + /* * We don't actually need to load the full TSS, * basically just the stack pointer and the eip. * @@ -760,8 +766,7 @@ * to release it as part of the "reschedule" return. */ spin_lock(&scheduler_lock); - asm volatile("lldt %%ax": :"a" (p->ldt)); - asm volatile("ltr %%ax": :"a" (p->tr)); + asm volatile( "movl %0,%%esp\n\t" "jmp *%1" diff -u --recursive --new-file v2.1.124/linux/arch/i386/lib/delay.c linux/arch/i386/lib/delay.c --- v2.1.124/linux/arch/i386/lib/delay.c Sun Dec 21 17:27:18 1997 +++ linux/arch/i386/lib/delay.c Tue Oct 6 08:55:47 1998 @@ -5,7 +5,9 @@ * Copyright (C) 1997 Martin Mares * * The __delay function must _NOT_ be inlined as its execution time - * depends wildly on alignment on many x86 processors. + * depends wildly on alignment on many x86 processors. The additional + * jump magic is needed to get the timing stable on all the CPU's + * we have to worry about. */ #include @@ -18,7 +20,11 @@ void __delay(unsigned long loops) { __asm__ __volatile__( - "1:\tdecl %0\n\tjns 1b" + "\tjmp 1f\n" + ".align 16\n" + "1:\tjmp 2f\n" + ".align 16\n" + "2:\tdecl %0\n\tjns 2b" :/* no outputs */ :"a" (loops) :"ax"); diff -u --recursive --new-file v2.1.124/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.124/linux/arch/i386/mm/fault.c Wed Sep 9 14:51:05 1998 +++ linux/arch/i386/mm/fault.c Thu Oct 8 14:20:02 1998 @@ -103,8 +103,13 @@ tsk = current; mm = tsk->mm; - if (in_interrupt()) - die("page fault from irq handler",regs,error_code); + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || mm == &init_mm) + goto no_context; down(&mm->mmap_sem); @@ -194,6 +199,7 @@ } } +no_context: /* Are we prepared to handle this kernel fault? */ if ((fixup = search_exception_table(regs->eip)) != 0) { regs->eip = fixup; @@ -235,8 +241,6 @@ page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; printk(KERN_ALERT "*pte = %08lx\n", page); } - lock_kernel(); die("Oops", regs, error_code); do_exit(SIGKILL); - unlock_kernel(); } diff -u --recursive --new-file v2.1.124/linux/arch/m68k/amiga/amiints.c linux/arch/m68k/amiga/amiints.c --- v2.1.124/linux/arch/m68k/amiga/amiints.c Mon Aug 3 12:45:44 1998 +++ linux/arch/m68k/amiga/amiints.c Mon Oct 5 13:54:39 1998 @@ -288,12 +288,14 @@ } if (irq >= IRQ_AMIGA_CIAB) { + cia_set_irq(&ciab_base, (1 << (irq - IRQ_AMIGA_CIAB))); cia_able_irq(&ciab_base, CIA_ICR_SETCLR | (1 << (irq - IRQ_AMIGA_CIAB))); return; } if (irq >= IRQ_AMIGA_CIAA) { + cia_set_irq(&ciaa_base, (1 << (irq - IRQ_AMIGA_CIAA))); cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | (1 << (irq - IRQ_AMIGA_CIAA))); return; diff -u --recursive --new-file v2.1.124/linux/arch/m68k/amiga/config.c linux/arch/m68k/amiga/config.c --- v2.1.124/linux/arch/m68k/amiga/config.c Sat Sep 5 16:46:40 1998 +++ linux/arch/m68k/amiga/config.c Mon Oct 5 13:54:39 1998 @@ -398,6 +398,28 @@ /* ensure that the DMA master bit is set */ custom.dmacon = DMAF_SETCLR | DMAF_MASTER; + /* don't use Z2 RAM as system memory on Z3 capable machines */ + if (AMIGAHW_PRESENT(ZORRO3)) { + int i, j; + u32 disabled_z2mem = 0; + for (i = 0; i < m68k_num_memory; i++) + if (m68k_memory[i].addr < 16*1024*1024) { + if (i == 0) { + /* don't cut off the branch we're sitting on */ + printk("Warning: kernel runs in Zorro II memory\n"); + continue; + } + disabled_z2mem += m68k_memory[i].size; + m68k_num_memory--; + for (j = i; j < m68k_num_memory; j++) + m68k_memory[j] = m68k_memory[j+1]; + i--; + } + if (disabled_z2mem) + printk("%dK of Zorro II memory will not be used as system memory\n", + disabled_z2mem>>10); + } + /* initialize chipram allocator */ amiga_chip_init (); diff -u --recursive --new-file v2.1.124/linux/arch/m68k/atari/ataints.c linux/arch/m68k/atari/ataints.c --- v2.1.124/linux/arch/m68k/atari/ataints.c Tue Jun 23 10:01:20 1998 +++ linux/arch/m68k/atari/ataints.c Mon Oct 5 13:54:39 1998 @@ -419,6 +419,7 @@ unsigned long flags, const char *devname, void *dev_id) { int vector; + unsigned long oflags = flags; /* * The following is a hack to make some PCI card drivers work, @@ -427,9 +428,14 @@ flags &= ~SA_SHIRQ; + if (flags == SA_INTERRUPT) { + printk ("%s: SA_INTERRUPT changed to IRQ_TYPE_SLOW for %s\n", + __FUNCTION__, devname); + flags = IRQ_TYPE_SLOW; + } if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) { - printk ("%s: Bad irq type %ld requested from %s\n", - __FUNCTION__, flags, devname); + printk ("%s: Bad irq type 0x%lx <0x%lx> requested from %s\n", + __FUNCTION__, flags, oflags, devname); return -EINVAL; } if (!IS_VALID_INTNO(irq)) { diff -u --recursive --new-file v2.1.124/linux/arch/m68k/atari/stram.c linux/arch/m68k/atari/stram.c --- v2.1.124/linux/arch/m68k/atari/stram.c Sat Sep 5 16:46:40 1998 +++ linux/arch/m68k/atari/stram.c Mon Oct 5 13:54:39 1998 @@ -273,7 +273,7 @@ /* determine whether kernel code resides in ST-RAM (then ST-RAM is the * first memory block at virtual 0x0) */ - stram_start = phys_to_virt( 0 ); + stram_start = (unsigned long)phys_to_virt(0); kernel_in_stram = (stram_start == 0); for( i = 0; i < m68k_num_memory; ++i ) { @@ -1465,9 +1465,10 @@ for( p = alloc_list; p; p = p->next ) { if (len + 50 >= PAGE_SIZE) break; - PRINT_PROC( "0x%08lx-0x%08lx: %s (", - virt_to_phys(p->start), - virt_to_phys(p->start+p->size-1), p->owner ); + PRINT_PROC("0x%08lx-0x%08lx: %s (", + virt_to_phys((void *)p->start), + virt_to_phys((void *)p->start+p->size-1), + p->owner); if (p->flags & BLOCK_STATIC) PRINT_PROC( "static)\n" ); else if (p->flags & BLOCK_GFP) diff -u --recursive --new-file v2.1.124/linux/arch/m68k/config.in linux/arch/m68k/config.in --- v2.1.124/linux/arch/m68k/config.in Sat Sep 5 16:46:40 1998 +++ linux/arch/m68k/config.in Mon Oct 5 13:54:39 1998 @@ -32,6 +32,7 @@ bool 'Macintosh support' CONFIG_MAC if [ "$CONFIG_MAC" = "y" ]; then define_bool CONFIG_NUBUS y + define_bool CONFIG_M68K_L2_CACHE y fi bool 'Apollo support' CONFIG_APOLLO bool 'VME (Motorola and BVM) support' CONFIG_VME @@ -155,6 +156,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'A4091 SCSI support' CONFIG_A4091_SCSI bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI + bool 'Blizzard PowerUP 603e+ SCSI' CONFIG_BLZ603EPLUS_SCSI bool 'Cyberstorm Mk III SCSI support' CONFIG_CYBERSTORMIII_SCSI # bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI fi @@ -171,6 +173,7 @@ fi if [ "$CONFIG_MAC" = "y" ]; then bool 'MAC NCR5380 SCSI' CONFIG_MAC_SCSI + bool 'MAC NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP fi #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI @@ -211,6 +214,7 @@ tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER if [ "$CONFIG_ZORRO" = "y" ]; then tristate 'Ariadne support' CONFIG_ARIADNE + tristate 'Ariadne II support' CONFIG_ARIADNE2 tristate 'A2065 support' CONFIG_A2065 tristate 'Hydra support' CONFIG_HYDRA fi @@ -222,6 +226,8 @@ fi if [ "$CONFIG_MAC" = "y" ]; then bool 'Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT + bool 'AV Macintosh onboard MACE ethernet' CONFIG_MACMACE + bool 'Macintosh onboard SONIC ethernet' CONFIG_MACSONIC fi if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME16x" = "y" ]; then bool 'MVME16x Ethernet support' CONFIG_MVME16x_NET diff -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/entry.S linux/arch/m68k/kernel/entry.S --- v2.1.124/linux/arch/m68k/kernel/entry.S Sat Sep 5 16:46:40 1998 +++ linux/arch/m68k/kernel/entry.S Mon Oct 5 13:54:39 1998 @@ -396,7 +396,7 @@ .data ALIGN SYMBOL_NAME_LABEL(sys_call_table) - .long SYMBOL_NAME(sys_setup) /* 0 */ + .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ .long SYMBOL_NAME(sys_exit) .long SYMBOL_NAME(sys_fork) .long SYMBOL_NAME(sys_read) diff -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c --- v2.1.124/linux/arch/m68k/kernel/m68k_ksyms.c Tue Aug 18 22:02:03 1998 +++ linux/arch/m68k/kernel/m68k_ksyms.c Thu Oct 8 09:31:52 1998 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -30,9 +31,13 @@ EXPORT_SYMBOL(m68k_is040or060); EXPORT_SYMBOL(cache_push); EXPORT_SYMBOL(cache_clear); +#ifndef CONFIG_SINGLE_MEMORY_CHUNK EXPORT_SYMBOL(mm_vtop); EXPORT_SYMBOL(mm_ptov); EXPORT_SYMBOL(mm_end_of_chunk); +#endif +EXPORT_SYMBOL(mm_vtop_fallback); +EXPORT_SYMBOL(m68k_memory); EXPORT_SYMBOL(kernel_map); EXPORT_SYMBOL(m68k_debug_device); EXPORT_SYMBOL(dump_fpu); diff -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c --- v2.1.124/linux/arch/m68k/kernel/process.c Sat Sep 5 16:46:40 1998 +++ linux/arch/m68k/kernel/process.c Wed Oct 7 18:52:07 1998 @@ -95,9 +95,6 @@ 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 -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/traps.c linux/arch/m68k/kernel/traps.c --- v2.1.124/linux/arch/m68k/kernel/traps.c Tue Feb 17 13:12:45 1998 +++ linux/arch/m68k/kernel/traps.c Mon Oct 5 13:54:39 1998 @@ -452,7 +452,7 @@ #endif errorcode = (mmusr & MMU_I) ? 0 : 1; - if (!(ssw & RW) || ssw & RM) + if (!(ssw & RW) || (ssw & RM)) errorcode |= 2; if (mmusr & (MMU_I | MMU_WP)) { diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/adb-bus.c linux/arch/m68k/mac/adb-bus.c --- v2.1.124/linux/arch/m68k/mac/adb-bus.c Wed Aug 26 11:37:33 1998 +++ linux/arch/m68k/mac/adb-bus.c Mon Oct 5 13:54:39 1998 @@ -245,13 +245,16 @@ via_write(via1, vIFR, SR_INT); /* get those pesky clock ticks we missed while booting */ - for ( i = 0; i < 30; i++) { + for ( i = 0; i < 60; i++) { udelay(ADB_DELAY); adb_hw_setup_IIsi(); udelay(ADB_DELAY); if (via_read(via1, vBufB) & TREQ) break; } + if (i == 60) + printk("adb_IIsi: maybe bus jammed ??\n"); + /* * Ok we probably ;) have a ready to use adb bus. Its also */ diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/config.c linux/arch/m68k/mac/config.c --- v2.1.124/linux/arch/m68k/mac/config.c Tue Aug 18 22:02:03 1998 +++ linux/arch/m68k/mac/config.c Mon Oct 5 13:54:39 1998 @@ -83,9 +83,9 @@ /* Mac specific timer functions */ extern unsigned long mac_gettimeoffset (void); -extern void mac_gettod (int *, int *, int *, int *, int *, int *); -extern int mac_hwclk (int, struct hwclk_time *); -extern int mac_set_clock_mmss (unsigned long); +static void mac_gettod (int *, int *, int *, int *, int *, int *); +static int mac_hwclk (int, struct hwclk_time *); +static int mac_set_clock_mmss (unsigned long); extern void via_init_clock(void (*func)(int, void *, struct pt_regs *)); extern void (*kd_mksound)(unsigned int, unsigned int); @@ -123,7 +123,7 @@ mac_reset(); } -void mac_sched_init(void (*vector)(int, void *, struct pt_regs *)) +static void mac_sched_init(void (*vector)(int, void *, struct pt_regs *)) { via_init_clock(vector); } @@ -135,7 +135,7 @@ * the system time. */ -void mac_gettod (int *yearp, int *monp, int *dayp, +static void mac_gettod (int *yearp, int *monp, int *dayp, int *hourp, int *minp, int *secp) { unsigned long time; @@ -192,7 +192,7 @@ * TBI: read and write hwclock */ -int mac_hwclk( int op, struct hwclk_time *t ) +static int mac_hwclk( int op, struct hwclk_time *t ) { return 0; } @@ -201,7 +201,7 @@ * TBI: set minutes/seconds in hwclock */ -int mac_set_clock_mmss (unsigned long nowtime) +static int mac_set_clock_mmss (unsigned long nowtime) { short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; @@ -280,10 +280,26 @@ return(unknown); } +/* + * Flip into 24bit mode for an instant - flushes the L2 cache card. We + * have to disable interrupts for this. Our IRQ handlers will crap + * themselves if they take an IRQ in 24bit mode! + */ + +static void mac_cache_card_flush(int writeback) +{ + unsigned long flags; + save_flags(flags); + cli(); + via_write(via2, vBufB, via_read(via2,vBufB)&~VIA2B_vMode32); + via_write(via2, vBufB, via_read(via2,vBufB)|VIA2B_vMode32); + restore_flags(flags); +} + __initfunc(void config_mac(void)) { - if (MACH_IS_ATARI || MACH_IS_AMIGA) { + if (!MACH_IS_MAC) { printk("ERROR: no Mac, but config_mac() called!! \n"); } @@ -338,7 +354,17 @@ mac_identify(); mac_report_hardware(); - + + if( + /* Cache cards */ + macintosh_config->ident == MAC_MODEL_IICI|| + macintosh_config->ident == MAC_MODEL_IISI|| + macintosh_config->ident == MAC_MODEL_IICX|| + /* On board L2 cache */ + macintosh_config->ident == MAC_MODEL_IIFX) + { + mach_l2_flush = mac_cache_card_flush; + } /* goes on forever if timers broken */ #ifdef MAC_DEBUG_SOUND mac_mksound(1000,10); @@ -349,7 +375,6 @@ */ nubus_sweep_video(); - } @@ -411,23 +436,23 @@ { MAC_MODEL_LCIII,"LC III", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, /* - * Quadra (only 68030 ones will actually work!). Not much odd. Video is at - * 0xF9000000, via is like a MacII. We label it differently as some of the - * stuff connected to VIA2 seems different. Better SCSI chip and ???? onboard ethernet - * in all cases using a NatSemi SONIC. The 700, 900 and 950 have some I/O chips in the wrong - * place to confuse us. The 840AV seems to have a scsi location of its own + * Quadra. Video is at 0xF9000000, via is like a MacII. We label it differently + * as some of the stuff connected to VIA2 seems different. Better SCSI chip and + * onboard ethernet using a NatSemi SONIC except the 660AV and 840AV which use an + * AMD 79C940 (MACE). + * The 700, 900 and 950 have some I/O chips in the wrong place to + * confuse us. The 840AV has a SCSI location of its own (same as + * the 660AV). */ - - { MAC_MODEL_Q605, "Quadra 605", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, + + { MAC_MODEL_Q605, "Quadra 605", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_Q610, "Quadra 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q630, "Quadra 630", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_QUADRA, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q650, "Quadra 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, /* The Q700 does have a NS Sonic */ - { MAC_MODEL_Q700, "Quadra 700", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_QUADRA2, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_Q700, "Quadra 700", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_QUADRA2, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q800, "Quadra 800", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, - /* Does the 840AV have ethernet ??? documents seem to indicate its not quite a - Quadra in this respect ? */ - { MAC_MODEL_Q840, "Quadra 840AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_Q840, "Quadra 840AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_MACE, MAC_NUBUS}, /* These might have IOP problems */ { MAC_MODEL_Q900, "Quadra 900", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q950, "Quadra 950", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, @@ -440,7 +465,7 @@ { MAC_MODEL_P475, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P520, "Performa 520", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P575, "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P588, "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_TV, "TV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, @@ -452,8 +477,8 @@ * Centris - just guessing again; maybe like Quadra */ - { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, /* @@ -467,10 +492,10 @@ { MAC_MODEL_PB160, "PowerBook 160", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_PB165, "PowerBook 165", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_PB165C, "PowerBook 165c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_PB180, "PowerBook 180", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_PB180C, "PowerBook 180c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB190, "PowerBook 190cs", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB190, "PowerBook 190", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_PB520, "PowerBook 520", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, /* @@ -535,8 +560,8 @@ printk (" Penguin bootinfo data:\n"); printk (" Video: addr 0x%lx row 0x%lx depth %lx dimensions %ld x %ld\n", mac_bi_data.videoaddr, mac_bi_data.videorow, - mac_bi_data.videodepth, (int) (mac_bi_data.dimensions & 0xFFFF), - (int) (mac_bi_data.dimensions >> 16)); + mac_bi_data.videodepth, mac_bi_data.dimensions & 0xFFFF, + mac_bi_data.dimensions >> 16); printk (" Videological 0x%lx phys. 0x%lx, SCC at 0x%lx \n", mac_bi_data.videological, mac_orig_videoaddr, mac_bi_data.sccbase); @@ -576,8 +601,7 @@ break; } - - + via_configure_base(); } void mac_report_hardware(void) @@ -589,6 +613,8 @@ { strcpy(str,"Macintosh "); strcat(str, macintosh_config->name); + if(mach_l2_flush && !(via_read(via2, vBufB)&VIA2B_vCDis)) + strcat(str, "(+L2 cache)"); } /* diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/debug.c linux/arch/m68k/mac/debug.c --- v2.1.124/linux/arch/m68k/mac/debug.c Tue Jun 23 10:01:21 1998 +++ linux/arch/m68k/mac/debug.c Mon Oct 5 13:54:39 1998 @@ -249,7 +249,6 @@ * TODO: serial debug code */ -#define SCC_BAS (0x50F04000) struct SCC { u_char cha_b_ctrl; @@ -260,7 +259,8 @@ u_char char_dummy3; u_char cha_a_data; }; -# define scc ((*(volatile struct SCC*)SCC_BAS)) + +# define scc (*((volatile struct SCC*)mac_bi_data.sccbase)) /* Flag that serial port is already initialized and used */ int mac_SCC_init_done = 0; @@ -268,6 +268,8 @@ * not be repeated; used by kgdb */ int mac_SCC_reset_done = 0; +static int scc_port = -1; + static struct console mac_console_driver = { "debug", NULL, /* write */ @@ -282,20 +284,18 @@ NULL }; -static int scc_port; - /* Mac: loops_per_sec min. 1900000 ^= .5 us; MFPDELAY was 0.6 us*/ -#define US 1 +#define uSEC 1 static inline void mac_sccb_out (char c) { int i; do { - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); scc.cha_b_data = c; } @@ -304,10 +304,10 @@ { int i; do { - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */ - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); scc.cha_a_data = c; } @@ -337,10 +337,10 @@ { int i; do { - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */ - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); return( scc.cha_b_data ); } @@ -349,10 +349,10 @@ { int i; do { - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); } while( !(scc.cha_a_ctrl & 0x01) ); /* wait for rx buf filled */ - for( i = US; i > 0; --i ) + for( i = uSEC; i > 0; --i ) barrier(); return( scc.cha_a_data ); } @@ -365,10 +365,10 @@ do { \ int i; \ scc.cha_b_ctrl = (reg); \ - for( i = US; i > 0; --i ) \ + for( i = uSEC; i > 0; --i ) \ barrier(); \ scc.cha_b_ctrl = (val); \ - for( i = US; i > 0; --i ) \ + for( i = uSEC; i > 0; --i ) \ barrier(); \ } while(0) @@ -376,10 +376,10 @@ do { \ int i; \ scc.cha_a_ctrl = (reg); \ - for( i = US; i > 0; --i ) \ + for( i = uSEC; i > 0; --i ) \ barrier(); \ scc.cha_a_ctrl = (val); \ - for( i = US; i > 0; --i ) \ + for( i = uSEC; i > 0; --i ) \ barrier(); \ } while(0) @@ -389,7 +389,7 @@ #define LONG_DELAY() \ do { \ int i; \ - for( i = 60*US; i > 0; --i ) \ + for( i = 60*uSEC; i > 0; --i ) \ barrier(); \ } while(0) @@ -399,19 +399,21 @@ void mac_init_scc_port( int cflag, int port ) #endif { - extern int mac_SCC_reset_done; - static int clksrc_table[9] = - /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */ - { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }; - static int brgsrc_table[9] = - /* reg 14: 0 = RTxC, 2 = PCLK */ - { 2, 2, 2, 2, 2, 2, 0, 2, 2 }; - static int clkmode_table[9] = - /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */ - { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 }; - static int div_table[9] = - /* reg12 (BRG low) */ - { 208, 138, 103, 50, 24, 11, 1, 0, 0 }; + extern int mac_SCC_reset_done; + + /* + * baud rates: 1200, 1800, 2400, 4800, 9600, 19.2k, 38.4k, 57.6k, 115.2k + */ + + static int clksrc_table[9] = + /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */ + { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }; + static int clkmode_table[9] = + /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */ + { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 }; + static int div_table[9] = + /* reg12 (BRG low) */ + { 94, 62, 46, 22, 10, 4, 1, 0, 0 }; int baud = cflag & CBAUD; int clksrc, clkmode, div, reg3, reg5; @@ -426,12 +428,10 @@ clkmode = clkmode_table[baud]; div = div_table[baud]; - reg3 = (cflag & CSIZE) == CS8 ? 0xc0 : 0x40; - reg5 = (cflag & CSIZE) == CS8 ? 0x60 : 0x20 | 0x82 /* assert DTR/RTS */; + reg3 = (((cflag & CSIZE) == CS8) ? 0xc0 : 0x40); + reg5 = (((cflag & CSIZE) == CS8) ? 0x60 : 0x20) | 0x82 /* assert DTR/RTS */; -#if 0 - if (port) { -#endif + if (port == 1) { (void)scc.cha_b_ctrl; /* reset reg pointer */ SCCB_WRITE( 9, 0xc0 ); /* reset */ LONG_DELAY(); /* extra delay after WR9 access */ @@ -442,17 +442,14 @@ SCCB_WRITE( 5, reg5 ); SCCB_WRITE( 9, 0 ); /* no interrupts */ LONG_DELAY(); /* extra delay after WR9 access */ - SCCB_WRITE( 10, 0 ); /* NRZ mode */ + SCCB_WRITE( 10, 0 ); /* NRZ mode */ SCCB_WRITE( 11, clksrc ); /* main clock source */ SCCB_WRITE( 12, div ); /* BRG value */ SCCB_WRITE( 13, 0 ); /* BRG high byte */ - SCCB_WRITE( 14, brgsrc_table[baud] ); - SCCB_WRITE( 14, brgsrc_table[baud] | (div ? 1 : 0) ); + SCCB_WRITE( 14, 1 ); SCCB_WRITE( 3, reg3 | 1 ); SCCB_WRITE( 5, reg5 | 8 ); -#if 0 - } else { -#endif + } else if (port == 0) { (void)scc.cha_a_ctrl; /* reset reg pointer */ SCCA_WRITE( 9, 0xc0 ); /* reset */ LONG_DELAY(); /* extra delay after WR9 access */ @@ -463,17 +460,15 @@ SCCA_WRITE( 5, reg5 ); SCCA_WRITE( 9, 0 ); /* no interrupts */ LONG_DELAY(); /* extra delay after WR9 access */ - SCCA_WRITE( 10, 0 ); /* NRZ mode */ + SCCA_WRITE( 10, 0 ); /* NRZ mode */ SCCA_WRITE( 11, clksrc ); /* main clock source */ SCCA_WRITE( 12, div ); /* BRG value */ SCCA_WRITE( 13, 0 ); /* BRG high byte */ - SCCA_WRITE( 14, brgsrc_table[baud] ); - SCCA_WRITE( 14, brgsrc_table[baud] | (div ? 1 : 0) ); + SCCA_WRITE( 14, 1 ); SCCA_WRITE( 3, reg3 | 1 ); SCCA_WRITE( 5, reg5 | 8 ); -#if 0 } -#endif + mac_SCC_reset_done = 1; mac_SCC_init_done = 1; } @@ -486,18 +481,20 @@ return; #endif #ifdef DEBUG_SERIAL - if (!strcmp( m68k_debug_device, "ser" )) { - strcpy( m68k_debug_device, "ser1" ); - } - if (!strcmp( m68k_debug_device, "ser1" )) { - /* ST-MFP Modem1 serial port */ + if ( !strcmp( m68k_debug_device, "ser" ) + || !strcmp( m68k_debug_device, "ser1" )) { + /* Mac modem port */ mac_init_scc_port( B9600|CS8, 0 ); mac_console_driver.write = mac_scca_console_write; + mac_console_driver.wait_key = mac_scca_console_wait_key; + scc_port = 0; } else if (!strcmp( m68k_debug_device, "ser2" )) { - /* SCC Modem2 serial port */ + /* Mac printer port */ mac_init_scc_port( B9600|CS8, 1 ); mac_console_driver.write = mac_sccb_console_write; + mac_console_driver.wait_key = mac_sccb_console_wait_key; + scc_port = 1; } if (mac_console_driver.write) register_console(&mac_console_driver); diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/macboing.c linux/arch/m68k/mac/macboing.c --- v2.1.124/linux/arch/m68k/mac/macboing.c Tue Feb 17 13:12:45 1998 +++ linux/arch/m68k/mac/macboing.c Mon Oct 5 13:54:39 1998 @@ -27,12 +27,52 @@ unsigned long flags; int samples=512; + if (macintosh_config->ident == MAC_MODEL_C660 + || macintosh_config->ident == MAC_MODEL_Q840) + { + /* + * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O. + * It appears to be similar to the "AWACS" custom ASIC in the Power Mac + * [678]100. Because Singer and AWACS may have a similar hardware + * interface, this would imply that the code in drivers/sound/dmasound.c + * for AWACS could be used as a basis for Singer support. All we have to + * do is figure out how to do DMA on the 660AV/840AV through the PSC and + * figure out where the Singer hardware sits in memory. (I'd look in the + * vicinity of the AWACS location in a Power Mac [678]100 first, or the + * current location of the Apple Sound Chip--ASC--in other Macs.) The + * Power Mac [678]100 info can be found in MkLinux Mach kernel sources. + * + * Quoted from Apple's Tech Info Library, article number 16405: + * "Among desktop Macintosh computers, only the 660AV, 840AV, and Power + * Macintosh models have 16-bit audio input and output capability + * because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer + * codec circuitry in the AVs. The Audio Waveform Amplifier and + * Converter (AWAC) chip in the Power Macintosh performs the same + * 16-bit I/O functionality. The PowerBook 500 series computers + * support 16-bit stereo output, but only mono input." + * + * http://til.info.apple.com/techinfo.nsf/artnum/n16405 + * + * --David Kilzer + */ + + return; + } + if(!inited) { int i=0; int j=0; int k=0; int l=0; + + /* + * The IIfx strikes again! + */ + + if(macintosh_config->ident==MAC_MODEL_IIFX) + asc_base=(void *)0x50010000; + for(i=0;iident == MAC_MODEL_C660 - || macintosh_config->ident == MAC_MODEL_Q840) { + || macintosh_config->ident == MAC_MODEL_Q840) + { psc_init(); handler_table[2] = &psc3_handler[0]; @@ -463,9 +462,15 @@ * 980429 MS: RBV is ok, OSS seems to be differentt */ if (!via2_is_oss) - /* CB2 (IRQ) indep. interrupt input, positive edge */ - /* CA2 (DRQ) indep. interrupt input, positive edge */ - via_write(via, vPCR, 0x66); + if (macintosh_config->scsi_type == MAC_SCSI_OLD) { + /* CB2 (IRQ) indep. interrupt input, positive edge */ + /* CA2 (DRQ) indep. interrupt input, positive edge */ + via_write(via, vPCR, 0x66); + } else { + /* CB2 (IRQ) indep. interrupt input, negative edge */ + /* CA2 (DRQ) indep. interrupt input, negative edge */ + via_write(via, vPCR, 0x22); + } #if 0 else /* CB2 (IRQ) indep. interrupt input, negative edge */ @@ -614,7 +619,7 @@ via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); else if (srcidx == SRC_VIA2 && via2_is_oss) via_write(oss_regp, oss_map[irqidx]+8, 2); - else if (srcidx >= SRC_VIA2) + else if (srcidx > SRC_VIA2) via_write(via, (0x104 + 0x10*srcidx), via_read(via, (0x104 + 0x10*srcidx))|0x80|(1<<(irqidx))); else @@ -636,7 +641,11 @@ via_write(via, rIER, (via_read(via, rIER)&(1<= SRC_VIA2) + /* + * VIA2 is fixed. The stuff above VIA2 is for later + * macintoshes only. + */ + else if (srcidx > SRC_VIA2) via_write(via, (0x104 + 0x10*srcidx), via_read(via, (0x104 + 0x10*srcidx))|(1<<(irqidx))); else @@ -677,7 +686,7 @@ pending |= via_read(via, rIFR)&(1<= SRC_VIA2) + else if (srcidx > SRC_VIA2) pending |= via_read(via, (0x100 + 0x10*srcidx))&(1< #include #include +#include +#include +#include #include #include #include "via6522.h" -#include "psc.h" +#include volatile unsigned char *via1=(unsigned char *)VIABASE; volatile unsigned char *via2=(unsigned char *)VIABASE2; volatile unsigned char *psc=(unsigned char *)PSCBASE; +volatile long *via_memory_bogon=(long *)&via_memory_bogon; + unsigned char via1_clock, via1_datab; static int rbv=0; @@ -38,9 +43,8 @@ #define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8) -void via_init_clock(void (*func)(int, void *, struct pt_regs *)) +void via_configure_base(void) { - unsigned char c; switch(macintosh_config->via_type) { @@ -66,6 +70,13 @@ break; default: } +} + + +void via_init_clock(void (*func)(int, void *, struct pt_regs *)) +{ + unsigned char c; + via1_clock=via_read(via1, vACR); via1_datab=via_read(via1, vBufB); @@ -179,29 +190,28 @@ } /* - * get time offset between scheduling timer ticks - * Code stolen from arch/m68k/atari/time.c; underflow check probably - * wrong. + * TBI: get time offset between scheduling timer ticks */ #define TICK_SIZE 10000 /* This is always executed with interrupts disabled. */ + unsigned long mac_gettimeoffset (void) { - unsigned long ticks, offset = 0; + unsigned long ticks, offset = 0; - /* read VIA1 timer 2 current value */ - ticks = via_read(via1, vT1CL) + (via_read(via1, vT1CH)<<8); - /* The probability of underflow is less than 2% */ - if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50) - /* Check for pending timer interrupt in VIA1 IFR */ - if (via_read(via1, vIFR) & 0x40) - offset = TICK_SIZE; + /* read VIA1 timer 2 current value */ + ticks = via_read(via1, vT1CL) + (via_read(via1, vT1CH)<<8); + /* The probability of underflow is less than 2% */ + if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50) + /* Check for pending timer interrupt in VIA1 IFR */ + if (via_read(via1, vIFR) & 0x40) + offset = TICK_SIZE; - ticks = MAC_CLOCK_TICK - ticks; - ticks = ticks * 10000L / MAC_CLOCK_TICK; + ticks = MAC_CLOCK_TICK - ticks; + ticks = ticks * 10000L / MAC_CLOCK_TICK; - return ticks + offset; + return ticks + offset; } /* @@ -218,27 +228,64 @@ } /* - * The power switch - yes its software! + * The power switch - yes it's software! */ - + void mac_poweroff(void) { -#if 0 + /* - * Powerdown, for the Macs that support it + * MAC_ADB_IISI may need to be moved up here if it doesn't actually + * work using the ADB packet method. --David Kilzer */ - if(rbv) { - via_write(via2, rBufB, via_read(via2, rBufB)&~0x04); - } else { - /* Direction of vDirB is output */ - via_write(via2,vDirB,via_read(via2,vDirB)|0x04); - /* Send a value of 0 on that line */ - via_write(via2,vBufB,via_read(via2,vBufB)&~0x04); + + if (macintosh_config->adb_type == MAC_ADB_II) + { + if(rbv) { + via_write(via2, rBufB, via_read(via2, rBufB)&~0x04); + } else { + /* Direction of vDirB is output */ + via_write(via2,vDirB,via_read(via2,vDirB)|0x04); + /* Send a value of 0 on that line */ + via_write(via2,vBufB,via_read(via2,vBufB)&~0x04); + /* Otherwise it prints "It is now.." then shuts off */ + mdelay(1000); + } + + /* We should never make it this far... */ + printk ("It is now safe to switch off your machine.\n"); + + /* XXX - delay do we need to spin here ? */ + while(1); /* Just in case .. */ + } + + /* + * Initially discovered this technique in the Mach kernel of MkLinux in + * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC + * code in arch/ppc/kernel/setup.c, which also has a PMU technique for PowerBooks! + * --David Kilzer + */ + + else if (macintosh_config->adb_type == MAC_ADB_IISI + || macintosh_config->adb_type == MAC_ADB_CUDA) + { + struct adb_request req; + + /* + * Print our "safe" message before we send the request + * just in case the request never returns. + */ + + printk ("It is now safe to switch off your machine.\n"); + + adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); + + printk ("ADB powerdown request sent.\n"); + for (;;) + { + adb_poll(); + } } -#endif - /* We should never make it this far... */ - /* XXX - delay do we need to spin here ? */ - while(1); /* Just in case .. */ } /* @@ -247,28 +294,65 @@ */ void mac_reset(void) { - unsigned long flags; - unsigned long *reset_hook; + /* + * MAC_ADB_IISI may need to be moved up here if it doesn't actually + * work using the ADB packet method. --David Kilzer + */ - save_flags(flags); - cli(); + if (macintosh_config->adb_type == MAC_ADB_II) + { + unsigned long flags; + unsigned long *reset_hook; -#if 0 /* need ROMBASE in booter */ -#if 0 /* works on some */ - rom_reset = (boot_info.bi_mac.rombase + 0xa); -#else /* testing, doesn't work on SE/30 either */ - reset_hook = (unsigned long *) (boot_info.bi_mac.rombase + 0x4); - printk("ROM reset hook: %p\n", *reset_hook); - rom_reset = *reset_hook; -#endif - rom_reset(); + save_flags(flags); + cli(); + + /* need ROMBASE in booter */ + + /* works on some */ + rom_reset = (void *) (mac_bi_data.rombase + 0xa); + +#if 0 + /* testing, doesn't work on SE/30 either */ + reset_hook = (unsigned long *) (mac_bi_data.rombase + 0x4); + printk("ROM reset hook: %p\n", *reset_hook); + rom_reset = *reset_hook; #endif - restore_flags(flags); - /* We never make it this far... */ - printk(" reboot failed, reboot manually!\n"); - /* XXX - delay do we need to spin here ? */ - while(1); /* Just in case .. */ + rom_reset(); + + restore_flags(flags); + + /* We never make it this far... */ + printk ("Restart failed. Please restart manually.\n"); + + /* XXX - delay do we need to spin here ? */ + while(1); /* Just in case .. */ + } + + /* + * Initially discovered this technique in the Mach kernel of MkLinux in + * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC + * code in arch/ppc/kernel/setup.c, which also has a PMU technique! + * --David Kilzer + * + * I suspect the MAC_ADB_CUDA code might work with other ADB types of machines + * but have no way to test this myself. --DDK + */ + + else if (macintosh_config->adb_type == MAC_ADB_IISI + || macintosh_config->adb_type == MAC_ADB_CUDA) + { + struct adb_request req; + + adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); + + printk ("Restart failed. Please restart manually.\n"); + for (;;) + { + adb_poll(); + } + } } /* diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/via6522.h linux/arch/m68k/mac/via6522.h --- v2.1.124/linux/arch/m68k/mac/via6522.h Tue Feb 17 13:12:45 1998 +++ linux/arch/m68k/mac/via6522.h Mon Oct 5 13:54:39 1998 @@ -6,6 +6,9 @@ * is a bit incomplete as the Mac documentation doesnt cover this well */ +#ifndef _ASM_VIA6522_H_ +#define _ASM_VIA6522_H_ + #define VIABASE 0x50F00000 #define VIABASE2 0x50F02000 @@ -45,17 +48,32 @@ * Register B has the fun stuff in it */ +#define VIA2B_vMode32 0x08 /* 24/32bit switch - doubles as cache flush */ #define VIA2B_vPower 0x04 /* Off switch */ -#define VIA2B_vBusLk 0x02 -#define VIA2B_vCDis 0x01 +#define VIA2B_vBusLk 0x02 /* Nubus in use ?? */ +#define VIA2B_vCDis 0x01 /* Cache disable */ + +/* + * The 6522 via is a 2MHz part, and needs a delay. MacOS seems to + * execute MOV (Ax),(Ax) for this... Oh and we can't use udelay + * here... see we need the via to calibrate the udelay loop ... + */ +extern volatile long *via_memory_bogon; + extern __inline__ void via_write(volatile unsigned char *via,int reg, int v) { + *via_memory_bogon; + *via_memory_bogon; + *via_memory_bogon; via[reg]=v; } extern __inline__ int via_read(volatile unsigned char *via,int reg) { + *via_memory_bogon; + *via_memory_bogon; + *via_memory_bogon; return (int)via[reg]; } @@ -109,3 +127,5 @@ extern void via2_irq(int, void *, struct pt_regs *); extern void via_setup_keyboard(void); + +#endif /* _ASM_VIA6522_H_ */ diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c --- v2.1.124/linux/arch/m68k/mm/memory.c Sat Sep 5 16:46:40 1998 +++ linux/arch/m68k/mm/memory.c Mon Oct 5 13:54:39 1998 @@ -18,6 +18,7 @@ #include #include #include +#include #ifdef CONFIG_AMIGA #include #endif @@ -554,6 +555,14 @@ * this?). So we have to push first and then additionally to invalidate. */ +#ifdef CONFIG_M68K_L2_CACHE +/* + * Jes was worried about performance (urhh ???) so its optional + */ + +extern void (*mach_l2_flush)(int) = NULL; +#endif + /* * cache_clear() semantics: Clear any cache entries for the area in question, * without writing back dirty entries first. This is useful if the data will @@ -593,6 +602,10 @@ "movec %/d0,%/cacr" : : "i" (FLUSH_I_AND_D) : "d0"); +#ifdef CONFIG_M68K_L2_CACHE + if(mach_l2_flush) + mach_l2_flush(0); +#endif } @@ -641,6 +654,10 @@ "movec %/d0,%/cacr" : : "i" (FLUSH_I) : "d0"); +#ifdef CONFIG_M68K_L2_CACHE + if(mach_l2_flush) + mach_l2_flush(1); +#endif } diff -u --recursive --new-file v2.1.124/linux/arch/ppc/config.in linux/arch/ppc/config.in --- v2.1.124/linux/arch/ppc/config.in Mon Oct 5 13:13:36 1998 +++ linux/arch/ppc/config.in Mon Oct 5 13:00:33 1998 @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.71 1998/09/18 13:25:17 cort Exp $ +# $Id: config.in,v 1.72 1998/10/01 14:09:40 cort Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -27,7 +27,7 @@ CHRP CONFIG_CHRP \ PowerMac/PReP/CHRP CONFIG_ALL_PPC \ APUS CONFIG_APUS \ - MBX CONFIG_MBX" PReP + MBX CONFIG_MBX" PowerMac endmenu if [ "$CONFIG_MBX" = "y" ];then @@ -184,10 +184,10 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then if [ "$CONFIG_APUS" = "y" -o "$CONFIG_PMAC" = "y" ]; then - tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND + tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND fi if [ "$CONFIG_PREP" = "y" -o "$CONFIG_CHRP" = "y" ]; then - source drivers/sound/Config.in + source drivers/sound/Config.in fi fi diff -u --recursive --new-file v2.1.124/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c --- v2.1.124/linux/arch/ppc/kernel/setup.c Mon Oct 5 13:13:36 1998 +++ linux/arch/ppc/kernel/setup.c Wed Oct 7 18:52:08 1998 @@ -226,10 +226,7 @@ case _MACH_prep: machine_restart(NULL); case _MACH_apus: -#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) - apm_set_power_state(APM_STATE_OFF); for (;;); -#endif } for (;;); #else /* CONFIG_MBX */ diff -u --recursive --new-file v2.1.124/linux/arch/sparc/kernel/sun4d_smp.c linux/arch/sparc/kernel/sun4d_smp.c --- v2.1.124/linux/arch/sparc/kernel/sun4d_smp.c Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc/kernel/sun4d_smp.c Mon Oct 5 12:28:08 1998 @@ -8,6 +8,7 @@ #include +#include #include #include #include diff -u --recursive --new-file v2.1.124/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c --- v2.1.124/linux/arch/sparc64/kernel/sparc64_ksyms.c Mon Oct 5 13:13:38 1998 +++ linux/arch/sparc64/kernel/sparc64_ksyms.c Mon Oct 5 10:04:35 1998 @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.41 1998/10/04 08:44:16 davem Exp $ +/* $Id: sparc64_ksyms.c,v 1.42 1998/10/05 03:18:50 davem Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -62,6 +62,7 @@ extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern int __strncmp(const char *, const char *, __kernel_size_t); +extern __kernel_size_t __strlen(const char *); extern char saved_command_line[]; extern char *getname32(u32 name); extern void linux_sparc_syscall(void); diff -u --recursive --new-file v2.1.124/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c --- v2.1.124/linux/drivers/block/ide-tape.c Wed Sep 9 14:51:07 1998 +++ linux/drivers/block/ide-tape.c Wed Oct 7 15:54:44 1998 @@ -45,7 +45,7 @@ * flag, can be configured by issuing an ioctl to the block device interface, * as any other ide device. * - * Our own ide-tape ioctl's can can be issued to either the block device or + * Our own ide-tape ioctl's can be issued to either the block device or * the character device interface. * * Maximal throughput with minimal bus load will usually be achieved in the @@ -276,7 +276,7 @@ * loop which checks if the pipeline is empty, and if it is, we * increase the maximum number of stages as necessary until we * reach the optimum value which just manages to keep the tape - * busy with with minimum allocated memory or until we reach + * busy with minimum allocated memory or until we reach * IDETAPE_MAX_PIPELINE_STAGES. * * Concerning (2): diff -u --recursive --new-file v2.1.124/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.1.124/linux/drivers/block/ide.c Thu Sep 17 17:53:35 1998 +++ linux/drivers/block/ide.c Wed Oct 7 15:54:44 1998 @@ -1434,9 +1434,9 @@ * This function issues a special IDE device request * onto the request queue. * - * If action is ide_wait, then then rq is queued at the end of - * the request queue, and the function sleeps until it has been - * processed. This is for use when invoked from an ioctl handler. + * If action is ide_wait, then the rq is queued at the end of the + * request queue, and the function sleeps until it has been processed. + * This is for use when invoked from an ioctl handler. * * If action is ide_preempt, then the rq is queued at the head of * the request queue, displacing the currently-being-processed diff -u --recursive --new-file v2.1.124/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.1.124/linux/drivers/block/ide.h Thu Sep 17 17:53:35 1998 +++ linux/drivers/block/ide.h Thu Oct 8 21:31:26 1998 @@ -611,9 +611,9 @@ * This function issues a special IDE device request * onto the request queue. * - * If action is ide_wait, then then rq is queued at the end of - * the request queue, and the function sleeps until it has been - * processed. This is for use when invoked from an ioctl handler. + * If action is ide_wait, then the rq is queued at the end of the + * request queue, and the function sleeps until it has been processed. + * This is for use when invoked from an ioctl handler. * * If action is ide_preempt, then the rq is queued at the head of * the request queue, displacing the currently-being-processed diff -u --recursive --new-file v2.1.124/linux/drivers/block/md.c linux/drivers/block/md.c --- v2.1.124/linux/drivers/block/md.c Wed Aug 26 11:37:35 1998 +++ linux/drivers/block/md.c Tue Oct 6 08:56:36 1998 @@ -696,7 +696,6 @@ RO_IOCTLS(inode->i_rdev,arg); default: - printk ("Unknown md_ioctl %d\n", cmd); return -EINVAL; } diff -u --recursive --new-file v2.1.124/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.1.124/linux/drivers/char/Config.in Mon Sep 28 10:51:33 1998 +++ linux/drivers/char/Config.in Thu Oct 8 17:25:16 1998 @@ -124,8 +124,10 @@ fi fi tristate '/dev/nvram support' CONFIG_NVRAM -tristate 'PC joystick support' CONFIG_JOYSTICK - +tristate 'Joystick support' CONFIG_JOYSTICK +if [ "$CONFIG_JOYSTICK" != "n" ]; then + source drivers/char/joystick/Config.in +fi mainmenu_option next_comment comment 'Ftape, the floppy tape device driver' tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE diff -u --recursive --new-file v2.1.124/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.1.124/linux/drivers/char/Makefile Mon Oct 5 13:13:39 1998 +++ linux/drivers/char/Makefile Thu Oct 8 17:25:16 1998 @@ -11,7 +11,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) ftape +ALL_SUB_DIRS := $(SUB_DIRS) ftape joystick # # This file contains the font map for the default (hardware) font @@ -159,10 +159,12 @@ endif ifeq ($(CONFIG_JOYSTICK),y) -L_OBJS += joystick.o +L_OBJS += joystick/js.o +SUB_DIRS += joystick +MOD_SUB_DIRS += joystick else ifeq ($(CONFIG_JOYSTICK),m) - M_OBJS += joystick.o + MOD_SUB_DIRS += joystick endif endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/console.c linux/drivers/char/console.c --- v2.1.124/linux/drivers/char/console.c Mon Oct 5 13:13:39 1998 +++ linux/drivers/char/console.c Wed Oct 7 11:18:31 1998 @@ -99,6 +99,7 @@ #include #include #include +#include #include @@ -1953,23 +1954,29 @@ } } +#ifdef CONFIG_VT_CONSOLE + /* * Console on virtual terminal + * + * NOTE NOTE NOTE! This code can do no global locking. In particular, + * we can't disable interrupts or bottom half handlers globally, because + * we can be called from contexts that hold critical spinlocks, and + * trying do get a global lock at this point will lead to deadlocks. */ -#ifdef CONFIG_VT_CONSOLE void vt_console_print(struct console *co, const char * b, unsigned count) { int currcons = fg_console; unsigned char c; - static int printing = 0; + static unsigned long printing = 0; const ushort *start; ushort cnt = 0; ushort myx = x; - if (!printable || printing) - return; /* console not yet initialized */ - printing = 1; + /* console busy or not yet initialized */ + if (!printable || test_and_set_bit(0, &printing)) + return; if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) currcons = kmsg_redirect - 1; @@ -1981,7 +1988,7 @@ } if (vcmode != KD_TEXT) - return; + goto quit; /* undraw cursor first */ if (IS_FG) @@ -1991,11 +1998,8 @@ /* Contrived structure to try to emulate original need_wrap behaviour * Problems caused when we have need_wrap set on '\n' character */ - disable_bh(CONSOLE_BH); while (count--) { - enable_bh(CONSOLE_BH); c = *b++; - disable_bh(CONSOLE_BH); if (c == 10 || c == 13 || c == 8 || need_wrap) { if (cnt > 0) { if (IS_VISIBLE) @@ -2037,12 +2041,11 @@ need_wrap = 1; } } - enable_bh(CONSOLE_BH); set_cursor(currcons); poke_blanked_console(); quit: - printing = 0; + clear_bit(0, &printing); } static kdev_t vt_console_device(struct console *c) diff -u --recursive --new-file v2.1.124/linux/drivers/char/i2c.c linux/drivers/char/i2c.c --- v2.1.124/linux/drivers/char/i2c.c Sun Jul 26 11:57:15 1998 +++ linux/drivers/char/i2c.c Tue Oct 6 09:39:49 1998 @@ -21,7 +21,7 @@ #define I2C_DEBUG(x) if (i2c_debug) (x) static int scan = 0; -static int verbose = 1; +static int verbose = 0; static int i2c_debug = 0; MODULE_PARM(scan,"i"); diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/Config.in linux/drivers/char/joystick/Config.in --- v2.1.124/linux/drivers/char/joystick/Config.in Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/Config.in Thu Oct 8 17:25:16 1998 @@ -0,0 +1,19 @@ +# +# Joystick lowlevel driver configuration +# + +dep_tristate ' Classic PC analog joysticks' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK +dep_tristate ' FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK +dep_tristate ' Gravis GrIP gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK +dep_tristate ' Logitech Digital joysticks' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK +dep_tristate ' Microsoft SideWinder, Genius Digital joysticks' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK +dep_tristate ' ThrustMaster DirectConnect (BSP) joysticks' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK +dep_tristate ' PDPI Lightning L4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK +if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate ' NES, SNES, PSX, Multisystem gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK + dep_tristate ' Sega, Multisystem gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK + dep_tristate ' TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK +fi +if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK +fi diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/Makefile linux/drivers/char/joystick/Makefile --- v2.1.124/linux/drivers/char/joystick/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/Makefile Thu Oct 8 17:25:16 1998 @@ -0,0 +1,112 @@ +# +# Makefile for the joystick drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +O_TARGET := js.o +O_OBJS := +M_OBJS := + +ifeq ($(CONFIG_JOYSTICK),y) +O_OBJS += joystick.o +else + ifeq ($(CONFIG_JOYSTICK),m) + M_OBJS += joystick.o + endif +endif + +ifeq ($(CONFIG_JOY_AMIGA),y) +O_OBJS += joy-amiga.o +else + ifeq ($(CONFIG_JOY_AMIGA),m) + M_OBJS += joy-amiga.o + endif +endif + +ifeq ($(CONFIG_JOY_ANALOG),y) +O_OBJS += joy-analog.o +else + ifeq ($(CONFIG_JOY_ANALOG),m) + M_OBJS += joy-analog.o + endif +endif + +ifeq ($(CONFIG_JOY_ASSASIN),y) +O_OBJS += joy-assasin.o +else + ifeq ($(CONFIG_JOY_ASSASIN),m) + M_OBJS += joy-assasin.o + endif +endif + +ifeq ($(CONFIG_JOY_CONSOLE),y) +O_OBJS += joy-console.o +else + ifeq ($(CONFIG_JOY_CONSOLE),m) + M_OBJS += joy-console.o + endif +endif + +ifeq ($(CONFIG_JOY_DB9),y) +O_OBJS += joy-db9.o +else + ifeq ($(CONFIG_JOY_DB9),m) + M_OBJS += joy-db9.o + endif +endif + +ifeq ($(CONFIG_JOY_GRAVIS),y) +O_OBJS += joy-gravis.o +else + ifeq ($(CONFIG_JOY_GRAVIS),m) + M_OBJS += joy-gravis.o + endif +endif + +ifeq ($(CONFIG_JOY_LIGHTNING),y) +O_OBJS += joy-lightning.o +else + ifeq ($(CONFIG_JOY_LIGHTNING),m) + M_OBJS += joy-lightning.o + endif +endif + +ifeq ($(CONFIG_JOY_LOGITECH),y) +O_OBJS += joy-logitech.o +else + ifeq ($(CONFIG_JOY_LOGITECH),m) + M_OBJS += joy-logitech.o + endif +endif + +ifeq ($(CONFIG_JOY_SIDEWINDER),y) +O_OBJS += joy-sidewinder.o +else + ifeq ($(CONFIG_JOY_SIDEWINDER),m) + M_OBJS += joy-sidewinder.o + endif +endif + +ifeq ($(CONFIG_JOY_THRUSTMASTER),y) +O_OBJS += joy-thrustmaster.o +else + ifeq ($(CONFIG_JOY_THRUSTMASTER),m) + M_OBJS += joy-thrustmaster.o + endif +endif + +ifeq ($(CONFIG_JOY_TURBOGRAFX),y) +O_OBJS += joy-turbografx.o +else + ifeq ($(CONFIG_JOY_TURBOGRAFX),m) + M_OBJS += joy-turbografx.o + endif +endif + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-amiga.c linux/drivers/char/joystick/joy-amiga.c --- v2.1.124/linux/drivers/char/joystick/joy-amiga.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-amiga.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,157 @@ +/* + * joy-amiga.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * microswitch based joystick connected to Amiga joystick port. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct js_port* js_am_port __initdata = NULL; + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_PARM(js_am, "1-2i"); + +static int js_am[]={0,0}; + +/* + * js_am_read() reads and Amiga joystick data. + */ + +static int js_am_read(void *info, int **axes, int **buttons) +{ + int data = 0; + + switch (*(int*)info) { + case 0: + data = ~custom.joy0dat; + buttons[0][0] = (~ciaa.pra >> 6) & 1; + break; + + case 1: + data = ~custom.joy1dat; + buttons[0][0] = (~ciaa.pra >> 7) & 1; + break; + + default: + return -1; + } + + axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); + data = ~(data ^ (data << 1)); + axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); + + return 0; +} + +/* + * js_am_open() is a callback from the file open routine. + */ + +static int js_am_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_am_close() is a callback from the file release routine. + */ + +static int js_am_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_am_init_corr() initializes correction values of + * Amiga joysticks. + */ + +static void __init js_am_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } +} + +#ifndef MODULE +void __init js_am_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_am_init(void) +#endif +{ + int i; + + for (i = 0; i < 2; i++) + if (js_am[i]) { + js_am_port = js_register_port(js_am_port, &i, 1, sizeof(int), js_am_read); + printk(KERN_INFO "js%d: Amiga joystick at joy%ddat\n", + js_register_device(js_am_port, 0, 2, 1, "Amiga joystick", js_am_open, js_am_close), i); + js_am_init_corr(js_am_port->corr); + } + if (js_am_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-amiga: no joysticks specified\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + while (js_am_port) { + if (js_am_port->devs[0]) + js_unregister_device(js_am_port->devs[0]); + js_am_port = js_unregister_port(js_am_port); + } +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-analog.c linux/drivers/char/joystick/joy-analog.c --- v2.1.124/linux/drivers/char/joystick/joy-analog.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-analog.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,208 @@ +/* + * joy-analog.c Version 1.2 + * + * Copyright (c) 1996-1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * up to two analog (or CHF/FCS) joysticks on a single joystick port. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_AN_MAX_TIME 3000 + +static int js_an_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_an_port __initdata = NULL; + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_PARM(js_an, "2-24i"); + +static int js_an[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; + +#include "joy-analog.h" + +/* + * js_an_read() reads analog joystick data. + */ + +static int js_an_read(void *xinfo, int **axes, int **buttons) +{ + struct js_an_info *info = xinfo; + unsigned char buf[4]; + int time[4]; + unsigned char u, v, a; + unsigned int t, t1; + int i, j; + int timeout; + int io = info->io; + + timeout = (JS_AN_MAX_TIME * js_time_speed_a) >> 10; + + info->buttons = (~inb(io) & JS_AN_BUTTONS_STD) >> 4; + + i = 0; + u = a = ((info->mask[0] | info->mask[1]) & JS_AN_AXES_STD) | (info->extensions & JS_AN_HAT_FCS) + | ((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); + + outb(inb(io),io); + t = js_get_time_a(); + do { + v = inb(io) & a; + t1 = js_get_time_a(); + if (u ^ v) { + time[i] = js_delta_a(t1,t); + buf[i] = u ^ v; + u = v; + i++; + } + } while (v && js_delta_a(t1,t) < timeout); + + for (--i; i >= 0; i--) + for (j = 0; j < 4; j++) + if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a; + + js_an_decode(info, axes, buttons); + + return 0; +} + +/* + * js_an_open() is a callback from the file open routine. + */ + +static int js_an_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_an_close() is a callback from the file release routine. + */ + +static int js_an_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_an_probe() probes for analog joysticks. + */ + +static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port) +{ + struct js_an_info info; + int i, numdev; + unsigned char u; + + if (io < 0) return port; + + if (check_region(io, 1)) return port; + + if (((u = inb(io)) & 3) == 3) return port; + outb(u,io); + u = inb(io); + udelay(JS_AN_MAX_TIME); + u = (inb(io) ^ u) & u; + + if (!u) return port; + if (u & 0xf0) return port; + + if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0) + return port; + + info.io = io; + request_region(info.io, 1, "joystick (analog)"); + port = js_register_port(port, &info, numdev, sizeof(struct js_an_info), js_an_read); + + for (i = 0; i < numdev; i++) + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, js_an_axes(i, &info), js_an_buttons(i, &info), + js_an_name(i, &info), js_an_open, js_an_close), + js_an_name(i, &info), info.io); + + js_an_read(port->info, port->axes, port->buttons); + js_an_init_corr(port->info, port->axes, port->corr, 8); + + return port; +} + +#ifndef MODULE +void __init js_an_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_an_init(void) +#endif +{ + int i; + + if (js_an[0] >= 0) { + for (i = 0; (js_an[i*3] >= 0) && i < 8; i++) + js_an_port = js_an_probe(js_an[i*3], js_an[i*3+1], js_an[i*3+2], js_an_port); + } else { + for (i = 0; js_an_port_list[i]; i++) + js_an_port = js_an_probe(js_an_port_list[i], 0, 0, js_an_port); + } + if (js_an_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-analog: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_an_info *info; + + while (js_an_port) { + for (i = 0; i < js_an_port->ndevs; i++) + if (js_an_port->devs[i]) + js_unregister_device(js_an_port->devs[i]); + info = js_an_port->info; + release_region(info->io, 1); + js_an_port = js_unregister_port(js_an_port); + } + +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-analog.h linux/drivers/char/joystick/joy-analog.h --- v2.1.124/linux/drivers/char/joystick/joy-analog.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-analog.h Thu Oct 8 17:25:16 1998 @@ -0,0 +1,297 @@ +/* + * joy-analog.h Version 1.2 + * + * Copyright (c) 1996-1998 Vojtech Pavlik + */ + +/* + * This file is designed to be included in any joystick driver + * that communicates with standard analog joysticks. This currently + * is: joy-analog.c, joy-assasin.c, and joy-lightning.c + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#define JS_AN_AXES_STD 0x0f +#define JS_AN_BUTTONS_STD 0xf0 + +#define JS_AN_BUTTONS_CHF 0x01 +#define JS_AN_HAT1_CHF 0x02 +#define JS_AN_HAT2_CHF 0x04 +#define JS_AN_ANY_CHF 0x07 +#define JS_AN_HAT_FCS 0x08 +#define JS_AN_HATS_ALL 0x0e +#define JS_AN_BUTTON_PXY_X 0x10 +#define JS_AN_BUTTON_PXY_Y 0x20 +#define JS_AN_BUTTON_PXY_U 0x40 +#define JS_AN_BUTTON_PXY_V 0x80 +#define JS_AN_BUTTONS_PXY_XY 0x30 +#define JS_AN_BUTTONS_PXY_UV 0xc0 +#define JS_AN_BUTTONS_PXY 0xf0 + +static struct { + int x; + int y; +} js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}}; + +struct js_an_info { + int io; + unsigned char mask[2]; + unsigned int extensions; + int axes[4]; + int initial[4]; + unsigned char buttons; +}; + +/* + * js_an_decode() decodes analog joystick data. + */ + +static void js_an_decode(struct js_an_info *info, int **axes, int **buttons) +{ + int i, j, k; + int hat1, hat2, hat3; + + hat1 = hat2 = hat3 = 0; + if (info->mask[0] & JS_AN_BUTTONS_STD) buttons[0][0] = 0; + if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0; + + if (info->extensions & JS_AN_ANY_CHF) { + switch (info->buttons) { + case 0x1: buttons[0][0] = 0x01; break; + case 0x2: buttons[0][0] = 0x02; break; + case 0x4: buttons[0][0] = 0x04; break; + case 0x8: buttons[0][0] = 0x08; break; + case 0x5: buttons[0][0] = 0x10; break; + case 0x9: buttons[0][0] = 0x20; break; + case 0xf: hat1 = 1; break; + case 0xb: hat1 = 2; break; + case 0x7: hat1 = 3; break; + case 0x3: hat1 = 4; break; + case 0xe: hat2 = 1; break; + case 0xa: hat2 = 2; break; + case 0x6: hat2 = 3; break; + case 0xc: hat2 = 4; break; + } + k = info->extensions & JS_AN_BUTTONS_CHF ? 6 : 4; + } else { + for (i = 1; i >= 0; i--) + for (j = k = 0; j < 4; j++) + if (info->mask[i] & (0x10 << j)) + buttons[i][0] |= ((info->buttons >> j) & 1) << k++; + } + + if (info->extensions & JS_AN_BUTTON_PXY_X) + buttons[0][0] |= (info->axes[2] < (info->initial[2] >> 1)) << k++; + if (info->extensions & JS_AN_BUTTON_PXY_Y) + buttons[0][0] |= (info->axes[3] < (info->initial[3] >> 1)) << k++; + if (info->extensions & JS_AN_BUTTON_PXY_U) + buttons[0][0] |= (info->axes[2] > (info->initial[2] + (info->initial[2] >> 1))) << k++; + if (info->extensions & JS_AN_BUTTON_PXY_V) + buttons[0][0] |= (info->axes[3] > (info->initial[3] + (info->initial[3] >> 1))) << k++; + + if (info->extensions & JS_AN_HAT_FCS) + for (j = 0; j < 4; j++) + if (info->axes[3] < ((info->initial[3] * ((j << 1) + 1)) >> 3)) { + hat3 = j + 1; + break; + } + + for (i = 1; i >= 0; i--) + for (j = k = 0; j < 4; j++) + if (info->mask[i] & (1 << j)) + axes[i][k++] = info->axes[j]; + + if (info->extensions & JS_AN_HAT1_CHF) { + axes[0][k++] = js_an_hat_to_axis[hat1].x; + axes[0][k++] = js_an_hat_to_axis[hat1].y; + } + if (info->extensions & JS_AN_HAT2_CHF) { + axes[0][k++] = js_an_hat_to_axis[hat2].x; + axes[0][k++] = js_an_hat_to_axis[hat2].y; + } + if (info->extensions & JS_AN_HAT_FCS) { + axes[0][k++] = js_an_hat_to_axis[hat3].x; + axes[0][k++] = js_an_hat_to_axis[hat3].y; + } +} + +/* + * js_an_count_bits() counts set bits in a byte. + */ + +static inline int js_an_count_bits(unsigned long c) +{ + int i = 0; + while (c) { + i += c & 1; + c >>= 1; + } + return i; +} + +/* + * js_an_init_corr() initializes the correction values for + * analog joysticks. + */ + +static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct js_corr **corr, int prec) +{ + int i, j, t; + + for (i = 0; i < 2; i++) + for (j = 0; j < js_an_count_bits(info->mask[i] & 0xf); j++) { + + if ((j == 2 && (info->mask[i] & 0xb) == 0xb) || + (j == 3 && (info->mask[i] & 0xf) == 0xf)) { + t = (axes[i][0] + axes[i][1]) >> 1; + } else { + t = axes[i][j]; + } + + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = prec; + corr[i][j].coef[0] = t - (t >> 3); + corr[i][j].coef[1] = t + (t >> 3); + corr[i][j].coef[2] = (1 << 29) / (t - (t >> 2) + 1); + corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1); + } + + i = js_an_count_bits(info->mask[0] & 0xf); + + for (j = i; j < i + (js_an_count_bits(info->extensions & JS_AN_HATS_ALL) << 1); j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = (1 << 29); + corr[0][j].coef[3] = (1 << 29); + } + + for (i = 0; i < 4; i++) + info->initial[i] = info->axes[i]; +} + + +/* + * js_an_probe_devs() probes for analog joysticks. + */ + +static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0, int mask1, struct js_port *port) +{ + info->mask[0] = info->mask[1] = info->extensions = 0; + + if (mask0 || mask1) { + info->mask[0] = mask0 & (exist | 0xf0); + info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0]; + info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) | + ((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF); + if (info->extensions & JS_AN_BUTTONS_PXY) { + info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2); + info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); + info->mask[1] = 0; + } + if (info->extensions & JS_AN_HAT_FCS) { + info->mask[0] &= ~JS_AN_HAT_FCS; + info->mask[1] = 0; + info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_U); + } + if (info->extensions & JS_AN_ANY_CHF) { + info->mask[0] |= 0xf0; + info->mask[1] = 0; + } + if (!(info->mask[0] | info->mask[1])) return -1; + } else { + switch (exist) { + case 0x0: + return -1; + case 0x3: + info->mask[0] = 0xf3; /* joystick 0, assuming 4-button */ + break; + case 0xb: + info->mask[0] = 0xfb; /* 3-axis, 4-button joystick */ + break; + case 0xc: + info->mask[0] = 0xcc; /* joystick 1 */ + break; + case 0xf: + info->mask[0] = 0x33; /* joysticks 0 and 1 */ + info->mask[1] = 0xcc; + break; + default: + printk(KERN_WARNING "joy-analog: Unknown joystick device detected " + "(data=%#x), contact \n", exist); + return -1; + } + } + + return !!info->mask[0] + !!info->mask[1]; +} + +/* + * js_an_axes() returns the number of axes for an analog joystick. + */ + +static inline int js_an_axes(int i, struct js_an_info *info) +{ + return js_an_count_bits(info->mask[i] & 0x0f) + js_an_count_bits(info->extensions & JS_AN_HATS_ALL) * 2; +} + +/* + * js_an_buttons() returns the number of buttons for an analog joystick. + */ + +static inline int js_an_buttons(int i, struct js_an_info *info) +{ + return js_an_count_bits(info->mask[i] & 0xf0) + + (info->extensions & JS_AN_BUTTONS_CHF) * 2 + + js_an_count_bits(info->extensions & JS_AN_BUTTONS_PXY); +} + +/* + * js_an_name() constructs a name for an analog joystick. + */ + +static char js_an_name_buf[128] __initdata = ""; + +static char __init *js_an_name(int i, struct js_an_info *info) +{ + + sprintf(js_an_name_buf, "Analog %d-axis %d-button", + js_an_count_bits(info->mask[i] & 0x0f), + js_an_buttons(i, info)); + + if (info->extensions & JS_AN_HATS_ALL) + sprintf(js_an_name_buf, "%s %d-hat", + js_an_name_buf, + js_an_count_bits(info->extensions & JS_AN_HATS_ALL)); + + strcat(js_an_name_buf, " joystick"); + + if (info->extensions) + sprintf(js_an_name_buf, "%s with%s%s%s extensions", + js_an_name_buf, + info->extensions & JS_AN_ANY_CHF ? " CHF" : "", + info->extensions & JS_AN_HAT_FCS ? " FCS" : "", + info->extensions & JS_AN_BUTTONS_PXY ? " XY-button" : ""); + + return js_an_name_buf; +} diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-assasin.c linux/drivers/char/joystick/joy-assasin.c --- v2.1.124/linux/drivers/char/joystick/joy-assasin.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-assasin.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,423 @@ +/* + * joy-assasin.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * joysticks using FP-Gaming's Assasin 3D protocol. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_AS_MAX_START 250 +#define JS_AS_MAX_STROBE 50 +#define JS_AS_MAX_TIME 2400 +#define JS_AS_MAX_LENGTH 40 + +#define JS_AS_MODE_A3D 1 /* Assasin 3D */ +#define JS_AS_MODE_PAN 2 /* Panther */ +#define JS_AS_MODE_OEM 3 /* Panther OEM version */ +#define JS_AS_MODE_PXL 4 /* Panther XL */ + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_PARM(js_as, "2-24i"); + +static int js_as[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; + +static int js_as_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_as_port __initdata = NULL; + +#include "joy-analog.h" + +struct js_as_info { + int io; + char mode; + char rudder; + struct js_an_info an; +}; + +/* + * js_as_read_packet() reads an Assasin 3D packet. + */ + +static int js_as_read_packet(int io, int length, char *data) +{ + unsigned char u, v; + int i; + unsigned int t, t1; + unsigned long flags; + + int start = (js_time_speed * JS_AS_MAX_START) >> 10; + int strobe = (js_time_speed * JS_AS_MAX_STROBE) >> 10; + + i = 0; + + __save_flags(flags); + __cli(); + outb(inb(io),io); + + u = inb(io); + t = js_get_time(); + + do { + v = inb(io); + t1 = js_get_time(); + } while (u == v && js_delta(t1, t) < start); + + t = t1; + + do { + v = inb(io); + t1 = js_get_time(); + if ((u ^ v) & u & 0x10) { + data[i++] = v >> 5; + t = t1; + } + u = v; + } while (i < length && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + return i; +} + +/* + * js_as_csum() computes checksum of triplet packet + */ + +static int js_as_csum(char *data, int count) +{ + int i, csum = 0; + for (i = 0; i < count - 2; i++) csum += data[i]; + return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); +} + +/* + * js_as_read() reads and analyzes A3D joystick data. + */ + +static int js_as_read(void *xinfo, int **axes, int **buttons) +{ + struct js_as_info *info = xinfo; + char data[JS_AS_MAX_LENGTH]; + + switch (info->mode) { + + case JS_AS_MODE_A3D: + case JS_AS_MODE_OEM: + case JS_AS_MODE_PAN: + + if (js_as_read_packet(info->io, 29, data) != 29) return -1; + if (data[0] != info->mode) return -1; + if (js_as_csum(data, 29)) return -1; + + axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7); + axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7); + + buttons[0][0] = (data[2] << 2) | (data[3] >> 1); + + info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; + info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; + info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; + info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; + + info->an.buttons = ((data[3] << 3) | data[4]) & 0xf; + + js_an_decode(&info->an, axes + 1, buttons + 1); + + return 0; + + case JS_AS_MODE_PXL: + + if (js_as_read_packet(info->io, 33, data) != 33) return -1; + if (data[0] != info->mode) return -1; + if (js_as_csum(data, 33)) return -1; + + axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128; + axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128; + info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128; + axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128; + + axes[0][3] = ( data[5] & 1) - ((data[5] >> 2) & 1); + axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1); + axes[0][5] = ((data[4] >> 1) & 1) - ( data[3] & 1); + axes[0][6] = ((data[4] >> 2) & 1) - ( data[4] & 1); + + axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7); + axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7); + + buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8]; + + if (info->rudder) axes[1][0] = info->an.axes[0]; + + return 0; + + default: + printk("Error.\n"); + return -1; + } +} + +/* + * js_as_open() is a callback from the file open routine. + */ + +static int js_as_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_as_close() is a callback from the file release routine. + */ + +static int js_as_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_as_pxl_init_corr() initializes the correction values for + * the Panther XL. + */ + +static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = axes[0][i] - 4; + corr[0][i].coef[1] = axes[0][i] + 4; + corr[0][i].coef[2] = (1 << 29) / (127 - 32); + corr[0][i].coef[3] = (1 << 29) / (127 - 32); + } + + corr[0][2].type = JS_CORR_BROKEN; + corr[0][2].prec = 0; + corr[0][2].coef[0] = 127 - 4; + corr[0][2].coef[1] = 128 + 4; + corr[0][2].coef[2] = (1 << 29) / (127 - 6); + corr[0][2].coef[3] = (1 << 29) / (127 - 6); + + for (i = 3; i < 7; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } + + for (i = 7; i < 9; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = -1; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (104 << 14); + corr[0][i].coef[3] = (104 << 14); + } +} + +/* + * js_as_as_init_corr() initializes the correction values for + * the Panther and Assasin. + */ + +static void __init js_as_as_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = -1; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (104 << 14); + corr[0][i].coef[3] = (104 << 14); + } +} + +/* + * js_as_rudder_init_corr() initializes the correction values for + * the Panther XL connected rudder. + */ + +static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes) +{ + corr[1][0].type = JS_CORR_BROKEN; + corr[1][0].prec = 0; + corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3); + corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3); + corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); + corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); +} + +/* + * js_as_probe() probes for A3D joysticks. + */ + +static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port) +{ + struct js_as_info iniinfo; + struct js_as_info *info = &iniinfo; + char *name; + char data[JS_AS_MAX_LENGTH]; + unsigned char u; + int i; + int numdev; + + memset(info, 0, sizeof(struct js_as_info)); + + if (io < 0) return port; + + if (check_region(io, 1)) return port; + if (((u = inb(io)) & 3) == 3) return port; + outb(u,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + if (js_as_read_packet(io, 1, data) != 1) return port; + + if (data[0] && data[0] <= 4) { + info->mode = data[0]; + info->io = io; + request_region(io, 1, "joystick (assasin)"); + port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read); + info = port->info; + } else { + printk(KERN_WARNING "joy-assasin: unknown joystick device detected " + "(io=%#x, id=%d), contact \n", io, data[0]); + return port; + } + + udelay(JS_AS_MAX_TIME); + + if (info->mode == JS_AS_MODE_PXL) { + printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n", + js_register_device(port, 0, 9, 9, "MadCatz Panther XL", js_as_open, js_as_close), + info->io); + js_as_read(port->info, port->axes, port->buttons); + js_as_pxl_init_corr(port->corr, port->axes); + if (info->an.axes[0] < 254) { + printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n", + js_register_device(port, 1, 1, 0, "Analog rudder", js_as_open, js_as_close)); + info->rudder = 1; + port->axes[1][0] = info->an.axes[0]; + js_as_rudder_init_corr(port->corr, port->axes); + } + return port; + } + + switch (info->mode) { + case JS_AS_MODE_A3D: name = "FP-Gaming Assasin 3D"; break; + case JS_AS_MODE_PAN: name = "MadCatz Panther"; break; + case JS_AS_MODE_OEM: name = "OEM Assasin 3D"; break; + default: name = "This cannot happen"; break; + } + + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, 0, 2, 3, name, js_as_open, js_as_close), + name, info->io); + + js_as_as_init_corr(port->corr); + + js_as_read(port->info, port->axes, port->buttons); + + for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i; + + if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) + return port; + + for (i = 0; i < numdev; i++) + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), + js_an_name(i, &info->an), js_as_open, js_as_close), + js_an_name(i, &info->an), name); + + js_an_decode(&info->an, port->axes + 1, port->buttons + 1); + js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0); + + return port; +} + +#ifndef MODULE +void __init js_as_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_as_init(void) +#endif +{ + int i; + + if (js_as[0] >= 0) { + for (i = 0; (js_as[i*3] >= 0) && i < 8; i++) + js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port); + } else { + for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port); + } + if (js_as_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-assasin: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_as_info *info; + + while (js_as_port) { + for (i = 0; i < js_as_port->ndevs; i++) + if (js_as_port->devs[i]) + js_unregister_device(js_as_port->devs[i]); + info = js_as_port->info; + release_region(info->io, 1); + js_as_port = js_unregister_port(js_as_port); + } + +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-console.c linux/drivers/char/joystick/joy-console.c --- v2.1.124/linux/drivers/char/joystick/joy-console.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-console.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,625 @@ +/* + * joy-console.c Version 0.10 + * + * Copyright (c) 1998 Andree Borrmann + */ + +/* + * This is a module for the Linux joystick driver, supporting + * console (NES, SNES, Multi1, Multi2, PSX) gamepads connected + * via parallel port. Up to five such controllers can be + * connected to one parallel port. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Andree Borrmann "); +MODULE_PARM(js_console, "2-6i"); +MODULE_PARM(js_console2,"2-6i"); +MODULE_PARM(js_console3,"2-6i"); + + +#define JS_NO_PAD 0 +#define JS_SNES_PAD 1 +#define JS_NES_PAD 2 +#define JS_NES4_PAD 3 +#define JS_MULTI_STICK 4 +#define JS_MULTI2_STICK 5 +#define JS_PSX_PAD 6 + +#define JS_MAX_PAD JS_PSX_PAD + +struct js_console_info { +#ifdef USE_PARPORT + struct pardevice *port; /* parport device */ + int use; /* use count */ + int wanted; /* parport wanted */ +#else + int port; /* hw port */ +#endif + int pads; /* total number of pads */ + int snes; /* SNES pads */ + int nes; /* NES pads */ + int multi; /* Multi joysticks */ + int multi2; /* Multi joysticks with 2 buttons */ + int psx; /* Normal PSX controllers */ + int negcon; /* PSX NEGCON controllers */ +}; + +static struct js_port* js_console_port = NULL; + +static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console2[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console3[] __initdata = { -1, 0, 0, 0, 0, 0 }; + +static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; + +/* + * NES/SNES support. + */ + +#define JS_NES_DELAY 6 /* Delay between bits - 6us */ + +#define JS_NES_LENGTH 8 /* The NES pads use 8 bits of data */ + +#define JS_NES_A 0 +#define JS_NES_B 1 +#define JS_NES_START 2 +#define JS_NES_SELECT 3 +#define JS_NES_UP 4 +#define JS_NES_DOWN 5 +#define JS_NES_LEFT 6 +#define JS_NES_RIGHT 7 + +#define JS_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */ + +#define JS_SNES_B 0 +#define JS_SNES_Y 1 +#define JS_SNES_START 2 +#define JS_SNES_SELECT 3 +#define JS_SNES_UP 4 +#define JS_SNES_DOWN 5 +#define JS_SNES_LEFT 6 +#define JS_SNES_RIGHT 7 +#define JS_SNES_A 8 +#define JS_SNES_X 9 +#define JS_SNES_L 10 +#define JS_SNES_R 11 + +#define JS_NES_POWER 0xf8 +#define JS_NES_CLOCK 0x01 +#define JS_NES_LATCH 0x02 + +/* + * js_nes_read_packet() reads a NES/SNES packet. + * Each pad uses one bit per byte. So all pads connected to + * this port are read in parallel. + */ + +static void js_nes_read_packet(struct js_console_info *info, int length, unsigned char *data) +{ + int i; + + JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK + JS_NES_LATCH, info->port); + udelay(JS_NES_DELAY * 2); + JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + + for (i = 0; i < length; i++) { + udelay(JS_NES_DELAY); + JS_PAR_DATA_OUT(JS_NES_POWER, info->port); + data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + udelay(JS_NES_DELAY); + JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + } +} + +/* + * Multisystem joystick support + */ + +#define JS_MULTI_LENGTH 5 /* Multi system joystick packet lenght is 5 */ +#define JS_MULTI2_LENGTH 6 /* One more bit for one more button */ + +#define JS_MULTI_UP 0 +#define JS_MULTI_DOWN 1 +#define JS_MULTI_LEFT 2 +#define JS_MULTI_RIGHT 3 +#define JS_MULTI_BUTTON 4 +#define JS_MULTI_BUTTON2 5 + +/* + * js_multi_read_packet() reads a Multisystem joystick packet. + */ + +static void js_multi_read_packet(struct js_console_info *info, int length, unsigned char *data) +{ + int i; + + for (i = 0; i < length; i++) { + JS_PAR_DATA_OUT(~(1 << i), info->port); + data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + } +} + +/* + * PSX support + */ + +#define JS_PSX_DELAY 10 + +#define JS_PSX_LENGTH 8 + +#define JS_PSX_NORMAL 0x41 +#define JS_PSX_NEGCON 0x23 +#define JS_PSX_MOUSE 0x12 + +#define JS_PSX_SELBUT 0x01 +#define JS_PSX_START 0x08 +#define JS_PSX_UP 0x10 +#define JS_PSX_RIGHT 0x20 +#define JS_PSX_DOWN 0x40 +#define JS_PSX_LEFT 0x80 + +#define JS_PSX_CLOCK 0x01 +#define JS_PSX_COMMAND 0x02 +#define JS_PSX_POWER 0xf8 +#define JS_PSX_NOPOWER 0x04 +#define JS_PSX_SELECT 0x08 + +#define JS_PSX_CTRL_OUT(X,Y) JS_PAR_CTRL_OUT((X)^0x0f, Y) + +/* + * js_psx_command() writes 8bit command and reads 8bit data from + * the psx pad. + */ + +static int js_psx_command(struct js_console_info *info, int b) +{ + int i, cmd, ret=0; + + cmd = (b&1)?JS_PSX_COMMAND:0; + for (i=0; i<8; i++) { + JS_PSX_CTRL_OUT(cmd, info->port); + udelay(JS_PSX_DELAY); + ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<port); + udelay(JS_PSX_DELAY); + b >>= 1; + } + return ret; +} + +/* + * js_psx_read_packet() reads a whole psx packet and returns + * device identifier code. + */ + +static int js_psx_read_packet(struct js_console_info *info, int length, unsigned char *data) +{ + int i, ret; + unsigned long flags; + + __save_flags(flags); + __cli(); + + JS_PAR_DATA_OUT(JS_PSX_POWER, info->port); + + JS_PSX_CTRL_OUT(JS_PSX_CLOCK | JS_PSX_SELECT, info->port); /* Select pad */ + udelay(JS_PSX_DELAY*2); + js_psx_command(info, 0x01); /* Access pad */ + ret = js_psx_command(info, 0x42); /* Get device id */ + if (js_psx_command(info, 0)=='Z') /* okay? */ + for (i=0; iport); + __restore_flags(flags); + + return ret; +} + + +/* + * js_console_read() reads and analyzes console pads data. + */ + +#define JS_MAX_LENGTH JS_SNES_LENGTH + +static int js_console_read(void *xinfo, int **axes, int **buttons) +{ + struct js_console_info *info = xinfo; + unsigned char data[JS_MAX_LENGTH]; + + int i, s; + int n = 0; + +/* + * NES and SNES pads + */ + + if (info->nes || info->snes) { + + js_nes_read_packet(info, info->snes ? JS_SNES_LENGTH : JS_NES_LENGTH, data); + + for (i = 0; i < 5; i++) { + s = status_bit[i]; + if (info->nes & s) { + axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); + axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); + + buttons[n][0] = ((data[JS_NES_A] &s)?1:0) | ((data[JS_NES_B] &s)?2:0) + | ((data[JS_NES_START]&s)?4:0) | ((data[JS_NES_SELECT]&s)?8:0); + + n++; + } else + if (info->snes & s) { + axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); + axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); + + buttons[n][0] = ((data[JS_SNES_A] &s)?0x01:0) | ((data[JS_SNES_B] &s)?0x02:0) + | ((data[JS_SNES_X] &s)?0x04:0) | ((data[JS_SNES_Y] &s)?0x08:0) + | ((data[JS_SNES_L] &s)?0x10:0) | ((data[JS_SNES_R] &s)?0x20:0) + | ((data[JS_SNES_START]&s)?0x40:0) | ((data[JS_SNES_SELECT]&s)?0x80:0); + n++; + } + } + } + +/* + * Multi and Multi2 joysticks + */ + + if (info->multi || info->multi2) { + + js_multi_read_packet(info, info->multi2 ? JS_MULTI2_LENGTH : JS_MULTI_LENGTH, data); + + for (i = 0; i < 5; i++) { + s = status_bit[i]; + if (info->multi & s) { + axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); + axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); + + buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0; + + n++; + } else + if (info->multi2 & s) { + axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); + axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); + + buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0; + + n++; + } + } + } + +/* + * PSX controllers + */ + + if (info->psx && (js_psx_read_packet(info, 2, data) == JS_PSX_NORMAL)) { /* FIXME? >1 PSX pads? */ + + axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); + axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); + + buttons[n][0] = ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | + (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); + + n++; + } + + return -(n != info->pads); +} + +/* + * open callback: claim parport. + */ + +int js_console_open(struct js_dev *dev) +{ + MOD_INC_USE_COUNT; + +#ifdef USE_PARPORT + { + struct js_console_info *info = dev->port->info; + if (!info->use && parport_claim(info->port)) { + printk(KERN_WARNING "joy-console: parport busy!\n"); /* port currently not available ... */ + info->wanted++; /* we'll claim it on wakeup */ + return 0; + } + info->use++; + } +#endif + return 0; +} + +/* + * close callback: release parport + */ + +int js_console_close(struct js_dev *dev) +{ +#ifdef USE_PARPORT + struct js_console_info *info = dev->port->info; + + if (!--info->use) + parport_release(info->port); +#endif + MOD_DEC_USE_COUNT; + + return 0; +} + +/* + * parport wakeup callback: claim the port! + */ + +#ifdef USE_PARPORT +static void js_console_wakeup(void *v) +{ + struct js_console_info *info = js_console_port->info; /* FIXME! We can have more than 1 port! */ + + if (!info->use && info->wanted) + { + parport_claim(info->port); + info->use++; + info->wanted--; + } +} +#endif + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_console_info *info; + int i; + + while (js_console_port) { + for (i = 0; i < js_console_port->ndevs; i++) + if (js_console_port->devs[i]) + js_unregister_device(js_console_port->devs[i]); + info = js_console_port->info; +#ifdef USE_PARPORT + parport_unregister_device(info->port); +#else + release_region(info->port, 3); +#endif + js_console_port = js_unregister_port(js_console_port); + } +} +#endif + +/* + * js_console_init_corr() initializes correction values of + * console gamepads. + */ + +static void __init js_console_init_corr(int num_axes, struct js_corr *corr) +{ + int i; + + for (i = 0; i < num_axes; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } +} + +/* + * js_console_probe() probes for console gamepads. + * Only PSX pads can really be probed for. + */ + +static struct js_port __init *js_console_probe(int *config, struct js_port *port) +{ + char *name[5]; + int i, psx, axes[5], buttons[5]; + unsigned char data[2]; /* used for PSX probe */ + struct js_console_info info; + + memset(&info, 0, sizeof(struct js_console_info)); + + if (config[0] < 0) return port; + +#ifdef USE_PARPORT + { + struct parport *pp; + + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; + + if (!pp) { + printk(KERN_ERR "joy-console: no such parport\n"); + return port; + } + + info.port = parport_register_device(pp, "joystick (console)", NULL, js_console_wakeup, NULL, 0, NULL); + info.wanted = 0; + info.use = 0; + } + + if (parport_claim(info.port)) + { + parport_unregister_device(info.port); /* port currently not available ... */ + return port; + } +#else + info.port = config[0]; + if (check_region(info.port, 3)) return port; + request_region(info.port, 3, "joystick (console pad)"); +#endif + + for (i = 0; i < 5; i++) + switch(config[i+1]) { + + case JS_NO_PAD: + + break; + + case JS_SNES_PAD: + + axes[info.pads] = 2; + buttons[info.pads] = 8; + name[info.pads] = "SNES pad"; + info.snes |= status_bit[i]; + info.pads++; + break; + + case JS_NES_PAD: + + axes[info.pads] = 2; + buttons[info.pads] = 4; + name[info.pads] = "NES pad"; + info.nes |= status_bit[i]; + info.pads++; + break; + + case JS_MULTI_STICK: + + axes[info.pads] = 2; + buttons[info.pads] = 1; + name[info.pads] = "Multisystem joystick"; + info.multi |= status_bit[i]; + info.pads++; + break; + + case JS_MULTI2_STICK: + + axes[info.pads] = 2; + buttons[info.pads] = 2; + name[info.pads] = "Multisystem joystick (2 fire)"; + info.multi |= status_bit[i]; + info.pads++; + break; + + case JS_PSX_PAD: + + info.psx |= status_bit[i]; + psx = js_psx_read_packet(&info, 2, data); + psx = js_psx_read_packet(&info, 2, data); + info.psx &= ~status_bit[i]; + + switch(psx) { + case JS_PSX_NORMAL: + axes[info.pads] = 2; + buttons[info.pads] = 10; + name[info.pads] = "PSX controller"; + info.psx |= status_bit[i]; + info.pads++; + break; + case JS_PSX_NEGCON: + printk(KERN_WARNING "joy-console: NegCon not yet supported...\n"); + break; + case JS_PSX_MOUSE: + printk(KERN_WARNING "joy-console: PSX mouse not supported...\n"); + break; + case -1: + printk(KERN_ERR "joy-console: no PSX controller found...\n"); + break; + default: + printk(KERN_WARNING "joy-console: unknown PSX controller 0x%x\n", psx); + } + break; + + default: + + printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]); + } + + if (!info.pads) { +#ifdef USE_PARPORT + parport_release(info.port); + parport_unregister_device(info.port); +#else + release_region(info.port, 3); +#endif + return port; + } + + port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read); + + for (i = 0; i < info.pads; i++) { +#ifdef USE_PARPORT + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), + name[i], info.port->port->name); +#else + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), + name[i], info.port); +#endif + + js_console_init_corr(axes[i], port->corr[i]); + } + +#ifdef USE_PARPORT + parport_release(info.port); +#endif + return port; +} + +#ifndef MODULE +void __init js_console_setup(char *str, int *ints) +{ + int i; + + if (!strcmp(str,"js_console")) + for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; + if (!strcmp(str,"js_console2")) + for (i = 0; i <= ints[0] && i < 6; i++) js_console2[i] = ints[i+1]; + if (!strcmp(str,"js_console3")) + for (i = 0; i <= ints[0] && i < 6; i++) js_console3[i] = ints[i+1]; + +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_console_init(void) +#endif +{ + js_console_port = js_console_probe(js_console, js_console_port); + js_console_port = js_console_probe(js_console2, js_console_port); + js_console_port = js_console_probe(js_console3, js_console_port); + + if (js_console_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-console: no joysticks specified\n"); +#endif + return -ENODEV; +} diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-db9.c linux/drivers/char/joystick/joy-db9.c --- v2.1.124/linux/drivers/char/joystick/joy-db9.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-db9.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,453 @@ +/* + * joy-db9.c Version 0.3V + * + * Copyright (c) 1998 Andree Borrmann + */ + +/* + * This is a module for the Linux joystick driver, supporting + * console (Atari, Amstrad, Commodore, Amiga, Sega) joysticks + * and gamepads connected to the parallel port. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Andree Borrmann "); +MODULE_PARM(js_db9, "2i"); +MODULE_PARM(js_db9_2, "0-2i"); +MODULE_PARM(js_db9_3, "0-2i"); + +#define JS_MULTI_STICK 0x01 +#define JS_MULTI2_STICK 0x02 +#define JS_GENESIS_PAD 0x03 +#define JS_GENESIS5_PAD 0x05 +#define JS_GENESIS6_PAD 0x06 +#define JS_SATURN_PAD 0x07 + +#define JS_MAX_PAD (JS_SATURN_PAD + 1) + +#define JS_DB9_UP 0x01 +#define JS_DB9_DOWN 0x02 +#define JS_DB9_LEFT 0x04 +#define JS_DB9_RIGHT 0x08 +#define JS_DB9_FIRE1 0x10 +#define JS_DB9_FIRE2 0x20 +#define JS_DB9_FIRE3 0x40 +#define JS_DB9_FIRE4 0x80 + +#define JS_DB9_NORMAL 0x22 +#define JS_DB9_NOSELECT 0x20 + +#define JS_DB9_SATURN0 0x20 +#define JS_DB9_SATURN1 0x22 +#define JS_DB9_SATURN2 0x24 +#define JS_DB9_SATURN3 0x26 + +#define JS_GENESIS6_DELAY 14 + +static struct js_port* js_db9_port = NULL; + +static int js_db9[] __initdata = { -1, 0 }; +static int js_db9_2[] __initdata = { -1, 0 }; +static int js_db9_3[] __initdata = { -1, 0 }; + +struct js_db9_info { +#ifdef USE_PARPORT + struct pardevice *port; /* parport device */ + int wanted; /* parport wanted */ +#else + int port; /* hw port */ +#endif + int use; /* use count */ + int mode; /* pad mode */ +}; + +/* + * js_db9_read() reads and analyzes db9 joystick data. + */ + +static int js_db9_read(void *xinfo, int **axes, int **buttons) +{ + struct js_db9_info *info = xinfo; + int data; + + switch(info->mode) + { + case JS_MULTI_STICK: + + data = JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:1); + + break; + + case JS_MULTI2_STICK: + + data=JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:2); + + break; + + case JS_GENESIS_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); + data = JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:2) | (data&JS_DB9_FIRE2?0:4); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:8); + + break; + + case JS_GENESIS5_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); + data=JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08) | + (data&JS_DB9_LEFT ?0:0x10) | (data&JS_DB9_RIGHT?0:0x20); + break; + + case JS_GENESIS6_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); /* 1 */ + udelay(JS_GENESIS6_DELAY); + data=JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + udelay(JS_GENESIS6_DELAY); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08); + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 2 */ + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */ + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN?0:0x20) | (data&JS_DB9_UP?0:0x40); + + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */ + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + + break; + + case JS_SATURN_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_SATURN0, info->port); + data = JS_PAR_DATA_IN(info->port); + + buttons[0][0] = (data&JS_DB9_UP ?0:0x20) | (data&JS_DB9_DOWN ?0:0x10) | + (data&JS_DB9_LEFT?0:0x08) | (data&JS_DB9_RIGHT?0:0x40); + + JS_PAR_CTRL_OUT(JS_DB9_SATURN2, info->port); + data = JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + data = JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_UP ?0:0x02) | (data&JS_DB9_DOWN ?0:0x04) | + (data&JS_DB9_LEFT?0:0x01) | (data&JS_DB9_RIGHT?0:0x80); + + + break; + + default: + return -1; + } + + return 0; +} + +/* + * js_db9_enable_ps2() enables PS/2 capabilities on a parallel port and + * switches data lines to input mode. We should use parport_change_mode() for + * that if parport present - unfortunately that does nothing and only contains + * a FIXME comment. + */ + +static inline void js_db9_enable_ps2(struct js_db9_info *info) +{ +#ifdef USE_PARPORT + int io = info->port->port->base; +#else + int io = info->port; +#endif + + outb(0x35,io+0x402); /* enable PS/2 mode: */ + outb(JS_DB9_NORMAL,io+2); /* reverse direction, enable Select signal: */ +} + +/* + * js_db9_disable_ps2() disables PS/2 capabilities on a parallel port + * and restores it to standard mode. + */ + +static inline void js_db9_disable_ps2(struct js_db9_info *info) +{ +#ifdef USE_PARPORT + int io = info->port->port->base; +#else + int io = info->port; +#endif + + outb(0,io+2); /* normal direction */ + outb(0x15,io+0x402); /* enable normal mode */ +} + +/* + * open callback: claim parport. + */ + +int js_db9_open(struct js_dev *dev) +{ + struct js_db9_info *info = dev->port->info; + + MOD_INC_USE_COUNT; + if (!info->use) { + +#ifdef USE_PARPORT + if (parport_claim(info->port)) { + printk(KERN_WARNING "joy-db9: parport busy\n"); /* port currently not available ... */ + info->wanted++; /* we'll claim it on wakeup */ + return 0; + } +#endif + js_db9_enable_ps2(info); + } + info->use++; + return 0; +} + +/* + * close callback: release parport + */ + +int js_db9_close(struct js_dev *dev) +{ + struct js_db9_info *info = dev->port->info; + + MOD_DEC_USE_COUNT; + info->use--; + + if (!info->use) { + js_db9_disable_ps2(info); +#ifdef USE_PARPORT + parport_release(info->port); +#endif + } + return 0; +} + +/* + * parport wakeup callback: claim the port! + */ + +#ifdef USE_PARPORT +static void js_db9_wakeup(void *v) +{ + struct js_db9_info *info = js_db9_port->info; /* FIXME! We can have more than 1 port! */ + + if (!info->use && info->wanted) + { + parport_claim(info->port); + js_db9_enable_ps2(info); + info->use++; + info->wanted--; + } +} +#endif + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_db9_info *info; + + while (js_db9_port) { + js_unregister_device(js_db9_port->devs[0]); + info = js_db9_port->info; +#ifdef USE_PARPORT + parport_unregister_device(info->port); +#else + release_region(info->port, 3); + release_region(info->port+0x402, 1); +#endif + js_db9_port = js_unregister_port(js_db9_port); + } + +} +#endif + +/* + * js_db9_init_corr() initializes correction values of + * db9 gamepads. + */ + +static void __init js_db9_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } +} + +/* + * js_db9_probe() probes for db9 gamepads. + */ + +static struct js_port __init *js_db9_probe(int *config, struct js_port *port) +{ + struct js_db9_info info; + char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,7,8}; + char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", + NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad"}; + + if (config[0] < 0) return port; + if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port; + +#ifdef USE_PARPORT + { + struct parport *pp; + + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; + + if (!pp) { + printk(KERN_ERR "joy-db9: no such parport\n"); + return port; + } + + if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2))) { + printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); + return port; + } + + info.port = parport_register_device(pp, "joystick (db9)", NULL, js_db9_wakeup, NULL, 0, NULL); + info.wanted = 0; + } +#else + info.port = config[0]; + if (check_region(info.port, 3) || check_region(info.port+0x402,1)) return port; + request_region(info.port, 3, "joystick (db9)"); + request_region(info.port+0x402, 1, "joystick (db9)"); +#endif + + info.mode = config[1]; + info.use = 0; + + port = js_register_port(port, &info, 1, sizeof(struct js_db9_info), js_db9_read); + +#ifdef USE_PARPORT + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), + name[info.mode], info.port->port->name); +#else + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), + name[info.mode], info.port); +#endif + + js_db9_init_corr(port->corr); + + return port; +} + +#ifndef MODULE +void __init js_db9_setup(char *str, int *ints) +{ + int i; + + if (!strcmp(str,"js_db9")) + for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; + if (!strcmp(str,"js_db9_2")) + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; + if (!strcmp(str,"js_db9_3")) + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; + +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_db9_init(void) +#endif +{ + js_db9_port = js_db9_probe(js_db9, js_db9_port); + js_db9_port = js_db9_probe(js_db9_2, js_db9_port); + js_db9_port = js_db9_probe(js_db9_3, js_db9_port); + + if (js_db9_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-db9: no joysticks specified\n"); +#endif + return -ENODEV; +} diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-gravis.c linux/drivers/char/joystick/joy-gravis.c --- v2.1.124/linux/drivers/char/joystick/joy-gravis.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-gravis.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,356 @@ +/* + * joy-gravis.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Gravis GrIP digital joystick family. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_GR_MODE_GPP 1 +#define JS_GR_LENGTH_GPP 24 +#define JS_GR_STROBE_GPP 75 + +#define JS_GR_MODE_XT 2 +#define JS_GR_LENGTH_XT 4 +#define JS_GR_STROBE_XT 30 +#define JS_GR_MAX_CHUNKS_XT 10 +#define JS_GR_MAX_BITS_XT 30 + +static int js_gr_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_gr_port __initdata = NULL; + +struct js_gr_info { + int io; + unsigned char mode[2]; +}; + +/* + * js_gr_gpp_read_packet() reads a Gravis GamePad Pro packet. + */ + +static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data) +{ + unsigned int t, t1; + unsigned char u, v; + int i; + unsigned long flags; + + int strobe = (js_time_speed * JS_GR_STROBE_GPP) >> 10; + + i = 0; + data[0] = 0; + + __save_flags(flags); + __cli(); + u = inb(io) >> shift; + t = js_get_time(); + + do { + v = (inb(io) >> shift) & 3; + t1 = js_get_time(); + if ((u ^ v) & u & 1) { + data[0] |= (v >> 1) << i++; + t = t1; + } + u = v; + } while (i < JS_GR_LENGTH_GPP && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + if (i < JS_GR_LENGTH_GPP) return -1; + + for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) + data[0] = data[0] >> 1 | (data[0] & 1) << 23; + + return -(i == JS_GR_LENGTH_GPP); +} + +/* + * js_gr_xt_read_packet() reads a Gravis Xterminator packet. + */ + +static int js_gr_xt_read_packet(int io, int shift, unsigned int *data) +{ + unsigned int t, t1; + unsigned char u, v, w; + unsigned int i, j, buf, crc; + unsigned long flags; + char status; + + int strobe = (js_time_speed * JS_GR_STROBE_XT) >> 10; + + data[0] = data[1] = data[2] = data[3] = 0; + status = buf = i = j = 0; + + __save_flags(flags); + __cli(); + + v = w = (inb(io) >> shift) & 3; + t = js_get_time(); + + do { + u = (inb(io) >> shift) & 3; + t1 = js_get_time(); + + if (u ^ v) { + + if ((u ^ v) & 1) { + buf = (buf << 1) | (u >> 1); + i++; + } else + + if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { + if (i == 20) { + crc = buf ^ (buf >> 7) ^ (buf >> 14); + if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { + data[buf >> 18] = buf >> 4; + status |= 1 << (buf >> 18); + } + j++; + } + buf = 0; + i = 0; + } + + t = t1; + w = v; + v = u; + } + + } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + return -(status != 0xf); +} + +/* + * js_gr_read() reads and analyzes GrIP joystick data. + */ + +static int js_gr_read(void *xinfo, int **axes, int **buttons) +{ + struct js_gr_info *info = xinfo; + unsigned int data[JS_GR_LENGTH_XT]; + int i; + + for (i = 0; i < 2; i++) + switch (info->mode[i]) { + + case JS_GR_MODE_GPP: + + if (js_gr_gpp_read_packet(info->io, (i << 1) + 4, data)) return -1; + + axes[i][0] = ((data[0] >> 15) & 1) - ((data[0] >> 16) & 1); + axes[i][1] = ((data[0] >> 13) & 1) - ((data[0] >> 12) & 1); + + data[0] = ((data[0] >> 6) & 0x37) | (data[0] & 0x08) | ((data[0] << 1) & 0x40) | + ((data[0] << 5) & 0x80) | ((data[0] << 8) & 0x300); + + buttons[i][0] = (data[0] & 0xfc) | ((data[0] >> 1) & 0x101) | ((data[0] << 1) & 0x202); + + break; + + case JS_GR_MODE_XT: + + if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1; + + axes[i][0] = (data[0] >> 2) & 0x3f; + axes[i][1] = 63 - ((data[0] >> 8) & 0x3f); + axes[i][2] = (data[1] >> 2) & 0x3f; + axes[i][3] = (data[1] >> 8) & 0x3f; + axes[i][4] = (data[2] >> 8) & 0x3f; + axes[i][5] = ((data[2] >> 1) & 1) - ( data[2] & 1); + axes[i][6] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1); + axes[i][7] = ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1); + axes[i][8] = ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1); + + buttons[i][0] = (data[3] >> 3) & 0x7ff; + + break; + + default: + break; + + } + + + return 0; +} + +/* + * js_gr_open() is a callback from the file open routine. + */ + +static int js_gr_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_gr_close() is a callback from the file release routine. + */ + +static int js_gr_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_gr_init_corr() initializes correction values of + * GrIP joysticks. + */ + +static void __init js_gr_init_corr(int mode, struct js_corr *corr) +{ + int i; + + switch (mode) { + + case JS_GR_MODE_GPP: + + for (i = 0; i < 2; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } + + break; + + case JS_GR_MODE_XT: + + for (i = 0; i < 5; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 31 - 4; + corr[i].coef[1] = 32 + 4; + corr[i].coef[2] = (1 << 29) / (32 - 14); + corr[i].coef[3] = (1 << 29) / (32 - 14); + } + + for (i = 5; i < 9; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } + + break; + + } +} + +/* + * js_gr_probe() probes fro GrIP joysticks. + */ + +static struct js_port __init *js_gr_probe(int io, struct js_port *port) +{ + struct js_gr_info info; + char *names[] = { NULL, "Gravis GamePad Pro", "Gravis Xterminator" }; + char axes[] = { 0, 2, 9 }; + char buttons[] = { 0, 10, 11 }; + unsigned int data[JS_GR_LENGTH_XT]; + int i; + + if (check_region(io, 1)) return port; + + info.mode[0] = info.mode[1] = 0; + + for (i = 0; i < 2; i++) { + if (!js_gr_gpp_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_GPP; + if (!js_gr_xt_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_XT; + } + + if (!info.mode[0] && !info.mode[1]) return port; + + info.io = io; + + request_region(io, 1, "joystick (gravis)"); + port = js_register_port(port, &info, 2, sizeof(struct js_gr_info), js_gr_read); + + for (i = 0; i < 2; i++) + if (info.mode[i]) { + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], + names[info.mode[i]], js_gr_open, js_gr_close), + names[info.mode[i]], io); + js_gr_init_corr(info.mode[i], port->corr[i]); + } + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_gr_init(void) +#endif +{ + int *p; + + for (p = js_gr_port_list; *p; p++) js_gr_port = js_gr_probe(*p, js_gr_port); + if (js_gr_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-gravis: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_gr_info *info; + + while (js_gr_port) { + for (i = 0; i < js_gr_port->ndevs; i++) + if (js_gr_port->devs[i]) + js_unregister_device(js_gr_port->devs[i]); + info = js_gr_port->info; + release_region(info->io, 1); + js_gr_port = js_unregister_port(js_gr_port); + } +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-joydump.c linux/drivers/char/joystick/joy-joydump.c --- v2.1.124/linux/drivers/char/joystick/joy-joydump.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-joydump.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,172 @@ +/* + * joydump.c Version 1.2 + * + * Copyright (c) 1996-1998 Vojtech Pavlik + */ + +/* + * This is just a very simple driver that can dump the data + * out of the joystick port into the syslog ... + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +#define JS_PORT 0x201 + +#ifdef __i386__ +static unsigned int get_time(void) +{ + unsigned int x; + __asm__ __volatile__ ( "rdtsc" : "=A" (x) ); + return x; +} +#else + +#ifdef __alpha__ +static unsigned int get_time(void) +{ + unsigned int x; + __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ); + return x; +} +#else + +#error "Sorry, you need a precise timer for this - define some in joydump.c" + +#endif +#endif + +#define BUF_SIZE 128 + +struct datatype { + unsigned int time; + unsigned char data; +}; + +static struct datatype buf[BUF_SIZE]; + +int init_module(void) +{ + int i, j; + unsigned int t, t1; + int timeout, div; + unsigned long flags; + unsigned char u; + + printk(KERN_INFO "joydump: ,------------------- START ------------------.\n"); + +/* + * Calibrate. + */ + + __save_flags(flags); + __cli(); + t = get_time(); + udelay(1000); + t1 = get_time(); + __restore_flags(flags); + + timeout = t1 - t; + div = timeout / 1000; + + printk(KERN_INFO "joydump: | Timer ticks at %d.%02d MHz. |\n", + timeout / 1000, timeout / 10 % 100); + + __save_flags(flags); + __cli(); + t = get_time(); + inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); + inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); + t1 = get_time(); + __restore_flags(flags); + + printk(KERN_INFO "joydump: | I/O at %#x takes %d.%02d us. |\n", + JS_PORT, (t1 - t) / div / 10, (t1 - t) * 10 / div % 100); + + timeout *= 20; + +/* + * Gather data. + */ + + __save_flags(flags); + __cli(); + + u = inb(JS_PORT); + t = t1 = get_time(); + buf[0].data = u; + buf[0].time = t1; + i = 1; + + outb(u,JS_PORT); + + while (i < BUF_SIZE && t1 - t < timeout) { + + t1 = get_time(); + buf[i].data = inb(JS_PORT); + + if (buf[i].data != u) { + u = buf[i].data; + buf[i].time = t1; + i++; + } + } + + __restore_flags(flags); + +/* + * Dump data. + */ + + + t = i; + + printk(KERN_INFO "joydump: >------------------- DATA -------------------<\n"); + printk(KERN_INFO "joydump: | index: %3d delta: %3d.%02d us data: ", 0, 0, 0); + for (j = 7; j >= 0; j--) + printk("%d",(buf[0].data >> j) & 1); + printk(" |\n"); + for (i = 1; i < t; i++) { + printk(KERN_INFO "joydump: | index: %3d delta: %3d.%02d us data: ", i, + (buf[i].time - buf[i-1].time) / div, (buf[i].time - buf[i-1].time) * 100 / div % 100); + for (j = 7; j >= 0; j--) + printk("%d",(buf[i].data >> j) & 1); + printk(" |\n"); + } + + printk(KERN_INFO "joydump: `-------------------- END -------------------'\n"); + + return -1; +} + +void cleanup_module(void) +{ +} diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-lightning.c linux/drivers/char/joystick/joy-lightning.c --- v2.1.124/linux/drivers/char/joystick/joy-lightning.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-lightning.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,340 @@ +/* + * joy-lightning.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * PDPI Lightning 4 gamecards and analog joysticks connected + * to them. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_L4_PORT 0x201 +#define JS_L4_SELECT_ANALOG 0xa4 +#define JS_L4_SELECT_DIGITAL 0xa5 +#define JS_L4_SELECT_SECONDARY 0xa6 +#define JS_L4_CMD_ID 0x80 +#define JS_L4_CMD_GETCAL 0x92 +#define JS_L4_CMD_SETCAL 0x93 +#define JS_L4_ID 0x04 +#define JS_L4_BUSY 0x01 +#define JS_L4_TIMEOUT 80 /* 80 us */ + +static struct js_port* __initdata js_l4_port = NULL; + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_PARM(js_l4, "2-24i"); + +static int js_l4[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; + +#include "joy-analog.h" + +struct js_l4_info { + int port; + struct js_an_info an; +}; + +/* + * js_l4_wait_ready() waits for the L4 to become ready. + */ + +static int js_l4_wait_ready(void) +{ + unsigned int t, t1, timeout; + timeout = (JS_L4_TIMEOUT * js_time_speed) >> 10; + t = t1 = js_get_time(); + while ((inb(JS_L4_PORT) & JS_L4_BUSY) && (js_delta(t1 = js_get_time(), t) < timeout)); + return -(js_delta(t1, t) >= timeout); +} + +/* + * js_l4_read() reads data from the Lightning 4. + */ + +static int js_l4_read(void *xinfo, int **axes, int **buttons) +{ + struct js_l4_info *info = xinfo; + int i; + unsigned char status; + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + outb(JS_L4_SELECT_DIGITAL + (info->port >> 2), JS_L4_PORT); + + if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; + outb(info->port & 3, JS_L4_PORT); + + if (js_l4_wait_ready()) return -1; + status = inb(JS_L4_PORT); + + for (i = 0; i < 4; i++) + if (status & (1 << i)) { + if (js_l4_wait_ready()) return -1; + info->an.axes[i] = inb(JS_L4_PORT); + } + + if (status & 0x10) { + if (js_l4_wait_ready()) return -1; + info->an.buttons = inb(JS_L4_PORT); + } + + js_an_decode(&info->an, axes, buttons); + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + + return 0; +} + +#if 0 + +/* + * js_l4_setcal() programs the L4 with calibration values. + */ + +static int js_l4_setcal(int port, int *calib) +{ + int i; + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT); + + if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; + + outb(JS_L4_CMD_SETCAL, JS_L4_PORT); + if (js_l4_wait_ready()) return -1; + + outb(port & 3, JS_L4_PORT); + if (js_l4_wait_ready()) return -1; + + for (i = 0; i < 4; i++) { + outb(calib[0], JS_L4_PORT); + if (js_l4_wait_ready()) return -1; + } + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + + return 0; +} + + +static void js_l4_calibrate(struct js_l4_info *info) +{ + int calib[4]; + int i; + + if ((info->an.extensions & JS_AN_BUTTON_PXY_X) && !(info->an.extensions & JS_AN_BUTTON_PXY_U)) + info->an.axes[2] >>= 2; /* Pad button X */ + + if ((info->an.extensions & JS_AN_BUTTON_PXY_Y) && !(info->an.extensions & JS_AN_BUTTON_PXY_V)) + info->an.axes[3] >>= 2; /* Pad button Y */ + + if (info->an.extensions & JS_AN_HAT_FCS) + info->an.axes[3] >>= 2; /* FCS hat */ + + if (((info->an.mask[0] & 0xb) == 0xb) || + ((info->an.mask[1] & 0xb) == 0xb)) + info->an.axes[3] = (info->an.axes[0] + info->an.axes[1]) >> 1; /* Throttle */ + + for (i = 0; i < 4; i++) + calib[i] = (info->an.axes[i] * 255) / 100; + + for (i = 0; i < 4; i++) + if (calib[i] > 255) calib[i] = 255; + + js_l4_setcal(info->port, calib); + +} + +#endif + +/* + * js_l4_open() is a callback from the file open routine. + */ + +static int js_l4_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_l4_close() is a callback from the file release routine. + */ + +static int js_l4_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_l4_probe() probes for joysticks on the L4 cards. + */ + +static struct js_port __init *js_l4_probe(int cards, int l4port, int mask0, int mask1, struct js_port *port) +{ + struct js_l4_info iniinfo; + struct js_l4_info *info = &iniinfo; +#if 0 + int calib[4] = {255, 255, 255, 255}; +#endif + int i, numdev; + unsigned char u; + + if (l4port < 0) return port; + if (~cards & (1 << (l4port >> 2))) return port; + + memset(info, 0, sizeof(struct js_l4_info)); + info->port = l4port; + +#if 0 + js_l4_setcal(info->port, calib); +#endif + if (js_l4_read(info, NULL, NULL)) return port; + + for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 253) u |= 1 << i; + + if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) + return port; + + port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read); + + for (i = 0; i < numdev; i++) + printk(KERN_INFO "js%d: %s on L4 port %d\n", + js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), + js_an_name(i, &info->an), js_l4_open, js_l4_close), + js_an_name(i, &info->an), info->port); + + info = port->info; + +#if 0 + js_l4_calibrate(info); +#endif + js_l4_read(info, port->axes, port->buttons); + js_an_init_corr(&info->an, port->axes, port->corr, 0); + + return port; +} + +/* + * js_l4_card_probe() probes for presence of the L4 card(s). + */ + +static int __init js_l4_card_probe(void) +{ + int i, cards = 0; + unsigned char rev = 0; + + if (check_region(JS_L4_PORT, 1)) return 0; + + for (i = 0; i < 2; i++) { + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + outb(JS_L4_SELECT_DIGITAL + i, JS_L4_PORT); /* Select card 0-1 */ + + if (inb(JS_L4_PORT) & JS_L4_BUSY) continue; + outb(JS_L4_CMD_ID, JS_L4_PORT); /* Get card ID & rev */ + + if (js_l4_wait_ready()) continue; + if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + i) continue; + + if (js_l4_wait_ready()) continue; + if (inb(JS_L4_PORT) != JS_L4_ID) continue; + + if (js_l4_wait_ready()) continue; + rev = inb(JS_L4_PORT); + + cards |= (1 << i); + + printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d found at %#x\n", + i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT); + } + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + + return cards; +} + +#ifndef MODULE +void __init js_l4_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_l4_an_init(void) +#endif +{ + int i, cards; + + cards = js_l4_card_probe(); + + if (js_l4[0] >= 0) { + for (i = 0; (js_l4[i*3] >= 0) && i < 8; i++) + js_l4_port = js_l4_probe(cards, js_l4[i*3], js_l4[i*3+1], js_l4[i*3+2], js_l4_port); + } else { + for (i = 0; i < (cards << 2); i++) + js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port); + } + + if (js_l4_port == NULL) { +#ifdef MODULE + printk(KERN_WARNING "joy-lightning: no joysticks found\n"); +#endif + return -ENODEV; + } + + request_region(JS_L4_PORT, 1, "joystick (lightning)"); + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + + while (js_l4_port) { + for (i = 0; i < js_l4_port->ndevs; i++) + if (js_l4_port->devs[i]) + js_unregister_device(js_l4_port->devs[i]); + js_l4_port = js_unregister_port(js_l4_port); + } + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + release_region(JS_L4_PORT, 1); + +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-logitech.c linux/drivers/char/joystick/joy-logitech.c --- v2.1.124/linux/drivers/char/joystick/joy-logitech.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-logitech.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,371 @@ +/* + * joy-logitech.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Logitech Digital joystick family. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_LT_MAX_START 250 +#define JS_LT_MAX_STROBE 25 +#define JS_LT_MAX_LENGTH 72 + +#define JS_LT_MAX_DELAY 12000 + +#define JS_LT_MODE_WMED 1 +#define JS_LT_MODE_CM2 2 +#define JS_LT_MODE_TPD 3 + +static int js_lt_seq_init[] __initdata = { 6000, 11000, 7000, 9000, 6000, 11000, 7000, 9000, 0 }; +static int js_lt_seq_reset[] __initdata = { 2000, 3000, 2000, 3000, 0 }; + +static int js_lt_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_lt_port __initdata = NULL; + +static struct { + int x; + int y; +} js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct js_lt_info { + int io; + unsigned char mode; +}; + +/* + * js_lt_read_packet() reads a Logitech packet. + */ + +static int js_lt_read_packet(int io, __u64 *data) +{ + + static unsigned char buf[JS_LT_MAX_LENGTH]; + unsigned char u, v, w, mask = 0; + int i; + unsigned long flags; + unsigned int t, t1; + + int start = (js_time_speed * JS_LT_MAX_START) >> 10; + int strobe = (js_time_speed * JS_LT_MAX_STROBE) >> 10; + + u = inb(io) >> 4; + + if (u == 0xc) mask = 0x10; + if (u == 0x0) mask = 0x50; + if (!mask) return 0; + + i = 0; + + __save_flags(flags); + __cli(); + + outb(u,io); + + u = inb(io); + t = js_get_time(); + + if ((u & 0xc) != 0xc) mask = 0x10; + + do { + u = inb(io); + t1 = js_get_time(); + } while ((((u >> 1) ^ u) & mask) != mask && js_delta(t1,t) < start); + + t = t1; + + do { + v = inb(io); + t1 = js_get_time(); + w = u ^ v; + if ((((w >> 1) ^ w) & mask) == mask) { + buf[i++] = w; + t = t1; + u = v; + } + } while (i < JS_LT_MAX_LENGTH && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + t = i; + *data = 0; + + if (mask == 0x10) { + for (i = 0; i < t; i++) + *data = ((buf[i] >> 5) & 1) | (*data << 1); + return t; + } + if (mask == 0x50) { + for (i = 0; i < t; i++) + *data = ((__u64)(buf[i] & 0x20) << (t - 5)) | (buf[i] >> 7) | (*data << 1); + return t << 1; + } + return 0; +} + +/* + * js_lt_reverse() reverses the order of bits in a byte. + */ + +static unsigned char js_lt_reverse(unsigned char u) +{ + u = ((u & 0x0f) << 4) | ((u >> 4) & 0x0f); + u = ((u & 0x33) << 2) | ((u >> 2) & 0x33); + u = ((u & 0x55) << 1) | ((u >> 1) & 0x55); + return u; +} + +/* + * js_lt_read() reads and analyzes Logitech joystick data. + */ + +static int js_lt_read(void *xinfo, int **axes, int **buttons) +{ + struct js_lt_info *info = xinfo; + __u64 data; + int hat; + + switch (info->mode) { + + case JS_LT_MODE_TPD: + + if (js_lt_read_packet(info->io, &data) != 20) return -1; + + axes[0][0] = ((data >> 6) & 1) - ((data >> 4) & 1); + axes[0][1] = ((data >> 5) & 1) - ((data >> 7) & 1); + + buttons[0][0] = js_lt_reverse((data & 0x0f) | ((data >> 4) & 0xf0)); + + return 0; + + case JS_LT_MODE_WMED: + + if (js_lt_read_packet(info->io, &data) != 42) return -1; + if ((hat = data & 0xf) > 8) return -1; + + axes[0][0] = (data >> 26) & 0xff; + axes[0][1] = (data >> 18) & 0xff; + axes[0][2] = (data >> 10) & 0xff; + axes[0][3] = js_lt_hat_to_axis[hat].x; + axes[0][4] = js_lt_hat_to_axis[hat].y; + + buttons[0][0] = js_lt_reverse((data >> 2) & 0xfc); + + return 0; + + case JS_LT_MODE_CM2: + + if (js_lt_read_packet(info->io, &data) != 64) return -1; + + axes[0][0] = (data >> 48) & 0xff; + axes[0][1] = (data >> 40) & 0xff; + axes[0][2] = (data >> 32) & 0xff; + axes[0][3] = (data >> 24) & 0xff; + axes[0][4] = (data >> 16) & 0xff; + axes[0][5] = (data >> 8) & 0xff; + + buttons[0][0] = js_lt_reverse(data & 0xff); + + return 0; + } + + return -1; +} + +/* + * js_lt_open() is a callback from the file open routine. + */ + +static int js_lt_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_lt_close() is a callback from the file release routine. + */ + +static int js_lt_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_lt_trigger_sequence() sends a trigger & delay sequence + * to reset/initialize a Logitech joystick. + */ + +static void __init js_lt_trigger_sequence(int io, int *seq) +{ + while (*seq) { + outb(inb(io),io); + udelay(*seq++); + } +} + +/* + * js_lt_init_corr() initializes the correction values for + * Logitech joysticks. + */ + +static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) +{ + int j; + + for (j = 0; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 2; + corr[0][j].coef[0] = axes[0][j] - 8; + corr[0][j].coef[1] = axes[0][j] + 8; + corr[0][j].coef[2] = (1 << 29) / (127 - 32); + corr[0][j].coef[3] = (1 << 29) / (127 - 32); + } + + switch (mode) { + case JS_LT_MODE_TPD: j = 0; break; + case JS_LT_MODE_WMED: j = 3; break; + case JS_LT_MODE_CM2: j = 6; break; + default: j = 0; break; + } + + for (; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = (1 << 29); + corr[0][j].coef[3] = (1 << 29); + } + +} + +/* + * js_lt_probe() probes for Logitech type joysticks. + */ + +static struct js_port __init *js_lt_probe(int io, struct js_port *port) +{ + struct js_lt_info info; + char *name; + int axes, buttons, i; + __u64 data; + unsigned char u; + + if (check_region(io, 1)) return port; + + if (((u = inb(io)) & 3) == 3) return port; + outb(u,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + if (!(i = js_lt_read_packet(io, &data))) { + udelay(JS_LT_MAX_DELAY); + js_lt_trigger_sequence(io, js_lt_seq_reset); + js_lt_trigger_sequence(io, js_lt_seq_init); + i = js_lt_read_packet(io, &data); + } + + switch (i) { + case 0: + return port; + case 20: + info.mode = JS_LT_MODE_TPD; + axes = 2; buttons = 8; name = "Logitech ThunderPad Digital"; + break; + case 42: + info.mode = JS_LT_MODE_WMED; + axes = 5; buttons = 6; name = "Logitech WingMan Extreme Digital"; + break; + case 64: + info.mode = JS_LT_MODE_CM2; + axes = 6; buttons = 8; name = "Logitech CyberMan 2"; + break; + case 72: + case 144: + return port; + default: + printk(KERN_WARNING "joy-logitech: unknown joystick device detected " + "(io=%#x, count=%d, data=0x%08x%08x), contact \n", + io, i, (int)(data >> 32), (int)(data & 0xffffffff)); + return port; + } + + info.io = io; + + request_region(io, 1, "joystick (logitech)"); + port = js_register_port(port, &info, 1, sizeof(struct js_lt_info), js_lt_read); + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, 0, axes, buttons, name, js_lt_open, js_lt_close), name, io); + + udelay(JS_LT_MAX_DELAY); + + js_lt_read(port->info, port->axes, port->buttons); + js_lt_init_corr(axes, info.mode, port->axes, port->corr); + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_lt_init(void) +#endif +{ + int *p; + + for (p = js_lt_port_list; *p; p++) js_lt_port = js_lt_probe(*p, js_lt_port); + if (js_lt_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-logitech: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_lt_info *info; + + while (js_lt_port) { + js_unregister_device(js_lt_port->devs[0]); + info = js_lt_port->info; + release_region(info->io, 1); + js_lt_port = js_unregister_port(js_lt_port); + } +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-sidewinder.c linux/drivers/char/joystick/joy-sidewinder.c --- v2.1.124/linux/drivers/char/joystick/joy-sidewinder.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-sidewinder.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,471 @@ +/* + * joy-sidewinder.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Microsoft SideWinder digital joystick family. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_SW_MAX_START 250 +#define JS_SW_MIN_STROBE 25 +#define JS_SW_EXT_STROBE 45 +#define JS_SW_MIN_TIME 1500 +#define JS_SW_MAX_TIME 4000 + +#define JS_SW_MAX_LENGTH 72 + +#define JS_SW_MODE_3DP 1 +#define JS_SW_MODE_PP 2 +#define JS_SW_MODE_GP 3 + +static int js_sw_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_sw_port __initdata = NULL; + +static struct { + int x; + int y; +} js_sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct js_sw_info { + int io; + unsigned char mode; + unsigned char number; + unsigned char optimize; +}; + +/* + * js_sw_init_digital() switches a SideWinder into digital mode. + * It currently switches to 2-bit mode only (FIXME). + */ + +static void __init js_sw_init_digital(int io) +{ + unsigned int t; + unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10; + int delays[] = {100, 850, 400, 0}; + int i = 0; + unsigned long flags; + + __save_flags(flags); + __cli(); + do { + outb(inb(io),io); + t = js_get_time(); + while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout)); + udelay(delays[i]); + } while (delays[i++]); + __restore_flags(flags); + + return; +} + +/* + * js_sw_read_packet() reads a SideWinder packet. + */ + +static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data) +{ + static unsigned char buf[JS_SW_MAX_LENGTH]; + unsigned char u, v; + int i; + unsigned long flags; + unsigned int t, t1; + + int length = l1 < l2 ? l2 : l1; + int start = (js_time_speed * JS_SW_MAX_START) >> 10; + strobe = (js_time_speed * strobe) >> 10; + + i = 0; + + __save_flags(flags); + __cli(); + outb(inb(io),io); + + v = inb(io); + t = js_get_time(); + + do { + u = v; + v = inb(io); + t1 = js_get_time(); + } while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start); + + t = t1; + + do { + v = inb(io); + t1 = js_get_time(); + if ((u ^ v) & v & 0x10) { + buf[i++] = v >> 5; + t = t1; + } + u = v; + } while (i < length && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + *data = 0; + t = i; + + if (t == l1) { + for (i = 0; i < t; i++) + *data |= (__u64) (buf[i] & 1) << i; + return t; + } + if (t == l2) { + for (i = 0; i < t; i++) + *data |= (__u64) buf[i] << (3 * i); + return t * 3; + } + return t; +} + +/* + * js_sw_parity computes parity of __u64 + */ + +static int js_sw_parity(__u64 t) +{ + t ^= t >> 32; + t ^= t >> 16; + t ^= t >> 8; + t ^= t >> 4; + t ^= t >> 2; + t ^= t >> 1; + return t & 1; +} + +/* + * js_sw_csum() computes checksum of nibbles in __u64 + */ + +static int js_sw_csum(__u64 t) +{ + char sum = 0; + while (t) { + sum += t & 0xf; + t >>= 4; + } + return sum & 0xf; +} + +/* + * js_sw_read() reads and analyzes SideWinder joystick data. + */ + +static int js_sw_read(void *xinfo, int **axes, int **buttons) +{ + struct js_sw_info *info = xinfo; + __u64 data; + int hat, i; + + switch (info->mode) { + + case JS_SW_MODE_3DP: + + if (info->optimize) { + i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data); + } else { + i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data); + if (i == 192) info->optimize = 1; + } + + if (i < 60) { + js_sw_init_digital(info->io); + info->optimize = 0; + return -1; + } + + if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) || + (hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) { + info->optimize = 0; + return -1; + } + + axes[0][0] = ((data << 4) & 0x380) | ((data >> 16) & 0x07f); + axes[0][1] = ((data << 7) & 0x380) | ((data >> 24) & 0x07f); + axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f); + axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f); + axes[0][4] = js_sw_hat_to_axis[hat].x; + axes[0][5] = js_sw_hat_to_axis[hat].y; + buttons[0][0] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f); + + return 0; + + case JS_SW_MODE_PP: + + if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1; + if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1; + + axes[0][0] = (data >> 9) & 0x3ff; + axes[0][1] = (data >> 19) & 0x3ff; + axes[0][2] = (data >> 29) & 0x07f; + axes[0][3] = (data >> 36) & 0x03f; + axes[0][4] = js_sw_hat_to_axis[hat].x; + axes[0][5] = js_sw_hat_to_axis[hat].y; + buttons[0][0] = ~data & 0x1ff; + + return 0; + + case JS_SW_MODE_GP: + + if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number, + JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1; + if (js_sw_parity(data)) return -1; + + for (i = 0; i < info->number; i++) { + axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1); + axes[i][1] = ( data & 1) - ((data >> 1) & 1); + buttons[i][0] = (~data >> 4) & 0x3ff; + data >>= 15; + } + + return 0; + + default: + return -1; + } +} + +/* + * js_sw_open() is a callback from the file open routine. + */ + +static int js_sw_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_sw_close() is a callback from the file release routine. + */ + +static int js_sw_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_sw_init_corr() initializes the correction values for + * SideWinders. + */ + +static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr) +{ + int i, j; + + for (i = 0; i < number; i++) { + + for (j = 0; j < num_axes; j++) { + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = 8; + corr[i][j].coef[0] = 511 - 32; + corr[i][j].coef[1] = 512 + 32; + corr[i][j].coef[2] = (1 << 29) / (511 - 32); + corr[i][j].coef[3] = (1 << 29) / (511 - 32); + } + + switch (mode) { + + case JS_SW_MODE_3DP: + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 4; + corr[i][2].coef[0] = 255 - 16; + corr[i][2].coef[1] = 256 + 16; + corr[i][2].coef[2] = (1 << 29) / (255 - 16); + corr[i][2].coef[3] = (1 << 29) / (255 - 16); + + j = 4; + + break; + + case JS_SW_MODE_PP: + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 1; + corr[i][2].coef[0] = 63 - 4; + corr[i][2].coef[1] = 64 + 4; + corr[i][2].coef[2] = (1 << 29) / (63 - 4); + corr[i][2].coef[3] = (1 << 29) / (63 - 4); + + corr[i][3].type = JS_CORR_BROKEN; + corr[i][3].prec = 0; + corr[i][3].coef[0] = 31 - 2; + corr[i][3].coef[1] = 32 + 2; + corr[i][3].coef[2] = (1 << 29) / (31 - 2); + corr[i][3].coef[3] = (1 << 29) / (31 - 2); + + j = 4; + + break; + + default: + + j = 0; + + } + + for (; j < num_axes; j++) { + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = 0; + corr[i][j].coef[0] = 0; + corr[i][j].coef[1] = 0; + corr[i][j].coef[2] = (1 << 29); + corr[i][j].coef[3] = (1 << 29); + } + } +} + +/* + * js_sw_probe() probes for SideWinder type joysticks. + */ + +static struct js_port __init *js_sw_probe(int io, struct js_port *port) +{ + struct js_sw_info info; + char *name; + int i, j, axes, buttons; + __u64 data; + unsigned char u; + + + if (check_region(io, 1)) return port; + if (((u = inb(io)) & 3) == 3) return port; + outb(u,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); + + if (!i) { + udelay(JS_SW_MIN_TIME); + js_sw_init_digital(io); + udelay(JS_SW_MAX_TIME); + i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); + } + + switch (i) { + case 0: + return port; + case 5: + case 10: + case 15: + case 20: + case 30: + case 45: + case 60: + info.mode = JS_SW_MODE_GP; + outb(inb(io),io); /* Kick into 3-bit mode */ + udelay(JS_SW_MAX_TIME); + i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data); /* Get total length */ + udelay(JS_SW_MIN_TIME); + j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data); /* Get subpacket length */ + if (!i || !j) { + printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d)," + " but not idenfitied.\n", i, j); + return port; + } + info.number = i / j; + axes = 2; buttons = 10; name = "SideWinder GamePad"; + break; + case 16: + case 48: + info.mode = JS_SW_MODE_PP; info.number = 1; + axes = 6; buttons = 9; name = "SideWinder Precision Pro"; + break; + case 64: + case 66: + info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0; + axes = 6; buttons = 8; name = "SideWinder 3D Pro"; + break; + case 72: + return port; + default: + printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " + "(io=%#x, count=%d, data=0x%08x%08x), contact \n", + io, i, (int)(data >> 32), (int)(data & 0xffffffff)); + return port; + } + + info.io = io; + + request_region(io, 1, "joystick (sidewinder)"); + port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read); + for (i = 0; i < info.number; i++) + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io); + js_sw_init_corr(axes, info.mode, info.number, port->corr); + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_sw_init(void) +#endif +{ + int *p; + + for (p = js_sw_port_list; *p; p++) js_sw_port = js_sw_probe(*p, js_sw_port); + if (js_sw_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-sidewinder: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_sw_info *info; + + while (js_sw_port) { + for (i = 0; i < js_sw_port->ndevs; i++) + if (js_sw_port->devs[i]) + js_unregister_device(js_sw_port->devs[i]); + info = js_sw_port->info; + release_region(info->io, 1); + js_sw_port = js_unregister_port(js_sw_port); + } + +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-thrustmaster.c linux/drivers/char/joystick/joy-thrustmaster.c --- v2.1.124/linux/drivers/char/joystick/joy-thrustmaster.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-thrustmaster.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,330 @@ +/* + * joy-thrustmaster.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * ThrustMaster DirectConnect (BSP) joystick family. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_TM_MAX_START 200 +#define JS_TM_MAX_STROBE 15 +#define JS_TM_MAX_LENGTH 13 + +#define JS_TM_MODE_M3DI 1 +#define JS_TM_MODE_3DRP 2 +#define JS_TM_MODE_WCS3 4 + +#define JS_TM_MODE_MAX 5 /* Last mode + 1 */ + +#define JS_TM_BYTE_A0 0 +#define JS_TM_BYTE_A1 1 +#define JS_TM_BYTE_A2 3 +#define JS_TM_BYTE_A3 4 +#define JS_TM_BYTE_A4 6 +#define JS_TM_BYTE_A5 7 + +#define JS_TM_BYTE_D0 2 +#define JS_TM_BYTE_D1 5 +#define JS_TM_BYTE_D2 8 +#define JS_TM_BYTE_D3 9 + +#define JS_TM_BYTE_ID 10 +#define JS_TM_BYTE_REV 11 +#define JS_TM_BYTE_DEF 12 + +static int js_tm_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_tm_port __initdata = NULL; + +struct js_tm_info { + int io; + unsigned char mode; +}; + +static int js_tm_id_to_def[JS_TM_MODE_MAX] = {0x00, 0x42, 0x22, 0x00, 0x00}; + +/* + * js_tm_read_packet() reads a ThrustMaster packet. + */ + +static int js_tm_read_packet(int io, unsigned char *data) +{ + unsigned int t, t1; + unsigned char u, v, error; + int i, j; + unsigned long flags; + + int start = (js_time_speed * JS_TM_MAX_START) >> 10; + int strobe = (js_time_speed * JS_TM_MAX_STROBE) >> 10; + + error = 0; + i = j = 0; + + __save_flags(flags); + __cli(); + outb(inb(io),io); + + t = js_get_time(); + + do { + u = inb(io); + t1 = js_get_time(); + } while ((u & 1) && js_delta(t1, t) < start); + + t = t1; + u >>= 4; + + do { + v = inb(io) >> 4; + t1 = js_get_time(); + if ((u ^ v) & u & 2) { + if (j) { + if (j < 9) { /* Data bit */ + data[i] |= (~v & 1) << (j - 1); + j++; + } else { /* Stop bit */ + error |= v & 1; + j = 0; + i++; + } + } else { /* Start bit */ + error |= ~v & 1; + j++; + } + t = t1; + } + u = v; + } while (!error && i < JS_TM_MAX_LENGTH && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + return -(i != JS_TM_MAX_LENGTH); +} + +/* + * js_tm_read() reads and analyzes ThrustMaster joystick data. + */ + +static int js_tm_read(void *xinfo, int **axes, int **buttons) +{ + struct js_tm_info *info = xinfo; + unsigned char data[JS_TM_MAX_LENGTH]; + + if (js_tm_read_packet(info->io, data)) { + printk(KERN_WARNING "joy-thrustmaster: failed to read data packet\n"); + return -1; + } + if (data[JS_TM_BYTE_ID] != info->mode) { + printk(KERN_WARNING "joy-thrustmaster: ID (%d) != mode (%d)\n", + data[JS_TM_BYTE_ID], info->mode); + return -1; + } + if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info->mode]) { + printk(KERN_WARNING "joy-thrustmaster: DEF (%d) != def(mode) (%d)\n", + data[JS_TM_BYTE_DEF], js_tm_id_to_def[info->mode]); + return -1; + } + + switch (info->mode) { + + case JS_TM_MODE_M3DI: + + axes[0][0] = data[JS_TM_BYTE_A0]; + axes[0][1] = data[JS_TM_BYTE_A1]; + axes[0][2] = data[JS_TM_BYTE_A2]; + axes[0][3] = data[JS_TM_BYTE_A3]; + + axes[0][4] = ((data[JS_TM_BYTE_D0] >> 3) & 1) - ((data[JS_TM_BYTE_D0] >> 1) & 1); + axes[0][5] = ((data[JS_TM_BYTE_D0] >> 2) & 1) - ( data[JS_TM_BYTE_D0] & 1); + + buttons[0][0] = ((data[JS_TM_BYTE_D0] >> 6) & 0x01) | ((data[JS_TM_BYTE_D0] >> 3) & 0x06) + | ((data[JS_TM_BYTE_D0] >> 4) & 0x08) | ((data[JS_TM_BYTE_D1] >> 2) & 0x30); + + return 0; + + case JS_TM_MODE_3DRP: + + axes[0][0] = data[JS_TM_BYTE_A0]; + axes[0][1] = data[JS_TM_BYTE_A1]; + + buttons[0][0] = ( data[JS_TM_BYTE_D0] & 0x3f) | ((data[JS_TM_BYTE_D1] << 6) & 0xc0) + | (( ((int) data[JS_TM_BYTE_D0]) << 2) & 0x300); + + return 0; + + } + + return -1; +} + +/* + * js_tm_open() is a callback from the file open routine. + */ + +static int js_tm_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_tm_close() is a callback from the file release routine. + */ + +static int js_tm_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_tm_init_corr() initializes the correction values for + * ThrustMaster joysticks. + */ + +static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) +{ + int j; + + for (j = 0; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 127 - 2; + corr[0][j].coef[1] = 128 + 2; + corr[0][j].coef[2] = (1 << 29) / (127 - 4); + corr[0][j].coef[3] = (1 << 29) / (127 - 4); + } + + switch (mode) { + case JS_TM_MODE_M3DI: j = 4; break; + case JS_TM_MODE_3DRP: j = 2; break; + default: j = 0; break; + } + + for (; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = (1 << 29); + corr[0][j].coef[3] = (1 << 29); + } + +} + +/* + * js_tm_probe() probes for ThrustMaster type joysticks. + */ + +static struct js_port __init *js_tm_probe(int io, struct js_port *port) +{ + struct js_tm_info info; + char *names[JS_TM_MODE_MAX] = { NULL, "ThrustMaster Millenium 3D Inceptor", + "ThrustMaster Rage 3D Gamepad", NULL, "ThrustMaster WCS III" }; + char axes[JS_TM_MODE_MAX] = { 0, 6, 2, 0, 0 }; + char buttons[JS_TM_MODE_MAX] = { 0, 5, 10, 0, 0 }; + + unsigned char data[JS_TM_MAX_LENGTH]; + unsigned char u; + + if (check_region(io, 1)) return port; + + if (((u = inb(io)) & 3) == 3) return port; + outb(u,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + if(js_tm_read_packet(io, data)) { + printk(KERN_WARNING "joy-thrustmaster: probe - can't read packet\n"); + return port; + } + + info.io = io; + info.mode = data[JS_TM_BYTE_ID]; + + if (!info.mode) return port; + + if (info.mode >= JS_TM_MODE_MAX || !names[info.mode]) { + printk(KERN_WARNING "joy-thrustmaster: unknown device detected " + "(io=%#x, id=%d), contact \n", + io, info.mode); + return port; + } + + if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info.mode]) { + printk(KERN_WARNING "joy-thrustmaster: wrong DEF (%d) for ID %d - should be %d\n", + data[JS_TM_BYTE_DEF], info.mode, js_tm_id_to_def[info.mode]); + } + + request_region(io, 1, "joystick (thrustmaster)"); + port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read); + printk(KERN_INFO "js%d: %s revision %d at %#x\n", + js_register_device(port, 0, axes[info.mode], buttons[info.mode], + names[info.mode], js_tm_open, js_tm_close), names[info.mode], data[JS_TM_BYTE_REV], io); + js_tm_init_corr(axes[info.mode], info.mode, port->axes, port->corr); + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_tm_init(void) +#endif +{ + int *p; + + for (p = js_tm_port_list; *p; p++) js_tm_port = js_tm_probe(*p, js_tm_port); + if (js_tm_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-thrustmaster: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_tm_info *info; + + while (js_tm_port) { + js_unregister_device(js_tm_port->devs[0]); + info = js_tm_port->info; + release_region(info->io, 1); + js_tm_port = js_unregister_port(js_tm_port); + } +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-turbografx.c linux/drivers/char/joystick/joy-turbografx.c --- v2.1.124/linux/drivers/char/joystick/joy-turbografx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-turbografx.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,282 @@ +/* + * joy-turbografx.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Steffen Schwenke's TurboGraFX parallel port + * interface. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_PARM(js_tg, "2i"); +MODULE_PARM(js_tg_2, "0-2i"); +MODULE_PARM(js_tg_3, "0-2i"); + +#define JS_TG_BUTTON 3 +#define JS_TG_UP 4 +#define JS_TG_DOWN 5 +#define JS_TG_LEFT 6 +#define JS_TG_RIGHT 7 + +static struct js_port* js_tg_port = NULL; + +static int js_tg[] __initdata = { -1, 0 }; +static int js_tg_2[] __initdata = { -1, 0 }; +static int js_tg_3[] __initdata = { -1, 0 }; + +struct js_tg_info { +#ifdef USE_PARPORT + struct pardevice *port; /* parport device */ + int use; /* use count */ + int wanted; /* parport wanted */ +#else + int port; /* hw port */ +#endif + int count; /* number of joysticks */ +}; + +/* + * js_tg_read() reads and analyzes tg joystick data. + */ + +static int js_tg_read(void *xinfo, int **axes, int **buttons) +{ + struct js_tg_info *info = xinfo; + int data, i; + + for (i = 0; i < info->count; i++) { + + JS_PAR_DATA_OUT(~(1 << i), info->port); + data = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + + axes[i][0] = ((data >> JS_TG_RIGHT) & 1) - ((data >> JS_TG_LEFT) & 1); + axes[i][1] = ((data >> JS_TG_DOWN ) & 1) - ((data >> JS_TG_UP ) & 1); + + buttons[i][0] = (data >> JS_TG_BUTTON) & 1; + + } + + return 0; +} + +/* + * open callback: claim parport. + */ + +int js_tg_open(struct js_dev *dev) +{ + MOD_INC_USE_COUNT; + +#ifdef USE_PARPORT + { + struct js_tg_info *info = dev->port->info; + if (!info->use && parport_claim(info->port)) { + printk(KERN_WARNING "joy-tg: parport busy\n"); /* port currently not available ... */ + info->wanted++; /* we'll claim it on wakeup */ + return 0; + } + info->use++; + } +#endif + return 0; +} + +/* + * close callback: release parport + */ + +int js_tg_close(struct js_dev *dev) +{ +#ifdef USE_PARPORT + struct js_tg_info *info = dev->port->info; + + if (!--info->use) + parport_release(info->port); +#endif + MOD_DEC_USE_COUNT; + + return 0; +} + +/* + * parport wakeup callback: claim the port! + */ + +#ifdef USE_PARPORT +static void js_tg_wakeup(void *v) +{ + struct js_tg_info *info = js_tg_port->info; /* FIXME! We can have more than 1 port! */ + + if (!info->use && info->wanted) + { + parport_claim(info->port); + info->use++; + info->wanted--; + } +} +#endif + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_tg_info *info; + int i; + + while (js_tg_port) { + for (i = 0; i < js_tg_port->ndevs; i++) + if (js_tg_port->devs[i]) + js_unregister_device(js_tg_port->devs[i]); + info = js_tg_port->info; +#ifdef USE_PARPORT + parport_unregister_device(info->port); +#else + release_region(info->port, 3); +#endif + js_tg_port = js_unregister_port(js_tg_port); + } +} +#endif + +/* + * js_tg_init_corr() initializes correction values of + * tg gamepads. + */ + +static void __init js_tg_init_corr(int count, struct js_corr **corr) +{ + int i, j; + + for (i = 0; i < count; i++) + for (j = 0; j < 2; j++) { + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = 0; + corr[i][j].coef[0] = 0; + corr[i][j].coef[1] = 0; + corr[i][j].coef[2] = (1 << 29); + corr[i][j].coef[3] = (1 << 29); + } +} + +/* + * js_tg_probe() probes for tg gamepads. + */ + +static struct js_port __init *js_tg_probe(int *config, struct js_port *port) +{ + struct js_tg_info info; + int i; + + if (config[0] < 0) return port; + if (config[1] < 1 || config[1] > 8) return port; + +#ifdef USE_PARPORT + { + struct parport *pp; + + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; + + if (!pp) { + printk(KERN_ERR "joy-tg: no such parport\n"); + return port; + } + + info.port = parport_register_device(pp, "joystick (turbografx)", NULL, js_tg_wakeup, NULL, 0, NULL); + info.wanted = 0; + info.use = 0; + } +#else + info.port = config[0]; + if (check_region(info.port, 3)) return port; + request_region(info.port, 3, "joystick (turbografx)"); +#endif + + info.count = config[1]; + + port = js_register_port(port, &info, info.count, sizeof(struct js_tg_info), js_tg_read); + + + for (i = 0; i < info.count; i++) +#ifdef USE_PARPORT + printk(KERN_INFO "js%d: Multisystem joystick on %s\n", + js_register_device(port, i, 2, 1, "Multisystem joystick", js_tg_open, js_tg_close), + info.port->port->name); +#else + printk(KERN_INFO "js%d: Multisystem joystick at %#x\n", + js_register_device(port, i, 2, 1, "Multisystem joystick", js_tg_open, js_tg_close), + info.port); +#endif + + js_tg_init_corr(info.count, port->corr); + + return port; +} + +#ifndef MODULE +void __init js_tg_setup(char *str, int *ints) +{ + int i; + + if (!strcmp(str,"js_tg")) + for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; + if (!strcmp(str,"js_tg_2")) + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; + if (!strcmp(str,"js_tg_3")) + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; + +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_tg_init(void) +#endif +{ + js_tg_port = js_tg_probe(js_tg, js_tg_port); + js_tg_port = js_tg_probe(js_tg_2, js_tg_port); + js_tg_port = js_tg_probe(js_tg_3, js_tg_port); + + if (js_tg_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-tg: no joysticks specified\n"); +#endif + return -ENODEV; +} diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joystick.c linux/drivers/char/joystick/joystick.c --- v2.1.124/linux/drivers/char/joystick/joystick.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joystick.c Thu Oct 8 17:25:16 1998 @@ -0,0 +1,1223 @@ +/* + * joystick.c Version 1.2 + * + * Copyright (c) 1996-1998 Vojtech Pavlik + */ + +/* + * This is the main joystick driver for Linux. It doesn't support any + * devices directly, rather is lets you use sub-modules to do that job. See + * Documentation/joystick.txt for more info. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#include +#include +#endif + +/* + * Configurable parameters. + */ + +#define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) */ + +/* + * Buffer macros. + */ + +#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C))))) +#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1) +#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1) +#define DIFF(X,Y) ((X)>(Y)?(X)-(Y):(Y)-(X)) + +/* + * Global variables. + */ + +static struct JS_DATA_SAVE_TYPE js_comp_glue; +static struct js_port *js_port = NULL; +static struct js_dev *js_dev = NULL; +static struct timer_list js_timer; +spinlock_t js_lock = SPIN_LOCK_UNLOCKED; +static int js_use_count = 0; + +/* + * Exported variables. + */ + +unsigned int js_time_speed = 0; +js_time_func js_get_time; +js_delta_func js_delta; + +unsigned int js_time_speed_a = 0; +js_time_func js_get_time_a; +js_delta_func js_delta_a; + +/* + * Module info. + */ + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_SUPPORTED_DEVICE("js"); + +/* + * js_get_time_*() are different functions to get current time. + * js_delta_*() are functions to compute time difference. + */ + +#ifdef __i386__ + +static unsigned int js_get_time_rdtsc(void) +{ + unsigned int x; + __asm__ __volatile__ ( "rdtsc" : "=A" (x) ); + return x; +} + +static unsigned int js_get_time_pit(void) +{ + unsigned long flags; + unsigned int x; + + __save_flags(flags); + __cli(); + outb(0, 0x43); + x = inb(0x40); + x |= inb(0x40) << 8; + __restore_flags(flags); + + return x; +} + +static int js_delta_pit(unsigned int x, unsigned int y) +{ + return y - x + ( y < x ? 1193180L / HZ : 0 ); +} + +static unsigned int js_get_time_counter(void) +{ + static int time_counter = 0; + return time_counter++; +} + +#else +#ifdef __alpha__ + +static unsigned int js_get_time_rpcc(void) +{ + unsigned int x; + __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ); + return x; +} + +#else + +#ifndef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +static unsigned int js_get_time_system(void) +{ + static struct timeval js_tv; + get_fast_time(&js_tv); + return js_tv.tv_sec * 1000000L + js_tv.tv_usec; +} +#endif +#endif + +#endif +#endif + +static int js_delta_normal(unsigned int x, unsigned int y) +{ + return x - y; +} + +/* + * js_calibrate_time() calibrates a given timer. + */ + +static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta) +{ + unsigned int t1, t2, t3; + unsigned long flags; + + __save_flags(flags); + __cli(); + t1 = get_time(); + udelay(1000); + t2 = get_time(); + t3 = get_time(); + __restore_flags(flags); + + return delta(t2, t1) - delta(t3, t2); +} + +/* + * js_calibrate_time_counter() calibrates the counter timer, which can't + * be calibrated using the above function. + */ + +#ifdef __i386__ + +static int __init js_calibrate_time_counter(void) +{ + unsigned int i, j, t1, t2, t3; + + j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies); + j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies); + + j = (t2 - t1) * HZ / 1000; + + t1 = js_get_time_pit(); + for (i = 0; i < 1000; i++) { + inb(0x201); + js_get_time_counter(); + } + t2 = js_get_time_pit(); + t3 = js_get_time_pit(); + + i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2)); + + if (DIFF(i,j) > 5) + printk(KERN_WARNING "js: Counter timer calibration unsure," + " pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i); + + return (i + j) >> 1; +} + +#endif + +/* + * js_setup_time chooses the best available timers + * on the system and calibrates them. + */ + +static int __init js_setup_time(void) +{ + int t; + char *name, *name_a; + + name = ""; + name_a = ""; + js_time_speed = 0; + js_time_speed_a = 0; + +#ifdef __i386__ + + t = js_calibrate_time(js_get_time_pit, js_delta_pit); + + if (DIFF(t, 1193) > 5) + printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n" + KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n", + t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193)); + + if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) { + + js_time_speed_a = t; + js_get_time_a = js_get_time_rdtsc; + js_delta_a = js_delta_normal; + js_time_speed = t; + js_get_time = js_get_time_rdtsc; + js_delta = js_delta_normal; + name = "RDTSC"; + + } else { + + js_time_speed_a = t; + js_get_time_a = js_get_time_pit; + js_delta_a = js_delta_pit; + name_a = "PIT"; + + t = js_calibrate_time_counter(); + + js_time_speed = t; + js_get_time = js_get_time_counter; + js_delta = js_delta_normal; + name = "counter"; + + } + +#else +#ifdef __alpha__ + + t = js_calibrate_time(js_get_time_rpcc, js_delta_normal); + + js_time_speed_a = t; + js_get_time_a = js_get_time_rpcc; + js_delta_a = js_delta_normal; + js_time_speed = t; + js_get_time = js_get_time_rpcc; + js_delta = js_delta_normal; + name = "RPCC"; + +#else + +#ifndef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + t = js_calibrate_time(js_get_time_system, js_delta_normal); + + js_time_speed_a = t; + js_get_time_a = js_get_time_system; + js_delta_a = js_delta_normal; + js_time_speed = t; + js_get_time = js_get_time_system; + js_delta = js_delta_normal; + name = "system"; +#endif +#endif + +#endif +#endif + + printk(KERN_INFO "js: Version %d.%d.%d ", + JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); + + if (js_time_speed_a <= 0 || js_time_speed <= 0) { + printk("\n"); + return -1; + } + + printk("using "); + + if (js_time_speed > 10000) { + t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500); + printk("%d MHz ", t); + } else { + t = js_time_speed / 10 + (js_time_speed % 10 >= 5); + printk("%d.%02d MHz ", t / 100, t % 100); + } + + if (js_get_time_a != js_get_time) { + t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5); + printk("%s timer and %d.%02d MHz %s timer.\n", + name, t / 100, t % 100, name_a); + } else { + printk("%s timer.\n", name); + } + + return 0; +} + + +/* + * js_correct() performs correction of raw joystick data. + */ + +static int js_correct(int value, struct js_corr *corr) +{ + switch (corr->type) { + case JS_CORR_NONE: + break; + case JS_CORR_BROKEN: + value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : + ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : + ((corr->coef[2] * (value - corr->coef[0])) >> 14); + break; + + default: + return 0; + } + + if (value < -32767) return -32767; + if (value > 32767) return 32767; + + return value; +} + +/* + * js_button() returns value of button number i. + */ + +static inline int js_button(int *buttons, int i) +{ + return (buttons[i >> 5] >> (i & 0x1f)) & 1; +} + + +/* + * js_add_event() adds an event to the buffer. This requires additional + * queue post-processing done by js_sync_buff. + */ + +static void js_add_event(struct js_dev *jd, __u32 time, __u8 type, __u8 number, __s16 value) +{ + jd->buff[jd->ahead].time = time; + jd->buff[jd->ahead].type = type; + jd->buff[jd->ahead].number = number; + jd->buff[jd->ahead].value = value; + if (++jd->ahead == JS_BUFF_SIZE) jd->ahead = 0; +} + +/* + * js_flush_data() does the same as js_process_data, except for that it doesn't + * generate any events - it just copies the data from new to cur. + */ + +static void js_flush_data(struct js_dev *jd) +{ + int i; + + for (i = 0; i < ((jd->num_buttons - 1) >> 5) + 1; i++) + jd->cur.buttons[i] = jd->new.buttons[i]; + for (i = 0; i < jd->num_axes; i++) + jd->cur.axes[i] = jd->new.axes[i]; +} + +/* + * js_process_data() finds changes in button states and axis positions and adds + * them as events to the buffer. + */ + +static void js_process_data(struct js_dev *jd) +{ + int i, t; + + for (i = 0; i < jd->num_buttons; i++) + if ((t = js_button(jd->new.buttons, i)) != js_button(jd->cur.buttons, i)) { + js_add_event(jd, jiffies, JS_EVENT_BUTTON, i, t); + jd->cur.buttons[i >> 5] ^= (1 << (i & 0x1f)); + } + + for (i = 0; i < jd->num_axes; i++) { + t = js_correct(jd->new.axes[i], &jd->corr[i]); + if (((jd->corr[i].prec == -1) && t) || + ((DIFF(jd->new.axes[i], jd->cur.axes[i]) > jd->corr[i].prec) && + (t != js_correct(jd->cur.axes[i], &jd->corr[i])))) { + js_add_event(jd, jiffies, JS_EVENT_AXIS, i, t); + jd->cur.axes[i] = jd->new.axes[i]; + } + } +} + +/* + * js_sync_buff() checks for all overflows caused by recent additions to the buffer. + * These happen only if some process is reading the data too slowly. It + * wakes up any process waiting for data. + */ + +static void js_sync_buff(struct js_dev *jd) +{ + struct js_list *curl = jd->list; + + if (jd->bhead != jd->ahead) { + if(ROT(jd->bhead, jd->tail, jd->ahead) || (jd->tail == jd->bhead)) { + while (curl) { + if (ROT(jd->bhead, curl->tail, jd->ahead) || (curl->tail == jd->bhead)) { + curl->tail = jd->ahead; + curl->startup = 0; + } + curl = curl->next; + } + jd->tail = jd->ahead; + } + jd->bhead = jd->ahead; + wake_up_interruptible(&jd->wait); + } +} + +/* + * js_do_timer() acts as an interrupt replacement. It reads the data + * from all ports and then generates events for all devices. + */ + +static void js_do_timer(unsigned long data) +{ + struct js_port *curp = js_port; + struct js_dev *curd = js_dev; + unsigned long flags; + + while (curp) { + curp->read(curp->info, curp->axes, curp->buttons); + curp = curp->next; + } + + spin_lock_irqsave(&js_lock, flags); + + while (curd) { + if (data) { + js_process_data(curd); + js_sync_buff(curd); + } else { + js_flush_data(curd); + } + curd = curd->next; + } + + spin_unlock_irqrestore(&js_lock, flags); + + js_timer.expires = jiffies + JS_REFRESH_TIME; + add_timer(&js_timer); +} + +/* + * js_read() copies one or more entries from jsd[].buff to user + * space. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) +#else +static int js_read(struct inode *inode, struct file *file, char *buf, int count) +#endif +{ + struct wait_queue wait = { current, NULL }; + struct js_event *buff = (void *) buf; + struct js_list *curl; + struct js_dev *jd; + unsigned long blocks = count / sizeof(struct js_event); + int written = 0; + int new_tail, orig_tail; + int retval = 0; + unsigned long flags; + + curl = file->private_data; + jd = curl->dev; + orig_tail = curl->tail; + +/* + * Check user data. + */ + + if (!blocks) + return -EINVAL; + +/* + * Lock it. + */ + + spin_lock_irqsave(&js_lock, flags); + +/* + * Handle (non)blocking i/o. + */ + if (count != sizeof(struct JS_DATA_TYPE)) { + + if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) { + + add_wait_queue(&jd->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (GOF(curl->tail) == jd->bhead) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + spin_unlock_irqrestore(&js_lock, flags); + schedule(); + spin_lock_irqsave(&js_lock, flags); + + } + + current->state = TASK_RUNNING; + remove_wait_queue(&jd->wait, &wait); + } + + if (retval) { + spin_unlock_irqrestore(&js_lock, flags); + return retval; + } + +/* + * Initial state. + */ + + while (curl->startup < jd->num_axes + jd->num_buttons && written < blocks && !retval) { + + struct js_event tmpevent; + + if (curl->startup < jd->num_buttons) { + tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; + tmpevent.value = js_button(jd->cur.buttons, curl->startup); + tmpevent.number = curl->startup; + } else { + tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; + tmpevent.value = js_correct(jd->cur.axes[curl->startup - jd->num_buttons], + &jd->corr[curl->startup - jd->num_buttons]); + tmpevent.number = curl->startup - jd->num_buttons; + } + + tmpevent.time = jiffies * (1000/HZ); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event))) + retval = -EFAULT; +#else + if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) + memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event)); +#endif + + curl->startup++; + written++; + } + +/* + * Buffer data. + */ + + while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event))) + retval = -EFAULT; + if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time)) + retval = -EFAULT; +#else + if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) { + memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event)); + put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time); + } +#endif + curl->tail = new_tail; + written++; + } + } + + else + +/* + * Handle version 0.x compatibility. + */ + + { + struct JS_DATA_TYPE data; + + data.buttons = jd->new.buttons[0]; + data.x = jd->num_axes < 1 ? 0 : + ((js_correct(jd->new.axes[0], &jd->corr[0]) / 256) + 128) >> js_comp_glue.JS_CORR.x; + data.y = jd->num_axes < 2 ? 0 : + ((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; +#else + if (!(retval = verify_area(VERIFY_WRITE, buf, sizeof(struct JS_DATA_TYPE)))) { + memcpy_tofs(buf, &data, sizeof(struct JS_DATA_TYPE)); + } +#endif + + curl->startup = 0; + curl->tail = GOB(jd->bhead); + if (!retval) retval = sizeof(struct JS_DATA_TYPE); + } + +/* + * Check main tail and move it. + */ + + if (orig_tail == jd->tail) { + new_tail = curl->tail; + curl = jd->list; + while (curl && curl->tail != jd->tail) { + if (ROT(jd->bhead, new_tail, curl->tail) || + (jd->bhead == curl->tail)) new_tail = curl->tail; + curl = curl->next; + } + if (!curl) jd->tail = new_tail; + } + + spin_unlock_irqrestore(&js_lock, flags); + + return retval ? retval : written * sizeof(struct js_event); +} + +/* + * js_poll() does select() support. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + +static unsigned int js_poll(struct file *file, poll_table *wait) +{ + struct js_list *curl = file->private_data; + unsigned long flags; + int retval = 0; + poll_wait(file, &curl->dev->wait, wait); + spin_lock_irqsave(&js_lock, flags); + if (GOF(curl->tail) != curl->dev->bhead || + curl->startup < curl->dev->num_axes + curl->dev->num_buttons) retval = POLLIN | POLLRDNORM; + spin_unlock_irqrestore(&js_lock, flags); + return retval; +} + +#else + +static int js_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct js_list *curl = file->private_data; + if (sel_type == SEL_IN) { + if (GOF(curl->tail) != curl->dev->bhead) return 1; + select_wait(&curl->dev->wait, wait); + } + return 0; +} + +#endif + +/* + * js_ioctl handles misc ioctl calls. + */ + +static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct js_list *curl; + struct js_dev *jd; + int len; + + curl = file->private_data; + jd = curl->dev; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + + switch (cmd) { + +/* + * 0.x compatibility + */ + + case JS_SET_CAL: + return copy_from_user(&js_comp_glue.JS_CORR, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_GET_CAL: + return copy_to_user((struct JS_DATA_TYPE *) arg, &js_comp_glue.JS_CORR, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_SET_TIMEOUT: + return get_user(js_comp_glue.JS_TIMEOUT, (int *) arg); + case JS_GET_TIMEOUT: + return put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); + case JS_SET_TIMELIMIT: + return get_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); + case JS_GET_TIMELIMIT: + return put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); + case JS_SET_ALL: + return copy_from_user(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + case JS_GET_ALL: + return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + +/* + * 1.x ioctl calls + */ + + case JSIOCGVERSION: + return put_user(JS_VERSION, (__u32 *) arg); + case JSIOCGAXES: + return put_user(jd->num_axes, (__u8 *) arg); + case JSIOCGBUTTONS: + return put_user(jd->num_buttons, (__u8 *) arg); + case JSIOCSCORR: + return copy_from_user(jd->corr, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; + case JSIOCGCORR: + return copy_to_user((struct js_corr *) arg, jd->corr, + sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; + default: + if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { + len = strlen(jd->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + if (copy_to_user((char *) arg, jd->name, len)) return -EFAULT; + return len; + } + } + +#else + + switch (cmd) { + +/* + * 0.x compatibility + */ + + case JS_SET_CAL: + if (verify_area(VERIFY_READ, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE))) return -EFAULT; + memcpy_fromfs(&js_comp_glue.JS_CORR, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_TYPE)); + return 0; + case JS_GET_CAL: + if (verify_area(VERIFY_WRITE, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE))) return -EFAULT; + memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue.JS_CORR, + sizeof(struct JS_DATA_TYPE)); + return 0; + case JS_SET_TIMEOUT: + if (verify_area(VERIFY_READ, (int *) arg, sizeof(int))) return -EFAULT; + js_comp_glue.JS_TIMEOUT = get_user((int *) arg); + return 0; + case JS_GET_TIMEOUT: + if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) return -EFAULT; + put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); + return 0; + case JS_SET_TIMELIMIT: + if (verify_area(VERIFY_READ, (long *) arg, sizeof(long))) return -EFAULT; + js_comp_glue.JS_TIMELIMIT = get_user((long *) arg); + return 0; + case JS_GET_TIMELIMIT: + if (verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))) return -EFAULT; + put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); + return 0; + case JS_SET_ALL: + if (verify_area(VERIFY_READ, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; + memcpy_fromfs(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE)); + return 0; + case JS_GET_ALL: + if (verify_area(VERIFY_WRITE, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; + memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, + sizeof(struct JS_DATA_SAVE_TYPE)); + return 0; + +/* + * 1.x ioctl calls + */ + + case JSIOCGVERSION: + if (verify_area(VERIFY_WRITE, (__u32 *) arg, sizeof(__u32))) return -EFAULT; + put_user(JS_VERSION, (__u32 *) arg); + return 0; + case JSIOCGAXES: + if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; + put_user(jd->num_axes, (__u8 *) arg); + return 0; + case JSIOCGBUTTONS: + if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; + put_user(jd->num_buttons, (__u8 *) arg); + return 0; + case JSIOCSCORR: + if (verify_area(VERIFY_READ, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; + memcpy_fromfs(jd->corr, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes); + return 0; + case JSIOCGCORR: + if (verify_area(VERIFY_WRITE, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; + memcpy_tofs((struct js_corr *) arg, + jd->corr, sizeof(struct js_corr) * jd->num_axes); + return 0; + default: + if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { + len = strlen(jd->name) + 1; + if (verify_area(VERIFY_WRITE, (char *) arg, len)) return -EFAULT; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + memcpy_tofs((char *) arg, jd->name, len); + return len; + } + } + +#endif + + return -EINVAL; +} + +/* + * js_open() performs necessary initialization and adds + * an entry to the linked list. + */ + +static int js_open(struct inode *inode, struct file *file) +{ + struct js_list *curl; + struct js_dev *jd = js_dev; + int i = MINOR(inode->i_rdev); + unsigned long flags; + + if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) + return -EINVAL; + + while (i > 0 && jd) { + jd = jd->next; + i--; + } + + if (!jd) return -ENODEV; + + jd->open(jd); + + MOD_INC_USE_COUNT; + if (!js_use_count++) js_do_timer(0); + + curl = jd->list; + + spin_lock_irqsave(&js_lock, flags); + + jd->list = kmalloc(sizeof(struct js_list), GFP_KERNEL); + jd->list->next = curl; + jd->list->dev = jd; + jd->list->startup = 0; + jd->list->tail = GOB(jd->bhead); + + spin_unlock_irqrestore(&js_lock, flags); + + file->private_data = jd->list; + + return 0; +} + +/* + * js_release() removes an entry from list and deallocates memory + * used by it. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +static int js_release(struct inode *inode, struct file *file) +#else +static void js_release(struct inode *inode, struct file *file) +#endif +{ + struct js_list *curl = file->private_data; + struct js_dev *jd = curl->dev; + struct js_list **curp = &jd->list; + int new_tail; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*curp && (*curp != curl)) curp = &((*curp)->next); + *curp = (*curp)->next; + + if (jd->list) + if (curl->tail == jd->tail) { + curl = jd->list; + new_tail = curl->tail; + while (curl && curl->tail != jd->tail) { + if (ROT(jd->bhead, new_tail, curl->tail) || + (jd->bhead == curl->tail)) new_tail = curl->tail; + curl = curl->next; + } + if (!curl) jd->tail = new_tail; + } + + spin_unlock_irqrestore(&js_lock, flags); + + kfree(file->private_data); + + if (!--js_use_count) del_timer(&js_timer); + MOD_DEC_USE_COUNT; + + jd->close(jd); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + return 0; +#endif +} + +/* + * js_dump_mem() dumps all data structures in memory. + * It's used for debugging only. + */ + +#if 0 +static void js_dump_mem(void) +{ + + struct js_port *curp = js_port; + struct js_dev *curd = js_dev; + int i; + + printk(",--- Dumping Devices:\n"); + printk("| js_dev = %x\n", (int) js_dev); + + while (curd) { + printk("| %s-device %x, next %x axes %d, buttons %d, port %x - %#x\n", + curd->next ? "|":"`", + (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port, curd->port->io); + curd = curd->next; + } + + printk(">--- Dumping ports:\n"); + printk("| js_port = %x\n", (int) js_port); + + while (curp) { + printk("| %s-port %x, next %x, io %#x, devices %d\n", + curp->next ? "|":"`", + (int) curp, (int) curp->next, curp->io, curp->ndevs); + for (i = 0; i < curp->ndevs; i++) { + curd = curp->devs[i]; + if (curd) + printk("| %s %s-device %x, next %x axes %d, buttons %d, port %x\n", + curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`", + (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port); + else + printk("| %s %s-device %x, not there\n", + curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`", (int) curd); + + } + curp = curp->next; + } + + printk("`--- Done\n"); +} +#endif + + +struct js_port *js_register_port(struct js_port *port, + void *info, int devs, int infos, js_read_func read) +{ + struct js_port **ptrp = &js_port; + struct js_port *curp; + int i; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*ptrp) ptrp=&((*ptrp)->next); + *ptrp = curp = kmalloc(sizeof(struct js_port), GFP_KERNEL); + + curp->next = NULL; + curp->prev = port; + curp->read = read; + curp->ndevs = devs; + + curp->devs = kmalloc(devs * sizeof(struct js_dev*), GFP_KERNEL); + + for (i = 0; i < devs; i++) + curp->devs[i] = NULL; + + curp->axes = kmalloc(devs * sizeof(int*), GFP_KERNEL); + curp->buttons = kmalloc(devs * sizeof(int*), GFP_KERNEL); + curp->corr = kmalloc(devs * sizeof(struct js_corr*), GFP_KERNEL); + + if (infos) { + curp->info = kmalloc(infos, GFP_KERNEL); + memcpy(curp->info, info, infos); + } else { + curp->info = NULL; + } + + spin_unlock_irqrestore(&js_lock, flags); + + return curp; +} + +struct js_port *js_unregister_port(struct js_port *port) +{ + struct js_port **curp = &js_port; + struct js_port *prev; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*curp && (*curp != port)) curp = &((*curp)->next); + *curp = (*curp)->next; + + prev = port->prev; + kfree(port->devs); + kfree(port->axes); + kfree(port->buttons); + kfree(port->corr); + if (port->info) kfree(port->info); + kfree(port); + + spin_unlock_irqrestore(&js_lock, flags); + + return prev; +} + +int js_register_device(struct js_port *port, int number, int axes, int buttons, char *name, + js_ops_func open, js_ops_func close) +{ + struct js_dev **ptrd = &js_dev; + struct js_dev *curd; + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*ptrd) { + ptrd=&(*ptrd)->next; + i++; + } + + *ptrd = curd = kmalloc(sizeof(struct js_dev), GFP_KERNEL); + + curd->next = NULL; + curd->list = NULL; + curd->port = port; + curd->wait = NULL; + curd->open = open; + curd->close = close; + + curd->ahead = 0; + curd->bhead = 0; + curd->tail = JS_BUFF_SIZE - 1; + curd->num_axes = axes; + curd->num_buttons = buttons; + + curd->name = kmalloc(strlen(name) + 1, GFP_KERNEL); + strcpy(curd->name, name); + + curd->cur.axes = kmalloc(axes * sizeof(int), GFP_KERNEL); + curd->cur.buttons = kmalloc((((buttons - 1) >> 5) + 1) * sizeof(int), GFP_KERNEL); + curd->new.axes = kmalloc(axes * sizeof(int), GFP_KERNEL); + curd->new.buttons = kmalloc((((buttons -1 ) >> 5) + 1) * sizeof(int), GFP_KERNEL); + curd->corr = kmalloc(axes * sizeof(struct js_corr), GFP_KERNEL); + + port->devs[number] = curd; + port->axes[number] = curd->new.axes; + port->buttons[number] = curd->new.buttons; + port->corr[number] = curd->corr; + + spin_unlock_irqrestore(&js_lock, flags); + + return i; +} + +void js_unregister_device(struct js_dev *dev) +{ + struct js_dev **curd = &js_dev; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*curd && (*curd != dev)) curd = &((*curd)->next); + *curd = (*curd)->next; + + spin_unlock_irqrestore(&js_lock, flags); + + kfree(dev->cur.axes); + kfree(dev->new.axes); + kfree(dev->cur.buttons); + kfree(dev->new.buttons); + kfree(dev->corr); + kfree(dev->name); + kfree(dev); +} + +/* + * The operations structure. + */ + +static struct file_operations js_fops = +{ + read: js_read, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + poll: js_poll, +#else + select: js_select, +#endif + ioctl: js_ioctl, + open: js_open, + release: js_release, +}; + +/* + * js_init() registers the driver and calls the probe function. + * also initializes some crucial variables. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_init(void) +#endif +{ + int result; + + js_setup_time(); + + if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { + printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); + return -EBUSY; + } + + spin_lock_init(&js_lock); + + init_timer(&js_timer); + js_timer.function = js_do_timer; + js_timer.data = 1; + + memset(&js_comp_glue, 0, sizeof(struct JS_DATA_SAVE_TYPE)); + js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT; + js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT; + +#ifdef MODULE + result = 0; +#else + result = -ENODEV; +#ifdef CONFIG_JOY_LIGHTNING + if (!js_l4_init()) result = 0; +#endif +#ifdef CONFIG_JOY_SIDEWINDER + if (!js_sw_init()) result = 0; +#endif +#ifdef CONFIG_JOY_ASSASIN + if (!js_as_init()) result = 0; +#endif +#ifdef CONFIG_JOY_LOGITECH + if (!js_lt_init()) result = 0; +#endif +#ifdef CONFIG_JOY_THRUSTMASTER + if (!js_tm_init()) result = 0; +#endif +#ifdef CONFIG_JOY_GRAVIS + if (!js_gr_init()) result = 0; +#endif +#ifdef CONFIG_JOY_ANALOG + if (!js_an_init()) result = 0; +#endif +#ifdef CONFIG_JOY_CONSOLE + if (!js_console_init()) result = 0; +#endif +#ifdef CONFIG_JOY_DB9 + if (!js_db9_init()) result = 0; +#endif +#ifdef CONFIG_JOY_AMIGA + if (!js_am_init()) result = 0; +#endif + if (result) printk(KERN_ERR "js: no joysticks found\n"); +#endif + + return result; +} + +/* + * cleanup_module() handles module removal. + */ + +#ifdef MODULE +void cleanup_module(void) +{ + del_timer(&js_timer); + if (unregister_chrdev(JOYSTICK_MAJOR, "js")) + printk(KERN_ERR "js: can't unregister device\n"); +} +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick.c linux/drivers/char/joystick.c --- v2.1.124/linux/drivers/char/joystick.c Thu Aug 27 19:56:29 1998 +++ linux/drivers/char/joystick.c Wed Dec 31 16:00:00 1969 @@ -1,861 +0,0 @@ -/* - * linux/drivers/char/joystick.c Version 1.0.9 - * Copyright (C) 1996-1998 Vojtech Pavlik - */ - -/* - * This is joystick driver for Linux. It supports up to two analog joysticks - * on a PC compatible machine. See Documentation/joystick.txt for changelog - * and credits. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PIT_HZ 1193180L /* PIT clock is 1.19318 MHz */ - -#define JS_MAXTIME PIT_HZ/250 /* timeout for read (4 ms) */ - -#define JS_TIMER_PERIOD HZ/50 /* button valid time (20 ms) */ -#define JS_BH_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */ -#define JS_BH_MAX_PERIOD HZ/25*2 /* axis max valid time (80 ms) */ - -#define JS_FIFO_SIZE 16 /* number of FIFO entries */ -#define JS_BUFF_SIZE 32 /* output buffer size */ -#define JS_RETRIES 4 /* number of retries */ -#define JS_DEF_PREC 16 /* initial precision for all axes */ - -#define JS_NUM 2 /* number of joysticks */ - -#define JS_AXES 0x0f /* bit mask for all axes */ -#define JS_BUTTONS 0xf0 /* bit mask for all buttons */ - -#define PIT_MODE 0x43 /* timer mode port */ -#define PIT_DATA 0x40 /* timer 0 data port */ -#define JS_PORT 0x201 /* joystick port */ - -#define JS_TRIGGER 0xff /* triggers one-shots */ -#define PIT_READ_TIMER 0x00 /* to read timer 0 */ - -#define DELTA(X,Y,Z) ((X)-(Y)+(((X)>=(Y))?0:Z)) /* cyclic delta */ -#define DELTA_T(X,Y) DELTA((X),(Y),(PIT_HZ/HZ)) /* for time measurement */ -#define DELTA_TX(X,Y,Z) DELTA_T((X),((Y)&0xFF)|(((Z)&0xFF)<<8)) -#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C))))) -#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1) -#define GOFF(X) (((X)==JS_FIFO_SIZE-1)?0:(X)+1) -#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1) - -struct js_data { - int ahead; - int bhead; - int tail; - struct js_event buff[JS_BUFF_SIZE]; - struct js_list *list; - struct wait_queue *wait; - unsigned int exist; -}; - -struct js_axis { - int value; - struct js_corr corr; -}; - -struct js_list { - struct js_list *next; /* next-in-list pointer */ - unsigned long time; /* when the device was open */ - int tail; /* a tail for js_buff */ - char startup; -}; - -struct js_fifo { - unsigned long time; - unsigned long event; -}; - -static struct js_data jsd[JS_NUM]; /* joystick data */ -static struct timer_list js_timer; /* joystick timer */ - -static unsigned char js_fifo_head = 0; /* head of the fifo */ -static unsigned char js_fifo_tail = JS_FIFO_SIZE - 1; /* tail of the fifo */ -static struct js_fifo js_fifo[JS_FIFO_SIZE]; /* the fifo */ - -static unsigned char js_last_buttons = 0; /* last read button state */ -static unsigned long js_bh_time = 0; /* last read axis time */ -static unsigned long js_mark_time = 0; - -static unsigned char js_axes_exist; /* all axes that exist */ -static unsigned char js_buttons_exist; /* all buttons that exist */ - -static struct js_axis js_axis[4]; -static unsigned int js_buttons = 0; - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_SUPPORTED_DEVICE("js"); -MODULE_PARM(js, "1-2b"); - -static char js[] = {0, 0}; - -/* - * get_pit() returns the immediate state of PIT0. Must be run - * with interrupts disabled. - */ - -static inline int get_pit(void) -{ - int t, flags; - - save_flags(flags); - cli(); - outb(PIT_READ_TIMER, PIT_MODE); - t = inb(PIT_DATA); - t |= (int) inb(PIT_DATA) << 8; - restore_flags(flags); - return t; -} - -/* - * count_bits() counts set bits in a byte. - */ - -static int count_bits(unsigned int c) -{ - int i = 0; - while (c) { - i += c & 1; - c >>= 1; - } - return i; -} - -/* - * js_correct() performs correction of raw joystick data. - */ - -static int js_correct(int value, struct js_corr *corr) -{ - int t; - - if (corr->type == JS_CORR_NONE) return value; - t = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : value - corr->coef[1]) : value - corr->coef[0]; - - switch (corr->type) { - case JS_CORR_BROKEN: - t = t < 0 ? ((corr->coef[2] * t) >> 14) : ((corr->coef[3] * t) >> 14); - break; - default: - return 0; - } - - if (t < -32767) return -32767; - if (t > 32767) return 32767; - - return t; -} - -/* - * js_compare() compares two close axis values and decides - * whether they are "same". - */ - -static int js_compare(int x, int y, int prec) -{ - return (x < y + prec) && (y < x + prec); -} - -/* - * js_probe() probes for joysticks - */ - -inline int js_probe(void) -{ - int t; - - outb(JS_TRIGGER, JS_PORT); - t = get_pit(); - while (DELTA_T(t, get_pit()) < JS_MAXTIME); - t = inb(JS_PORT); - - if (js[0] || js[1]) { - jsd[0].exist = js[0] & ~(t & JS_AXES); - jsd[1].exist = js[1] & ~(t & JS_AXES); - } else - switch (t & JS_AXES) { - case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break; /* joystick 0 connected */ - case 0x03: jsd[0].exist = 0xcc; jsd[1].exist = 0x00; break; /* joystick 1 connected */ - case 0x04: jsd[0].exist = 0xfb; jsd[1].exist = 0x00; break; /* 3-axis joystick connected */ - case 0x00: jsd[0].exist = 0x33; jsd[1].exist = 0xcc; break; /* joysticks 0 and 1 connected */ - default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */ - } - - js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES; - js_buttons_exist = (jsd[0].exist | jsd[1].exist) & JS_BUTTONS; - - return 0; -} - -/* - * js_do_timer() controls the action by adding entries to the event - * fifo each time a button changes its state or axis valid time - * expires. - */ - -static void js_do_timer(unsigned long data) -{ - int t = ~inb(JS_PORT) & js_buttons_exist; - if ((js_last_buttons != t) && (js_fifo_head != js_fifo_tail)) { - js_fifo[js_fifo_head].event = js_last_buttons = t; - js_fifo[js_fifo_head].time = jiffies; - js_fifo_head++; - if (js_fifo_head == JS_FIFO_SIZE) js_fifo_head = 0; - if (!js_mark_time) { - js_mark_time = jiffies; - mark_bh(JS_BH); - } - } - else - if ((jiffies > js_bh_time + JS_BH_MAX_PERIOD) && !js_mark_time) { - js_mark_time = jiffies; - mark_bh(JS_BH); - } - js_timer.expires = jiffies + JS_TIMER_PERIOD; - add_timer(&js_timer); -} - - -/* - * Put an event in the buffer. This requires additional queue processing - * done by js_sync_buff, otherwise the buffer will be corrupted. - */ - -static void js_add_event(int i, __u32 time, __u8 type, __u8 number, __u16 value) -{ - int ahead = jsd[i].ahead++; - jsd[i].buff[ahead].time = time; - jsd[i].buff[ahead].type = type; - jsd[i].buff[ahead].number = number; - jsd[i].buff[ahead].value = value; - if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead=0; -} - -/* - * This checks for all owerflows caused by recent additions to the buffer. - * It does anything only if some processes are reading the data too slowly. - */ - -static void js_sync_buff(void) -{ - int i; - - for (i = 0; i < JS_NUM; i++) - if (jsd[i].list) - if (jsd[i].bhead != jsd[i].ahead) { - if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) { - struct js_list *curl; - curl = jsd[i].list; - while (curl) { - if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) { - curl->tail = jsd[i].ahead; - curl->startup = jsd[i].exist; - } - curl = curl->next; - } - jsd[i].tail = jsd[i].ahead; - } - jsd[i].bhead = jsd[i].ahead; - wake_up_interruptible(&jsd[i].wait); - } -} - -/* - * js_do_bh() does the main processing and adds events to output buffers. - */ - -static void js_do_bh(void) -{ - - int i, j, k; - unsigned int t; - - if (jiffies > js_bh_time + JS_BH_MIN_PERIOD) { - - unsigned int old_axis[4]; - unsigned int t_low, t_high; - unsigned int flags, joy_state; - unsigned int t1l, t1h, jsm; - unsigned char jss; - unsigned char again; - unsigned char retries = 0; - - for (i = 0; i < 4; i++) - old_axis[i] = js_axis[i].value; - - do { - i = 0; - again = 0; - t_low = 0; - t_high = 0; - joy_state = JS_AXES; - -/* - * Measure the axes. - */ - - save_flags(flags); - cli(); /* no interrupts */ - outb(JS_TRIGGER, JS_PORT); /* trigger one-shots */ - outb(PIT_READ_TIMER, PIT_MODE); /* read timer */ - t = (t1l = inb(PIT_DATA)) | - (t1h = inb(PIT_DATA)) << 8; - restore_flags(flags); - - do { - jss = inb(JS_PORT); - if ((jss ^ joy_state) & js_axes_exist) { - t_low = (t_low << 8) | t1l; - t_high = (t_high << 8) | t1h; - joy_state = (joy_state << 8) | jss; - i++; - } - - cli(); - outb(PIT_READ_TIMER, PIT_MODE); - t1l = inb(PIT_DATA); - t1h = inb(PIT_DATA); - restore_flags(flags); - - } while ((jss & js_axes_exist) && (DELTA_TX(t, t1l, t1h) < JS_MAXTIME)); - -/* - * Process the gathered axis data in joy_state. - */ - - joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */ - - for (; i > 0; i--) { - for (j = 0; j < 4; j++) - if (joy_state & js_axes_exist & (1 << j)) { - jsm = DELTA_TX(t, t_low, t_high); - if (!js_compare(jsm, js_axis[j].value, js_axis[j].corr.prec)) { - if (jsm < js_axis[j].value || !retries) - js_axis[j].value = jsm; - again = 1; - } - } - joy_state = joy_state >> 8; - t_low = t_low >> 8; - t_high = t_high >> 8; - } - - } while (retries++ < JS_RETRIES && again); - -/* - * Check if joystick lost. - */ - - for (i = 0; i < JS_NUM; i++) { - - if (jsd[i].exist && ((jss & jsd[i].exist & JS_AXES) == (jsd[i].exist & JS_AXES))) { - printk(KERN_WARNING "js%d: joystick lost.\n", i); - js_buttons_exist &= ~jsd[i].exist; - js_axes_exist &= ~jsd[i].exist; - jsd[i].exist = 0; - wake_up_interruptible(&jsd[i].wait); - } - - if ((jss & jsd[i].exist & JS_AXES)) { - printk(KERN_WARNING "js%d: joystick broken. Check cables.\n", i); - } - - } - -/* - * Put changed axes into output buffer. - */ - - if (retries > 1) - for (i = 0; i < JS_NUM; i++) - if (jsd[i].list) { - k = 0; - for (j = 0; j < 4; j++) - if ((1 << j) & jsd[i].exist) { - if ((t = js_correct(js_axis[j].value, &js_axis[j].corr)) != - js_correct(old_axis[j], &js_axis[j].corr)) - js_add_event(i, js_mark_time, JS_EVENT_AXIS, k, t); - k++; - } - } - js_bh_time = jiffies; - } - js_mark_time = 0; - -/* - * And now process the button fifo. - */ - - while (js_fifo_head != (t = GOFF(js_fifo_tail))) { - for (i = 0; i < JS_NUM; i++) - if (jsd[i].list) { - k = 0; - for (j = 4; j < 8; j++) - if ((1 << j) & jsd[i].exist) { - if ((1 << j) & (js_buttons ^ js_fifo[t].event)) - js_add_event(i, js_fifo[t].time, JS_EVENT_BUTTON, k, (js_fifo[t].event >> j) & 1); - k++; - } - } - js_buttons = js_fifo[js_fifo_tail = t].event; - } - -/* - * Synchronize the buffer. - */ - - js_sync_buff(); - -} - -/* - * js_lseek() just returns with error. - */ - -static loff_t js_lseek(struct file *file, loff_t offset, int origin) -{ - return -ESPIPE; -} - -/* - * js_read() copies one or more entries from jsd[].buff to user - * space. - */ - -static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); - struct wait_queue wait = { current, NULL }; - struct js_list *curl = file->private_data; - struct js_event *buff = (void *) buf; - unsigned long blocks = count / sizeof(struct js_event); - unsigned long i = 0, j; - int t, u = curl->tail; - int retval = 0; - -/* - * Check user data. - */ - - if (MAJOR(file->f_dentry->d_inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - if (file->f_pos < 0) - return -EINVAL; - if (!blocks) - return -EINVAL; - if (!curl) - return -EINVAL; - - if (minor > JS_NUM) - return -ENODEV; - if (!jsd[minor].exist) - return -ENODEV; - -/* - * Handle (non)blocking i/o. - */ - - - if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE)) - || (curl->startup && !js_bh_time)) { - - add_wait_queue(&jsd[minor].wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE)) - || (curl->startup && !js_bh_time)) { - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - if (!jsd[minor].exist) { - retval = -ENODEV; - break; - } - } - current->state = TASK_RUNNING; - remove_wait_queue(&jsd[minor].wait, &wait); - } - - if (retval) return retval; - -/* - * Do the i/o. - */ - if (count != sizeof(struct JS_DATA_TYPE)) { - - if (curl->startup) { - struct js_event tmpevent; -/* - * Initial button state. - */ - - t = 0; - for (j = 0; j < 4 && (i < blocks) && !retval; j++) - if (jsd[minor].exist & (1 << j)) { - if (curl->startup & (1 << j)) { - tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; - tmpevent.number = t; - tmpevent.value = js_correct(js_axis[j].value, &js_axis[j].corr); - if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) - retval = -EFAULT; - curl->startup &= ~(1 << j); - i++; - } - t++; - } - -/* - * Initial axis state. - */ - - t = 0; - for (j = 4; j < 8 && (i < blocks) && !retval; j++) - if (jsd[minor].exist & (1 << j)) { - if (curl->startup & (1 << j)) { - tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - tmpevent.number = t; - tmpevent.value = (js_buttons >> j) & 1; - if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) - retval = -EFAULT; - curl->startup &= ~(1 << j); - i++; - } - t++; - } - } - -/* - * Buffer data. - */ - - while ((jsd[minor].ahead != (t = GOF(curl->tail))) && (i < blocks) && !retval) { - if (copy_to_user(&buff[i], &jsd[minor].buff[t], sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)((jsd[minor].buff[t].time - curl->time) * (1000/HZ)), &buff[i].time)) - retval = -EFAULT; - curl->tail = t; - i++; - } - - } - - else - -/* - * Handle version 0.x compatibility. - */ - - { - struct JS_DATA_TYPE *bufo = (void *) buf; - int buttons = 0; - - while (~jsd[minor].exist & (1<x, &js_axis[i].value, sizeof(int)); - - i++; - while (~jsd[minor].exist & (1<y, &js_axis[i].value, sizeof(int)); - - i = 0; - for (j = 4; j < 8; j++) - if ((1 << j) & jsd[minor].exist) - buttons |= (!!(js_last_buttons & (1 << j))) << (i++); - copy_to_user(&bufo->buttons, &buttons, sizeof(int)); - - curl->startup = 0; - curl->tail = GOB(jsd[minor].ahead); - retval = sizeof(struct JS_DATA_TYPE); - } - -/* - * Check main tail and move it. - */ - - if (u == jsd[minor].tail) { - t = curl->tail; - curl = jsd[minor].list; - while (curl && curl->tail != jsd[minor].tail) { - if (ROT(jsd[minor].ahead, t, curl->tail) || - (jsd[minor].ahead == curl->tail)) t = curl->tail; - curl = curl->next; - } - if (!curl) jsd[minor].tail = t; - } - - return retval ? retval : i*sizeof(struct js_event); -} - -/* - * js_poll() does select() support. - */ - -static unsigned int js_poll(struct file *file, poll_table *wait) -{ - struct js_list *curl; - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); - curl = file->private_data; - - poll_wait(file, &jsd[minor].wait, wait); - if (GOF(curl->tail) != jsd[minor].ahead) - return POLLIN | POLLRDNORM; - return 0; -} - -/* - * js_ioctl handles misc ioctl calls. - */ - -static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - unsigned int minor = MINOR(inode->i_rdev); - int i, j; - - if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - if (minor > JS_NUM) - return -ENODEV; - if (!jsd[minor].exist) - return -ENODEV; - - switch (cmd) { - case JSIOCGVERSION: - if(put_user(JS_VERSION, (__u32 *) arg)) return -EFAULT; - break; - case JSIOCGAXES: - if(put_user(count_bits(jsd[minor].exist & JS_AXES), (__u8 *) arg)) return -EFAULT; - break; - case JSIOCGBUTTONS: - if(put_user(count_bits(jsd[minor].exist & JS_BUTTONS), (__u8 *) arg)) return -EFAULT; - break; - case JSIOCSCORR: - j = 0; - for (i = 0; i < 4; i++) - if ((1 << i) & jsd[minor].exist) { - if (copy_from_user(&js_axis[i].corr, (void *) arg + j * sizeof(struct js_corr), - sizeof(struct js_corr))) return -EFAULT; - j++; - } - js_bh_time = 0; - break; - case JSIOCGCORR: - j = 0; - for (i = 0; i < 4; i++) - if ((1 << i) & jsd[minor].exist) { - if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr, - sizeof(struct js_corr))) return -EFAULT; - j++; - } - break; - default: - return -EINVAL; - } - - return 0; -} - -/* - * js_open() performs necessary initialization and adds - * an entry to the linked list. - */ - -static int js_open(struct inode *inode, struct file *file) -{ - unsigned int minor = MINOR(inode->i_rdev); - struct js_list *curl; - int t; - - if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - if (minor > JS_NUM) - return -ENODEV; - if (!jsd[minor].exist) { - js_probe(); - if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n", - minor, count_bits(jsd[minor].exist & JS_AXES), count_bits(jsd[minor].exist & JS_BUTTONS), JS_PORT); - else return -ENODEV; - } - - MOD_INC_USE_COUNT; - - if (!jsd[0].list && !jsd[1].list) { - js_timer.expires = jiffies + JS_TIMER_PERIOD; - add_timer(&js_timer); - } - - curl = jsd[minor].list; - jsd[minor].list = kmalloc(sizeof(struct js_list), GFP_KERNEL); - jsd[minor].list->next = curl; - jsd[minor].list->startup = jsd[minor].exist; - jsd[minor].list->tail = t = GOB(jsd[minor].ahead); - jsd[minor].list->time = jiffies; - - file->private_data = jsd[minor].list; - - return 0; -} - -/* - * js_release() removes an entry from list and deallocates memory - * used by it. - */ - -static int js_release(struct inode *inode, struct file *file) -{ - unsigned int minor = MINOR(inode->i_rdev); - struct js_list **curp, *curl; - int t; - - curp = &jsd[minor].list; - curl = file->private_data; - - while (*curp && (*curp != curl)) curp = &((*curp)->next); - *curp = (*curp)->next; - - if (jsd[minor].list) { - if (curl->tail == jsd[minor].tail) { - curl = jsd[minor].list; - t = curl->tail; - while (curl && curl->tail != jsd[minor].tail) { - if (ROT(jsd[minor].ahead, t, curl->tail) || - (jsd[minor].ahead == curl->tail)) t = curl->tail; - curl = curl->next; - } - if (!curl) jsd[minor].tail = t; - } - } - - kfree(file->private_data); - if (!jsd[0].list && !jsd[1].list) del_timer(&js_timer); - - MOD_DEC_USE_COUNT; - return 0; -} - -/* - * The operations structure. - */ - -static struct file_operations js_fops = -{ - js_lseek, /* js_lseek */ - js_read, /* js_read */ - NULL, /* js_write */ - NULL, /* js_readdir */ - js_poll, /* js_poll */ - js_ioctl, /* js_ioctl */ - NULL, /* js_mmap */ - js_open, /* js_open */ - NULL, /* js_flush */ - js_release, /* js_release */ - NULL /* js_sync */ -}; - -/* - * js_setup() parses kernel command line parametres. - */ - -#ifndef MODULE -__initfunc(void js_setup(char *str, int *ints)) - -{ - js[0] = ((ints[0] > 0) ? ints[1] : 0 ); - js[1] = ((ints[0] > 1) ? ints[2] : 0 ); -} -#endif - -/* - * js_init() registres the driver and calls the probe function. - * also initializes some crucial variables. - */ - -#ifdef MODULE -int init_module(void) -#else -__initfunc(int js_init(void)) -#endif -{ - int i; - - if (check_region(JS_PORT, 1)) { - printk(KERN_ERR "js: port %#x already in use\n", JS_PORT); - return -EBUSY; - } - - if (js_probe() < 0) { - printk(KERN_INFO "js: no joysticks found\n"); - return -ENODEV; - } - - if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { - printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); - return -EBUSY; - } - - for (i = 0; i < JS_NUM; i++) { - if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n", - i, count_bits(jsd[i].exist & JS_AXES), count_bits(jsd[i].exist & JS_BUTTONS), JS_PORT); - jsd[i].ahead = jsd[i].bhead = 0; - jsd[i].tail = JS_BUFF_SIZE - 1; - jsd[i].list = NULL; - jsd[i].wait = NULL; - memset(jsd[i].buff, 0, JS_BUFF_SIZE * sizeof(struct js_event)); - } - - for (i = 0; i < 4; i++) { - js_axis[i].corr.type = JS_CORR_NONE; - js_axis[i].corr.prec = JS_DEF_PREC; - } - - request_region(JS_PORT, 1, "js"); - init_bh(JS_BH, &js_do_bh); - enable_bh(JS_BH); - init_timer(&js_timer); - js_timer.function = js_do_timer; - - return 0; -} - -/* - * cleanup_module() handles module removal. - */ - -#ifdef MODULE -void cleanup_module(void) -{ - if (MOD_IN_USE) - printk(KERN_NOTICE "js: device busy, remove delayed\n"); - else { - del_timer(&js_timer); - disable_bh(JS_BH); - if (unregister_chrdev(JOYSTICK_MAJOR, "js")) - printk(KERN_ERR "js: module cleanup failed\n"); - release_region(JS_PORT, 1); - } -} -#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/mac_SCC.c linux/drivers/char/mac_SCC.c --- v2.1.124/linux/drivers/char/mac_SCC.c Tue Jun 23 10:01:22 1998 +++ linux/drivers/char/mac_SCC.c Wed Oct 7 09:26:37 1998 @@ -67,6 +67,7 @@ #include #include #include +#include #include "mac_SCC.h" @@ -1300,6 +1301,12 @@ /* testing: fix up broken 24 bit addresses (ClassicII) */ if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase) mac_bi_data.sccbase |= 0x50000000; + + if ( !hwreg_present((void *)mac_bi_data.sccbase)) + { + printk(KERN_WARNING "z8530: Serial devices not accessible. Check serial switch.\n"); + return; + } for(n=0;n<2;n++) { @@ -1459,11 +1466,7 @@ printk("Mac68K Z8530 serial driver version 1.01\n"); /* SCC present at all? */ - if (MACH_IS_ATARI || MACH_IS_AMIGA -#if 0 - || !(MACHW_PRESENT(SCC) || MACHW_PRESENT(ST_ESCC)) -#endif - ) + if (!MACH_IS_MAC) return( -ENODEV ); if (zs_chain == 0) diff -u --recursive --new-file v2.1.124/linux/drivers/char/radio-sf16fmi.c linux/drivers/char/radio-sf16fmi.c --- v2.1.124/linux/drivers/char/radio-sf16fmi.c Wed Aug 26 11:37:37 1998 +++ linux/drivers/char/radio-sf16fmi.c Tue Oct 6 09:39:49 1998 @@ -26,7 +26,7 @@ { int port; int curvol; /* 1 or 0 */ - unsigned long curfreq; /* RSF16_PREC * freq in MHz */ + unsigned long curfreq; /* freq in kHz */ __u32 flags; }; diff -u --recursive --new-file v2.1.124/linux/drivers/char/random.c linux/drivers/char/random.c --- v2.1.124/linux/drivers/char/random.c Wed Aug 26 11:37:37 1998 +++ linux/drivers/char/random.c Mon Oct 5 10:04:34 1998 @@ -1250,7 +1250,7 @@ } #if HASH_BUFFER_SIZE & 1 /* There's a middle word to deal with */ x = tmp[HASH_BUFFER_SIZE/2]; - add_entropy_words(r, x, (__u32)buf); + add_entropy_words(r, x, (__u32)((unsigned long)buf)); x ^= (x >> 16); /* Fold it in half */ ((__u16 *)tmp)[HASH_BUFFER_SIZE-1] = (__u16)x; #endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/saa5249.c linux/drivers/char/saa5249.c --- v2.1.124/linux/drivers/char/saa5249.c Sat Sep 5 16:46:40 1998 +++ linux/drivers/char/saa5249.c Tue Oct 6 09:39:49 1998 @@ -149,7 +149,6 @@ if(device->bus->id!=I2C_BUSID_BT848) return -EINVAL; - printk(KERN_DEBUG "saa5249_attach: bus %p\n", device->bus); strcpy(device->name, IF_NAME); /* @@ -205,7 +204,6 @@ static int saa5249_detach(struct i2c_device *device) { struct video_device *vd=device->data; - printk(KERN_DEBUG "saa5249_detach\n"); video_unregister_device(vd); kfree(vd->priv); kfree(vd); @@ -215,7 +213,6 @@ static int saa5249_command(struct i2c_device *device, unsigned int cmd, void *arg) { - printk(KERN_DEBUG "saa5249_command\n"); return -EINVAL; } @@ -621,11 +618,9 @@ struct saa5249_device *t=vd->priv; int pgbuf; - printk("t=%p\n",t); if (t->bus==NULL) return -ENODEV; - printk("Do i2c %p\n",t->bus); if (i2c_senddata(t, CCTWR, 0, 0, -1) || /* Select R11 */ /* Turn off parity checks (we do this ourselves) */ i2c_senddata(t, CCTWR, 1, disp_modes[t->disp_mode][0], 0, -1) || @@ -636,7 +631,6 @@ return -EIO; } - printk("clean\n"); for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); @@ -648,7 +642,6 @@ t->is_searching[pgbuf] = FALSE; } t->virtual_mode=FALSE; - printk("Go\n"); MOD_INC_USE_COUNT; return 0; } diff -u --recursive --new-file v2.1.124/linux/drivers/char/serial167.c linux/drivers/char/serial167.c --- v2.1.124/linux/drivers/char/serial167.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/serial167.c Mon Oct 5 14:12:12 1998 @@ -0,0 +1,2984 @@ +/* + * linux/drivers/char/serial167.c + * + * Driver for MVME166/7 board serial ports, which are via a CD2401. + * Based very much on cyclades.c. + * + * MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk] + * + * ============================================================== + * + * static char rcsid[] = + * "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $"; + * + * linux/kernel/cyclades.c + * + * Maintained by Marcio Saito (cyclades@netcom.com) and + * Randolph Bentson (bentson@grieg.seaslug.org) + * + * Much of the design and some of the code came from serial.c + * which was copyright (C) 1991, 1992 Linus Torvalds. It was + * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, + * and then fixed as suggested by Michael K. Johnson 12/12/92. + * + * This version does not support shared irq's. + * + * This module exports the following rs232 io functions: + * int cy_init(void); + * int cy_open(struct tty_struct *tty, struct file *filp); + * + * $Log: cyclades.c,v $ + * Revision 1.36.1.4 1995/03/29 06:14:14 bentson + * disambiguate between Cyclom-16Y and Cyclom-32Ye; + * + * 200 lines of changes record removed - RGH 11-10-95, starting work on + * converting this to drive serial ports on mvme166 (cd2401). + */ + +#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 cy_put_user put_user + +static unsigned long cy_get_user(unsigned long *addr) +{ + unsigned long result = 0; + int error = get_user (result, addr); + if (error) + printk ("serial167: cy_get_user: error == %d\n", error); + return result; +} + +#define SERIAL_PARANOIA_CHECK +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_DEBUG_OTHER +#undef SERIAL_DEBUG_IO +#undef SERIAL_DEBUG_COUNT +#undef SERIAL_DEBUG_DTR +#undef CYCLOM_16Y_HACK +#define CYCLOM_ENABLE_MONITORING + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define WAKEUP_CHARS 256 + +#define STD_COM_FLAGS (0) + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + + +DECLARE_TASK_QUEUE(tq_cyclades); + +struct tty_driver cy_serial_driver, cy_callout_driver; +extern int serial_console; +static struct cyclades_port *serial_console_info = NULL; +static unsigned int serial_console_cflag = 0; +u_char initial_console_speed; + +/* Base address of cd2401 chip on mvme166/7 */ + +#define BASE_ADDR (0xfff45000) +#define pcc2chip ((volatile u_char *)0xfff42000) +#define PccSCCMICR 0x1d +#define PccSCCTICR 0x1e +#define PccSCCRICR 0x1f +#define PccTPIACKR 0x25 +#define PccRPIACKR 0x27 +#define PccIMLR 0x3f + +/* This is the per-port data structure */ +struct cyclades_port cy_port[] = { + /* CARD# */ + {-1 }, /* ttyS0 */ + {-1 }, /* ttyS1 */ + {-1 }, /* ttyS2 */ + {-1 }, /* ttyS3 */ +}; +#define NR_PORTS (sizeof(cy_port)/sizeof(struct cyclades_port)) + +static int serial_refcount; + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf = 0; +static struct semaphore tmp_buf_sem = MUTEX; + +/* + * This is used to look up the divisor speeds and the timeouts + * We're normally limited to 15 distinct baud rates. The extra + * are accessed via settings in info->flags. + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, + 0}; + +#if 0 +static char baud_co[] = { /* 25 MHz clock option table */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static char baud_bpr[] = { /* 25 MHz baud rate period table */ + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; +#endif + +/* I think 166 brd clocks 2401 at 20MHz.... */ + +/* These values are written directly to tcor, and >> 5 for writing to rcor */ +static u_char baud_co[] = { /* 20 MHz clock option table */ + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x60, 0x60, 0x40, + 0x40, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* These values written directly to tbpr/rbpr */ +static u_char baud_bpr[] = { /* 20 MHz baud rate period table */ + 0x00, 0xc0, 0x80, 0x58, 0x6c, 0x40, 0xc0, 0x81, 0x40, 0x81, + 0x57, 0x40, 0x81, 0x40, 0x81, 0x40, 0x2b, 0x20, 0x15, 0x10}; + +static u_char baud_cor4[] = { /* receive threshold */ + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; + + + +static void shutdown(struct cyclades_port *); +static int startup (struct cyclades_port *); +static void cy_throttle(struct tty_struct *); +static void cy_unthrottle(struct tty_struct *); +static void config_setup(struct cyclades_port *); +extern void console_print(const char *); +#ifdef CYCLOM_SHOW_STATUS +static void show_status(int); +#endif + +#ifdef CONFIG_REMOTE_DEBUG +static void debug_setup(void); +void queueDebugChar (int c); +int getDebugChar(void); + +#define DEBUG_PORT 1 +#define DEBUG_LEN 256 + +typedef struct { + int in; + int out; + unsigned char buf[DEBUG_LEN]; +} debugq; + +debugq debugiq; +#endif + +/* + * I have my own version of udelay(), as it is needed when initialising + * the chip, before the delay loop has been calibrated. Should probably + * reference one of the vmechip2 or pccchip2 counter for an accurate + * delay, but this wild guess will do for now. + */ + +void my_udelay (long us) +{ + u_char x; + volatile u_char *p = &x; + int i; + + while (us--) + for (i = 100; i; i--) + x |= *p; +} + +static inline int +serial_paranoia_check(struct cyclades_port *info, + dev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%d, %d) in %s\n"; + static const char *badinfo = + "Warning: null cyclades_port for (%d, %d) in %s\n"; + static const char *badrange = + "Warning: cyclades_port out of range for (%d, %d) in %s\n"; + + if (!info) { + printk(badinfo, MAJOR(device), MINOR(device), routine); + return 1; + } + + if( (long)info < (long)(&cy_port[0]) + || (long)(&cy_port[NR_PORTS]) < (long)info ){ + printk(badrange, MAJOR(device), MINOR(device), routine); + return 1; + } + + if (info->magic != CYCLADES_MAGIC) { + printk(badmagic, MAJOR(device), MINOR(device), routine); + return 1; + } +#endif + return 0; +} /* serial_paranoia_check */ + +#if 0 +/* The following diagnostic routines allow the driver to spew + information on the screen, even (especially!) during interrupts. + */ +void +SP(char *data){ + unsigned long flags; + save_flags(flags); cli(); + console_print(data); + restore_flags(flags); +} +char scrn[2]; +void +CP(char data){ + unsigned long flags; + save_flags(flags); cli(); + scrn[0] = data; + console_print(scrn); + restore_flags(flags); +}/* CP */ + +void CP1(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP1 */ +void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */ +void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */ +void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */ +#endif + +/* This routine waits up to 1000 micro-seconds for the previous + command to the Cirrus chip to complete and then issues the + new command. An error is returned if the previous command + didn't finish within the time limit. + */ +u_short +write_cy_cmd(volatile u_char *base_addr, u_char cmd) +{ + unsigned long flags; + volatile int i; + + save_flags(flags); cli(); + /* Check to see that the previous command has completed */ + for(i = 0 ; i < 100 ; i++){ + if (base_addr[CyCCR] == 0){ + break; + } + my_udelay(10L); + } + /* if the CCR never cleared, the previous command + didn't finish within the "reasonable time" */ + if ( i == 10 ) { + restore_flags(flags); + return (-1); + } + + /* Issue the new command */ + base_addr[CyCCR] = cmd; + restore_flags(flags); + return(0); +} /* write_cy_cmd */ + + +/* cy_start and cy_stop provide software output flow control as a + function of XON/XOFF, software CTS, and other such stuff. */ + +static void +cy_stop(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + unsigned long flags; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_stop ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_stop")) + return; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)(channel); /* index channel */ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + restore_flags(flags); + + return; +} /* cy_stop */ + +static void +cy_start(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + unsigned long flags; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_start ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_start")) + return; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)(channel); + base_addr[CyIER] |= CyTxMpty; + restore_flags(flags); + + return; +} /* cy_start */ + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver + * (also known as the "bottom half"). This can be called any + * number of times for any channel without harm. + */ +static inline void +cy_sched_event(struct cyclades_port *info, int event) +{ + info->event |= 1 << event; /* remember what kind of event and who */ + queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */ + mark_bh(CYCLADES_BH); /* then trigger event */ +} /* cy_sched_event */ + + +/* The real interrupt service routines are called + whenever the card wants its hand held--chars + received, out buffer empty, modem change, etc. + */ +static void +cd2401_rxerr_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct tty_struct *tty; + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + unsigned char err, rfoc; + int channel; + char data; + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + + if ((err = base_addr[CyRISR]) & CyTIMEOUT) { + /* This is a receive timeout interrupt, ignore it */ + base_addr[CyREOIR] = CyNOTRANS; + return; + } + + /* Read a byte of data if there is any - assume the error + * is associated with this character */ + + if ((rfoc = base_addr[CyRFOC]) != 0) + data = base_addr[CyRDR]; + else + data = 0; + + /* if there is nowhere to put the data, discard it */ + if(info->tty == 0) { + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; + return; + } + else { /* there is an open port for this data */ + tty = info->tty; + if(err & info->ignore_status_mask){ + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; + return; + } + if (tty->flip.count < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + if (err & info->read_status_mask){ + if(err & CyBREAK){ + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + *tty->flip.char_buf_ptr++ = data; + if (info->flags & ASYNC_SAK){ + do_SAK(tty); + } + }else if(err & CyFRAME){ + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + *tty->flip.char_buf_ptr++ = data; + }else if(err & CyPARITY){ + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + *tty->flip.char_buf_ptr++ = data; + }else if(err & CyOVERRUN){ + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + *tty->flip.char_buf_ptr++ = 0; + /* + If the flip buffer itself is + overflowing, we still loose + the next incoming character. + */ + if(tty->flip.count < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; + } + /* These two conditions may imply */ + /* a normal read should be done. */ + /* else if(data & CyTIMEOUT) */ + /* else if(data & CySPECHAR) */ + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + /* there was a software buffer overrun + and nothing could be done about it!!! */ + } + } + queue_task(&tty->flip.tqueue, &tq_timer); + /* end of service */ + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; +} /* cy_rxerr_interrupt */ + +static void +cd2401_modem_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + int mdm_change; + int mdm_status; + + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + + mdm_change = base_addr[CyMISR]; + mdm_status = base_addr[CyMSVR1]; + + if(info->tty == 0){ /* nowhere to put the data, ignore it */ + ; + }else{ + if((mdm_change & CyDCD) + && (info->flags & ASYNC_CHECK_CD)){ + if(mdm_status & CyDCD){ +/* CP('!'); */ + cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) + &&(info->flags & ASYNC_CALLOUT_NOHUP))){ +/* CP('@'); */ + cy_sched_event(info, Cy_EVENT_HANGUP); + } + } + if((mdm_change & CyCTS) + && (info->flags & ASYNC_CTS_FLOW)){ + if(info->tty->stopped){ + if(mdm_status & CyCTS){ + /* !!! cy_start isn't used because... */ + info->tty->stopped = 0; + base_addr[CyIER] |= CyTxMpty; + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + }else{ + if(!(mdm_status & CyCTS)){ + /* !!! cy_stop isn't used because... */ + info->tty->stopped = 1; + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + } + } + } + if(mdm_status & CyDSR){ + } + } + base_addr[CyMEOIR] = 0; +} /* cy_modem_interrupt */ + +static void +cd2401_tx_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + int char_count, saved_cnt; + int outch; + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + +#ifdef CONFIG_REMOTE_DEBUG + if (channel == DEBUG_PORT) { + panic ("TxInt on debug port!!!"); + } +#endif + + info = &cy_port[channel]; + + /* validate the port number (as configured and open) */ + if( (channel < 0) || (NR_PORTS <= channel) ){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + base_addr[CyTEOIR] = CyNOTRANS; + return; + } + info->last_active = jiffies; + if(info->tty == 0){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + base_addr[CyTEOIR] = CyNOTRANS; + return; + } + + /* load the on-chip space available for outbound data */ + saved_cnt = char_count = base_addr[CyTFTC]; + + if(info->x_char) { /* send special char */ + outch = info->x_char; + base_addr[CyTDR] = outch; + char_count--; + info->x_char = 0; + } + + if (info->x_break){ + /* The Cirrus chip requires the "Embedded Transmit + Commands" of start break, delay, and end break + sequences to be sent. The duration of the + break is given in TICs, which runs at HZ + (typically 100) and the PPR runs at 200 Hz, + so the delay is duration * 200/HZ, and thus a + break can run from 1/100 sec to about 5/4 sec. + Need to check these values - RGH 141095. + */ + base_addr[CyTDR] = 0; /* start break */ + base_addr[CyTDR] = 0x81; + base_addr[CyTDR] = 0; /* delay a bit */ + base_addr[CyTDR] = 0x82; + base_addr[CyTDR] = info->x_break*200/HZ; + base_addr[CyTDR] = 0; /* terminate break */ + base_addr[CyTDR] = 0x83; + char_count -= 7; + info->x_break = 0; + } + + while (char_count > 0){ + if (!info->xmit_cnt){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + break; + } + if (info->xmit_buf == 0){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + break; + } + if (info->tty->stopped || info->tty->hw_stopped){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + break; + } + /* Because the Embedded Transmit Commands have been + enabled, we must check to see if the escape + character, NULL, is being sent. If it is, we + must ensure that there is room for it to be + doubled in the output stream. Therefore we + no longer advance the pointer when the character + is fetched, but rather wait until after the check + for a NULL output character. (This is necessary + because there may not be room for the two chars + needed to send a NULL. + */ + outch = info->xmit_buf[info->xmit_tail]; + if( outch ){ + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR] = outch; + char_count--; + }else{ + if(char_count > 1){ + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR] = outch; + base_addr[CyTDR] = 0; + char_count--; + char_count--; + }else{ + break; + } + } + } + + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS; +} /* cy_tx_interrupt */ + +static void +cd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct tty_struct *tty; + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + char data; + int char_count; + int save_cnt; + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + save_cnt = char_count = base_addr[CyRFOC]; + +#ifdef CONFIG_REMOTE_DEBUG + if (channel == DEBUG_PORT) { + while (char_count--) { + data = base_addr[CyRDR]; + queueDebugChar(data); + } + } + else +#endif + /* if there is nowhere to put the data, discard it */ + if(info->tty == 0){ + while(char_count--){ + data = base_addr[CyRDR]; + } + }else{ /* there is an open port for this data */ + tty = info->tty; + /* load # characters available from the chip */ + +#ifdef CYCLOM_ENABLE_MONITORING + ++info->mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + while(char_count--){ + data = base_addr[CyRDR]; + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + continue; + } + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; +#ifdef CYCLOM_16Y_HACK + udelay(10L); +#endif + } + queue_task(&tty->flip.tqueue, &tq_timer); + } + /* end of service */ + base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS; +} /* cy_rx_interrupt */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using cy_sched_event(), and they get done here. + * + * This is done through one level of indirection--the task queue. + * When a hardware interrupt service routine wants service by the + * driver's bottom half, it enqueues the appropriate tq_struct (one + * per port) to the tq_cyclades work queue and sets a request flag + * via mark_bh for processing that queue. When the time is right, + * do_cyclades_bh is called (because of the mark_bh) and it requests + * that the work queue be processed. + * + * Although this may seem unwieldy, it gives the system a way to + * pass an argument (in this case the pointer to the cyclades_port + * structure) to the bottom half of the driver. Previous kernels + * had to poll every port to see if that port needed servicing. + */ +static void +do_cyclades_bh(void) +{ + run_task_queue(&tq_cyclades); +} /* do_cyclades_bh */ + +static void +do_softint(void *private_) +{ + struct cyclades_port *info = (struct cyclades_port *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } +} /* do_softint */ + + +/* This is called whenever a port becomes active; + interrupts are enabled and DTR & RTS are turned on. + */ +static int +startup(struct cyclades_port * info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + + if (info->flags & ASYNC_INITIALIZED){ + return 0; + } + + if (!info->type){ + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + return 0; + } + if (!info->xmit_buf){ + info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); + if (!info->xmit_buf){ + return -ENOMEM; + } + } + + config_setup(info); + + channel = info->line; + +#ifdef SERIAL_DEBUG_OPEN + printk("startup channel %d\n", channel); +#endif + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR); + + base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */ + base_addr[CyMSVR1] = CyRTS; +/* CP('S');CP('1'); */ + base_addr[CyMSVR2] = CyDTR; + +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + + base_addr[CyIER] |= CyRxData; + info->flags |= ASYNC_INITIALIZED; + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + restore_flags(flags); + +#ifdef SERIAL_DEBUG_OPEN + printk(" done\n"); +#endif + return 0; +} /* startup */ + +void +start_xmit( struct cyclades_port *info ) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + channel = info->line; + save_flags(flags); cli(); + base_addr[CyCAR] = channel; + base_addr[CyIER] |= CyTxMpty; + restore_flags(flags); +} /* start_xmit */ + +/* + * This routine shuts down a serial port; interrupts are disabled, + * and DTR is dropped if the hangup on close termio flag is on. + */ +static void +shutdown(struct cyclades_port * info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + if (!(info->flags & ASYNC_INITIALIZED)){ +/* CP('$'); */ + return; + } + + channel = info->line; + +#ifdef SERIAL_DEBUG_OPEN + printk("shutdown channel %d\n", channel); +#endif + + /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE + SENT BEFORE DROPPING THE LINE !!! (Perhaps + set some flag that is read when XMTY happens.) + Other choices are to delay some fixed interval + or schedule some later processing. + */ + save_flags(flags); cli(); + if (info->xmit_buf){ + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + base_addr[CyCAR] = (u_char)channel; + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + base_addr[CyMSVR1] = 0; +/* CP('C');CP('1'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + } + write_cy_cmd(base_addr,CyDIS_RCVR); + /* it may be appropriate to clear _XMIT at + some later date (after testing)!!! */ + + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + +#ifdef SERIAL_DEBUG_OPEN + printk(" done\n"); +#endif + return; +} /* shutdown */ + +/* + * This routine finds or computes the various line characteristics. + */ +static void +config_setup(struct cyclades_port * info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + unsigned cflag; + int i; + unsigned char ti, need_init_chan = 0; + + if (!info->tty || !info->tty->termios){ + return; + } + if (info->line == -1){ + return; + } + cflag = info->tty->termios->c_cflag; + + /* baud rate */ + i = cflag & CBAUD; +#ifdef CBAUDEX +/* Starting with kernel 1.1.65, there is direct support for + higher baud rates. The following code supports those + changes. The conditional aspect allows this driver to be + used for earlier as well as later kernel versions. (The + mapping is slightly different from serial.c because there + is still the possibility of supporting 75 kbit/sec with + the Cyclades board.) + */ + if (i & CBAUDEX) { + if (i == B57600) + i = 16; + else if(i == B115200) + i = 18; +#ifdef B78600 + else if(i == B78600) + i = 17; +#endif + else + info->tty->termios->c_cflag &= ~CBAUDEX; + } +#endif + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + } + /* Don't ever change the speed of the console port. It will + * run at the speed specified in bootinfo, or at 19.2K */ + /* Actually, it should run at whatever speed 166Bug was using */ + /* Note info->timeout isn't used at present */ + if (info != serial_console_info) { + info->tbpr = baud_bpr[i]; /* Tx BPR */ + info->tco = baud_co[i]; /* Tx CO */ + info->rbpr = baud_bpr[i]; /* Rx BPR */ + info->rco = baud_co[i] >> 5; /* Rx CO */ + if (baud_table[i] == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (baud_table[i]) { + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor7 = 0; + info->cor6 = 0; + info->cor5 = 0; + info->cor4 = (info->default_threshold + ? info->default_threshold + : baud_cor4[i]); /* receive threshold */ + /* Following two lines added 101295, RGH. */ + /* It is obviously wrong to access CyCORx, and not info->corx here, + * try and remember to fix it later! */ + channel = info->line; + base_addr[CyCAR] = (u_char)channel; + if (C_CLOCAL(info->tty)) { + if (base_addr[CyIER] & CyMdmCh) + base_addr[CyIER] &= ~CyMdmCh; /* without modem intr */ + /* ignore 1->0 modem transitions */ + if (base_addr[CyCOR4] & (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR4] &= ~(CyDSR|CyCTS|CyDCD); + /* ignore 0->1 modem transitions */ + if (base_addr[CyCOR5] & (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR5] &= ~(CyDSR|CyCTS|CyDCD); + } else { + if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh) + base_addr[CyIER] |= CyMdmCh; /* with modem intr */ + /* act on 1->0 modem transitions */ + if ((base_addr[CyCOR4] & (CyDSR|CyCTS|CyDCD)) != (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR4] |= CyDSR|CyCTS|CyDCD; + /* act on 0->1 modem transitions */ + if ((base_addr[CyCOR5] & (CyDSR|CyCTS|CyDCD)) != (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR5] |= CyDSR|CyCTS|CyDCD; + } + info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP; + info->cor2 = CyETC; + switch(cflag & CSIZE){ + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + info->cor1 |= CyPARITY_O; + }else{ + info->cor1 |= CyPARITY_E; + } + }else{ + info->cor1 |= CyPARITY_NONE; + } + + /* CTS flow control flag */ +#if 0 + /* Don't complcate matters for now! RGH 141095 */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } +#endif + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + + /* CyCMR set once only in mvme167_init_serial() */ + if (base_addr[CyLICR] != channel << 2) + base_addr[CyLICR] = channel << 2; + if (base_addr[CyLIVR] != 0x5c) + base_addr[CyLIVR] = 0x5c; + + /* tx and rx baud rate */ + + if (base_addr[CyCOR1] != info->cor1) + need_init_chan = 1; + if (base_addr[CyTCOR] != info->tco) + base_addr[CyTCOR] = info->tco; + if (base_addr[CyTBPR] != info->tbpr) + base_addr[CyTBPR] = info->tbpr; + if (base_addr[CyRCOR] != info->rco) + base_addr[CyRCOR] = info->rco; + if (base_addr[CyRBPR] != info->rbpr) + base_addr[CyRBPR] = info->rbpr; + + /* set line characteristics according configuration */ + + if (base_addr[CySCHR1] != START_CHAR(info->tty)) + base_addr[CySCHR1] = START_CHAR(info->tty); + if (base_addr[CySCHR2] != STOP_CHAR(info->tty)) + base_addr[CySCHR2] = STOP_CHAR(info->tty); + if (base_addr[CySCRL] != START_CHAR(info->tty)) + base_addr[CySCRL] = START_CHAR(info->tty); + if (base_addr[CySCRH] != START_CHAR(info->tty)) + base_addr[CySCRH] = START_CHAR(info->tty); + if (base_addr[CyCOR1] != info->cor1) + base_addr[CyCOR1] = info->cor1; + if (base_addr[CyCOR2] != info->cor2) + base_addr[CyCOR2] = info->cor2; + if (base_addr[CyCOR3] != info->cor3) + base_addr[CyCOR3] = info->cor3; + if (base_addr[CyCOR4] != info->cor4) + base_addr[CyCOR4] = info->cor4; + if (base_addr[CyCOR5] != info->cor5) + base_addr[CyCOR5] = info->cor5; + if (base_addr[CyCOR6] != info->cor6) + base_addr[CyCOR6] = info->cor6; + if (base_addr[CyCOR7] != info->cor7) + base_addr[CyCOR7] = info->cor7; + + if (need_init_chan) + write_cy_cmd(base_addr,CyINIT_CHAN); + + base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */ + + /* 2ms default rx timeout */ + ti = info->default_timeout ? info->default_timeout : 0x02; + if (base_addr[CyRTPRL] != ti) + base_addr[CyRTPRL] = ti; + if (base_addr[CyRTPRH] != 0) + base_addr[CyRTPRH] = 0; + + /* Set up RTS here also ????? RGH 141095 */ + if(i == 0){ /* baud rate is zero, turn off line */ + if ((base_addr[CyMSVR2] & CyDTR) == CyDTR) + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + }else{ + if ((base_addr[CyMSVR2] & CyDTR) != CyDTR) + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + } + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + restore_flags(flags); + +} /* config_setup */ + + +static void +cy_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cy_put_char ttyS%d(0x%02x)\n", info->line, ch); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt >= PAGE_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= PAGE_SIZE - 1; + info->xmit_cnt++; + restore_flags(flags); +} /* cy_put_char */ + + +static void +cy_flush_chars(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_IO + printk("cy_flush_chars ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped + || tty->hw_stopped || !info->xmit_buf) + return; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = channel; + base_addr[CyIER] |= CyTxMpty; + restore_flags(flags); +} /* cy_flush_chars */ + + +/* This routine gets called when tty_write has put something into + the write_queue. If the port is not already transmitting stuff, + start it off by enabling interrupts. The interrupt service + routine will then ensure that the characters are sent. If the + port is already active, there is no need to kick it. + */ +static int +cy_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + int c, total = 0; + +#ifdef SERIAL_DEBUG_IO + printk("cy_write ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_write")){ + return 0; + } + + if (!tty || !info->xmit_buf || !tmp_buf){ + return 0; + } + + while (1) { + save_flags(flags); cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0){ + restore_flags(flags); + break; + } + + if (from_user) { + down(&tmp_buf_sem); + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + up(&tmp_buf_sem); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + + + if (info->xmit_cnt + && !tty->stopped + && !tty->hw_stopped ) { + start_xmit(info); + } + return total; +} /* cy_write */ + + +static int +cy_write_room(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + int ret; + +#ifdef SERIAL_DEBUG_IO + printk("cy_write_room ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_write_room")) + return 0; + ret = PAGE_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} /* cy_write_room */ + + +static int +cy_chars_in_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + +#ifdef SERIAL_DEBUG_IO + printk("cy_chars_in_buffer ttyS%d %d\n", info->line, info->xmit_cnt); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) + return 0; + + return info->xmit_cnt; +} /* cy_chars_in_buffer */ + + +static void +cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cy_flush_buffer ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} /* cy_flush_buffer */ + + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled or that the + throttle should be released. + */ +static void +cy_throttle(struct tty_struct * tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); + printk("cy_throttle ttyS%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; + } + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = 0; + restore_flags(flags); + + return; +} /* cy_throttle */ + + +static void +cy_unthrottle(struct tty_struct * tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); + printk("cy_unthrottle ttyS%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; + } + + if (I_IXOFF(tty)) { + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; + restore_flags(flags); + + return; +} /* cy_unthrottle */ + +static int +get_serial_info(struct cyclades_port * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + +/* CP('g'); */ + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->line; + tmp.irq = 0; + tmp.flags = info->flags; + tmp.baud_base = 0; /*!!!*/ + tmp.close_delay = info->close_delay; + tmp.custom_divisor = 0; /*!!!*/ + tmp.hub6 = 0; /*!!!*/ + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); + return 0; +} /* get_serial_info */ + +static int +set_serial_info(struct cyclades_port * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct cyclades_port old_info; + +/* CP('s'); */ + if (!new_info) + return -EFAULT; + copy_from_user(&new_serial,new_info,sizeof(new_serial)); + old_info = *info; + + if (!suser()) { + if ((new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != + (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + goto check_and_exit; + } + + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay; + + +check_and_exit: + if (info->flags & ASYNC_INITIALIZED){ + config_setup(info); + return 0; + }else{ + return startup(info); + } +} /* set_serial_info */ + +static int +get_modem_info(struct cyclades_port * info, unsigned int *value) +{ + int channel; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + unsigned char status; + unsigned int result; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + status = base_addr[CyMSVR1] | base_addr[CyMSVR2]; + restore_flags(flags); + + result = ((status & CyRTS) ? TIOCM_RTS : 0) + | ((status & CyDTR) ? TIOCM_DTR : 0) + | ((status & CyDCD) ? TIOCM_CAR : 0) + | ((status & CyDSR) ? TIOCM_DSR : 0) + | ((status & CyCTS) ? TIOCM_CTS : 0); + cy_put_user(result,(unsigned long *) value); + return 0; +} /* get_modem_info */ + +static int +set_modem_info(struct cyclades_port * info, unsigned int cmd, + unsigned int *value) +{ + int channel; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + unsigned int arg = cy_get_user((unsigned long *) value); + + channel = info->line; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('S');CP('2'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = 0; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('C');CP('2'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; + restore_flags(flags); + }else{ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = 0; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('S');CP('3'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + }else{ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('C');CP('3'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + } + break; + default: + return -EINVAL; + } + return 0; +} /* set_modem_info */ + +static void +send_break( struct cyclades_port * info, int duration) +{ /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + info->x_break = duration; + if (!info->xmit_cnt ) { + start_xmit(info); + } +} /* send_break */ + +static int +get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) +{ + + copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); + info->mon.int_count = 0; + info->mon.char_count = 0; + info->mon.char_max = 0; + info->mon.char_last = 0; + return 0; +} + +static int +set_threshold(struct cyclades_port * info, unsigned long value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + channel = info->line; + + info->cor4 &= ~CyREC_FIFO; + info->cor4 |= value & CyREC_FIFO; + base_addr[CyCOR4] = info->cor4; + return 0; +} + +static int +get_threshold(struct cyclades_port * info, unsigned long *value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + unsigned long tmp; + + channel = info->line; + + tmp = base_addr[CyCOR4] & CyREC_FIFO; + cy_put_user(tmp,value); + return 0; +} + +static int +set_default_threshold(struct cyclades_port * info, unsigned long value) +{ + info->default_threshold = value & 0x0f; + return 0; +} + +static int +get_default_threshold(struct cyclades_port * info, unsigned long *value) +{ + cy_put_user(info->default_threshold,value); + return 0; +} + +static int +set_timeout(struct cyclades_port * info, unsigned long value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + channel = info->line; + + base_addr[CyRTPRL] = value & 0xff; + base_addr[CyRTPRH] = (value >> 8) & 0xff; + return 0; +} + +static int +get_timeout(struct cyclades_port * info, unsigned long *value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + unsigned long tmp; + + channel = info->line; + + tmp = base_addr[CyRTPRL]; + cy_put_user(tmp,value); + return 0; +} + +static int +set_default_timeout(struct cyclades_port * info, unsigned long value) +{ + info->default_timeout = value & 0xff; + return 0; +} + +static int +get_default_timeout(struct cyclades_port * info, unsigned long *value) +{ + cy_put_user(info->default_timeout,value); + return 0; +} + +static int +cy_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + int ret_val = 0; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_ioctl ttyS%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */ +#endif + + switch (cmd) { + case CYGETMON: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(struct cyclades_monitor)); + if (error){ + ret_val = error; + break; + } + ret_val = get_mon_info(info, (struct cyclades_monitor *)arg); + break; + case CYGETTHRESH: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_threshold(info, (unsigned long *)arg); + break; + case CYSETTHRESH: + ret_val = set_threshold(info, (unsigned long)arg); + break; + case CYGETDEFTHRESH: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_default_threshold(info, (unsigned long *)arg); + break; + case CYSETDEFTHRESH: + ret_val = set_default_threshold(info, (unsigned long)arg); + break; + case CYGETTIMEOUT: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_timeout(info, (unsigned long *)arg); + break; + case CYSETTIMEOUT: + ret_val = set_timeout(info, (unsigned long)arg); + break; + case CYGETDEFTIMEOUT: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_default_timeout(info, (unsigned long *)arg); + break; + case CYSETDEFTIMEOUT: + ret_val = set_default_timeout(info, (unsigned long)arg); + break; + case TCSBRK: /* SVID version: non-zero arg --> no break */ + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; + tty_wait_until_sent(tty,0); + if (!arg) + send_break(info, HZ/4); /* 1/4 second */ + break; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; + tty_wait_until_sent(tty,0); + send_break(info, arg ? arg*(HZ/10) : HZ/4); + break; + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + ret_val = set_modem_info(info, cmd, (unsigned int *) arg); + break; + +/* The following commands are incompletely implemented!!! */ + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned int *)); + if (error){ + ret_val = error; + break; + } + cy_put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + break; + case TIOCSSOFTCAR: + error = verify_area(VERIFY_READ, (void *) arg + ,sizeof(unsigned long *)); + if (error){ + ret_val = error; + break; + } + + arg = cy_get_user((unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + break; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned int *)); + if (error){ + ret_val = error; + break; + } + ret_val = get_modem_info(info, (unsigned int *) arg); + break; + case TIOCGSERIAL: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(struct serial_struct)); + if (error){ + ret_val = error; + break; + } + ret_val = get_serial_info(info, + (struct serial_struct *) arg); + break; + case TIOCSSERIAL: + ret_val = set_serial_info(info, + (struct serial_struct *) arg); + break; + default: + ret_val = -ENOIOCTLCMD; + } + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_ioctl done\n"); +#endif + + return ret_val; +} /* cy_ioctl */ + + + + +static void +cy_set_termios(struct tty_struct *tty, struct termios * old_termios) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_set_termios ttyS%d\n", info->line); +#endif + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + config_setup(info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->stopped = 0; + cy_start(tty); + } +#ifdef tytso_patch_94Nov25_1726 + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif + + return; +} /* cy_set_termios */ + + +static void +cy_close(struct tty_struct * tty, struct file * filp) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + +/* CP('C'); */ +#ifdef SERIAL_DEBUG_OTHER + printk("cy_close ttyS%d\n", info->line); +#endif + + if (!info + || serial_paranoia_check(info, tty->device, "cy_close")){ + return; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cy_close ttyS%d, count = %d\n", info->line, info->count); +#endif + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("cy_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count - 1); +#endif + if (--info->count < 0) { + printk("cy_close: bad serial port count for ttys%d: %d\n", + info->line, info->count); +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->count = 0; + } + if (info->count) + return; + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + if (info->flags & ASYNC_INITIALIZED) + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + info->event = 0; + info->tty = 0; + if (tty->ldisc.num != ldiscs[N_TTY].num) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + tty->ldisc = ldiscs[N_TTY]; + tty->termios->c_line = N_TTY; + if (tty->ldisc.open) + (tty->ldisc.open)(tty); + } + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_close done\n"); +#endif + + return; +} /* cy_close */ + +/* + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void +cy_hangup(struct tty_struct *tty) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_hangup ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_hangup")) + return; + + shutdown(info); +#if 0 + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->tty = 0; +#endif + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ + + + +/* + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ + */ + +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct cyclades_port *info) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int channel; + int retval; + volatile u_char *base_addr = (u_char *)BASE_ADDR; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + if (info->flags & ASYNC_HUP_NOTIFY){ + return -EAGAIN; + }else{ + return -ERESTARTSYS; + } + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)){ + return -EBUSY; + } + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, then make the check up front + * and then exit. + */ + if (filp->f_flags & O_NONBLOCK) { + if (info->flags & ASYNC_CALLOUT_ACTIVE){ + return -EBUSY; + } + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * cy_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttyS%d, count = %d\n", + info->line, info->count);/**/ +#endif + info->count--; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count); +#endif + info->blocked_open++; + + channel = info->line; + + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; +/* CP('S');CP('4'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + } + restore_flags(flags); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */ + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (base_addr[CyMSVR1] & CyDCD))) { + restore_flags(flags); + break; + } + restore_flags(flags); + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttyS%d, count = %d\n", + info->line, info->count);/**/ +#endif + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)){ + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count); +#endif + } + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttyS%d, count = %d\n", + info->line, info->count);/**/ +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} /* block_til_ready */ + +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +int +cy_open(struct tty_struct *tty, struct file * filp) +{ + struct cyclades_port *info; + int retval, line; + +/* CP('O'); */ + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (NR_PORTS <= line)){ + return -ENODEV; + } + info = &cy_port[line]; + if (info->line < 0){ + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OTHER + printk("cy_open ttyS%d\n", info->line); /* */ +#endif + if (serial_paranoia_check(info, tty->device, "cy_open")){ + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open ttyS%d, count = %d\n", info->line, info->count);/**/ +#endif + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count); +#endif + tty->driver_data = info; + info->tty = tty; + + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!tmp_buf){ + return -ENOMEM; + } + } + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + } + /* + * Start up serial port + */ + retval = startup(info); + if (retval){ + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open done\n");/**/ +#endif + return 0; +} /* cy_open */ + + + +/* + * --------------------------------------------------------------------- + * serial167_init() and friends + * + * serial167_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static void +show_version(void) +{ + printk("MVME166/167 cd2401 driver\n"); +} /* show_version */ + +/* initialize chips on card -- return number of valid + chips (which is number of ports/4) */ + +/* + * This initialises the hardware to a reasonable state. It should + * probe the chip first so as to copy 166-Bug setup as a default for + * port 0. It initialises CMR to CyASYNC; that is never done again, so + * as to limit the number of CyINIT_CHAN commands in normal running. + * + * ... I wonder what I should do if this fails ... + */ + +void +mvme167_serial_console_setup(int cflag) +{ + volatile unsigned char* base_addr = (u_char *)BASE_ADDR; + int ch; + u_char spd; + u_char rcor, rbpr, badspeed = 0; + unsigned long flags; + + save_flags(flags); cli(); + + /* + * First probe channel zero of the chip, to see what speed has + * been selected. + */ + + base_addr[CyCAR] = 0; + + rcor = base_addr[CyRCOR] << 5; + rbpr = base_addr[CyRBPR]; + + for (spd = 0; spd < sizeof(baud_bpr); spd++) + if (rbpr == baud_bpr[spd] && rcor == baud_co[spd]) + break; + if (spd >= sizeof(baud_bpr)) { + spd = 14; /* 19200 */ + badspeed = 1; /* Failed to identify speed */ + } + initial_console_speed = spd; + + /* OK, we have chosen a speed, now reset and reinitialise */ + + my_udelay(20000L); /* Allow time for any active o/p to complete */ + if(base_addr[CyCCR] != 0x00){ + /* printk(" chip is never idle (CCR != 0)\n"); */ + return; + } + + base_addr[CyCCR] = CyCHIP_RESET; /* Reset the chip */ + my_udelay(1000L); + + if(base_addr[CyGFRCR] == 0x00){ + /* printk(" chip is not responding (GFRCR stayed 0)\n"); */ + return; + } + + /* + * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms + * tick + */ + + base_addr[CyTPR] = 10; + + base_addr[CyPILR1] = 0x01; /* Interrupt level for modem change */ + base_addr[CyPILR2] = 0x02; /* Interrupt level for tx ints */ + base_addr[CyPILR3] = 0x03; /* Interrupt level for rx ints */ + + /* + * Attempt to set up all channels to something reasonable, and + * bang out a INIT_CHAN command. We should then be able to limit + * the ammount of fiddling we have to do in normal running. + */ + + for (ch = 3; ch >= 0 ; ch--) { + base_addr[CyCAR] = (u_char)ch; + base_addr[CyIER] = 0; + base_addr[CyCMR] = CyASYNC; + base_addr[CyLICR] = (u_char)ch << 2; + base_addr[CyLIVR] = 0x5c; + base_addr[CyTCOR] = baud_co[spd]; + base_addr[CyTBPR] = baud_bpr[spd]; + base_addr[CyRCOR] = baud_co[spd] >> 5; + base_addr[CyRBPR] = baud_bpr[spd]; + base_addr[CySCHR1] = 'Q' & 0x1f; + base_addr[CySCHR2] = 'X' & 0x1f; + base_addr[CySCRL] = 0; + base_addr[CySCRH] = 0; + base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; + base_addr[CyCOR2] = 0; + base_addr[CyCOR3] = Cy_1_STOP; + base_addr[CyCOR4] = baud_cor4[spd]; + base_addr[CyCOR5] = 0; + base_addr[CyCOR6] = 0; + base_addr[CyCOR7] = 0; + base_addr[CyRTPRL] = 2; + base_addr[CyRTPRH] = 0; + base_addr[CyMSVR1] = 0; + base_addr[CyMSVR2] = 0; + write_cy_cmd(base_addr,CyINIT_CHAN|CyDIS_RCVR|CyDIS_XMTR); + } + + /* + * Now do specials for channel zero.... + */ + + base_addr[CyMSVR1] = CyRTS; + base_addr[CyMSVR2] = CyDTR; + base_addr[CyIER] = CyRxData; + write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR); + + restore_flags(flags); + + my_udelay(20000L); /* Let it all settle down */ + + printk("CD2401 initialised, chip is rev 0x%02x\n", base_addr[CyGFRCR]); + if (badspeed) + printk(" WARNING: Failed to identify line speed, rcor=%02x,rbpr=%02x\n", + rcor >> 5, rbpr); +} /* serial_console_init */ + +/* The serial driver boot-time initialization code! + Hardware I/O ports are mapped to character special devices on a + first found, first allocated manner. That is, this code searches + for Cyclom cards in the system. As each is found, it is probed + to discover how many chips (and thus how many ports) are present. + These ports are mapped to the tty ports 64 and upward in monotonic + fashion. If an 8-port card is replaced with a 16-port card, the + port mapping on a following card will shift. + + This approach is different from what is used in the other serial + device driver because the Cyclom is more properly a multiplexer, + not just an aggregation of serial ports on one card. + + If there are more cards with more ports than have been statically + allocated above, a warning is printed and the extra ports are ignored. + */ +int +serial167_init(void) +{ + struct cyclades_port *info; + int good_ports = 0; + int port_num = 0; + int index; + int DefSpeed; +#ifdef notyet + struct sigaction sa; +#endif + + if (!(mvme16x_config &MVME16x_CONFIG_GOT_CD2401)) + return 0; + +#if 0 +scrn[1] = '\0'; +#endif + + show_version(); + + /* Has "console=0,9600n8" been used in bootinfo to change speed? */ + if (serial_console_cflag) + DefSpeed = serial_console_cflag & 0017; + else { + DefSpeed = initial_console_speed; + serial_console_info = &cy_port[0]; + serial_console_cflag = DefSpeed | CS8; +#if 0 + serial_console = 64; /*callout_driver.minor_start*/ +#endif + } + + /* Initialize the tty_driver structure */ + + memset(&cy_serial_driver, 0, sizeof(struct tty_driver)); + cy_serial_driver.magic = TTY_DRIVER_MAGIC; + cy_serial_driver.name = "ttyS"; + cy_serial_driver.major = TTY_MAJOR; + cy_serial_driver.minor_start = 64; + cy_serial_driver.num = NR_PORTS; + cy_serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + cy_serial_driver.subtype = SERIAL_TYPE_NORMAL; + cy_serial_driver.init_termios = tty_std_termios; + cy_serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + cy_serial_driver.flags = TTY_DRIVER_REAL_RAW; + cy_serial_driver.refcount = &serial_refcount; + cy_serial_driver.table = serial_table; + cy_serial_driver.termios = serial_termios; + cy_serial_driver.termios_locked = serial_termios_locked; + cy_serial_driver.open = cy_open; + cy_serial_driver.close = cy_close; + cy_serial_driver.write = cy_write; + cy_serial_driver.put_char = cy_put_char; + cy_serial_driver.flush_chars = cy_flush_chars; + cy_serial_driver.write_room = cy_write_room; + cy_serial_driver.chars_in_buffer = cy_chars_in_buffer; + cy_serial_driver.flush_buffer = cy_flush_buffer; + cy_serial_driver.ioctl = cy_ioctl; + cy_serial_driver.throttle = cy_throttle; + cy_serial_driver.unthrottle = cy_unthrottle; + cy_serial_driver.set_termios = cy_set_termios; + cy_serial_driver.stop = cy_stop; + cy_serial_driver.start = cy_start; + cy_serial_driver.hangup = cy_hangup; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + cy_callout_driver = cy_serial_driver; + cy_callout_driver.name = "cua"; + cy_callout_driver.major = TTYAUX_MAJOR; + cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if (tty_register_driver(&cy_serial_driver)) + panic("Couldn't register Cyclom serial driver\n"); + if (tty_register_driver(&cy_callout_driver)) + panic("Couldn't register Cyclom callout driver\n"); + + init_bh(CYCLADES_BH, do_cyclades_bh); + + port_num = 0; + info = cy_port; + for (index = 0; index < 1; index++) { + + good_ports = 4; + + if(port_num < NR_PORTS){ + while( good_ports-- && port_num < NR_PORTS){ + /*** initialize port ***/ + info->magic = CYCLADES_MAGIC; + info->type = PORT_CIRRUS; + info->card = index; + info->line = port_num; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = 12; + info->cor1 = CyPARITY_NONE|Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = Cy_1_STOP; + info->cor4 = 0x08; /* _very_ small receive threshold */ + info->cor5 = 0; + info->cor6 = 0; + info->cor7 = 0; + info->tbpr = baud_bpr[DefSpeed]; /* Tx BPR */ + info->tco = baud_co[DefSpeed]; /* Tx CO */ + info->rbpr = baud_bpr[DefSpeed]; /* Rx BPR */ + info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */ + info->close_delay = 0; + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios =cy_callout_driver.init_termios; + info->normal_termios = cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + /* info->session */ + /* info->pgrp */ +/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/ + info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK + | CyPARITY| CyFRAME| CyOVERRUN; + /* info->timeout */ + + printk("ttyS%1d ", info->line); + port_num++;info++; + if(!(port_num & 7)){ + printk("\n "); + } + } + } + printk("\n"); + } + while( port_num < NR_PORTS){ + info->line = -1; + port_num++;info++; + } +#ifdef CONFIG_REMOTE_DEBUG + debug_setup(); +#endif + if (request_irq (MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0, + "cd2401_errors", cd2401_rxerr_interrupt) || + request_irq (MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0, + "cd2401_modem", cd2401_modem_interrupt) || + request_irq (MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0, + "cd2401_txints", cd2401_tx_interrupt) || + request_irq (MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0, + "cd2401_rxints", cd2401_rx_interrupt)) + { + panic ("Couldn't get serial IRQs"); + } + + /* Now we have registered the interrupt handlers, allow the interrupts */ + + pcc2chip[PccSCCMICR] = 0x15; /* Serial ints are level 5 */ + pcc2chip[PccSCCTICR] = 0x15; + pcc2chip[PccSCCRICR] = 0x15; + + pcc2chip[PccIMLR] = 3; /* Allow PCC2 ints above 3!? */ + + return 0; +} /* serial167_init */ + + +#ifdef CYCLOM_SHOW_STATUS +static void +show_status(int line_num) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + struct cyclades_port * info; + unsigned long flags; + + info = &cy_port[line_num]; + channel = info->line; + printk(" channel %d\n", channel);/**/ + + printk(" cy_port\n"); + printk(" card line flags = %d %d %x\n", + info->card, info->line, info->flags); + printk(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", + (long)info->tty, info->read_status_mask, + info->timeout, info->xmit_fifo_size); + printk(" cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n", + info->cor1, info->cor2, info->cor3, info->cor4, info->cor5, + info->cor6, info->cor7); + printk(" tbpr,tco,rbpr,rco = %d %d %d %d\n", + info->tbpr, info->tco, info->rbpr, info->rco); + printk(" close_delay event count = %d %d %d\n", + info->close_delay, info->event, info->count); + printk(" x_char blocked_open = %x %x\n", + info->x_char, info->blocked_open); + printk(" session pgrp open_wait = %lx %lx %lx\n", + info->session, info->pgrp, (long)info->open_wait); + + + save_flags(flags); cli(); + +/* Global Registers */ + + printk(" CyGFRCR %x\n", base_addr[CyGFRCR]); + printk(" CyCAR %x\n", base_addr[CyCAR]); + printk(" CyRISR %x\n", base_addr[CyRISR]); + printk(" CyTISR %x\n", base_addr[CyTISR]); + printk(" CyMISR %x\n", base_addr[CyMISR]); + printk(" CyRIR %x\n", base_addr[CyRIR]); + printk(" CyTIR %x\n", base_addr[CyTIR]); + printk(" CyMIR %x\n", base_addr[CyMIR]); + printk(" CyTPR %x\n", base_addr[CyTPR]); + + base_addr[CyCAR] = (u_char)channel; + +/* Virtual Registers */ + +#if 0 + printk(" CyRIVR %x\n", base_addr[CyRIVR]); + printk(" CyTIVR %x\n", base_addr[CyTIVR]); + printk(" CyMIVR %x\n", base_addr[CyMIVR]); + printk(" CyMISR %x\n", base_addr[CyMISR]); +#endif + +/* Channel Registers */ + + printk(" CyCCR %x\n", base_addr[CyCCR]); + printk(" CyIER %x\n", base_addr[CyIER]); + printk(" CyCOR1 %x\n", base_addr[CyCOR1]); + printk(" CyCOR2 %x\n", base_addr[CyCOR2]); + printk(" CyCOR3 %x\n", base_addr[CyCOR3]); + printk(" CyCOR4 %x\n", base_addr[CyCOR4]); + printk(" CyCOR5 %x\n", base_addr[CyCOR5]); +#if 0 + printk(" CyCCSR %x\n", base_addr[CyCCSR]); + printk(" CyRDCR %x\n", base_addr[CyRDCR]); +#endif + printk(" CySCHR1 %x\n", base_addr[CySCHR1]); + printk(" CySCHR2 %x\n", base_addr[CySCHR2]); +#if 0 + printk(" CySCHR3 %x\n", base_addr[CySCHR3]); + printk(" CySCHR4 %x\n", base_addr[CySCHR4]); + printk(" CySCRL %x\n", base_addr[CySCRL]); + printk(" CySCRH %x\n", base_addr[CySCRH]); + printk(" CyLNC %x\n", base_addr[CyLNC]); + printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]); + printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]); +#endif + printk(" CyRTPRL %x\n", base_addr[CyRTPRL]); + printk(" CyRTPRH %x\n", base_addr[CyRTPRH]); + printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]); + printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]); + printk(" CyRBPR %x\n", base_addr[CyRBPR]); + printk(" CyRCOR %x\n", base_addr[CyRCOR]); + printk(" CyTBPR %x\n", base_addr[CyTBPR]); + printk(" CyTCOR %x\n", base_addr[CyTCOR]); + + restore_flags(flags); +} /* show_status */ +#endif + + +#if 0 +/* Dummy routine in mvme16x/config.c for now */ + +/* Serial console setup. Called from linux/init/main.c */ + +void console_setup(char *str, int *ints) +{ + char *s; + int baud, bits, parity; + int cflag = 0; + + /* Sanity check. */ + if (ints[0] > 3 || ints[1] > 3) return; + + /* Get baud, bits and parity */ + baud = 2400; + bits = 8; + parity = 'n'; + if (ints[2]) baud = ints[2]; + if ((s = strchr(str, ','))) { + do { + s++; + } while(*s >= '0' && *s <= '9'); + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* Now construct a cflag setting. */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 9600: + cflag |= B9600; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 2400: + default: + cflag |= B2400; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + + serial_console_info = &cy_port[ints[1]]; + serial_console_cflag = cflag; + serial_console = ints[1] + 64; /*callout_driver.minor_start*/ +} +#endif + +/* + * The following is probably out of date for 2.1.x serial console stuff. + * + * The console is registered early on from arch/m68k/kernel/setup.c, and + * it therefore relies on the chip being setup correctly by 166-Bug. This + * seems reasonable, as the serial port has been used to invoke the system + * boot. It also means that this function must not rely on any data + * initialisation performed by serial167_init() etc. + * + * Of course, once the console has been registered, we had better ensure + * that serial167_init() doesn't leave the chip non-functional. + */ + +void serial167_write(struct console *co, const char *str, unsigned count) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + u_char do_lf = 0; + int i = 0; + + save_flags(flags); cli(); + + /* Ensure transmitter is enabled! */ + + port = 0; + base_addr[CyCAR] = (u_char)port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_XMTR; + + ier = base_addr[CyIER]; + base_addr[CyIER] = CyTxMpty; + + while (1) { + if (pcc2chip[PccSCCTICR] & 0x20) + { + /* We have a Tx int. Acknowledge it */ + sink = pcc2chip[PccTPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + if (i == count) { + /* Last char of string is now output */ + base_addr[CyTEOIR] = CyNOTRANS; + break; + } + if (do_lf) { + base_addr[CyTDR] = '\n'; + str++; + i++; + do_lf = 0; + } + else if (*str == '\n') { + base_addr[CyTDR] = '\r'; + do_lf = 1; + } + else { + base_addr[CyTDR] = *str++; + i++; + } + base_addr[CyTEOIR] = 0; + } + else + base_addr[CyTEOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); +} + +#ifdef CONFIG_REMOTE_DEBUG +void putDebugChar (int c) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + + save_flags(flags); cli(); + + /* Ensure transmitter is enabled! */ + + port = DEBUG_PORT; + base_addr[CyCAR] = (u_char)port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_XMTR; + + ier = base_addr[CyIER]; + base_addr[CyIER] = CyTxMpty; + + while (1) { + if (pcc2chip[PccSCCTICR] & 0x20) + { + /* We have a Tx int. Acknowledge it */ + sink = pcc2chip[PccTPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + base_addr[CyTDR] = c; + base_addr[CyTEOIR] = 0; + break; + } + else + base_addr[CyTEOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); +} + +int getDebugChar() +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + int i, c; + + i = debugiq.out; + if (i != debugiq.in) { + c = debugiq.buf[i]; + if (++i == DEBUG_LEN) + i = 0; + debugiq.out = i; + return c; + } + /* OK, nothing in queue, wait in poll loop */ + + save_flags(flags); cli(); + + /* Ensure receiver is enabled! */ + + port = DEBUG_PORT; + base_addr[CyCAR] = (u_char)port; +#if 0 + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_RCVR; +#endif + ier = base_addr[CyIER]; + base_addr[CyIER] = CyRxData; + + while (1) { + if (pcc2chip[PccSCCRICR] & 0x20) + { + /* We have a Rx int. Acknowledge it */ + sink = pcc2chip[PccRPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + int cnt = base_addr[CyRFOC]; + while (cnt-- > 0) + { + c = base_addr[CyRDR]; + if (c == 0) + printk ("!! debug char is null (cnt=%d) !!", cnt); + else + queueDebugChar (c); + } + base_addr[CyREOIR] = 0; + i = debugiq.out; + if (i == debugiq.in) + panic ("Debug input queue empty!"); + c = debugiq.buf[i]; + if (++i == DEBUG_LEN) + i = 0; + debugiq.out = i; + break; + } + else + base_addr[CyREOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); + + return (c); +} + +void queueDebugChar (int c) +{ + int i; + + i = debugiq.in; + debugiq.buf[i] = c; + if (++i == DEBUG_LEN) + i = 0; + if (i != debugiq.out) + debugiq.in = i; +} + +static void +debug_setup() +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int i, cflag; + + cflag = B19200; + + save_flags(flags); cli(); + + for (i = 0; i < 4; i++) + { + base_addr[CyCAR] = i; + base_addr[CyLICR] = i << 2; + } + + debugiq.in = debugiq.out = 0; + + base_addr[CyCAR] = DEBUG_PORT; + + /* baud rate */ + i = cflag & CBAUD; + + base_addr[CyIER] = 0; + + base_addr[CyCMR] = CyASYNC; + base_addr[CyLICR] = DEBUG_PORT << 2; + base_addr[CyLIVR] = 0x5c; + + /* tx and rx baud rate */ + + base_addr[CyTCOR] = baud_co[i]; + base_addr[CyTBPR] = baud_bpr[i]; + base_addr[CyRCOR] = baud_co[i] >> 5; + base_addr[CyRBPR] = baud_bpr[i]; + + /* set line characteristics according configuration */ + + base_addr[CySCHR1] = 0; + base_addr[CySCHR2] = 0; + base_addr[CySCRL] = 0; + base_addr[CySCRH] = 0; + base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; + base_addr[CyCOR2] = 0; + base_addr[CyCOR3] = Cy_1_STOP; + base_addr[CyCOR4] = baud_cor4[i]; + base_addr[CyCOR5] = 0; + base_addr[CyCOR6] = 0; + base_addr[CyCOR7] = 0; + + write_cy_cmd(base_addr,CyINIT_CHAN); + write_cy_cmd(base_addr,CyENB_RCVR); + + base_addr[CyCAR] = DEBUG_PORT; /* !!! Is this needed? */ + + base_addr[CyRTPRL] = 2; + base_addr[CyRTPRH] = 0; + + base_addr[CyMSVR1] = CyRTS; + base_addr[CyMSVR2] = CyDTR; + + base_addr[CyIER] = CyRxData; + + restore_flags(flags); + +} /* debug_setup */ + +#endif diff -u --recursive --new-file v2.1.124/linux/drivers/char/sysrq.c linux/drivers/char/sysrq.c --- v2.1.124/linux/drivers/char/sysrq.c Mon Sep 28 10:51:33 1998 +++ linux/drivers/char/sysrq.c Wed Oct 7 18:52:09 1998 @@ -85,7 +85,7 @@ #ifdef CONFIG_APM case 'o': /* O -- power off */ printk("Power off\n"); - apm_set_power_state(APM_STATE_OFF); + apm_power_off(); break; #endif case 's': /* S -- emergency sync */ diff -u --recursive --new-file v2.1.124/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.1.124/linux/drivers/char/tty_io.c Mon Oct 5 13:13:39 1998 +++ linux/drivers/char/tty_io.c Mon Oct 5 21:54:35 1998 @@ -390,7 +390,9 @@ { struct tty_struct *tty = (struct tty_struct *) data; struct file * filp; + struct file * cons_filp = NULL; struct task_struct *p; + int closecount = 0, n; if (!tty) return; @@ -407,10 +409,13 @@ if (!filp->f_dentry->d_inode) continue; if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV || - filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) + filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) { + cons_filp = filp; continue; + } if (filp->f_op != &tty_fops) continue; + closecount++; tty_fasync(-1, filp, 0); filp->f_op = &hung_up_tty_fops; } @@ -470,7 +475,17 @@ tty->session = 0; tty->pgrp = -1; tty->ctrl_status = 0; - if (tty->driver.hangup) + /* + * If one of the devices matches a console pointer, we + * cannot just call hangup() because that will cause + * tty->count and state->count to go out of sync. + * So we just call close() the right number of times. + */ + if (cons_filp) { + if (tty->driver.close) + for (n = 0; n < closecount; n++) + tty->driver.close(tty, cons_filp); + } else if (tty->driver.hangup) (tty->driver.hangup)(tty); unlock_kernel(); } @@ -1243,6 +1258,7 @@ if (!c) return -ENODEV; device = c->device(c); + filp->f_flags |= O_NONBLOCK; /* Don't let /dev/console block */ noctty = 1; } #ifdef CONFIG_UNIX98_PTYS @@ -2103,6 +2119,9 @@ #endif #ifdef CONFIG_ROCKETPORT rp_init(); +#endif +#ifdef CONFIG_MVME16x + serial167_init(); #endif #ifdef CONFIG_CYCLADES cy_init(); diff -u --recursive --new-file v2.1.124/linux/drivers/char/videodev.c linux/drivers/char/videodev.c --- v2.1.124/linux/drivers/char/videodev.c Wed Aug 26 11:37:37 1998 +++ linux/drivers/char/videodev.c Tue Oct 6 09:39:49 1998 @@ -333,7 +333,7 @@ { struct video_init *vfli = video_init_list; - printk(KERN_INFO "Linux video capture interface: v0.01 ALPHA\n"); + printk(KERN_INFO "Linux video capture interface: v1.00\n"); if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops)) { printk("video_dev: unable to get major %d\n", VIDEO_MAJOR); diff -u --recursive --new-file v2.1.124/linux/drivers/net/3c523.c linux/drivers/net/3c523.c --- v2.1.124/linux/drivers/net/3c523.c Thu Sep 17 17:53:36 1998 +++ linux/drivers/net/3c523.c Wed Oct 7 15:51:45 1998 @@ -1255,8 +1255,9 @@ /*************************************************************************/ #ifdef MODULE +static char devicename[9] = { 0, }; static struct device dev_elmc = { - " " /*"3c523"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, elmc_probe }; + devicename /*"3c523"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, elmc_probe }; static int irq=0; static int io=0; diff -u --recursive --new-file v2.1.124/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.1.124/linux/drivers/net/Config.in Mon Oct 5 13:13:39 1998 +++ linux/drivers/net/Config.in Wed Oct 7 15:51:45 1998 @@ -121,9 +121,11 @@ tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 + tristate 'Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 fi tristate 'PCI NE2000 support' CONFIG_NE2K_PCI tristate 'TI ThunderLAN support' CONFIG_TLAN + tristate 'VIA Rhine support' CONFIG_VIA_RHINE if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 tristate 'SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100 @@ -141,6 +143,17 @@ bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX +fi + +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI + if [ "$CONFIG_HIPPI" = "y" ]; then + bool 'CERN HIPPI PCI adapter support' CONFIG_CERN_HIPPI + bool 'Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER + if [ "$CONFIG_ROADRUNNER" != "n" ]; then + bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS + fi + fi fi tristate 'Frame relay DLCI support' CONFIG_DLCI diff -u --recursive --new-file v2.1.124/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.1.124/linux/drivers/net/Makefile Mon Oct 5 13:13:39 1998 +++ linux/drivers/net/Makefile Wed Oct 7 15:51:45 1998 @@ -279,6 +279,16 @@ endif endif +ifeq ($(CONFIG_NE3210),y) +L_OBJS += ne3210.o +CONFIG_8390_BUILTIN = y +else + ifeq ($(CONFIG_NE3210),m) + CONFIG_8390_MODULE = y + M_OBJS += ne3210.o + endif +endif + ifeq ($(CONFIG_PLIP),y) L_OBJS += plip.o else @@ -526,6 +536,14 @@ endif endif +ifeq ($(CONFIG_VIA_RHINE),y) +L_OBJS += via-rhine.o +else + ifeq ($(CONFIG_VIA_RHINE),m) + M_OBJS += via-rhine.o + endif +endif + ifeq ($(CONFIG_ZNET),y) L_OBJS += znet.o endif @@ -963,6 +981,25 @@ else ifeq ($(CONFIG_X25_ASY),m) M_OBJS += x25_asy.o + endif +endif + +# +# HIPPI adapters +# +ifeq ($(CONFIG_CERN_HIPPI),y) +L_OBJS += cern_hippi.o +else + ifeq ($(CONFIG_CERN_HIPPI),m) + M_OBJS += cern_hippi.o + endif +endif + +ifeq ($(CONFIG_ROADRUNNER),y) +L_OBJS += rrunner.o +else + ifeq ($(CONFIG_ROADRUNNER),m) + M_OBJS += rrunner.o endif endif diff -u --recursive --new-file v2.1.124/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.1.124/linux/drivers/net/Space.c Mon Oct 5 13:13:39 1998 +++ linux/drivers/net/Space.c Wed Oct 7 15:51:45 1998 @@ -72,6 +72,7 @@ extern int ac3200_probe(struct device *); extern int es_probe(struct device *); extern int lne390_probe(struct device *); +extern int ne3210_probe(struct device *); extern int e2100_probe(struct device *); extern int ni5010_probe(struct device *); extern int ni52_probe(struct device *); @@ -122,6 +123,7 @@ /* HIPPI boards */ extern int cern_hippi_probe(struct device *); +extern int rr_hippi_probe(struct device *); struct devprobe { @@ -215,6 +217,9 @@ #ifdef CONFIG_LNE390 {lne390_probe, 0}, #endif +#ifdef CONFIG_NE3210 + {ne3210_probe, 0}, +#endif {NULL, 0}, }; @@ -515,6 +520,9 @@ #ifdef CONFIG_CERN_HIPPI && cern_hippi_probe(dev) #endif +#ifdef CONFIG_ROADRUNNER + && rr_hippi_probe(dev) +#endif && 1 ) { return 1; /* -ENODEV or -EAGAIN would be more accurate. */ } @@ -735,11 +743,11 @@ #ifdef CONFIG_HIPPI static struct device hip3_dev = - {"hip3", 0, 0, 0, 0, -1, 0, 0, 0, 0, NEXT_DEV, hippi_probe}; + {"hip3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, hippi_probe}; static struct device hip2_dev = - {"hip2", 0, 0, 0, 0, -1, 0, 0, 0, 0, &hip3_dev, hippi_probe}; + {"hip2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &hip3_dev, hippi_probe}; static struct device hip1_dev = - {"hip1", 0, 0, 0, 0, -1, 0, 0, 0, 0, &hip2_dev, hippi_probe}; + {"hip1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &hip2_dev, hippi_probe}; static struct device hip0_dev = {"hip0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &hip1_dev, hippi_probe}; diff -u --recursive --new-file v2.1.124/linux/drivers/net/ac3200.c linux/drivers/net/ac3200.c --- v2.1.124/linux/drivers/net/ac3200.c Tue Jun 9 11:57:29 1998 +++ linux/drivers/net/ac3200.c Wed Oct 7 15:51:45 1998 @@ -205,10 +205,10 @@ * the card mem within the region covered by `normal' RAM !!! */ if (dev->mem_start > 1024*1024) { /* phys addr > 1MB */ - if (dev->mem_start < (unsigned long)high_memory) { + if (dev->mem_start < virt_to_bus(high_memory)) { printk(KERN_CRIT "ac3200.c: Card RAM overlaps with normal memory!!!\n"); printk(KERN_CRIT "ac3200.c: Use EISA SCU to set card memory below 1MB,\n"); - printk(KERN_CRIT "ac3200.c: or to an address above %p.\n", high_memory); + printk(KERN_CRIT "ac3200.c: or to an address above 0x%lx.\n", virt_to_bus(high_memory)); printk(KERN_CRIT "ac3200.c: Driver NOT installed.\n"); free_irq(dev->irq, dev); kfree(dev->priv); @@ -225,6 +225,7 @@ dev->priv = NULL; return EAGAIN; } + ei_status.reg0 = 1; /* Use as remap flag */ printk("ac3200.c: remapped %dkB card memory to virtual address %#lx\n", AC_STOP_PG/4, dev->mem_start); } @@ -404,6 +405,8 @@ /* Someday free_irq may be in ac_close_card() */ free_irq(dev->irq, dev); release_region(dev->base_addr, AC_IO_EXTENT); + if (ei_status.reg0) + iounmap((void *)dev->mem_start); dev->priv = NULL; unregister_netdev(dev); kfree(priv); diff -u --recursive --new-file v2.1.124/linux/drivers/net/atari_pamsnet.c linux/drivers/net/atari_pamsnet.c --- v2.1.124/linux/drivers/net/atari_pamsnet.c Thu Feb 12 20:56:07 1998 +++ linux/drivers/net/atari_pamsnet.c Wed Oct 7 15:51:45 1998 @@ -864,9 +864,10 @@ #ifdef MODULE +static char devicename[9] = { 0, }; static struct device pam_dev = { - " ", /* filled in by register_netdev() */ + devicename, /* filled in by register_netdev() */ 0, 0, 0, 0, /* memory */ 0, 0, /* base, irq */ 0, 0, 0, NULL, pamsnet_probe, diff -u --recursive --new-file v2.1.124/linux/drivers/net/cops.c linux/drivers/net/cops.c --- v2.1.124/linux/drivers/net/cops.c Wed Jun 24 22:54:06 1998 +++ linux/drivers/net/cops.c Wed Oct 7 15:51:45 1998 @@ -140,7 +140,7 @@ * N.B. * * The Daystar Digital LT200 boards do not support interrupt-driven - * IO. You must specify 'io=0xff' as a module parameter to invoke + * IO. You must specify 'irq=0xff' as a module parameter to invoke * polled mode. I also believe that the port probing logic is quite * dangerous at best and certainly hopeless for a polled card. Best to * specify both. - Steve H. diff -u --recursive --new-file v2.1.124/linux/drivers/net/daynaport.c linux/drivers/net/daynaport.c --- v2.1.124/linux/drivers/net/daynaport.c Thu Sep 17 17:53:36 1998 +++ linux/drivers/net/daynaport.c Mon Oct 5 13:39:20 1998 @@ -50,14 +50,14 @@ static void dayna_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void dayna_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void sane_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void sane_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void slow_sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); diff -u --recursive --new-file v2.1.124/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c --- v2.1.124/linux/drivers/net/de4x5.c Sat Sep 5 16:46:40 1998 +++ linux/drivers/net/de4x5.c Mon Oct 5 13:02:58 1998 @@ -409,11 +409,17 @@ Fix lastPCI to correctly work with compiled in kernels and modules from bug report by et al. + 0.542 15-Sep-98 Fix dc2114x_autoconf() to stop multiple messages + when media is unconnected. + Change dev->interrupt to lp->interrupt to ensure + alignment for Alpha's and avoid their unaligned + access traps. This flag is merely for log messages: + should do something more definitive though... ========================================================================= */ -static const char *version = "de4x5.c:V0.541 1998/8/24 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.542 1998/9/15 davies@maniac.ultranet.com\n"; #include #include @@ -744,6 +750,7 @@ struct de4x5_private { char adapter_name[80]; /* Adapter name */ + u_long interrupt; /* Aligned ISR flag */ struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring */ struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring */ struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */ @@ -1362,7 +1369,7 @@ dev->tbusy = 0; dev->start = 1; - dev->interrupt = UNMASK_INTERRUPTS; + lp->interrupt = UNMASK_INTERRUPTS; dev->trans_start = jiffies; START_DE4X5; @@ -1506,12 +1513,12 @@ sti(); /* Test if cache is already locked - requeue skb if so */ - if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) + if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) return -1; /* Transmit descriptor ring full or stale skb */ if (dev->tbusy || lp->tx_skb[lp->tx_new]) { - if (dev->interrupt) { + if (lp->interrupt) { de4x5_putb_cache(dev, skb); /* Requeue the buffer */ } else { de4x5_put_cache(dev, skb); @@ -1521,7 +1528,7 @@ } } else if (skb->len > 0) { /* If we already have stuff queued locally, use that first */ - if (lp->cache.skb && !dev->interrupt) { + if (lp->cache.skb && !lp->interrupt) { de4x5_put_cache(dev, skb); skb = de4x5_get_cache(dev); } @@ -1576,10 +1583,11 @@ lp = (struct de4x5_private *)dev->priv; iobase = dev->base_addr; - if (test_and_set_bit(MASK_INTERRUPTS, (void*) &dev->interrupt)) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - DISABLE_IRQs; /* Ensure non re-entrancy */ + + if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + synchronize_irq(); for (limit=0; limit<8; limit++) { @@ -1618,7 +1626,7 @@ lp->cache.lock = 0; } - dev->interrupt = UNMASK_INTERRUPTS; + lp->interrupt = UNMASK_INTERRUPTS; ENABLE_IRQs; return; @@ -1748,7 +1756,7 @@ if (TX_BUFFS_AVAIL && dev->tbusy) { /* Any resources available? */ dev->tbusy = 0; /* Clear TX busy flag */ - if (dev->interrupt) mark_bh(NET_BH); + if (lp->interrupt) mark_bh(NET_BH); } return 0; @@ -2351,7 +2359,7 @@ s32 imr; switch (lp->media) { - case INIT: + case INIT: DISABLE_IRQs; lp->tx_enable = NO; lp->timeout = -1; @@ -2369,36 +2377,36 @@ next_tick = dc21040_autoconf(dev); break; - case TP: + case TP: next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, TP_SUSPECT, test_tp); break; - case TP_SUSPECT: + case TP_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); break; - case BNC: - case AUI: - case BNC_AUI: + case BNC: + case AUI: + case BNC_AUI: next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, BNC_AUI_SUSPECT, ping_media); break; - case BNC_AUI_SUSPECT: + case BNC_AUI_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); break; - case EXT_SIA: + case EXT_SIA: next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, NC, EXT_SIA_SUSPECT, ping_media); break; - case EXT_SIA_SUSPECT: + case EXT_SIA_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); break; - case NC: + case NC: /* default to TP for all */ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); if (lp->media != lp->c_media) { @@ -2423,13 +2431,13 @@ int linkBad; switch (lp->local_state) { - case 0: + case 0: reset_init_sia(dev, csr13, csr14, csr15); lp->local_state++; next_tick = 500; break; - case 1: + case 1: if (!lp->tx_enable) { linkBad = fn(dev, timeout); if (linkBad < 0) { @@ -2462,7 +2470,7 @@ int linkBad; switch (lp->local_state) { - case 1: + case 1: if (lp->linkOK) { lp->media = prev_state; } else { @@ -2471,7 +2479,7 @@ } break; - case 2: + case 2: linkBad = fn(dev, timeout); if (linkBad < 0) { next_tick = linkBad & ~TIMER_CB; @@ -2505,7 +2513,7 @@ int next_tick = DE4X5_AUTOSENSE_MS; switch (lp->media) { - case INIT: + case INIT: DISABLE_IRQs; lp->tx_enable = NO; lp->timeout = -1; @@ -2525,7 +2533,7 @@ next_tick = dc21041_autoconf(dev); break; - case TP_NW: + case TP_NW: if (lp->timeout < 0) { omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ outl(omr | OMR_FDX, DE4X5_OMR); @@ -2545,7 +2553,7 @@ } break; - case ANS: + case ANS: if (!lp->tx_enable) { irqs = STS_LNP; irq_mask = IMR_LPM; @@ -2567,11 +2575,11 @@ } break; - case ANS_SUSPECT: + case ANS_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); break; - case TP: + case TP: if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ @@ -2601,11 +2609,11 @@ } break; - case TP_SUSPECT: + case TP_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); break; - case AUI: + case AUI: if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ @@ -2631,13 +2639,13 @@ } break; - case AUI_SUSPECT: + case AUI_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); break; - case BNC: + case BNC: switch (lp->local_state) { - case 0: + case 0: if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ outl(omr & ~OMR_FDX, DE4X5_OMR); @@ -2653,7 +2661,7 @@ } break; - case 1: + case 1: if (!lp->tx_enable) { if ((sts = ping_media(dev, 3000)) < 0) { next_tick = sts & ~TIMER_CB; @@ -2673,11 +2681,11 @@ } break; - case BNC_SUSPECT: + case BNC_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); break; - case NC: + case NC: omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ outl(omr | OMR_FDX, DE4X5_OMR); reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ @@ -3081,9 +3089,9 @@ } next_tick = dc2114x_autoconf(dev); } else if (((lp->media == _100Mb) && is_100_up(dev)) || - ((lp->media == _10Mb) && is_10_up(dev)) || - (lp->media == TP) || - (lp->media == BNC) || (lp->media == AUI)) { + (((lp->media == _10Mb) || (lp->media == TP) || + (lp->media == BNC) || (lp->media == AUI)) && + is_10_up(dev))) { next_tick = dc2114x_autoconf(dev); } else { lp->tcount++; @@ -4049,7 +4057,7 @@ if (data == dev.Sig[0]) { /* rare case.... */ j=1; } else { - j=0; + j=0; } } } @@ -4121,8 +4129,10 @@ srom_repair(dev, broken); #ifdef CONFIG_PMAC - /* If the address starts with 00 a0, we have to bit-reverse - each byte of the address. */ + /* + ** If the address starts with 00 a0, we have to bit-reverse + ** each byte of the address. + */ if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0xa0) { for (i = 0; i < ETH_ALEN; ++i) { int x = dev->dev_addr[i]; diff -u --recursive --new-file v2.1.124/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c --- v2.1.124/linux/drivers/net/eexpress.c Wed Jun 24 22:54:06 1998 +++ linux/drivers/net/eexpress.c Wed Oct 7 15:51:45 1998 @@ -8,6 +8,11 @@ * * Many modifications, and currently maintained, by * Philip Blundell + * Added the Compaq LTE Alan Cox + * + * Note - this driver is experimental still - it has problems on faster + * machines. Someone needs to sit down and go through it line by line with + * a databook... */ /* The EtherExpress 16 is a fairly simple card, based on a shared-memory @@ -326,7 +331,7 @@ if (sum==0xbaba && !eexp_hw_probe(dev,*port)) return 0; } - return ENODEV; + return -ENODEV; } /* @@ -931,11 +936,13 @@ hw_addr[1] = eexp_hw_readeeprom(ioaddr,3); hw_addr[2] = eexp_hw_readeeprom(ioaddr,4); - if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000)) + /* Standard Address or Compaq LTE Address */ + if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) || + (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00)))) { printk(" rejected: invalid address %04x%04x%04x\n", hw_addr[2],hw_addr[1],hw_addr[0]); - return ENODEV; + return -ENODEV; } /* Calculate the EEPROM checksum. Carry on anyway if it's bad, diff -u --recursive --new-file v2.1.124/linux/drivers/net/ethertap.c linux/drivers/net/ethertap.c --- v2.1.124/linux/drivers/net/ethertap.c Tue Mar 10 10:03:32 1998 +++ linux/drivers/net/ethertap.c Wed Oct 7 15:51:45 1998 @@ -7,7 +7,7 @@ * on it will attempt to ARP the user space and reply to ARPS from the * user space. * - * As this is an ethernet device you cau use it for appletalk, IPX etc + * As this is an ethernet device you can use it for appletalk, IPX etc * even for building bridging tunnels. */ diff -u --recursive --new-file v2.1.124/linux/drivers/net/hp100.c linux/drivers/net/hp100.c --- v2.1.124/linux/drivers/net/hp100.c Sun Jun 7 11:16:32 1998 +++ linux/drivers/net/hp100.c Wed Oct 7 15:54:44 1998 @@ -1144,7 +1144,7 @@ /* * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and - * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded + * 4 bytes for header). We will leave NUM_RXPDLS * 508 (rounded * to the next higher 1k boundary) bytes for the rx-pdl's * Note: For non-etr chips the transmit stop register must be * programmed on a 1k boundary, i.e. bits 9:0 must be zero. diff -u --recursive --new-file v2.1.124/linux/drivers/net/lne390.c linux/drivers/net/lne390.c --- v2.1.124/linux/drivers/net/lne390.c Tue Jun 9 11:57:30 1998 +++ linux/drivers/net/lne390.c Wed Oct 7 15:51:45 1998 @@ -194,7 +194,7 @@ } printk(" IRQ %d,", dev->irq); - if (request_irq(dev->irq, ei_interrupt, 0, "lne390", NULL)) { + if (request_irq(dev->irq, ei_interrupt, 0, "lne390", dev)) { printk (" unable to get IRQ %d.\n", dev->irq); kfree(dev->priv); dev->priv = NULL; @@ -223,10 +223,10 @@ the card mem within the region covered by `normal' RAM !!! */ if (dev->mem_start > 1024*1024) { /* phys addr > 1MB */ - if (dev->mem_start < (unsigned long)high_memory) { + if (dev->mem_start < virt_to_bus(high_memory)) { printk(KERN_CRIT "lne390.c: Card RAM overlaps with normal memory!!!\n"); printk(KERN_CRIT "lne390.c: Use EISA SCU to set card memory below 1MB,\n"); - printk(KERN_CRIT "lne390.c: or to an address above %p.\n", high_memory); + printk(KERN_CRIT "lne390.c: or to an address above 0x%lx.\n", virt_to_bus(high_memory)); printk(KERN_CRIT "lne390.c: Driver NOT installed.\n"); free_irq(dev->irq, dev); kfree(dev->priv); @@ -243,6 +243,7 @@ dev->priv = NULL; return EAGAIN; } + ei_status.reg0 = 1; /* Use as remap flag */ printk("lne390.c: remapped %dkB card memory to virtual address %#lx\n", LNE390_STOP_PG/4, dev->mem_start); } @@ -427,6 +428,8 @@ void *priv = dev->priv; free_irq(dev->irq, dev); release_region(dev->base_addr, LNE390_IO_EXTENT); + if (ei_status.reg0) + iounmap((void *)dev->mem_start); dev->priv = NULL; unregister_netdev(dev); kfree(priv); diff -u --recursive --new-file v2.1.124/linux/drivers/net/ne3210.c linux/drivers/net/ne3210.c --- v2.1.124/linux/drivers/net/ne3210.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/ne3210.c Wed Oct 7 15:51:45 1998 @@ -0,0 +1,432 @@ +/* + ne3210.c + + Linux driver for Novell NE3210 EISA Network Adapter + + Copyright (C) 1998, Paul Gortmaker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + Information and Code Sources: + + 1) Based upon my other EISA 8390 drivers (lne390, es3210, smc-ultra32) + 2) The existing myriad of other Linux 8390 drivers by Donald Becker. + 3) Info for getting IRQ and sh-mem gleaned from the EISA cfg file + + The NE3210 is an EISA shared memory NS8390 implementation. Shared + memory address > 1MB should work with this driver. + + Note that the .cfg file (3/11/93, v1.0) has AUI and BNC switched + around (or perhaps there are some defective/backwards cards ???) + + This driver WILL NOT WORK FOR THE NE3200 - it is completely different + and does not use an 8390 at all. + +*/ + +static const char *version = + "ne3210.c: Driver revision v0.03, 30/09/98\n"; + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "8390.h" + +int ne3210_probe(struct device *dev); +int ne3210_probe1(struct device *dev, int ioaddr); + +static int ne3210_open(struct device *dev); +static int ne3210_close(struct device *dev); + +static void ne3210_reset_8390(struct device *dev); + +static void ne3210_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); +static void ne3210_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); +static void ne3210_block_output(struct device *dev, int count, const unsigned char *buf, const int start_page); + +#define NE3210_START_PG 0x00 /* First page of TX buffer */ +#define NE3210_STOP_PG 0x80 /* Last page +1 of RX ring */ + +#define NE3210_ID_PORT 0xc80 /* Same for all EISA cards */ +#define NE3210_IO_EXTENT 0x20 +#define NE3210_SA_PROM 0x16 /* Start of e'net addr. */ +#define NE3210_RESET_PORT 0xc84 +#define NE3210_NIC_OFFSET 0x00 /* Hello, the 8390 is *here* */ + +#define NE3210_ADDR0 0x00 /* 3 byte vendor prefix */ +#define NE3210_ADDR1 0x00 +#define NE3210_ADDR2 0x1b + +#define NE3210_ID 0x0118cc3a /* 0x3acc = 1110 10110 01100 = nvl */ + +#define NE3210_CFG1 0xc84 /* NB: 0xc84 is also "reset" port. */ +#define NE3210_CFG2 0xc90 + +/* + * You can OR any of the following bits together and assign it + * to NE3210_DEBUG to get verbose driver info during operation. + * Currently only the probe one is implemented. + */ + +#define NE3210_D_PROBE 0x01 +#define NE3210_D_RX_PKT 0x02 +#define NE3210_D_TX_PKT 0x04 +#define NE3210_D_IRQ 0x08 + +#define NE3210_DEBUG 0x0 + +static unsigned char irq_map[] __initdata = {15, 12, 11, 10, 9, 7, 5, 3}; +static unsigned int shmem_map[] __initdata = {0xff0, 0xfe0, 0xfff0, 0xd8, 0xffe0, 0xffc0, 0xd0, 0x0}; + +/* + * Probe for the card. The best way is to read the EISA ID if it + * is known. Then we can check the prefix of the station address + * PROM for a match against the value assigned to Novell. + */ + +__initfunc(int ne3210_probe(struct device *dev)) +{ + unsigned short ioaddr = dev->base_addr; + + if (ioaddr > 0x1ff) /* Check a single specified location. */ + return ne3210_probe1(dev, ioaddr); + else if (ioaddr > 0) /* Don't probe at all. */ + return ENXIO; + + if (!EISA_bus) { +#if NE3210_DEBUG & NE3210_D_PROBE + printk("ne3210-debug: Not an EISA bus. Not probing high ports.\n"); +#endif + return ENXIO; + } + + /* EISA spec allows for up to 16 slots, but 8 is typical. */ + for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { + if (check_region(ioaddr , NE3210_IO_EXTENT)) + continue; + if (ne3210_probe1(dev, ioaddr) == 0) + return 0; + } + + return ENODEV; +} + +__initfunc(int ne3210_probe1(struct device *dev, int ioaddr)) +{ + int i; + unsigned long eisa_id; + const char *ifmap[] = {"UTP", "?", "BNC", "AUI"}; + + if (inb_p(ioaddr + NE3210_ID_PORT) == 0xff) return -ENODEV; + +#if NE3210_DEBUG & NE3210_D_PROBE + printk("ne3210-debug: probe at %#x, ID %#8x\n", ioaddr, inl(ioaddr + NE3210_ID_PORT)); + printk("ne3210-debug: config regs: %#x %#x\n", + inb(ioaddr + NE3210_CFG1), inb(ioaddr + NE3210_CFG2)); +#endif + + +/* Check the EISA ID of the card. */ + eisa_id = inl(ioaddr + NE3210_ID_PORT); + if (eisa_id != NE3210_ID) { + return ENODEV; + } + + +#if 0 +/* Check the vendor ID as well. Not really required. */ + if (inb(ioaddr + NE3210_SA_PROM + 0) != NE3210_ADDR0 + || inb(ioaddr + NE3210_SA_PROM + 1) != NE3210_ADDR1 + || inb(ioaddr + NE3210_SA_PROM + 2) != NE3210_ADDR2 ) { + printk("ne3210.c: card not found"); + for(i = 0; i < ETHER_ADDR_LEN; i++) + printk(" %02x", inb(ioaddr + NE3210_SA_PROM + i)); + printk(" (invalid prefix).\n"); + return ENODEV; + } +#endif + + if (load_8390_module("ne3210.c")) + return -ENOSYS; + + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("ne3210.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk ("ne3210.c: unable to allocate memory for dev->priv!\n"); + return -ENOMEM; + } + + printk("ne3210.c: NE3210 in EISA slot %d, media: %s, addr:", + ioaddr/0x1000, ifmap[inb(ioaddr + NE3210_CFG2) >> 6]); + for(i = 0; i < ETHER_ADDR_LEN; i++) + printk(" %02x", (dev->dev_addr[i] = inb(ioaddr + NE3210_SA_PROM + i))); + printk(".\nne3210.c: "); + + /* Snarf the interrupt now. CFG file has them all listed as `edge' with share=NO */ + if (dev->irq == 0) { + unsigned char irq_reg = inb(ioaddr + NE3210_CFG2) >> 3; + dev->irq = irq_map[irq_reg & 0x07]; + printk("using"); + } else { + /* This is useless unless we reprogram the card here too */ + if (dev->irq == 2) dev->irq = 9; /* Doh! */ + printk("assigning"); + } + printk(" IRQ %d,", dev->irq); + + if (request_irq(dev->irq, ei_interrupt, 0, "ne3210", dev)) { + printk (" unable to get IRQ %d.\n", dev->irq); + kfree(dev->priv); + dev->priv = NULL; + return EAGAIN; + } + + if (dev->mem_start == 0) { + unsigned char mem_reg = inb(ioaddr + NE3210_CFG2) & 0x07; + dev->mem_start = shmem_map[mem_reg] * 0x1000; + printk(" using "); + } else { + /* Should check for value in shmem_map and reprogram the card to use it */ + dev->mem_start &= 0xfff8000; + printk(" assigning "); + } + + printk("%dkB memory at physical address %#lx\n", + NE3210_STOP_PG/4, dev->mem_start); + + /* + BEWARE!! Some dain-bramaged EISA SCUs will allow you to put + the card mem within the region covered by `normal' RAM !!! + */ + if (dev->mem_start > 1024*1024) { /* phys addr > 1MB */ + if (dev->mem_start < virt_to_bus(high_memory)) { + printk(KERN_CRIT "ne3210.c: Card RAM overlaps with normal memory!!!\n"); + printk(KERN_CRIT "ne3210.c: Use EISA SCU to set card memory below 1MB,\n"); + printk(KERN_CRIT "ne3210.c: or to an address above 0x%lx.\n", virt_to_bus(high_memory)); + printk(KERN_CRIT "ne3210.c: Driver NOT installed.\n"); + free_irq(dev->irq, dev); + kfree(dev->priv); + dev->priv = NULL; + return EINVAL; + } + dev->mem_start = (unsigned long)ioremap(dev->mem_start, NE3210_STOP_PG*0x100); + if (dev->mem_start == 0) { + printk(KERN_ERR "ne3210.c: Unable to remap card memory above 1MB !!\n"); + printk(KERN_ERR "ne3210.c: Try using EISA SCU to set memory below 1MB.\n"); + printk(KERN_ERR "ne3210.c: Driver NOT installed.\n"); + free_irq(dev->irq, dev); + kfree(dev->priv); + dev->priv = NULL; + return EAGAIN; + } + ei_status.reg0 = 1; /* Use as remap flag */ + printk("ne3210.c: remapped %dkB card memory to virtual address %#lx\n", + NE3210_STOP_PG/4, dev->mem_start); + } + + dev->mem_end = dev->rmem_end = dev->mem_start + + (NE3210_STOP_PG - NE3210_START_PG)*256; + dev->rmem_start = dev->mem_start + TX_PAGES*256; + + /* The 8390 offset is zero for the NE3210 */ + dev->base_addr = ioaddr; + request_region(dev->base_addr, NE3210_IO_EXTENT, "ne3210"); + + ei_status.name = "NE3210"; + ei_status.tx_start_page = NE3210_START_PG; + ei_status.rx_start_page = NE3210_START_PG + TX_PAGES; + ei_status.stop_page = NE3210_STOP_PG; + ei_status.word16 = 1; + + if (ei_debug > 0) + printk(version); + + ei_status.reset_8390 = &ne3210_reset_8390; + ei_status.block_input = &ne3210_block_input; + ei_status.block_output = &ne3210_block_output; + ei_status.get_8390_hdr = &ne3210_get_8390_hdr; + + dev->open = &ne3210_open; + dev->stop = &ne3210_close; + NS8390_init(dev, 0); + return 0; +} + +/* + * Reset by toggling the "Board Enable" bits (bit 2 and 0). + */ + +static void ne3210_reset_8390(struct device *dev) +{ + unsigned short ioaddr = dev->base_addr; + + outb(0x04, ioaddr + NE3210_RESET_PORT); + if (ei_debug > 1) printk("%s: resetting the NE3210...", dev->name); + + mdelay(2); + + ei_status.txing = 0; + outb(0x01, ioaddr + NE3210_RESET_PORT); + if (ei_debug > 1) printk("reset done\n"); + + return; +} + +/* + * Note: In the following three functions is the implicit assumption + * that the associated memcpy will only use "rep; movsl" as long as + * we keep the counts as some multiple of doublewords. This is a + * requirement of the hardware, and also prevents us from using + * eth_io_copy_and_sum() since we can't guarantee it will limit + * itself to doubleword access. + */ + +/* + * Grab the 8390 specific header. Similar to the block_input routine, but + * we don't need to be concerned with ring wrap as the header will be at + * the start of a page, so we optimize accordingly. (A single doubleword.) + */ + +static void +ne3210_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + unsigned long hdr_start = dev->mem_start + ((ring_page - NE3210_START_PG)<<8); + memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); + hdr->count = (hdr->count + 3) & ~3; /* Round up allocation. */ +} + +/* + * Block input and output are easy on shared memory ethercards, the only + * complication is when the ring buffer wraps. The count will already + * be rounded up to a doubleword value via ne3210_get_8390_hdr() above. + */ + +static void ne3210_block_input(struct device *dev, int count, struct sk_buff *skb, + int ring_offset) +{ + unsigned long xfer_start = dev->mem_start + ring_offset - (NE3210_START_PG<<8); + + if (xfer_start + count > dev->rmem_end) { + /* Packet wraps over end of ring buffer. */ + int semi_count = dev->rmem_end - xfer_start; + memcpy_fromio(skb->data, xfer_start, semi_count); + count -= semi_count; + memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); + } else { + /* Packet is in one chunk. */ + memcpy_fromio(skb->data, xfer_start, count); + } +} + +static void ne3210_block_output(struct device *dev, int count, + const unsigned char *buf, int start_page) +{ + unsigned long shmem = dev->mem_start + ((start_page - NE3210_START_PG)<<8); + + count = (count + 3) & ~3; /* Round up to doubleword */ + memcpy_toio(shmem, buf, count); +} + +static int ne3210_open(struct device *dev) +{ + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; +} + +static int ne3210_close(struct device *dev) +{ + + if (ei_debug > 1) + printk("%s: Shutting down ethercard.\n", dev->name); + + ei_close(dev); + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef MODULE +#define MAX_NE3210_CARDS 4 /* Max number of NE3210 cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_NE3210_CARDS] = { 0, }; +static struct device dev_ne3210[MAX_NE3210_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_NE3210_CARDS] = { 0, }; +static int irq[MAX_NE3210_CARDS] = { 0, }; +static int mem[MAX_NE3210_CARDS] = { 0, }; + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); +MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); + +int init_module(void) +{ + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_NE3210_CARDS; this_dev++) { + struct device *dev = &dev_ne3210[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->mem_start = mem[this_dev]; + dev->init = ne3210_probe; + /* Default is to only install one card. */ + if (io[this_dev] == 0 && this_dev != 0) break; + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "ne3210.c: No NE3210 card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) { /* Got at least one. */ + lock_8390_module(); + return 0; + } + return -ENXIO; + } + found++; + } + lock_8390_module(); + return 0; +} + +void cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_NE3210_CARDS; this_dev++) { + struct device *dev = &dev_ne3210[this_dev]; + if (dev->priv != NULL) { + void *priv = dev->priv; + free_irq(dev->irq, dev); + release_region(dev->base_addr, NE3210_IO_EXTENT); + if (ei_status.reg0) + iounmap((void *)dev->mem_start); + dev->priv = NULL; + unregister_netdev(dev); + kfree(priv); + } + } + unlock_8390_module(); +} +#endif /* MODULE */ + diff -u --recursive --new-file v2.1.124/linux/drivers/net/ni5010.c linux/drivers/net/ni5010.c --- v2.1.124/linux/drivers/net/ni5010.c Thu Feb 12 20:56:08 1998 +++ linux/drivers/net/ni5010.c Wed Oct 7 15:51:45 1998 @@ -771,8 +771,9 @@ } #ifdef MODULE +static char devicename[9] = { 0, }; static struct device dev_ni5010 = { - " ", + devicename, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ni5010_probe }; diff -u --recursive --new-file v2.1.124/linux/drivers/net/ni52.c linux/drivers/net/ni52.c --- v2.1.124/linux/drivers/net/ni52.c Wed May 20 19:10:39 1998 +++ linux/drivers/net/ni52.c Wed Oct 7 15:51:45 1998 @@ -1303,8 +1303,9 @@ } #ifdef MODULE +static char devicename[9] = { 0, }; static struct device dev_ni52 = { - " ", /* "ni5210": device name inserted by net_init.c */ + devicename, /* "ni5210": device name inserted by net_init.c */ 0, 0, 0, 0, 0x300, 9, /* I/O address, IRQ */ 0, 0, 0, NULL, ni52_probe }; diff -u --recursive --new-file v2.1.124/linux/drivers/net/ppp.c linux/drivers/net/ppp.c --- v2.1.124/linux/drivers/net/ppp.c Mon Sep 28 10:51:33 1998 +++ linux/drivers/net/ppp.c Tue Oct 6 09:39:49 1998 @@ -4,7 +4,7 @@ * Al Longyear * Extensively rewritten by Paul Mackerras * - * ==FILEVERSION 980704== + * ==FILEVERSION 981004== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the number above to the @@ -115,6 +115,7 @@ static void ppp_output_wakeup(struct ppp *ppp); static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb); static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb); +static void ppp_send_frames(struct ppp *ppp); static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb); static struct ppp *ppp_find (int pid_value); @@ -439,6 +440,8 @@ return; if (ppp->backup_tty) { ppp->tty = ppp->backup_tty; + if (ppp_tty_push(ppp)) + ppp_output_wakeup(ppp); } else { ppp->tty = 0; ppp->sc_xfer = 0; @@ -448,7 +451,6 @@ ppp_async_release(ppp); ppp_release(ppp); - ppp->inuse = 0; MOD_DEC_USE_COUNT; } } @@ -1289,6 +1291,9 @@ CHECK_PPP_MAGIC(ppp); + /* ppp_dev_close may be called with tbusy==1 so we must set it to 0 */ + dev->tbusy=0; + MOD_DEC_USE_COUNT; return 0; @@ -2113,8 +2118,8 @@ /* * Compress and send an frame to the peer. - * Should be called with dev->tbusy == 1, having been set by the caller. - * That is, we use dev->tbusy as a lock to prevent reentry of this + * Should be called with xmit_busy == 1, having been set by the caller. + * That is, we use xmit_busy as a lock to prevent reentry of this * procedure. */ static void @@ -2186,7 +2191,7 @@ if (new_skb == NULL) { printk(KERN_ERR "ppp_send_frame: no memory\n"); kfree_skb(skb); - ppp->dev.tbusy = 0; + ppp->xmit_busy = 0; return; } @@ -2215,9 +2220,9 @@ ret = ppp_async_send(ppp, skb); if (ret > 0) { /* we can release the lock */ - ppp->dev.tbusy = 0; + ppp->xmit_busy = 0; } else if (ret < 0) { - /* this can't happen, since the caller got the tbusy lock */ + /* can't happen, since the caller got the xmit_busy lock */ printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n"); } } @@ -2276,15 +2281,18 @@ { struct sk_buff *skb; - while (!test_and_set_bit(0, &ppp->dev.tbusy)) { + while (!test_and_set_bit(0, &ppp->xmit_busy)) { skb = skb_dequeue(&ppp->xmt_q); if (skb == NULL) { - ppp->dev.tbusy = 0; - mark_bh(NET_BH); + ppp->xmit_busy = 0; break; } ppp_send_frame(ppp, skb); } + if (!ppp->xmit_busy && ppp->dev.tbusy) { + ppp->dev.tbusy = 0; + mark_bh(NET_BH); + } } /* @@ -2296,11 +2304,11 @@ { CHECK_PPP_VOID(); - if (!ppp->dev.tbusy) { - printk(KERN_ERR "ppp_output_wakeup called but tbusy==0\n"); + if (!ppp->xmit_busy) { + printk(KERN_ERR "ppp_output_wakeup called but xmit_busy==0\n"); return; } - ppp->dev.tbusy = 0; + ppp->xmit_busy = 0; ppp_send_frames(ppp); } @@ -2425,9 +2433,15 @@ * The dev->tbusy field acts as a lock to allow only * one packet to be processed at a time. If we can't * get the lock, try again later. + * We deliberately queue as little as possible inside + * the ppp driver in order to minimize the latency + * for high-priority packets. */ - if (test_and_set_bit(0, &dev->tbusy)) + if (test_and_set_bit(0, &ppp->xmit_busy)) { + dev->tbusy = 1; /* can't take it now */ return 1; + } + dev->tbusy = 0; /* * Put the 4-byte PPP header on the packet. @@ -2441,7 +2455,8 @@ printk(KERN_ERR "%s: skb hdr alloc failed\n", ppp->name); dev_kfree_skb(skb); - dev->tbusy = 0; + ppp->xmit_busy = 0; + ppp_send_frames(ppp); return 0; } skb_reserve(new_skb, PPP_HDRLEN); @@ -2457,6 +2472,8 @@ hdr[3] = proto; ppp_send_frame(ppp, skb); + if (!ppp->xmit_busy) + ppp_send_frames(ppp); return 0; } @@ -2600,6 +2617,7 @@ ppp->last_xmit = jiffies; ppp->last_recv = jiffies; + ppp->xmit_busy = 0; /* clear statistics */ memset(&ppp->stats, 0, sizeof (struct pppstat)); @@ -2640,6 +2658,12 @@ kfree_skb(skb); while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL) kfree_skb(skb); + + ppp->inuse = 0; + if (ppp->dev.tbusy) { + ppp->dev.tbusy = 0; + mark_bh(NET_BH); + } } /* diff -u --recursive --new-file v2.1.124/linux/drivers/net/rrunner.c linux/drivers/net/rrunner.c --- v2.1.124/linux/drivers/net/rrunner.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/rrunner.c Wed Oct 7 11:13:49 1998 @@ -0,0 +1,1208 @@ +/* + * rrunner.c: Linux driver for the Essential RoadRunner HIPPI board. + * + * Written 1998 by Jes Sorensen, . + * + * Thanks to Essential Communication for providing us with hardware + * and very comprehensive documentation without which I would not have + * been able to write this driver. A special thank you to John Gibbon + * for sorting out the legal issues, with the NDA, allowing the code to + * be released under the GPL. + * + * 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. + */ + +#define DEBUG 1 +#define RX_DMA_SKBUFF 1 +#define PKT_COPY_THRESHOLD 512 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "rrunner.h" + +/* + * Implementation notes: + * + * The DMA engine only allows for DMA within physical 64KB chunks of + * memory. The current approach of the driver (and stack) is to use + * linear blocks of memory for the skbuffs. However, as the data block + * is always the first part of the skb and skbs are 2^n aligned so we + * are guarantted to get the whole block within one 64KB align 64KB + * chunk. + * + * On the long term, relying on being able to allocate 64KB linear + * chunks of memory is not feasible and the skb handling code and the + * stack will need to know about I/O vectors or something similar. + */ + +static const char *version = "rrunner.c: v0.06 09/02/98 Jes Sorensen (Jes.Sorensen@cern.ch)\n"; + +static unsigned int read_eeprom(struct rr_private *rrpriv, + unsigned long offset, + unsigned char *buf, + unsigned long length); +static u32 read_eeprom_word(struct rr_private *rrpriv, + void * offset); +static int rr_load_firmware(struct device *dev); + + +__initfunc(int rr_hippi_probe (struct device *dev)) +{ + static int i = 0; + int boards_found = 0; + int version_disp; /* was version info already displayed? */ + u8 pci_bus; /* PCI bus number (0-255) */ + u8 pci_dev_fun; /* PCI device and function numbers (0-255) */ + u8 pci_latency; + u16 command; /* PCI Configuration space Command register */ + unsigned int tmp; + u8 irq; + struct rr_private *rrpriv; + + if (!pci_present()) /* is PCI BIOS even present? */ + return -ENODEV; + + version_disp = 0; + + for (; i < 255; i++) + { + if (pcibios_find_device(PCI_VENDOR_ID_ESSENTIAL, + PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER, + i, &pci_bus, &pci_dev_fun) != 0) + break; + + pcibios_read_config_word(pci_bus, pci_dev_fun, + PCI_COMMAND, &command); + + /* Enable mastering */ + + command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, pci_dev_fun, + PCI_COMMAND, command); + + if (!(command & PCI_COMMAND_MEMORY)){ + printk("shared mem not enabled - unable to configure RoadRunner\n"); + break; + } + + /* + * So we found our HIPPI ... time to tell the system. + */ + + dev = init_hippi_dev(dev, sizeof(struct rr_private)); + + if (dev == NULL) + break; + + rrpriv = (struct rr_private *)dev->priv; + + /* Read register base address from + PCI Configuration Space */ + + pcibios_read_config_dword(pci_bus, pci_dev_fun, + PCI_BASE_ADDRESS_0, &tmp); + + pcibios_read_config_byte(pci_bus, pci_dev_fun, + PCI_INTERRUPT_LINE, &irq); + + dev->irq = irq; + rrpriv->pci_bus = pci_bus; + rrpriv->pci_dev_fun = pci_dev_fun; + sprintf(rrpriv->name, "RoadRunner serial HIPPI"); +#ifdef __SMP__ + spin_lock_init(&rrpriv->lock); +#endif + + dev->open = &rr_open; + dev->hard_start_xmit = &rr_start_xmit; + dev->stop = &rr_close; + dev->get_stats = &rr_get_stats; + dev->do_ioctl = &rr_ioctl; + + /* + * Dummy value. + */ + dev->base_addr = 42; + + /* display version info if adapter is found */ + if (!version_disp) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + version_disp = 1; + printk(version); + } + + printk(KERN_INFO "%s: Essential RoadRunner serial HIPPI at 0x%08x, irq %i\n", + dev->name, tmp, dev->irq); + + pcibios_read_config_byte(pci_bus, pci_dev_fun, + PCI_LATENCY_TIMER, &pci_latency); +#if 0 + if (pci_latency <= 48){ + printk(" PCI latency counter too low (%i), setting to 48 clocks\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_dev_fun, + PCI_LATENCY_TIMER, 48); + } +#else + if (pci_latency <= 0x58) + pcibios_write_config_byte(pci_bus, pci_dev_fun, + PCI_LATENCY_TIMER, 0x58); +#endif + /* + * Remap the regs into kernel space. + */ + + rrpriv->regs = (struct rr_regs *)ioremap(tmp, 0x1000); + if (!rrpriv->regs){ + printk(KERN_ERR "%s: Unable to map I/O register, RoadRunner %i will be disabled.\n", dev->name, i); + break; + } + + /* + * Don't access any registes before this point! + */ +#ifdef __BIG_ENDIAN + regs->HostCtrl |= NO_SWAP; +#endif + /* + * Need to add a case for little-endian 64-bit hosts here. + */ + + rr_init(dev); + + boards_found++; + + /* + * This is bollocks, but we need to tell the net-init + * code that it shall go for the next device. + */ + dev->base_addr = 0; + } + + /* + * If we're at this point we're going through rr_hippi_probe() + * for the first time. Return success (0) if we've initialized + * 1 or more boards. Otherwise, return failure (-ENODEV). + */ + + if (boards_found > 0) + return 0; + else + return -ENODEV; +} + + +/* + * Commands are considered to be slow, thus there is no reason to + * inline this. + */ +static void rr_issue_cmd(struct rr_private *rrpriv, struct cmd *cmd) +{ + struct rr_regs *regs; + u32 idx; + + regs = rrpriv->regs; + /* + * This is temporary - it will go away in the final version. + * We probably also want to make this function inline. + */ + if (regs->HostCtrl & NIC_HALTED){ + printk("issuing command for halted NIC, code 0x%x, HostCtrl %08x\n", cmd->code, regs->HostCtrl); + if (regs->Mode & FATAL_ERR) + printk("error code %02x\n", regs->Fail1); + } + + idx = rrpriv->info->cmd_ctrl.pi; + + regs->CmdRing[idx] = *(u32*)(cmd); + + idx = (idx - 1) % CMD_RING_ENTRIES; + rrpriv->info->cmd_ctrl.pi = idx; + + if (regs->Mode & FATAL_ERR) + printk("error code %02x\n", regs->Fail1); +} + + +/* + * Reset the board in a sensible manner. The NIC is already halted + * when we get here and a spin-lock is held. + */ +static int rr_reset(struct device *dev) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + struct eeprom *hw = NULL; + u32 start_pc; + int i; + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + + rr_load_firmware(dev); + + regs->TX_state = 0x01000000; + regs->RX_state = 0xff800000; + regs->AssistState = 0; + regs->LocalCtrl = CLEAR_INTA; + regs->BrkPt = 0x01; + regs->Timer = 0; + regs->TimerRef = 0; + regs->DmaReadState = RESET_DMA; + regs->DmaWriteState = RESET_DMA; + regs->DmaWriteHostHi = 0; + regs->DmaWriteHostLo = 0; + regs->DmaReadHostHi = 0; + regs->DmaReadHostLo = 0; + regs->DmaReadLen = 0; + regs->DmaWriteLen = 0; + regs->DmaWriteLcl = 0; + regs->DmaWriteIPchecksum = 0; + regs->DmaReadLcl = 0; + regs->DmaReadIPchecksum = 0; + regs->PciState = 0; /* 0x90 for GE? */ + regs->Mode = SWAP_DATA; + +#if 0 + /* + * Don't worry, this is just black magic. + */ + regs->RxBase = 0xdf000; + regs->RxPrd = 0xdf000; + regs->RxCon = 0xdf000; + regs->TxBase = 0xce000; + regs->TxPrd = 0xce000; + regs->TxCon = 0xce000; + regs->RxIndPro = 0; + regs->RxIndCon = 0; + regs->RxIndRef = 0; + regs->TxIndPro = 0; + regs->TxIndCon = 0; + regs->TxIndRef = 0; + regs->pad10[0] = 0xcc000; + regs->DrCmndPro = 0; + regs->DrCmndCon = 0; + regs->DwCmndPro = 0; + regs->DwCmndCon = 0; + regs->DwCmndRef = 0; + regs->DrDataPro = 0; + regs->DrDataCon = 0; + regs->DrDataRef = 0; + regs->DwDataPro = 0; + regs->DwDataCon = 0; + regs->DwDataRef = 0; +#endif + + regs->MbEvent = 0xffffffff; + regs->Event = 0; + + regs->TxPi = 0; + regs->IpRxPi = 0; + + regs->EvtCon = 0; + regs->EvtPrd = 0; + + rrpriv->info->evt_ctrl.pi = 0; + + for (i = 0; i < CMD_RING_ENTRIES; i++) + regs->CmdRing[i] = 0; + + regs->PciState = 0; + + start_pc = read_eeprom_word(rrpriv, &hw->rncd_info.FwStart); + +#if (DEBUG > 1) + printk("%s: Executing firmware at address 0x%06x\n", + dev->name, start_pc); +#endif + + regs->Pc = start_pc + 0x800; + udelay(5); + + regs->Pc = start_pc; + + return 0; +} + +/* + * Read a string from the EEPROM. + */ +static unsigned int read_eeprom(struct rr_private *rrpriv, + unsigned long offset, + unsigned char *buf, + unsigned long length) +{ + struct rr_regs *regs = rrpriv->regs; + u32 misc, io, i; + + io = regs->ExtIo; + regs->ExtIo = 0; + misc = regs->LocalCtrl; + regs->LocalCtrl = 0; + + for (i = 0; i < length; i++){ + regs->WinBase = (EEPROM_BASE + ((offset+i) << 3)); + buf[i] = (regs->WinData >> 24) & 0xff; + } + + regs->LocalCtrl = misc; + regs->ExtIo = io; + + return i; +} + + +/* + * Shortcut to read one word (4 bytes) out of the EEPROM and convert + * it to our CPU byte-order. + */ +static u32 read_eeprom_word(struct rr_private *rrpriv, + void * offset) +{ + u32 word; + + if ((read_eeprom(rrpriv, (unsigned long)offset, + (char *)&word, 4) == 4)) + return be32_to_cpu(word); + return 0; +} + + +__initfunc(static int rr_init(struct device *dev)) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + u32 sram_size, rev; + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + + rev = regs->FwRev; + if (rev > 0x00020024) + printk(" Firmware revision: %i.%i.%i\n", (rev >> 16), + ((rev >> 8) & 0xff), (rev & 0xff)); + else{ + printk(" Firmware revision too old: %i.%i.%i, please upgrade to 2.0.37 or later.\n", + (rev >> 16), ((rev >> 8) & 0xff), (rev & 0xff)); + return -EFAULT; + + } + + printk(" Maximum receive rings %i\n", regs->MaxRxRng); + + sram_size = read_eeprom_word(rrpriv, (void *)8); + printk(" SRAM size 0x%06x\n", sram_size); + + return 0; +} + + +static int rr_init1(struct device *dev) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + u32 hostctrl; + unsigned long myjif, flags, tmp_ptr; + struct cmd cmd; + short i; + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + + spin_lock_irqsave(&rrpriv->lock, flags); + + hostctrl = regs->HostCtrl; + regs->HostCtrl |= HALT_NIC; + + if (hostctrl & PARITY_ERR){ + printk("%s: Parity error halting NIC - this is serious!\n", + dev->name); + spin_unlock_irqrestore(&rrpriv->lock, flags); + return -EFAULT; + } + + + memset(rrpriv->rx_ctrl, 0, 256 * sizeof(struct ring_ctrl)); + memset(rrpriv->info, 0, sizeof(struct rr_info)); + + tmp_ptr = virt_to_bus((void *)rrpriv->rx_ctrl); +#if (BITS_PER_LONG == 64) + regs->RxRingHi = (tmp_ptr >> 32); +#else + regs->RxRingHi = 0; +#endif + regs->RxRingLo = ((tmp_ptr) & 0xffffffff); + + tmp_ptr = virt_to_bus((void *)rrpriv->info); +#if (BITS_PER_LONG == 64) + regs->InfoPtrHi = (tmp_ptr >> 32); +#else + regs->InfoPtrHi = 0; +#endif + regs->InfoPtrLo = ((tmp_ptr) & 0xffffffff); + + rrpriv->info->evt_ctrl.entry_size = sizeof(struct event); + rrpriv->info->evt_ctrl.entries = EVT_RING_ENTRIES; + rrpriv->info->evt_ctrl.mode = 0; + rrpriv->info->evt_ctrl.pi = 0; + rrpriv->info->evt_ctrl.rngptr = virt_to_bus(rrpriv->evt_ring); + + rrpriv->info->cmd_ctrl.entry_size = sizeof(struct cmd); + rrpriv->info->cmd_ctrl.entries = CMD_RING_ENTRIES; + rrpriv->info->cmd_ctrl.mode = 0; + rrpriv->info->cmd_ctrl.pi = 15; + + for (i = 0; i < CMD_RING_ENTRIES; i++) { + regs->CmdRing[i] = 0; + } + + for (i = 0; i < TX_RING_ENTRIES; i++) { + rrpriv->tx_ring[i].size = 0; + rrpriv->tx_ring[i].addr = 0; + rrpriv->tx_skbuff[i] = 0; + } + + rrpriv->info->tx_ctrl.entry_size = sizeof(struct tx_desc); + rrpriv->info->tx_ctrl.entries = TX_RING_ENTRIES; + rrpriv->info->tx_ctrl.mode = 0; + rrpriv->info->tx_ctrl.pi = 0; + rrpriv->info->tx_ctrl.rngptr = virt_to_bus(rrpriv->tx_ring); + + /* + * Set dirty_tx before we start receiving interrupts, otherwise + * the interrupt handler might think it is supposed to process + * tx ints before we are up and running, which may cause a null + * pointer access in the int handler. + */ + rrpriv->tx_full = 0; + rrpriv->cur_rx = 0; + rrpriv->dirty_rx = rrpriv->dirty_tx = 0; + + rr_reset(dev); + + regs->IntrTmr = 0x60; + regs->WriteDmaThresh = 0x80 | 0x1f; + regs->ReadDmaThresh = 0x80 | 0x1f; + + rrpriv->fw_running = 0; + + hostctrl &= ~(HALT_NIC | INVALID_INST_B | PARITY_ERR); + regs->HostCtrl = hostctrl; + + spin_unlock_irqrestore(&rrpriv->lock, flags); + + udelay(1000); + + /* + * Now start the FirmWare. + */ + cmd.code = C_START_FW; + cmd.ring = 0; + cmd.index = 0; + + rr_issue_cmd(rrpriv, &cmd); + + /* + * Give the FirmWare time to chew on the `get running' command. + */ + myjif = jiffies + 5 * HZ; + while ((jiffies < myjif) && !rrpriv->fw_running); + + for (i = 0; i < RX_RING_ENTRIES; i++) { + struct sk_buff *skb; + + rrpriv->rx_ring[i].mode = 0; + skb = alloc_skb(dev->mtu + HIPPI_HLEN, GFP_ATOMIC); + rrpriv->rx_skbuff[i] = skb; + /* + * Sanity test to see if we conflict with the DMA + * limitations of the Roadrunner. + */ + if ((((unsigned long)skb->data) & 0xfff) > ~65320) + printk("skb alloc error\n"); + +#if (BITS_PER_LONG == 32) + rrpriv->rx_ring[i].zero = 0; +#endif + rrpriv->rx_ring[i].addr = virt_to_bus(skb->data); + rrpriv->rx_ring[i].size = dev->mtu + HIPPI_HLEN; + } + + rrpriv->rx_ctrl[4].entry_size = sizeof(struct rx_desc); + rrpriv->rx_ctrl[4].entries = RX_RING_ENTRIES; + rrpriv->rx_ctrl[4].mode = 8; + rrpriv->rx_ctrl[4].pi = 0; + rrpriv->rx_ctrl[4].rngptr = virt_to_bus(rrpriv->rx_ring); + + cmd.code = C_NEW_RNG; + cmd.ring = 4; + cmd.index = 0; + rr_issue_cmd(rrpriv, &cmd); + +#if 0 +{ + u32 tmp; + tmp = regs->ExtIo; + regs->ExtIo = 0x80; + + i = jiffies + 1 * HZ; + while (jiffies < i); + regs->ExtIo = tmp; +} +#endif + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + return 0; +} + + +/* + * All events are considered to be slow (RX/TX ints do not generate + * events) and are handled here, outside the main interrupt handler, + * to reduce the size of the handler. + */ +static u32 rr_handle_event(struct device *dev, u32 prodidx) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + u32 tmp, eidx; +#if 0 + short i; +#endif + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + eidx = rrpriv->info->evt_ctrl.pi; + + while (prodidx != eidx){ + switch (rrpriv->evt_ring[eidx].code){ + case E_NIC_UP: + tmp = regs->FwRev; + printk("%s: Firmware revision %i.%i.%i up and running\n", + dev->name, (tmp >> 16), ((tmp >> 8) & 0xff), + (tmp & 0xff)); + rrpriv->fw_running = 1; + break; + case E_LINK_ON: + printk("%s: Optical link ON\n", dev->name); + break; + case E_LINK_OFF: + printk("%s: Optical link OFF\n", dev->name); + break; + case E_RX_IDLE: + printk("%s: RX data not moving\n", dev->name); + break; + case E_WATCHDOG: + printk("%s: The watchdog is here to see us\n", + dev->name); + break; + /* + * TX events. + */ + case E_CON_REJ: + printk("%s: Connection rejected\n", dev->name); + rrpriv->stats.tx_aborted_errors++; + break; + case E_CON_TMOUT: + printk("%s: Connection timeout\n", dev->name); + break; + case E_DISC_ERR: + printk("%s: HIPPI disconnect error\n", dev->name); + rrpriv->stats.tx_aborted_errors++; + break; + case E_TX_IDLE: + printk("%s: Transmitter idle\n", dev->name); + break; + case E_TX_LINK_DROP: + printk("%s: Link lost during transmit\n", dev->name); + rrpriv->stats.tx_aborted_errors++; + break; + /* + * RX events. + */ + case E_VAL_RNG: /* Should be ignored */ +#if (DEBUG > 2) + printk("%s: RX ring valid event\n", dev->name); +#endif + regs->IpRxPi = RX_RING_ENTRIES - 1; + break; + case E_INV_RNG: + printk("%s: RX ring invalid event\n", dev->name); + break; + case E_RX_RNG_OUT: + printk("%s: Receive ring full\n", dev->name); + break; + + case E_RX_PAR_ERR: + printk("%s: Receive parity error.\n", dev->name); + break; + case E_RX_LLRC_ERR: + printk("%s: Receive LLRC error.\n", dev->name); + break; + case E_PKT_LN_ERR: + printk("%s: Receive packet length error.\n", + dev->name); + break; + default: + printk("%s: Unhandled event 0x%02x\n", + dev->name, rrpriv->evt_ring[eidx].code); + } + eidx = (eidx + 1) % EVT_RING_ENTRIES; + } + + rrpriv->info->evt_ctrl.pi = eidx; + return eidx; +} + + +static int rx_int(struct device *dev, u32 rxlimit) +{ + struct rr_private *rrpriv = (struct rr_private *)dev->priv; + u32 index, pkt_len; + struct rr_regs *regs = rrpriv->regs; + + index = rrpriv->cur_rx; + + while(index != rxlimit){ + pkt_len = rrpriv->rx_ring[index].size; +#if (DEBUG > 2) + printk("index %i, rxlimit %i\n", index, rxlimit); + printk("len %x, mode %x\n", pkt_len, + rrpriv->rx_ring[index].mode); +#endif +#if 0 +/* + * I have never seen this occur + */ + if(!(rrpriv->rx_skbuff[index])){ + printk("Trying to receive in empty skbuff\n"); + goto out; + } +#endif + + if (pkt_len > 0){ + struct sk_buff *skb; + + if (pkt_len < PKT_COPY_THRESHOLD) { + skb = alloc_skb(pkt_len, GFP_ATOMIC); + if (skb == NULL){ + printk("%s: Out of memory deferring packet\n", dev->name); + rrpriv->stats.rx_dropped++; + goto defer; + }else + memcpy(skb_put(skb, pkt_len), + rrpriv->rx_skbuff[index]->data, + pkt_len); + }else{ + struct sk_buff *newskb; + + newskb = alloc_skb(dev->mtu + HIPPI_HLEN, GFP_ATOMIC); + if (newskb){ + skb = rrpriv->rx_skbuff[index]; + skb_put(skb, pkt_len); + rrpriv->rx_skbuff[index] = newskb; + rrpriv->rx_ring[index].addr = virt_to_bus(newskb->data); + }else{ + printk("%s: Out of memory, deferring packet\n", dev->name); + rrpriv->stats.rx_dropped++; + goto defer; + } + } + skb->dev = dev; + skb->protocol = hippi_type_trans(skb, dev); + + netif_rx(skb); /* send it up */ + + rrpriv->stats.rx_packets++; + rrpriv->stats.rx_bytes += skb->len; + } + defer: + rrpriv->rx_ring[index].mode = 0; + rrpriv->rx_ring[index].size = dev->mtu + HIPPI_HLEN; + + if ((index & 7) == 7) + regs->IpRxPi = index; + + index = (index + 1) % RX_RING_ENTRIES; + } + + rrpriv->cur_rx = index; + return index; +} + + +static void rr_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + struct device *dev = (struct device *)dev_id; + u32 prodidx, eidx, txcsmr, rxlimit, txcon; + unsigned long flags; + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + + if (!(regs->HostCtrl & RR_INT)){ +#if 0 + /* These are harmless */ + printk("%s: spurious interrupt detected\n", dev->name); +#endif + return; + } + + if (test_and_set_bit(0, (void*)&dev->interrupt) != 0) { + printk("%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + + spin_lock_irqsave(&rrpriv->lock, flags); + + prodidx = regs->EvtPrd; + txcsmr = (prodidx >> 8) & 0xff; + rxlimit = (prodidx >> 16) & 0xff; + prodidx &= 0xff; + +#if (DEBUG > 2) + printk("%s: interrupt, prodidx = %i, eidx = %i\n", dev->name, + prodidx, rrpriv->info->evt_ctrl.pi); +#endif + + txcon = rrpriv->dirty_tx; + if (txcsmr != txcon) { + do { + rrpriv->stats.tx_packets++; + rrpriv->stats.tx_bytes +=rrpriv->tx_skbuff[txcon]->len; + dev_kfree_skb(rrpriv->tx_skbuff[txcon]); + + rrpriv->tx_skbuff[txcon] = NULL; + rrpriv->tx_ring[txcon].size = 0; + rrpriv->tx_ring[txcon].addr = 0; + rrpriv->tx_ring[txcon].mode = 0; + + txcon = (txcon + 1) % TX_RING_ENTRIES; + } while (txcsmr != txcon); + + rrpriv->dirty_tx = txcon; + if (rrpriv->tx_full && dev->tbusy && + (((rrpriv->info->tx_ctrl.pi + 1) % TX_RING_ENTRIES) + != rrpriv->dirty_tx)){ + rrpriv->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + } + + rx_int(dev, rxlimit); + + eidx = rrpriv->info->evt_ctrl.pi; + + if (prodidx != eidx) + eidx = rr_handle_event(dev, prodidx); + + eidx |= ((txcsmr << 8) | (rxlimit << 16)); + regs->EvtCon = eidx; + + spin_unlock_irqrestore(&rrpriv->lock, flags); + + dev->interrupt = 0; +} + + +static int rr_open(struct device *dev) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + +#if 0 + regs->HostCtrl |= (HALT_NIC | RR_CLEAR_INT); +#endif + + if (request_irq(dev->irq, rr_interrupt, 0, rrpriv->name, dev)) + { + printk(KERN_WARNING "%s: Requested IRQ %d is busy\n", + dev->name, dev->irq); + return -EAGAIN; + } + + rrpriv->rx_ctrl = kmalloc(256*sizeof(struct ring_ctrl), + GFP_KERNEL | GFP_DMA); + rrpriv->info = kmalloc(sizeof(struct rr_info), GFP_KERNEL | GFP_DMA); + + rr_init1(dev); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + MOD_INC_USE_COUNT; + return 0; +} + + +static void rr_dump(struct device *dev) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + u32 index, cons; + short i; + int len; + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + + printk("%s: dumping NIC TX rings\n", dev->name); + + printk("RxPrd %08x, TxPrd %02x, EvtPrd %08x, TxPi %02x, TxCtrlPi %02x\n", + regs->RxPrd, regs->TxPrd, regs->EvtPrd, regs->TxPi, + rrpriv->info->tx_ctrl.pi); + + printk("Error code 0x%x\n", regs->Fail1); + + index = (((regs->EvtPrd >> 8) & 0xff ) - 1) % EVT_RING_ENTRIES; + cons = rrpriv->dirty_tx; + printk("TX ring index %i, TX consumer %i\n", + index, cons); + + if (rrpriv->tx_skbuff[index]){ + len = min(0x80, rrpriv->tx_skbuff[index]->len); + printk("skbuff for index %i is valid - dumping data (0x%x bytes - DMA len 0x%x)\n", index, len, rrpriv->tx_ring[index].size); + for (i = 0; i < len; i++){ + if (!(i & 7)) + printk("\n"); + printk("%02x ", (unsigned char) rrpriv->tx_skbuff[index]->data[i]); + } + printk("\n"); + } + + if (rrpriv->tx_skbuff[cons]){ + len = min(0x80, rrpriv->tx_skbuff[cons]->len); + printk("skbuff for cons %i is valid - dumping data (0x%x bytes - skbuff len 0x%x)\n", cons, len, rrpriv->tx_skbuff[cons]->len); + printk("mode 0x%x, size 0x%x,\n phys %08x (virt %08x), skbuff-addr %08x, truesize 0x%x\n", + rrpriv->tx_ring[cons].mode, + rrpriv->tx_ring[cons].size, + rrpriv->tx_ring[cons].addr, + (unsigned int)bus_to_virt(rrpriv->tx_ring[cons].addr), + (unsigned int)rrpriv->tx_skbuff[cons]->data, + (unsigned int)rrpriv->tx_skbuff[cons]->truesize); + for (i = 0; i < len; i++){ + if (!(i & 7)) + printk("\n"); + printk("%02x ", (unsigned char)rrpriv->tx_ring[cons].size); + } + printk("\n"); + } + + printk("dumping TX ring info:\n"); + for (i = 0; i < TX_RING_ENTRIES; i++) + printk("mode 0x%x, size 0x%x, phys-addr %08x\n", + rrpriv->tx_ring[i].mode, + rrpriv->tx_ring[i].size, + rrpriv->tx_ring[i].addr); + +} + + +static int rr_close(struct device *dev) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; + u32 tmp; + short i; + + dev->start = 0; + set_bit(0, (void*)&dev->tbusy); + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + + tmp = regs->HostCtrl; + if (tmp & NIC_HALTED){ + printk("%s: NIC already halted\n", dev->name); + rr_dump(dev); + }else + tmp |= HALT_NIC; + regs->HostCtrl = tmp; + + /* + * Lock to make sure we are not cleaning up while another CPU + * handling interrupts. + */ + spin_lock(&rrpriv->lock); + + regs->TxPi = 0; + regs->IpRxPi = 0; + + regs->EvtCon = 0; + regs->EvtPrd = 0; + + for (i = 0; i < CMD_RING_ENTRIES; i++) + regs->CmdRing[i] = 0; + + rrpriv->info->tx_ctrl.entries = 0; + rrpriv->info->cmd_ctrl.pi = 0; + rrpriv->info->evt_ctrl.pi = 0; + rrpriv->rx_ctrl[4].entries = 0; + + for (i = 0; i < TX_RING_ENTRIES; i++) { + if (rrpriv->tx_skbuff[i]) { + rrpriv->tx_ring[i].size = 0; + rrpriv->tx_ring[i].addr = 0; + dev_kfree_skb(rrpriv->tx_skbuff[i]); + } + } + + for (i = 0; i < RX_RING_ENTRIES; i++) { + if (rrpriv->rx_skbuff[i]) { + rrpriv->rx_ring[i].size = 0; + rrpriv->rx_ring[i].addr = 0; + dev_kfree_skb(rrpriv->rx_skbuff[i]); + } + } + + kfree(rrpriv->rx_ctrl); + kfree(rrpriv->info); + + free_irq(dev->irq, dev); + spin_unlock(&rrpriv->lock); + + MOD_DEC_USE_COUNT; + return 0; +} + + +static int rr_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct rr_private *rrpriv = (struct rr_private *)dev->priv; + struct rr_regs *regs = rrpriv->regs; + struct ring_ctrl *txctrl; + unsigned long flags; + u32 index, len = skb->len; + u32 *ifield; + struct sk_buff *new_skb; + + /* + * We probably need to deal with tbusy here to prevent overruns. + */ + + if (skb_headroom(skb) < 8){ + printk("incoming skb too small - reallocating\n"); + if (!(new_skb = dev_alloc_skb(len + 8))) { + dev_kfree_skb(skb); + dev->tbusy = 0; + return -EBUSY; + } + skb_reserve(new_skb, 8); + skb_put(new_skb, len); + memcpy(new_skb->data, skb->data, len); + dev_kfree_skb(skb); + skb = new_skb; + } + + ifield = (u32 *)skb_push(skb, 8); + + ifield[0] = 0; + ifield[1] = skb->private.ifield; + + /* + * We don't need the lock before we are actually going to start + * fiddling with the control blocks. + */ + spin_lock_irqsave(&rrpriv->lock, flags); + + txctrl = &rrpriv->info->tx_ctrl; + + index = txctrl->pi; + + rrpriv->tx_skbuff[index] = skb; + rrpriv->tx_ring[index].addr = virt_to_bus(skb->data); + rrpriv->tx_ring[index].size = len + 8; /* include IFIELD */ + rrpriv->tx_ring[index].mode = PACKET_START | PACKET_END; + txctrl->pi = (index + 1) % TX_RING_ENTRIES; + regs->TxPi = txctrl->pi; + + if (txctrl->pi == rrpriv->dirty_tx){ + rrpriv->tx_full = 1; + set_bit(0, (void*)&dev->tbusy); + } + + spin_unlock_irqrestore(&rrpriv->lock, flags); + + dev->trans_start = jiffies; + return 0; +} + + +static struct net_device_stats *rr_get_stats(struct device *dev) +{ + struct rr_private *rrpriv; + + rrpriv = (struct rr_private *)dev->priv; + + return(&rrpriv->stats); +} + + +/* + * Read the firmware out of the EEPROM and put it into the SRAM + * (or from user space - later) + * + * This operation requires the NIC to be halted and is performed with + * interrupts disabled and with the spinlock hold. + */ +static int rr_load_firmware(struct device *dev) +{ + struct rr_private *rrpriv; + struct rr_regs *regs; +#if 0 + unsigned long flags; +#endif + int i, j; + u32 localctrl, eptr, sptr, segptr, len, tmp; + u32 p2len, p2size, nr_seg, revision, io, sram_size; + struct eeprom *hw = NULL; + + rrpriv = (struct rr_private *)dev->priv; + regs = rrpriv->regs; + + if (dev->flags & IFF_UP) + return -EBUSY; + + if (!(regs->HostCtrl & NIC_HALTED)){ + printk("%s: Trying to load firmware to a running NIC.\n", + dev->name); + return -EBUSY; + } + + localctrl = regs->LocalCtrl; + regs->LocalCtrl = 0; + + regs->EvtPrd = 0; + regs->RxPrd = 0; + regs->TxPrd = 0; + + /* + * First wipe the entire SRAM, otherwise we might run into all + * kinds of trouble ... sigh, this took almost all afternoon + * to track down ;-( + */ + io = regs->ExtIo; + regs->ExtIo = 0; + sram_size = read_eeprom_word(rrpriv, (void *)8); + + for (i = 200; i < sram_size / 4; i++){ + regs->WinBase = i * 4; + regs->WinData = 0; + } + regs->ExtIo = io; + + eptr = read_eeprom_word(rrpriv, &hw->rncd_info.AddrRunCodeSegs); + eptr = ((eptr & 0x1fffff) >> 3); + + p2len = read_eeprom_word(rrpriv, (void *)(0x83*4)); + p2len = (p2len << 2); + p2size = read_eeprom_word(rrpriv, (void *)(0x84*4)); + p2size = ((p2size & 0x1fffff) >> 3); + + if ((eptr < p2size) || (eptr > (p2size + p2len))){ + printk("%s: eptr is invalid\n", dev->name); + goto out; + } + + revision = read_eeprom_word(rrpriv, &hw->manf.HeaderFmt); + + if (revision != 1){ + printk("%s: invalid firmware format (%i)\n", + dev->name, revision); + goto out; + } + + nr_seg = read_eeprom_word(rrpriv, (void *)eptr); + eptr +=4; +#if (DEBUG > 1) + printk("%s: nr_seg %i\n", dev->name, nr_seg); +#endif + + for (i = 0; i < nr_seg; i++){ + sptr = read_eeprom_word(rrpriv, (void *)eptr); + eptr += 4; + len = read_eeprom_word(rrpriv, (void *)eptr); + eptr += 4; + segptr = read_eeprom_word(rrpriv, (void *)eptr); + segptr = ((segptr & 0x1fffff) >> 3); + eptr += 4; +#if (DEBUG > 1) + printk("%s: segment %i, sram address %06x, length %04x, segptr %06x\n", + dev->name, i, sptr, len, segptr); +#endif + for (j = 0; j < len; j++){ + tmp = read_eeprom_word(rrpriv, (void *)segptr); + regs->WinBase = sptr; + regs->WinData = tmp; + segptr += 4; + sptr += 4; + } + } + +out: + regs->LocalCtrl = localctrl; + return 0; +} + + +static int rr_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct rr_private *rrpriv; + + rrpriv = (struct rr_private *)dev->priv; + + switch(cmd){ + case SIOCRRPFW: + if (!suser()) + return -EPERM; + + if (rrpriv->fw_running){ + printk("%s: firmware already running\n", dev->name); + return -EPERM; + } + printk("%s: updating firmware", dev->name); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 -c rrunner.c" + * End: + */ diff -u --recursive --new-file v2.1.124/linux/drivers/net/rrunner.h linux/drivers/net/rrunner.h --- v2.1.124/linux/drivers/net/rrunner.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/rrunner.h Wed Oct 7 11:13:49 1998 @@ -0,0 +1,790 @@ +#ifndef _RRUNNER_H_ +#define _RRUNNER_H_ + +#include + +#if ((BITS_PER_LONG != 32) && (BITS_PER_LONG != 64)) +#error "BITS_PER_LONG not defined or not valid" +#endif + + +struct rr_regs { + + u32 pad0[16]; + + u32 HostCtrl; + u32 LocalCtrl; + u32 Pc; + u32 BrkPt; + +/* Timer increments every 0.97 micro-seconds (unsigned int) */ + u32 Timer_Hi; + u32 Timer; + u32 TimerRef; + u32 PciState; + + u32 Event; + u32 MbEvent; + + u32 WinBase; + u32 WinData; + u32 RX_state; + u32 TX_state; + + u32 Overhead; + u32 ExtIo; + + u32 DmaWriteHostHi; + u32 DmaWriteHostLo; + + u32 pad1[2]; + + u32 DmaReadHostHi; + u32 DmaReadHostLo; + + u32 pad2; + + u32 DmaReadLen; + u32 DmaWriteState; + + u32 DmaWriteLcl; + u32 DmaWriteIPchecksum; + u32 DmaWriteLen; + u32 DmaReadState; + u32 DmaReadLcl; + u32 DmaReadIPchecksum; + u32 pad3; + + u32 RxBase; + u32 RxPrd; + u32 RxCon; + + u32 pad4; + + u32 TxBase; + u32 TxPrd; + u32 TxCon; + + u32 pad5; + + u32 RxIndPro; + u32 RxIndCon; + u32 RxIndRef; + + u32 pad6; + + u32 TxIndPro; + u32 TxIndCon; + u32 TxIndRef; + + u32 pad7[17]; + + u32 DrCmndPro; + u32 DrCmndCon; + u32 DrCmndRef; + + u32 pad8; + + u32 DwCmndPro; + u32 DwCmndCon; + u32 DwCmndRef; + + u32 AssistState; + + u32 DrDataPro; + u32 DrDataCon; + u32 DrDataRef; + + u32 pad9; + + u32 DwDataPro; + u32 DwDataCon; + u32 DwDataRef; + + u32 pad10[33]; + + u32 EvtCon; + + u32 pad11[5]; + + u32 TxPi; + u32 IpRxPi; + + u32 pad11a[8]; + + u32 CmdRing[16]; + +/* The ULA is in two registers the high order two bytes of the first + * word contain the RunCode features. + * ula0 res res byte0 byte1 + * ula1 byte2 byte3 byte4 byte5 + */ + u32 Ula0; + u32 Ula1; + + u32 RxRingHi; + u32 RxRingLo; + + u32 InfoPtrHi; + u32 InfoPtrLo; + + u32 Mode; + + u32 ConRetry; + u32 ConRetryTmr; + + u32 ConTmout; + u32 CtatTmr; + + u32 MaxRxRng; + + u32 IntrTmr; + u32 TxDataMvTimeout; + u32 RxDataMvTimeout; + + u32 EvtPrd; + u32 TraceIdx; + + u32 Fail1; + u32 Fail2; + + u32 DrvPrm; + + u32 FilterLA; + + u32 FwRev; + u32 FwRes1; + u32 FwRes2; + u32 FwRes3; + + u32 WriteDmaThresh; + u32 ReadDmaThresh; + + u32 pad12[325]; + u32 Window[512]; +}; + +/* + * Host control register bits. + */ + +#define RR_INT 0x01 +#define RR_CLEAR_INT 0x02 +#define NO_SWAP 0x04000004 +#define NO_SWAP1 0x00000004 +#define PCI_RESET_NIC 0x08 +#define HALT_NIC 0x10 +#define SSTEP_NIC 0x20 +#define MEM_READ_MULTI 0x40 +#define NIC_HALTED 0x100 +#define HALT_INST 0x200 +#define PARITY_ERR 0x400 +#define INVALID_INST_B 0x800 +#define RR_REV_2 0x20000000 +#define RR_REV_MASK 0xf0000000 + +/* + * Local control register bits. + */ + +#define INTA_STATE 0x01 +#define CLEAR_INTA 0x02 +#define FAST_EEPROM_ACCESS 0x08 +#define ENABLE_EXTRA_SRAM 0x100 +#define ENABLE_EXTRA_DESC 0x200 +#define ENABLE_PARITY 0x400 +#define FORCE_DMA_PARITY_ERROR 0x800 +#define ENABLE_EEPROM_WRITE 0x1000 +#define ENABLE_DATA_CACHE 0x2000 +#define SRAM_LO_PARITY_ERR 0x4000 +#define SRAM_HI_PARITY_ERR 0x8000 + +/* + * PCI state bits. + */ + +#define FORCE_PCI_RESET 0x01 +#define PROVIDE_LENGTH 0x02 +#define MASK_DMA_READ_MAX 0x1C +#define RBURST_DISABLE 0x00 +#define RBURST_4 0x04 +#define RBURST_16 0x08 +#define RBURST_32 0x0C +#define RBURST_64 0x10 +#define RBURST_128 0x14 +#define RBURST_256 0x18 +#define RBURST_1024 0x1C +#define MASK_DMA_WRITE_MAX 0xE0 +#define WBURST_DISABLE 0x00 +#define WBURST_4 0x20 +#define WBURST_16 0x40 +#define WBURST_32 0x60 +#define WBURST_64 0x80 +#define WBURST_128 0xa0 +#define WBURST_256 0xc0 +#define WBURST_1024 0xe0 +#define MASK_MIN_DMA 0xFF00 +#define FIFO_RETRY_ENABLE 0x10000 + +/* + * Event register + */ + +#define DMA_WRITE_DONE 0x10000 +#define DMA_READ_DONE 0x20000 +#define DMA_WRITE_ERR 0x40000 +#define DMA_READ_ERR 0x80000 + +/* + * Receive state + * + * RoadRunner HIPPI Receive State Register controls and monitors the + * HIPPI receive interface in the NIC. Look at err bits when a HIPPI + * receive Error Event occurs. + */ + +#define ENABLE_NEW_CON 0x01 +#define RESET_RECV 0x02 +#define RECV_ALL 0x00 +#define RECV_1K 0x20 +#define RECV_2K 0x40 +#define RECV_4K 0x60 +#define RECV_8K 0x80 +#define RECV_16K 0xa0 +#define RECV_32K 0xc0 +#define RECV_64K 0xe0 + +/* + * Transmit status. + */ + +#define ENA_XMIT 0x01 +#define PERM_CON 0x02 + +/* + * DMA write state + */ + +#define RESET_DMA 0x01 +#define NO_SWAP_DMA 0x02 +#define DMA_ACTIVE 0x04 +#define THRESH_MASK 0x1F +#define DMA_ERROR_MASK 0xff000000 + +/* + * Gooddies stored in the ULA registers. + */ + +#define TRACE_ON_WHAT_BIT 0x00020000 /* Traces on */ +#define ONEM_BUF_WHAT_BIT 0x00040000 /* 1Meg vs 256K */ +#define CHAR_API_WHAT_BIT 0x00080000 /* Char API vs network only */ +#define MS_DOS_WHAT_BIT 0x00100000 /* MS_DOS */ +#define CMD_EVT_WHAT_BIT 0x00200000 /* Command event */ +#define LONG_TX_WHAT_BIT 0x00400000 +#define LONG_RX_WHAT_BIT 0x00800000 +#define WHAT_BIT_MASK 0xFFFD0000 /* Feature bit mask */ + +/* + * Mode status + */ + +#define EVENT_OVFL 0x80000000 +#define FATAL_ERR 0x40000000 +#define LOOP_BACK 0x01 +#define MODE_PH 0x02 +#define MODE_FP 0x00 +#define PTR64BIT 0x04 +#define PTR32BIT 0x00 +#define PTR_WD_SWAP 0x08 +#define PTR_WD_NOSWAP 0x00 +#define POST_WARN_EVENT 0x10 +#define ERR_TERM 0x20 +#define DIRECT_CONN 0x40 +#define NO_NIC_WATCHDOG 0x80 +#define SWAP_DATA 0x100 +#define SWAP_CONTROL 0x200 +#define NIC_HALT_ON_ERR 0x400 +#define NIC_NO_RESTART 0x800 +#define HALF_DUP_TX 0x1000 +#define HALF_DUP_RX 0x2000 + + +/* + * Error codes + */ + +/* Host Error Codes - values of fail1 */ +#define ERR_UNKNOWN_MBOX 0x1001 +#define ERR_UNKNOWN_CMD 0x1002 +#define ERR_MAX_RING 0x1003 +#define ERR_RING_CLOSED 0x1004 +#define ERR_RING_OPEN 0x1005 +/* Firmware internal errors */ +#define ERR_EVENT_RING_FULL 0x01 +#define ERR_DW_PEND_CMND_FULL 0x02 +#define ERR_DR_PEND_CMND_FULL 0x03 +#define ERR_DW_PEND_DATA_FULL 0x04 +#define ERR_DR_PEND_DATA_FULL 0x05 +#define ERR_ILLEGAL_JUMP 0x06 +#define ERR_UNIMPLEMENTED 0x07 +#define ERR_TX_INFO_FULL 0x08 +#define ERR_RX_INFO_FULL 0x09 +#define ERR_ILLEGAL_MODE 0x0A +#define ERR_MAIN_TIMEOUT 0x0B +#define ERR_EVENT_BITS 0x0C +#define ERR_UNPEND_FULL 0x0D +#define ERR_TIMER_QUEUE_FULL 0x0E +#define ERR_TIMER_QUEUE_EMPTY 0x0F +#define ERR_TIMER_NO_FREE 0x10 +#define ERR_INTR_START 0x11 +#define ERR_BAD_STARTUP 0x12 +#define ERR_NO_PKT_END 0x13 +#define ERR_HALTED_ON_ERR 0x14 +/* Hardware NIC Errors */ +#define ERR_WRITE_DMA 0x0101 +#define ERR_READ_DMA 0x0102 +#define ERR_EXT_SERIAL 0x0103 +#define ERR_TX_INT_PARITY 0x0104 + + +/* + * Event definitions + */ + +#define EVT_RING_ENTRIES 64 +#define EVT_RING_SIZE (EVT_RING_ENTRIES * sizeof(struct event)) + +struct event { +#ifdef __LITTLE_ENDIAN + u16 index; + u8 ring; + u8 code; +#else + u8 code; + u8 ring; + u16 index; +#endif + u32 timestamp; +}; + +/* + * General Events + */ + +#define E_NIC_UP 0x01 +#define E_WATCHDOG 0x02 + +#define E_STAT_UPD 0x04 +#define E_INVAL_CMD 0x05 +#define E_SET_CMD_CONS 0x06 +#define E_LINK_ON 0x07 +#define E_LINK_OFF 0x08 +#define E_INTERN_ERR 0x09 +#define E_HOST_ERR 0x0A +#define E_STATS_UPDATE 0x0B +#define E_REJECTING 0x0C + +/* + * Send Events + */ +#define E_CON_REJ 0x13 +#define E_CON_TMOUT 0x14 +#define E_CON_NC_TMOUT 0x15 /* I , Connection No Campon Timeout */ +#define E_DISC_ERR 0x16 +#define E_INT_PRTY 0x17 +#define E_TX_IDLE 0x18 +#define E_TX_LINK_DROP 0x19 +#define E_TX_INV_RNG 0x1A +#define E_TX_INV_BUF 0x1B +#define E_TX_INV_DSC 0x1C + +/* + * Destination Events + */ +/* + * General Receive events + */ +#define E_VAL_RNG 0x20 +#define E_RX_RNG_ENER 0x21 +#define E_INV_RNG 0x22 +#define E_RX_RNG_SPC 0x23 +#define E_RX_RNG_OUT 0x24 +#define E_PKT_DISCARD 0x25 +#define E_INFO_EVT 0x27 + +/* + * Data corrupted events + */ +#define E_RX_PAR_ERR 0x2B +#define E_RX_LLRC_ERR 0x2C +#define E_IP_CKSM_ERR 0x2D +#define E_DTA_CKSM_ERR 0x2E +#define E_SHT_BST 0x2F + +/* + * Data lost events + */ +#define E_LST_LNK_ERR 0x30 +#define E_FLG_SYN_ERR 0x31 +#define E_FRM_ERR 0x32 +#define E_RX_IDLE 0x33 +#define E_PKT_LN_ERR 0x34 +#define E_STATE_ERR 0x35 +#define E_UNEXP_DATA 0x3C + +/* + * Fatal events + */ +#define E_RX_INV_BUF 0x36 +#define E_RX_INV_DSC 0x37 +#define E_RNG_BLK 0x38 + +/* + * Warning events + */ +#define E_RX_TO 0x39 +#define E_BFR_SPC 0x3A +#define E_INV_ULP 0x3B + +#define E_NOT_IMPLEMENTED 0x40 + + +/* + * Commands + */ + +#define CMD_RING_ENTRIES 16 + +struct cmd { +#ifdef __LITTLE_ENDIAN + u16 index; + u8 ring; + u8 code; +#else + u8 code; + u8 ring; + u16 index; +#endif +}; + +#define C_START_FW 0x01 +#define C_UPD_STAT 0x02 +#define C_WATCHDOG 0x05 +#define C_DEL_RNG 0x09 +#define C_NEW_RNG 0x0A +#define C_CONN 0x0D + + +/* + * Mode bits + */ + +#define INTERRUPT 0x02 +#define TX_IP_CKSUM 0x04 +#define PACKET_END 0x08 +#define PACKET_START 0x10 +#define SAME_IFIELD 0x80 + + +/* + * TX ring + */ + +#ifdef CONFIG_ROADRUNNER_LARGE_RINGS +#define TX_RING_ENTRIES 32 +#else +#define TX_RING_ENTRIES 16 +#endif +#define TX_RING_SIZE (TX_RING_ENTRIES * sizeof(struct tx_desc)) + +struct tx_desc{ +#if (BITS_PER_LONG == 64) + u64 addr; +#else + u32 zero; + u32 addr; +#endif + u32 res; +#ifdef __LITTLE_ENDIAN + u16 size; + u8 pad; + u8 mode; +#else + u8 mode; + u8 pad; + u16 size; +#endif +}; + + +#ifdef CONFIG_ROADRUNNER_LARGE_RINGS +#define RX_RING_ENTRIES 32 +#else +#define RX_RING_ENTRIES 16 +#endif +#define RX_RING_SIZE (RX_RING_ENTRIES * sizeof(struct rx_desc)) + +struct rx_desc{ +#if (BITS_PER_LONG == 64) + u64 addr; +#else + u32 zero; + u32 addr; +#endif + u32 res; +#ifdef __LITTLE_ENDIAN + u16 size; + u8 pad; + u8 mode; +#else + u8 mode; + u8 pad; + u16 size; +#endif +}; + + +/* + * ioctl's + */ + +#define SIOCRRPFW SIOCDEVPRIVATE /* put firmware */ +#define SIOCRRGFW SIOCDEVPRIVATE+1 /* get firmware */ + + +struct seg_hdr { + u32 seg_start; + u32 seg_len; + u32 seg_eestart; +}; + + +#define EEPROM_BASE 0x80000000 + +struct eeprom_boot { + u32 key1; + u32 key2; + u32 sram_size; + struct seg_hdr loader; + u32 init_chksum; + u32 reserved1; +}; + +struct eeprom_manf { + u32 HeaderFmt; + u32 Firmware; + u32 BoardRevision; + u32 RoadrunnerRev; + char OpticsPart[8]; + u32 OpticsRev; + u32 pad1; + char SramPart[8]; + u32 SramRev; + u32 pad2; + char EepromPart[8]; + u32 EepromRev; + u32 EepromSize; + char PalPart[8]; + u32 PalRev; + u32 pad3; + char PalCodeFile[12]; + u32 PalCodeRev; + char BoardULA[8]; + char SerialNo[8]; + char MfgDate[8]; + char MfgTime[8]; + char ModifyDate[8]; + u32 ModCount; + u32 pad4[13]; +}; + + +struct eeprom_phase_info { + char phase1File[12]; + u32 phase1Rev; + char phase1Date[8]; + char phase2File[12]; + u32 phase2Rev; + char phase2Date[8]; + u32 reserved7[4]; +}; + +struct eeprom_rncd_info { + u32 FwStart; + u32 FwRev; + char FwDate[8]; + u32 AddrRunCodeSegs; + u32 FileNames; + char File[13][8]; +}; + + +/* Phase 1 region (starts are word offset 0x80) */ +struct phase1_hdr{ + u32 jump; + u32 noop; + struct seg_hdr phase2Seg; +}; + +struct eeprom { + struct eeprom_boot boot; + u32 pad1[8]; + struct eeprom_manf manf; + struct eeprom_phase_info phase_info; + struct eeprom_rncd_info rncd_info; + u32 pad2[15]; + u32 hdr_checksum; + struct phase1_hdr phase1; +}; + + +struct rr_stats { + u32 NicTimeStamp; + u32 RngCreated; + u32 RngDeleted; + u32 IntrGen; + u32 NEvtOvfl; + u32 InvCmd; + u32 DmaReadErrs; + u32 DmaWriteErrs; + u32 StatUpdtT; + u32 StatUpdtC; + u32 WatchDog; + u32 Trace; + + /* Serial HIPPI */ + u32 LnkRdyEst; + u32 GLinkErr; + u32 AltFlgErr; + u32 OvhdBit8Sync; + u32 RmtSerPrtyErr; + u32 RmtParPrtyErr; + u32 RmtLoopBk; + u32 pad1; + + /* HIPPI tx */ + u32 ConEst; + u32 ConRejS; + u32 ConRetry; + u32 ConTmOut; + u32 SndConDiscon; + u32 SndParErr; + u32 PktSnt; + u32 pad2[2]; + u32 ShFBstSnt; + u64 BytSent; + u32 TxTimeout; + u32 pad3[3]; + + /* HIPPI rx */ + u32 ConAcc; + u32 ConRejdiPrty; + u32 ConRejd64b; + u32 ConRejdBuf; + u32 RxConDiscon; + u32 RxConNoData; + u32 PktRx; + u32 pad4[2]; + u32 ShFBstRx; + u64 BytRx; + u32 RxParErr; + u32 RxLLRCerr; + u32 RxBstSZerr; + u32 RxStateErr; + u32 RxRdyErr; + u32 RxInvULP; + u32 RxSpcBuf; + u32 RxSpcDesc; + u32 RxRngSpc; + u32 RxRngFull; + u32 RxPktLenErr; + u32 RxCksmErr; + u32 RxPktDrp; + u32 RngLowSpc; + u32 RngDataClose; + u32 RxTimeout; + u32 RxIdle; +}; + + +/* + * This struct is shared with the NIC firmware. + */ +struct ring_ctrl { +#if (BITS_PER_LONG == 64) + u64 rngptr; +#else + u32 zero; + u32 rngptr; +#endif +#ifdef __LITTLE_ENDIAN + u16 entries; + u8 pad; + u8 entry_size; + u16 pi; + u16 mode; +#else + u8 entry_size; + u8 pad; + u16 entries; + u16 mode; + u16 pi; +#endif +}; + +struct rr_info { + union { + struct rr_stats stats; + u32 stati[128]; + } s; + struct ring_ctrl evt_ctrl; + struct ring_ctrl cmd_ctrl; + struct ring_ctrl tx_ctrl; + u8 pad[464]; + u8 trace[3072]; +}; + +/* + * The linux structure for the RoadRunner. + * + * RX/TX descriptors are put first to make sure they are properly + * aligned and do not cross cache-line boundaries. + */ + +struct rr_private +{ + struct rx_desc rx_ring[RX_RING_ENTRIES]; + struct tx_desc tx_ring[TX_RING_ENTRIES]; + struct event evt_ring[EVT_RING_ENTRIES]; + struct sk_buff *tx_skbuff[TX_RING_ENTRIES]; + struct sk_buff *rx_skbuff[RX_RING_ENTRIES]; + struct rr_regs *regs; /* Register base */ + struct ring_ctrl *rx_ctrl; /* Receive ring control */ + struct rr_info *info; /* Shared info page */ + spinlock_t lock; + struct timer_list timer; + u32 cur_rx, cur_cmd, cur_evt; + u32 dirty_rx, dirty_tx; + u32 tx_full; + short fw_running; + u8 pci_bus; /* PCI bus number */ + u8 pci_dev_fun; /* PCI device numbers */ + char name[24]; /* The assigned name */ + struct net_device_stats stats; +}; + + +/* + * Prototypes + */ +static int rr_init(struct device *dev); +static int rr_init1(struct device *dev); +static void rr_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static int rr_open(struct device *dev); +static int rr_start_xmit(struct sk_buff *skb, struct device *dev); +static int rr_close(struct device *dev); +static struct net_device_stats *rr_get_stats(struct device *dev); +static int rr_ioctl(struct device *dev, struct ifreq *rq, int cmd); + +#endif /* _RRUNNER_H_ */ diff -u --recursive --new-file v2.1.124/linux/drivers/net/sdla_x25.c linux/drivers/net/sdla_x25.c --- v2.1.124/linux/drivers/net/sdla_x25.c Sat Sep 5 16:46:40 1998 +++ linux/drivers/net/sdla_x25.c Tue Oct 6 09:39:49 1998 @@ -830,7 +830,7 @@ { x25_channel_t* chan = dev->priv; if(chan==NULL) - return chan; + return NULL; return &chan->ifstats; } diff -u --recursive --new-file v2.1.124/linux/drivers/net/strip.c linux/drivers/net/strip.c --- v2.1.124/linux/drivers/net/strip.c Wed Sep 9 14:51:08 1998 +++ linux/drivers/net/strip.c Wed Oct 7 15:51:45 1998 @@ -14,7 +14,7 @@ * for kernel-based devices like TTY. It interfaces between a * raw TTY, and the kernel's INET protocol layers (via DDI). * - * Version: @(#)strip.c 1.2 February 1997 + * Version: @(#)strip.c 1.3 July 1997 * * Author: Stuart Cheshire * @@ -60,12 +60,18 @@ * * v1.2 January 1997 (SC) * Put portables list back in + * + * v1.3 July 1997 (SC) + * Made STRIP driver set the radio's baud rate automatically. + * It is no longer necessarily to manually set the radio's + * rate permanently to 115200 -- the driver handles setting + * the rate automatically. */ #ifdef MODULE -static const char StripVersion[] = "1.2-STUART.CHESHIRE-MODULAR"; +static const char StripVersion[] = "1.3-STUART.CHESHIRE-MODULAR"; #else -static const char StripVersion[] = "1.2-STUART.CHESHIRE"; +static const char StripVersion[] = "1.3-STUART.CHESHIRE"; #endif #define TICKLE_TIMERS 0 @@ -84,6 +90,7 @@ #include #include +#include #include /* @@ -111,13 +118,12 @@ #include #include #include +#include #include -#ifdef CONFIG_INET #include #include #include -#endif /************************************************************************/ @@ -285,6 +291,7 @@ int working; /* Is radio working correctly? */ int firmware_level; /* Message structuring level */ int next_command; /* Next periodic command */ + unsigned int user_baud; /* The user-selected baud rate */ int mtu; /* Our mtu (to spot changes!) */ long watchdog_doprobe; /* Next time to test the radio */ long watchdog_doreset; /* Time to do next reset */ @@ -459,7 +466,7 @@ /* Macros */ /* Returns TRUE if text T begins with prefix P */ -#define has_prefix(T,P) (!strncmp((T), (P), sizeof(P)-1)) +#define has_prefix(T,L,P) (((L) >= sizeof(P)-1) && !strncmp((T), (P), sizeof(P)-1)) /* Returns TRUE if text T of length L is equal to string S */ #define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1)) @@ -498,6 +505,24 @@ restore_flags(x); } +static int arp_query(unsigned char *haddr, u32 paddr, struct device * dev) +{ + struct neighbour *neighbor_entry; + + neighbor_entry = neigh_lookup(&arp_tbl, &paddr, dev); + + if (neighbor_entry != NULL) + { + neighbor_entry->used = jiffies; + if (neighbor_entry->nud_state & NUD_VALID) + { + memcpy(haddr, neighbor_entry->ha, dev->addr_len); + return 1; + } + } + return 0; +} + static void DumpData(char *msg, struct strip *strip_info, __u8 *ptr, __u8 *end) { static const int MAX_DumpData = 80; @@ -834,6 +859,41 @@ /* General routines for STRIP */ /* + * get_baud returns the current baud rate, as one of the constants defined in + * termbits.h + * If the user has issued a baud rate override using the 'setserial' command + * and the logical current rate is set to 38.4, then the true baud rate + * currently in effect (57.6 or 115.2) is returned. + */ +static unsigned int get_baud(struct tty_struct *tty) + { + if (!tty || !tty->termios) return(0); + if ((tty->termios->c_cflag & CBAUD) == B38400 && tty->driver_data) + { + struct async_struct *info = (struct async_struct *)tty->driver_data; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI ) return(B57600); + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) return(B115200); + } + return(tty->termios->c_cflag & CBAUD); + } + +/* + * set_baud sets the baud rate to the rate defined by baudcode + * Note: The rate B38400 should be avoided, because the user may have + * issued a 'setserial' speed override to map that to a different speed. + * We could achieve a true rate of 38400 if we needed to by cancelling + * any user speed override that is in place, but that might annoy the + * user, so it is simplest to just avoid using 38400. + */ +static void set_baud(struct tty_struct *tty, unsigned int baudcode) + { + struct termios old_termios = *(tty->termios); + tty->termios->c_cflag &= ~CBAUD; /* Clear the old baud setting */ + tty->termios->c_cflag |= baudcode; /* Set the new baud setting */ + tty->driver.set_termios(tty, &old_termios); + } + +/* * Convert a string to a Metricom Address. */ @@ -971,9 +1031,9 @@ static void strip_unlock(struct strip *strip_info) { /* - * Set the time to go off in one second. + * Set the timer to go off in one second. */ - strip_info->idle_timer.expires = jiffies + 1*HZ; + strip_info->idle_timer.expires = jiffies + 1*HZ; add_timer(&strip_info->idle_timer); if (!test_and_clear_bit(0, (void *)&strip_info->dev.tbusy)) printk(KERN_ERR "%s: trying to unlock already unlocked device!\n", @@ -1229,13 +1289,16 @@ /************************************************************************/ /* Sending routines */ -#define InitString "ate0q1dt**starmode" - static void ResetRadio(struct strip *strip_info) { - static const char s[] = "\r" InitString "\r**"; - - /* If the radio isn't working anymore, we should clear the old status information. */ + struct tty_struct *tty = strip_info->tty; + static const char init[] = "ate0q1dt**starmode\r**"; + StringDescriptor s = { init, sizeof(init)-1 }; + + /* + * If the radio isn't working anymore, + * we should clear the old status information. + */ if (strip_info->working) { printk(KERN_INFO "%s: No response: Resetting radio.\n", strip_info->dev.name); @@ -1258,15 +1321,43 @@ /* Mark radio address as unknown */ *(MetricomAddress*)&strip_info->true_dev_addr = zero_address; - if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = zero_address; + if (!strip_info->manual_dev_addr) + *(MetricomAddress*)strip_info->dev.dev_addr = zero_address; strip_info->working = FALSE; strip_info->firmware_level = NoStructure; strip_info->next_command = CompatibilityCommand; strip_info->watchdog_doprobe = jiffies + 10 * HZ; strip_info->watchdog_doreset = jiffies + 1 * HZ; - strip_info->tty->driver.write(strip_info->tty, 0, (char *)s, sizeof(s)-1); + + /* If the user has selected a baud rate above 38.4 see what magic we have to do */ + if (strip_info->user_baud > B38400) + { + /* + * Subtle stuff: Pay attention :-) + * If the serial port is currently at the user's selected (>38.4) rate, + * then we temporarily switch to 19.2 and issue the ATS304 command + * to tell the radio to switch to the user's selected rate. + * If the serial port is not currently at that rate, that means we just + * issued the ATS304 command last time through, so this time we restore + * the user's selected rate and issue the normal starmode reset string. + */ + if (strip_info->user_baud == get_baud(tty)) + { + static const char b0[] = "ate0q1s304=57600\r"; + static const char b1[] = "ate0q1s304=115200\r"; + static const StringDescriptor baudstring[2] = + { { b0, sizeof(b0)-1 }, { b1, sizeof(b1)-1 } }; + set_baud(tty, B19200); + if (strip_info->user_baud == B57600 ) s = baudstring[0]; + else if (strip_info->user_baud == B115200) s = baudstring[1]; + else s = baudstring[1]; /* For now */ + } + else set_baud(tty, strip_info->user_baud); + } + + tty->driver.write(tty, 0, s.string, s.length); #ifdef EXT_COUNTERS - strip_info->tx_ebytes += sizeof(s) - 1; + strip_info->tx_ebytes += s.length; #endif } @@ -1356,25 +1447,37 @@ } /* + * If we're sending to ourselves, discard the packet. + * (Metricom radios choke if they try to send a packet to their own address.) + */ + if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) + { + printk(KERN_ERR "%s: Dropping packet addressed to self\n", strip_info->dev.name); + return(NULL); + } + + /* * If this is a broadcast packet, send it to our designated Metricom * 'broadcast hub' radio (First byte of address being 0xFF means broadcast) */ if (haddr.c[0] == 0xFF) { - memcpy(haddr.c, strip_info->dev.broadcast, sizeof(haddr)); - if (haddr.c[0] == 0xFF) - { - strip_info->tx_dropped++; - return(NULL); - } + struct in_device *in_dev = strip_info->dev.ip_ptr; + /* arp_query returns 1 if it succeeds in looking up the address, 0 if it fails */ + if (!arp_query(haddr.c, in_dev->ifa_list->ifa_broadcast, &strip_info->dev)) + { + printk(KERN_ERR "%s: Unable to send packet (no broadcast hub configured)\n", + strip_info->dev.name); + return(NULL); + } + /* + * If we are the broadcast hub, don't bother sending to ourselves. + * (Metricom radios choke if they try to send a packet to their own address.) + */ + if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) return(NULL); } - /* - * If we're sending to ourselves, discard the packet. - * (Metricom radios choke if they try to send a packet to their own address.) - */ - if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) - return(NULL); + *ptr++ = 0x0D; *ptr++ = '*'; *ptr++ = hextable[haddr.c[2] >> 4]; *ptr++ = hextable[haddr.c[2] & 0xF]; @@ -1401,9 +1504,11 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb) { + MetricomAddress haddr; unsigned char *ptr = strip_info->tx_buff; int doreset = (long)jiffies - strip_info->watchdog_doreset >= 0; int doprobe = (long)jiffies - strip_info->watchdog_doprobe >= 0 && !doreset; + struct in_device *in_dev = strip_info->dev.ip_ptr; /* * 1. If we have a packet, encapsulate it and put it in the buffer @@ -1503,9 +1608,8 @@ */ if (strip_info->working && (long)jiffies - strip_info->gratuitous_arp >= 0 && memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) && - *strip_info->dev.broadcast!=0xFF) + arp_query(haddr.c, in_dev->ifa_list->ifa_broadcast, &strip_info->dev)) { - struct in_device *in_dev = strip_info->dev.ip_ptr; /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n", strip_info->dev.name, strip_info->arp_interval / HZ);*/ strip_info->gratuitous_arp = jiffies + strip_info->arp_interval; @@ -1513,13 +1617,14 @@ if (strip_info->arp_interval > MaxARPInterval) strip_info->arp_interval = MaxARPInterval; if (in_dev && in_dev->ifa_list) - arp_send(ARPOP_REPLY, ETH_P_ARP, - in_dev->ifa_list->ifa_address,/* Target address of ARP packet is our address */ - &strip_info->dev, /* Device to send packet on */ - in_dev->ifa_list->ifa_address,/* Source IP address this ARP packet comes from */ - NULL, /* Destination HW address is NULL (broadcast it) */ - strip_info->dev.dev_addr, /* Source HW address is our HW address */ - strip_info->dev.dev_addr); /* Target HW address is our HW address (redundant) */ + arp_send( + ARPOP_REPLY, ETH_P_ARP, + in_dev->ifa_list->ifa_address, /* Target address of ARP packet is our address */ + &strip_info->dev, /* Device to send packet on */ + in_dev->ifa_list->ifa_address, /* Source IP address this ARP packet comes from */ + NULL, /* Destination HW address is NULL (broadcast it) */ + strip_info->dev.dev_addr, /* Source HW address is our HW address */ + strip_info->dev.dev_addr); /* Target HW address is our HW address (redundant) */ } /* @@ -1631,7 +1736,7 @@ static int strip_rebuild_header(struct sk_buff *skb) { #ifdef CONFIG_INET - STRIP_Header *header = (STRIP_Header *)skb->data; + STRIP_Header *header = (STRIP_Header *) skb->data; /* Arp find returns zero if if knows the address, */ /* or if it doesn't know the address it sends an ARP packet and returns non-zero */ @@ -1775,28 +1880,28 @@ strip_info->rx_errors++; } -static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg) +static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg, u_long len) { - if (has_prefix(msg, "001")) /* Not in StarMode! */ + if (has_prefix(msg, len, "001")) /* Not in StarMode! */ { RecvErr("Error Msg:", strip_info); printk(KERN_INFO "%s: Radio %s is not in StarMode\n", strip_info->dev.name, sendername); } - else if (has_prefix(msg, "002")) /* Remap handle */ + else if (has_prefix(msg, len, "002")) /* Remap handle */ { /* We ignore "Remap handle" messages for now */ } - else if (has_prefix(msg, "003")) /* Can't resolve name */ + else if (has_prefix(msg, len, "003")) /* Can't resolve name */ { RecvErr("Error Msg:", strip_info); printk(KERN_INFO "%s: Destination radio name is unknown\n", strip_info->dev.name); } - else if (has_prefix(msg, "004")) /* Name too small or missing */ + else if (has_prefix(msg, len, "004")) /* Name too small or missing */ { strip_info->watchdog_doreset = jiffies + LongTime; #if TICKLE_TIMERS @@ -1825,45 +1930,50 @@ } if (strip_info->firmware_level >= StructuredMessages) { + /* + * If this message has a valid checksum on the end, then the call to verify_checksum + * will elevate the firmware_level to ChecksummedMessages for us. (The actual return + * code from verify_checksum is ignored here.) + */ verify_checksum(strip_info); /* - * If the radio has structured messages but we don't yet have all our information about it, we should do - * probes without delay, until we have gathered all the information + * If the radio has structured messages but we don't yet have all our information about it, + * we should do probes without delay, until we have gathered all the information */ if (!GOT_ALL_RADIO_INFO(strip_info)) strip_info->watchdog_doprobe = jiffies; } } - else if (has_prefix(msg, "005")) /* Bad count specification */ + else if (has_prefix(msg, len, "005")) /* Bad count specification */ RecvErr("Error Msg:", strip_info); - else if (has_prefix(msg, "006")) /* Header too big */ + else if (has_prefix(msg, len, "006")) /* Header too big */ RecvErr("Error Msg:", strip_info); - else if (has_prefix(msg, "007")) /* Body too big */ + else if (has_prefix(msg, len, "007")) /* Body too big */ { RecvErr("Error Msg:", strip_info); printk(KERN_ERR "%s: Error! Packet size too big for radio.\n", strip_info->dev.name); } - else if (has_prefix(msg, "008")) /* Bad character in name */ + else if (has_prefix(msg, len, "008")) /* Bad character in name */ { RecvErr("Error Msg:", strip_info); printk(KERN_ERR "%s: Radio name contains illegal character\n", strip_info->dev.name); } - else if (has_prefix(msg, "009")) /* No count or line terminator */ + else if (has_prefix(msg, len, "009")) /* No count or line terminator */ RecvErr("Error Msg:", strip_info); - else if (has_prefix(msg, "010")) /* Invalid checksum */ + else if (has_prefix(msg, len, "010")) /* Invalid checksum */ RecvErr("Error Msg:", strip_info); - else if (has_prefix(msg, "011")) /* Checksum didn't match */ + else if (has_prefix(msg, len, "011")) /* Checksum didn't match */ RecvErr("Error Msg:", strip_info); - else if (has_prefix(msg, "012")) /* Failed to transmit packet */ + else if (has_prefix(msg, len, "012")) /* Failed to transmit packet */ RecvErr("Error Msg:", strip_info); else @@ -1872,9 +1982,11 @@ static void process_AT_response(struct strip *strip_info, __u8 *ptr, __u8 *end) { + u_long len; __u8 *p = ptr; while (p < end && p[-1] != 10) p++; /* Skip past first newline character */ /* Now ptr points to the AT command, and p points to the text of the response. */ + len = p-ptr; #if TICKLE_TIMERS { @@ -1885,13 +1997,13 @@ } #endif - if (has_prefix(ptr, "ATS300?" )) get_radio_version(strip_info, p, end); - else if (has_prefix(ptr, "ATS305?" )) get_radio_address(strip_info, p); - else if (has_prefix(ptr, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end); - else if (has_prefix(ptr, "ATS319=7")) verify_checksum(strip_info); - else if (has_prefix(ptr, "ATS325?" )) get_radio_voltage(strip_info, p, end); - else if (has_prefix(ptr, "AT~LA" )) get_radio_neighbours(&strip_info->portables, p, end); - else RecvErr("Unknown AT Response:", strip_info); + if (has_prefix(ptr, len, "ATS300?" )) get_radio_version(strip_info, p, end); + else if (has_prefix(ptr, len, "ATS305?" )) get_radio_address(strip_info, p); + else if (has_prefix(ptr, len, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end); + else if (has_prefix(ptr, len, "ATS319=7")) verify_checksum(strip_info); + else if (has_prefix(ptr, len, "ATS325?" )) get_radio_voltage(strip_info, p, end); + else if (has_prefix(ptr, len, "AT~LA" )) get_radio_neighbours(&strip_info->portables, p, end); + else RecvErr("Unknown AT Response:", strip_info); } static void process_ACK(struct strip *strip_info, __u8 *ptr, __u8 *end) @@ -2072,11 +2184,11 @@ if (text_equal(msg, len, "OK" )) return; /* Ignore 'OK' responses from prior commands */ if (text_equal(msg, len, "ERROR" )) return; /* Ignore 'ERROR' messages */ - if (text_equal(msg, len, InitString)) return; /* Ignore character echo back from the radio */ + if (has_prefix(msg, len, "ate0q1" )) return; /* Ignore character echo back from the radio */ /* Catch other error messages */ /* (This is here for backwards compatibility with old firmware) */ - if (has_prefix(msg, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4]); return; } + if (has_prefix(msg, len, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4], len-4); return; } RecvErr("No initial *", strip_info); } @@ -2181,7 +2293,7 @@ process_Info(strip_info, ptr, end); } else if (key.l == ERR_Key.l) { strip_info->rx_ebytes += (end - ptr); - RecvErr_Message(strip_info, sendername, ptr); + RecvErr_Message(strip_info, sendername, ptr, end-ptr); } else RecvErr("Unrecognized protocol key", strip_info); #else if (key.l == SIP0Key.l) process_IP_packet (strip_info, &header, ptr, end); @@ -2189,7 +2301,7 @@ else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end); else if (key.l == ACK_Key.l) process_ACK (strip_info, ptr, end); else if (key.l == INF_Key.l) process_Info (strip_info, ptr, end); - else if (key.l == ERR_Key.l) RecvErr_Message (strip_info, sendername, ptr); + else if (key.l == ERR_Key.l) RecvErr_Message (strip_info, sendername, ptr, end-ptr); else RecvErr("Unrecognized protocol key", strip_info); #endif } @@ -2305,12 +2417,12 @@ return 0; } -static struct net_device_stats *strip_get_stats(struct device *dev) +static struct enet_statistics *strip_get_stats(struct device *dev) { - static struct net_device_stats stats; + static struct enet_statistics stats; struct strip *strip_info = (struct strip *)(dev->priv); - memset(&stats, 0, sizeof(struct net_device_stats)); + memset(&stats, 0, sizeof(struct enet_statistics)); stats.rx_packets = strip_info->rx_packets; stats.tx_packets = strip_info->tx_packets; @@ -2353,6 +2465,7 @@ static int strip_open_low(struct device *dev) { struct strip *strip_info = (struct strip *)(dev->priv); + struct in_device *in_dev = dev->ip_ptr; if (strip_info->tty == NULL) return(-ENODEV); @@ -2360,19 +2473,27 @@ if (!allocate_buffers(strip_info)) return(-ENOMEM); + strip_info->sx_count = 0; + strip_info->tx_left = 0; + strip_info->discard = 0; strip_info->working = FALSE; strip_info->firmware_level = NoStructure; strip_info->next_command = CompatibilityCommand; - strip_info->sx_count = 0; - strip_info->tx_left = 0; + strip_info->user_baud = get_baud(strip_info->tty); + /* + * Needed because address '0' is special + */ + + if (in_dev->ifa_list->ifa_address == 0) + in_dev->ifa_list->ifa_address = ntohl(0xC0A80001); dev->tbusy = 0; dev->start = 1; printk(KERN_INFO "%s: Initializing Radio.\n", strip_info->dev.name); ResetRadio(strip_info); - strip_info->idle_timer.expires = jiffies + 2 * HZ; + strip_info->idle_timer.expires = jiffies + 1*HZ; add_timer(&strip_info->idle_timer); return(0); } @@ -2442,12 +2563,6 @@ dev->addr_len = sizeof(MetricomAddress); /* - * Pointer to the interface buffers. - */ - - dev_init_buffers(dev); - - /* * Pointers to interface service routines. */ @@ -2628,6 +2743,7 @@ if (!strip_info || strip_info->magic != STRIP_MAGIC) return; + dev_close(&strip_info->dev); unregister_netdev(&strip_info->dev); tty->disc_data = 0; @@ -2648,7 +2764,6 @@ unsigned int cmd, unsigned long arg) { struct strip *strip_info = (struct strip *) tty->disc_data; - int err; /* * First make sure we're connected. @@ -2660,21 +2775,18 @@ switch(cmd) { case SIOCGIFNAME: - err = verify_area(VERIFY_WRITE, (void*)arg, 16); - if (err) - return -err; - return copy_to_user((void*)arg, strip_info->dev.name, - strlen(strip_info->dev.name) + 1)?-EFAULT:0; - + return copy_to_user((void*)arg, strip_info->dev.name, + strlen(strip_info->dev.name) + 1) ? + -EFAULT : 0; + break; case SIOCSIFHWADDR: { MetricomAddress addr; printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev.name); - if(copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress))) - return -EFAULT; - return(set_mac_address(strip_info, &addr)); - } - + return copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress)) ? + -EFAULT : set_mac_address(strip_info, &addr); + break; + } /* * Allow stty to read, but not set, the serial port */ @@ -2683,9 +2795,10 @@ case TCGETA: return n_tty_ioctl(tty, (struct file *) file, cmd, (unsigned long) arg); - + break; default: return -ENOIOCTLCMD; + break; } } diff -u --recursive --new-file v2.1.124/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c --- v2.1.124/linux/drivers/net/via-rhine.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/via-rhine.c Wed Oct 7 15:51:45 1998 @@ -0,0 +1,1314 @@ +/* via-rhine.c: A Linux Ethernet device driver for VIA Rhine family chips. */ +/* + Written 1998 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + Drivers derived from this code also fall under the GPL and must retain + this authorship and copyright notice. + + This driver is designed for the VIA VT86c100A Rhine-II PCI Fast Ethernet + controller. It also works with the older 3043 Rhine-I chip. + + The author may be reached as becker@cesdis.edu, or + Donald Becker + 312 Severn Ave. #W302 + Annapolis MD 21403 + + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html +*/ + +static const char *versionA = +"via-rhine.c:v1.00 9/5/98 Written by Donald Becker\n"; +static const char *versionB = +" http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n"; + +/* A few user-configurable values. These may be modified when a driver + module is loaded.*/ + +static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ +static int max_interrupt_work = 20; +static int min_pci_latency = 64; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. + Both 'options[]' and 'full_duplex[]' should exist for driver + interoperability. + The media type is usually passed in 'options[]'. +*/ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). + The Rhine has a 64 element 8390-like hash table. */ +static const int multicast_filter_limit = 32; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. + The compiler will convert '%'<2^N> into a bit mask. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 8 +#define RX_RING_SIZE 16 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include + +/* This driver was written to use PCI memory space, however some boards + only work with I/O space accesses. */ +#define VIA_USE_IO +#ifdef VIA_USE_IO +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Kernel compatibility defines, some common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ + +#define RUN_AT(x) (jiffies + (x)) + +#if (LINUX_VERSION_CODE >= 0x20100) +char kernel_version[] = UTS_RELEASE; +#else +#ifndef __alpha__ +#define ioremap vremap +#define iounmap vfree +#endif +#endif +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(min_pci_latency, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +#if LINUX_VERSION_CODE < 0x20123 +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#else +#define NETSTATS_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) +/* Grrrr, the PCI code changed, but did not consider CardBus... */ +#include +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif + + +/* + Theory of Operation + +I. Board Compatibility + +This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet +controller. + +II. Board-specific settings + +Boards with this chip are functional only in a bus-master PCI slot. + +Many operational settings are loaded from the EEPROM to the Config word at +offset 0x78. This driver assumes that they are correct. +If this driver is compiled to use PCI memory space operations the EEPROM +must be configured to enable memory ops. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. + +IIIb/c. Transmit/Receive Structure + +This driver attempts to use a zero-copy receive and transmit scheme. + +Alas, all data buffers are required to start on a 32 bit boundary, so +the driver must often copy transmit packets into bounce buffers. + +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers. When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack. Buffers consumed this way are replaced by newly allocated +skbuffs in the last phase of netdev_rx(). + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets. When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine. Copying also preloads the cache, which is +most useful with small frames. + +Since the VIA chips are only able to transfer data to buffers on 32 bit +boundaries, the the IP header at offset 14 in an ethernet frame isn't +longword aligned for further processing. Copying these unaligned buffers +has the beneficial effect of 16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +IVb. References + +Preliminary VT86C100A manual from http://www.via.com.tw/ +http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html + +IVc. Errata + +The VT86C100A manual is not reliable information. +The chip does not handle unaligned transmit or receive buffers, resulting +in significant performance degradation for bounce buffer copies on transmit +and unaligned IP headers on receive. +The chip does not pad to minimum transmit length. + +*/ + + + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; + +static struct device *via_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, int irq, + int chp_idx, int fnd_cnt); + +static struct pci_id_info pci_tbl[] = { + { "VIA VT86C100A Rhine-II", 0x1106, 0x6100, 0xffff, + PCI_USES_MEM|PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1}, + { "VIA VT3043 Rhine", 0x1106, 0x3043, 0xffff, + PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1}, + {0,}, /* 0 terminated list. */ +}; + + +/* A chip capabilities table, matching the entries in pci_tbl[] above. */ +enum chip_capability_flags {CanHaveMII=1, }; +struct chip_info { + int io_size; + int flags; +} static cap_tbl[] = { + {128, CanHaveMII, }, + {128, CanHaveMII, }, +}; + + +/* Offsets to the device registers. +*/ +enum register_offsets { + StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, + IntrStatus=0x0C, IntrEnable=0x0E, + MulticastFilter0=0x10, MulticastFilter1=0x14, + RxRingPtr=0x18, TxRingPtr=0x1C, + MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIConfig=0x6E, + MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, + Config=0x78, RxMissed=0x7C, RxCRCErrs=0x7E, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { + IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020, + IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010, + IntrPCIErr=0x0040, + IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200, + IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, + IntrTxAborted=0x2000, IntrLinkChange=0x4000, + IntrRxWakeUp=0x8000, + IntrNormalSummary=0x0003, IntrAbnormalSummary=0x8260, +}; + + +/* The Rx and Tx buffer descriptors. */ +struct rx_desc { + u16 rx_status; + u16 rx_length; + u32 desc_length; + u32 addr; + u32 next_desc; +}; +struct tx_desc { + u16 tx_status; + u16 tx_own; + u32 desc_length; + u32 addr; + u32 next_desc; +}; + +/* Bits in *_desc.status */ +enum rx_status_bits { + RxDescOwn=0x80000000, RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F}; +enum desc_status_bits { + DescOwn=0x8000, DescEndPacket=0x4000, DescIntr=0x1000, +}; + +/* Bits in ChipCmd. */ +enum chip_cmd_bits { + CmdInit=0x0001, CmdStart=0x0002, CmdStop=0x0004, CmdRxOn=0x0008, + CmdTxOn=0x0010, CmdTxDemand=0x0020, CmdRxDemand=0x0040, + CmdEarlyRx=0x0100, CmdEarlyTx=0x0200, CmdFDuplex=0x0400, + CmdNoTxPoll=0x0800, CmdReset=0x8000, +}; + +struct netdev_private { + /* Descriptor rings first for alignment. */ + struct rx_desc rx_ring[RX_RING_SIZE]; + struct tx_desc tx_ring[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for later free(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + unsigned char *tx_buf[TX_RING_SIZE]; /* Tx bounce buffers */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + struct device *next_module; /* Link for devices of this type. */ + struct net_device_stats stats; + struct timer_list timer; /* Media monitoring timer. */ + unsigned char pci_bus, pci_devfn; + /* Frequently used values: keep some adjacent for cache effect. */ + int chip_id; + long in_interrupt; /* Word-long for SMP locks. */ + struct rx_desc *rx_head_desc; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ + unsigned int cur_tx, dirty_tx; + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + u16 chip_cmd; /* Current setting for ChipCmd */ + unsigned int tx_full:1; /* The Tx queue is full. */ + /* These values are keep track of the transceiver/media in use. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int duplex_lock:1; + unsigned int medialock:1; /* Do not sense media. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + u8 tx_thresh, rx_thresh; + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + u16 advertising; /* NWay media advertisement */ + unsigned char phys[2]; /* MII device addresses. */ +}; + +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int value); +static int netdev_open(struct device *dev); +static void check_duplex(struct device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct device *dev); +static void init_ring(struct device *dev); +static int start_tx(struct sk_buff *skb, struct device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static int netdev_rx(struct device *dev); +static void netdev_error(struct device *dev, int intr_status); +static void set_rx_mode(struct device *dev); +static struct net_device_stats *get_stats(struct device *dev); +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); +static int netdev_close(struct device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct device *root_net_dev = NULL; + +/* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well when dynamically adding drivers. So instead we detect just the + cards we know about in slot order. */ + +static int pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[]) +{ + int cards_found = 0; + int pci_index = 0; + unsigned char pci_bus, pci_device_fn; + + if ( ! pcibios_present()) + return -ENODEV; + + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + int chip_idx, irq; + long pciaddr; + long ioaddr; + + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, + &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) + break; + if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ + continue; + + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); +#ifdef VIA_USE_IO + pciaddr = pdev->base_address[0]; +#else + pciaddr = pdev->base_address[1]; +#endif + irq = pdev->irq; +#else + u32 pci_memaddr; + u8 pci_irq_line; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); +#ifdef VIA_USE_IO + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + pciaddr = pci_memaddr; +#else + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_memaddr); + pciaddr = pci_memaddr; +#endif + irq = pci_irq_line; +#endif + } + + if (debug > 2) + printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n", + pci_tbl[chip_idx].name, pciaddr, irq); + + if (pci_tbl[chip_idx].flags & PCI_USES_IO) { + if (check_region(pciaddr, pci_tbl[chip_idx].io_size)) + continue; + ioaddr = pciaddr & ~3; + } else if ((ioaddr = (long)ioremap(pciaddr & ~0xf, + pci_tbl[chip_idx].io_size)) == 0) { + printk(KERN_INFO "Failed to map PCI address %#lx.\n", + pciaddr); + continue; + } + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | (pci_tbl[chip_idx].flags & 7); + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, + irq, chip_idx, cards_found); + + if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { + u8 pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < min_pci_latency) { + printk(KERN_INFO " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to %d clocks.\n", + pci_latency, min_pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, min_pci_latency); + } + } + dev = 0; + cards_found++; + } + + return cards_found ? 0 : -ENODEV; +} + +#ifndef MODULE +static int via_rhine_probe(struct device *dev) +{ + printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); + return pci_etherdev_probe(dev, pci_tbl); +} +#endif + +static struct device *via_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, int irq, + int chip_id, int card_idx) +{ + struct netdev_private *np; + int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + + dev = init_etherdev(dev, 0); + + printk(KERN_INFO "%s: %s at 0x%lx, ", + dev->name, pci_tbl[chip_id].name, ioaddr); + + /* Ideally we would be read the EEPROM but access may be locked. */ + for (i = 0; i <6; i++) + dev->dev_addr[i] = readb(ioaddr + StationAddr + i); + for (i = 0; i < 5; i++) + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +#ifdef VIA_USE_IO + request_region(ioaddr, pci_tbl[chip_id].io_size, dev->name); +#endif + + /* Reset the chip to erase previous misconfiguration. */ + writew(CmdReset, ioaddr + ChipCmd); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Make certain the descriptor lists are cache-aligned. */ + np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31); + memset(np, 0, sizeof(*np)); + dev->priv = np; + + np->next_module = root_net_dev; + root_net_dev = dev; + + np->pci_bus = pci_bus; + np->pci_devfn = pci_devfn; + np->chip_id = chip_id; + + if (dev->mem_start) + option = dev->mem_start; + + /* The lower four bits are the media type. */ + if (option > 0) { + if (option & 0x200) + np->full_duplex = 1; + np->default_port = option & 15; + if (np->default_port) + np->medialock = 1; + } + if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) + np->full_duplex = 1; + + if (np->full_duplex) + np->duplex_lock = 1; + + /* The chip-specific entries in the device structure. */ + dev->open = &netdev_open; + dev->hard_start_xmit = &start_tx; + dev->stop = &netdev_close; + dev->get_stats = &get_stats; + dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &mii_ioctl; + + if (cap_tbl[np->chip_id].flags & CanHaveMII) { + int phy, phy_idx = 0; + np->phys[0] = 1; /* Standard for this chip. */ + for (phy = 1; phy < 32 && phy_idx < 4; phy++) { + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx++] = phy; + np->advertising = mdio_read(dev, phy, 4); + printk(KERN_INFO "%s: MII PHY found at address %d, status " + "0x%4.4x advertising %4.4x Link %4.4x.\n", + dev->name, phy, mii_status, np->advertising, + mdio_read(dev, phy, 5)); + } + } + np->mii_cnt = phy_idx; + } + + return dev; +} + + +/* Read and write over the MII Management Data I/O (MDIO) interface. */ + +static int mdio_read(struct device *dev, int phy_id, int regnum) +{ + long ioaddr = dev->base_addr; + int boguscnt = 1024; + + /* Wait for a previous command to complete. */ + while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) + ; + writeb(0x00, ioaddr + MIICmd); + writeb(phy_id, ioaddr + MIIPhyAddr); + writeb(regnum, ioaddr + MIIRegAddr); + writeb(0x40, ioaddr + MIICmd); /* Trigger read */ + boguscnt = 1024; + while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0) + ; + return readw(ioaddr + MIIData); +} + +static void mdio_write(struct device *dev, int phy_id, int regnum, int value) +{ + long ioaddr = dev->base_addr; + int boguscnt = 1024; + + /* Wait for a previous command to complete. */ + while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) + ; + writeb(0x00, ioaddr + MIICmd); + writeb(phy_id, ioaddr + MIIPhyAddr); + writeb(regnum, ioaddr + MIIRegAddr); + writew(value, ioaddr + MIIData); + writeb(0x20, ioaddr + MIICmd); /* Trigger write. */ + return; +} + + +static int netdev_open(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + /* Reset the chip. */ + writew(CmdReset, ioaddr + ChipCmd); + + if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + + if (debug > 1) + printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", + dev->name, dev->irq); + + MOD_INC_USE_COUNT; + + init_ring(dev); + + writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); + writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + + for (i = 0; i < 6; i++) + writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + + /* Initialize other registers. */ + writew(0x0006, ioaddr + PCIConfig); /* Tune configuration??? */ + /* Configure the FIFO thresholds. */ + writeb(0x20, ioaddr + TxConfig); /* Initial threshold 32 bytes */ + np->tx_thresh = 0x20; + np->rx_thresh = 0x60; /* Written in set_rx_mode(). */ + + if (dev->if_port == 0) + dev->if_port = np->default_port; + + dev->tbusy = 0; + dev->interrupt = 0; + np->in_interrupt = 0; + + set_rx_mode(dev); + + dev->start = 1; + + /* Enable interrupts by setting the interrupt mask. */ + writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped| + IntrTxDone | IntrTxAbort | IntrTxUnderrun | + IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange, + ioaddr + IntrEnable); + + np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll; + writew(np->chip_cmd, ioaddr + ChipCmd); + + check_duplex(dev); + + if (debug > 2) + printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x " + "MII status: %4.4x.\n", + dev->name, readw(ioaddr + ChipCmd), + mdio_read(dev, np->phys[0], 1)); + + /* Set the timer to check for link beat. */ + init_timer(&np->timer); + np->timer.expires = RUN_AT(1); + np->timer.data = (unsigned long)dev; + np->timer.function = &netdev_timer; /* timer handler */ + add_timer(&np->timer); + + return 0; +} + +static void check_duplex(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + int mii_reg5 = mdio_read(dev, np->phys[0], 5); + int duplex; + + if (np->duplex_lock || mii_reg5 == 0xffff) + return; + duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040; + if (np->full_duplex != duplex) { + np->full_duplex = duplex; + if (debug) + printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" + " partner capability of %4.4x.\n", dev->name, + duplex ? "full" : "half", np->phys[0], mii_reg5); + if (duplex) + np->chip_cmd |= CmdFDuplex; + else + np->chip_cmd &= ~CmdFDuplex; + writew(np->chip_cmd, ioaddr + ChipCmd); + } +} + +static void netdev_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 10*HZ; + + if (debug > 3) { + printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n", + dev->name, readw(ioaddr + IntrStatus)); + } + check_duplex(dev); + + np->timer.expires = RUN_AT(next_tick); + add_timer(&np->timer); +} + +static void tx_timeout(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + + printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status " + "%4.4x, resetting...\n", + dev->name, readw(ioaddr + IntrStatus), + mdio_read(dev, np->phys[0], 1)); + + /* Perhaps we should reinitialize the hardware here. */ + dev->if_port = 0; + /* Stop and restart the chip's Tx processes . */ + + /* Trigger an immediate transmit demand. */ + + dev->trans_start = jiffies; + np->stats.tx_errors++; + return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + int i; + + np->tx_full = 0; + np->cur_rx = np->cur_tx = 0; + np->dirty_rx = np->dirty_tx = 0; + + np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + np->rx_head_desc = &np->rx_ring[0]; + + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].rx_status = 0; + np->rx_ring[i].rx_length = 0; + np->rx_ring[i].desc_length = np->rx_buf_sz; + np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]); + np->rx_skbuff[i] = 0; + } + /* Mark the last entry as wrapping the ring. */ + np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]); + + /* Fill in the Rx buffers. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_ring[i].addr = virt_to_bus(skb->tail); + np->rx_ring[i].rx_status = 0; + np->rx_ring[i].rx_length = DescOwn; + } + np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_skbuff[i] = 0; + np->tx_ring[i].tx_own = 0; + np->tx_ring[i].desc_length = 0x00e08000; + np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]); + np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL); + } + np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]); + + return; +} + +static int start_tx(struct sk_buff *skb, struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + unsigned entry; + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + tx_timeout(dev); + return 1; + } + + /* Caution: the write order is important here, set the field + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = np->cur_tx % TX_RING_SIZE; + + np->tx_skbuff[entry] = skb; + + if ((long)skb->data & 3) { /* Must use alignment buffer. */ + if (np->tx_buf[entry] == NULL && + (np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) + return 1; + memcpy(np->tx_buf[entry], skb->data, skb->len); + np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]); + } else + np->tx_ring[entry].addr = virt_to_bus(skb->data); + + np->tx_ring[entry].desc_length = 0x00E08000 | + (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN); + np->tx_ring[entry].tx_own = DescOwn; + + np->cur_tx++; + + /* Non-x86 Todo: explicitly flush cache lines here. */ + + /* Wake the potentially-idle transmit channel. */ + writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + + if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1) + clear_bit(0, (void*)&dev->tbusy); /* Typical path */ + else + np->tx_full = 1; + dev->trans_start = jiffies; + + if (debug > 4) { + printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", + dev->name, np->cur_tx, entry); + } + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ + struct device *dev = (struct device *)dev_instance; + struct netdev_private *np; + long ioaddr, boguscnt = max_interrupt_work; + + ioaddr = dev->base_addr; + np = (struct netdev_private *)dev->priv; +#if defined(__i386__) + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); + dev->interrupt = 0; /* Avoid halting machine. */ + return; + } +#else + if (dev->interrupt) { + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; +#endif + + do { + u32 intr_status = readw(ioaddr + IntrStatus); + + /* Acknowledge all of the current interrupt sources ASAP. */ + writew(intr_status & 0xffff, ioaddr + IntrStatus); + + if (debug > 4) + printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", + dev->name, intr_status); + + if (intr_status == 0) + break; + + if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped | + IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf)) + netdev_rx(dev); + + for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { + int entry = np->dirty_tx % TX_RING_SIZE; + int txstatus; + if (np->tx_ring[entry].tx_own) + break; + txstatus = np->tx_ring[entry].tx_status; + if (debug > 6) + printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n", + entry, txstatus); + if (txstatus & 0x8000) { + if (debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n", + dev->name, txstatus); + np->stats.tx_errors++; + if (txstatus & 0x0400) np->stats.tx_carrier_errors++; + if (txstatus & 0x0200) np->stats.tx_window_errors++; + if (txstatus & 0x0100) np->stats.tx_aborted_errors++; + if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++; + if (txstatus & 0x0002) np->stats.tx_fifo_errors++; +#ifdef ETHER_STATS + if (txstatus & 0x0100) np->stats.collisions16++; +#endif + /* Transmitter restarted in 'abnormal' handler. */ + } else { +#ifdef ETHER_STATS + if (txstatus & 0x0001) np->stats.tx_deferred++; +#endif + np->stats.collisions += (txstatus >> 3) & 15; +#if defined(NETSTATS_VER2) + np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff; +#endif + np->stats.tx_packets++; + } + /* Free the original skb. */ + dev_free_skb(np->tx_skbuff[entry]); + np->tx_skbuff[entry] = 0; + } + if (np->tx_full && dev->tbusy + && np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) { + /* The ring is no longer full, clear tbusy. */ + np->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } + + /* Abnormal error summary/uncommon events handlers. */ + if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange | + IntrStatsMax | IntrTxAbort | IntrTxUnderrun)) + netdev_error(dev, intr_status); + + if (--boguscnt < 0) { + printk(KERN_WARNING "%s: Too much work at interrupt, " + "status=0x%4.4x.\n", + dev->name, intr_status); + break; + } + } while (1); + + if (debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", + dev->name, readw(ioaddr + IntrStatus)); + +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else + dev->interrupt = 0; +#endif + return; +} + +/* This routine is logically part of the interrupt handler, but isolated + for clarity and better register allocation. */ +static int netdev_rx(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + int entry = np->cur_rx % RX_RING_SIZE; + int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + + if (debug > 4) { + printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", + entry, np->rx_head_desc->rx_length); + } + + /* If EOP is set on the next entry, it's a new packet. Send it up. */ + while ( ! (np->rx_head_desc->rx_length & DescOwn)) { + struct rx_desc *desc = np->rx_head_desc; + int data_size = desc->rx_length; + u16 desc_status = desc->rx_status; + + if (debug > 4) + printk(KERN_DEBUG " netdev_rx() status is %4.4x.\n", + desc_status); + if (--boguscnt < 0) + break; + if ( (desc_status & (RxWholePkt | RxErr)) != RxWholePkt) { + if ((desc_status & RxWholePkt) != RxWholePkt) { + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, entry %#x length %d status %4.4x!\n", + dev->name, np->cur_rx, data_size, desc_status); + printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", + dev->name, np->rx_head_desc, + &np->rx_ring[np->cur_rx % RX_RING_SIZE]); + np->stats.rx_length_errors++; + } else if (desc_status & RxErr) { + /* There was a error. */ + if (debug > 2) + printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n", + desc_status); + np->stats.rx_errors++; + if (desc_status & 0x0030) np->stats.rx_length_errors++; + if (desc_status & 0x0048) np->stats.rx_fifo_errors++; + if (desc_status & 0x0004) np->stats.rx_frame_errors++; + if (desc_status & 0x0002) np->stats.rx_crc_errors++; + } + } else { + struct sk_buff *skb; + /* Length should omit the CRC */ + u16 pkt_len = data_size - 4; + + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if ! defined(__alpha__) || USE_IP_COPYSUM /* Avoid misaligned on Alpha */ + eth_copy_and_sum(skb, bus_to_virt(desc->addr), + pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb,pkt_len), bus_to_virt(desc->addr), pkt_len); +#endif + } else { + skb_put(skb = np->rx_skbuff[entry], pkt_len); + np->rx_skbuff[entry] = NULL; + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + np->stats.rx_packets++; + } + entry = (++np->cur_rx) % RX_RING_SIZE; + np->rx_head_desc = &np->rx_ring[entry]; + } + + /* Refill the Rx ring buffers. */ + for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { + struct sk_buff *skb; + entry = np->dirty_rx % RX_RING_SIZE; + if (np->rx_skbuff[entry] == NULL) { + skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[entry] = skb; + if (skb == NULL) + break; /* Better luck next round. */ + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_ring[entry].addr = virt_to_bus(skb->tail); + } + np->rx_ring[entry].rx_status = 0; + np->rx_ring[entry].rx_length = DescOwn; + } + + /* Pre-emptively restart Rx engine. */ + writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + return 0; +} + +static void netdev_error(struct device *dev, int intr_status) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + + if (intr_status & (IntrMIIChange | IntrLinkChange)) { + if (readb(ioaddr + MIIStatus) & 0x02) + /* Link failed, restart autonegotiation. */ + mdio_write(dev, np->phys[0], 0, 0x3300); + else + check_duplex(dev); + if (debug) + printk(KERN_ERR "%s: MII status changed: Autonegotiation " + "advertising %4.4x partner %4.4x.\n", dev->name, + mdio_read(dev, np->phys[0], 4), + mdio_read(dev, np->phys[0], 5)); + } + if (intr_status & IntrStatsMax) { + np->stats.rx_crc_errors += readw(ioaddr + RxCRCErrs); + np->stats.rx_missed_errors += readw(ioaddr + RxMissed); + writel(0, RxMissed); + } + if (intr_status & IntrTxAbort) { + /* Stats counted in Tx-done handler, just restart Tx. */ + writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + } + if (intr_status & IntrTxUnderrun) { + if (np->tx_thresh < 0xE0) + writeb(np->tx_thresh += 0x20, ioaddr + TxConfig); + if (debug > 1) + printk(KERN_INFO "%s: Transmitter underrun, increasing Tx " + "threshold setting to %2.2x.\n", dev->name, np->tx_thresh); + } + if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug) { + printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", + dev->name, intr_status); + /* Recovery for other fault sources not known. */ + writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + } +} + +static struct enet_statistics *get_stats(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + + /* Nominally we should lock this segment of code for SMP, although + the vulnerability window is very small and statistics are + non-critical. */ + np->stats.rx_crc_errors += readw(ioaddr + RxCRCErrs); + np->stats.rx_missed_errors += readw(ioaddr + RxMissed); + writel(0, RxMissed); + + return &np->stats; +} + +/* The big-endian AUTODIN II ethernet CRC calculation. + N.B. Do not use for bulk data, use a table-based routine instead. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ + int crc = -1; + + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) { + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + } + return crc; +} + +static void set_rx_mode(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + u8 rx_mode; /* Note: 0x02=accept runt, 0x01=accept errs */ + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + /* Unconditionally log net taps. */ + printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); + rx_mode = 0x1C; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to match, or accept all multicasts. */ + rx_mode = 0x0C; + } else { + struct dev_mc_list *mclist; + int i; + memset(mc_filter, 0, sizeof(mc_filter)); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, + mc_filter); + } + writel(mc_filter[0], ioaddr + MulticastFilter0); + writel(mc_filter[1], ioaddr + MulticastFilter1); + rx_mode = 0x0C; + } + writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig); +} + +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + u16 *data = (u16 *)&rq->ifr_data; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f; + /* Fall Through */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int netdev_close(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = (struct netdev_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, readw(ioaddr + ChipCmd)); + + /* Disable interrupts by clearing the interrupt mask. */ + writew(0x0000, ioaddr + IntrEnable); + + /* Stop the chip's Tx and Rx processes. */ + writew(CmdStop, ioaddr + ChipCmd); + + del_timer(&np->timer); + + free_irq(dev->irq, dev); + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].rx_length = 0; + np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ + if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 + np->rx_skbuff[i]->free = 1; +#endif + dev_free_skb(np->rx_skbuff[i]); + } + np->rx_skbuff[i] = 0; + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (np->tx_skbuff[i]) + dev_free_skb(np->tx_skbuff[i]); + np->tx_skbuff[i] = 0; + } + + MOD_DEC_USE_COUNT; + + return 0; +} + + +#ifdef MODULE +int init_module(void) +{ + if (debug) /* Emit version even if no cards detected. */ + printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); +#ifdef CARDBUS + register_driver(ðerdev_ops); + return 0; +#else + return pci_etherdev_probe(NULL, pci_tbl); +#endif +} + +void cleanup_module(void) +{ + +#ifdef CARDBUS + unregister_driver(ðerdev_ops); +#endif + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_net_dev) { + struct netdev_private *np = + (struct netdev_private *)(root_net_dev->priv); + unregister_netdev(root_net_dev); +#ifdef VIA_USE_IO + release_region(root_net_dev->base_addr, pci_tbl[np->chip_id].io_size); +#else + iounmap((char *)(root_net_dev->base_addr)); +#endif + kfree(root_net_dev); + root_net_dev = np->next_module; +#if 0 + kfree(np); /* Assumption: no struct realignment. */ +#endif + } +} + +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.1.124/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c --- v2.1.124/linux/drivers/net/wavelan.c Tue Jul 28 14:21:08 1998 +++ linux/drivers/net/wavelan.c Wed Oct 7 15:51:45 1998 @@ -2950,8 +2950,8 @@ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) * Does it work for everybody, especially old cards? */ /* Note: WFREQSEL verifies that it is able to read a sensible - * frequency from from EEPROM (address 0x00) and that - * MMR_FEE_STATUS_ID is 0xA (Xilinx version) or 0xB (Ariadne version). + * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID + * is 0xA (Xilinx version) or 0xB (Ariadne version). * My test is more crude but does work. */ if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) diff -u --recursive --new-file v2.1.124/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c --- v2.1.124/linux/drivers/pci/oldproc.c Thu Sep 17 17:53:36 1998 +++ linux/drivers/pci/oldproc.c Thu Oct 8 08:07:33 1998 @@ -528,6 +528,10 @@ DEVICE( ADAPTEC, ADAPTEC_7883, "AIC-7883U"), DEVICE( ADAPTEC, ADAPTEC_7884, "AIC-7884U"), DEVICE( ADAPTEC, ADAPTEC_1030, "ABA-1030 DVB receiver"), + DEVICE( ADAPTEC2, ADAPTEC2_2940U2, "AHA-2940U2"), + DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"), + DEVICE( ADAPTEC2, ADAPTEC2_3940U2, "AHA-3940U2"), + DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"), DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"), DEVICE( TIGERJET, TIGERJET_300, "Tiger300 ISDN"), DEVICE( ARK, ARK_STING, "Stingray"), @@ -778,6 +782,7 @@ case PCI_VENDOR_ID_INTEL: return "Intel"; case PCI_VENDOR_ID_KTI: return "KTI"; case PCI_VENDOR_ID_ADAPTEC: return "Adaptec"; + case PCI_VENDOR_ID_ADAPTEC2: return "Adaptec"; case PCI_VENDOR_ID_ATRONICS: return "Atronics"; case PCI_VENDOR_ID_TIGERJET: return "TigerJet"; case PCI_VENDOR_ID_ARK: return "ARK Logic"; diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/53c7xx.c linux/drivers/scsi/53c7xx.c --- v2.1.124/linux/drivers/scsi/53c7xx.c Mon Aug 3 12:45:46 1998 +++ linux/drivers/scsi/53c7xx.c Mon Oct 5 13:43:31 1998 @@ -325,6 +325,7 @@ static int disable (struct Scsi_Host *host); static int NCR53c7xx_run_tests (struct Scsi_Host *host); static void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs); +static void NCR53c7x0_intfly (struct Scsi_Host *host); static int ncr_halt (struct Scsi_Host *host); static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd); @@ -847,7 +848,6 @@ unsigned char revision; struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0]; - struct Scsi_Host *search; /* * There are some things which we need to know about in order to provide * a semblance of support. Print 'em if they aren't what we expect, @@ -1072,26 +1072,13 @@ NCR53c7x0_driver_init (host); - /* - * Set up an interrupt handler if we aren't already sharing an IRQ - * with another board. - */ - - for (search = first_host; search && !(search->hostt == the_template && - search->irq == host->irq && search != host); search=search->next); - - if (!search) { - if (request_irq(host->irq, NCR53c7x0_intr, 0, "53c7xx", NCR53c7x0_intr)) - { - printk("scsi%d : IRQ%d not free, detaching\n", + if (request_irq(host->irq, NCR53c7x0_intr, 0, "53c7xx", host)) + { + printk("scsi%d : IRQ%d not free, detaching\n", host->host_no, host->irq); - scsi_unregister (host); - return -1; - } - } else { - printk("scsi%d : using interrupt handler previously installed for scsi%d\n", - host->host_no, search->host_no); - } + scsi_unregister (host); + return -1; + } if ((hostdata->run_tests && hostdata->run_tests(host) == -1) || (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) { @@ -2509,18 +2496,7 @@ abnormal_finished (cmd, DID_ERROR << 16); return SPECIFIC_INT_NOTHING; case A_int_norm_emulateintfly: - /* I'm not sure this is the right ! thing to do, but it works - * with the A4000T when copyback is disabled, and also the - * WarpEngine with copyback enabled, so it looks as though - * it does work to some extent. - * - * RGH: I don't really like it - You get an interrupt which - * calls NCR53c7x0_intr(), which calls this function (via - * intr_dma()), which calls NCR53c7x0_intr()..... - * Anyway let's see how it goes for now. - */ - hostdata->emulated_intfly = 1; - NCR53c7x0_intr(host->irq, NULL, NULL); + NCR53c7x0_intfly(host); return SPECIFIC_INT_NOTHING; case A_int_test_1: case A_int_test_2: @@ -4141,288 +4117,230 @@ } #endif + /* - * Function : static void NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) + * Function : static void NCR53c7x0_intfly (struct Scsi_Host *host) * - * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing - * the same IRQ line. + * Purpose : Scan command queue for specified host, looking for completed + * commands. * - * Inputs : Since we're using the SA_INTERRUPT interrupt handler - * semantics, irq indicates the interrupt which invoked - * this handler. + * Inputs : Scsi_Host pointer. * - * On the 710 we simualte an INTFLY with a script interrupt, and the - * script interrupt handler will call back to this function. + * This is called from the interrupt handler, when a simulated INTFLY + * interrupt occurs. */ -static void -NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) { +static void +NCR53c7x0_intfly (struct Scsi_Host *host) +{ NCR53c7x0_local_declare(); - struct Scsi_Host *host; /* Host we are looking at */ - unsigned char istat; /* Values of interrupt regs */ struct NCR53c7x0_hostdata *hostdata; /* host->hostdata[0] */ struct NCR53c7x0_cmd *cmd, /* command which halted */ **cmd_prev_ptr; - u32 *dsa; /* DSA */ - int done = 1; /* Indicates when handler - should terminate */ - int interrupted = 0; /* This HA generated - an interrupt */ - int have_intfly; /* Don't print warning - messages when we stack - INTFLYs */ unsigned long flags; + char search_found = 0; /* Got at least one ? */ -#ifdef NCR_DEBUG - char buf[80]; /* Debugging sprintf buffer */ - size_t buflen; /* Length of same */ -#endif - -#if defined(CONFIG_AMIGA) - custom.intena = IF_PORTS; -#endif - - do { - done = 1; - for (host = first_host; host; host = host->next) - if (host->hostt == the_template -#if defined(MVME16x_INTFLY) - /* We have two different interrupts pointing - * at this routine, so remove this check */ -#else - && host->irq == irq -#endif - ) { - NCR53c7x0_local_setup(host); + hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0]; + NCR53c7x0_local_setup(host); - hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0]; - hostdata->dsp_changed = 0; - interrupted = 0; - have_intfly = 0; - - do { - hostdata->dstat_valid = 0; - interrupted = 0; - /* - * Only read istat once, since reading it again will unstack - * interrupts? - */ - istat = NCR53c7x0_read8(hostdata->istat); - - if ((hostdata->options & OPTION_INTFLY) && -#ifdef MVME16x_INTFLY - /* the bit is set which indicates an on-the-fly int */ - (*(volatile unsigned long *)0xfff40068 & 0x8000)) -#else - (hostdata->emulated_intfly != 0)) -#endif - { - char search_found = 0; /* Got at least one ? */ - done = 0; - interrupted = 1; + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : INTFLY\n", host->host_no); -#ifdef MVME16x_INTFLY - /* clear the INTFLY bit */ - *(volatile unsigned long *)0xfff40074 = 0x8000; -#endif + /* + * Traverse our list of running commands, and look + * for those with valid (non-0xff ff) status and message + * bytes encoded in the result which signify command + * completion. + */ - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : INTFLY\n", host->host_no); + save_flags(flags); + cli(); +restart: + for (cmd_prev_ptr = (struct NCR53c7x0_cmd **)&(hostdata->running_list), + cmd = (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ; + cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next), + cmd = (struct NCR53c7x0_cmd *) cmd->next) + { + Scsi_Cmnd *tmp; - /* - * Traverse our list of running commands, and look - * for those with valid (non-0xff ff) status and message - * bytes encoded in the result which signify command - * completion. - */ + if (!cmd) { + printk("scsi%d : very weird.\n", host->host_no); + break; + } + if (!(tmp = cmd->cmd)) { + printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n", + host->host_no); + continue; + } + /* Copy the result over now; may not be complete, + * but subsequent tests may as well be done on + * cached memory. + */ + tmp->result = cmd->result; - save_flags(flags); - cli(); -restart: - for (cmd_prev_ptr = (struct NCR53c7x0_cmd **) - &(hostdata->running_list), cmd = - (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ; - cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next), - cmd = (struct NCR53c7x0_cmd *) cmd->next) { - Scsi_Cmnd *tmp; - - if (!cmd) { - printk("scsi%d : very weird.\n", host->host_no); - break; - } - - if (!(tmp = cmd->cmd)) { - printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n", - host->host_no); - continue; - } - /* Copy the result over now; may not be complete, - * but subsequent tests may as well be done on - * cached memory. - */ - tmp->result = cmd->result; -#if 0 - printk ("scsi%d : looking at result of 0x%x\n", - host->host_no, cmd->cmd->result); -#endif - - if (((tmp->result & 0xff) == 0xff) || + if (((tmp->result & 0xff) == 0xff) || ((tmp->result & 0xff00) == 0xff00)) - continue; + continue; - search_found = 1; + search_found = 1; - if (cmd->bounce.len) - memcpy ((void *)cmd->bounce.addr, + if (cmd->bounce.len) + memcpy ((void *)cmd->bounce.addr, (void *)cmd->bounce.buf, cmd->bounce.len); - /* Important - remove from list _before_ done is called */ - if (cmd_prev_ptr) - *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next; - - --hostdata->busy[tmp->target][tmp->lun]; - cmd->next = hostdata->free; - hostdata->free = cmd; - - tmp->host_scribble = NULL; - - if (hostdata->options & OPTION_DEBUG_INTR) { - printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", - host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result); - print_command (tmp->cmnd); - } + /* Important - remove from list _before_ done is called */ + if (cmd_prev_ptr) + *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next; -#if 0 - hostdata->options &= ~OPTION_DEBUG_INTR; -#endif - tmp->scsi_done(tmp); - goto restart; + --hostdata->busy[tmp->target][tmp->lun]; + cmd->next = hostdata->free; + hostdata->free = cmd; - } - restore_flags(flags); - - if (!search_found && !have_intfly) { - printk ("scsi%d : WARNING : INTFLY with no completed commands.\n", - host->host_no); - } else if (!have_intfly) { - have_intfly = 1; - run_process_issue_queue(); - } - } + tmp->host_scribble = NULL; - if (hostdata->emulated_intfly) - { - hostdata->emulated_intfly = 0; - return; - } + if (hostdata->options & OPTION_DEBUG_INTR) { + printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", + host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result); + print_command (tmp->cmnd); + } - if (istat & (ISTAT_SIP|ISTAT_DIP)) { - done = 0; - interrupted = 1; - hostdata->state = STATE_HALTED; + tmp->scsi_done(tmp); + goto restart; + } + restore_flags(flags); - if (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) - printk ("scsi%d : SCSI FIFO not empty\n", + if (!search_found) { + printk ("scsi%d : WARNING : INTFLY with no completed commands.\n", host->host_no); + } else { + run_process_issue_queue(); + } + return; +} - /* - * NCR53c700 and NCR53c700-66 change the current SCSI - * process, hostdata->ncrcurrent, in the Linux driver so - * cmd = hostdata->ncrcurrent. - * - * With other chips, we must look through the commands - * executing and find the command structure which - * corresponds to the DSA register. - */ - - if (hostdata->options & OPTION_700) { - cmd = (struct NCR53c7x0_cmd *) hostdata->ncrcurrent; - } else { - dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); - for (cmd = (struct NCR53c7x0_cmd *) - hostdata->running_list; cmd && - (dsa + (hostdata->dsa_start / sizeof(u32))) != - cmd->dsa; - cmd = (struct NCR53c7x0_cmd *)(cmd->next)); - } - if (hostdata->options & OPTION_DEBUG_INTR) { - if (cmd) { - printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", - host->host_no, cmd->cmd->pid, (int) cmd->cmd->target, - (int) cmd->cmd->lun); - print_command (cmd->cmd->cmnd); - } else { - printk("scsi%d : no active command\n", host->host_no); - } - } +/* + * Function : static void NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) + * + * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing + * the same IRQ line. + * + * Inputs : Since we're using the SA_INTERRUPT interrupt handler + * semantics, irq indicates the interrupt which invoked + * this handler. + * + * On the 710 we simualte an INTFLY with a script interrupt, and the + * script interrupt handler will call back to this function. + */ - if (istat & ISTAT_SIP) { - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : ISTAT_SIP\n", host->host_no); - intr_scsi (host, cmd); - } - - if (istat & ISTAT_DIP) { - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : ISTAT_DIP\n", host->host_no); - intr_dma (host, cmd); - } +static void +NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) { + NCR53c7x0_local_declare(); + struct Scsi_Host *host; /* Host we are looking at */ + unsigned char istat; /* Values of interrupt regs */ + struct NCR53c7x0_hostdata *hostdata; /* host->hostdata[0] */ + struct NCR53c7x0_cmd *cmd; /* command which halted */ + u32 *dsa; /* DSA */ - if (!hostdata->dstat_valid) { - hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); - hostdata->dstat_valid = 1; - } +#ifdef NCR_DEBUG + char buf[80]; /* Debugging sprintf buffer */ + size_t buflen; /* Length of same */ +#endif - if (!(hostdata->dstat & DSTAT_DFE)) { - printk ("scsi%d : DMA FIFO not empty\n", host->host_no); - /* Really need to check this out for 710 RGH */ - NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF); - while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF); - hostdata->dstat |= DSTAT_DFE; - } - } - } while (interrupted); + host = (struct Scsi_Host *)dev_id; + hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0]; + NCR53c7x0_local_setup(host); + /* + * Only read istat once per loop, since reading it again will unstack + * interrupts + */ + while ((istat = NCR53c7x0_read8(hostdata->istat)) & (ISTAT_SIP|ISTAT_DIP)) { + hostdata->dsp_changed = 0; + hostdata->dstat_valid = 0; + hostdata->state = STATE_HALTED; - if (hostdata->intrs != -1) - hostdata->intrs++; -#if 0 - if (hostdata->intrs > 40) { - printk("scsi%d : too many interrupts, halting", host->host_no); - disable(host); + if (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) + printk ("scsi%d : SCSI FIFO not empty\n", host->host_no); + + /* + * NCR53c700 and NCR53c700-66 change the current SCSI + * process, hostdata->ncrcurrent, in the Linux driver so + * cmd = hostdata->ncrcurrent. + * + * With other chips, we must look through the commands + * executing and find the command structure which + * corresponds to the DSA register. + */ + + if (hostdata->options & OPTION_700) { + cmd = (struct NCR53c7x0_cmd *) hostdata->ncrcurrent; + } else { + dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + for (cmd = (struct NCR53c7x0_cmd *) hostdata->running_list; + cmd && (dsa + (hostdata->dsa_start / sizeof(u32))) != cmd->dsa; + cmd = (struct NCR53c7x0_cmd *)(cmd->next)) + ; + } + if (hostdata->options & OPTION_DEBUG_INTR) { + if (cmd) { + printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", + host->host_no, cmd->cmd->pid, (int) cmd->cmd->target, + (int) cmd->cmd->lun); + print_command (cmd->cmd->cmnd); + } else { + printk("scsi%d : no active command\n", host->host_no); } -#endif + } + + if (istat & ISTAT_SIP) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ISTAT_SIP\n", host->host_no); + intr_scsi (host, cmd); + } + + if (istat & ISTAT_DIP) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ISTAT_DIP\n", host->host_no); + intr_dma (host, cmd); + } + + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + + if (!(hostdata->dstat & DSTAT_DFE)) { + printk ("scsi%d : DMA FIFO not empty\n", host->host_no); + /* Really need to check this out for 710 RGH */ + NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF); + while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF) + ; + hostdata->dstat |= DSTAT_DFE; + } - if (!hostdata->idle && hostdata->state == STATE_HALTED) { - if (!hostdata->dsp_changed) { - hostdata->dsp = (u32 *) - bus_to_virt(NCR53c7x0_read32(DSP_REG)); - } - + if (!hostdata->idle && hostdata->state == STATE_HALTED) { + if (!hostdata->dsp_changed) + hostdata->dsp = (u32 *)bus_to_virt(NCR53c7x0_read32(DSP_REG)); #if 0 - printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n", - host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp); + printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n", + host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp); #endif - hostdata->state = STATE_RUNNING; - NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp)); - if (hostdata->options & OPTION_DEBUG_TRACE) { + hostdata->state = STATE_RUNNING; + NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp)); + if (hostdata->options & OPTION_DEBUG_TRACE) { #ifdef CYCLIC_TRACE - log_insn (hostdata->dsp); + log_insn (hostdata->dsp); #else - print_insn (host, hostdata->dsp, "t ", 1); + print_insn (host, hostdata->dsp, "t ", 1); #endif - NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | - DCNTL_SSM | DCNTL_STD); - } + NCR53c7x0_write8 (DCNTL_REG, + hostdata->saved_dcntl | DCNTL_SSM | DCNTL_STD); } } - } while (!done); -#ifdef CONFIG_AMIGA - custom.intena = IF_SETCLR | IF_PORTS; -#endif + } } diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx --- v2.1.124/linux/drivers/scsi/ChangeLog.ncr53c8xx Tue Jul 21 00:15:31 1998 +++ linux/drivers/scsi/ChangeLog.ncr53c8xx Mon Oct 5 13:40:23 1998 @@ -1,4 +1,26 @@ -Sar Jul 18 13:00 1998 Gerard Roudier (groudier@club-internet.fr) +Sun Oct 4 14:00 1998 Gerard Roudier (groudier@club-internet.fr) + * revision 3.0i + - Cosmetic changes for sparc (but not for the driver) that needs + __irq_itoa() to be used for printed IRQ value to be understandable. + - Some problems with the driver that didn't occur using driver 2.5f + were due to a SCSI selection problem triggered by a clearly + documented feature that in fact seems not to work: (53C8XX chips + are claimed by the manuals to be able to execute SCSI scripts just + after abitration while the SCSI core is performing SCSI selection). + This optimization is broken and has been removed. + - Some broken scsi devices are confused when a negotiation is started + on a LUN that does not correspond to a real device. According to + SCSI specs, this is a device firmware bug. This has been worked + around by only starting negotiation if the LUN has previously be + used for at least 1 successful SCSI command. + - The 'last message sent' printed out on M_REJECT message reception + was read from the SFBR i/o register after the previous message had + been sent. + This was not correct and affects all previous driver versions and + the original FreeBSD one as well. The SCSI scripts has been fixed + so that it now provides the right information to the C code. + +Sat Jul 18 13:00 1998 Gerard Roudier (groudier@club-internet.fr) * revision 3.0g - Preliminary fixes for Big Endian (sent by Eddie C. Dost). Big Endian architectures should work again with the driver. diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.1.124/linux/drivers/scsi/Makefile Thu Sep 17 17:53:36 1998 +++ linux/drivers/scsi/Makefile Mon Oct 5 13:43:31 1998 @@ -147,6 +147,14 @@ endif endif +ifeq ($(CONFIG_BLZ603EPLUS_SCSI),y) +L_OBJS += amiga7xx.o 53c7xx.o +else + ifeq ($(CONFIG_BLZ603EPLUS_SCSI),m) + M_OBJS += amiga7xx.o 53c7xx.o + endif +endif + ifeq ($(CONFIG_WARPENGINE_SCSI),y) L_OBJS += amiga7xx.o 53c7xx.o else diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx --- v2.1.124/linux/drivers/scsi/README.aic7xxx Thu Aug 6 14:06:32 1998 +++ linux/drivers/scsi/README.aic7xxx Thu Oct 8 08:07:33 1998 @@ -21,15 +21,24 @@ AHA-2940W AHA-2940U AHA-2940UW - AHA-2940AU + AHA-2940AU + AHA-2940U2W + AHA-2940U2 + AHA-2940U2B + AHA-2940U2BOEM AHA-2944D AHA-2944WD AHA-2944UD AHA-2944UWD + AHA-2950U2 + AHA-2950U2W + AHA-2950U2B AHA-3940 AHA-3940U AHA-3940W AHA-3940UW + AHA-3940U2W + AHA-3950U2B AHA-3985 AHA-3985U AHA-3985W @@ -42,13 +51,14 @@ AIC-786x AIC-787x AIC-788x - AIC-7895 + AIC-789x Bus Types ---------------------------- W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s. U - Ultra SCSI, transfer rates up to 40MB/s. + U2- Ultra 2 SCSI, transfer rates up to 80MB/s. D - Differential SCSI. T - Twin Channel SCSI. Up to 14 SCSI devices. @@ -72,7 +82,7 @@ (Original Linux FTP/patch maintainer) Jess Johnson jester@frenzy.com (AIC7xxx FAQ author) - Doug Ledford dledford@dialnet.net + Doug Ledford dledford@redhat.com (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer) Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original @@ -104,38 +114,41 @@ Some SCSI devices need the initial reset that this option disables in order to work. If you have problems at bootup, please make sure you aren't using this option. - "aic7xxx=reverse_scan" - Have the driver register the SCSI cards in the - reverse of the normal order. This may help those people who have more - than one PCI Adaptec controller force the correct controller to be - scsi0 under linux so that their boot hard drive is also sda under - linux + + "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at + bootup by scanning from the highest numbered PCI device to the + lowest numbered PCI device, others do just the opposite and scan + from lowest to highest numbered PCI device. There is no reliable + way to autodetect this ordering. So, we default to the most common + order, which is lowest to highest. Then, in case your motherboard + scans from highest to lowest, we have this option. If your BIOS + finds the drives on controller A before controller B but the linux + kernel finds your drives on controller B before A, then you should + use this option. + "aic7xxx=extended" - Force the driver to detect extended drive translation on your controller. This helps those people who have cards without a SEEPROM make sure that linux and all other operating systems think the same way about your hard drives. + "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel to use the correct IRQ type for your card. This only applies to EISA based controllers. On these controllers, 0 is for Edge triggered interrupts, and 1 is for Level triggered interrupts. If you aren't sure or don't know which IRQ trigger type your EISA card uses, then let the kernel autodetect the trigger type. + "aic7xxx=verbose" - This option can be used in one of two ways. If you - simply specify aic7xxx=verbose, then the kernel will automatically pick - the default set of verbose messages for you to see. Alternatively, you - can specify the command as "aic7xxx=verbose:0xXXXX" where the X entries - are replaced with hexadecimal digits. This option is a bit field type - option. For a full listing of the available options, search for the - #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want verbose - messages, then it is recommended that you simply use the aic7xxx=verbose - variant of this command. - "aic7xxx=7895_irq_hack:x" - This option enables some work around code to - fix a bug in the Tyan Thunder II motherboard BIOS. The BIOS - incorrectly sets the IRQs on the two channels of the 7895 to two - different values even though the motherboard hardware doesn't support - this mode of operation. The valid values for x are: 0 to force - both channels to use the IRQ assigned to Channel A, 1 to force both - channels to use the IRQ assigned to Channel B, and -1 will disable - this horrible abomination of a hack. The default is disabled (-1). + simply specify aic7xxx=verbose, then the kernel will automatically + pick the default set of verbose messages for you to see. + Alternatively, you can specify the command as + "aic7xxx=verbose:0xXXXX" where the X entries are replaced with + hexadecimal digits. This option is a bit field type option. For + a full listing of the available options, search for the + #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want + verbose messages, then it is recommended that you simply use the + aic7xxx=verbose variant of this command. + "aic7xxx=pci_parity:x" - This option controls whether or not the driver enables PCI parity error checking on the PCI bus. By default, this checking is disabled. To enable the checks, simply specify pci_parity @@ -147,6 +160,145 @@ NOTE: In order to get Even PCI parity checking, you must use the version of the option that does not include the : and a number at the end (unless you want to enter exactly 2^32 - 1 as the number). + + "aic7xxx=no_probe" - This option will disable the probing for any VLB + based 2842 controllers and any EISA based controllers. This is + needed on certain newer motherboards where the normal EISA I/O ranges + have been claimed by other PCI devices. Probing on those machines + will often result in the machine crashing or spontaneously rebooting + during startup. Examples of machines that need this are the + Dell PowerEdge 6300 machines. + + "aic7xxx=panic_on_abort" - This option is for debugging and will cause + the driver to panic the linux kernel and freeze the system the first + time the drivers abort or reset routines are called. This is most + helpful when some problem causes infinite reset loops that scroll too + fast to see. By using this option, you can write down what the errors + actually are and send that information to me so it can be fixed. + + "aic7xxx=dump_card" - This option will print out the *entire* set of + configuration registers on the card during the init sequence. This + is a debugging aid used to see exactly what state the card is in + when we finally finish our initialization routines. If you don't + have documentation on the chipsets, this will do you absolutely + no good unless you are simply trying to write all the information + down in order to send it to me. + + "aic7xxx=dump_sequencer" - This is the same as the above options except + that instead of dumping the register contents on the card, this + option dumps the contents of the sequencer program RAM. This gives + the ability to verify that the instructions downloaded to the + card's sequencer are indeed what they are suppossed to be. Again, + unless you have documentation to tell you how to interpret these + numbers, then it is totally useless. + + "aic7xxx=override_term:0xffffffff" - This option is used to force the + termination on your SCSI controllers to a particular setting. This + is a bit mask variable that applies for up to 8 aic7xxx SCSI channels. + Each channel gets 4 bits, divided as follows: + bit 3 2 1 0 + | | | Enable/Disable Single Ended Low Byte Termination + | | En/Disable Single Ended High Byte Termination + | En/Disable Low Byte LVD Termination + En/Disable High Byte LVD Termination + + The upper 2 bits that deal with LVD termination only apply to Ultra2 + controllers. Futhermore, due to the current Ultra2 controller + designs, these bits are tied together such that setting either bit + enables both low and high byte LVD termination. It is not possible + to only set high or low byte LVD termination in this manner. This is + an artifact of the BIOS definition on Ultra2 controllers. For other + controllers, the only important bits are the two lowest bits. Setting + the higher bits on non-Ultra2 controllers has no effect. A few + examples of how to use this option: + + Enable low and high byte termination on a non-ultra2 controller that + is the first aic7xxx controller (the correct bits are 0011), + aic7xxx=override_term:0x3 + + Enable all termination on the third aic7xxx controller, high byte + termination on the second aic7xxx controller, and low and high byte + SE termination on the first aic7xxx controller + (bits are 1111 0010 0011), + aic7xxx=override_term:0xf23 + + No attempt has been made to make this option non-cryptic. It really + shouldn't be used except in dire circumstances, and if that happens, + I'm probably going to be telling you what to set this to anyway :) + + "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV + bit in the DEVCONFIG PCI register. Currently, this is one of the + very few registers that we have absolutely *no* way of detecting + what the variable should be. It depends entirely on how the chipset + and external terminators were coupled by the card/motherboard maker. + Further, a chip reset (at power up) always sets this bit to 0. If + there is no BIOS to run on the chipset/card (such as with a 2910C + or a motherboard controller with the BIOS totally disabled) then + the variable may not get set properly. Of course, if the proper + setting was 0, then that's what it would be after the reset, but if + the proper setting is actually 1.....you get the picture. Now, since + we can't detect this at all, I've added this option to force the + setting. If you have a BIOS on your controller then you should never + need to use this option. However, if you are having lots of SCSI + reset problems and can't seem to get them knocked out, this may help. + + Here's a test to know for certain if you need this option. Make + a boot floppy that you can use to boot your computer up and that + will detect the aic7xxx controller. Next, power down your computer. + While it's down, unplug all SCSI cables from your Adaptec SCSI + controller. Boot the system back up to the Adaptec EZ-SCSI BIOS + and then make sure that termination is enabled on your adapter (if + you have an Adaptec BIOS of course). Next, boot up the floppy you + made and wait for it to detect the aic7xxx controller. If the kernel + finds the controller fine, says scsi : x hosts and then tries to + detect your devices like normal, up to the point where it fails to + mount your root file system and panics, then you're fine. If, on + the other hand, the system goes into an infinite reset loop, then + you need to use this option and/or the previous option to force the + proper termination settings on your controller. If this happens, + then you next need to figure out what your settings should be. + + To find the correct settings, power your machine back down, connect + back up the SCSI cables, and boot back into your machine like normal. + However, boot with the aic7xxx=verbose:0x39 option. Record the + initial DEVCONFIG values for each of your aic7xxx controllers as + they are listed, and also record what the machine is detecting as + the proper termination on your controllers. NOTE: the order in + which the initial DEVCONFIG values are printed out is not gauranteed + to be the same order as the SCSI controllers are registered. The + above option and this option both work on the order of the SCSI + controllers as they are registered, so make sure you match the right + DEVCONFIG values with the right controllers if you have more than + one aic7xxx controller. + + Once you have the detected termination settings and the initial + DEVCONFIG values for each controller, then figure out what the + termination on each of the controllers *should* be. Hopefully, that + part is correct, but it could possibly be wrong if there is + bogus cable detection logic on your controller or something similar. + If all the controllers have the correct termination settings, then + don't set the aic7xxx=override_term variable at all, leave it alone. + Next, on any controllers that go into an infinite reset loop when + you unplug all the SCSI cables, get the starting DEVCONFIG value. + If the initial DEVCONFIG value is divisible by 2, then the correct + setting for that controller is 0. If it's an odd number, then + the correct setting for that controller is 1. For any other + controllers that didn't have an infinite reset problem, then reverse + the above options. If DEVCONFIG was even, then the correct setting + is 1, if not then the correct setting is 0. + + Now that you know what the correct setting was for each controller, + we need to encode that into the aic7xxx=stpwlev:0x... variable. + This variable is a bit field encoded variable. Bit 0 is for the first + aic7xxx controller, bit 1 for the next, etc. Put all these bits + together and you get a number. For example, if the third aic7xxx + needed a 1, but the second and first both needed a 0, then the bits + would be 100 in binary. This then translates to 0x04. You would + therefore set aic7xxx=stpwlev:0x04. This is fairly standard binary + to hexadecimal conversions here. If you aren't up to speed on the + binary->hex conversion then send an email to the aic7xxx mailing + list and someone can help you out. + "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable tagged queueing on specific devices. As of driver version 5.0.6, we now globally enable tagged queueing by default, but we also disable @@ -214,8 +366,8 @@ aic7xxx=verbose,extended,irq_trigger:1 - The only requirement is that individual options be separated by a comma on - the command line. + The only requirement is that individual options be separated by a comma or + a period on the command line. Module Loading command options ------------------------------ @@ -263,11 +415,35 @@ Thanks to Michael Neuffer for for his upper-level SCSI help, and Matthew Jacob for statistics support. + Debugging the driver + ------------------------------ + Should you have problems with this driver, and would like some help in + getting them solved, there are a couple debugging items built into + the driver to facilitate getting the needed information from the system. + In general, I need a complete description of the problem, with as many + logs as possible concerning what happens. To help with this, there is + a command option aic7xxx=panic_on_abort. This option, when set, forces + the driver to panic the kernel on the first SCSI abort issued by the + mid level SCSI code. If your system is going to reset loops and you + can't read the screen, then this is what you need. Not only will it + stop the system, but it also prints out a large amount of state + information in the process. Second, if you specify the option + "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much + information as it runs that you won't be able to see anything. + However, this can actually be very usefull if your machine simply + locks up when trying to boot, since it will pin-point what was last + happening (in regards to the aic7xxx driver) immediately prior to + the lockup. This is really only usefull if your machine simply can + not boot up successfully. If you can get your machine to run, then + this will produce far too much information. + FTP sites ------------------------------ - ftp://ftp.dialnet.net/pub/linux/aic7xxx/ + ftp://ftp.redhat.com/pub/aic/ - Primary site for Doug Ledford developed driver releases - - US Linux mirror of Teleport site + ftp://ftp.dialnet.net/pub/linux/aic7xxx + - Temporary mirror of the redhat.com ftp site while people + get used to the new address ftp://ftp.pcnet.com/users/eischen/Linux/ - Dan Eischen's driver distribution area ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/ diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c --- v2.1.124/linux/drivers/scsi/aha1542.c Wed Sep 9 14:51:08 1998 +++ linux/drivers/scsi/aha1542.c Wed Oct 7 15:52:55 1998 @@ -16,6 +16,8 @@ * Modified by Mike McLagan * Recognise extended mode on AHA1542CP, different bit than 1542CF * 1-Jan-97 + * Modified by Bjorn L. Thordarson and Einar Thor Einarsson + * Recognize that DMA0 is valid DMA channel -- 13-Jul-98 */ #include @@ -98,13 +100,6 @@ * Factory default is 5 MB/s. */ - -/* The DMA-Controller. We need to fool with this because we want to - be able to use the aha1542 without having to have the bios enabled */ -#define DMA_MODE_REG 0xd6 -#define DMA_MASK_REG 0xd4 -#define CASCADE 0xc0 - #define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */ #define BIOS_TRANSLATION_6432 1 /* Default case these days */ #define BIOS_TRANSLATION_25563 2 /* Big disk case */ @@ -767,8 +762,8 @@ *dma_chan = 5; break; case 0x01: - printk("DMA priority 0 not available for Adaptec driver\n"); - return -1; + *dma_chan = 0; + break; case 0: /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel. Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */ @@ -1038,9 +1033,9 @@ goto unregister; } - if (dma_chan >= 5) { - outb((dma_chan - 4) | CASCADE, DMA_MODE_REG); - outb(dma_chan - 4, DMA_MASK_REG); + if (dma_chan == 0 || dma_chan >= 5) { + set_dma_mode(dma_chan, DMA_MODE_CASCADE); + enable_dma(dma_chan); } } aha_host[irq_level - 9] = shpnt; diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg --- v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.reg Tue Apr 14 14:29:21 1998 +++ linux/drivers/scsi/aic7xxx/aic7xxx.reg Thu Oct 8 08:07:33 1998 @@ -1,7 +1,7 @@ /* * Aic7xxx register and scratch ram definitions. * - * Copyright (c) 1994-1997 Justin Gibbs. + * Copyright (c) 1994-1998 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -161,6 +161,7 @@ access_mode RW bit WIDEXFER 0x80 /* Wide transfer control */ mask SXFR 0x70 /* Sync transfer rate */ + mask SXFR_ULTRA2 0x7f /* Sync transfer rate */ mask SOFS 0x0f /* Sync offset */ } @@ -174,6 +175,13 @@ access_mode RW mask TID 0xf0 /* Target ID mask */ mask OID 0x0f /* Our ID mask */ + /* + * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book) + * The aic7890/91 allow an offset of up to 127 transfers in both wide + * and narrow mode. + */ + alias SCSIOFFSET + mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */ } /* @@ -227,14 +235,15 @@ register SSTAT0 { address 0x00b access_mode RO - bit TARGET 0x80 /* Board acting as target */ - bit SELDO 0x40 /* Selection Done */ - bit SELDI 0x20 /* Board has been selected */ - bit SELINGO 0x10 /* Selection In Progress */ - bit SWRAP 0x08 /* 24bit counter wrap */ - bit SDONE 0x04 /* STCNT = 0x000000 */ - bit SPIORDY 0x02 /* SCSI PIO Ready */ - bit DMADONE 0x01 /* DMA transfer completed */ + bit TARGET 0x80 /* Board acting as target */ + bit SELDO 0x40 /* Selection Done */ + bit SELDI 0x20 /* Board has been selected */ + bit SELINGO 0x10 /* Selection In Progress */ + bit SWRAP 0x08 /* 24bit counter wrap */ + bit IOERR 0x08 /* LVD Tranceiver mode changed */ + bit SDONE 0x04 /* STCNT = 0x000000 */ + bit SPIORDY 0x02 /* SCSI PIO Ready */ + bit DMADONE 0x01 /* DMA transfer completed */ } /* @@ -276,6 +285,7 @@ address 0x00d access_mode RO bit OVERRUN 0x80 + bit EXP_ACTIVE 0x10 /* SCSI Expander Active */ mask SFCNT 0x1f } @@ -290,14 +300,13 @@ } /* - * SCSI Test Control (p. 3-27) + * SCSI ID for the aic7890/91 chips */ -register SCSITEST { +register SCSIID_ULTRA2 { address 0x00f access_mode RW - bit RQAKCNT 0x04 - bit CNTRTEST 0x02 - bit CMODE 0x01 + mask TID 0xf0 /* Target ID mask */ + mask OID 0x0f /* Our ID mask */ } /* @@ -312,6 +321,7 @@ bit ENSELDI 0x20 bit ENSELINGO 0x10 bit ENSWRAP 0x08 + bit ENIOERR 0x08 /* LVD Tranceiver mode changes */ bit ENSDONE 0x04 bit ENSPIORDY 0x02 bit ENDMADONE 0x01 @@ -424,7 +434,10 @@ bit DIAGLEDON 0x40 /* Aic78X0 only */ bit AUTOFLUSHDIS 0x20 bit SELBUSB 0x08 + bit ENAB40 0x08 /* LVD transceiver active */ + bit ENAB20 0x04 /* SE/HVD transceiver active */ bit SELWIDE 0x02 + bit XCVR 0x01 /* External transceiver active */ } /* @@ -547,6 +560,19 @@ bit ENABLE 0x01 } +register DSCOMMAND0 { + address 0x084 + access_mode RW + bit CACHETHEN 0x80 + bit DPARCKEN 0x40 + bit MPARCKEN 0x20 + bit EXTREQLCK 0x10 + bit INTSCBRAMSEL 0x08 + bit RAMPS 0x04 + bit USCBSIZE32 0x02 + bit CIOPARCKEN 0x01 +} + /* * On the aic78X0 chips, Board Control is replaced by the DSCommand * register (p. 4-64) @@ -676,6 +702,7 @@ register ERROR { address 0x092 access_mode RO + bit CIOPARERR 0x80 /* Ultra2 only */ bit PCIERRSTAT 0x40 /* PCI only */ bit MPARERR 0x20 /* PCI only */ bit DPARERR 0x10 /* PCI only */ @@ -701,6 +728,7 @@ register DFCNTRL { address 0x093 access_mode RW + bit PRELOADEN 0x80 /* aic7890 only */ bit WIDEODD 0x40 bit SCSIEN 0x20 bit SDMAEN 0x10 @@ -715,6 +743,7 @@ register DFSTATUS { address 0x094 access_mode RO + bit PRELOAD_AVAIL 0x80 bit DWORDEMP 0x20 bit MREQPEND 0x10 bit HDONE 0x08 @@ -777,6 +806,14 @@ } /* + * Special Function + */ +register SFUNCT { + address 0x09f + access_mode RW +} + +/* * SCB Definition (p. 5-4) */ scb { @@ -865,6 +902,109 @@ register DSPCISTATUS { address 0x086 + mask DFTHRSH_100 0xc0 +} + +register CCHADDR { + address 0x0E0 + size 8 +} + +register CCHCNT { + address 0x0E8 +} + +register CCSGRAM { + address 0x0E9 +} + +register CCSGADDR { + address 0x0EA +} + +register CCSGCTL { + address 0x0EB + bit CCSGDONE 0x80 + bit CCSGEN 0x08 + bit FLAG 0x02 + bit CCSGRESET 0x01 +} + +register CCSCBCNT { + address 0xEF +} + +register CCSCBCTL { + address 0x0EE + bit CCSCBDONE 0x80 + bit ARRDONE 0x40 /* SCB Array prefetch done */ + bit CCARREN 0x10 + bit CCSCBEN 0x08 + bit CCSCBDIR 0x04 + bit CCSCBRESET 0x01 +} + +register CCSCBADDR { + address 0x0ED +} + +register CCSCBRAM { + address 0xEC +} + +register CCSCBPTR { + address 0x0F1 +} + +register HNSCB_QOFF { + address 0x0F4 +} + +register SNSCB_QOFF { + address 0x0F6 +} + +register SDSCB_QOFF { + address 0x0F8 +} + +register QOFF_CTLSTA { + address 0x0FA + bit SCB_AVAIL 0x40 + bit SNSCB_ROLLOVER 0x20 + bit SDSCB_ROLLOVER 0x10 + mask SCB_QSIZE 0x07 + mask SCB_QSIZE_256 0x06 +} + +register DFF_THRSH { + address 0x0FB + mask WR_DFTHRSH 0x70 + mask RD_DFTHRSH 0x07 + mask RD_DFTHRSH_MIN 0x00 + mask RD_DFTHRSH_25 0x01 + mask RD_DFTHRSH_50 0x02 + mask RD_DFTHRSH_63 0x03 + mask RD_DFTHRSH_75 0x04 + mask RD_DFTHRSH_85 0x05 + mask RD_DFTHRSH_90 0x06 + mask RD_DFTHRSH_MAX 0x07 + mask WR_DFTHRSH_MIN 0x00 + mask WR_DFTHRSH_25 0x10 + mask WR_DFTHRSH_50 0x20 + mask WR_DFTHRSH_63 0x30 + mask WR_DFTHRSH_75 0x40 + mask WR_DFTHRSH_85 0x50 + mask WR_DFTHRSH_90 0x60 + mask WR_DFTHRSH_MAX 0x70 +} + +register SG_CACHEPTR { + access_mode RW + address 0x0fc + mask SG_USER_DATA 0xfc + bit LAST_SEG 0x02 + bit LAST_SEG_DONE 0x01 } register BRDCTL { @@ -877,6 +1017,12 @@ bit BRDRW 0x04 bit BRDCTL1 0x02 bit BRDCTL0 0x01 + /* 7890 Definitions */ + bit BRDDAT4 0x10 + bit BRDDAT3 0x08 + bit BRDDAT2 0x04 + bit BRDRW_ULTRA2 0x02 + bit BRDSTB_ULTRA2 0x01 } /* @@ -935,7 +1081,7 @@ /* * 1 byte per target starting at this address for configuration values */ - TARG_SCRATCH { + TARG_SCSIRATE { size 16 } /* @@ -960,6 +1106,7 @@ /* Parameters for DMA Logic */ DMAPARAMS { size 1 + bit PRELOADEN 0x80 bit WIDEODD 0x40 bit SCSIEN 0x20 bit SDMAEN 0x10 @@ -1078,12 +1225,27 @@ mask MSGOUT_PHASEMIS 0x10 alias RETURN_1 } + ARG_2 { + size 1 + alias RETURN_2 + } + /* * Snapshot of MSG_OUT taken after each message is sent. */ LAST_MSG { size 1 } + + /* + * Number of times we have filled the CCSGRAM with prefetched + * SG elements. + */ + PREFETCH_CNT { + size 1 + } + + /* * These are reserved registers in the card's scratch ram. Some of * the values are specified in the AHA2742 technical reference manual @@ -1108,22 +1270,35 @@ mask BIOSDISABLED 0x30 bit CHANNEL_B_PRIMARY 0x08 } + /* + * Per target SCSI offset values for Ultra2 controllers. + */ + TARG_OFFSET { + address 0x070 + size 16 + } } const SCB_LIST_NULL 0xff +const CCSGADDR_MAX 0x80 +const CCSGRAM_MAXSEGS 16 + /* Offsets into the SCBID array where different data is stored */ const UNTAGGEDSCB_OFFSET 0 const QOUTFIFO_OFFSET 1 const QINFIFO_OFFSET 2 /* WDTR Message values */ -const BUS_8_BIT 0x00 +const BUS_8_BIT 0x00 const BUS_16_BIT 0x01 const BUS_32_BIT 0x02 + +/* Offset maximums */ const MAX_OFFSET_8BIT 0x0f -const MAX_OFFSET_16BIT 0x08 -const HOST_MSG 0xFF +const MAX_OFFSET_16BIT 0x08 +const MAX_OFFSET_ULTRA2 0x7f +const HOST_MSG 0xff /* Target mode command processing constants */ const CMD_GROUP_CODE_SHIFT 0x05 @@ -1135,11 +1310,7 @@ /* * Downloaded (kernel inserted) constants */ -/* - * Mask of bits to test against when looking at the Queue Count - * registers. Works around a bug on aic7850 chips. - */ -const QCNTMASK download + /* * Number of command descriptors in the command descriptor array. */ diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq --- v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.seq Tue Apr 14 14:29:21 1998 +++ linux/drivers/scsi/aic7xxx/aic7xxx.seq Thu Oct 8 08:07:33 1998 @@ -1,7 +1,7 @@ /* * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. * - * Copyright (c) 1994-1997 Justin Gibbs. + * Copyright (c) 1994-1998 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -14,7 +14,7 @@ * derived from this software without specific prior written permission. * * Where this Software is combined with software released under the terms of - * the GNU Public License ("GPL") and the terms of the GPL would require the + * the GNU Public License (GPL) and the terms of the GPL would require the * combined work to also be released under the terms of the GPL, the terms * and conditions of this License will apply in addition to those of the * GPL with the exception of any terms or conditions of this License that @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $ + * $Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $ */ #include "aic7xxx.reg" @@ -59,38 +59,55 @@ reset: clr SCSISIGO; /* De-assert BSY */ /* Always allow reselection */ -.if ( TARGET_MODE ) - mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; -.else - mvi SCSISEQ, ENRSELI|ENAUTOATNP; -.endif + if ((p->flags & AHC_TARGETMODE) != 0) { + mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; + } else { + mvi SCSISEQ, ENRSELI|ENAUTOATNP; + } + + if ((p->features & AHC_CMD_CHAN) != 0) { + /* Ensure that no DMA operations are in progress */ + clr CCSGCTL; + clr CCSCBCTL; + } + call clear_target_state; and SXFRCTL0, ~SPIOEN; poll_for_work: - mov A, QINPOS; + if ((p->features & AHC_QUEUE_REGS) == 0) { + mov A, QINPOS; + } poll_for_work_loop: - and SEQCTL, ~PAUSEDIS; - test SSTAT0, SELDO|SELDI jnz selection; - test SCSISEQ, ENSELO jnz poll_for_work; -.if ( TWIN_CHANNEL ) - /* - * Twin channel devices cannot handle things like SELTO - * interrupts on the "background" channel. So, if we - * are selecting, keep polling the current channel util - * either a selection or reselection occurs. - */ - xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + if ((p->features & AHC_QUEUE_REGS) == 0) { + and SEQCTL, ~PAUSEDIS; + } test SSTAT0, SELDO|SELDI jnz selection; test SCSISEQ, ENSELO jnz poll_for_work; - xor SBLKCTL,SELBUSB; /* Toggle back */ -.endif + if ((p->features & AHC_TWIN) != 0) { + /* + * Twin channel devices cannot handle things like SELTO + * interrupts on the "background" channel. So, if we + * are selecting, keep polling the current channel util + * either a selection or reselection occurs. + */ + xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + test SSTAT0, SELDO|SELDI jnz selection; + test SCSISEQ, ENSELO jnz poll_for_work; + xor SBLKCTL,SELBUSB; /* Toggle back */ + } cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; test_queue: /* Has the driver posted any work for us? */ - or SEQCTL, PAUSEDIS; - cmp KERNEL_QINPOS, A je poll_for_work_loop; - inc QINPOS; - and SEQCTL, ~PAUSEDIS; + if ((p->features & AHC_QUEUE_REGS) != 0) { + test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop; + mov NONE, SNSCB_QOFF; + inc QINPOS; + } else { + or SEQCTL, PAUSEDIS; + cmp KERNEL_QINPOS, A je poll_for_work_loop; + inc QINPOS; + and SEQCTL, ~PAUSEDIS; + } /* * We have at least one queued SCB now and we don't have any @@ -98,26 +115,24 @@ * any SCBs available for use, pull the tag from the QINFIFO * and get to work on it. */ -.if ( SCB_PAGING ) - mov ALLZEROS call get_free_or_disc_scb; -.endif + if ((p->flags & AHC_PAGESCBS) != 0) { + mov ALLZEROS call get_free_or_disc_scb; + } + dequeue_scb: add A, -1, QINPOS; - mvi QINFIFO_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + mvi QINFIFO_OFFSET call fetch_byte; - call dma_finish; - mov SINDEX, DFDAT; -.if !( SCB_PAGING ) - /* In the non-paging case, the SCBID == hardware SCB index */ - mov SCBPTR, SINDEX; -.endif + if ((p->flags & AHC_PAGESCBS) == 0) { + /* In the non-paging case, the SCBID == hardware SCB index */ + mov SCBPTR, RETURN_2; + } dma_queued_scb: /* * DMA the SCB from host ram into the current SCB location. */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; - call dma_scb; + mov RETURN_2 call dma_scb; start_scb: /* @@ -135,16 +150,22 @@ jmp poll_for_work; start_selection: -.if ( TWIN_CHANNEL ) - and SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */ - and A,SELBUSB,SCB_TCL; /* Get new channel bit */ - or SINDEX,A; - mov SBLKCTL,SINDEX; /* select channel */ -.endif + if ((p->features & AHC_TWIN) != 0) { + and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ + and A,SELBUSB,SCB_TCL; /* Get new channel bit */ + or SINDEX,A; + mov SBLKCTL,SINDEX; /* select channel */ + } initialize_scsiid: - and A, TID, SCB_TCL; /* Get target ID */ - and SCSIID, OID; /* Clear old target */ - or SCSIID, A; + if ((p->features & AHC_ULTRA2) != 0) { + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID_ULTRA2, OID; /* Clear old target */ + or SCSIID_ULTRA2, A; + } else { + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID, OID; /* Clear old target */ + or SCSIID, A; + } mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; /* @@ -155,143 +176,154 @@ initialize_channel: or A, CLRSTCNT|CLRCHN, SINDEX; or SXFRCTL0, A; -.if ( ULTRA ) + if ((p->features & AHC_ULTRA) != 0) { ultra: - mvi SINDEX, ULTRA_ENB+1; - test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ - dec SINDEX; + mvi SINDEX, ULTRA_ENB+1; + test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ + dec SINDEX; ultra_2: - mov FUNCTION1,SAVED_TCL; - mov A,FUNCTION1; - test SINDIR, A jz ndx_dtr; - or SXFRCTL0, FAST20; -.endif - + mov FUNCTION1,SAVED_TCL; + mov A,FUNCTION1; + test SINDIR, A jz ndx_dtr; + or SXFRCTL0, FAST20; + } /* * Initialize SCSIRATE with the appropriate value for this target. * The SCSIRATE settings for each target are stored in an array - * based at TARG_SCRATCH. + * based at TARG_SCSIRATE. */ ndx_dtr: shr A,4,SAVED_TCL; - test SBLKCTL,SELBUSB jz ndx_dtr_2; - or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */ - or A,0x08; /* Channel B entries add 8 */ + if ((p->features & AHC_TWIN) != 0) { + test SBLKCTL,SELBUSB jz ndx_dtr_2; + or SAVED_TCL, SELBUSB; + or A,0x08; /* Channel B entries add 8 */ ndx_dtr_2: - add SINDEX,TARG_SCRATCH,A; + } + + if ((p->features & AHC_ULTRA2) != 0) { + add SINDEX, TARG_OFFSET, A; + mov SCSIOFFSET, SINDIR; + } + + add SINDEX,TARG_SCSIRATE,A; mov SCSIRATE,SINDIR ret; selection: test SSTAT0,SELDO jnz select_out; select_in: -.if ( TARGET_MODE ) - test SSTAT0, TARGET jz initiator_reselect; - /* - * We've just been selected. Assert BSY and - * setup the phase for receiving the messages - * from the target. - */ - mvi SCSISIGO, P_MESGOUT|BSYO; - mvi CLRSINT0, CLRSELDO; - - /* - * If ATN isn't asserted, go directly to bus free. - */ - test SCSISIGI, ATNI jz target_busfree; - - /* - * Setup the DMA for sending the identify and - * command information. - */ - mov A, TMODE_CMDADDR_NEXT; - mvi TMODE_CMDADDR call set_32byte_haddr_and_clrcnt; - mvi DFCNTRL, FIFORESET; + if ((p->flags & AHC_TARGETMODE) != 0) { + test SSTAT0, TARGET jz initiator_reselect; + /* + * We've just been selected. Assert BSY and + * setup the phase for receiving the messages + * from the target. + */ + mvi SCSISIGO, P_MESGOUT|BSYO; + mvi CLRSINT0, CLRSELDO; + + /* + * If ATN isn't asserted, go directly to bus free. + */ + test SCSISIGI, ATNI jz target_busfree; + + /* + * Setup the DMA for sending the identify and + * command information. + */ + mov A, TMODE_CMDADDR_NEXT; + mvi DINDEX, HADDR; + mvi TMODE_CMDADDR call set_32byte_addr; + mvi DFCNTRL, FIFORESET; - clr SINDEX; - /* Watch ATN closely now */ + clr SINDEX; + /* Watch ATN closely now */ message_loop: - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - and SXFRCTL0, ~SPIOEN; - mov DINDEX, SCSIDATL; - mov DFDAT, DINDEX; - inc SINDEX; - - /* Message Testing... */ - test DINDEX, MSG_IDENTIFYFLAG jz . + 2; - mov ARG_1, DINDEX; - - test SCSISIGI, ATNI jnz message_loop; - add A, -4, SINDEX; - jc target_cmdphase; - mvi DFDAT, SCB_LIST_NULL; /* Terminate the message list */ + or SXFRCTL0, SPIOEN; + test SSTAT0, SPIORDY jz .; + and SXFRCTL0, ~SPIOEN; + mov DINDEX, SCSIDATL; + mov DFDAT, DINDEX; + inc SINDEX; + + /* Message Testing... */ + test DINDEX, MSG_IDENTIFYFLAG jz . + 2; + mov ARG_1, DINDEX; + + test SCSISIGI, ATNI jnz message_loop; + add A, -4, SINDEX; + jc target_cmdphase; + mvi DFDAT, SCB_LIST_NULL; /* Terminate the message list */ target_cmdphase: - add HCNT[0], 1, A; - mvi SCSISIGO, P_COMMAND|BSYO; - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - mov A, SCSIDATL; - mov DFDAT, A; /* Store for host */ - - /* - * Determine the number of bytes to read - * based on the command group code. Count is - * one less than the total since we've already - * fetched the first byte. - */ - clr SINDEX; - shr A, CMD_GROUP_CODE_SHIFT; - add SEQADDR0, A; - - add SINDEX, CMD_GROUP0_BYTE_DELTA; - nop; /* Group 1 and 2 are the same */ - add SINDEX, CMD_GROUP2_BYTE_DELTA; - nop; /* Group 3 is reserved */ - add SINDEX, CMD_GROUP4_BYTE_DELTA; - add SINDEX, CMD_GROUP5_BYTE_DELTA; + add HCNT[0], 1, A; + clr HCNT[1]; + clr HCNT[2]; + mvi SCSISIGO, P_COMMAND|BSYO; + or SXFRCTL0, SPIOEN; + test SSTAT0, SPIORDY jz .; + mov A, SCSIDATL; + mov DFDAT, A; /* Store for host */ + + /* + * Determine the number of bytes to read + * based on the command group code. Count is + * one less than the total since we've already + * fetched the first byte. + */ + clr SINDEX; + shr A, CMD_GROUP_CODE_SHIFT; + add SEQADDR0, A; + + add SINDEX, CMD_GROUP0_BYTE_DELTA; + nop; /* Group 1 and 2 are the same */ + add SINDEX, CMD_GROUP2_BYTE_DELTA; + nop; /* Group 3 is reserved */ + add SINDEX, CMD_GROUP4_BYTE_DELTA; + add SINDEX, CMD_GROUP5_BYTE_DELTA; /* Group 6 and 7 are not handled yet */ - mov A, SINDEX; - add HCNT[0], A; + mov A, SINDEX; + add HCNT[0], A; command_loop: - test SSTAT0, SPIORDY jz .; - cmp SINDEX, 1 jne . + 2; - and SXFRCTL0, ~SPIOEN; /* Last Byte */ - mov DFDAT, SCSIDATL; - dec SINDEX; - test SINDEX, 0xFF jnz command_loop; + test SSTAT0, SPIORDY jz .; + cmp SINDEX, 1 jne . + 2; + and SXFRCTL0, ~SPIOEN; /* Last Byte */ + mov DFDAT, SCSIDATL; + dec SINDEX; + test SINDEX, 0xFF jnz command_loop; - or DFCNTRL, HDMAEN|FIFOFLUSH; + or DFCNTRL, HDMAEN|FIFOFLUSH; - call dma_finish; + call dma_finish; - test ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post; + test ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post; - mvi SCSISIGO, P_MESGIN|BSYO; + mvi SCSISIGO, P_MESGIN|BSYO; - or SXFRCTL0, SPIOEN; + or SXFRCTL0, SPIOEN; - mvi MSG_DISCONNECT call target_outb; + mvi MSG_DISCONNECT call target_outb; selectin_post: - inc TMODE_CMDADDR_NEXT; - cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2; - clr TMODE_CMDADDR_NEXT; - mvi QOUTFIFO, SCB_LIST_NULL; - mvi INTSTAT,CMDCMPLT; + inc TMODE_CMDADDR_NEXT; + cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2; + clr TMODE_CMDADDR_NEXT; + mvi QOUTFIFO, SCB_LIST_NULL; + mvi INTSTAT,CMDCMPLT; - test ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree; + test ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree; - /* Busy loop on something then go to data or status phase */ + /* Busy loop on something then go to data or status phase */ target_busfree: - clr SCSISIGO; - jmp poll_for_work; + clr SCSISIGO; + jmp poll_for_work; + + } -.endif /* TARGET_MODE */ /* * Reselection has been initiated by a target. Make a note that we've been * reselected, but haven't seen an IDENTIFY message from the target yet. @@ -396,26 +428,41 @@ jmp data_phase_loop; p_data: - mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; + if ((p->features & AHC_ULTRA2) != 0) { + mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN; + } else { + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; + } test LASTPHASE, IOI jnz . + 2; or DMAPARAMS, DIRECTION; call assert; /* * Ensure entering a data * phase is okay - seen identify, etc. */ - + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi CCSGADDR, CCSGADDR_MAX; + } test SEQ_FLAGS, DPHASE jnz data_phase_reinit; + /* We have seen a data phase */ + or SEQ_FLAGS, DPHASE; + /* * Initialize the DMA address and counter from the SCB. * Also set SG_COUNT and SG_NEXT in memory since we cannot * modify the values in the SCB itself until we see a * save data pointers message. */ - mvi DINDEX, HADDR; - mvi SCB_DATAPTR call bcopy_7; - - call set_stcnt_from_hcnt; + if ((p->features & AHC_CMD_CHAN) != 0) { + bmov HADDR, SCB_DATAPTR, 7; + } else { + mvi DINDEX, HADDR; + mvi SCB_DATAPTR call bcopy_7; + } + + if ((p->features & AHC_ULTRA2) == 0) { + call set_stcnt_from_hcnt; + } mov SG_COUNT,SCB_SGCOUNT; @@ -432,19 +479,38 @@ * had an overrun. */ or SXFRCTL1,BITBUCKET; - mvi HCNT[0], 0xff; - mvi HCNT[1], 0xff; - mvi HCNT[2], 0xff; - call set_stcnt_from_hcnt; and DMAPARAMS, ~(HDMAEN|SDMAEN); - + if ((p->features & AHC_ULTRA2) != 0) { + bmov HCNT, ALLONES, 3; + } else { + mvi STCNT[0], 0xFF; + mvi STCNT[1], 0xFF; + mvi STCNT[2], 0xFF; + } data_phase_inbounds: -/* If we are the last SG block, ensure wideodd is off. */ +/* If we are the last SG block, tell the hardware. */ cmp SG_COUNT,0x01 jne data_phase_wideodd; - and DMAPARAMS, ~WIDEODD; + if ((p->features & AHC_ULTRA2) != 0) { + or SG_CACHEPTR, LAST_SEG; + } else { + and DMAPARAMS, ~WIDEODD; + } data_phase_wideodd: - mov DMAPARAMS call dma; + if ((p->features & AHC_ULTRA2) != 0) { + mov SINDEX, ALLONES; + mov DFCNTRL, DMAPARAMS; + test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ +data_phase_dma_loop: + test SSTAT0, SDONE jnz data_phase_dma_done; + test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */ +data_phase_dma_phasemis: + test SSTAT0,SDONE jnz . + 2; + mov SINDEX,ALLZEROS; /* Remeber the phasemiss */ + } else { + mov DMAPARAMS call dma; + } +data_phase_dma_done: /* Go tell the host about any overruns */ test SXFRCTL1,BITBUCKET jnz data_phase_overrun; @@ -458,11 +524,6 @@ dec SG_COUNT; /* one less segment to go */ test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ - - clr A; /* add sizeof(struct scatter) */ - add SG_NEXT[0],SG_SIZEOF; - adc SG_NEXT[1],A; - /* * Load a struct scatter and set up the data address and length. * If the working value of the SG count is nonzero, then @@ -471,33 +532,71 @@ * This, like all DMA's, assumes little-endian host data storage. */ sg_load: - mvi DINDEX, HADDR; - mvi SG_NEXT call bcopy_4; - - mvi HCNT[0],SG_SIZEOF; - clr HCNT[1]; - clr HCNT[2]; - - or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; - - call dma_finish; + if ((p->features & AHC_CMD_CHAN) != 0) { + /* + * Do we have any prefetch left??? + */ + cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail; + + /* + * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes. + */ + add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT; + mvi A, CCSGADDR_MAX; + jc . + 2; + shl A, 3, SG_COUNT; + mov CCHCNT, A; + bmov CCHADDR, SG_NEXT, 4; + mvi CCSGCTL, CCSGEN|CCSGRESET; + test CCSGCTL, CCSGDONE jz .; + and CCSGCTL, ~CCSGEN; + test CCSGCTL, CCSGEN jnz .; + mvi CCSGCTL, CCSGRESET; +prefetched_segs_avail: + bmov HADDR, CCSGRAM, 8; + } else { + mvi DINDEX, HADDR; + mvi SG_NEXT call bcopy_4; + + mvi HCNT[0],SG_SIZEOF; + clr HCNT[1]; + clr HCNT[2]; + + or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + + call dma_finish; + + /* + * Copy data from FIFO into SCB data pointer and data count. + * This assumes that the SG segments are of the form: + * struct ahc_dma_seg { + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order + * }; + */ + mvi HADDR call dfdat_in_7; + } + + if ((p->features & AHC_ULTRA2) == 0) { + /* Load STCNT as well. It is a mirror of HCNT */ + call set_stcnt_from_hcnt; + } -/* - * Copy data from FIFO into SCB data pointer and data count. This assumes - * that the SG segments are of the form: - * - * struct ahc_dma_seg { - * u_int32_t addr; four bytes, little-endian order - * u_int32_t len; four bytes, little endian order - * }; - */ - mvi HADDR call dfdat_in_7; +/* Advance the SG pointer */ + clr A; /* add sizeof(struct scatter) */ + add SG_NEXT[0],SG_SIZEOF; + adc SG_NEXT[1],A; -/* Load STCNT as well. It is a mirror of HCNT */ - call set_stcnt_from_hcnt; test SSTAT1,PHASEMIS jz data_phase_loop; + /* Ensure the last seg is visable at the shaddow layer */ + if ((p->features & AHC_ULTRA2) != 0) { + or DFCNTRL, PRELOADEN; + } data_phase_finish: + if ((p->features & AHC_ULTRA2) != 0) { + call ultra2_dmafinish; + } /* * After a DMA finishes, save the SG and STCNT residuals back into the SCB * We use STCNT instead of HCNT, since it's a reflection of how many bytes @@ -508,12 +607,17 @@ mov SCB_RESID_DCNT[2],STCNT[2]; mov SCB_RESID_SGCNT, SG_COUNT; - /* We have seen a data phase */ - or SEQ_FLAGS, DPHASE; + if ((p->features & AHC_ULTRA2) != 0) { + or SXFRCTL0, CLRSTCNT|CLRCHN; + } jmp ITloop; data_phase_overrun: + if ((p->features & AHC_ULTRA2) != 0) { + call ultra2_dmafinish; + or SXFRCTL0, CLRSTCNT|CLRCHN; + } /* * Turn off BITBUCKET mode and notify the host */ @@ -521,6 +625,19 @@ mvi INTSTAT,DATA_OVERRUN; jmp ITloop; +ultra2_dmafinish: + if ((p->features & AHC_ULTRA2) != 0) { + test DFCNTRL, DIRECTION jnz ultra2_dmahalt; + and DFCNTRL, ~SCSIEN; + test DFCNTRL, SCSIEN jnz .; + or DFCNTRL, FIFOFLUSH; + test DFSTATUS, FIFOEMP jz . - 1; +ultra2_dmahalt: + and DFCNTRL, ~(SCSIEN|HDMAEN); + test DFCNTRL, HDMAEN jnz .; + ret; + } + /* * Command phase. Set up the DMA registers and let 'er rip. */ @@ -530,14 +647,26 @@ /* * Load HADDR and HCNT. */ - mvi DINDEX, HADDR; - mvi SCB_CMDPTR call bcopy_5; - clr HCNT[1]; - clr HCNT[2]; - - call set_stcnt_from_hcnt; - - mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; + if ((p->features & AHC_ULTRA2) != 0) { + or SG_CACHEPTR, LAST_SEG; + } + + if ((p->features & AHC_CMD_CHAN) != 0) { + bmov HADDR, SCB_CMDPTR, 5; + bmov HCNT[1], ALLZEROS, 2; + } else { + mvi DINDEX, HADDR; + mvi SCB_CMDPTR call bcopy_5; + clr HCNT[1]; + clr HCNT[2]; + } + + if ((p->features & AHC_ULTRA2) == 0) { + call set_stcnt_from_hcnt; + mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; + } else { + mvi (PRELOADEN|SCSIEN|HDMAEN|DIRECTION) call dma; + } jmp ITloop; /* @@ -562,7 +691,6 @@ * on an SCB that might not be for the current nexus. (For example, a * BDR message in responce to a bad reselection would leave us pointed to * an SCB that doesn't have anything to do with the current target). - * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag, * bus device reset). * @@ -574,11 +702,11 @@ mov SINDEX, MSG_OUT; cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host; p_mesgout_identify: -.if ( WIDE ) - and SINDEX,0xf,SCB_TCL; /* lun */ -.else - and SINDEX,0x7,SCB_TCL; /* lun */ -.endif + if ((p->features & AHC_WIDE) != 0) { + and SINDEX,0xf,SCB_TCL; /* lun */ + } else { + and SINDEX,0x7,SCB_TCL; /* lun */ + } and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ or SINDEX,A; /* or in disconnect privledge */ or SINDEX,MSG_IDENTIFYFLAG; @@ -606,6 +734,7 @@ p_mesgout_from_host: cmp SINDEX, HOST_MSG jne p_mesgout_onebyte; mvi INTSTAT,AWAITING_MSG; + nop; /* * Did the host detect a phase change? */ @@ -692,6 +821,7 @@ check_status: test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */ mvi INTSTAT,BAD_STATUS; /* let driver know */ + nop; cmp RETURN_1, SEND_SENSE jne complete; /* This SCB becomes the next to execute as it will retrieve sense */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; @@ -710,21 +840,21 @@ /* If we are untagged, clear our address up in host ram */ test SCB_CONTROL, TAG_ENB jnz complete_post; mov A, SAVED_TCL; - mvi UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, FIFORESET; - mvi DFDAT, SCB_LIST_NULL; - or DFCNTRL, HDMAEN|FIFOFLUSH; - call dma_finish; + mvi UNTAGGEDSCB_OFFSET call post_byte_setup; + mvi SCB_LIST_NULL call post_byte; complete_post: /* Post the SCB and issue an interrupt */ - mov A, QOUTPOS; - mvi QOUTFIFO_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, FIFORESET; - mov DFDAT, SCB_TAG; - or DFCNTRL, HDMAEN|FIFOFLUSH; - call dma_finish; - inc QOUTPOS; + if ((p->features & AHC_QUEUE_REGS) != 0) { + mov A, SDSCB_QOFF; + } else { + mov A, QOUTPOS; + } + mvi QOUTFIFO_OFFSET call post_byte_setup; + mov SCB_TAG call post_byte; + if ((p->features & AHC_QUEUE_REGS) == 0) { + inc QOUTPOS; + } mvi INTSTAT,CMDCMPLT; add_to_free_list: @@ -796,18 +926,19 @@ * clearing the "disconnected" bit so we don't "find" it by accident later. */ mesgin_identify: -.if ( WIDE ) - and A,0x0f; /* lun in lower four bits */ -.else - and A,0x07; /* lun in lower three bits */ -.endif + + if ((p->features & AHC_WIDE) != 0) { + and A,0x0f; /* lun in lower four bits */ + } else { + and A,0x07; /* lun in lower three bits */ + } or SAVED_TCL,A; /* SAVED_TCL should be complete now */ call get_untagged_SCBID; cmp ARG_1, SCB_LIST_NULL je snoop_tag; -.if ( SCB_PAGING ) - test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; -.endif + if ((p->flags & AHC_PAGESCBS) != 0) { + test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; + } /* * If the SCB was found in the disconnected list (as is * always the case in non-paging scenarios), SCBPTR is already @@ -833,19 +964,19 @@ get_tag: mvi ARG_1 call inb_next; /* tag value */ -.if ! ( SCB_PAGING ) + if ((p->flags & AHC_PAGESCBS) == 0) { index_by_tag: - mov SCBPTR,ARG_1; - test SCB_CONTROL,TAG_ENB jz not_found; - mov SCBPTR call rem_scb_from_disc_list; -.else -/* - * Ensure that the SCB the tag points to is for an SCB transaction - * to the reconnecting target. - */ + mov SCBPTR,ARG_1; + test SCB_CONTROL,TAG_ENB jz not_found; + mov SCBPTR call rem_scb_from_disc_list; + } else { + /* + * Ensure that the SCB the tag points to is for + * an SCB transaction to the reconnecting target. + */ use_retrieveSCB: - call retrieveSCB; -.endif + call retrieveSCB; + } setup_SCB: mov A, SAVED_TCL; cmp SCB_TCL, A jne not_found_cleanup_scb; @@ -924,16 +1055,16 @@ inb_last: mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ -.if ( TARGET_MODE ) -/* - * Send a byte to an initiator in Automatic PIO mode. - * SPIOEN must be on prior to calling this routine. - */ +if ((p->flags & AHC_TARGETMODE) != 0) { + /* + * Send a byte to an initiator in Automatic PIO mode. + * SPIOEN must be on prior to calling this routine. + */ target_outb: mov SCSIDATL, SINDEX; test SSTAT0, SPIORDY jz .; ret; -.endif +} mesgin_phasemis: /* @@ -980,7 +1111,17 @@ dma_dmadone: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); dma_halt: - test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; + /* + * Some revisions of the aic7880 have a problem where, if the + * data fifo is full, but the PCI input latch is not empty, + * HDMAEN cannot be cleared. The fix used here is to attempt + * to drain the data fifo until there is space for the input + * latch to drain and HDMAEN de-asserts. + */ + if ((p->features & AHC_ULTRA2) == 0) { + mov NONE, DFDAT; + } + test DFCNTRL, HDMAEN jnz dma_halt; return: ret; @@ -1079,18 +1220,67 @@ mov ARG_1, SCB_TAG ret; mvi ARG_1, SCB_LIST_NULL ret; -set_SCBID_host_addr_and_cnt: - mov DINDEX, SINDEX; - mvi SCBID_ADDR call set_1byte_haddr_and_clrcnt; - mvi HCNT[0], 1 ret; +/* + * Fetch a byte from host memory given an index of (A + (256 * SINDEX)) + * and a base address of SCBID_ADDR. The byte is returned in RETURN_2. + */ +fetch_byte: + mov ARG_2, SINDEX; + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi CCHCNT, 1; + mvi CCSGCTL, CCSGEN|CCSGRESET; + test CCSGCTL, CCSGDONE jz .; + mvi CCSGCTL, CCSGRESET; + bmov RETURN_2, CCSGRAM, 1 ret; + } else { + mvi DINDEX, HADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi HCNT[0], 1; + clr HCNT[1]; + clr HCNT[2]; + mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + call dma_finish; + mov RETURN_2, DFDAT ret; + } + +/* + * Prepare the hardware to post a byte to host memory given an + * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR. + */ +post_byte_setup: + mov ARG_2, SINDEX; + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi CCHCNT, 1; + mvi CCSCBCTL, CCSCBRESET ret; + } else { + mvi DINDEX, HADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi HCNT[0], 1; + clr HCNT[1]; + clr HCNT[2]; + mvi DFCNTRL, FIFORESET ret; + } + +post_byte: + if ((p->features & AHC_CMD_CHAN) != 0) { + bmov CCSCBRAM, SINDEX, 1; + or CCSCBCTL, CCSCBEN|CCSCBRESET; + test CCSCBCTL, CCSCBDONE jz .; + clr CCSCBCTL ret; + } else { + mov DFDAT, SINDEX; + or DFCNTRL, HDMAEN|FIFOFLUSH; + jmp dma_finish; + } get_SCBID_from_host: mov A, SAVED_TCL; - mvi UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; - - call dma_finish; - mov ARG_1, DFDAT ret; + mvi UNTAGGEDSCB_OFFSET call fetch_byte; + mov RETURN_1, RETURN_2 ret; phase_lock: test SSTAT1, REQINIT jz phase_lock; @@ -1116,65 +1306,102 @@ mov DINDIR, SINDIR ret; /* - * Setup haddr and count assuming that A is an - * index into an array of 32byte objects. + * Setup addr assuming that A is an index into + * an array of 32byte objects, SINDEX contains + * the base address of that array, and DINDEX + * contains the base address of the location + * to store the indexed address. */ -set_32byte_haddr_and_clrcnt: - shr DINDEX, 3, A; +set_32byte_addr: + shr ARG_2, 3, A; shl A, 5; -set_1byte_haddr_and_clrcnt: /* DINDEX must be 0 upon call */ - add HADDR[0], A, SINDIR; - mov A, DINDEX; - adc HADDR[1], A, SINDIR; +/* + * Setup addr assuming that A + (ARG_1 * 256) is an + * index into an array of 1byte objects, SINDEX contains + * the base address of that array, and DINDEX contains + * the base address of the location to store the computed + * address. + */ +set_1byte_addr: + add DINDIR, A, SINDIR; + mov A, ARG_2; + adc DINDIR, A, SINDIR; clr A; - adc HADDR[2], A, SINDIR; - adc HADDR[3], A, SINDIR; - /* Clear Count */ - clr HCNT[1]; - clr HCNT[2] ret; + adc DINDIR, A, SINDIR; + adc DINDIR, A, SINDIR ret; +/* + * Either post or fetch and SCB from host memory based on the + * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX. + */ dma_scb: - /* - * SCB index is in SINDEX. Determine the physical address in - * the host where this SCB is located and load HADDR with it. - */ mov A, SINDEX; - mvi HSCB_ADDR call set_32byte_haddr_and_clrcnt; - mvi HCNT[0], 28; - mov DFCNTRL, DMAPARAMS; - test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; - /* Fill it with the SCB data */ + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi HSCB_ADDR call set_32byte_addr; + mov CCSCBPTR, SCBPTR; + mvi CCHCNT, 32; + test DMAPARAMS, DIRECTION jz dma_scb_tohost; + mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .; + jmp dma_scb_finish; +dma_scb_tohost: + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { + mvi CCSCBCTL, CCSCBRESET; + bmov CCSCBRAM, SCB_CONTROL, 32; + or CCSCBCTL, CCSCBEN|CCSCBRESET; + test CCSCBCTL, CCSCBDONE jz .; + } else { + mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; + } +dma_scb_finish: + clr CCSCBCTL; + test CCSCBCTL, CCARREN|CCSCBEN jnz .; + ret; + } else { + mvi DINDEX, HADDR; + mvi HSCB_ADDR call set_32byte_addr; + mvi HCNT[0], 32; + clr HCNT[1]; + clr HCNT[2]; + mov DFCNTRL, DMAPARAMS; + test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; + /* Fill it with the SCB data */ copy_scb_tofifo: - mvi SINDEX, SCB_CONTROL; - add A, 28, SINDEX; + mvi SINDEX, SCB_CONTROL; + add A, 32, SINDEX; copy_scb_tofifo_loop: - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - cmp SINDEX, A jne copy_scb_tofifo_loop; - or DFCNTRL, HDMAEN|FIFOFLUSH; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + cmp SINDEX, A jne copy_scb_tofifo_loop; + or DFCNTRL, HDMAEN|FIFOFLUSH; dma_scb_fromhost: - call dma_finish; - /* If we were putting the SCB, we are done */ - test DMAPARAMS, DIRECTION jz return; - mvi SCB_CONTROL call dfdat_in_7; - call dfdat_in_7_continued; - call dfdat_in_7_continued; - jmp dfdat_in_7_continued; + call dma_finish; + /* If we were putting the SCB, we are done */ + test DMAPARAMS, DIRECTION jz return; + mvi SCB_CONTROL call dfdat_in_7; + call dfdat_in_7_continued; + call dfdat_in_7_continued; + jmp dfdat_in_7_continued; dfdat_in_7: - mov DINDEX,SINDEX; + mov DINDEX,SINDEX; dfdat_in_7_continued: - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT ret; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT ret; + } + /* * Wait for DMA from host memory to data FIFO to complete, then disable @@ -1188,13 +1415,13 @@ ret; add_scb_to_free_list: -.if ( SCB_PAGING ) - mov SCB_NEXT, FREE_SCBH; - mov FREE_SCBH, SCBPTR; -.endif + if ((p->flags & AHC_PAGESCBS) != 0) { + mov SCB_NEXT, FREE_SCBH; + mov FREE_SCBH, SCBPTR; + } mvi SCB_TAG, SCB_LIST_NULL ret; -.if ( SCB_PAGING ) +if ((p->flags & AHC_PAGESCBS) != 0) { get_free_or_disc_scb: cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; @@ -1211,7 +1438,7 @@ dequeue_free_scb: mov SCBPTR, FREE_SCBH; mov FREE_SCBH, SCB_NEXT ret; -.endif +} add_scb_to_disc_list: /* diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/bsd_q.h linux/drivers/scsi/aic7xxx/bsd_q.h --- v2.1.124/linux/drivers/scsi/aic7xxx/bsd_q.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/bsd_q.h Thu Oct 8 08:07:33 1998 @@ -0,0 +1,507 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $Id: queue.h,v 1.21 1998/05/12 03:55:25 gibbs Exp $ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * slingly-linked tail queues, lists, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may only be traversed in the forward direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ CIRCLEQ + * _HEAD + + + + + + * _ENTRY + + + + + + * _INIT + + + + + + * _EMPTY + + + + + + * _FIRST + + - + + + * _NEXT + + - + + + * _PREV - - - + + + * _LAST - - - + + + * _FOREACH + + - + - + * _INSERT_HEAD + + + + + + * _INSERT_BEFORE - + - + + + * _INSERT_AFTER + + + + + + * _INSERT_TAIL - - + + + + * _REMOVE_HEAD + - + - - + * _REMOVE + + + + + + * + */ + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +#define SLIST_INIT(head) { \ + (head)->slh_first = NULL; \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while( curelm->field.sle_next != (elm) ) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (0) + +/* + * Singly-linked Tail queue definitions. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (0) + +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_LAST(head) (*(head)->stqh_last) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if (((elm)->field.stqe_next = (tqelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (tqelm)->field.stqe_next = (elm); \ +} while (0) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = \ + (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD(head, field); \ + } \ + else { \ + struct type *curelm = (head)->stqh_first; \ + while( curelm->field.stqe_next != (elm) ) \ + curelm = curelm->field.stqe_next; \ + if((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (0) + +/* + * List definitions. + */ + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for((var) = (head)->lh_first; (var); (var) = (var)->field.le_next) + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FOREACH(var, head, field) \ + for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field)) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) + +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = (head)->cqh_first; \ + (var) != (void *)(head); \ + (var) = (var)->field.cqe_next) + +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_LAST(head) ((head)->cqh_last) + +#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next) + +#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (0) + +#ifdef KERNEL + +/* + * XXX insque() and remque() are an old way of handling certain queues. + * They bogusly assumes that all queue heads look alike. + */ + +struct quehead { + struct quehead *qh_link; + struct quehead *qh_rlink; +}; + +#ifdef __GNUC__ + +static __inline void +insque(void *a, void *b) +{ + struct quehead *element = a, *head = b; + + element->qh_link = head->qh_link; + element->qh_rlink = head; + head->qh_link = element; + element->qh_link->qh_rlink = element; +} + +static __inline void +remque(void *a) +{ + struct quehead *element = a; + + element->qh_link->qh_rlink = element->qh_rlink; + element->qh_rlink->qh_link = element->qh_link; + element->qh_rlink = 0; +} + +#else /* !__GNUC__ */ + +void insque __P((void *a, void *b)); +void remque __P((void *a)); + +#endif /* __GNUC__ */ + +#endif /* KERNEL */ + +#endif /* !_SYS_QUEUE_H_ */ diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h --- v2.1.124/linux/drivers/scsi/aic7xxx/sequencer.h Tue Apr 14 14:29:21 1998 +++ linux/drivers/scsi/aic7xxx/sequencer.h Thu Oct 8 08:07:33 1998 @@ -2,7 +2,7 @@ * Instruction formats for the sequencer program downloaded to * Aic7xxx SCSI host adapters * - * Copyright (c) 1997 Justin T. Gibbs. + * Copyright (c) 1997, 1998 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,42 +37,44 @@ */ struct ins_format1 { - unsigned char immediate; - unsigned char source; - unsigned char destination; - unsigned char opcode_ret; -#define DOWNLOAD_CONST_IMMEDIATE 0x80 + unsigned int immediate : 8, + source : 9, + destination : 9, + ret : 1, + opcode : 4, + parity : 1; }; struct ins_format2 { - unsigned char shift_control; - unsigned char source; - unsigned char destination; - unsigned char opcode_ret; -#define RETURN_BIT 0x01 + unsigned int shift_control : 8, + source : 9, + destination : 9, + ret : 1, + opcode : 4, + parity : 1; }; struct ins_format3 { - unsigned char immediate; - unsigned char source; - unsigned char address; - unsigned char opcode_addr; -#define ADDR_HIGH_BIT 0x01 + unsigned int immediate : 8, + source : 9, + address : 10, + opcode : 4, + parity : 1; }; -#ifndef __KERNEL__ -struct instruction { - union { +union ins_formats { struct ins_format1 format1; struct ins_format2 format2; struct ins_format3 format3; unsigned char bytes[4]; - } format; - u_int srcline; + unsigned int integer; +}; +struct instruction { + union ins_formats format; + unsigned int srcline; struct symbol *patch_label; STAILQ_ENTRY(instruction) links; }; -#endif #define AIC_OP_OR 0x0 #define AIC_OP_AND 0x1 @@ -80,6 +82,7 @@ #define AIC_OP_ADD 0x3 #define AIC_OP_ADC 0x4 #define AIC_OP_ROL 0x5 +#define AIC_OP_BMOV 0x6 #define AIC_OP_JMP 0x8 #define AIC_OP_JC 0x9 diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- v2.1.124/linux/drivers/scsi/aic7xxx.c Mon Sep 28 10:51:34 1998 +++ linux/drivers/scsi/aic7xxx.c Thu Oct 8 08:07:34 1998 @@ -98,7 +98,7 @@ /*+M************************************************************************** * - * Further driver modifications made by Doug Ledford + * Further driver modifications made by Doug Ledford * * Copyright (c) 1997-1998 Doug Ledford * @@ -163,6 +163,147 @@ * *_M*************************************************************************/ +/* + * The next three defines are user configurable. These should be the only + * defines a user might need to get in here and change. There are other + * defines buried deeper in the code, but those really shouldn't need touched + * under normal conditions. + */ + +/* + * AIC7XXX_FAKE_NEGOTIATION_CMDS + * We now have two distinctly different methods of device negotiation + * in this code. The two methods are selected by either defining or not + * defining this option. The difference is as follows: + * + * With AIC7XXX_FAKE_NEGOTIATION_CMDS not set (commented out) + * When the driver is in need of issuing a negotiation command for any + * given device, it will add the negotiation message on to part of a + * regular SCSI command for the device. In the process, if the device + * is configured for and using tagged queueing, then the code will + * also issue that single command as a non-tagged command, attach the + * negotiation message to that one command, and use a temporary + * queue depth of one to keep the untagged and tagged commands from + * overlapping. + * Pros: This doesn't use any extra SCB structures, it's simple, it + * works most of the time (if not all of the time now), and + * since we get the device capability info frmo the INQUIRY data + * now, shouldn't cause any problems. + * Cons: When we need to send a negotiation command to a device, we + * must use a command that is being sent to LUN 0 of the device. + * If we try sending one to high LUN numbers, then some devices + * get noticeably upset. Since we have to wait for a command with + * LUN == 0 to come along, we may not be able to renegotiate when + * we want if the user is actually using say LUN 1 of a CD Changer + * instead of using LUN 0 for an extended period of time. + * + * With AIC7XXX_FAKE_NEGOTIATION_CMDS defined + * When we need to negotiate with a device, instead of attaching our + * negotiation message to an existing command, we insert our own + * fictional Scsi_Cmnd into the chain that has the negotiation message + * attached to it. We send this one command as untagged regardless + * of the device type, and we fiddle with the queue depth the same as + * we would with the option unset to avoid overlapping commands. The + * primary difference between this and the unset option is that the + * negotiation message is no longer attached to a specific command, + * instead it is its own command and is merely triggered by a + * combination of both A) We need to negotiate and B) The mid level + * SCSI code has sent us a command. We still don't do any negotiation + * unless there is a valid SCSI command to be processed. + * Pros: This fixes the problem above in the Cons section. Since we + * issue our own fake command, we can set the LUN to 0 regardless + * of what the LUN is in the real command. It also means that if + * the device get's nasty over negotiation issues, it won't be + * showing up on a regular command, so we won't get any SENSE buffer + * data or STATUS_BYTE returns to the mid level code that are caused + * by snits in the negotiation code. + * Cons: We add more code, and more complexity. This means more ways + * in which things could break. It means a larger driver. It means + * more resource consumption for the fake commands. However, the + * biggest problem is this. Take a system where there is a CD-ROM + * on the SCSI bus. Someone has a CD in the CD-ROM and is using it. + * For some reason the SCSI bus gets reset. We don't touch the + * CD-ROM again for quite a period of time (so we don't renegotiate + * after the reset until we do touch the CD-ROM again). In the + * time while we aren't using the CD-ROM, the current disc is + * removed and a new one put in. When we go to check that disc, we + * will first have to renegotiate. In so doing, we issue our fake + * SCSI command, which happens to be TEST_UNIT_READY. The CD-ROM + * negotiates with us, then responds to our fake command with a + * CHECK_CONDITION status. We REQUEST_SENSE from the CD-ROM, it + * then sends the SENSE data to our fake command to tell it that + * it has been through a disc change. There, now we've cleared out + * the SENSE data along with our negotiation command, and when the + * real command executes, it won't pick up that the CD was changed. + * That's the biggest Con to this approach. In the future, I could + * probably code around this problem though, so this option is still + * viable. + * + * So, which command style should you use? I would appreciate it if people + * could try out both types. I want to know about any cases where one + * method works and the other doesn't. If one method works on significantly + * more systems than another, then it will become the default. If the second + * option turns out to work best, then I'll find a way to work around that + * big con I listed. + * + * -- July 7, 02:33 + * OK...I just added some code that should make the Con listed for the + * fake commands a non issue now. However, it needs testing. For now, + * I'm going to make the default to use the fake commands, we'll see how + * it goes. + */ + +#define AIC7XXX_FAKE_NEGOTIATION_CMDS + +/* + * AIC7XXX_STRICT_PCI_SETUP + * Should we assume the PCI config options on our controllers are set with + * sane and proper values, or should we be anal about our PCI config + * registers and force them to what we want? The main advantage to + * defining this option is on non-Intel hardware where the BIOS may not + * have been run to set things up, or if you have one of the BIOSless + * Adaptec controllers, such as a 2910, that don't get set up by the + * BIOS. However, keep in mind that we really do set the most important + * items in the driver regardless of this setting, this only controls some + * of the more esoteric PCI options on these cards. In that sense, I + * would default to leaving this off. However, if people wish to try + * things both ways, that would also help me to know if there are some + * machines where it works one way but not another. + * + * -- July 7, 17:09 + * OK...I need this on my machine for testing, so the default is to + * leave it defined. + * + * -- July 7, 18:49 + * I needed it for testing, but it didn't make any difference, so back + * off she goes. + * + * -- July 16, 23:04 + * I turned it back on to try and compensate for the 2.1.x PCI code + * which no longer relies solely on the BIOS and now tries to set + * things itself. + */ + +#define AIC7XXX_STRICT_PCI_SETUP + +/* + * AIC7XXX_VERBOSE_DEBUGGING + * This option enables a lot of extra printk();s in the code, surrounded + * by if (aic7xxx_verbose ...) statements. Executing all of those if + * statements and the extra checks can get to where it actually does have + * an impact on CPU usage and such, as well as code size. Disabling this + * define will keep some of those from becoming part of the code. + * + * NOTE: Currently, this option has no real effect, I will be adding the + * various #ifdef's in the code later when I've decided a section is + * complete and no longer needs debugging. OK...a lot of things are now + * surrounded by this define, so turning this off does have an impact. + */ + +/* + * #define AIC7XXX_VERBOSE_DEBUGGING + */ + #ifdef MODULE #include #endif @@ -187,10 +328,10 @@ #include "hosts.h" #include "aic7xxx.h" +#include "aic7xxx/bsd_q.h" #include "aic7xxx/sequencer.h" #include "aic7xxx/scsi_message.h" #include "aic7xxx_reg.h" -#include "aic7xxx_seq.h" #include #include /* for kmalloc() */ @@ -209,7 +350,7 @@ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.0.20" +#define AIC7XXX_C_VERSION "5.1.0" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -242,17 +383,15 @@ # include #endif -#if defined(__powerpc__) || defined(__i386__) -# define MMAPIO -#endif - #if defined(__powerpc__) +# define MMAPIO # ifdef mb # undef mb # endif # define mb() \ __asm__ __volatile__("eieio" ::: "memory") #elif defined(__i386__) +# define MMAPIO # ifdef mb # undef mb # endif @@ -330,7 +469,7 @@ * tagged queue value array is always active now. I've added * a setup option to set this particular array and I'm hoping * insmod will be smart enough to set it properly as well. It's - * by use of this array that a person can disable tagged queueing. + * by use of this array that a person can enable tagged queueing. * The DEFAULT_TAG_COMMANDS define has been changed to disable * tagged queueing by default, so if your devices can handle tagged * queueing you will need to add a line to their lilo.conf file like: @@ -396,6 +535,9 @@ */ /* + * NOTE: The below structure is for reference only, the actual structure + * to modify in order to change things is located around line + * number 1305 adapter_tag_info_t aic7xxx_tag_info[] = { {DEFAULT_TAG_COMMANDS}, @@ -429,7 +571,11 @@ "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */ - "Adaptec AIC-7895 Ultra SCSI host adapter" /* AIC_7895 */ + "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */ + "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */ + "Adaptec AHA-294X Ultra2 SCSI host adapter", /* AIC_7890 */ + "Adaptec AIC-7896/7 Ultra2 SCSI host adapter", /* AIC_7896 */ + "Adaptec AHA-394X Ultra2 SCSI host adapter" /* AIC_7897 */ }; /* @@ -467,10 +613,10 @@ /* * Standard EISA Host ID regs (Offset from slot base) */ -#define HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */ -#define HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */ -#define HID2 0x82 /* product */ -#define HID3 0x83 /* firmware revision */ +#define AHC_HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */ +#define AHC_HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */ +#define AHC_HID2 0x82 /* product */ +#define AHC_HID3 0x83 /* firmware revision */ /* * AIC-7770 I/O range to reserve for a card @@ -494,11 +640,13 @@ #define LATTIME 0x0000FF00ul #define DEVCONFIG 0x40 -#define SCBSIZE32 0x00010400ul /* aic789X only */ +#define SCBSIZE32 0x00010000ul /* aic789X only */ #define MPORTMODE 0x00000400ul /* aic7870 only */ #define RAMPSM 0x00000200ul /* aic7870 only */ +#define RAMPSM_ULTRA2 0x00000004 #define VOLSENSE 0x00000100ul #define SCBRAMSEL 0x00000080ul +#define SCBRAMSEL_ULTRA2 0x00000008 #define MRDCEN 0x00000040ul #define EXTSCBTIME 0x00000020ul /* aic7870 only */ #define EXTSCBPEN 0x00000010ul /* aic7870 only */ @@ -507,6 +655,8 @@ #define STPWLEVEL 0x00000002ul #define DIFACTNEGEN 0x00000001ul /* aic7870 only */ +#define SCAMCTL 0x1a /* Ultra2 only */ +#define CCSCBBADDR 0xf0 /* aic7895/6/7 */ /* * Define the different types of SEEPROMs on aic7xxx adapters @@ -569,7 +719,9 @@ #define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */ #define CFRESETB 0x0040 /* reset SCSI bus at boot */ #define CFBPRIMARY 0x0100 /* Channel B primary on 7895 chipsets */ -/* UNUSED 0xFE80 */ +#define CFSEAUTOTERM 0x0400 /* aic7890 Perform SE Auto Term */ +#define CFLVDSTERM 0x0800 /* aic7890 LVD Termination */ +/* UNUSED 0xF280 */ unsigned short adapter_control; /* word 17 */ /* @@ -682,7 +834,6 @@ typedef enum { SCB_FREE = 0x0000, - SCB_WDTR_16BIT = 0x0001, SCB_WAITINGQ = 0x0002, SCB_ACTIVE = 0x0004, SCB_SENSE = 0x0008, @@ -691,11 +842,12 @@ SCB_RESET = 0x0040, SCB_RECOVERY_SCB = 0x0080, SCB_WAS_BUSY = 0x0100, + SCB_MSGOUT_SENT = 0x0200, SCB_MSGOUT_SDTR = 0x0400, SCB_MSGOUT_WDTR = 0x0800, - SCB_MSGOUT_WDTR_8BIT = 0x0800, - SCB_MSGOUT_WDTR_16BIT = 0x0801, - SCB_MSGOUT_BITS = SCB_MSGOUT_SDTR | SCB_MSGOUT_WDTR_16BIT, + SCB_MSGOUT_BITS = SCB_MSGOUT_SENT | + SCB_MSGOUT_SDTR | + SCB_MSGOUT_WDTR, SCB_QUEUED_ABORT = 0x1000, SCB_QUEUED_FOR_DONE = 0x2000 } scb_flag_type; @@ -711,7 +863,9 @@ AHC_EXTEND_TRANS_A = 0x00000100, AHC_EXTEND_TRANS_B = 0x00000200, AHC_TERM_ENB_A = 0x00000400, + AHC_TERM_ENB_SE_LOW = 0x00000400, AHC_TERM_ENB_B = 0x00000800, + AHC_TERM_ENB_SE_HIGH = 0x00000800, AHC_HANDLING_REQINITS = 0x00001000, AHC_TARGETMODE = 0x00002000, AHC_NEWEEPROM_FMT = 0x00004000, @@ -726,45 +880,62 @@ AHC_B_SCANNED = 0x00200000, AHC_MULTI_CHANNEL = 0x00400000, AHC_BIOS_ENABLED = 0x00800000, - AHC_ABORT_PENDING = 0x02000000, - AHC_RESET_PENDING = 0x04000000, + AHC_SEEPROM_FOUND = 0x01000000, + AHC_TERM_ENB_LVD = 0x02000000, + AHC_ABORT_PENDING = 0x04000000, + AHC_RESET_PENDING = 0x08000000, #define AHC_IN_ISR_BIT 28 AHC_IN_ISR = 0x10000000, AHC_IN_ABORT = 0x20000000, - AHC_IN_RESET = 0x40000000 + AHC_IN_RESET = 0x40000000, + AHC_EXTERNAL_SRAM = 0x80000000 } ahc_flag_type; typedef enum { - AHC_NONE = 0x00000000, - AHC_ULTRA = 0x00000001, - AHC_WIDE = 0x00000002, - AHC_TWIN = 0x00000008, - AHC_AIC7770 = 0x00000010, - AHC_AIC7850 = 0x00000020, - AHC_AIC7860 = 0x00000021, - AHC_AIC7870 = 0x00000040, - AHC_AIC7880 = 0x00000041, - AHC_AIC7895 = 0x00000081, - AHC_AIC78x0 = 0x000000E0, - AHC_274 = 0x00000110, - AHC_284 = 0x00000210, - AHC_294AU = 0x00000421, - AHC_294 = 0x00000440, - AHC_294U = 0x00000441, - AHC_394 = 0x00000840, - AHC_394U = 0x00000841, - AHC_394AU = 0x00000881, - AHC_398 = 0x00001040, - AHC_398U = 0x00001041, - AHC_39x = 0x00001880 -} ahc_type; + AHC_NONE = 0x0000, + AHC_CHIPID_MASK = 0x00ff, + AHC_AIC7770 = 0x0001, + AHC_AIC7850 = 0x0002, + AHC_AIC7860 = 0x0003, + AHC_AIC7870 = 0x0004, + AHC_AIC7880 = 0x0005, + AHC_AIC7890 = 0x0006, + AHC_AIC7895 = 0x0007, + AHC_AIC7896 = 0x0008, + AHC_VL = 0x0100, + AHC_EISA = 0x0200, + AHC_PCI = 0x0400, +} ahc_chip; + +typedef enum { + AHC_FENONE = 0x0000, + AHC_ULTRA = 0x0001, + AHC_ULTRA2 = 0x0002, + AHC_WIDE = 0x0004, + AHC_TWIN = 0x0008, + AHC_MORE_SRAM = 0x0010, + AHC_CMD_CHAN = 0x0020, + AHC_QUEUE_REGS = 0x0040, + AHC_SG_PRELOAD = 0x0080, + AHC_SPIOCAP = 0x0100, + AHC_AIC7770_FE = AHC_FENONE, + AHC_AIC7850_FE = AHC_SPIOCAP, + AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP, + AHC_AIC7870_FE = AHC_FENONE, + AHC_AIC7880_FE = AHC_ULTRA, + AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2| + AHC_QUEUE_REGS|AHC_SG_PRELOAD, + AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA, + AHC_AIC7896_FE = AHC_AIC7890_FE, +} ahc_feature; struct aic7xxx_scb { struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ struct aic7xxx_scb *q_next; /* next scb in queue */ - scb_flag_type flags; /* current state of scb */ + volatile scb_flag_type flags; /* current state of scb */ struct hw_scatterlist *sg_list; /* SG list in adapter format */ + void *kmalloc_ptr; unsigned char tag_action; unsigned char sg_count; unsigned char sense_cmd[6]; /* @@ -792,28 +963,50 @@ { ILLHADDR, "Illegal Host Access" }, { ILLSADDR, "Illegal Sequencer Address referenced" }, { ILLOPCODE, "Illegal Opcode in sequencer program" }, - { SQPARERR, "Sequencer Ram Parity Error" } + { SQPARERR, "Sequencer Ram Parity Error" }, + { DPARERR, "Data-Path Ram Parity Error" }, + { MPARERR, "Scratch Ram/SCB Array Ram Parity Error" }, + { PCIERRSTAT,"PCI Error detected" }, + { CIOPARERR, "CIOBUS Parity Error" } }; static unsigned char generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 }; typedef struct { - struct aic7xxx_hwscb *hscbs; scb_queue_type free_scbs; /* * SCBs assigned to free slot on * card (no paging required) */ + struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; + struct aic7xxx_hwscb *hscbs; unsigned char numscbs; /* current number of scbs */ unsigned char maxhscbs; /* hardware scbs */ unsigned char maxscbs; /* max scbs including pageable scbs */ - struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; + void *hscb_kmalloc_ptr; } scb_data_type; +struct target_cmd { + unsigned char mesg_bytes[4]; + unsigned char command[28]; +}; + +#define AHC_TRANS_CUR 0x0001 +#define AHC_TRANS_ACTIVE 0x0002 +#define AHC_TRANS_GOAL 0x0004 +#define AHC_TRANS_USER 0x0008 +#define AHC_TRANS_QUITE 0x0010 typedef struct { - unsigned char period; - unsigned char offset; -} syncinfo_type; + unsigned char cur_width; + unsigned char goal_width; + unsigned char cur_period; + unsigned char goal_period; + unsigned char cur_offset; + unsigned char goal_offset; + unsigned char user_width; + unsigned char user_period; + unsigned char user_offset; +} transinfo_type; /* * Define a structure used for each host adapter. Note, in order to avoid @@ -841,15 +1034,18 @@ spinlock_t spin_lock; #endif volatile unsigned char cpu_lock_count[NR_CPUS]; - ahc_type type; /* card type */ + ahc_chip chip; /* chip type */ + ahc_feature features; /* chip features */ unsigned long last_reset; unsigned long isr_count; /* Interrupt count */ unsigned long spurious_int; + struct target_cmd *targetcmds; + unsigned int num_targetcmds; unsigned short discenable; /* Targets allowed to disconnect */ unsigned short tagenable; /* Targets using tagged I/O */ unsigned short orderedtag; /* Ordered Q tags allowed */ volatile unsigned char activescbs; /* active scbs */ - unsigned char max_activescbs; + volatile unsigned char max_activescbs; unsigned char unpause; /* unpause value for HCNTRL */ unsigned char pause; /* pause value for HCNTRL */ volatile unsigned char qoutfifonext; @@ -870,7 +1066,7 @@ #define DEVICE_SCANNED 0x80 volatile unsigned char dev_flags[MAX_TARGETS]; volatile unsigned char dev_active_cmds[MAX_TARGETS]; - unsigned char dev_temp_queue_depth[MAX_TARGETS]; + volatile unsigned char dev_temp_queue_depth[MAX_TARGETS]; unsigned char dev_commands_sent[MAX_TARGETS]; /* @@ -917,7 +1113,7 @@ #define MSG_TYPE_INITIATOR_MSGIN 0x02 unsigned char msg_len; /* Length of message */ unsigned char msg_index; /* Index into msg_buf array */ - syncinfo_type syncinfo[MAX_TARGETS]; + transinfo_type transinfo[MAX_TARGETS]; volatile scb_queue_type waiting_scbs; /* * SCBs waiting for space in * the QINFIFO. @@ -945,10 +1141,10 @@ volatile unsigned char qoutfifo[256]; volatile unsigned char qinfifo[256]; unsigned int irq; /* IRQ for this adapter */ - unsigned short needsdtr; - unsigned short sdtr_pending; - unsigned short needwdtr; - unsigned short wdtr_pending; + volatile unsigned short needsdtr; + volatile unsigned short sdtr_pending; + volatile unsigned short needwdtr; + volatile unsigned short wdtr_pending; int instance; /* aic7xxx instance number */ int scsi_id; /* host adapter SCSI ID */ int scsi_id_b; /* channel B for twin adapters */ @@ -965,8 +1161,10 @@ #endif unsigned char pci_bus; unsigned char pci_device_fn; + struct seeprom_config sc; + unsigned short sc_type; + unsigned short sc_size; -#ifdef AIC7XXX_PROC_STATS /* * Statistics Kept: * @@ -980,18 +1178,21 @@ * * NOTE: Enabling this feature is likely to cause a noticeable performance * decrease as the accesses into the stats structures blows apart multiple - * cache lines and is CPU time consuming. + * cache lines and is CPU time consuming. We keep the xfer count always + * for use by the aic7xxx_proc.c code, but only do the bins if the + * proc stats code is enabled. */ struct aic7xxx_xferstats { long xfers; /* total xfer count */ long w_total; /* total writes */ long w_total512; /* 512 byte blocks written */ - long w_bins[10]; /* binned write */ long r_total; /* total reads */ long r_total512; /* 512 byte blocks read */ +#ifdef AIC7XXX_PROC_STATS + long w_bins[10]; /* binned write */ long r_bins[10]; /* binned reads */ - } stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */ #endif /* AIC7XXX_PROC_STATS */ + } stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */ }; /* @@ -999,29 +1200,33 @@ * Provides a mapping of transfer periods in ns/4 to the proper value to * stick in the SCSIRATE reg to use that transfer rate. */ -static struct { - short period; +#define AHC_SYNCRATE_ULTRA2 0 +#define AHC_SYNCRATE_ULTRA 2 +#define AHC_SYNCRATE_FAST 5 +static struct aic7xxx_syncrate { /* Rates in Ultra mode have bit 8 of sxfr set */ #define ULTRA_SXFR 0x100 - short rate; - const char *english; + int sxfr_ultra2; + int sxfr; + unsigned char period; + const char *rate[2]; } aic7xxx_syncrates[] = { - { 12, 0x100, "20.0" }, - { 15, 0x110, "16.0" }, - { 18, 0x120, "13.4" }, - { 25, 0x000, "10.0" }, - { 31, 0x010, "8.0" }, - { 37, 0x020, "6.67" }, - { 43, 0x030, "5.7" }, - { 50, 0x040, "5.0" }, - { 56, 0x050, "4.4" }, - { 62, 0x060, "4.0" }, - { 68, 0x070, "3.6" } + { 0x13, 0x000, 10, {"40.0", "80.0"} }, + { 0x14, 0x000, 11, {"33.0", "66.6"} }, + { 0x15, 0x100, 12, {"20.0", "40.0"} }, + { 0x16, 0x110, 15, {"16.0", "32.0"} }, + { 0x17, 0x120, 18, {"13.4", "26.8"} }, + { 0x18, 0x000, 25, {"10.0", "20.0"} }, + { 0x19, 0x010, 31, {"8.0", "16.0"} }, + { 0x1a, 0x020, 37, {"6.67", "13.3"} }, + { 0x1b, 0x030, 43, {"5.7", "11.4"} }, + { 0x10, 0x040, 50, {"5.0", "10.0"} }, + { 0x00, 0x050, 56, {"4.4", "8.8" } }, + { 0x00, 0x060, 62, {"4.0", "8.0" } }, + { 0x00, 0x070, 68, {"3.6", "7.2" } }, + { 0x00, 0x000, 0, {NULL, NULL} }, }; -static int num_aic7xxx_syncrates = - sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]); - #define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1), \ (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ ((scb->hscb)->target_channel_lun & 0x07) @@ -1041,58 +1246,170 @@ /* * XXX - these options apply unilaterally to _all_ 274x/284x/294x - * cards in the system. This should be fixed. + * cards in the system. This should be fixed. Exceptions to this + * rule are noted in the comments. */ -static int aic7xxx_7895_irq_hack = -1; /* This enables a hack to fix - * IRQ settings on buggy 7895 - * MB controller setups - * -1 == Disable this hack - * 0 == Use the Channel A IRQ - * 1 == Use the Channel B IRQ - */ -static unsigned int aic7xxx_extended = 0; /* extended translation on? */ -static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */ -static int aic7xxx_irq_trigger = -1; /* - * -1 use board setting - * 0 use edge triggered - * 1 use level triggered - */ -static int aic7xxx_reverse_scan = 0; /* - * Set this to anything but 0 - * to make the probe code - * reverse the order of PCI - * devices - */ -static int aic7xxx_override_term = 0; /* - * Set this to non-0 to make the - * driver override any BIOS - * configured termination - * settings based upon the - * results of the cable detect - * logic. This only applies - * to cards that have cable - * detection logic and a SEEPROM - */ -static int aic7xxx_panic_on_abort = 0; /* - * Set this to non-0 in order - * to force the driver to panic - * the kernel and print out - * debugging info on an abort - * or reset call into the - * driver. - */ -static int aic7xxx_pci_parity = 0; /* - * Set this to: - * 0 - Shut off PCI parity check - * -1 - Normal parity check - * anything else - reverse pci - * pci parity checking - */ + + +/* + * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This + * has no effect on any later resets that might occur due to things like + * SCSI bus timeouts. + */ +static unsigned int aic7xxx_no_reset = 0; +/* + * Certain PCI motherboards will scan PCI devices from highest to lowest, + * others scan from lowest to highest, and they tend to do all kinds of + * strange things when they come into contact with PCI bridge chips. The + * net result of all this is that the PCI card that is actually used to boot + * the machine is very hard to detect. Most motherboards go from lowest + * PCI slot number to highest, and the first SCSI controller found is the + * one you boot from. The only exceptions to this are when a controller + * has its BIOS disabled. So, we by default sort all of our SCSI controllers + * from lowest PCI slot number to highest PCI slot number. We also force + * all controllers with their BIOS disabled to the end of the list. This + * works on *almost* all computers. Where it doesn't work, we have this + * option. Setting this option to non-0 will reverse the order of the sort + * to highest first, then lowest, but will still leave cards with their BIOS + * disabled at the very end. That should fix everyone up unless there are + * really strange cirumstances. + */ +static int aic7xxx_reverse_scan = 0; +/* + * This setting enables a hack to fix the IRQ settings on buggy 7895 + * MB controller setups: + * -1 == Disable this hack + * 0 == Use the Channel A IRQ for both channels + * 1 == Use the Channel B IRQ for both channels + */ +static unsigned int aic7xxx_extended = 0; +/* + * The IRQ trigger method used on EISA controllers. Does not effect PCI cards. + * -1 = Use detected settings. + * 0 = Force Edge triggered mode. + * 1 = Force Level triggered mode. + */ +static int aic7xxx_irq_trigger = -1; +/* + * This variable is used to override the termination settings on a controller. + * This should not be used under normal conditions. However, in the case + * that a controller does not have a readable SEEPROM (so that we can't + * read the SEEPROM settings directly) and that a controller has a buggered + * version of the cable detection logic, this can be used to force the + * correct termination. It is preferable to use the manual termination + * settings in the BIOS if possible, but some motherboard controllers store + * those settings in a format we can't read. In other cases, auto term + * should also work, but the chipset was put together with no auto term + * logic (common on motherboard controllers). In those cases, we have + * 32 bits here to work with. That's good for 8 controllers/channels. The + * bits are organized as 4 bits per channel, with scsi0 getting the lowest + * 4 bits in the int. A 1 in a bit position indicates the termination setting + * that corresponds to that bit should be enabled, a 0 is disabled. + * It looks something like this: + * + * 0x0f = 1111-Single Ended Low Byte Termination on/off + * ||\-Single Ended High Byte Termination on/off + * |\-LVD Low Byte Termination on/off + * \-LVD High Byte Termination on/off + * + * For non-Ultra2 controllers, the upper 2 bits are not important. So, to + * enable both high byte and low byte termination on scsi0, I would need to + * make sure that the override_term variable was set to 0x03 (bits 0011). + * To make sure that all termination is enabled on an Ultra2 controller at + * scsi2 and only high byte termination on scsi1 and high and low byte + * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011) + * + * For the most part, users should never have to use this, that's why I + * left it fairly cryptic instead of easy to understand. If you need it, + * most likely someone will be telling you what your's needs to be set to. + */ +static int aic7xxx_override_term = -1; +/* + * Certain motherboard chipset controllers tend to screw + * up the polarity of the term enable output pin. Use this variable + * to force the correct polarity for your system. This is a bitfield variable + * similar to the previous one, but this one has one bit per channel instead + * of four. + * 0 = Force the setting to active low. + * 1 = Force setting to active high. + * Most Adaptec cards are active high, several motherboards are active low. + * To force a 2940 card at SCSI 0 to active high and a motherboard 7895 + * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3 + * to active high, you would need to set stpwlev=0x9 (bits 1001). + * + * People shouldn't need to use this, but if you are experiencing lots of + * SCSI timeout problems, this may help. There is one sure way to test what + * this option needs to be. Using a boot floppy to boot the system, configure + * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and + * if needed then also pass a value to override_term to make sure that the + * driver is enabling SCSI termination, then set this variable to either 0 + * or 1. When the driver boots, make sure there are *NO* SCSI cables + * connected to your controller. If it finds and inits the controller + * without problem, then the setting you passed to stpwlev was correct. If + * the driver goes into a reset loop and hangs the system, then you need the + * other setting for this variable. If neither setting lets the machine + * boot then you have definite termination problems that may not be fixable. + */ +static int aic7xxx_stpwlev = -1; +/* + * Set this to non-0 in order to force the driver to panic the kernel + * and print out debugging info on a SCSI abort or reset cycle. + */ +static int aic7xxx_panic_on_abort = 0; +/* + * PCI bus parity checking of the Adaptec controllers. This is somewhat + * dubious at best. To my knowledge, this option has never actually + * solved a PCI parity problem, but on certain machines with broken PCI + * chipset configurations, it can generate tons of false error messages. + * It's included in the driver for completeness. + * 0 = Shut off PCI parity check + * -1 = Normal polarity pci parity checking + * 1 = reverse polarity pci parity checking + * + * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this + * variable to -1 you would actually want to simply pass the variable + * name without a number. That will invert the 0 which will result in + * -1. + */ +static int aic7xxx_pci_parity = 0; +/* + * Set this to any non-0 value to cause us to dump the contents of all + * the card's registers in a hex dump format tailored to each model of + * controller. + * + * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION. + * YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES + * ONLY + */ +static int aic7xxx_dump_card = 0; +/* + * Set this to a non-0 value to make us dump out the 32 bit instruction + * registers on the card after completing the sequencer download. This + * allows the actual sequencer download to be verified. It is possible + * to use this option and still boot up and run your system. This is + * only intended for debugging purposes. + */ +static int aic7xxx_dump_sequencer = 0; +/* + * Certain newer motherboards have put new PCI based devices into the + * IO spaces that used to typically be occupied by VLB or EISA cards. + * This overlap can cause these newer motherboards to lock up when scanned + * for older EISA and VLB devices. Setting this option to non-0 will + * cause the driver to skip scanning for any VLB or EISA controllers and + * only support the PCI controllers. NOTE: this means that if the kernel + * os compiled with PCI support disabled, then setting this to non-0 + * would result in never finding any devices :) + */ +static int aic7xxx_no_probe = 0; + /* * So that insmod can find the variable and make it point to something */ #ifdef MODULE static char * aic7xxx = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,18) +MODULE_PARM(aic7xxx, "s"); +#endif /* * Just in case someone uses commas to separate items on the insmod @@ -1138,9 +1455,9 @@ #define VERBOSE_SCSIINT 0x0004 #define VERBOSE_PROBE 0x0008 #define VERBOSE_PROBE2 0x0010 -#define VERBOSE_QUEUE 0x0020 +#define VERBOSE_NEGOTIATION2 0x0020 #define VERBOSE_MINOR_ERROR 0x0040 -#define VERBOSE_QUEUE_FULL 0x0080 +#define VERBOSE_TRACING 0x0080 #define VERBOSE_ABORT 0x0f00 #define VERBOSE_ABORT_MID 0x0100 #define VERBOSE_ABORT_FIND 0x0200 @@ -1157,49 +1474,95 @@ /**************************************************************************** * - * These functions are not used yet, but when we do memory mapped - * IO, we'll use them then. + * We're going to start putting in function declarations so that order of + * functions is no longer important. As needed, they are added here. + * + ***************************************************************************/ + +static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd); +static void aic7xxx_print_card(struct aic7xxx_host *p); +static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p); +#ifdef AIC7XXX_VERBOSE_DEBUGGING +static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded); +static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer); +#endif + +/**************************************************************************** + * + * These functions are now used. They happen to be wrapped in useless + * inb/outb port read/writes around the real reads and writes because it + * seems that certain very fast CPUs have a problem dealing with us when + * going at full speed. * ***************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +static inline void +mdelay(int milliseconds) +{ + int i; + + for(i=0; imaddr) + { x = p->maddr[port]; + } else + { x = inb(p->base + port); + } mb(); return(x); +#else + return(inb(p->base + port)); +#endif } static inline void aic_outb(struct aic7xxx_host *p, unsigned char val, long port) { +#ifdef MMAPIO if(p->maddr) + { p->maddr[port] = val; + } else + { outb(val, p->base + port); + } mb(); +#else + outb(val, p->base + port); +#endif } -static inline void +static void aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size) { +#ifdef MMAPIO if(p->maddr) { int i; - for (i=0; i < size; i++) { p->maddr[port] = valp[i]; } + mb(); } else outsb(p->base + port, valp, size); - mb(); +#else + outsb(p->base + port, valp, size); +#endif } /*+F************************************************************************* @@ -1227,10 +1590,13 @@ { "irq_trigger", &aic7xxx_irq_trigger }, { "verbose", &aic7xxx_verbose }, { "reverse_scan",&aic7xxx_reverse_scan }, - { "7895_irq_hack", &aic7xxx_7895_irq_hack }, { "override_term", &aic7xxx_override_term }, + { "stpwlev", &aic7xxx_stpwlev }, + { "no_probe", &aic7xxx_no_probe }, { "panic_on_abort", &aic7xxx_panic_on_abort }, { "pci_parity", &aic7xxx_pci_parity }, + { "dump_card", &aic7xxx_dump_card }, + { "dump_sequencer", &aic7xxx_dump_sequencer }, { "tag_info", NULL } }; @@ -1327,7 +1693,7 @@ } else if (!strncmp(p, "verbose", n)) { - *(options[i].flag) = 0xff69; + *(options[i].flag) = 0xff09; } else { @@ -1387,54 +1753,64 @@ static inline void restart_sequencer(struct aic7xxx_host *p) { - /* Set the sequencer address to 0. */ - aic_outb(p, 0, SEQADDR0); - aic_outb(p, 0, SEQADDR1); - - /* - * Reset and unpause the sequencer. The reset is suppose to - * start the sequencer running, so we immediately do a pause_sequencer - * since some of our code expects the sequencer paused after a restart - */ - aic_outb(p, SEQRESET | FASTMODE, SEQCTL); - pause_sequencer(p); + aic_outb(p, FASTMODE|SEQRESET, SEQCTL); } +/* + * We include the aic7xxx_seq.c file here so that the other defines have + * already been made, and so that it comes before the code that actually + * downloads the instructions (since we don't typically use function + * prototype, our code has to be ordered that way, it's a left-over from + * the original driver days.....I should fix it some time DL). + */ +#include "aic7xxx_seq.c" /*+F************************************************************************* * Function: - * aic7xxx_next_patch + * aic7xxx_check_patch * * Description: - * Find the next patch to download. + * See if the next patch to download should be downloaded. *-F*************************************************************************/ -static struct sequencer_patch * -aic7xxx_next_patch(struct sequencer_patch *cur_patch, int options, int instrptr) +static int +aic7xxx_check_patch(struct aic7xxx_host *p, + struct sequencer_patch **start_patch, int start_instr, int *skip_addr) { - while (cur_patch != NULL) + struct sequencer_patch *cur_patch; + struct sequencer_patch *last_patch; + int num_patches; + + num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch); + last_patch = &sequencer_patches[num_patches]; + cur_patch = *start_patch; + + while ((cur_patch < last_patch) && (start_instr == cur_patch->begin)) { - if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE)) - || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE)) - || (instrptr >= cur_patch->end)) + if (cur_patch->patch_func(p) == 0) { /* - * Either we want to keep this section of code, or we have consumed - * this patch. Skip to the next patch. + * Start rejecting code. */ - cur_patch++; - if (cur_patch->options == 0) - { - /* Out of patches. */ - cur_patch = NULL; - } + *skip_addr = start_instr + cur_patch->skip_instr; + cur_patch += cur_patch->skip_patch; } else { - /* Found an OK patch. */ - break; + /* + * Found an OK patch. Advance the patch pointer to the next patch + * and wait for our instruction pointer to get here. + */ + cur_patch++; } } - return (cur_patch); + + *start_patch = cur_patch; + if (start_instr < *skip_addr) + /* + * Still skipping + */ + return (0); + return(1); } @@ -1446,15 +1822,21 @@ * Find the next patch to download. *-F*************************************************************************/ static void -aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr) +aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, + unsigned char *dconsts) { + union ins_formats instr; + struct ins_format1 *fmt1_ins; + struct ins_format3 *fmt3_ins; unsigned char opcode; - struct ins_format3 instr; - unsigned char dconsts[4] = { 0, 0, 0, 0 }; - instr = *(struct ins_format3 *) &seqprog[instrptr * 4]; + instr = *(union ins_formats*) &seqprog[instrptr * 4]; + + fmt1_ins = &instr.format1; + fmt3_ins = NULL; + /* Pull the opcode */ - opcode = (instr.opcode_addr & ~DOWNLOAD_CONST_IMMEDIATE) >> 1; + opcode = instr.format1.opcode; switch (opcode) { case AIC_OP_JMP: @@ -1466,44 +1848,86 @@ case AIC_OP_JE: case AIC_OP_JZ: { + struct sequencer_patch *cur_patch; int address_offset; unsigned int address; - struct sequencer_patch *patch; + int skip_addr; int i; + fmt3_ins = &instr.format3; address_offset = 0; - address = instr.address; - address |= (instr.opcode_addr & ADDR_HIGH_BIT) << 8; - for (i = 0; i < NUMBER(sequencer_patches); i++) + address = fmt3_ins->address; + cur_patch = sequencer_patches; + skip_addr = 0; + + for (i = 0; i < address;) { - patch = &sequencer_patches[i]; - if ((((patch->options & options) == 0) && (patch->negative == FALSE)) || - (((patch->options & options) != 0) && (patch->negative == TRUE))) + aic7xxx_check_patch(p, &cur_patch, i, &skip_addr); + if (skip_addr > i) { - if (address >= patch->end) - { - address_offset += patch->end - patch->begin; - } + int end_addr; + + end_addr = MIN(address, skip_addr); + address_offset += end_addr - i; + i = skip_addr; + } + else + { + i++; } } address -= address_offset; - instr.address = address & 0xFF; - instr.opcode_addr &= ~ADDR_HIGH_BIT; - instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT; + fmt3_ins->address = address; + /* Fall Through to the next code section */ } - /* Fall through */ case AIC_OP_OR: case AIC_OP_AND: case AIC_OP_XOR: case AIC_OP_ADD: case AIC_OP_ADC: - if (instr.opcode_addr & DOWNLOAD_CONST_IMMEDIATE) + case AIC_OP_BMOV: + if (fmt1_ins->parity != 0) { - instr.immediate = dconsts[instr.immediate]; + fmt1_ins->immediate = dconsts[fmt1_ins->immediate]; } - instr.opcode_addr &= ~DOWNLOAD_CONST_IMMEDIATE; + fmt1_ins->parity = 0; + /* Fall Through to the next code section */ case AIC_OP_ROL: - aic_outsb(p, SEQRAM, &instr.immediate, 4); + if ((p->features & AHC_ULTRA2) != 0) + { + int i, count; + + /* Calculate odd parity for the instruction */ + for ( i=0, count=0; i < 31; i++) + { + unsigned int mask; + + mask = 0x01 << i; + if ((instr.integer & mask) != 0) + count++; + } + if (!(count & 0x01)) + instr.format1.parity = 1; + } + else + { + if (fmt3_ins != NULL) + { + instr.integer = fmt3_ins->immediate | + (fmt3_ins->source << 8) | + (fmt3_ins->address << 16) | + (fmt3_ins->opcode << 25); + } + else + { + instr.integer = fmt1_ins->immediate | + (fmt1_ins->source << 8) | + (fmt1_ins->destination << 16) | + (fmt1_ins->ret << 24) | + (fmt1_ins->opcode << 25); + } + } + aic_outsb(p, SEQRAM, instr.bytes, 4); break; default: @@ -1523,104 +1947,100 @@ static void aic7xxx_loadseq(struct aic7xxx_host *p) { - int options; struct sequencer_patch *cur_patch; int i; int downloaded; + int skip_addr; + unsigned char download_consts[4] = {0, 0, 0, 0}; if (aic7xxx_verbose & VERBOSE_PROBE) { printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no); } - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("\n"); - options = 1; /* Code for all options. */ + download_consts[TMODE_NUMCMDS] = p->num_targetcmds; + cur_patch = &sequencer_patches[0]; downloaded = 0; - if (p->type & AHC_ULTRA) - { - options |= ULTRA; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option ULTRA\n", - p->host_no); - } - if (p->type & AHC_TWIN) - { - options |= TWIN_CHANNEL; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option " - "TWIN_CHANNEL\n", p->host_no); - } - if (p->type & AHC_WIDE) - { - options |= WIDE; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option WIDE\n", - p->host_no); - } - /* if (p->scb_data->maxscbs > p->scb_data->maxhscbs) this should always - be true, don't test, - just do. */ - { - options |= SCB_PAGING; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option SCB_PAGING\n", - p->host_no); - } - /* We don't actually support target mode yet, so leave this out - if (p->flags & AHC_TARGETMODE) - options |= TARGET_MODE; */ - - if ( (options & ~(ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01)) ) - { - printk(KERN_INFO "(scsi%d) Unknown bits set in the options field, " - "correcting.\n", p->host_no); - options &= ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01; - } - + skip_addr = 0; - cur_patch = sequencer_patches; - aic_outb(p, PERRORDIS | LOADRAM, SEQCTL); + aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL); aic_outb(p, 0, SEQADDR0); aic_outb(p, 0, SEQADDR1); for (i = 0; i < sizeof(seqprog) / 4; i++) { - cur_patch = aic7xxx_next_patch(cur_patch, options, i); - if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i)) + if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0) { /* Skip this instruction for this configuration. */ continue; } - aic7xxx_download_instr(p, options, i); + aic7xxx_download_instr(p, i, &download_consts[0]); downloaded++; } - aic_outb(p, FASTMODE, SEQCTL); + aic_outb(p, FASTMODE|SEQRESET, SEQCTL); + if (aic7xxx_verbose & VERBOSE_PROBE) + { + printk(" %d instructions downloaded\n", downloaded); + } +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_dump_sequencer) + aic7xxx_print_sequencer(p, downloaded); +#endif +} + +#ifdef AIC7XXX_VERBOSE_DEBUGGING +/*+F************************************************************************* + * Function: + * aic7xxx_print_sequencer + * + * Description: + * Print the contents of the sequencer memory to the screen. + *-F*************************************************************************/ +static void +aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded) +{ + int i, k, temp; + + aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL); aic_outb(p, 0, SEQADDR0); aic_outb(p, 0, SEQADDR1); - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Download complete,", p->host_no); - - if (aic7xxx_verbose & VERBOSE_PROBE) + k = 0; + for (i=0; i < downloaded; i++) { - printk(" %d instructions downloaded\n", downloaded); + if ( k == 0 ) + printk("%03x: ", i); + temp = aic_inb(p, SEQRAM); + temp |= (aic_inb(p, SEQRAM) << 8); + temp |= (aic_inb(p, SEQRAM) << 16); + temp |= (aic_inb(p, SEQRAM) << 24); + printk("%08x", temp); + if ( ++k == 8 ) + { + printk("\n"); + k = 0; + } + else + printk(" "); } + aic_outb(p, FASTMODE|SEQRESET, SEQCTL); + printk("\n"); } +#endif /*+F************************************************************************* * Function: * aic7xxx_delay * * Description: - * Delay for specified amount of time. We use udelay because the timer + * Delay for specified amount of time. We use mdelay because the timer * interrupt is not guaranteed to be enabled. This will cause an * infinite loop since jiffies (clock ticks) is not updated. *-F*************************************************************************/ static void aic7xxx_delay(int seconds) { - mdelay(seconds*1000); + mdelay(seconds * 1000); } /*+F************************************************************************* @@ -1652,170 +2072,345 @@ return(bp); } - /*+F************************************************************************* * Function: - * aic7xxx_scsirate + * aic7xxx_find_syncrate * * Description: * Look up the valid period to SCSIRATE conversion in our table *-F*************************************************************************/ -static unsigned char -aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate, - unsigned char *period, unsigned char *offset, int target, int channel, - int set) +static struct aic7xxx_syncrate * +aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period, + unsigned int maxsync) { - int i = num_aic7xxx_syncrates; - unsigned char response_period; - unsigned char tindex; - unsigned short target_mask; - unsigned char lun; - - tindex = target | (channel << 3); - target_mask = 0x01 << tindex; - lun = aic_inb(p, SCB_TCL) & 0x07; - - response_period = *period; + struct aic7xxx_syncrate *syncrate; - /* - * If the offset is 0, then the device is requesting asynchronous - * transfers. - */ - if ((*period != 0) && (*offset != 0)) + syncrate = &aic7xxx_syncrates[maxsync]; + while ( (syncrate->rate[0] != NULL) && + (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) ) { - for (i = 0; i < num_aic7xxx_syncrates; i++) + if ( *period <= syncrate->period ) { - if (*period <= aic7xxx_syncrates[i].period) + /* + * When responding to a target that requests sync, the requested rate + * may fall between two rates that we can output, but still be a rate + * that we can receive. Because of this, we want to respond with the + * same rate that it sent to us even if the persiod we use to send + * data to it is lower. Only lower the response period if we must. + */ + if(syncrate == &aic7xxx_syncrates[maxsync]) { - /* - * Watch out for Ultra speeds when ultra is not enabled and - * vice-versa. - */ - if (!(p->type & AHC_ULTRA) && - (aic7xxx_syncrates[i].rate & ULTRA_SXFR)) - { - /* - * This should only happen if the drive is the first to negotiate - * and chooses a high rate. We'll just move down the table until - * we hit a non ultra speed. - */ - continue; - } - *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F); - *period = aic7xxx_syncrates[i].period; - - /* - * When responding to a target that requests - * sync, that rate may fall between two rates - * that we can output, but still be a rate - * that we can receive. Because of this, - * we may want to respond to the target with - * the same rate that it sent to us even - * if the period we use to send data to it - * is lower. Only lower the response period - * if we must. - */ - if ((i == 0) || - ((aic7xxx_syncrates[i-1].rate & ULTRA_SXFR) != 0 - && (p->type & AHC_ULTRA) == 0)) - { - response_period = *period; - } - - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) - { - printk(INFO_LEAD "Synchronous at %sMHz, " - "offset %d.\n", p->host_no, channel, target, lun, - aic7xxx_syncrates[i].english, *offset); - p->dev_flags[tindex] &= ~ DEVICE_PRINT_SDTR; - } - break; + *period = syncrate->period; } + break; } + syncrate++; } - - if (i >= num_aic7xxx_syncrates) + if ( (*period == 0) || (syncrate->rate[0] == NULL) || + ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) ) { /* - * Use asynchronous transfers. + * Use async transfers for this target */ - *scsirate = 0; *period = 0; - *offset = 0; - response_period = 0; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) - { - printk(INFO_LEAD "Using asynchronous transfers.\n", - p->host_no, channel, target, lun); - p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR; - } + syncrate = NULL; } + return (syncrate); +} - /* - * Ensure Ultra mode is set properly for this target. - */ - if ( (*scsirate != 0) && - (aic7xxx_syncrates[i].rate & ULTRA_SXFR) ) + +/*+F************************************************************************* + * Function: + * aic7xxx_find_period + * + * Description: + * Look up the valid SCSIRATE to period conversion in our table + *-F*************************************************************************/ +static unsigned int +aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate, + unsigned int maxsync) +{ + struct aic7xxx_syncrate *syncrate; + + if ((p->features & AHC_ULTRA2) != 0) { - p->ultraenb |= target_mask; + scsirate &= SXFR_ULTRA2; } else { - p->ultraenb &= ~target_mask; + scsirate &= SXFR; } - if (set) - { - unsigned char sxfrctl0; - sxfrctl0 = aic_inb(p, SXFRCTL0); - sxfrctl0 &= ~FAST20; - if (p->ultraenb & target_mask) + syncrate = &aic7xxx_syncrates[maxsync]; + while (syncrate->rate[0] != NULL) + { + if ((p->features & AHC_ULTRA2) != 0) { - sxfrctl0 |= FAST20; + if (syncrate->sxfr_ultra2 == 0) + break; + else if (scsirate == syncrate->sxfr_ultra2) + return (syncrate->period); + } + else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR)) + { + return (syncrate->period); } - aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB); - aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1); - aic_outb(p, sxfrctl0, SXFRCTL0); + syncrate++; } - return(response_period); + return (0); /* async */ } /*+F************************************************************************* * Function: - * scbq_init + * aic7xxx_validate_offset * * Description: - * SCB queue initialization. - * + * Set a valid offset value for a particular card in use and transfer + * settings in use. *-F*************************************************************************/ -static inline void -scbq_init(volatile scb_queue_type *queue) +static void +aic7xxx_validate_offset(struct aic7xxx_host *p, + struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide) { - queue->head = NULL; - queue->tail = NULL; + unsigned int maxoffset; + + /* Limit offset to what the card (and device) can do */ + if (syncrate == NULL) + { + maxoffset = 0; + } + else if (p->features & AHC_ULTRA2) + { + maxoffset = MAX_OFFSET_ULTRA2; + } + else + { + if (wide) + maxoffset = MAX_OFFSET_16BIT; + else + maxoffset = MAX_OFFSET_8BIT; + } + *offset = MIN(*offset, maxoffset); } /*+F************************************************************************* * Function: - * scbq_insert_head + * aic7xxx_set_syncrate * * Description: - * Add an SCB to the head of the list. - * + * Set the actual syncrate down in the card and in our host structs *-F*************************************************************************/ -static inline void -scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb) +static void +aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate, + int target, int channel, unsigned int period, unsigned int offset, + unsigned int type) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) - unsigned long cpu_flags; -#endif + unsigned char tindex; + unsigned short target_mask; + unsigned char lun; + unsigned int old_period, old_offset; - DRIVER_LOCK - scb->q_next = queue->head; - queue->head = scb; - if (queue->tail == NULL) /* If list was empty, update tail. */ + tindex = target | (channel << 3); + target_mask = 0x01 << tindex; + lun = aic_inb(p, SCB_TCL) & 0x07; + + if (syncrate == NULL) + { + period = 0; + offset = 0; + } + + old_period = p->transinfo[tindex].cur_period; + old_offset = p->transinfo[tindex].cur_offset; + + + if (type & AHC_TRANS_CUR) + { + unsigned int scsirate; + + scsirate = aic_inb(p, TARG_SCSIRATE + tindex); + if (p->features & AHC_ULTRA2) + { + scsirate &= ~SXFR_ULTRA2; + if (syncrate != NULL) + { + scsirate |= syncrate->sxfr_ultra2; + } + if (type & AHC_TRANS_ACTIVE) + { + aic_outb(p, offset, SCSIOFFSET); + } + aic_outb(p, offset, TARG_OFFSET + tindex); + } + else /* Not an Ultra2 controller */ + { + scsirate &= ~(SXFR|SOFS); + p->ultraenb &= ~target_mask; + if (syncrate != NULL) + { + if (syncrate->sxfr & ULTRA_SXFR) + { + p->ultraenb |= target_mask; + } + scsirate |= (syncrate->sxfr & SXFR); + scsirate |= (offset & SOFS); + } + if (type & AHC_TRANS_ACTIVE) + { + unsigned char sxfrctl0; + + sxfrctl0 = aic_inb(p, SXFRCTL0); + sxfrctl0 &= ~FAST20; + if (p->ultraenb & target_mask) + sxfrctl0 |= FAST20; + aic_outb(p, sxfrctl0, SXFRCTL0); + } + aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB); + aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 ); + } + if (type & AHC_TRANS_ACTIVE) + { + aic_outb(p, scsirate, SCSIRATE); + } + aic_outb(p, scsirate, TARG_SCSIRATE + tindex); + p->transinfo[tindex].cur_period = period; + p->transinfo[tindex].cur_offset = offset; + if ( !(type & AHC_TRANS_QUITE) && + (aic7xxx_verbose & VERBOSE_NEGOTIATION) && + (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) + { + if (offset) + { + int rate_mod = (scsirate & WIDEXFER) ? 1 : 0; + + printk(INFO_LEAD "Synchronous at %s Mbyte/sec, " + "offset %d.\n", p->host_no, channel, target, lun, + syncrate->rate[rate_mod], offset); + } + else + { + printk(INFO_LEAD "Using asynchronous transfers.\n", + p->host_no, channel, target, lun); + } + p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR; + } + } + + if (type & AHC_TRANS_GOAL) + { + p->transinfo[tindex].goal_period = period; + p->transinfo[tindex].goal_offset = offset; + } + + if (type & AHC_TRANS_USER) + { + p->transinfo[tindex].user_period = period; + p->transinfo[tindex].user_offset = offset; + } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_set_width + * + * Description: + * Set the actual width down in the card and in our host structs + *-F*************************************************************************/ +static void +aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun, + unsigned int width, unsigned int type) +{ + unsigned char tindex; + unsigned short target_mask; + unsigned int old_width, new_offset; + + tindex = target | (channel << 3); + target_mask = 1 << tindex; + + old_width = p->transinfo[tindex].cur_width; + + if (p->features & AHC_ULTRA2) + new_offset = MAX_OFFSET_ULTRA2; + else if (width == MSG_EXT_WDTR_BUS_16_BIT) + new_offset = MAX_OFFSET_16BIT; + else + new_offset = MAX_OFFSET_8BIT; + + if (type & AHC_TRANS_CUR) + { + unsigned char scsirate; + + scsirate = aic_inb(p, TARG_SCSIRATE + tindex); + + scsirate &= ~WIDEXFER; + if (width == MSG_EXT_WDTR_BUS_16_BIT) + scsirate |= WIDEXFER; + + aic_outb(p, scsirate, TARG_SCSIRATE + tindex); + + if (type & AHC_TRANS_ACTIVE) + aic_outb(p, scsirate, SCSIRATE); + + p->transinfo[tindex].cur_width = width; + + if ((aic7xxx_verbose & VERBOSE_NEGOTIATION2) && + (p->dev_flags[tindex] & DEVICE_PRINT_WDTR)) + { + printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target, + lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" ); + p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR; + } + } + + if (type & AHC_TRANS_GOAL) + p->transinfo[tindex].goal_width = width; + if (type & AHC_TRANS_USER) + p->transinfo[tindex].user_width = width; + + /* + * Having just set the width, the SDTR should come next, and we need a valid + * offset for the SDTR. So, we make sure we put a valid one in here now as + * the goal_offset. + */ + if (p->transinfo[tindex].goal_offset) + p->transinfo[tindex].goal_offset = new_offset; + +} + +/*+F************************************************************************* + * Function: + * scbq_init + * + * Description: + * SCB queue initialization. + * + *-F*************************************************************************/ +static void +scbq_init(volatile scb_queue_type *queue) +{ + queue->head = NULL; + queue->tail = NULL; +} + +/*+F************************************************************************* + * Function: + * scbq_insert_head + * + * Description: + * Add an SCB to the head of the list. + * + *-F*************************************************************************/ +static inline void +scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) + unsigned long cpu_flags; +#endif + + DRIVER_LOCK + scb->q_next = queue->head; + queue->head = scb; + if (queue->tail == NULL) /* If list was empty, update tail. */ queue->tail = queue->head; DRIVER_UNLOCK } @@ -1828,7 +2423,7 @@ * Remove an SCB from the head of the list. * *-F*************************************************************************/ -static __inline struct aic7xxx_scb * +static inline struct aic7xxx_scb * scbq_remove_head(volatile scb_queue_type *queue) { struct aic7xxx_scb * scbp; @@ -2021,7 +2616,7 @@ * Description: * Set the specified target busy. *-F*************************************************************************/ -static __inline void +static inline void aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag; @@ -2035,7 +2630,7 @@ * Returns the index of the busy target, and optionally sets the * target inactive. *-F*************************************************************************/ -static __inline unsigned char +static inline unsigned char aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl, int unbusy) { @@ -2091,47 +2686,47 @@ * Description: * Get an SCB from the free list or by allocating a new one. *-F*************************************************************************/ -static struct aic7xxx_scb * -aic7xxx_allocate_scb(struct aic7xxx_host *p, int force_alloc) +static int +aic7xxx_allocate_scb(struct aic7xxx_host *p) { struct aic7xxx_scb *scbp = NULL; int scb_size = sizeof(struct aic7xxx_scb) + sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG; int i; + int step = PAGE_SIZE / 1024; unsigned long scb_count = 0; struct hw_scatterlist *hsgp; struct aic7xxx_scb *scb_ap; + unsigned long temp; - if (force_alloc == FALSE) - { - scbp = scbq_remove_head(&p->scb_data->free_scbs); - if (scbp != NULL) - return(scbp); - } - /* - * Either there wasn't an SCB or this is a strictly allocation call - */ - if (p->scb_data->numscbs < p->scb_data->maxscbs) { - /* - * Optimize for 30 scbs at a time, but allow a final allocation of - * fewer than 30 scbs. Except on 64 bit platforms, we optimize for - * 29 SCBs at a time because a pointer is 4 bytes larger and we don't - * want to overrun this suppossedly 32K allocation to 64K and waste - * tons of space. + * Calculate the optimal number of SCBs to allocate. + * + * NOTE: This formula works because the sizeof(sg_array) is always + * 1024. Therefore, scb_size * i would always be > PAGE_SIZE * + * (i/step). The (i-1) allows the left hand side of the equation + * to grow into the right hand side to a point of near perfect + * efficiency since scb_size * (i -1) is growing slightly faster + * than the right hand side. If the number of SG array elements + * is changed, this function may not be near so efficient any more. */ - if( sizeof(void *) == sizeof(int) ) - scb_count = MIN(30, p->scb_data->maxscbs - p->scb_data->numscbs); - else - scb_count = MIN(29, p->scb_data->maxscbs - p->scb_data->numscbs); - + for ( i=step;; i *= 2 ) + { + if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) ) + { + i /= 2; + break; + } + } + scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs); scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC); if (scb_ap != NULL) { - if (aic7xxx_verbose & VERBOSE_QUEUE) +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) { if (p->scb_data->numscbs == 0) printk(INFO_LEAD "Allocating initial %ld SCB structures.\n", @@ -2140,8 +2735,12 @@ printk(INFO_LEAD "Allocating %ld additional SCB structures.\n", p->host_no, -1, -1, -1, scb_count); } +#endif memset(scb_ap, 0, scb_count * scb_size); - hsgp = (struct hw_scatterlist *) &scb_ap[scb_count]; + temp = (unsigned long) &scb_ap[scb_count]; + temp += 1023; + temp &= ~1023; + hsgp = (struct hw_scatterlist *)temp; for (i=0; i < scb_count; i++) { scbp = &scb_ap[i]; @@ -2155,20 +2754,14 @@ p->scb_data->scb_array[p->scb_data->numscbs++] = scbp; scbq_insert_head(&p->scb_data->free_scbs, scbp); } + scbp->kmalloc_ptr = scb_ap; } else { - return(NULL); + return(0); } } - if (force_alloc == TRUE) - { - return((struct aic7xxx_scb *)scb_count); - } - else - { - return(scbq_remove_head(&p->scb_data->free_scbs)); - } + return(scb_count); } /*+F************************************************************************* @@ -2180,7 +2773,7 @@ * to queue completed commands, then call scsi_done() on them when * we're finished. This function queues the completed commands. *-F*************************************************************************/ -static inline void +static void aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { cmd->host_scribble = (char *)p->completeq.head; @@ -2194,7 +2787,7 @@ * Description: * Process the completed command queue. *-F*************************************************************************/ -static inline void +static void aic7xxx_done_cmds_complete(struct aic7xxx_host *p) { Scsi_Cmnd *cmd; @@ -2267,17 +2860,15 @@ } if (scb->flags & SCB_RESET) { - cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | - (cmd->result & 0xffff); + cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff); } else if (scb->flags & SCB_ABORT) { - cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | - (cmd->result & 0xffff); + cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff); } - if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) + else if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) { - if(cmd->cmnd[0] == INQUIRY) + if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) ) { char *buffer; @@ -2295,35 +2886,65 @@ #define WIDE_INQUIRY_BITS 0x60 #define SYNC_INQUIRY_BITS 0x10 if ( (buffer[7] & WIDE_INQUIRY_BITS) && - (p->needwdtr_copy & (1<type & AHC_WIDE) ) + (p->features & AHC_WIDE) ) { p->needwdtr |= (1<needwdtr_copy |= (1<syncinfo[tindex].offset = MAX_OFFSET_16BIT; + if ( (p->flags & AHC_SEEPROM_FOUND) && + (p->transinfo[tindex].user_width != MSG_EXT_WDTR_BUS_16_BIT) ) + p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT; + else + p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT; } else { p->needwdtr &= ~(1<needwdtr_copy &= ~(1<syncinfo[tindex].offset = MAX_OFFSET_8BIT; + pause_sequencer(p); + aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun, + MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE | + AHC_TRANS_GOAL | + AHC_TRANS_CUR) ); + unpause_sequencer(p, FALSE); } if (buffer[7] & SYNC_INQUIRY_BITS) { p->needsdtr |= (1<needsdtr_copy |= (1<flags & AHC_SEEPROM_FOUND) + p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period; + else if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_period = + aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period; + else if (p->features & AHC_ULTRA) + p->transinfo[tindex].goal_period = + aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period; + else + p->transinfo[tindex].goal_period = + aic7xxx_syncrates[AHC_SYNCRATE_FAST].period; + + if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) + p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + else + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; } else { p->needsdtr &= ~(1<needsdtr_copy &= ~(1<transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_offset = 0; } p->dev_flags[tindex] |= DEVICE_SCANNED; + p->dev_flags[tindex] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR; #undef WIDE_INQUIRY_BITS #undef SYNC_INQUIRY_BITS } } - if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0) + else if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0) { unsigned short mask; int message_error = FALSE; @@ -2346,7 +2967,7 @@ p->wdtr_pending &= ~mask; if (message_error) { - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && + if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) ) { printk(INFO_LEAD "Device failed to complete Wide Negotiation " @@ -2366,7 +2987,7 @@ p->sdtr_pending &= ~mask; if (message_error) { - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && + if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) { printk(INFO_LEAD "Device failed to complete Sync Negotiation " @@ -2402,12 +3023,17 @@ { scbq_insert_tail(&p->waiting_scbs, scbp); } - } - if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp ) - { - scbp = scbq_remove_head(&p->delayed_scbs[tindex]); - if (scbp) - scbq_insert_tail(&p->waiting_scbs, scbp); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n", + p->host_no, CTL_OF_SCB(scbp)); +#endif + if (queue_depth > p->dev_active_cmds[tindex]) + { + scbp = scbq_remove_head(&p->delayed_scbs[tindex]); + if (scbp) + scbq_insert_tail(&p->waiting_scbs, scbp); + } } } if ( !(scb->tag_action) && (p->tagenable & (1<hscb->target_channel_lun, TRUE); } -#ifdef AIC7XXX_PROC_STATS { int actual; @@ -2445,11 +3070,17 @@ if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK)) { struct aic7xxx_xferstats *sp; +#ifdef AIC7XXX_PROC_STATS long *ptr; int x; +#endif /* AIC7XXX_PROC_STATS */ sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7]; sp->xfers++; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (sp->xfers > 16) && (aic7xxx_verbose > 0xffff) ) + aic7xxx_verbose &= 0xffff; +#endif /* * For block devices, cmd->request.cmd is always == either READ or @@ -2462,14 +3093,19 @@ { sp->w_total++; sp->w_total512 += (actual >> 9); +#ifdef AIC7XXX_PROC_STATS ptr = sp->w_bins; +#endif /* AIC7XXX_PROC_STATS */ } else { sp->r_total++; sp->r_total512 += (actual >> 9); +#ifdef AIC7XXX_PROC_STATS ptr = sp->r_bins; +#endif /* AIC7XXX_PROC_STATS */ } +#ifdef AIC7XXX_PROC_STATS for (x = 9; x <= 17; x++) { if (actual < (1 << x)) @@ -2482,10 +3118,9 @@ { ptr[x - 9]++; } +#endif /* AIC7XXX_PROC_STATS */ } } -#endif /* AIC7XXX_PROC_STATS */ - aic7xxx_free_scb(p, scb); aic7xxx_queue_cmd_complete(p, cmd); @@ -2667,7 +3302,10 @@ { p->qinfifo[qinpos++] = SCB_LIST_NULL; } - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); return (found); } @@ -2740,7 +3378,7 @@ { case 0: min_target = 0; - max_target = (p->type & AHC_WIDE) ? 15 : 7; + max_target = (p->features & AHC_WIDE) ? 15 : 7; break; case 1: min_target = 8; @@ -2749,7 +3387,7 @@ case ALL_CHANNELS: default: min_target = 0; - max_target = (p->type & (AHC_TWIN|AHC_WIDE)) ? 15 : 7; + max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7; break; } } @@ -2812,7 +3450,7 @@ j = 0; prev_scbp = NULL; scbp = p->delayed_scbs[i].head; - while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) ) + while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) ) { prev_scbp = scbp; scbp = scbp->q_next; @@ -2837,7 +3475,7 @@ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE; } } - if ( j > p->scb_data->numscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET)) printk(WARN_LEAD "Yikes!! There's a loop in the " @@ -2871,7 +3509,7 @@ j = 0; prev_scbp = NULL; scbp = p->waiting_scbs.head; - while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) ) + while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) ) { prev_scbp = scbp; scbp = scbp->q_next; @@ -2896,7 +3534,7 @@ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE; } } - if ( j > p->scb_data->numscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET)) printk(WARN_LEAD "Yikes!! There's a loop in the " @@ -2918,7 +3556,7 @@ next = aic_inb(p, WAITING_SCBH); /* Start at head of list. */ prev = SCB_LIST_NULL; j = 0; - while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) ) + while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) ) { aic_outb(p, next, SCBPTR); scb_index = aic_inb(p, SCB_TAG); @@ -2966,7 +3604,7 @@ } } } - if ( j > p->scb_data->maxhscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { printk(WARN_LEAD "Yikes!! There is a loop in the waiting for " "selection list!\n", p->host_no, channel, target, lun); @@ -2981,13 +3619,14 @@ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS)) printk(INFO_LEAD "Cleaning disconnected scbs " "list.\n", p->host_no, channel, target, lun); + if (p->features & AHC_PAGESCBS) { unsigned char next, prev, scb_index; next = aic_inb(p, DISCONNECTED_SCBH); prev = SCB_LIST_NULL; j = 0; - while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) ) + while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) ) { aic_outb(p, next, SCBPTR); scb_index = aic_inb(p, SCB_TAG); @@ -3020,7 +3659,7 @@ } } } - if ( j > p->scb_data->maxhscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { printk(WARN_LEAD "Yikes!! There is a loop in the disconnected list!\n", p->host_no, channel, target, lun); @@ -3032,15 +3671,23 @@ * Walk the free list making sure no entries on the free list have * a valid SCB_TAG value or SCB_CONTROL byte. */ + if (p->features & AHC_PAGESCBS) { unsigned char next; j = 0; next = aic_inb(p, FREE_SCBH); - while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) ) + if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) ) + { + printk(WARN_LEAD "Bogus FREE_SCBH!.\n", p->host_no, channel, + target, lun); + init_lists = TRUE; + next = SCB_LIST_NULL; + } + while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) ) { aic_outb(p, next, SCBPTR); - if ( aic_inb(p, SCB_TAG) < p->scb_data->numscbs ) + if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs) { printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel, target, lun); @@ -3054,7 +3701,7 @@ next = aic_inb(p, SCB_NEXT); } } - if ( j > p->scb_data->maxhscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { printk(WARN_LEAD "Yikes!! There is a loop in the free list!\n", p->host_no, channel, target, lun); @@ -3072,7 +3719,7 @@ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH); aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH); } - for (i = p->scb_data->maxhscbs; i >= 0; --i) + for (i = p->scb_data->maxhscbs - 1; i >= 0; i--) { unsigned char scbid; @@ -3148,7 +3795,7 @@ aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0); aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR | CLRPHASECHG | CLRREQINIT, CLRSINT1); - aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT, CLRINT); + aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT); } /*+F************************************************************************* @@ -3161,21 +3808,26 @@ static void aic7xxx_reset_current_bus(struct aic7xxx_host *p) { - unsigned char scsiseq; /* Disable reset interrupts. */ aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1); + /* Turn off the bus' current operations, after all, we shouldn't have any + * valid commands left to cause a RSELI and SELO once we've tossed the + * bus away with this reset, so we might as well shut down the sequencer + * until the bus is restarted as oppossed to saving the current settings + * and restoring them (which makes no sense to me). */ + /* Turn on the bus reset. */ - scsiseq = aic_inb(p, SCSISEQ); - aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ); + aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ); + while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0) + mdelay(5); - mdelay(5); + mdelay(10); /* Turn off the bus reset. */ - aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ); - - mdelay(2); + aic_outb(p, 0, SCSISEQ); + mdelay(5); aic7xxx_clear_intstat(p); /* Re-enable reset interrupts. */ @@ -3193,7 +3845,7 @@ static void aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset) { - unsigned long offset, offset_max; + unsigned long offset_min, offset_max; unsigned char sblkctl; int cur_channel; @@ -3206,48 +3858,52 @@ { p->needsdtr |= (p->needsdtr_copy & 0xFF00); p->sdtr_pending &= 0x00FF; - offset = TARG_SCRATCH + 8; - offset_max = TARG_SCRATCH + 16; + offset_min = 8; + offset_max = 16; } else { - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) { p->needsdtr = p->needsdtr_copy; p->needwdtr = p->needwdtr_copy; p->sdtr_pending = 0x0; p->wdtr_pending = 0x0; - offset = TARG_SCRATCH; - offset_max = TARG_SCRATCH + 16; + offset_min = 0; + offset_max = 16; } else { /* Channel A */ p->needsdtr |= (p->needsdtr_copy & 0x00FF); p->sdtr_pending &= 0xFF00; - offset = TARG_SCRATCH; - offset_max = TARG_SCRATCH + 8; + offset_min = 0; + offset_max = 8; } } - while (offset < offset_max) + while (offset_min < offset_max) { /* * Revert to async/narrow transfers until we renegotiate. */ - u_char targ_scratch; - - targ_scratch = aic_inb(p, offset); - targ_scratch &= SXFR; - aic_outb(p, targ_scratch, offset++); + aic_outb(p, 0, TARG_SCSIRATE + offset_min); + if (p->features & AHC_ULTRA2) + { + aic_outb(p, 0, TARG_OFFSET + offset_min); + } + offset_min++; } /* * Reset the bus and unpause/restart the controller */ sblkctl = aic_inb(p, SBLKCTL); - cur_channel = (sblkctl & SELBUSB) >> 3; - if (cur_channel != channel) + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + cur_channel = (sblkctl & SELBUSB) >> 3; + else + cur_channel = 0; + if ( (cur_channel != channel) && (p->features & AHC_TWIN) ) { /* * Case 1: Command for another bus is active @@ -3264,7 +3920,7 @@ { aic7xxx_reset_current_bus(p); } - aic_outb(p, 0, SCSISEQ); + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ); aic7xxx_clear_intstat(p); aic_outb(p, sblkctl, SBLKCTL); } @@ -3285,7 +3941,7 @@ { aic7xxx_reset_current_bus(p); } - aic_outb(p, 0, SCSISEQ); + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ); aic7xxx_clear_intstat(p); } if (aic7xxx_verbose & VERBOSE_RESET_RETURN) @@ -3296,7 +3952,13 @@ */ aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL); - if ( !(p->type & AHC_TWIN) ) + /* + * Convince Mid Level SCSI code to leave us be for a little bit... + */ + p->last_reset = jiffies; + p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY)); + + if ( !(p->features & AHC_TWIN) ) { restart_sequencer(p); } @@ -3312,7 +3974,7 @@ * Scan the awaiting_scbs queue downloading and starting as many * scbs as we can. *-F*************************************************************************/ -static inline void +static void aic7xxx_run_waiting_queues(struct aic7xxx_host *p) { struct aic7xxx_scb *scb; @@ -3337,23 +3999,38 @@ tindex = TARGET_INDEX(scb->cmd); if ( !scb->tag_action && (p->tagenable & (1< 0xffff) + printk(INFO_LEAD "Reducing Queue depth for untagged command.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif p->dev_temp_queue_depth[tindex] = 1; } if ( (p->dev_active_cmds[tindex] >= p->dev_temp_queue_depth[tindex]) || (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) ) { - scbq_insert_tail(&p->delayed_scbs[tindex], scb); - if ( !(p->dev_timer[tindex].expires) && - !(p->dev_active_cmds[tindex]) ) - { - p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ); - add_timer(&p->dev_timer[tindex]); - } +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Moving SCB to Delayed Queue.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif + scbq_insert_tail(&p->delayed_scbs[tindex], scb); + if ( !(p->dev_timer[tindex].expires) && + !(p->dev_active_cmds[tindex]) ) + { + p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ); + add_timer(&p->dev_timer[tindex]); + } } else { scb->flags &= ~SCB_WAITINGQ; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Sending command %d/0x%x to QINFIFO\n", p->host_no, + CTL_OF_SCB(scb), scb->hscb->tag, scb->flags); +#endif p->dev_active_cmds[tindex]++; p->activescbs++; if ( !(scb->tag_action) ) @@ -3366,62 +4043,142 @@ } if (sent) { - pause_sequencer(p); - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); - unpause_sequencer(p, FALSE); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + printk(INFO_LEAD "Sending commands to QINFIFO\n", p->host_no, + -1, -1, -1); + if ( (p->isr_count < 16) && (aic7xxx_panic_on_abort) && + (p->flags & AHC_PAGESCBS) ) + aic7xxx_check_scbs(p, "While sending commands to QINFIFO"); + } +#endif + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + { + pause_sequencer(p); + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + unpause_sequencer(p, FALSE); + } if (p->activescbs > p->max_activescbs) p->max_activescbs = p->activescbs; } DRIVER_UNLOCK } +#ifdef CONFIG_PCI + +#define DPE 0x80 +#define SSE 0x40 +#define RMA 0x20 +#define RTA 0x10 +#define STA 0x08 +#define DPR 0x01 /*+F************************************************************************* * Function: - * aic7xxx_timer + * aic7xxx_pci_intr * * Description: - * Take expired extries off of delayed queues and place on waiting queue - * then run waiting queue to start commands. - ***************************************************************************/ + * Check the scsi card for PCI errors and clear the interrupt + * + * NOTE: If you don't have this function and a 2940 card encounters + * a PCI error condition, the machine will end up locked as the + * interrupt handler gets slammed with non-stop PCI error interrupts + *-F*************************************************************************/ static void -aic7xxx_timer(struct aic7xxx_host *p) +aic7xxx_pci_intr(struct aic7xxx_host *p) { - int i, j; - unsigned long cpu_flags = 0; - struct aic7xxx_scb *scb; + unsigned char status1; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) - DRIVER_LOCK +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1); #else - spin_lock_irqsave(&io_request_lock, cpu_flags); + pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, + PCI_STATUS + 1, &status1); #endif - for(i=0; idev_timer[i].expires) && - (p->dev_timer[i].expires <= jiffies) ) - { - p->dev_timer[i].expires = 0; - if ( (p->dev_timer[i].prev != NULL) || - (p->dev_timer[i].next != NULL) ) - { - del_timer(&p->dev_timer[i]); - } - p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i]; - j = 0; - while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) && - (j++ < p->scb_data->numscbs) ) - { - scbq_insert_tail(&p->waiting_scbs, scb); - } - if (j == p->scb_data->numscbs) - { - printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.\n", - p->host_no, 0, i, -1); - scbq_init(&p->delayed_scbs[i]); - scbq_init(&p->waiting_scbs); - /* - * Well, things are screwed now, wait for a reset to clean the junk + + if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Data Parity Error during PCI address or PCI write" + "phase.\n", p->host_no, -1, -1, -1); + if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Signal System Error Detected\n", p->host_no, + -1, -1, -1); + if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no, + -1, -1, -1); + if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no, + -1, -1, -1); + if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no, + -1, -1, -1); + if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Data Parity Error has been reported via PCI pin " + "PERR#\n", p->host_no, -1, -1, -1); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1); +#else + pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, + PCI_STATUS + 1, status1); +#endif + if (status1 & (DPR|RMA|RTA)) + aic_outb(p, CLRPARERR, CLRINT); + + if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) ) + aic7xxx_panic_abort(p, NULL); + +} +#endif /* CONFIG_PCI */ + +/*+F************************************************************************* + * Function: + * aic7xxx_timer + * + * Description: + * Take expired extries off of delayed queues and place on waiting queue + * then run waiting queue to start commands. + ***************************************************************************/ +static void +aic7xxx_timer(struct aic7xxx_host *p) +{ + int i, j; + unsigned long cpu_flags = 0; + struct aic7xxx_scb *scb; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) + DRIVER_LOCK +#else + spin_lock_irqsave(&io_request_lock, cpu_flags); +#endif + for(i=0; idev_timer[i].expires) && + (p->dev_timer[i].expires <= jiffies) ) + { + p->dev_timer[i].expires = 0; + if ( (p->dev_timer[i].prev != NULL) || + (p->dev_timer[i].next != NULL) ) + { + del_timer(&p->dev_timer[i]); + } + p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i]; + j = 0; + while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) && + (j++ < p->scb_data->numscbs) ) + { + scbq_insert_tail(&p->waiting_scbs, scb); + } + if (j == p->scb_data->numscbs) + { + printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.\n", + p->host_no, 0, i, -1); + scbq_init(&p->delayed_scbs[i]); + scbq_init(&p->waiting_scbs); + /* + * Well, things are screwed now, wait for a reset to clean the junk * out. */ } @@ -3545,12 +4302,11 @@ aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel) { unsigned short targ_mask; - unsigned char targ_scratch; - int scratch_offset = target; + unsigned char tindex = target; - scratch_offset += channel << 3; + tindex |= ((channel & 0x01) << 3); - targ_mask = (0x01 << scratch_offset); + targ_mask = (0x01 << tindex); /* * Go back to async/narrow transfers and renegotiate. */ @@ -3558,9 +4314,9 @@ p->needwdtr |= (p->needwdtr_copy & targ_mask); p->sdtr_pending &= ~targ_mask; p->wdtr_pending &= ~targ_mask; - targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset); - targ_scratch &= SXFR; - aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset); + aic_outb(p, 0, TARG_SCSIRATE + tindex); + if (p->features & AHC_ULTRA2) + aic_outb(p, 0, TARG_OFFSET + tindex); aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); if (aic7xxx_verbose & VERBOSE_RESET_PROCESS) printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel, @@ -3580,20 +4336,30 @@ { struct aic7xxx_scb *scb; unsigned short target_mask; - unsigned char target, scratch_offset, lun; + unsigned char target, lun, tindex; unsigned char queue_flag = FALSE; char channel; target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f); - channel = (aic_inb(p, SBLKCTL) >> 3) & 0x01; - scratch_offset = target + (channel << 3); + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + else + channel = 0; + tindex = target + (channel << 3); lun = aic_inb(p, SAVED_TCL) & 0x07; - target_mask = (0x01 << scratch_offset); + target_mask = (0x01 << tindex); + /* + * Go ahead and clear the SEQINT now, that avoids any interrupt race + * conditions later on in case we enable some other interrupt. + */ + aic_outb(p, CLRSEQINT, CLRINT); switch (intstat & SEQINT_MASK) { case NO_MATCH: { + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), + SCSISEQ); printk(WARN_LEAD "No active SCB for reconnecting target - Issuing " "BUS DEVICE RESET.\n", p->host_no, channel, target, lun); printk(WARN_LEAD " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n", @@ -3654,12 +4420,11 @@ p->msg_len = 0; p->msg_index = 0; - /* - * We have to clear the SEQINT *BEFORE* we set the REQINIT handler - * active or else VLB and edge triggered EISA cards could loose the - * first REQINIT and cause a bus hang/reset cycle. - */ - aic_outb(p, CLRSEQINT, CLRINT); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no, + channel, target, lun); +#endif /* * To actually receive the message, simply turn on @@ -3682,13 +4447,11 @@ * or WDTR message for this target. If we did, this is a * signal that the target is refusing negotiation. */ - unsigned char targ_scratch; unsigned char scb_index; unsigned char last_msg; scb_index = aic_inb(p, SCB_TAG); scb = p->scb_data->scb_array[scb_index]; - targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset); last_msg = aic_inb(p, LAST_MSG); if ( (last_msg == MSG_IDENTIFYFLAG) && @@ -3696,7 +4459,7 @@ !(scb->flags & SCB_MSGOUT_BITS) ) { if ((scb->tag_action == MSG_ORDERED_Q_TAG) && - (p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS)) + (p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS)) { /* * OK...the device seems able to accept tagged commands, but @@ -3718,7 +4481,7 @@ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO); } else if ( (scb->tag_action == MSG_SIMPLE_Q_TAG) && - !(p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS) ) + !(p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS) ) { unsigned char i, reset = 0; struct aic7xxx_scb *scbp; @@ -3733,8 +4496,8 @@ */ p->tagenable &= ~target_mask; p->orderedtag &= ~target_mask; - p->dev_max_queue_depth[scratch_offset] = - p->dev_temp_queue_depth[scratch_offset] = 1; + p->dev_max_queue_depth[tindex] = + p->dev_temp_queue_depth[tindex] = 1; /* * We set this command up as a bus device reset. However, we have * to clear the tag type as it's causing us problems. We shouldnt @@ -3783,40 +4546,34 @@ /* * note 8bit xfers and clear flag */ - targ_scratch &= 0x7F; p->needwdtr &= ~target_mask; p->needwdtr_copy &= ~target_mask; p->wdtr_pending &= ~target_mask; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) + scb->flags &= ~SCB_MSGOUT_BITS; + aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT, + (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR)); + aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE); + if ( (p->needsdtr_copy & target_mask) && + !(p->sdtr_pending & target_mask) ) { - printk(INFO_LEAD "Refusing WIDE negotiation; using 8 bit " - "transfers.\n", p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } - scb->flags &= ~SCB_MSGOUT_WDTR_16BIT; - p->syncinfo[scratch_offset].offset = MAX_OFFSET_8BIT; - if (p->needsdtr_copy & target_mask) - p->needsdtr |= target_mask; + p->sdtr_pending |= target_mask; + scb->flags |= SCB_MSGOUT_SDTR; + aic_outb(p, HOST_MSG, MSG_OUT); + aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); + } } else if (scb->flags & SCB_MSGOUT_SDTR) { /* * note asynch xfers and clear flag */ - targ_scratch &= 0xF0; p->needsdtr &= ~target_mask; p->needsdtr_copy &= ~target_mask; p->sdtr_pending &= ~target_mask; - p->syncinfo[scratch_offset].period = 0; - p->syncinfo[scratch_offset].offset = 0; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_SDTR) ) - { - printk(INFO_LEAD "Refusing synchronous negotiation; using " - "asynchronous transfers.\n", p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_SDTR; - } + scb->flags &= ~SCB_MSGOUT_SDTR; + aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, + (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL)); } else if (aic7xxx_verbose & VERBOSE_SEQINT) { @@ -3826,8 +4583,6 @@ printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause. " "Ignoring.\n", p->host_no, channel, target, lun); } - aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset); - aic_outb(p, targ_scratch, SCSIRATE); } break; @@ -3842,11 +4597,15 @@ * the sequencer running in the common case of command completes * without error. The sequencer will have DMA'd the SCB back * up to us, so we can reference the drivers SCB array. + * + * Set the default return value to 0 indicating not to send + * sense. The sense code will change this if needed and this + * reduces code duplication. */ + aic_outb(p, 0, RETURN_1); scb_index = aic_inb(p, SCB_TAG); if (scb_index > p->scb_data->numscbs) { - aic_outb(p, 0, RETURN_1); printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n", p->host_no, channel, target, lun, intstat, scb_index); break; @@ -3854,12 +4613,6 @@ scb = p->scb_data->scb_array[scb_index]; hscb = scb->hscb; - /* - * Set the default return value to 0 indicating not to send - * sense. The sense code will change this if needed and this - * reduces code duplication. - */ - aic_outb(p, 0, RETURN_1); if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x," @@ -3928,7 +4681,6 @@ scb->sg_count = hscb->SG_segment_count = 1; scb->sg_length = sizeof(cmd->sense_buffer); - scb->flags &= ~SCB_MSGOUT_BITS; scb->tag_action = 0; /* * This problem could be caused if the target has lost power @@ -3936,12 +4688,67 @@ * so if needed, we'll re-negotiate while doing the sense cmd. * However, if this SCB already was attempting to negotiate, * then we assume this isn't the problem and skip this part. - * - * 1998/04/23 - We also don't want to set the flag if the - * original command was a TEST_UNIT_READY since that - * implies a SEND_SENSE anyway. */ - if (scb->cmd->cmnd[0] != TEST_UNIT_READY) +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS + if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) && + (p->dev_flags[tindex] & DEVICE_SCANNED) && + !(p->wdtr_pending & target_mask) && + !(p->sdtr_pending & target_mask) ) + { + p->needwdtr |= (p->needwdtr_copy & target_mask); + p->needsdtr |= (p->needsdtr_copy & target_mask); + } + else if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) || + (scb->cmd == p->dev_sdtr_cmnd[tindex]) ) + { + /* + * This is already a negotiation command, so we must have + * already done either WDTR or SDTR (or maybe both). So + * we simply check sdtr_pending and needsdtr to see if we + * should throw out SDTR on this command. + * + * Note: Don't check the needsdtr_copy here, instead just + * check to see if WDTR wiped out our SDTR and set needsdtr. + * Even if WDTR did wipe out SDTR and set needsdtr, if + * parse_msg() then turned around and started our SDTR + * in back to back fasion, then conclusion of that should + * have negated any needsdtr setting. That's why we only + * check needsdtr and sdtr_pending. + */ + scb->flags &= ~SCB_MSGOUT_BITS; + if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) && + !(p->sdtr_pending & target_mask) && + (p->needsdtr & target_mask) ) + { + p->sdtr_pending |= target_mask; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_SDTR; + } + + /* + * This is the important part though. We are getting sense + * info back from this device. It's going into a fake + * command. We need to put that into the real command + * instead so that the mid level SCSI code can act upon it. + * So, when we set up these fake commands, the next pointer + * is used to point to the real command. Use that to change + * the address of our sense_buffer[] to the real command. + * However, don't do this if the real command is also a + * TEST_UNIT_READY as it will most likely pull down its own + * SENSE information anyway. + */ + if (cmd->next->cmnd[0] != TEST_UNIT_READY) + { + scb->sg_list[0].address = + cpu_to_le32(VIRT_TO_BUS(&cmd->next->sense_buffer[0])); + hscb->data_pointer = scb->sg_list[0].address; + } + } +#else + if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) && + !(scb->flags & SCB_MSGOUT_BITS) && + (scb->cmd->lun == 0) && + (p->dev_flags[TARGET_INDEX(scb->cmd)] & DEVICE_SCANNED) ) { if ( (p->needwdtr_copy & target_mask) && !(p->wdtr_pending & target_mask) && @@ -3950,7 +4757,7 @@ p->needwdtr |= target_mask; p->wdtr_pending |= target_mask; hscb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_WDTR_16BIT; + scb->flags |= SCB_MSGOUT_WDTR; } if ( p->needsdtr_copy & target_mask ) { @@ -3964,12 +4771,26 @@ } } } - + else + scb->flags &= ~SCB_MSGOUT_BITS; +#endif /* AIC7XXX_FAKE_NEGOTIATION_CMDS */ scb->flags |= SCB_SENSE; /* * Ensure the target is busy since this will be an * an untagged request. */ +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + if (scb->flags & SCB_MSGOUT_BITS) + printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no, + CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ? + "SDTR" : "WDTR"); + else + printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no, + CTL_OF_SCB(scb)); + } +#endif aic7xxx_busy_target(p, scb); aic_outb(p, SEND_SENSE, RETURN_1); aic7xxx_error(cmd) = DID_OK; @@ -3994,7 +4815,7 @@ */ aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL, 0, TRUE, - &p->delayed_scbs[scratch_offset]); + &p->delayed_scbs[tindex]); next_scbp = p->waiting_scbs.head; while ( next_scbp != NULL ) { @@ -4004,7 +4825,7 @@ SCB_LIST_NULL) ) { scbq_remove(&p->waiting_scbs, prev_scbp); - scbq_insert_tail(&p->delayed_scbs[scratch_offset], + scbq_insert_tail(&p->delayed_scbs[tindex], prev_scbp); } } @@ -4024,15 +4845,15 @@ { if (next_scbp->flags & SCB_WAITINGQ) { - p->dev_active_cmds[scratch_offset]++; + p->dev_active_cmds[tindex]++; p->activescbs--; - scbq_remove(&p->delayed_scbs[scratch_offset], next_scbp); + scbq_remove(&p->delayed_scbs[tindex], next_scbp); scbq_remove(&p->waiting_scbs, next_scbp); } - scbq_insert_head(&p->delayed_scbs[scratch_offset], + scbq_insert_head(&p->delayed_scbs[tindex], next_scbp); next_scbp->flags |= SCB_WAITINGQ; - p->dev_active_cmds[scratch_offset]--; + p->dev_active_cmds[tindex]--; p->activescbs--; next_hscb = aic_inb(p, SCB_NEXT); aic_outb(p, 0, SCB_CONTROL); @@ -4065,65 +4886,67 @@ aic_outb(p, active_hscb, SCBPTR); if (scb->flags & SCB_WAITINGQ) { - scbq_remove(&p->delayed_scbs[scratch_offset], scb); + scbq_remove(&p->delayed_scbs[tindex], scb); scbq_remove(&p->waiting_scbs, scb); - p->dev_active_cmds[scratch_offset]++; + p->dev_active_cmds[tindex]++; p->activescbs++; } - scbq_insert_head(&p->delayed_scbs[scratch_offset], scb); - p->dev_active_cmds[scratch_offset]--; + scbq_insert_head(&p->delayed_scbs[tindex], scb); + p->dev_active_cmds[tindex]--; p->activescbs--; scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY; - if (p->dev_timer[scratch_offset].expires == 0) + if (p->dev_timer[tindex].expires == 0) { - if ( p->dev_active_cmds[scratch_offset] ) + if ( p->dev_active_cmds[tindex] ) { - p->dev_timer[scratch_offset].expires = jiffies + (HZ * 2); - add_timer(&p->dev_timer[scratch_offset]); + p->dev_timer[tindex].expires = jiffies + (HZ * 2); + add_timer(&p->dev_timer[tindex]); } else { - p->dev_timer[scratch_offset].expires = jiffies + (HZ / 2); - add_timer(&p->dev_timer[scratch_offset]); + p->dev_timer[tindex].expires = jiffies + (HZ / 2); + add_timer(&p->dev_timer[tindex]); } } - if (aic7xxx_verbose & VERBOSE_QUEUE_FULL) +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose & VERBOSE_MINOR_ERROR) { if (queue_flag) printk(INFO_LEAD "Queue full received; queue depth %d, " "active %d\n", p->host_no, CTL_OF_SCB(scb), - p->dev_max_queue_depth[scratch_offset], - p->dev_active_cmds[scratch_offset]); + p->dev_max_queue_depth[tindex], + p->dev_active_cmds[tindex]); else printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb)); } +#endif if (queue_flag) { - p->dev_temp_queue_depth[scratch_offset] = - p->dev_active_cmds[scratch_offset]; - if ( p->dev_last_queue_full[scratch_offset] != - p->dev_active_cmds[scratch_offset] ) + p->dev_temp_queue_depth[tindex] = + p->dev_active_cmds[tindex]; + if ( p->dev_last_queue_full[tindex] != + p->dev_active_cmds[tindex] ) { - p->dev_last_queue_full[scratch_offset] = - p->dev_active_cmds[scratch_offset]; - p->dev_last_queue_full_count[scratch_offset] = 0; + p->dev_last_queue_full[tindex] = + p->dev_active_cmds[tindex]; + p->dev_last_queue_full_count[tindex] = 0; } else { - p->dev_last_queue_full_count[scratch_offset]++; + p->dev_last_queue_full_count[tindex]++; } - if ( (p->dev_last_queue_full_count[scratch_offset] > 14) && - (p->dev_active_cmds[scratch_offset] > 4) ) + if ( (p->dev_last_queue_full_count[tindex] > 14) && + (p->dev_active_cmds[tindex] > 4) ) { - if (aic7xxx_verbose & VERBOSE_NEGOTIATION) + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no, - CTL_OF_SCB(scb), p->dev_active_cmds[scratch_offset]); - p->dev_max_queue_depth[scratch_offset] = - p->dev_active_cmds[scratch_offset]; - p->dev_last_queue_full[scratch_offset] = 0; - p->dev_last_queue_full_count[scratch_offset] = 0; + CTL_OF_SCB(scb), p->dev_active_cmds[tindex]); + p->dev_max_queue_depth[tindex] = + p->dev_active_cmds[tindex]; + p->dev_last_queue_full[tindex] = 0; + p->dev_last_queue_full_count[tindex] = 0; } } break; @@ -4157,6 +4980,15 @@ * this target. */ + if ( !(scb->flags & SCB_DEVICE_RESET) && + (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG) && + (scb->hscb->control & TAG_ENB) ) + { + p->msg_buf[p->msg_index++] = scb->tag_action; + p->msg_buf[p->msg_index++] = scb->hscb->tag; + p->msg_len += 2; + } + if (scb->flags & SCB_DEVICE_RESET) { p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET; @@ -4169,12 +5001,6 @@ { if (scb->tag_action) { - if (msg_out == MSG_IDENTIFYFLAG) - { - p->msg_buf[p->msg_index++] = scb->tag_action; - p->msg_buf[p->msg_index++] = scb->hscb->tag; - p->msg_len += 2; - } p->msg_buf[p->msg_index++] = MSG_ABORT_TAG; } else @@ -4188,23 +5014,67 @@ } else if (scb->flags & SCB_MSGOUT_WDTR) { - aic7xxx_construct_wdtr(p, (scb->flags & SCB_WDTR_16BIT)); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Sending WDTR message.\n", p->host_no, + CTL_OF_SCB(scb)); +#endif + aic7xxx_construct_wdtr(p, + p->transinfo[TARGET_INDEX(scb->cmd)].goal_width); } else if (scb->flags & SCB_MSGOUT_SDTR) { - unsigned char period, offset; - + unsigned int max_sync, period; + /* + * We need to set an accurate goal_offset instead of + * the ridiculously high one we default to. We should + * now know if we are wide. Plus, the WDTR code will + * set our goal_offset for us as well. + */ + if (p->transinfo[tindex].goal_offset) + { + if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + else if (p->transinfo[tindex].cur_width == MSG_EXT_WDTR_BUS_16_BIT) + p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + else + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + } /* - * Pull the user defined setting from scratch RAM. + * Now that the device is selected, use the bits in SBLKCTL and + * SSTAT2 to determine the max sync rate for this device. */ - period = p->syncinfo[scratch_offset].period; - offset = p->syncinfo[scratch_offset].offset; - if ( (p->needsdtr_copy & target_mask) == 0) + if (p->features & AHC_ULTRA2) + { + if ( (aic_inb(p, SBLKCTL) & ENAB40) && + !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) + { + max_sync = AHC_SYNCRATE_ULTRA2; + } + else + { + max_sync = AHC_SYNCRATE_ULTRA; + } + } + else if (p->features & AHC_ULTRA) { - period = 0; - offset = 0; + max_sync = AHC_SYNCRATE_ULTRA; } - aic7xxx_construct_sdtr(p, period, offset); + else + { + max_sync = AHC_SYNCRATE_FAST; + } + period = p->transinfo[tindex].goal_period; + aic7xxx_find_syncrate(p, &period, max_sync); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no, + CTL_OF_SCB(scb), + p->transinfo[tindex].goal_period, + p->transinfo[tindex].goal_offset); +#endif + aic7xxx_construct_sdtr(p, period, + p->transinfo[tindex].goal_offset); } else { @@ -4224,7 +5094,7 @@ * have this problem since they continually interrupt the kernel * until we take care of the situation. */ - aic_outb(p, CLRSEQINT, CLRINT); + scb->flags |= SCB_MSGOUT_SENT; p->msg_index = 0; p->msg_type = MSG_TYPE_INITIATOR_MSGOUT; p->flags |= AHC_HANDLING_REQINITS; @@ -4273,6 +5143,7 @@ } break; +#if AIC7XXX_NOT_YET case TRACEPOINT: { printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel, @@ -4287,7 +5158,6 @@ } break; -#if AIC7XXX_NOT_YET /* XXX Fill these in later */ case MSG_BUFFER_BUSY: printk("aic7xxx: Message buffer busy.\n"); @@ -4307,7 +5177,6 @@ /* * Clear the sequencer interrupt and unpause the sequencer. */ - aic_outb(p, CLRSEQINT, CLRINT); unpause_sequencer(p, /* unpause always */ TRUE); } @@ -4322,14 +5191,18 @@ static int aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - int reject, done; - unsigned char target_scratch, scratch_offset; + int reject, reply, done; + unsigned char target_scsirate, tindex; unsigned short target_mask; + unsigned char target, channel, lun; - reject = done = FALSE; - scratch_offset = TARGET_INDEX(scb->cmd); - target_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset); - target_mask = (0x01 << scratch_offset); + target = scb->cmd->target; + channel = scb->cmd->channel; + lun = scb->cmd->lun; + reply = reject = done = FALSE; + tindex = TARGET_INDEX(scb->cmd); + target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex); + target_mask = (0x01 << tindex); /* * Parse as much of the message as is availible, @@ -4355,9 +5228,10 @@ { case MSG_EXT_SDTR: { - unsigned char period, response_period, offset; - unsigned char max_offset, saved_offset, rate; - + unsigned int period, offset; + unsigned char maxsync, saved_offset; + struct aic7xxx_syncrate *syncrate; + if (p->msg_buf[1] != MSG_EXT_SDTR_LEN) { reject = TRUE; @@ -4370,51 +5244,114 @@ } period = p->msg_buf[3]; - saved_offset = p->msg_buf[4]; + saved_offset = offset = p->msg_buf[4]; - if (target_scratch & WIDEXFER) + if (p->features & AHC_ULTRA2) + { + if ( (aic_inb(p, SBLKCTL) & ENAB40) && + !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) + { + maxsync = AHC_SYNCRATE_ULTRA2; + } + else + { + maxsync = AHC_SYNCRATE_ULTRA; + } + } + else if (p->features & AHC_ULTRA) { - max_offset = MAX_OFFSET_16BIT; + maxsync = AHC_SYNCRATE_ULTRA; } else { - max_offset = MAX_OFFSET_8BIT; + maxsync = AHC_SYNCRATE_FAST; } - offset = MIN(saved_offset, max_offset); - response_period = aic7xxx_scsirate(p, &rate, &period, - &offset, scb->cmd->target, scb->cmd->channel, /* set */ TRUE); - /* Preserve the WideXfer flag */ - target_scratch = rate | (target_scratch & WIDEXFER); - - /* - * Update the TARGET_SCRATCH, the SCSIRATE, and our syncinfo - * areas. - */ - aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset); - aic_outb(p, target_scratch, SCSIRATE); - p->syncinfo[scratch_offset].period = response_period; - p->syncinfo[scratch_offset].offset = offset; + +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + printk(INFO_LEAD "Finished receipt of SDTR, parsing %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + syncrate = aic7xxx_find_syncrate(p, &period, maxsync); + printk(INFO_LEAD "After find_syncrate() %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + aic7xxx_validate_offset(p, syncrate, &offset, + target_scsirate & WIDEXFER); + printk(INFO_LEAD "After validate_offset() %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + printk(INFO_LEAD "Final values of Period/Offset as set: %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + } + else + { + syncrate = aic7xxx_find_syncrate(p, &period, maxsync); + aic7xxx_validate_offset(p, syncrate, &offset, + target_scsirate & WIDEXFER); + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + } +#else + syncrate = aic7xxx_find_syncrate(p, &period, maxsync); + aic7xxx_validate_offset(p, syncrate, &offset, + target_scsirate & WIDEXFER); + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR); +#endif + if (offset == 0) + { + /* + * Uhh ohh, things fell through to async....update the goal + * items and the needsdtr_copy to reflect this... + */ + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE); + p->needsdtr_copy &= ~target_mask; + } /* * Did we start this, if not, or if we went to low and had to * go async, then send an SDTR back to the target */ p->needsdtr &= ~target_mask; - if (scb->flags & SCB_MSGOUT_SDTR) + p->sdtr_pending &= ~target_mask; + if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) == + (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) && + (offset == saved_offset) ) { - if (saved_offset != offset) - { - p->needsdtr_copy &= ~target_mask; - reject = TRUE; - } - scb->flags &= ~SCB_MSGOUT_SDTR; - p->sdtr_pending &= ~target_mask; + scb->flags &= ~SCB_MSGOUT_BITS; } else { + /* + * Send a reply SDTR back. Even if we sent the first one, it + * is valid to send another one out immediately to re-negotiate + * things, and a few devices don't like getting rejects after + * we already sent them one SDTR. Just send an SDTR for async + * this time if need be (or for the correct params if we didn't + * start all of this). If this is a Reject Reply type message, + * then we've put the async settings into the goal area for + * future reference (when we get the AWAITING_MSG interrupt). + * If this is a case where we are responding to the target's + * initiated SDTR, then leave our own goal and user values in + * place (unless the device hasn't been scanned yet, in which + * case, put the user values into the goal values so we don't + * send out an Async message). + */ + if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) ) + { + p->transinfo[tindex].goal_width = + p->transinfo[tindex].user_width; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->needwdtr_copy |= target_mask; + p->needsdtr_copy |= target_mask; + } scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_SDTR; - p->sdtr_pending |= target_mask; aic_outb(p, HOST_MSG, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); } @@ -4437,112 +5374,111 @@ } bus_width = p->msg_buf[3]; - p->needwdtr &= ~target_mask; - if (scb->flags & SCB_MSGOUT_WDTR) + if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) == + (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) ) { switch(bus_width) { default: { reject = TRUE; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) + if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && + ((p->dev_flags[tindex] & DEVICE_PRINT_WDTR) || + (aic7xxx_verbose > 0xffff)) ) { printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n", p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; + p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR; } } /* We fall through on purpose */ case MSG_EXT_WDTR_BUS_8_BIT: { bus_width = MSG_EXT_WDTR_BUS_8_BIT; p->needwdtr_copy &= ~target_mask; - target_scratch &= 0x7f; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using narrow (8 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } break; } case MSG_EXT_WDTR_BUS_16_BIT: { - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using wide (16 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } - target_scratch |= WIDEXFER; break; } } - scb->flags &= ~SCB_MSGOUT_WDTR_16BIT; + scb->flags &= ~SCB_MSGOUT_BITS; p->wdtr_pending &= ~target_mask; - /* - * By virtue of the SCSI spec, a WDTR message negates any existing - * SDTR negotiations. So, even if needsdtr isn't marked for this - * device, we still have to do a new SDTR message if the device - * supports SDTR at all. Therefore, we check needsdtr_copy instead - * of needstr. - */ - if ( (p->needsdtr_copy & target_mask) && - !(p->sdtr_pending & target_mask)) - { - p->needsdtr |= target_mask; - } + p->needwdtr &= ~target_mask; } else { scb->flags &= ~SCB_MSGOUT_BITS; + scb->flags |= SCB_MSGOUT_WDTR; + reply = TRUE; + if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) ) + { + /* + * Well, we now know the WDTR and SYNC caps of this device since + * it contacted us first, mark it as such and copy the user stuff + * over to the goal stuff. + */ + p->transinfo[tindex].goal_width = + p->transinfo[tindex].user_width; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->needwdtr_copy |= target_mask; + p->needsdtr_copy |= target_mask; + } switch(bus_width) { default: { - if (p->type & AHC_WIDE) + if ( (p->features & AHC_WIDE) && + (p->transinfo[tindex].goal_width == + MSG_EXT_WDTR_BUS_16_BIT) ) { bus_width = MSG_EXT_WDTR_BUS_16_BIT; - p->needwdtr_copy |= target_mask; - scb->flags |= SCB_MSGOUT_WDTR_16BIT; - target_scratch |= WIDEXFER; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using wide (16 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } break; } } /* Fall through if we aren't a wide card */ case MSG_EXT_WDTR_BUS_8_BIT: { - bus_width = MSG_EXT_WDTR_BUS_8_BIT; p->needwdtr_copy &= ~target_mask; - scb->flags |= SCB_MSGOUT_WDTR_8BIT; - target_scratch &= 0x7f; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using narrow (8 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } + bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } } + p->needwdtr &= ~target_mask; + p->wdtr_pending &= ~target_mask; aic_outb(p, HOST_MSG, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); - p->wdtr_pending |= target_mask; } - aic_outb(p, target_scratch, SCSIRATE); - aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset); - p->syncinfo[scratch_offset].offset = - (bus_width == MSG_EXT_WDTR_BUS_8_BIT) ? - MAX_OFFSET_8BIT : MAX_OFFSET_16BIT; + aic7xxx_set_width(p, target, channel, lun, bus_width, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + + /* + * By virtue of the SCSI spec, a WDTR message negates any existing + * SDTR negotiations. So, even if needsdtr isn't marked for this + * device, we still have to do a new SDTR message if the device + * supports SDTR at all. Therefore, we check needsdtr_copy instead + * of needstr. + */ + aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE); + if ( (p->needsdtr_copy & target_mask) && + !(p->sdtr_pending & target_mask)) + { + p->needsdtr |= target_mask; + if ( !reject && !reply ) + { + scb->flags &= ~SCB_MSGOUT_WDTR; + if (p->transinfo[tindex].goal_period) + { + p->sdtr_pending |= target_mask; + scb->flags |= SCB_MSGOUT_SDTR; + aic_outb(p, HOST_MSG, MSG_OUT); + aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); + } + } + } done = TRUE; break; } @@ -4558,10 +5494,12 @@ { aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); + done = TRUE; } return(done); } + /*+F************************************************************************* * Function: * aic7xxx_handle_reqinit @@ -4605,10 +5543,20 @@ { aic_outb(p, p->msg_buf[p->msg_index], SINDEX); aic_outb(p, 0, RETURN_1); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Completed sending of REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif } else { aic_outb(p, MSGOUT_PHASEMIS, RETURN_1); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif } unpause_sequencer(p, TRUE); } @@ -4642,12 +5590,23 @@ } if (phasemis || done) { +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + if (phasemis) + printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); + else + printk(INFO_LEAD "Completed receipt of REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); + } +#endif /* Time to end our message session */ p->msg_len = 0; p->msg_type = MSG_TYPE_NONE; - p->flags &= ~AHC_HANDLING_REQINITS; aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1); aic_outb(p, CLRSCSIINT, CLRINT); + p->flags &= ~AHC_HANDLING_REQINITS; unpause_sequencer(p, TRUE); } break; @@ -4691,30 +5650,14 @@ } - if ( (p->flags & AHC_HANDLING_REQINITS) && (status & REQINIT) ) - { - if (scb) - { - aic7xxx_handle_reqinit(p, scb); - } - else - { - p->flags &= ~AHC_HANDLING_REQINITS; - aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1); - aic_outb(p, CLRREQINIT, CLRSINT1); - aic_outb(p, CLRSCSIINT, CLRINT); - p->msg_type = MSG_TYPE_NONE; - p->msg_index = 0; - p->msg_len = 0; - } - return; - } - if ((status & SCSIRSTI) != 0) { int channel; - channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + else + channel = 0; if (aic7xxx_verbose & VERBOSE_RESET) printk(WARN_LEAD "Someone else reset the channel!!\n", @@ -4737,10 +5680,16 @@ unsigned char lastphase = aic_inb(p, LASTPHASE); unsigned char saved_tcl = aic_inb(p, SAVED_TCL); unsigned char target = (saved_tcl >> 4) & 0x0F; - int channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + int channel; int printerror = TRUE; - aic_outb(p, 0, SCSISEQ); + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + else + channel = 0; + + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), + SCSISEQ); if (lastphase == P_MESGOUT) { unsigned char message; @@ -4782,20 +5731,16 @@ aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag); aic7xxx_run_done_queue(p, FALSE); } - else - { /* Since we don't really know what happened here, we'll wait */ - /* for the commands to timeout and get aborted if need be */ - aic7xxx_add_curscb_to_free_list(p); - } printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, " "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase, (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0)); scb = NULL; } + aic_outb(p, MSG_NOOP, MSG_OUT); aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT), SIMODE1); p->flags &= ~AHC_HANDLING_REQINITS; - aic_outb(p, CLRBUSFREE | CLRREQINIT, CLRSINT1); + aic_outb(p, CLRBUSFREE, CLRSINT1); aic_outb(p, CLRSCSIINT, CLRINT); restart_sequencer(p); unpause_sequencer(p, TRUE); @@ -4807,6 +5752,24 @@ Scsi_Cmnd *cmd; scbptr = aic_inb(p, WAITING_SCBH); + if (scbptr > p->scb_data->maxhscbs) + { + /* + * I'm still trying to track down exactly how this happens, but until + * I find it, this code will make sure we aren't passing bogus values + * into the SCBPTR register, even if that register will just wrap + * things around, we still don't like having out of range variables. + * + * NOTE: Don't check the aic7xxx_verbose variable, I want this message + * to always be displayed. + */ + printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n", + p->host_no, -1, -1, -1, scbptr); + if (p->scb_data->maxhscbs > 4) + scbptr &= (p->scb_data->maxhscbs - 1); + else + scbptr &= 0x03; + } aic_outb(p, scbptr, SCBPTR); scb_index = aic_inb(p, SCB_TAG); @@ -4827,6 +5790,8 @@ "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ), aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), aic_inb(p, SSTAT0), aic_inb(p, SSTAT1)); + if (aic7xxx_panic_on_abort) + aic7xxx_panic_abort(p, NULL); } else { @@ -4863,6 +5828,10 @@ * What we need to do then is to let the command timeout again so * we get a reset since this abort just failed. */ +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb)); +#endif if (p->flags & SCB_QUEUED_ABORT) { cmd->result = 0; @@ -4875,9 +5844,9 @@ * are allowed to reselect in. */ aic_outb(p, 0, SCSISEQ); - aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1); + aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1); p->flags &= ~AHC_HANDLING_REQINITS; - aic_outb(p, CLRSELTIMEO | CLRBUSFREE | CLRREQINIT, CLRSINT1); + aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1); aic_outb(p, CLRSCSIINT, CLRINT); restart_sequencer(p); unpause_sequencer(p, TRUE); @@ -4942,7 +5911,7 @@ * A parity error has occurred during a data * transfer phase. Flag it and continue. */ - printk(WARN_LEAD "Parity error during phase %s.\n", + printk(WARN_LEAD "Parity error during %s phase.\n", p->host_no, CTL_OF_SCB(scb), phase); /* @@ -4960,6 +5929,17 @@ aic_outb(p, CLRSCSIINT, CLRINT); unpause_sequencer(p, /* unpause_always */ TRUE); } + else if ( (status & REQINIT) && + (p->flags & AHC_HANDLING_REQINITS) ) + { +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no, + CTL_OF_SCB(scb), aic_inb(p, SSTAT1)); +#endif + aic7xxx_handle_reqinit(p, scb); + return; + } else { /* @@ -4980,66 +5960,151 @@ } } -#ifdef CONFIG_PCI - -#define DPE 0x80 -#define SSE 0x40 -#define RMA 0x20 -#define RTA 0x10 -#define STA 0x08 -#define DPR 0x01 - -/*+F************************************************************************* - * Function: - * aic7xxx_pci_intr - * - * Description: - * Check the scsi card for PCI errors and clear the interrupt - * - * NOTE: If you don't have this function and a 2940 card encounters - * a PCI error condition, the machine will end up locked as the - * interrupt handler gets slammed with non-stop PCI error interrupts - *-F*************************************************************************/ +#ifdef AIC7XXX_VERBOSE_DEBUGGING static void -aic7xxx_pci_intr(struct aic7xxx_host *p) +aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer) { - unsigned char status1; - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pci_read_config_byte(p->pdev, PCI_STATUS, &status1); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS, &status1); -#endif + unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp; + int i, bogus, lost; + static unsigned char scb_status[AIC7XXX_MAXSCB]; + +#define SCB_NO_LIST 0 +#define SCB_FREE_LIST 1 +#define SCB_WAITING_LIST 2 +#define SCB_DISCONNECTED_LIST 4 +#define SCB_CURRENTLY_ACTIVE 8 + + /* + * Note, these checks will fail on a regular basis once the machine moves + * beyond the bus scan phase. The problem is race conditions concerning + * the scbs and where they are linked in. When you have 30 or so commands + * outstanding on the bus, and run this twice with every interrupt, the + * chances get pretty good that you'll catch the sequencer with an SCB + * only partially linked in. Therefore, once we pass the scan phase + * of the bus, we really should disable this function. + */ + bogus = FALSE; + memset(&scb_status[0], 0, sizeof(scb_status)); + pause_sequencer(p); + saved_scbptr = aic_inb(p, SCBPTR); + if (saved_scbptr >= p->scb_data->maxhscbs) + { + printk("Bogus SCBPTR %d\n", saved_scbptr); + bogus = TRUE; + } + scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE; + free_scbh = aic_inb(p, FREE_SCBH); + if ( (free_scbh != SCB_LIST_NULL) && + (free_scbh >= p->scb_data->maxhscbs) ) + { + printk("Bogus FREE_SCBH %d\n", free_scbh); + bogus = TRUE; + } + else + { + temp = free_scbh; + while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) ) + { + if(scb_status[temp] & 0x07) + { + printk("HSCB %d on multiple lists, status 0x%02x", temp, + scb_status[temp] | SCB_FREE_LIST); + bogus = TRUE; + } + scb_status[temp] |= SCB_FREE_LIST; + aic_outb(p, temp, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + } + } - if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Data Parity Error during PCI address or PCI write" - "phase.\n", p->host_no, -1, -1, -1); - if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Signal System Error Detected\n", p->host_no, - -1, -1, -1); - if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no, - -1, -1, -1); - if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no, - -1, -1, -1); - if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no, - -1, -1, -1); - if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Data Parity Error has been reported via PCI pin " - "PERR#\n", p->host_no, -1, -1, -1); + dis_scbh = aic_inb(p, DISCONNECTED_SCBH); + if ( (dis_scbh != SCB_LIST_NULL) && + (dis_scbh >= p->scb_data->maxhscbs) ) + { + printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh); + bogus = TRUE; + } + else + { + temp = dis_scbh; + while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) ) + { + if(scb_status[temp] & 0x07) + { + printk("HSCB %d on multiple lists, status 0x%02x", temp, + scb_status[temp] | SCB_DISCONNECTED_LIST); + bogus = TRUE; + } + scb_status[temp] |= SCB_DISCONNECTED_LIST; + aic_outb(p, temp, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + } + } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pci_write_config_byte(p->pdev, PCI_STATUS, status1); -#else - pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS, status1); -#endif - if (status1 & (DPR|RMA|RTA)) - aic_outb(p, CLRPARERR, CLRINT); + wait_scbh = aic_inb(p, WAITING_SCBH); + if ( (wait_scbh != SCB_LIST_NULL) && + (wait_scbh >= p->scb_data->maxhscbs) ) + { + printk("Bogus WAITING_SCBH %d\n", wait_scbh); + bogus = TRUE; + } + else + { + temp = wait_scbh; + while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) ) + { + if(scb_status[temp] & 0x07) + { + printk("HSCB %d on multiple lists, status 0x%02x", temp, + scb_status[temp] | SCB_WAITING_LIST); + bogus = TRUE; + } + scb_status[temp] |= SCB_WAITING_LIST; + aic_outb(p, temp, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + } + } + lost=0; + for(i=0; i < p->scb_data->maxhscbs; i++) + { + aic_outb(p, i, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + if ( ((temp != SCB_LIST_NULL) && + (temp >= p->scb_data->maxhscbs)) ) + { + printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp); + bogus = TRUE; + } + if ( temp == i ) + { + printk("HSCB %d bad, SCB_NEXT points to self.\n", i); + bogus = TRUE; + } + temp = aic_inb(p, SCB_PREV); + if ((temp != SCB_LIST_NULL) && + (temp >= p->scb_data->maxhscbs)) + { + printk("HSCB %d bad, SCB_PREV invalid(%d).\n", i, temp); + bogus = TRUE; + } + if (scb_status[i] == 0) + lost++; + if (lost > 1) + { + printk("Too many lost scbs.\n"); + bogus=TRUE; + } + } + aic_outb(p, saved_scbptr, SCBPTR); + unpause_sequencer(p, FALSE); + if (bogus) + { + printk("Bogus parameters found in card SCB array structures.\n"); + printk("%s\n", buffer); + aic7xxx_panic_abort(p, NULL); + } + return; } #endif @@ -5066,7 +6131,8 @@ if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND)) { #ifdef CONFIG_PCI - if ((p->type & AHC_AIC78x0) && (p->spurious_int > 500)) + if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) && + !(p->flags & AHC_HANDLING_REQINITS) ) { if ( aic_inb(p, ERROR) & PCIERRSTAT ) { @@ -5074,7 +6140,7 @@ } p->spurious_int = 0; } - else + else if ( !(p->flags & AHC_HANDLING_REQINITS) ) { p->spurious_int++; } @@ -5089,6 +6155,12 @@ */ p->isr_count++; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) && + (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) ) + aic7xxx_check_scbs(p, "Bogus settings at start of interrupt."); +#endif + /* * Handle all the interrupt sources - especially for SCSI * interrupts, we won't get a second chance at them. @@ -5099,6 +6171,11 @@ Scsi_Cmnd *cmd; unsigned char scb_index; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if(aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1); +#endif + /* * Clear interrupt status before running the completion loop. * This eliminates a race condition whereby a command could @@ -5152,6 +6229,14 @@ SCB_QUEUED_ABORT); unpause_sequencer(p, FALSE); } + else if (scb->flags & SCB_ABORT) + { + /* + * We started to abort this, but it completed on us, let it + * through as successful + */ + scb->flags &= ~(SCB_ABORT|SCB_RESET); + } switch (status_byte(scb->hscb->target_status)) { case QUEUE_FULL: @@ -5192,16 +6277,33 @@ printk(KERN_ERR " %s\n", hard_error[i].errmesg); } } - printk(KERN_ERR "(scsi%d) LINE=%d\n", p->host_no, + printk(KERN_ERR "(scsi%d) SEQADDR=0x%x\n", p->host_no, (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0))); - aic7xxx_reset_channel(p, 0, TRUE); - if ( p->type & AHC_TWIN ) + if (aic7xxx_panic_on_abort) + aic7xxx_panic_abort(p, NULL); + if (errno & PCIERRSTAT) + aic7xxx_pci_intr(p); + if (errno & (SQPARERR | ILLOPCODE | ILLSADDR)) + { + sti(); + panic("aic7xxx: unrecoverable BRKADRINT.\n"); + } + if (errno & ILLHADDR) { - aic7xxx_reset_channel(p, 1, TRUE); - restart_sequencer(p); + printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first " + "pausing controller!\n", p->host_no); } - aic7xxx_run_done_queue(p, FALSE); - aic_outb(p, CLRBRKADRINT, CLRINT); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (errno & DPARERR) + { + if (aic_inb(p, DMAPARAMS) & DIRECTION) + printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no); + else + printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no); + } +#endif + aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT); + unpause_sequencer(p, FALSE); } if (intstat & SEQINT) @@ -5213,6 +6315,13 @@ { aic7xxx_handle_scsiint(p, intstat); } + +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) && + (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) ) + aic7xxx_check_scbs(p, "Bogus settings at end of interrupt."); +#endif + } /*+F************************************************************************* @@ -5229,49 +6338,34 @@ { unsigned long cpu_flags; struct aic7xxx_host *p; - static unsigned int re_entry_counter = 0; p = (struct aic7xxx_host *)dev_id; if(!p) return; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, cpu_flags); if(test_and_set_bit(AHC_IN_ISR_BIT, &p->flags)) { - if(re_entry_counter++ > 100000UL) - { - /* - * Hmmm...we seem to be looping here. This usually means that our - * interrupt routine got killed by a NULL pointer deref. Panic. - */ - sti(); - panic("aic7xxx: The interrupt routine appears to have seg faulted.\n"); - } return; } - re_entry_counter = 0; - spin_lock_irqsave(&io_request_lock, cpu_flags); - aic7xxx_isr(irq, dev_id, regs); + do + { + aic7xxx_isr(irq, dev_id, regs); + } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); - spin_unlock_irqrestore(&io_request_lock, cpu_flags); clear_bit(AHC_IN_ISR_BIT, &p->flags); + spin_unlock_irqrestore(&io_request_lock, cpu_flags); #else if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags)) { - if(re_entry_counter++ > 100000UL) - { - /* - * Hmmm...we seem to be looping here. This usually means that our - * interrupt routine got killed by a NULL pointer deref. Panic. - */ - sti(); - panic("aic7xxx: The interrupt routine appears to have seg faulted.\n"); - } return; } - re_entry_counter = 0; DRIVER_LOCK - aic7xxx_isr(irq, dev_id, regs); + do + { + aic7xxx_isr(irq, dev_id, regs); + } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); DRIVER_UNLOCK aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); @@ -5324,7 +6418,7 @@ if (!(p->discenable & target_mask)) { - if (aic7xxx_verbose & VERBOSE_QUEUE) + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) printk(INFO_LEAD "Disconnection disabled, unable to " "enable tagged queueing.\n", p->host_no, device->channel, device->id, device->lun); @@ -5364,7 +6458,7 @@ } if ((device->tagged_queue == 0) && tag_enabled) { - if (aic7xxx_verbose & VERBOSE_QUEUE) + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n", p->host_no, device->channel, device->id, @@ -5418,7 +6512,7 @@ * to allocate some when memory is more or less exhausted and we need * the SCB in order to perform a swap operation (possible deadlock) */ - if ( aic7xxx_allocate_scb(p, TRUE) == NULL ) + if ( aic7xxx_allocate_scb(p) == 0 ) return; } } @@ -5451,7 +6545,7 @@ * an unused function. *-F*************************************************************************/ #if defined(__i386__) || defined(__alpha__) -static ahc_type +static int aic7xxx_probe(int slot, int base, ahc_flag_type *flags) { int i; @@ -5460,13 +6554,17 @@ static struct { int n; unsigned char signature[sizeof(buf)]; - ahc_type type; + ahc_chip type; int bios_disabled; } AIC7xxx[] = { - { 4, { 0x04, 0x90, 0x77, 0x71 }, AHC_274, FALSE }, /* host adapter 274x */ - { 4, { 0x04, 0x90, 0x77, 0x70 }, AHC_AIC7770, FALSE }, /* mb 7770 */ - { 4, { 0x04, 0x90, 0x77, 0x56 }, AHC_284, FALSE }, /* 284x BIOS enabled */ - { 4, { 0x04, 0x90, 0x77, 0x57 }, AHC_284, TRUE } /* 284x BIOS disabled */ + { 4, { 0x04, 0x90, 0x77, 0x70 }, + AHC_AIC7770|AHC_EISA, FALSE }, /* mb 7770 */ + { 4, { 0x04, 0x90, 0x77, 0x71 }, + AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */ + { 4, { 0x04, 0x90, 0x77, 0x56 }, + AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */ + { 4, { 0x04, 0x90, 0x77, 0x57 }, + AHC_AIC7770|AHC_VL, TRUE } /* 284x BIOS disabled */ }; /* @@ -5496,7 +6594,7 @@ { *flags |= AHC_BIOS_ENABLED; } - return (AIC7xxx[i].type); + return (i); } printk("aic7xxx: " @@ -5504,7 +6602,7 @@ } } - return (AHC_NONE); + return (-1); } #endif /* (__i386__) || (__alpha__) */ @@ -5661,7 +6759,7 @@ * Description: * Acquires access to the memory port on PCI controllers. *-F*************************************************************************/ -static inline int +static int acquire_seeprom(struct aic7xxx_host *p) { int wait; @@ -5695,7 +6793,7 @@ * Description: * Releases access to the memory port on PCI controllers. *-F*************************************************************************/ -static inline void +static void release_seeprom(struct aic7xxx_host *p) { aic_outb(p, 0, SEECTL); @@ -5895,19 +6993,38 @@ * Description: * Writes a value to the BRDCTL register. *-F*************************************************************************/ -static inline void +static void write_brdctl(struct aic7xxx_host *p, unsigned char value) { unsigned char brdctl; - brdctl = BRDCS | BRDSTB; + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) + { + brdctl = BRDSTB; + if (p->flags & AHC_CHNLB) + brdctl |= BRDCS; + } + else if (p->features & AHC_ULTRA2) + brdctl = 0; + else + brdctl = BRDSTB | BRDCS; aic_outb(p, brdctl, BRDCTL); + udelay(1); brdctl |= value; aic_outb(p, brdctl, BRDCTL); - brdctl &= ~BRDSTB; + udelay(1); + if (p->features & AHC_ULTRA2) + brdctl |= BRDSTB_ULTRA2; + else + brdctl &= ~BRDSTB; aic_outb(p, brdctl, BRDCTL); - brdctl &= ~BRDCS; + udelay(1); + if (p->features & AHC_ULTRA2) + brdctl = 0; + else + brdctl &= ~BRDCS; aic_outb(p, brdctl, BRDCTL); + udelay(1); } /*+F************************************************************************* @@ -5917,11 +7034,27 @@ * Description: * Reads the BRDCTL register. *-F*************************************************************************/ -static inline unsigned char +static unsigned char read_brdctl(struct aic7xxx_host *p) { - aic_outb(p, BRDRW | BRDCS, BRDCTL); - return (aic_inb(p, BRDCTL)); + unsigned char brdctl, value; + + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) + { + brdctl = BRDRW; + if (p->flags & AHC_CHNLB) + brdctl |= BRDCS; + } + else if (p->features & AHC_ULTRA2) + brdctl = BRDRW_ULTRA2; + else + brdctl = BRDRW | BRDCS; + aic_outb(p, brdctl, BRDCTL); + udelay(1); + value = aic_inb(p, BRDCTL); + aic_outb(p, 0, BRDCTL); + udelay(1); + return (value); } /*+F************************************************************************* @@ -5938,11 +7071,14 @@ unsigned char brdctl; aic_outb(p, BRDRW | BRDCS, BRDCTL); + udelay(1); aic_outb(p, 0, BRDCTL); + udelay(1); brdctl = aic_inb(p, BRDCTL); + udelay(1); *int_50 = !(brdctl & BRDDAT5); *ext_present = !(brdctl & BRDDAT6); - *eeprom = ( aic_inb(p, SPIOCAP) & EEPROM ) != 0; + *eeprom = (aic_inb(p, SPIOCAP) & EEPROM); } /*+F************************************************************************* @@ -5985,12 +7121,37 @@ brdctl = read_brdctl(p); *ext_present = !(brdctl & BRDDAT6); - *eeprom = (brdctl & BRDDAT7); + *eeprom = !(brdctl & BRDDAT7); /* * We're done, the calling function will release the SEEPROM for us */ - +} + +/*+F************************************************************************* + * Function: + * aic787x_ultra2_term_detect + * + * Description: + * Detect the termination settings present on ultra2 class controllers + * + * NOTE: This functions assumes the SEEPROM will have already been aquired + * prior to invocation of this function. + *-F*************************************************************************/ +static void +aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low, + int *enableSE_high, int *enableLVD_low, + int *enableLVD_high, int *eprom_present) +{ + unsigned char brdctl; + + brdctl = read_brdctl(p); + + *eprom_present = (brdctl & BRDDAT7); + *enableSE_high = (brdctl & BRDDAT6); + *enableSE_low = (brdctl & BRDDAT5); + *enableLVD_high = (brdctl & BRDDAT4); + *enableLVD_low = (brdctl & BRDDAT3); } /*+F************************************************************************* @@ -6002,146 +7163,192 @@ * SEEPROMs available. *-F*************************************************************************/ static void -configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1, - unsigned short adapter_control, unsigned char max_targ) +configure_termination(struct aic7xxx_host *p) { - int internal50_present; + int internal50_present = 0; int internal68_present = 0; int external_present = 0; - int eprom_present; - int high_on; - int low_on; + int eprom_present = 0; + int enableSE_low = 0; + int enableSE_high = 0; + int enableLVD_low = 0; + int enableLVD_high = 0; + unsigned char brddat = 0; + unsigned char max_target = 0; + unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1); if (acquire_seeprom(p)) { - if (adapter_control & CFAUTOTERM) - { - printk(KERN_INFO "aic7xxx: Warning - detected auto-termination on " - "controller:\n"); - printk(KERN_INFO "aic7xxx: <%s> at ", board_names[p->board_name_index]); - switch(p->type & 0x1ff1) - { - case AHC_AIC7770: - case AHC_274: - printk("EISA slot %d\n", p->pci_device_fn); - break; - case AHC_284: - printk("VLB slot %d\n", p->pci_device_fn); - break; - default: - printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn), - PCI_FUNC(p->pci_device_fn)); - break; + if (p->features & (AHC_WIDE|AHC_TWIN)) + max_target = 16; + else + max_target = 8; + aic_outb(p, SEEMS | SEECS, SEECTL); + sxfrctl1 &= ~STPWEN; + if ( (p->adapter_control & CFAUTOTERM) || + (p->features & AHC_ULTRA2) ) + { + if ( (p->adapter_control & CFAUTOTERM) && !(p->features & AHC_ULTRA2) ) + { + printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", + p->host_no); + printk(KERN_INFO "(scsi%d) Please verify driver detected settings are " + "correct.\n", p->host_no); + printk(KERN_INFO "(scsi%d) If not, then please properly set the device " + "termination\n", p->host_no); + printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting CTRL-A " + "when prompted\n", p->host_no); + printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no); } - printk(KERN_INFO "aic7xxx: Please verify driver detected settings are " - "correct.\n"); - printk(KERN_INFO "aic7xxx: If not, then please properly set the device " - "termination\n"); - printk(KERN_INFO "aic7xxx: in the Adaptec SCSI BIOS by hitting CTRL-A " - "when prompted\n"); - printk(KERN_INFO "aic7xxx: during machine bootup.\n"); /* Configure auto termination. */ - aic_outb(p, SEECS | SEEMS, SEECTL); - if ( (p->type & AHC_AIC7860) == AHC_AIC7860 ) + if (p->features & AHC_ULTRA2) { - aic785x_cable_detect(p, &internal50_present, &external_present, - &eprom_present); + if (aic7xxx_override_term == -1) + aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, + &enableLVD_low, &enableLVD_high, + &eprom_present); + if (!(p->adapter_control & CFSEAUTOTERM)) + { + enableSE_low = (p->adapter_control & CFSTERM); + enableSE_high = (p->adapter_control & CFWSTERM); + } + if (!(p->adapter_control & CFAUTOTERM)) + { + enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM); + } + internal50_present = 0; + internal68_present = 1; + external_present = 1; } - else + else if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 ) { aic787x_cable_detect(p, &internal50_present, &internal68_present, &external_present, &eprom_present); } - if (max_targ > 8) - { - printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, " - "Ext-68 %s)\n", - internal50_present ? "YES" : "NO", - internal68_present ? "YES" : "NO", - external_present ? "YES" : "NO"); - } else { - printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n", - internal50_present ? "YES" : "NO", - external_present ? "YES" : "NO"); + aic785x_cable_detect(p, &internal50_present, &external_present, + &eprom_present); + } + + if (max_target <= 8) internal68_present = 0; + + if ( !(p->features & AHC_ULTRA2) ) + { + if (max_target > 8) + { + printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, " + "Ext-68 %s)\n", p->host_no, + internal50_present ? "YES" : "NO", + internal68_present ? "YES" : "NO", + external_present ? "YES" : "NO"); + } + else + { + printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n", + p->host_no, + internal50_present ? "YES" : "NO", + external_present ? "YES" : "NO"); + } } if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "aic7xxx: EEPROM %s present.\n", + printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, eprom_present ? "is" : "is not"); /* * Now set the termination based on what we found. BRDDAT6 * controls wide termination enable. + * Flash Enable = BRDDAT7 + * SE High Term Enable = BRDDAT6 + * SE Low Term Enable = BRDDAT5 (7890) + * LVD High Term Enable = BRDDAT4 (7890) */ - high_on = FALSE; - low_on = FALSE; - if ((max_targ > 8) && - ((external_present == 0) || (internal68_present == 0))) + if ( !(p->features & AHC_ULTRA2) && + (internal50_present && internal68_present && external_present) ) { - high_on = TRUE; + printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", + p->host_no); + printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be " + "in use at a time!\n", p->host_no); + /* + * Force termination (low and high byte) on. This is safer than + * leaving it completely off, especially since this message comes + * most often from motherboard controllers that don't even have 3 + * connectors, but instead are failing the cable detection. + */ + internal50_present = external_present = 0; + enableSE_high = enableSE_low = 1; } - if ( ( (internal50_present ? 1 : 0) + - (internal68_present ? 1 : 0) + - (external_present ? 1 : 0) ) <= 1) + if ((max_target > 8) && + ((external_present == 0) || (internal68_present == 0) || + (enableSE_high != 0))) { - low_on = TRUE; + brddat |= BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); } - - if (internal50_present && internal68_present && external_present) + + if ( (((internal50_present ? 1 : 0) + + (internal68_present ? 1 : 0) + + (external_present ? 1 : 0)) <= 1) || + (enableSE_low != 0) ) { - printk(KERN_INFO "aic7xxx: Illegal cable configuration!! Only two\n"); - printk(KERN_INFO "aic7xxx: connectors on the SCSI controller may be " - "in use at a time!\n"); + if (p->features & AHC_ULTRA2) + brddat |= BRDDAT5; + else + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_SE_LOW; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); } - if (high_on == TRUE) - write_brdctl(p, BRDDAT6); - else - write_brdctl(p, 0); - - if (low_on == TRUE) - *sxfrctl1 |= STPWEN; - - if (max_targ > 8) + if (enableLVD_low != 0) { - printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", - low_on ? "ON" : "OFF", high_on ? "ON" : "OFF"); + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_LVD; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD Low byte termination Enabled\n", + p->host_no); } - else + + if (enableLVD_high != 0) { - printk(KERN_INFO "aic7xxx: Termination %s\n", - low_on ? "Enabled" : "Disabled"); + brddat |= BRDDAT4; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD High byte termination Enabled\n", + p->host_no); } } else { - if (adapter_control & CFSTERM) - *sxfrctl1 |= STPWEN; - - aic_outb(p, SEEMS | SEECS, SEECTL); - /* - * Configure high byte termination. - */ - if (adapter_control & CFWSTERM) - { - write_brdctl(p, BRDDAT6); - } - else + if (p->adapter_control & CFSTERM) { - write_brdctl(p, 0); + if (p->features & AHC_ULTRA2) + brddat |= BRDDAT5; + else + sxfrctl1 |= STPWEN; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); } - if (aic7xxx_verbose & VERBOSE_PROBE2) + + if (p->adapter_control & CFWSTERM) { - printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", - (adapter_control & CFSTERM) ? "ON" : "OFF", - (adapter_control & CFWSTERM) ? "ON" : "OFF"); + brddat |= BRDDAT6; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); } } + write_brdctl(p, brddat); release_seeprom(p); + aic_outb(p, sxfrctl1, SXFRCTL1); } } @@ -6183,7 +7390,12 @@ aic_outb(p, i, SCBPTR); aic_outb(p, 0, SCB_CONTROL); /* Clear the control byte. */ aic_outb(p, i + 1, SCB_NEXT); /* Set the next pointer. */ + aic_outb(p, i - 1, SCB_PREV); /* Set the prev pointer. */ aic_outb(p, SCB_LIST_NULL, SCB_TAG); /* Make the tag invalid. */ + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS); /* no busy untagged */ + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */ + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2); + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3); } /* Make sure the last SCB terminates the free list. */ @@ -6195,6 +7407,11 @@ aic_outb(p, 0, SCB_CONTROL); p->scb_data->maxhscbs = i; + /* + * Use direct indexing instead for speed + */ + if ( i == AIC7XXX_MAXSCB ) + p->flags &= ~AHC_PAGESCBS; } } @@ -6213,9 +7430,7 @@ int i, result; int max_targets; int found = 1; - unsigned char target_settings; - unsigned char term, scsi_conf, sxfrctl1; - unsigned short ultraenable = 0; + unsigned char term, scsi_conf; struct Scsi_Host *host; /* @@ -6236,11 +7451,11 @@ host->n_io_port = 0xFF; host->base = (unsigned char *) p->mbase; host->irq = p->irq; - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) { host->max_id = 16; } - if (p->type & AHC_TWIN) + if (p->features & AHC_TWIN) { host->max_channel = 1; } @@ -6248,6 +7463,7 @@ p->host = host; p->last_reset = 0; p->host_no = host->host_no; + host->unique_id = p->instance; p->isr_count = 0; p->next = NULL; p->completeq.head = NULL; @@ -6270,7 +7486,7 @@ for (i = 0; i < MAX_TARGETS; i++) { p->dev_commands_sent[i] = 0; - p->dev_flags[i] = DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR; + p->dev_flags[i] = 0; p->dev_active_cmds[i] = 0; p->dev_last_reset[i] = 0; p->dev_last_queue_full[i] = 0; @@ -6283,19 +7499,16 @@ p->dev_timer[i].expires = 0; p->dev_timer[i].data = (unsigned long)p; p->dev_timer[i].function = (void *)aic7xxx_timer; - p->syncinfo[i].period = 0; - p->syncinfo[i].offset = 0; } printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no, board_names[p->board_name_index]); - switch(p->type & 0x1ff1) + switch(p->chip) { - case AHC_AIC7770: - case AHC_274: + case (AHC_AIC7770|AHC_EISA): printk("EISA slot %d\n", p->pci_device_fn); break; - case AHC_284: + case (AHC_AIC7770|AHC_VL): printk("VLB slot %d\n", p->pci_device_fn); break; default: @@ -6303,7 +7516,7 @@ PCI_FUNC(p->pci_device_fn)); break; } - if (p->type & AHC_TWIN) + if (p->features & AHC_TWIN) { printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ", p->host_no, p->scsi_id, p->scsi_id_b); @@ -6314,7 +7527,7 @@ channel = ""; - if ((p->type & AHC_39x) != 0) + if ((p->flags & AHC_MULTI_CHANNEL) != 0) { channel = " A"; @@ -6323,7 +7536,7 @@ channel = (p->flags & AHC_CHNLB) ? " B" : " C"; } } - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) { printk(KERN_INFO "(scsi%d) Wide ", p->host_no); } @@ -6340,129 +7553,159 @@ */ detect_maxscb(p); printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs); - printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n", - p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis", - p->base, p->irq); - printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n", - p->host_no, p->mbase, (unsigned long)p->maddr); - - + if (aic7xxx_verbose & VERBOSE_PROBE2) + { + printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n", + p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis", + p->base, p->irq); + printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n", + p->host_no, p->mbase, (unsigned long)p->maddr); + } /* - * Register IRQ with the kernel. Only allow sharing IRQs with - * PCI devices. + * Now that we know our instance number, we can set the flags we need to + * force termination if need be. */ - if ((p->type & AHC_AIC7770) == AHC_AIC7770) - { - result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p)); - } - else + if (aic7xxx_stpwlev != -1) { - result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ, - "aic7xxx", p)); - if (result < 0) + /* + * This option only applies to PCI controllers. + */ + if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) { - result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ, - "aic7xxx", p)); + unsigned char devconfig; + +#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92) + pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig); +#else + pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, + DEVCONFIG, &devconfig); +#endif + if ( (aic7xxx_stpwlev >> p->instance) & 0x01 ) + { + devconfig |= 0x02; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("(scsi%d) Force setting STPWLEV bit\n", p->host_no); + } + else + { + devconfig &= ~0x02; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("(scsi%d) Force clearing STPWLEV bit\n", p->host_no); + } +#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92) + pci_write_config_byte(p->pdev, DEVCONFIG, devconfig); +#else + pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, + DEVCONFIG, devconfig); +#endif } } - if (result < 0) - { - printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring.\n", - p->host_no, p->irq); - return (0); - } /* - * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels + * That took care of devconfig and stpwlev, now for the actual termination + * settings. */ - if (p->type & AHC_TWIN) + if (aic7xxx_override_term != -1) { /* - * The controller is gated to channel B after a chip reset; set - * bus B values first. + * Again, this only applies to PCI controllers. We don't have problems + * with the termination on 274x controllers to the best of my knowledge. */ - term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0; - aic_outb(p, p->scsi_id_b, SCSIID); - scsi_conf = aic_inb(p, SCSICONF + 1); - sxfrctl1 = aic_inb(p, SXFRCTL1); - aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | - ENSTIMER | ACTNEGEN, SXFRCTL1); - aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); - if (p->type & AHC_ULTRA) + if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) { - aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0); - } - else - { - aic_outb(p, DFON | SPIOEN, SXFRCTL0); + unsigned char term_override; + + term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f); + p->adapter_control &= + ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM); + if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) ) + { + p->adapter_control |= CFLVDSTERM; + } + if (term_override & 0x02) + { + p->adapter_control |= CFWSTERM; + } + if (term_override & 0x01) + { + p->adapter_control |= CFSTERM; + } } - if ( (p->type & AHC_AIC7770) == AHC_AIC7770 ) + } + + if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) ) + { + if (p->features & AHC_SPIOCAP) { - scsi_conf &= ~0x07; - scsi_conf |= p->scsi_id_b; - aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF + 1); + if ( aic_inb(p, SPIOCAP) & SSPIOCPS ) + /* + * Update the settings in sxfrctl1 to match the termination + * settings. + */ + configure_termination(p); } - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) + else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870) { - /* Reset SCSI bus B. */ - if (aic7xxx_verbose & VERBOSE_PROBE) - printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no); - - aic7xxx_reset_current_bus(p); + configure_termination(p); } - - /* Select channel A */ - aic_outb(p, SELNARROW, SBLKCTL); } - term = ((p->flags & AHC_TERM_ENB_A) != 0) ? STPWEN : 0; - aic_outb(p, p->scsi_id, SCSIID); - scsi_conf = aic_inb(p, SCSICONF); - sxfrctl1 = aic_inb(p, SXFRCTL1); - aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | - ENSTIMER | ACTNEGEN, SXFRCTL1); - aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); - if (p->type & AHC_ULTRA) - { - aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0); - } - else - { - aic_outb(p, DFON | SPIOEN, SXFRCTL0); - } - if ( (p->type & AHC_AIC7770) == AHC_AIC7770 ) + /* + * Load the sequencer program, then re-enable the board - + * resetting the AIC-7770 disables it, leaving the lights + * on with nobody home. + */ + aic7xxx_loadseq(p); + + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) { - scsi_conf &= ~0x07; - scsi_conf |= p->scsi_id; - aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF); + aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */ } + aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL); + /* + * Clear out any possible pending interrupts. + */ + aic7xxx_clear_intstat(p); - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) + /* + * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels + */ + if (p->features & AHC_TWIN) { - /* Reset SCSI bus A. */ - if (aic7xxx_verbose & VERBOSE_PROBE) - { /* In case we are a 3940, 3985, or 7895, print the right channel */ - char *channel = ""; - if (p->flags & AHC_MULTI_CHANNEL) - { - channel = " A"; - if (p->flags & (AHC_CHNLB|AHC_CHNLC)) - channel = (p->flags & AHC_CHNLB) ? " B" : " C"; - } - printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel); - } + /* Select channel B */ + aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL); - aic7xxx_reset_current_bus(p); + term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0; + aic_outb(p, p->scsi_id_b, SCSIID); + scsi_conf = aic_inb(p, SCSICONF + 1); + aic_outb(p, DFON | SPIOEN, SXFRCTL0); + aic_outb(p, (scsi_conf & ENSPCHK) | term | + ENSTIMER | ACTNEGEN, SXFRCTL1); + aic_outb(p, 0, SIMODE0); + aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); + aic_outb(p, 0, SCSIRATE); - /* - * Delay for the reset delay. - */ - if (!reset_delay) - aic7xxx_delay(AIC7XXX_RESET_DELAY); + /* Select channel A */ + aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL); } + term = ((p->flags & AHC_TERM_ENB_SE_LOW) != 0) ? STPWEN : 0; + if (p->features & AHC_ULTRA2) + aic_outb(p, p->scsi_id, SCSIID_ULTRA2); + else + aic_outb(p, p->scsi_id, SCSIID); + scsi_conf = aic_inb(p, SCSICONF); + aic_outb(p, DFON | SPIOEN, SXFRCTL0); + aic_outb(p, (scsi_conf & ENSPCHK) | term | + ENSTIMER | ACTNEGEN, SXFRCTL1); + aic_outb(p, 0, SIMODE0); + aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); + aic_outb(p, 0, SCSIRATE); + if ( p->features & AHC_ULTRA2) + aic_outb(p, 0, SCSIOFFSET); + /* * Look at the information that board initialization or the board * BIOS has left us. In the lower four bits of each target's @@ -6471,14 +7714,7 @@ * BIOS has decided to disable synchronous negotiation to that * target so we don't activate the needsdtr flag. */ - p->needsdtr_copy = 0x0; - p->sdtr_pending = 0x0; - p->needwdtr_copy = 0x0; - p->wdtr_pending = 0x0; - p->tagenable = 0x0; - p->ultraenb = 0x0; - p->discenable = 0xffff; - if ((p->type & (AHC_TWIN|AHC_WIDE)) == 0) + if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0) { max_targets = 8; } @@ -6487,110 +7723,31 @@ max_targets = 16; } - /* - * Grab the disconnection disable table and invert it for our needs - */ - if (p->flags & AHC_USEDEFAULTS) - { - printk(KERN_INFO "(scsi%d) Host adapter BIOS disabled. Using default SCSI " - "device parameters.\n", p->host_no); - if (p->type & AHC_ULTRA) - p->ultraenb = 0xffff; - p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B; - } - else + if (!(aic7xxx_no_reset)) { - p->discenable = ~((aic_inb(p, DISC_DSB + 1) << 8) | - aic_inb(p, DISC_DSB)); - if (p->type & AHC_ULTRA) - p->ultraenb = (aic_inb(p, ULTRA_ENB + 1) << 8) | - aic_inb(p, ULTRA_ENB); - } - - for (i = 0; i < max_targets; i++) - { - if (p->flags & AHC_USEDEFAULTS) - { - target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */ - p->needsdtr_copy |= (0x01 << i); - p->needwdtr_copy |= (0x01 << i); - if (p->type & AHC_ULTRA) - ultraenable |= (0x01 << i); - } - else - { - target_settings = aic_inb(p, TARG_SCRATCH + i); - if (target_settings & 0x0F) - { - p->needsdtr_copy |= (0x01 << i); - /* - * Default to asynchronous transfers (0 offset) - */ - target_settings &= 0xF0; - } - if (target_settings & 0x80) - { - p->needwdtr_copy |= (0x01 << i); - /* - * Clear the wide flag. When wide negotiation is successful, - * we'll enable it. - */ - target_settings &= 0x7F; - } - } - /* - * If we reset the bus, then clear the transfer ssettings, else leave + * If we reset the bus, then clear the transfer settings, else leave * them be */ - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) - aic_outb(p, target_settings, TARG_SCRATCH + i); - if (p->needsdtr_copy & (0x01 << i)) + for (i = 0; i < max_targets; i++) { - short sxfr, j; - - sxfr = target_settings & SXFR; - if ((p->ultraenb & (1 << i)) != 0) + aic_outb(p, 0, TARG_SCSIRATE + i); + if (p->features & AHC_ULTRA2) { - /* Want an ultra speed in the table */ - sxfr |= 0x100; + aic_outb(p, 0, TARG_OFFSET + i); } - for (j = 0; j < NUMBER(aic7xxx_syncrates); j++) - { - if (sxfr == aic7xxx_syncrates[j].rate) - break; - } - p->syncinfo[i].period = aic7xxx_syncrates[j].period; - p->syncinfo[i].offset = - (p->type & AHC_WIDE) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT; + p->transinfo[i].cur_offset = 0; + p->transinfo[i].cur_period = 0; + p->transinfo[i].cur_width = MSG_EXT_WDTR_BUS_8_BIT; } - else - { - p->syncinfo[i].period = 0; - p->syncinfo[i].offset = 0; - } - } - /* - * If we are not wide, forget WDTR. This makes the driver - * work on some cards that don't leave these fields cleared - * when BIOS is not installed. - */ - if ( !(p->type & AHC_WIDE)) - { - p->needwdtr_copy = 0; - } - p->needsdtr = p->needsdtr_copy; - p->needwdtr = p->needwdtr_copy; - - /* - * If we reset the bus, then clear the transfer ssettings, else leave - * them be - */ - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) - { + /* + * If we reset the bus, then clear the transfer settings, else leave + * them be. + */ aic_outb(p, 0, ULTRA_ENB); aic_outb(p, 0, ULTRA_ENB + 1); + p->ultraenb = 0; } /* @@ -6600,21 +7757,35 @@ * routine should only allocate contiguous memory, but note that * this could be a problem if kmalloc() is changed. */ - if (p->scb_data->hscbs == NULL) { size_t array_size; unsigned int hscb_physaddr; + unsigned long temp; array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb); - p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC); + if (p->scb_data->hscbs == NULL) + { + /* + * A little padding so we can align thing the way we want + */ + p->scb_data->hscbs = kmalloc(array_size + 0x1f, GFP_ATOMIC); + } if (p->scb_data->hscbs == NULL) { printk("(scsi%d) Unable to allocate hardware SCB array; " "failing detection.\n", p->host_no); - release_region(p->base, MAXREG - MINREG); - free_irq(p->irq, p); + p->irq = 0; return(0); } + /* + * Save the actual kmalloc buffer pointer off, then align our + * buffer to a 32 byte boundary + */ + p->scb_data->hscb_kmalloc_ptr = p->scb_data->hscbs; + temp = (unsigned long)p->scb_data->hscbs; + temp += 0x1f; + temp &= ~0x1f; + p->scb_data->hscbs = (struct aic7xxx_hwscb *)temp; /* At least the control byte of each SCB needs to be 0. */ memset(p->scb_data->hscbs, 0, array_size); @@ -6631,12 +7802,19 @@ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1); aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2); aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3); + } - /* The Q-FIFOs we just set up are all empty */ - aic_outb(p, 0, QINPOS); - aic_outb(p, 0, KERNEL_QINPOS); - aic_outb(p, 0, QOUTPOS); - + /* The Q-FIFOs we just set up are all empty */ + aic_outb(p, 0, QINPOS); + aic_outb(p, 0, KERNEL_QINPOS); + aic_outb(p, 0, QOUTPOS); + + if(p->features & AHC_QUEUE_REGS) + { + aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA); + aic_outb(p, 0, SDSCB_QOFF); + aic_outb(p, 0, SNSCB_QOFF); + aic_outb(p, 0, HNSCB_QOFF); } /* @@ -6649,20 +7827,19 @@ * Message out buffer starts empty */ aic_outb(p, MSG_NOOP, MSG_OUT); + aic_outb(p, MSG_NOOP, LAST_MSG); /* - * Load the sequencer program, then re-enable the board - - * resetting the AIC-7770 disables it, leaving the lights - * on with nobody home. On the PCI bus you *may* be home, - * but then your mailing address is dynamically assigned - * so no one can find you anyway :-) - */ - aic7xxx_loadseq(p); - - if ( (p->type & AHC_AIC7770) == AHC_AIC7770 ) - { - aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */ - } + * Set all the other asundry items that haven't been set yet. + * This includes just dumping init values to a lot of registers simply + * to make sure they've been touched and are ready for use parity wise + * speaking. + */ + aic_outb(p, 0, TMODE_CMDADDR); + aic_outb(p, 0, TMODE_CMDADDR + 1); + aic_outb(p, 0, TMODE_CMDADDR + 2); + aic_outb(p, 0, TMODE_CMDADDR + 3); + aic_outb(p, 0, TMODE_CMDADDR_NEXT); /* * Link us into the list of valid hosts @@ -6671,10 +7848,104 @@ first_aic7xxx = p; /* - * Unpause the sequencer before returning and enable - * interrupts - we shouldn't get any until the first - * command is sent to us by the high-level SCSI code. + * Clear out any possible pending interrupts, again. + */ + aic7xxx_clear_intstat(p); + + /* + * Allocate the first set of scbs for this controller. This is to stream- + * line code elsewhere in the driver. If we have to check for the existence + * of scbs in certain code sections, it slows things down. However, as + * soon as we register the IRQ for this card, we could get an interrupt that + * includes possibly the SCSI_RSTI interrupt. If we catch that interrupt + * then we are likely to segfault if we don't have at least one chunk of + * SCBs allocated or add checks all through the reset code to make sure + * that the SCBs have been allocated which is an invalid running condition + * and therefore I think it's preferable to simply pre-allocate the first + * chunk of SCBs. */ + aic7xxx_allocate_scb(p); + + if ( !(aic7xxx_no_reset) ) + { + if (p->features & AHC_TWIN) + { + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no); + aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL); + aic7xxx_reset_current_bus(p); + aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL); + } + /* Reset SCSI bus A. */ + if (aic7xxx_verbose & VERBOSE_PROBE2) + { /* In case we are a 3940, 3985, or 7895, print the right channel */ + char *channel = ""; + if (p->flags & AHC_MULTI_CHANNEL) + { + channel = " A"; + if (p->flags & (AHC_CHNLB|AHC_CHNLC)) + channel = (p->flags & AHC_CHNLB) ? " B" : " C"; + } + printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel); + } + + /* + * Some of the new Ultra2 chipsets need a longer delay after a chip + * reset than just the init setup creates, so we have to delay here + * before we go into a reset in order to make the chips happy. + */ + if (p->features & AHC_ULTRA2) + mdelay(250); + aic7xxx_reset_current_bus(p); + + /* + * Delay for the reset delay. + */ + if (!reset_delay) + aic7xxx_delay(AIC7XXX_RESET_DELAY); + } + else + { + if (!reset_delay) + { + printk(KERN_INFO "(scsi%d) Not resetting SCSI bus. Note: Don't use " + "the no_reset\n", p->host_no); + printk(KERN_INFO "(scsi%d) option unless you have a verifiable need " + "for it.\n", p->host_no); + printk(KERN_INFO "(scsi%d) The no_reset option is known to break some " + "systems,\n", p->host_no); + printk(KERN_INFO "(scsi%d) and is not supported by the driver author\n", + p->host_no); + aic7xxx_delay(AIC7XXX_RESET_DELAY); + } + } + + /* + * Register IRQ with the kernel. Only allow sharing IRQs with + * PCI devices. + */ + if (!(p->chip & AHC_PCI)) + { + result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p)); + } + else + { + result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ, + "aic7xxx", p)); + if (result < 0) + { + result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ, + "aic7xxx", p)); + } + } + if (result < 0) + { + printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring " + "controller.\n", p->host_no, p->irq); + p->irq = 0; + return (0); + } + unpause_sequencer(p, /* unpause_always */ TRUE); return (found); @@ -6691,12 +7962,9 @@ int aic7xxx_chip_reset(struct aic7xxx_host *p) { - unsigned char hcntrl; + unsigned char sblkctl; int wait; - /* Retain the IRQ type across the chip reset. */ - hcntrl = (aic_inb(p, HCNTRL) & IRQMS) | INTEN; - /* * For some 274x boards, we must clear the CHIPRST bit and pause * the sequencer. For some reason, this makes the driver work. @@ -6707,29 +7975,26 @@ * In the future, we may call this function as a last resort for * error handling. Let's be nice and not do any unecessary delays. */ - wait = 1000; /* 1 second (1000 * 1000 usec) */ - while ((wait > 0) && ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0)) + wait = 1000; /* 1 second (1000 * 1 msec) */ + while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK)) { - mdelay(1); /* 1 msec = 1000 usec */ - wait = wait - 1; - } - - if ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0) - { - printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); + mdelay(1); /* 1 msec */ } - aic_outb(p, hcntrl | PAUSE, HCNTRL); + pause_sequencer(p); - switch( aic_inb(p, SBLKCTL) & 0x0a ) + sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE); + if (p->chip & AHC_PCI) + sblkctl &= ~SELBUSB; + switch( sblkctl ) { case 0: /* normal narrow card */ break; case 2: /* Wide card */ - p->type |= AHC_WIDE; + p->features |= AHC_WIDE; break; case 8: /* Twin card */ - p->type |= AHC_TWIN; + p->features |= AHC_TWIN; p->flags |= AHC_MULTI_CHANNEL; break; default: /* hmmm...we don't know what this is */ @@ -6753,6 +8018,7 @@ { struct aic7xxx_host *p = NULL; struct Scsi_Host *host; + int i; /* * Allocate a storage area by registering us with the mid-level @@ -6764,6 +8030,7 @@ { p = (struct aic7xxx_host *) host->hostdata; memset(p, 0, sizeof(struct aic7xxx_host)); + *p = *temp; p->host = host; p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC); @@ -6778,35 +8045,20 @@ * For some reason we don't have enough memory. Free the * allocated memory for the aic7xxx_host struct, and return NULL. */ + release_region(p->base, MAXREG - MINREG); scsi_unregister(host); - p = NULL; + return(NULL); } - if (p != NULL) - { - p->host_no = host->host_no; - p->base = temp->base; - p->mbase = temp->mbase; - p->maddr = temp->maddr; - p->flags = temp->flags; - p->type = temp->type; - p->unpause = temp->unpause; - p->pause = temp->pause; - p->pdev = temp->pdev; - p->pci_bus = temp->pci_bus; - p->pci_device_fn = temp->pci_device_fn; - p->bios_address = temp->bios_address; - p->irq = temp->irq; - p->scsi_id = temp->scsi_id; - p->scsi_id_b = temp->scsi_id_b; - p->discenable = temp->discenable; - p->ultraenb = temp->ultraenb; - p->tagenable = 0; - p->orderedtag = 0; - p->board_name_index = temp->board_name_index; - p->adapter_control = temp->adapter_control; - p->bios_control = temp->bios_control; - DRIVER_LOCK_INIT + p->host_no = host->host_no; + p->tagenable = 0; + p->orderedtag = 0; + for (i=0; itransinfo[i].goal_period = 0; + p->transinfo[i].goal_offset = 0; + p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT; } + DRIVER_LOCK_INIT } return (p); } @@ -6822,28 +8074,35 @@ static void aic7xxx_free(struct aic7xxx_host *p) { - int i, jump; + int i; /* * Free the allocated hardware SCB space. */ - if (p->scb_data->hscbs != NULL) + if (p->scb_data != NULL) { - kfree(p->scb_data->hscbs); - } - /* - * Free the driver SCBs. These were allocated on an as-need - * basis. However, we allocated them 30 at a time up until the - * very last allocation (if there was one). So, we need to free - * every 30th pointer to free the array (this also frees the - * SG_array structs as well). - * - * Note, on 64 bit machines we allocate 29 at a time instead. - */ - jump = (sizeof(int) == sizeof(void *)) ? 30 : 29; - for (i = 0; i < p->scb_data->numscbs; i += jump) - { - kfree(p->scb_data->scb_array[i]); + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscb_kmalloc_ptr); + p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL; + } + /* + * Free the driver SCBs. These were allocated on an as-need + * basis. We allocated these in groups depending on how many + * we could fit into a given amount of RAM. The tail SCB for + * these allocations has a pointer to the alloced area. + */ + for (i = 0; i < p->scb_data->numscbs; i++) + { + if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL) + kfree(p->scb_data->scb_array[i]->kmalloc_ptr); + p->scb_data->scb_array[i] = NULL; + } + + /* + * Free the SCB data area. + */ + kfree(p->scb_data); } /* @@ -6858,10 +8117,6 @@ kfree(p->dev_sdtr_cmnd[i]); } - /* - * Free the SCB data area. - */ - kfree(p->scb_data); } /*+F************************************************************************* @@ -6872,12 +8127,12 @@ * Load the seeprom and configure adapter and target settings. * Returns 1 if the load was successful and 0 otherwise. *-F*************************************************************************/ -static int -load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1) +static void +aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1) { int have_seeprom = 0; - int i, max_targets; - unsigned char target_settings, scsi_conf; + int i, max_targets, mask; + unsigned char scsirate, scsi_conf; unsigned short scarray[128]; struct seeprom_config *sc = (struct seeprom_config *) scarray; @@ -6885,42 +8140,31 @@ { printk(KERN_INFO "aic7xxx: Loading serial EEPROM..."); } - switch (p->type & 0x00001ff1) + switch (p->chip) { - case AHC_AIC7770: /* None of these adapters have seeproms. */ - case AHC_274: + case (AHC_AIC7770|AHC_EISA): /* None of these adapters have seeproms. */ + if (aic_inb(p, SCSICONF) & TERM_ENB) + p->flags |= AHC_TERM_ENB_A; + if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) ) + p->flags |= AHC_TERM_ENB_B; break; - case AHC_284: + case (AHC_AIC7770|AHC_VL): have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray); break; - case AHC_AIC7850: /* The 2910B is a 7850 with a seeprom. */ - case AHC_294AU: - case AHC_AIC7870: /* For these controllers we try the three possible */ - case AHC_AIC7895: /* SEEPROM read types. If none works, then we are */ - case AHC_294: /* SOL. This should catch any SEEPROM variety */ - case AHC_394: /* Adaptec or some motherboard manufacturer might */ - case AHC_294U: /* throw at us, and since we perform a checksum */ - case AHC_394U: /* during the read, we should get bogus seeprom */ - case AHC_AIC7860: /* reads. */ - case AHC_AIC7880: - case AHC_398: - case AHC_398U: + default: have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(*sc)/2, C46); - if (!have_seeprom) - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(scarray)/2, C46); - if (!have_seeprom) - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(*sc)/2, C56_66); + scarray, p->sc_size, p->sc_type); if (!have_seeprom) - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(scarray)/2, C56_66); - break; - - default: + { + if(p->sc_type == C46) + have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + scarray, p->sc_size, C56_66); + else + have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + scarray, p->sc_size, C46); + } break; } @@ -6928,10 +8172,31 @@ { if (aic7xxx_verbose & VERBOSE_PROBE2) { - printk("\naic7xxx: No SEEPROM available; using defaults.\n"); + printk("\naic7xxx: No SEEPROM available.\n"); + } + p->flags |= AHC_NEWEEPROM_FMT; + if (aic_inb(p, SCSISEQ) == 0) + { + p->flags |= AHC_USEDEFAULTS; + p->flags &= ~AHC_BIOS_ENABLED; + p->scsi_id = p->scsi_id_b = 7; + *sxfrctl1 |= STPWEN; + if (aic7xxx_verbose & VERBOSE_PROBE2) + { + printk("aic7xxx: Using default values.\n"); + } + } + else if (aic7xxx_verbose & VERBOSE_PROBE2) + { + printk("aic7xxx: Using leftover BIOS values.\n"); } - p->flags |= AHC_USEDEFAULTS; - p->flags &= ~AHC_BIOS_ENABLED; + if ( *sxfrctl1 & STPWEN ) + { + p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; + sc->adapter_control &= ~CFAUTOTERM; + sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM; + } + p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B; } else { @@ -6941,15 +8206,25 @@ } /* + * Note things in our flags + */ + p->flags |= AHC_SEEPROM_FOUND; + + /* * Update the settings in sxfrctl1 to match the termination settings. */ *sxfrctl1 = 0; /* + * Get our SCSI ID from the SEEPROM setting... + */ + p->scsi_id = (sc->brtime_id & CFSCSIID); + + /* * First process the settings that are different between the VLB * and PCI adapter seeproms. */ - if (p->type & AHC_284) + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770) { /* VLB adapter seeproms */ if (sc->bios_control & CF284XEXTEND) @@ -6958,42 +8233,42 @@ if (sc->adapter_control & CF284XSTERM) { *sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_A; + p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; } - /* - * The 284x SEEPROM doesn't have a max targets field. We - * set it to 16 to make sure we take care of the 284x-wide - * adapters. For narrow adapters, going through the extra - * 8 target entries will not cause any harm since they will - * will not be used. - * - * XXX - We should probably break out the bus detection - * from the register function so we can use it here - * to tell us how many targets there really are. - */ - max_targets = 16; } else { /* PCI adapter seeproms */ if (sc->bios_control & CFEXTEND) p->flags |= AHC_EXTEND_TRANS_A; + if (sc->bios_control & CFBIOSEN) + p->flags |= AHC_BIOS_ENABLED; + else + p->flags &= ~AHC_BIOS_ENABLED; if (sc->adapter_control & CFSTERM) { *sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_A; + p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; } - - /* Limit to 16 targets just in case. */ - max_targets = MIN(sc->max_targets & CFMAXTARG, 16); } + p->sc = *sc; + } - p->discenable = 0; + p->discenable = 0; + + /* + * Limit to 16 targets just in case. The 2842 for one is known to + * blow the max_targets setting, future cards might also. + */ + max_targets = MIN(sc->max_targets & CFMAXTARG, + ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8)); + if (have_seeprom) + { for (i = 0; i < max_targets; i++) { - if( (p->type & AHC_ULTRA) && + if( (p->features & AHC_ULTRA) && !(sc->adapter_control & CFULTRAEN) && (sc->device_flags[i] & CFSYNCHISULTRA) ) { @@ -7001,114 +8276,203 @@ break; } } + } - for (i = 0; i < max_targets; i++) + for (i = 0; i < max_targets; i++) + { + mask = (0x01 << i); + if (!have_seeprom) + { + if(aic_inb(p, SCSISEQ) != 0) + { + /* + * OK...the BIOS set things up and left behind the settings we need. + * Just make our sc->device_flags[i] entry match what the card has + * set for this device. + */ + p->discenable = + ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) ); + p->ultraenb = + (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) ); + sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0; + if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) + sc->device_flags[i] |= CFWIDEB; + if (p->features & AHC_ULTRA2) + { + if (aic_inb(p, TARG_OFFSET + i)) + { + sc->device_flags[i] |= CFSYNCH; + sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07); + if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 ) + sc->device_flags[i] |= CFSYNCHISULTRA; + } + } + else + { + if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER) + { + sc->device_flags[i] |= CFSYNCH; + if (p->features & AHC_ULTRA) + sc->device_flags[i] |= ((p->ultraenb & mask) ? + CFSYNCHISULTRA : 0); + } + } + } + else + { + /* + * Assume the BIOS has NOT been run on this card and nothing between + * the card and the devices is configured yet. + */ + sc->device_flags[i] = CFDISC; + if (p->features & AHC_WIDE) + sc->device_flags[i] |= CFWIDEB; + if (p->features & AHC_ULTRA2) + sc->device_flags[i] |= 3; + else if (p->features & AHC_ULTRA) + sc->device_flags[i] |= CFSYNCHISULTRA; + sc->device_flags[i] |= CFSYNCH; + aic_outb(p, 0, TARG_SCSIRATE + i); + if (p->features & AHC_ULTRA2) + aic_outb(p, 0, TARG_OFFSET + i); + } + } + if (sc->device_flags[i] & CFDISC) + { + p->discenable |= mask; + } + if (p->flags & AHC_NEWEEPROM_FMT) { - target_settings = (sc->device_flags[i] & CFXFER) << 4; - if (sc->device_flags[i] & CFSYNCH) - { - target_settings |= SOFS; - } - if (sc->device_flags[i] & CFWIDEB) + if (sc->device_flags[i] & CFSYNCHISULTRA) { - target_settings |= WIDEXFER; + p->ultraenb |= mask; } - if (sc->device_flags[i] & CFDISC) + } + else if (sc->adapter_control & CFULTRAEN) + { + p->ultraenb |= mask; + } + if ( (sc->device_flags[i] & CFSYNCH) == 0) + { + sc->device_flags[i] &= ~CFXFER; + p->ultraenb &= ~mask; + p->transinfo[i].user_offset = 0; + p->transinfo[i].user_period = 0; + p->transinfo[i].cur_offset = 0; + p->transinfo[i].cur_period = 0; + p->needsdtr_copy &= ~mask; + } + else + { + if (p->features & AHC_ULTRA2) { - p->discenable |= (0x01 << i); + p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2; + p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i); + scsirate = (sc->device_flags[i] & CFXFER) | + ((p->ultraenb & mask) ? 0x18 : 0x10); + p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate, + AHC_SYNCRATE_ULTRA2); + p->transinfo[i].cur_period = aic7xxx_find_period(p, + aic_inb(p, TARG_SCSIRATE + i), + AHC_SYNCRATE_ULTRA2); } - if (p->flags & AHC_NEWEEPROM_FMT) + else { - if (sc->device_flags[i] & CFSYNCHISULTRA) + scsirate = (sc->device_flags[i] & CFXFER) << 4; + if (sc->device_flags[i] & CFWIDEB) + p->transinfo[i].user_offset = MAX_OFFSET_16BIT; + else + p->transinfo[i].user_offset = MAX_OFFSET_8BIT; + if (p->features & AHC_ULTRA) { - p->ultraenb |= (0x01 << i); + short ultraenb; + ultraenb = aic_inb(p, ULTRA_ENB) | + (aic_inb(p, ULTRA_ENB + 1) << 8); + p->transinfo[i].user_period = aic7xxx_find_period(p, + scsirate, + (p->ultraenb & mask) ? + AHC_SYNCRATE_ULTRA : + AHC_SYNCRATE_FAST); + p->transinfo[i].cur_period = aic7xxx_find_period(p, + aic_inb(p, TARG_SCSIRATE + i), + (ultraenb & mask) ? + AHC_SYNCRATE_ULTRA : + AHC_SYNCRATE_FAST); } + else + p->transinfo[i].user_period = aic7xxx_find_period(p, + scsirate, AHC_SYNCRATE_FAST); } - else if (sc->adapter_control & CFULTRAEN) - { - p->ultraenb |= (0x01 << i); - } - if ( ((target_settings & 0x70) == 0x40) && - (p->ultraenb & (0x01 << i)) ) - { - target_settings &= ~0x70; - p->ultraenb &= ~(0x01 << i); - } - /* - * Don't output these settings if we aren't resetting the bus, instead, - * leave the devices current settings in place - */ - if (!(aic7xxx_no_reset)) - aic_outb(p, target_settings, TARG_SCRATCH + i); + p->needsdtr_copy |= mask; } - aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB); - aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1); - aic_outb(p, (p->ultraenb & 0xFF), ULTRA_ENB); - aic_outb(p, ((p->ultraenb >> 8) & 0xFF), ULTRA_ENB + 1); - - p->scsi_id = sc->brtime_id & CFSCSIID; - p->adapter_control = sc->adapter_control; - p->bios_control = sc->bios_control; - - if (p->bios_control & CFBIOSEN) + if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) ) { - p->flags &= ~AHC_USEDEFAULTS; - p->flags |= AHC_BIOS_ENABLED; + p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_16_BIT; + p->needwdtr_copy |= mask; } else { - p->flags &= ~AHC_BIOS_ENABLED; - p->flags |= AHC_USEDEFAULTS; + p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_8_BIT; + p->needwdtr_copy &= ~mask; } + p->transinfo[i].cur_width = + (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) ? + MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT; + } + aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB); + aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1); + p->needwdtr = p->needwdtr_copy; + p->needsdtr = p->needsdtr_copy; + p->wdtr_pending = p->sdtr_pending = 0; + + /* + * We set the p->ultraenb from the SEEPROM to begin with, but now we make + * it match what is already down in the card. If we are doing a reset + * on the card then this will get put back to a default state anyway. + * This allows us to not have to pre-emptively negotiate when using the + * no_reset option. + */ + if (p->features & AHC_ULTRA) + p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8); + + + scsi_conf = (p->scsi_id & HSCSIID); + + if(have_seeprom) + { + p->adapter_control = sc->adapter_control; + p->bios_control = sc->bios_control; - if ((p->type & 0x1ff1) == AHC_AIC7895) + switch (p->chip & AHC_CHIPID_MASK) { - if (p->adapter_control & CFBPRIMARY) - p->flags |= AHC_CHANNEL_B_PRIMARY; + case AHC_AIC7895: + case AHC_AIC7896: + if (p->adapter_control & CFBPRIMARY) + p->flags |= AHC_CHANNEL_B_PRIMARY; + default: + break; } - scsi_conf = (p->scsi_id & 0x7); if (sc->adapter_control & CFSPARITY) scsi_conf |= ENSPCHK; - /* - * The 7850 controllers with a seeprom, do not honor the CFRESETB - * flag in the seeprom. Assume that we want to reset the SCSI bus. - */ - if (sc->adapter_control & CFRESETB) - scsi_conf |= RESET_SCSI; - /* - * We may be a 2842, if so, preserve the TERM_ENB bit in scsi conf - */ - if ( (p->flags & AHC_TERM_ENB_A) && - ((p->type & AHC_AIC7770) == AHC_AIC7770) ) - scsi_conf |= TERM_ENB; - /* - * If this is an Ultra card, is Ultra mode enabled? If not, disable - * it in the host struct as well - */ - if ( (p->type & AHC_ULTRA) && - !(sc->adapter_control & CFULTRAEN) && - !(p->flags & AHC_NEWEEPROM_FMT) ) - p->type &= ~AHC_ULTRA; + } + else + { + scsi_conf |= ENSPCHK | RESET_SCSI; + } + /* + * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card. + * The 2842 and 2742 cards already have these registers set and we don't + * want to muck with them since we don't set all the bits they do. + */ + if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI ) + { /* Set the host ID */ aic_outb(p, scsi_conf, SCSICONF); /* In case we are a wide card */ aic_outb(p, p->scsi_id, SCSICONF + 1); - - if ((p->type & AHC_AIC7860) == AHC_AIC7860) - { - if ( aic_inb(p, SPIOCAP) & SSPIOCPS ) - /* - * Update the settings in sxfrctl1 to match the termination - * settings. - */ - configure_termination(p, sxfrctl1, sc->adapter_control, max_targets); - } - else if (have_seeprom && ((p->type & AHC_AIC7770) != AHC_AIC7770)) - configure_termination(p, sxfrctl1, sc->adapter_control, max_targets); } - return (have_seeprom); } /*+F************************************************************************* @@ -7131,7 +8495,7 @@ struct aic7xxx_host *list_p = NULL; int found = 0; ahc_flag_type flags = 0; - ahc_type type; + int type; unsigned char sxfrctl1; #if defined(__i386__) || defined(__alpha__) unsigned char hcntrl, hostconf; @@ -7165,7 +8529,7 @@ * EISA/VL-bus card signature probe. */ slot = MINSLOT; - while (slot <= MAXSLOT) + while ( (slot <= MAXSLOT) && !(aic7xxx_no_probe) ) { base = SLOTBASE(slot) + MINREG; @@ -7179,27 +8543,11 @@ continue; /* back to the beginning of the for loop */ } flags = 0; - type = aic7xxx_probe(slot, base + HID0, &flags); - switch (type) + type = aic7xxx_probe(slot, base + AHC_HID0, &flags); + if (type == -1) { - case AHC_AIC7770: - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at EISA %d\n", - board_names[2], slot); - break; - case AHC_274: - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at EISA %d\n", - board_names[3], slot); - break; - case AHC_284: - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at VLB %d\n", - board_names[4], slot); - break; - default: - slot++; - continue; /* back to the beginning of the while loop */ + slot++; + continue; } temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC); if (temp_p == NULL) @@ -7222,8 +8570,6 @@ temp_p->unpause = hcntrl | INTEN; temp_p->pause = hcntrl | PAUSE | INTEN; temp_p->base = base; - temp_p->type = type; - temp_p->flags = flags | AHC_PAGESCBS; temp_p->mbase = 0; temp_p->maddr = 0; temp_p->pci_bus = 0; @@ -7234,6 +8580,8 @@ temp_p->irq = 0; else temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F; + temp_p->flags |= AHC_PAGESCBS; + switch (temp_p->irq) { case 9: @@ -7256,6 +8604,7 @@ * We are commited now, everything has been checked and this card * has been found, now we just set it up */ + /* * Insert our new struct into the list at the end */ @@ -7277,12 +8626,18 @@ temp_p->flags |= AHC_EXTEND_TRANS_B; } - switch (temp_p->type & 0x1ff1) + switch (type) { - case AHC_AIC7770: + case 0: temp_p->board_name_index = 2; - case AHC_274: + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at EISA %d\n", + board_names[2], slot); + /* FALLTHROUGH */ + case 1: { + temp_p->chip = AHC_AIC7770 | AHC_EISA; + temp_p->features |= AHC_AIC7770_FE; temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL); /* @@ -7291,7 +8646,12 @@ * the mid-level SCSI code which channel is primary. */ if (temp_p->board_name_index == 0) + { temp_p->board_name_index = 3; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at EISA %d\n", + board_names[3], slot); + } if (temp_p->bios_control & CHANNEL_B_PRIMARY) { temp_p->flags |= AHC_CHANNEL_B_PRIMARY; @@ -7299,7 +8659,6 @@ if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED) { - temp_p->flags |= AHC_USEDEFAULTS; temp_p->flags &= ~AHC_BIOS_ENABLED; } else @@ -7308,79 +8667,46 @@ temp_p->flags |= AHC_BIOS_ENABLED; if ( (temp_p->bios_control & 0x20) == 0 ) { - switch(temp_p->bios_control & 0x07) - { - case 0x0: - temp_p->bios_address = 0xcc000; - break; - case 0x1: - temp_p->bios_address = 0xd0000; - break; - case 0x2: - temp_p->bios_address = 0xd4000; - break; - case 0x3: - temp_p->bios_address = 0xd8000; - break; - case 0x4: - temp_p->bios_address = 0xdc000; - break; - case 0x5: - temp_p->bios_address = 0xe0000; - break; - case 0x6: - temp_p->bios_address = 0xe4000; - break; - case 0x7: - temp_p->bios_address = 0xe8000; - break; - default: - break; /* can't get here */ - } + temp_p->bios_address = 0xcc000; + temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07)); } else { - switch(temp_p->bios_control & 0x06) - { - case 0x0: - temp_p->bios_address = 0xd0000; - break; - case 0x2: - temp_p->bios_address = 0xd8000; - break; - case 0x4: - temp_p->bios_address = 0xe0000; - break; - case 0x6: - temp_p->bios_address = 0xe8000; - break; - default: - break; /* can't get here */ - } + temp_p->bios_address = 0xd0000; + temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06)); } } temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8; temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1); - if (temp_p->flags & AHC_USEDEFAULTS) + if (temp_p->features & AHC_WIDE) { - temp_p->scsi_id = temp_p->scsi_id_b = 7; - temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B; + temp_p->scsi_id = temp_p->adapter_control & HWSCSIID; + temp_p->scsi_id_b = temp_p->scsi_id; } else { - if ( ((temp_p->adapter_control >> 8) & TERM_ENB) != 0 ) - temp_p->flags |= AHC_TERM_ENB_A; - if ( (temp_p->adapter_control & TERM_ENB) != 0 ) - temp_p->flags |= AHC_TERM_ENB_B; temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID; temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID; } + aic7xxx_load_seeprom(temp_p, &sxfrctl1); break; } - case AHC_284: - load_seeprom(temp_p, &sxfrctl1); + case 2: + case 3: + temp_p->chip = AHC_AIC7770 | AHC_VL; + temp_p->features |= AHC_AIC7770_FE; + if (type == 2) + temp_p->flags |= AHC_BIOS_ENABLED; + else + temp_p->flags &= ~AHC_BIOS_ENABLED; + if (aic_inb(temp_p, SCSICONF) & TERM_ENB) + sxfrctl1 = STPWEN; + aic7xxx_load_seeprom(temp_p, &sxfrctl1); temp_p->board_name_index = 4; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at VLB %d\n", + board_names[2], slot); switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL ) { case 0x00: @@ -7439,42 +8765,84 @@ { unsigned short vendor_id; unsigned short device_id; - ahc_type type; + ahc_chip chip; ahc_flag_type flags; + ahc_feature features; int board_name_index; - } const aic7xxx_pci_devices[] = { + unsigned short seeprom_size; + unsigned short seeprom_type; + } const aic_pdevs[] = { {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE, - AHC_FNONE, 1 }, + AHC_FNONE, AHC_FENONE, 1, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850, - AHC_PAGESCBS | AHC_USEDEFAULTS, 5 }, + AHC_PAGESCBS, AHC_AIC7850_FE, 5, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850, - AHC_PAGESCBS | AHC_USEDEFAULTS, 6 }, + AHC_PAGESCBS, AHC_AIC7850_FE, 6, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 7 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_294AU, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 8 }, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7860_FE, 7, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7860_FE, 8, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 9 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_294, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 10 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_394, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 11 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_398, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 12 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_294, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 13 }, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 9, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 10, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7870_FE, 11, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7870_FE, 12, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 13, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 14 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_294U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 15 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_394U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 16 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_398U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 17 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_294U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 18 }, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 14, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 15, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7880_FE, 16, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7880_FE, 17, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 19 } + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7895_FE, 19, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7890_FE, 20, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7890_FE, 21, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7896_FE, 22, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7896_FE, 23, + 32, C56_66 }, }; unsigned short command; @@ -7482,7 +8850,6 @@ #ifdef MMAPIO unsigned long page_offset, base; #endif - struct aic7xxx_host *first_7895 = NULL; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) struct pci_dev *pdev = NULL; #else @@ -7491,17 +8858,17 @@ unsigned char pci_bus, pci_devfn, pci_irq; #endif - for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++) + for (i = 0; i < NUMBER(aic_pdevs); i++) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pdev = NULL; - while ((pdev = pci_find_device(aic7xxx_pci_devices[i].vendor_id, - aic7xxx_pci_devices[i].device_id, + while ((pdev = pci_find_device(aic_pdevs[i].vendor_id, + aic_pdevs[i].device_id, pdev))) #else index = 0; - while (!(pcibios_find_device(aic7xxx_pci_devices[i].vendor_id, - aic7xxx_pci_devices[i].device_id, + while (!(pcibios_find_device(aic_pdevs[i].vendor_id, + aic_pdevs[i].device_id, index++, &pci_bus, &pci_devfn)) ) #endif { @@ -7518,9 +8885,12 @@ GFP_ATOMIC)) != NULL ) { memset(temp_p, 0, sizeof(struct aic7xxx_host)); - temp_p->type = aic7xxx_pci_devices[i].type; - temp_p->flags = aic7xxx_pci_devices[i].flags; - temp_p->board_name_index = aic7xxx_pci_devices[i].board_name_index; + temp_p->chip = aic_pdevs[i].chip | AHC_PCI; + temp_p->flags = aic_pdevs[i].flags; + temp_p->features = aic_pdevs[i].features; + temp_p->board_name_index = aic_pdevs[i].board_name_index; + temp_p->sc_size = aic_pdevs[i].seeprom_size; + temp_p->sc_type = aic_pdevs[i].seeprom_type; /* * Read sundry information from PCI BIOS. @@ -7532,19 +8902,29 @@ temp_p->pci_device_fn = pdev->devfn; temp_p->base = pdev->base_address[0]; temp_p->mbase = pdev->base_address[1]; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pdev->devfn), + PCI_FUNC(temp_p->pdev->devfn)); pci_read_config_word(pdev, PCI_COMMAND, &command); if (aic7xxx_verbose & VERBOSE_PROBE2) { printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n", (int)command); } +#ifdef AIC7XXX_STRICT_PCI_SETUP command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#else + command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#endif if (aic7xxx_pci_parity == 0) command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); pci_write_config_word(pdev, PCI_COMMAND, command); - pci_read_config_dword(pdev, PCI_COMMAND, &devconfig); +#ifdef AIC7XXX_STRICT_PCI_SETUP + pci_read_config_dword(pdev, DEVCONFIG, &devconfig); if (aic7xxx_verbose & VERBOSE_PROBE2) { printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig); @@ -7558,15 +8938,16 @@ { devconfig |= 0x00000008; } - pci_write_config_dword(pdev, PCI_COMMAND, devconfig); - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at PCI %d/%d\n", - board_names[aic7xxx_pci_devices[i].board_name_index], - PCI_SLOT(temp_p->pdev->devfn), - PCI_FUNC(temp_p->pdev->devfn)); -#else + pci_write_config_dword(pdev, DEVCONFIG, devconfig); +#endif /* AIC7XXX_STRICT_PCI_SETUP */ +#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ temp_p->pci_bus = pci_bus; temp_p->pci_device_fn = pci_devfn; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE, &pci_irq); temp_p->irq = pci_irq; @@ -7582,12 +8963,17 @@ printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n", (int)command); } +#ifdef AIC7XXX_STRICT_PCI_SETUP command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#else + command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#endif if (aic7xxx_pci_parity == 0) command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command); +#ifdef AIC7XXX_STRICT_PCI_SETUP pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig); if (aic7xxx_verbose & VERBOSE_PROBE2) { @@ -7603,12 +8989,8 @@ devconfig |= 0x00000008; } pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig); - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at PCI %d/%d\n", - board_names[aic7xxx_pci_devices[i].board_name_index], - PCI_SLOT(temp_p->pci_device_fn), - PCI_FUNC(temp_p->pci_device_fn)); -#endif +#endif /* AIC7XXX_STRICT_PCI_SETUP */ +#endif /* LINUIX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ /* * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so @@ -7616,49 +8998,24 @@ */ temp_p->base &= PCI_BASE_ADDRESS_IO_MASK; temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK; - temp_p->unpause = (aic_inb(temp_p, HCNTRL) & IRQMS) | INTEN; + temp_p->unpause = INTEN; temp_p->pause = temp_p->unpause | PAUSE; #ifdef MMAPIO - if((temp_p->type & AHC_AIC7850) != AHC_AIC7850) - { - base = temp_p->mbase & PAGE_MASK; - page_offset = temp_p->mbase - base; - /* - * replace the next line with this one if you are using 2.1.x: - * temp_p->maddr = ioremap(base, page_offset + 256); - */ + base = temp_p->mbase & PAGE_MASK; + page_offset = temp_p->mbase - base; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - temp_p->maddr = ioremap(base, page_offset + 256); + temp_p->maddr = ioremap_nocache(base, page_offset + 256); #else - temp_p->maddr = vremap(base, page_offset + 256); + temp_p->maddr = vremap(base, page_offset + 256); #endif - if(temp_p->maddr) - { - temp_p->maddr += page_offset; - } - } - else + if(temp_p->maddr) { -#ifdef __i386__ - /* - * Resort to PIO mode on these controllers and Intel hardware. - * For other hardware we need to either disable these controllers - * or do without MMAPed IO. However, for PPC, we can't do - * MMAPed IO (according to what I've heard) so we may be forced - * to just fail detection on those cards. - */ - temp_p->maddr = NULL; -#else - kfree(temp_p); - temp_p = NULL; - continue; -#endif /* __i386__ */ + temp_p->maddr += page_offset; } #endif - aic_outb(temp_p, temp_p->pause, HCNTRL); - while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ; + pause_sequencer(temp_p); /* * Clear out any pending PCI error status messages. Also set @@ -7675,7 +9032,10 @@ /* * Remember how the card was setup in case there is no seeprom. */ - temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID; + if (temp_p->features & AHC_ULTRA2) + temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID; + else + temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID; /* * Get current termination setting */ @@ -7688,11 +9048,17 @@ continue; } - switch (temp_p->type & 0x1ff1) + /* + * Doing a switch based upon i is really gross, but since Justin + * changed around the chip ID stuff, we can't use that any more. + * Since we don't scan the devices the same way as FreeBSD, we end + * up doing this gross hack in order to avoid totally splitting + * away from Justin's init code in ahc_pci.c + */ + switch (i) { - case AHC_394: /* 3940 */ - case AHC_394U: /* 3940-Ultra */ - temp_p->flags |= AHC_MULTI_CHANNEL; + case 7: /* 3940 */ + case 12: /* 3940-Ultra */ switch(PCI_SLOT(temp_p->pci_device_fn)) { case 5: @@ -7703,9 +9069,8 @@ } break; - case AHC_398: /* 3985 */ - case AHC_398U: /* 3985-Ultra */ - temp_p->flags |= AHC_MULTI_CHANNEL; + case 8: /* 3985 */ + case 13: /* 3985-Ultra */ switch(PCI_SLOT(temp_p->pci_device_fn)) { case 8: @@ -7719,54 +9084,44 @@ } break; - case AHC_AIC7895: - temp_p->flags |= AHC_MULTI_CHANNEL; + case 15: + case 18: + case 19: #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) if (PCI_FUNC(temp_p->pdev->devfn) != 0) { temp_p->flags |= AHC_CHNLB; } - pci_read_config_dword(pdev, DEVCONFIG, &devconfig); - devconfig = le32_to_cpu(devconfig); - devconfig |= SCBSIZE32; - devconfig = cpu_to_le32(devconfig); - pci_write_config_dword(pdev, DEVCONFIG, devconfig); + /* + * The 7895 is the only chipset that sets the SCBSIZE32 param + * in the DEVCONFIG register. The Ultra2 chipsets use + * the DSCOMMAND0 register instead. + */ + if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) + { + pci_read_config_dword(pdev, DEVCONFIG, &devconfig); + devconfig |= SCBSIZE32; + pci_write_config_dword(pdev, DEVCONFIG, devconfig); + } #else if (PCI_FUNC(temp_p->pci_device_fn) != 0) { temp_p->flags |= AHC_CHNLB; } - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); - devconfig |= SCBSIZE32; - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, - devconfig); -#endif - if (aic7xxx_7895_irq_hack != -1) + /* + * The 7895 is the only chipset that sets the SCBSIZE32 param + * in the DEVCONFIG register. The Ultra2 chipsets use + * the DSCOMMAND0 register instead. + */ + if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { - if (first_7895 == NULL) - { - printk(KERN_INFO "aic7xxx: Using 7895_irq_hack. Please " - "upgrade your motherboard BIOS\n"); - first_7895 = temp_p; - } - else if (aic7xxx_7895_irq_hack == 0) - { - if (temp_p->flags & AHC_CHNLB) - temp_p->irq = first_7895->irq; - else - first_7895->irq = temp_p->irq; - first_7895 = NULL; - } - else - { - if ( !(temp_p->flags & AHC_CHNLB) ) - temp_p->irq = first_7895->irq; - else - first_7895->irq = temp_p->irq; - first_7895 = NULL; - } + pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, + &devconfig); + devconfig |= SCBSIZE32; + pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, + devconfig); } +#endif break; default: break; @@ -7778,21 +9133,74 @@ * on 394x and 398x cards we'll end up reading the wrong settings * for channels B and C */ - if ( !(load_seeprom(temp_p, &sxfrctl1)) ) + switch (temp_p->chip & AHC_CHIPID_MASK) { - temp_p->flags |= AHC_USEDEFAULTS; - if (sxfrctl1 & STPWEN) - temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B; + case AHC_AIC7890: + case AHC_AIC7896: + aic_outb(temp_p, 0, SCAMCTL); + /* + * We used to set DPARCKEN in this register, but after talking + * to a tech from Adaptec, I found out they don't use that + * particular bit in their own register settings, and when you + * combine that with the fact that I determined that we were + * seeing Data-Path Parity Errors on things we shouldn't see + * them on, I think there is a bug in the silicon and the way + * to work around it is to disable this particular check. Also + * This bug only showed up on certain commands, so it seems to + * be pattern related or some such. The commands we would + * typically send as a linux TEST_UNIT_READY or INQUIRY command + * could cause it to be triggered, while regular commands that + * actually made reasonable use of the SG array capabilities + * seemed not to cause the problem. + */ + /* + aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) | + CACHETHEN | DPARCKEN | MPARCKEN | + USCBSIZE32 | CIOPARCKEN, + DSCOMMAND0); + */ + aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | + CACHETHEN | MPARCKEN | USCBSIZE32 | + CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0); + /* FALLTHROUGH */ + default: + /* + * We attempt to read a SEEPROM on *everything*. If we fail, + * then we fail, but this covers things like 2910c cards that + * now have SEEPROMs with their 7856 chipset that we would + * otherwise ignore. They still don't have a BIOS, but they + * have a SEEPROM that the SCSISelect utility on the Adaptec + * diskettes can configure. + */ + aic7xxx_load_seeprom(temp_p, &sxfrctl1); + break; + case AHC_AIC7850: + case AHC_AIC7860: + /* + * Set the DSCOMMAND0 register on these cards different from + * on the 789x cards. Also, read the SEEPROM as well. + */ + aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | + CACHETHEN | MPARCKEN) & ~DPARCKEN, + DSCOMMAND0); + aic7xxx_load_seeprom(temp_p, &sxfrctl1); + break; } + /* * and then we need another switch based on the type in order to * make sure the channel B primary flag is set properly on 7895 - * controllers....Arrrgggghhh!!! + * controllers....Arrrgggghhh!!! We also have to catch the fact + * that when you disable the BIOS on the 7895 on the Intel DK440LX + * motherboard, and possibly others, it only sets the BIOS disabled + * bit on the A channel...I think I'm starting to lean towards + * going postal.... */ - switch(temp_p->type & 0x1ff1) + switch(temp_p->chip & AHC_CHIPID_MASK) { case AHC_AIC7895: + case AHC_AIC7896: current_p = list_p; while(current_p != NULL) { @@ -7801,11 +9209,21 @@ PCI_SLOT(temp_p->pci_device_fn)) ) { if ( PCI_FUNC(current_p->pci_device_fn) == 0 ) + { temp_p->flags |= (current_p->flags & AHC_CHANNEL_B_PRIMARY); + temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS); + temp_p->flags |= + (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS)); + } else + { current_p->flags |= (temp_p->flags & AHC_CHANNEL_B_PRIMARY); + current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS); + current_p->flags |= + (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS)); + } } current_p = current_p->next; } @@ -7815,6 +9233,56 @@ } /* + * We do another switch based on i so that we can exclude all + * 3895 devices from the next option since the 3895 cards use + * shared external SCB RAM while all other cards have dedicated + * external SCB RAM per channel. Also exclude the 7850 and + * 7860 based stuff since they can have garbage in the bit + * that indicates external RAM and get some of this stuff + * wrong as a result. + */ + switch(temp_p->chip & AHC_CHIPID_MASK) + { + default: + break; + case AHC_AIC7895: + case AHC_AIC7896: +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_read_config_dword(pdev, DEVCONFIG, &devconfig); +#else + pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, + &devconfig); +#endif + if (temp_p->features & AHC_ULTRA2) + { + if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) + { + aic_outb(temp_p, + aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2, + DSCOMMAND0); + temp_p->flags |= AHC_EXTERNAL_SRAM; + devconfig |= EXTSCBPEN; + } + } + else if (devconfig & RAMPSM) + { + devconfig &= ~SCBRAMSEL; + devconfig |= EXTSCBPEN; + temp_p->flags |= AHC_EXTERNAL_SRAM; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_write_config_dword(pdev, DEVCONFIG, devconfig); +#else + pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, + devconfig); +#endif + if ( (temp_p->flags & AHC_EXTERNAL_SRAM) && + (temp_p->flags & AHC_CHNLB) ) + aic_outb(temp_p, 1, CCSCBBADDR); + break; + } + + /* * Take the LED out of diagnostic mode */ aic_outb(temp_p, @@ -7823,75 +9291,21 @@ /* * We don't know where this is set in the SEEPROM or by the - * BIOS, so we default to 100%. + * BIOS, so we default to 100%. On Ultra2 controllers, use 75% + * instead. */ - aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS); - - if (temp_p->flags & AHC_USEDEFAULTS) + if (temp_p->features & AHC_ULTRA2) { - int j; - unsigned char k; - /* - * Default setup; should only be used if the adapter does - * not have a SEEPROM. - */ - /* - * Check the target scratch area to see if someone set us - * up already. We are previously set up if the scratch - * area contains something other than all zeroes and ones. - */ - for (j = TARG_SCRATCH; j < 0x60; j++) - { - k = aic_inb(temp_p, j); - /* Check for all zeros and ones. Break out if we pass */ - if( (k != 0x00) && (k != 0xff) ) - break; - } - /* If j makes it to 0x60, then all entries are either 0x00 or - * 0xff. We would then assume we have *not* been initialized - * and drop through here. OTOH, if even one entry is inited, - * then as long as we appear to have a valid SCSI ID, we'll use - * the leftover BIOS values. - */ - if ((j != 0x60) && (temp_p->scsi_id != 0)) - { - temp_p->flags &= ~AHC_USEDEFAULTS; - if (aic7xxx_verbose & VERBOSE_PROBE2) - { - printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n"); - } - } - else - { - /* - * Assume only one connector and always turn on - * termination. - */ - temp_p->flags &= ~AHC_BIOS_ENABLED; - temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B; - sxfrctl1 = STPWEN; - temp_p->scsi_id = 7; - } - aic_outb(temp_p, (temp_p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI, - SCSICONF); - /* In case we are a wide card. */ - aic_outb(temp_p, temp_p->scsi_id, SCSICONF + 1); + aic_outb(temp_p, RD_DFTHRSH_75 | WR_DFTHRSH_75, DFF_THRSH); } - else /* not using defaults */ + else { - if (sxfrctl1 & STPWEN) - temp_p->flags |= AHC_TERM_ENB_A; + aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS); } if (aic7xxx_extended) temp_p->flags |= AHC_EXTEND_TRANS_A; - /* - * Put our termination setting into sxfrctl1 now that the - * generic initialization is complete. - */ - sxfrctl1 |= aic_inb(temp_p, SXFRCTL1); - aic_outb(temp_p, sxfrctl1, SXFRCTL1); if ( list_p == NULL ) { list_p = current_p = temp_p; @@ -7909,7 +9323,7 @@ else /* Well, we found one, but we couldn't get any memory */ { printk("aic7xxx: Found <%s>\n", - board_names[aic7xxx_pci_devices[i].board_name_index]); + board_names[aic_pdevs[i].board_name_index]); printk(KERN_INFO "aic7xxx: Unable to allocate device memory, " "skipping.\n"); } @@ -7930,87 +9344,74 @@ */ { - struct aic7xxx_host *vlb_enab, *vlb_disab, *pci; + struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL }; + struct aic7xxx_host *vlb, *pci; struct aic7xxx_host *prev_p; struct aic7xxx_host *p; unsigned char left; - prev_p = vlb_enab = vlb_disab = pci = NULL; + prev_p = vlb = pci = NULL; temp_p = list_p; while (temp_p != NULL) { - switch(temp_p->type) + switch(temp_p->chip & ~AHC_CHIPID_MASK) { - case AHC_AIC7770: - case AHC_274: - case AHC_284: - if (temp_p->flags & AHC_BIOS_ENABLED) + case AHC_EISA: + case AHC_VL: + { + p = temp_p; + if (p->flags & AHC_BIOS_ENABLED) + vlb = sort_list[0]; + else + vlb = sort_list[2]; + + if (vlb == NULL) { - if (vlb_enab == NULL) - { - vlb_enab = temp_p; - temp_p = temp_p->next; - vlb_enab->next = NULL; - } - else - { - current_p = vlb_enab; - prev_p = NULL; - while ( (current_p != NULL) && - (current_p->bios_address < temp_p->bios_address)) - { - prev_p = current_p; - current_p = current_p->next; - } - if (prev_p != NULL) - { - prev_p->next = temp_p; - temp_p = temp_p->next; - prev_p->next->next = current_p; - } - else - { - vlb_enab = temp_p; - temp_p = temp_p->next; - vlb_enab->next = current_p; - } - } + vlb = temp_p; + temp_p = temp_p->next; + vlb->next = NULL; } else { - if (vlb_disab == NULL) + current_p = vlb; + prev_p = NULL; + while ( (current_p != NULL) && + (current_p->bios_address < temp_p->bios_address)) + { + prev_p = current_p; + current_p = current_p->next; + } + if (prev_p != NULL) { - vlb_disab = temp_p; + prev_p->next = temp_p; temp_p = temp_p->next; - vlb_disab->next = NULL; + prev_p->next->next = current_p; } else { - current_p = vlb_disab; - prev_p = NULL; - while ( (current_p != NULL) && - (current_p->base < temp_p->base)) - { - prev_p = current_p; - current_p = current_p->next; - } - if (prev_p != NULL) - { - prev_p->next = temp_p; - temp_p = temp_p->next; - prev_p->next->next = current_p; - } - else - { - vlb_disab = temp_p; - temp_p = temp_p->next; - vlb_disab->next = current_p; - } + vlb = temp_p; + temp_p = temp_p->next; + vlb->next = current_p; } } + + if (p->flags & AHC_BIOS_ENABLED) + sort_list[0] = vlb; + else + sort_list[2] = vlb; + break; + } default: /* All PCI controllers fall through to default */ + { + + p = temp_p; + if (p->flags & AHC_BIOS_ENABLED) + pci = sort_list[1]; + else + pci = sort_list[3]; + if (pci == NULL) { pci = temp_p; @@ -8085,75 +9486,60 @@ pci->next = current_p; } } + + if (p->flags & AHC_BIOS_ENABLED) + sort_list[1] = pci; + else + sort_list[3] = pci; + break; + } } /* End of switch(temp_p->type) */ } /* End of while (temp_p != NULL) */ /* * At this point, the cards have been broken into 4 sorted lists, now * we run through the lists in order and register each controller */ - left = found; - temp_p = vlb_enab; - while(temp_p != NULL) - { - template->name = board_names[temp_p->board_name_index]; - p = aic7xxx_alloc(template, temp_p); - if (p != NULL) - { - p->instance = found - left; - if (aic7xxx_register(template, p, (--left)) == 0) - { - found--; - aic7xxx_free(p); - scsi_unregister(p->host); - } - } - current_p = temp_p; - temp_p = (struct aic7xxx_host *)temp_p->next; - kfree(current_p); - } - temp_p = pci; - while(temp_p != NULL) - { - template->name = board_names[temp_p->board_name_index]; - p = aic7xxx_alloc(template, temp_p); - if (p != NULL) - { - p->instance = found - left; - if (aic7xxx_register(template, p, (--left)) == 0) - { - found--; - aic7xxx_free(p); - scsi_unregister(p->host); - } - } - current_p = temp_p; - temp_p = (struct aic7xxx_host *)temp_p->next; - kfree(current_p); - } - temp_p = vlb_disab; - while(temp_p != NULL) { - template->name = board_names[temp_p->board_name_index]; - p = aic7xxx_alloc(template, temp_p); - if (p != NULL) + int i; + + left = found; + for (i=0; iinstance = found - left; - if (aic7xxx_register(template, p, (--left)) == 0) + temp_p = sort_list[i]; + while(temp_p != NULL) { - found--; - aic7xxx_free(p); - scsi_unregister(p->host); + template->name = board_names[temp_p->board_name_index]; + p = aic7xxx_alloc(template, temp_p); + if (p != NULL) + { + p->instance = found - left; + if (aic7xxx_register(template, p, (--left)) == 0) + { + found--; + aic7xxx_release(p->host); + scsi_unregister(p->host); + } + else if (aic7xxx_dump_card) + { + pause_sequencer(p); + aic7xxx_print_card(p); + aic7xxx_print_scratch_ram(p); + unpause_sequencer(p, TRUE); + } + } + current_p = temp_p; + temp_p = (struct aic7xxx_host *)temp_p->next; + kfree(current_p); } } - current_p = temp_p; - temp_p = (struct aic7xxx_host *)temp_p->next; - kfree(current_p); } } return (found); } +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS + /*+F************************************************************************* * Function: * aic7xxx_negotiation_complete @@ -8165,7 +9551,7 @@ static void aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) { - memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer)); + return; } /*+F************************************************************************* @@ -8205,10 +9591,19 @@ cmd->underflow = 0; cmd->cmd_len = 6; } + /* + * Before sending this thing out, we also amke the cmd->next pointer + * point to the real command so we can stuff any possible SENSE data + * intp the real command instead of this fake command. This has to be + * done each time the command is built, not just the first time, hence + * it's outside of the above if()... + */ + p->dev_wdtr_cmnd[tindex]->next = old_cmd; aic7xxx_queue(p->dev_wdtr_cmnd[tindex], aic7xxx_negotiation_complete); } - else if ( (p->needsdtr & (1<sdtr_pending & (1<needsdtr & (1<sdtr_pending & (1<wdtr_pending & (1<dev_sdtr_cmnd[tindex] == NULL) { @@ -8232,11 +9627,45 @@ cmd->underflow = 0; cmd->cmd_len = 6; } + /* + * Before sending this thing out, we also amke the cmd->next pointer + * point to the real command so we can stuff any possible SENSE data + * intp the real command instead of this fake command. This has to be + * done each time the command is built, not just the first time, hence + * it's outside of the above if()... + */ + p->dev_sdtr_cmnd[tindex]->next = old_cmd; aic7xxx_queue(p->dev_sdtr_cmnd[tindex], aic7xxx_negotiation_complete); } } +#endif + +#ifdef AIC7XXX_VERBOSE_DEBUGGING +/*+F************************************************************************* + * Function: + * aic7xxx_print_scb + * + * Description: + * Dump the byte codes for an about to be sent SCB. + *-F*************************************************************************/ +static void +aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) +{ + int i; + unsigned char *x; + + x = (unsigned char *)&scb->hscb->control; + + for(i=0; i<32; i++) + { + printk("%02x ", x[i]); + } + printk("\n"); +} +#endif + /*+F************************************************************************* * Function: * aic7xxx_buildscb @@ -8290,12 +9719,13 @@ } if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED) { +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) ) { if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)]) { p->wdtr_pending |= mask; - scb->flags |= SCB_MSGOUT_WDTR_16BIT; + scb->flags |= SCB_MSGOUT_WDTR; hscb->control &= DISCENB; hscb->control |= MK_MESSAGE; scb->tag_action = 0; @@ -8305,7 +9735,8 @@ aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd)); } } - if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) ) + else if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) && + !(p->wdtr_pending & mask) ) { if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)]) { @@ -8320,6 +9751,36 @@ aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd)); } } +#else + if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) && + !(p->sdtr_pending & mask) && (cmd->lun == 0) ) + { + p->wdtr_pending |= mask; + scb->flags |= SCB_MSGOUT_WDTR; + hscb->control &= DISCENB; + hscb->control |= MK_MESSAGE; + scb->tag_action = 0; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Building WDTR command.\n", p->host_no, + CTL_OF_CMD(cmd)); +#endif + } + else if ( (p->needsdtr & mask) && !(p->wdtr_pending & mask) && + !(p->sdtr_pending & mask) && (cmd->lun == 0) ) + { + p->sdtr_pending |= mask; + scb->flags |= SCB_MSGOUT_SDTR; + hscb->control &= DISCENB; + hscb->control |= MK_MESSAGE; + scb->tag_action = 0; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Building SDTR command.\n", p->host_no, + CTL_OF_CMD(cmd)); +#endif + } +#endif } hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) | ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07); @@ -8352,19 +9813,26 @@ sg = (struct scatterlist *)cmd->request_buffer; scb->sg_length = 0; + /* + * Copy the segments into the SG array. NOTE!!! - We used to + * have the first entry both in the data_pointer area and the first + * SG element. That has changed somewhat. We still have the first + * entry in both places, but now we download the address of + * scb->sg_list[1] instead of 0 to the sg pointer in the hscb. + */ for (i = 0; i < cmd->use_sg; i++) { scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address)); scb->sg_list[i].length = cpu_to_le32(sg[i].length); scb->sg_length += sg[i].length; } - hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(scb->sg_list)); - hscb->SG_segment_count = cmd->use_sg; - scb->sg_count = cmd->use_sg; - /* Copy the first SG into the data pointer area. */ hscb->data_pointer = scb->sg_list[0].address; hscb->data_count = scb->sg_list[0].length; + scb->sg_count = cmd->use_sg; + hscb->SG_segment_count = cmd->use_sg; + hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1])); + } else { @@ -8389,6 +9857,12 @@ hscb->data_pointer = 0; } } +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if((cmd->cmnd[0] == TEST_UNIT_READY) && (aic7xxx_verbose & VERBOSE_PROBE2)) + { + aic7xxx_print_scb(p, scb); + } +#endif } /*+F************************************************************************* @@ -8403,7 +9877,9 @@ { struct aic7xxx_host *p; struct aic7xxx_scb *scb; +#ifdef AIC7XXX_VERBOSE_DEBUGGING int tindex = TARGET_INDEX(cmd); +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) unsigned long cpu_flags = 0; #endif @@ -8412,18 +9888,22 @@ /* * Check to see if channel was scanned. */ + +#ifdef AIC7XXX_VERBOSE_DEBUGGING if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0)) { - printk(INFO_LEAD "Scanning channel for devices.\n", - p->host_no, 0, -1, -1); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(INFO_LEAD "Scanning channel for devices.\n", + p->host_no, 0, -1, -1); p->flags |= AHC_A_SCANNED; } else { if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1)) { - printk(INFO_LEAD "Scanning channel for devices.\n", - p->host_no, 1, -1, -1); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(INFO_LEAD "Scanning channel for devices.\n", + p->host_no, 1, -1, -1); p->flags |= AHC_B_SCANNED; } } @@ -8437,12 +9917,25 @@ if ( p->dev_active_cmds[tindex] > 220 ) p->dev_active_cmds[tindex] = 0; } - DRIVER_LOCK - scb = aic7xxx_allocate_scb(p, FALSE); - DRIVER_UNLOCK +#endif + + scb = scbq_remove_head(&p->scb_data->free_scbs); + if (scb == NULL) + { + DRIVER_LOCK + aic7xxx_allocate_scb(p); + DRIVER_UNLOCK + scb = scbq_remove_head(&p->scb_data->free_scbs); + } if (scb == NULL) { - panic("(scsi%d) aic7xxx_queue:Couldn't get a free SCB.\n", p->host_no); + printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no, + CTL_OF_CMD(cmd)); + cmd->result = (DID_BUS_BUSY << 16); + DRIVER_LOCK + aic7xxx_queue_cmd_complete(p, cmd); + DRIVER_UNLOCK + return 0; } else { @@ -8461,23 +9954,16 @@ * the SCB to the sequencer and watch the fun begin. */ cmd->scsi_done = fn; + cmd->result = DID_OK; + memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); aic7xxx_error(cmd) = DID_OK; aic7xxx_status(cmd) = 0; - cmd->result = 0; cmd->host_scribble = NULL; - memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer)); scb->flags |= SCB_ACTIVE | SCB_WAITINGQ; DRIVER_LOCK - if (p->delayed_scbs[tindex].head != NULL) - { - scbq_insert_tail(&p->delayed_scbs[tindex], scb); - } - else - { - scbq_insert_tail(&p->waiting_scbs, scb); - } + scbq_insert_tail(&p->waiting_scbs, scb); if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0) { aic7xxx_run_waiting_queues(p); @@ -8665,7 +10151,10 @@ printk(INFO_LEAD "Queueing device reset " "command.\n", p->host_no, CTL_OF_SCB(scb)); p->qinfifo[p->qinfifonext++] = scb->hscb->tag; - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); scb->flags |= SCB_QUEUED_ABORT; result = SCSI_RESET_PENDING; } @@ -8686,30 +10175,142 @@ * Abort the current SCSI command(s). *-F*************************************************************************/ void -aic7xxx_panic_abort(struct aic7xxx_host *p) +aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { - int i; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) + int i, mask, found, need_tag; + struct aic7xxx_scb *scb; + unsigned char qinpos, hscbp; + + found = FALSE; +#endif - printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION); + printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION, + UTS_RELEASE); printk("Controller type:\n %s\n", board_names[p->board_name_index]); + printk("p->flags=0x%x, p->chip=0x%x, p->features=0x%x, " + "sequencer %s paused\n", + p->flags, p->chip, p->features, + (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" ); + pause_sequencer(p); + disable_irq(p->irq); + aic7xxx_print_card(p); + aic7xxx_print_scratch_ram(p); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) for(i=0; idev_flags[i] & DEVICE_PRESENT) { - printk(INFO_LEAD "dev_flags=0x%x, WDTR:%s, SDTR:%s, q_depth=%d:%d\n", + mask = (0x01 << i); + printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c," + " q_depth=%d:%d:%d\n", p->host_no, 0, i, 0, p->dev_flags[i], - (p->needwdtr_copy & (1 << i)) ? "Yes" : "No", - (p->needsdtr_copy & (1 << i)) ? "Yes" : "No", + (p->wdtr_pending & mask) ? 'Y' : 'N', + (p->needwdtr & mask) ? 'Y' : 'N', + (p->needwdtr_copy & mask) ? 'Y' : 'N', + (p->sdtr_pending & mask) ? 'Y' : 'N', + (p->needsdtr & mask) ? 'Y' : 'N', + (p->needsdtr_copy & mask) ? 'Y' : 'N', + p->dev_active_cmds[i], p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]); + printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0, + aic_inb(p, TARG_SCSIRATE + i)); + if (p->features & AHC_ULTRA2) + printk(", targ_offset=%d", aic_inb(p, TARG_OFFSET + i)); + printk("\n"); } } - printk("SIMODE0=0x%x, SIMODE1=0x%x, SSTAT0=0x%x, SSTAT1=0x%x, INTSTAT=0x%x\n", - aic_inb(p, SIMODE0), aic_inb(p, SIMODE1), aic_inb(p, SSTAT0), - aic_inb(p, SSTAT1), aic_inb(p, INTSTAT) ); - printk("p->flags=0x%x, p->type=0x%x, sequencer %s paused\n", - p->flags, p->type, - (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" ); - panic("Stopping to debug\n"); + /* + * Search for this command and see if we can't track it down, it's the + * one causing the timeout. Print out this command first, then all other + * active commands afterwords. + */ + need_tag = -1; + if ( cmd ) + { + scb = p->scb_data->scb_array[aic7xxx_position(cmd)]; + if ( (scb->flags & SCB_ACTIVE) && (scb->cmd == cmd) ) + { + printk("Timed out command is scb #%d:\n", scb->hscb->tag); + printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, + scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, + (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); + need_tag = scb->hscb->tag; + if (scb->flags & SCB_WAITINGQ) found=TRUE; + } + } + printk("QINFIFO: (TAG) "); + qinpos = aic_inb(p, QINPOS); + while ( qinpos != p->qinfifonext ) + { + if (p->qinfifo[qinpos] == need_tag) + found=TRUE; + printk("%d ", p->qinfifo[qinpos++]); + } + printk("\n"); + printk("Current SCB: (SCBPTR/TAG/CONTROL) %d/%d/0x%x\n", aic_inb(p, SCBPTR), + aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL) ); + if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; + printk("WAITING_SCBS: (SCBPTR/TAG/CONTROL) %d->", + hscbp = aic_inb(p, WAITING_SCBH)); + while (hscbp != SCB_LIST_NULL) + { + aic_outb(p, hscbp, SCBPTR); + printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); + hscbp = aic_inb(p, SCB_NEXT); + if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; + } + printk("\n"); + printk("DISCONNECTED_SCBS: (SCBPTR/TAG/CONTROL) %d->", + hscbp = aic_inb(p, DISCONNECTED_SCBH)); + while (hscbp != SCB_LIST_NULL) + { + aic_outb(p, hscbp, SCBPTR); + printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); + hscbp = aic_inb(p, SCB_NEXT); + if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; + } + printk("\n"); + printk("FREE_SCBS: (SCBPTR/TAG/CONTROL) %d->", + hscbp = aic_inb(p, FREE_SCBH)); + while (hscbp != SCB_LIST_NULL) + { + aic_outb(p, hscbp, SCBPTR); + printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); + hscbp = aic_inb(p, SCB_NEXT); + } + printk("\n"); + + if (found == FALSE) + { + /* + * We haven't found the offending SCB yet, and it should be around + * somewhere, so go look for it in the cards SCBs. + */ + printk("SCBPTR CONTROL TAG PREV NEXT\n"); + for(i=0; iscb_data->maxhscbs; i++) + { + aic_outb(p, i, SCBPTR); + printk(" %3d %02x %02x %02x %02x\n", i, + aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG), + aic_inb(p, SCB_PREV), aic_inb(p, SCB_NEXT)); + } + } + + + for (i=0; i < p->scb_data->numscbs; i++) + { + scb = p->scb_data->scb_array[i]; + if ( (scb->flags & SCB_ACTIVE) && (scb->cmd != cmd) ) + { + printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, + scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, + (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); + } + } +#endif + sti(); + for(;;) barrier(); } /*+F************************************************************************* @@ -8743,7 +10344,7 @@ * code. */ if (aic7xxx_panic_on_abort) - aic7xxx_panic_abort(p); + aic7xxx_panic_abort(p, cmd); DRIVER_LOCK @@ -8798,7 +10399,7 @@ p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble; else cmd_prev->host_scribble = cmd_next->host_scribble; - cmd_next->done(cmd_next); + cmd_next->scsi_done(cmd_next); unpause_sequencer(p, FALSE); DRIVER_UNLOCK return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful @@ -8822,7 +10423,12 @@ * command held by the scb pointer and is a valid abort request. * Now, we just have to figure out what to do from here. Current plan is: * if we have already been here on this command, escalate to a reset - * if scb is on waiting list or QINFIFO, send it back as aborted + * if scb is on waiting list or QINFIFO, send it back as aborted, but + * we also need to be aware of the possibility that we could be using + * a faked negotiation command that is holding this command up, if + * so we need to take care of that command instead, which means we + * would then treat this one like it was sitting around disconnected + * instead. * if scb is on WAITING_SCB list in sequencer, free scb and send back * if scb is disconnected and not completed, abort with abort message * if scb is currently running, then it may be causing the bus to hang @@ -8902,18 +10508,55 @@ if ((found == 0) && (scb->flags & SCB_WAITINGQ)) { - int tindex = TARGET_INDEX(cmd); - - if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) - printk(INFO_LEAD "SCB found on waiting list and " - "aborted.\n", p->host_no, CTL_OF_SCB(scb)); - scbq_remove(&p->waiting_scbs, scb); - scbq_remove(&p->delayed_scbs[tindex], scb); - p->dev_active_cmds[tindex]++; - p->activescbs++; - scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE); - scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE; + int tindex = TARGET_INDEX(cmd); +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS + unsigned short mask; + + mask = (1 << tindex); + + if (p->wdtr_pending & mask) + { + if (p->dev_wdtr_cmnd[tindex]->next != cmd) + found = 1; + else + found = 0; + } + else if (p->sdtr_pending & mask) + { + if (p->dev_sdtr_cmnd[tindex]->next != cmd) + found = 1; + else + found = 0; + } + else + { found = 1; + } + if (found == 0) + { + /* + * OK..this means the command we are currently getting an abort + * for has an outstanding negotiation command in front of it. + * We don't really have a way to tie back into the negotiation + * commands, so we just send this back as pending, then it + * will get reset in 2 seconds. + */ + unpause_sequencer(p, TRUE); + scb->flags |= SCB_ABORT; + DRIVER_UNLOCK + return(SCSI_ABORT_PENDING); + } +#endif + if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) + printk(INFO_LEAD "SCB found on waiting list and " + "aborted.\n", p->host_no, CTL_OF_SCB(scb)); + scbq_remove(&p->waiting_scbs, scb); + scbq_remove(&p->delayed_scbs[tindex], scb); + p->dev_active_cmds[tindex]++; + p->activescbs++; + scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE); + scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE; + found = 1; } /* @@ -9006,7 +10649,10 @@ printk(INFO_LEAD "SCB disconnected. Queueing Abort" " SCB.\n", p->host_no, CTL_OF_SCB(scb)); p->qinfifo[p->qinfifonext++] = scb->hscb->tag; - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); } if (found) { @@ -9083,7 +10729,7 @@ * code. */ if (aic7xxx_panic_on_abort) - aic7xxx_panic_abort(p); + aic7xxx_panic_abort(p, cmd); DRIVER_LOCK @@ -9203,7 +10849,7 @@ "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd)); action = BUS_RESET; } - if ( (action & BUS_RESET) && !(p->type & AHC_TWIN) ) + if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) ) { action = HOST_RESET; } @@ -9270,16 +10916,11 @@ p->reset_start = jiffies; p->flags |= AHC_IN_RESET; aic7xxx_reset_channel(p, cmd->channel, TRUE); - if ( (p->type & AHC_TWIN) && (action & HOST_RESET) ) + if ( (p->features & AHC_TWIN) && (action & HOST_RESET) ) { aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE); restart_sequencer(p); } - if (scb == NULL) - { - cmd->result = DID_RESET << 16; - cmd->done(cmd); - } p->last_reset = jiffies; if (action != HOST_RESET) result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET; @@ -9391,6 +11032,207 @@ aic7xxx_free(p); return(0); } + +/*+F************************************************************************* + * Function: + * aic7xxx_print_card + * + * Description: + * Print out all of the control registers on the card + * + * NOTE: This function is not yet safe for use on the VLB and EISA + * controllers, so it isn't used on those controllers at all. + *-F*************************************************************************/ +static void +aic7xxx_print_card(struct aic7xxx_host *p) +{ + int i, j, k, chip; + static struct register_ranges { + int num_ranges; + int range_val[32]; + } cards_ds[] = { + { 0, {0,} }, /* none */ + {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} }, + { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/ + 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/ + 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/ + 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f, + 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc, + 0xfe, 0xff} }, + {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, + 0x9f, 0x9f, 0xe0, 0xf1} }, + {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/ + 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f, + 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc, + 0xfe, 0xff} }, + }; +#ifdef CONFIG_PCI + static struct register_ranges cards_ns[] = { + { 0, {0,} }, /* none */ + { 0, {0,} }, /* 7771 */ + { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, + 0x3c, 0x41, 0x43, 0x47} }, + { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, + 0x3c, 0x41, 0x43, 0x47} }, + { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x33, 0x3c, 0x41} }, + { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47} }, + { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} }, + { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47, + 0xdc, 0xe3} }, + { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, + 0xff, 0xff} } + }; +#endif + chip = p->chip & AHC_CHIPID_MASK; + /* + * Let's run through the PCI space first.... + */ + printk("%s at ", + board_names[p->board_name_index]); + switch(p->chip & ~AHC_CHIPID_MASK) + { + case AHC_VL: + printk("VLB Slot %d.\n", p->pci_device_fn); + break; + case AHC_EISA: + printk("EISA Slot %d.\n", p->pci_device_fn); + break; + case AHC_PCI: + default: + printk("PCI %d/%d.\n", PCI_SLOT(p->pci_device_fn), + PCI_FUNC(p->pci_device_fn)); + break; + } + +#ifdef CONFIG_PCI + { + unsigned char temp; + + printk("PCI Dump:\n"); + k=0; + for(i=0; i KERNEL_VERSION(2,1,92) + pci_read_config_byte(p->pdev, j, &temp); +#else + pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, j, &temp); +#endif + printk("%02x:%02x ", j, temp); + if(++k == 13) + { + printk("\n"); + k = 0; + } + } + } + } + if(k != 0) + printk("\n"); +#endif /* CONFIG_PCI */ + + /* + * Now the registers on the card.... + */ + printk("Card Dump:\n"); + k = 0; + for(i=0; iflags & AHC_SEEPROM_FOUND) + { + unsigned short *sc1; + sc1 = (unsigned short *)&p->sc; + + printk("SEEPROM dump.\n"); + for(i=1; i<=32; i++) + { + printk("0x%04x", sc1[i-1]); + if ( (i % 8) == 0 ) + printk("\n"); + else + printk(" "); + } + } + + /* + * If this was an Ultra2 controller, then we just hosed the card in terms + * of the QUEUE REGS. This function is only called at init time or by + * the panic_abort function, so it's safe to assume a generic init time + * setting here + */ + + if(p->features & AHC_QUEUE_REGS) + { + aic_outb(p, 0, SDSCB_QOFF); + aic_outb(p, 0, SNSCB_QOFF); + aic_outb(p, 0, HNSCB_QOFF); + } + +} + +/*+F************************************************************************* + * Function: + * aic7xxx_print_scratch_ram + * + * Description: + * Print out the scratch RAM values on the card. + *-F*************************************************************************/ +static void +aic7xxx_print_scratch_ram(struct aic7xxx_host *p) +{ + int i, k; + + k = 0; + printk("Scratch RAM:\n"); + for(i = SRAM_BASE; i < SEQCTL; i++) + { + printk("%02x:%02x ", i, aic_inb(p, i)); + if(++k == 13) + { + printk("\n"); + k=0; + } + } + if (p->features & AHC_MORE_SRAM) + { + for(i = TARG_OFFSET; i < 0x80; i++) + { + printk("%02x:%02x ", i, aic_inb(p, i)); + if(++k == 13) + { + printk("\n"); + k=0; + } + } + } + printk("\n"); +} + #include "aic7xxx_proc.c" diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c --- v2.1.124/linux/drivers/scsi/aic7xxx_proc.c Sun Jun 7 11:16:34 1998 +++ linux/drivers/scsi/aic7xxx_proc.c Thu Oct 8 08:07:34 1998 @@ -85,10 +85,8 @@ struct aic7xxx_host *p; int size = 0; unsigned char i; -#ifdef AIC7XXX_PROC_STATS struct aic7xxx_xferstats *sp; unsigned char target, lun; -#endif HBAptr = NULL; @@ -130,16 +128,18 @@ */ size = 4096; -#ifdef AIC7XXX_PROC_STATS for (target = 0; target < MAX_TARGETS; target++) { for (lun = 0; lun < MAX_LUNS; lun++) { if (p->stats[target][lun].xfers != 0) +#ifdef AIC7XXX_PROC_STATS size += 512; +#else + size += 256; +#endif } } -#endif if (aic7xxx_buffer_size != size) { if (aic7xxx_buffer != NULL) @@ -195,9 +195,11 @@ if (p->flags & (AHC_CHNLB|AHC_CHNLC)) channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C"; } - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) wide = "Wide "; - if (p->type & AHC_ULTRA) + if (p->features & AHC_ULTRA2) + ultra = "Ultra2-LVD/SE "; + else if (p->features & AHC_ULTRA) ultra = "Ultra "; size += sprintf(BLS, " %s%sController%s\n", ultra, wide, channel); @@ -210,25 +212,26 @@ { size += sprintf(BLS, " PCI MMAPed I/O Base: 0x%lx\n", p->mbase); } - if( !(p->type & AHC_AIC78x0) ) + if( (p->chip & (AHC_VL | AHC_EISA)) ) { size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address); - size += sprintf(BLS, " %s\n", - (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled"); - } - else - { - size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n", - (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled"); } + size += sprintf(BLS, " Adapter SEEPROM Config: %s\n", + (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." : + ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." : + "SEEPROM not found, using leftover BIOS values.") ); + size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n", + (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled"); size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); size += sprintf(BLS, " SCBs: Active %d, Max Active %d,\n", p->activescbs, p->max_activescbs); size += sprintf(BLS, " Allocated %d, HW %d, " "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs); + if (p->flags & AHC_EXTERNAL_SRAM) + size += sprintf(BLS, " Using External SCB SRAM\n"); size += sprintf(BLS, " Interrupts: %ld", p->isr_count); - if (p->type & AHC_AIC7770) + if (p->chip & AHC_EISA) { size += sprintf(BLS, " %s\n", (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)"); @@ -244,7 +247,7 @@ size += sprintf(BLS, " Extended Translation: %sabled\n", (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis"); size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable); - if (p->type & AHC_ULTRA) + if (p->features & (AHC_ULTRA | AHC_ULTRA2)) { size += sprintf(BLS, " Ultra Enable Flags: 0x%04x\n", p->ultraenb); } @@ -268,7 +271,6 @@ size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]); size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]); -#ifdef AIC7XXX_PROC_STATS size += sprintf(BLS, "\n"); size += sprintf(BLS, "Statistics:\n"); for (target = 0; target < MAX_TARGETS; target++) @@ -280,7 +282,7 @@ { continue; } - if (p->type & AHC_TWIN) + if (p->features & AHC_TWIN) { size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", p->host_no, (target >> 3), (target & 0x7), lun); @@ -290,10 +292,50 @@ size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", p->host_no, 0, target, lun); } - size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n", + size += sprintf(BLS, " Device using %s/%s\n", + (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ? + "Wide" : "Narrow", + (p->transinfo[target].cur_offset != 0) ? + "Sync transfers at" : "Async transfers." ); + if (p->transinfo[target].cur_offset != 0) + { + struct aic7xxx_syncrate *sync_rate; + int period = p->transinfo[target].cur_period; + int rate = (p->transinfo[target].cur_width == + MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0; + + sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2); + if (sync_rate != NULL) + { + size += sprintf(BLS, " %s MByte/sec, offset %d\n", + sync_rate->rate[rate], + p->transinfo[target].cur_offset ); + } + else + { + size += sprintf(BLS, " 3.3 MByte/sec, offset %d\n", + p->transinfo[target].cur_offset ); + } + } + size += sprintf(BLS, " Device Negotiation Settings\n"); + size += sprintf(BLS, " Period Offset Bus Width\n"); + size += sprintf(BLS, "User %03d %03d %d\n", + p->transinfo[target].user_period, + p->transinfo[target].user_offset, + p->transinfo[target].user_width); + size += sprintf(BLS, "Goal %03d %03d %d\n", + p->transinfo[target].goal_period, + p->transinfo[target].goal_offset, + p->transinfo[target].goal_width); + size += sprintf(BLS, "Current %03d %03d %d\n", + p->transinfo[target].cur_period, + p->transinfo[target].cur_offset, + p->transinfo[target].cur_width); + size += sprintf(BLS, " Total transfers %ld (%ld read;%ld written)\n", sp->xfers, sp->r_total, sp->w_total); - size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n", + size += sprintf(BLS, " blks(512) rd=%ld; blks(512) wr=%ld\n", sp->r_total512, sp->w_total512); +#ifdef AIC7XXX_PROC_STATS size += sprintf(BLS, "%s\n", HDRB); size += sprintf(BLS, " Reads:"); for (i = 0; i < NUMBER(sp->r_bins); i++) @@ -306,10 +348,10 @@ { size += sprintf(BLS, "%6ld ", sp->w_bins[i]); } +#endif /* AIC7XXX_PROC_STATS */ size += sprintf(BLS, "\n\n"); } } -#endif /* AIC7XXX_PROC_STATS */ if (size >= aic7xxx_buffer_size) { diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h --- v2.1.124/linux/drivers/scsi/aic7xxx_reg.h Tue Apr 14 14:29:21 1998 +++ linux/drivers/scsi/aic7xxx_reg.h Thu Oct 8 08:07:34 1998 @@ -49,11 +49,13 @@ #define SCSIRATE 0x04 #define WIDEXFER 0x80 +#define SXFR_ULTRA2 0x7f #define SXFR 0x70 #define SOFS 0x0f #define SCSIID 0x05 -#define OID 0x0f +#define SCSIOFFSET 0x05 +#define SOFS_ULTRA2 0x7f #define SCSIDATL 0x06 @@ -73,6 +75,7 @@ #define SELDO 0x40 #define SELDI 0x20 #define SELINGO 0x10 +#define IOERR 0x08 #define SWRAP 0x08 #define SDONE 0x04 #define SPIORDY 0x02 @@ -100,20 +103,20 @@ #define SSTAT2 0x0d #define OVERRUN 0x80 #define SFCNT 0x1f +#define EXP_ACTIVE 0x10 #define SSTAT3 0x0e #define SCSICNT 0xf0 #define OFFCNT 0x0f -#define SCSITEST 0x0f -#define RQAKCNT 0x04 -#define CNTRTEST 0x02 -#define CMODE 0x01 +#define SCSIID_ULTRA2 0x0f +#define OID 0x0f #define SIMODE0 0x10 #define ENSELDO 0x40 #define ENSELDI 0x20 #define ENSELINGO 0x10 +#define ENIOERR 0x08 #define ENSWRAP 0x08 #define ENSDONE 0x04 #define ENSPIORDY 0x02 @@ -161,10 +164,15 @@ #define BRDDAT7 0x80 #define BRDDAT6 0x40 #define BRDDAT5 0x20 +#define BRDDAT4 0x10 #define BRDSTB 0x10 #define BRDCS 0x08 +#define BRDDAT3 0x08 +#define BRDDAT2 0x04 #define BRDRW 0x04 +#define BRDRW_ULTRA2 0x02 #define BRDCTL1 0x02 +#define BRDSTB_ULTRA2 0x01 #define BRDCTL0 0x01 #define SEECTL 0x1e @@ -181,11 +189,14 @@ #define DIAGLEDEN 0x80 #define DIAGLEDON 0x40 #define AUTOFLUSHDIS 0x20 +#define ENAB40 0x08 +#define ENAB20 0x04 #define SELWIDE 0x02 +#define XCVR 0x01 #define SRAM_BASE 0x20 -#define TARG_SCRATCH 0x20 +#define TARG_SCSIRATE 0x20 #define ULTRA_ENB 0x30 @@ -194,6 +205,7 @@ #define MSG_OUT 0x34 #define DMAPARAMS 0x35 +#define PRELOADEN 0x80 #define WIDEODD 0x40 #define SCSIEN 0x20 #define SDMAENACK 0x10 @@ -258,7 +270,12 @@ #define SEND_REJ 0x20 #define MSGOUT_PHASEMIS 0x10 -#define LAST_MSG 0x52 +#define ARG_2 0x52 +#define RETURN_2 0x52 + +#define LAST_MSG 0x53 + +#define PREFETCH_CNT 0x54 #define SCSICONF 0x5a #define TERM_ENB 0x80 @@ -314,10 +331,18 @@ #define STACK 0x6f +#define TARG_OFFSET 0x70 + #define BCTL 0x84 #define ACE 0x08 #define ENABLE 0x01 +#define DSCOMMAND0 0x84 +#define INTSCBRAMSEL 0x08 +#define RAMPS 0x04 +#define USCBSIZE32 0x02 +#define CIOPARCKEN 0x01 + #define DSCOMMAND 0x84 #define CACHETHEN 0x80 #define DPARCKEN 0x40 @@ -329,12 +354,12 @@ #define BON 0x0f #define BUSSPD 0x86 -#define DFTHRSH_100 0xc0 #define DFTHRSH 0xc0 #define STBOFF 0x38 #define STBON 0x07 #define DSPCISTATUS 0x86 +#define DFTHRSH_100 0xc0 #define HCNTRL 0x87 #define POWRDN 0x40 @@ -381,6 +406,7 @@ #define CLRSEQINT 0x01 #define ERROR 0x92 +#define CIOPARERR 0x80 #define PCIERRSTAT 0x40 #define MPARERR 0x20 #define DPARERR 0x10 @@ -392,6 +418,7 @@ #define DFCNTRL 0x93 #define DFSTATUS 0x94 +#define PRELOAD_AVAIL 0x80 #define DWORDEMP 0x20 #define MREQPEND 0x10 #define HDONE 0x08 @@ -413,6 +440,8 @@ #define QOUTCNT 0x9e +#define SFUNCT 0x9f + #define SCB_CONTROL 0xa0 #define MK_MESSAGE 0x80 #define DISCENB 0x40 @@ -464,25 +493,95 @@ #define ADSEL 0x1e #define DI_2840 0x01 +#define CCHADDR 0xe0 + +#define CCHCNT 0xe8 + +#define CCSGRAM 0xe9 + +#define CCSGADDR 0xea + +#define CCSGCTL 0xeb +#define CCSGDONE 0x80 +#define CCSGEN 0x08 +#define FLAG 0x02 +#define CCSGRESET 0x01 + +#define CCSCBRAM 0xec + +#define CCSCBADDR 0xed + +#define CCSCBCTL 0xee +#define CCSCBDONE 0x80 +#define ARRDONE 0x40 +#define CCARREN 0x10 +#define CCSCBEN 0x08 +#define CCSCBDIR 0x04 +#define CCSCBRESET 0x01 + +#define CCSCBCNT 0xef + +#define CCSCBPTR 0xf1 + +#define HNSCB_QOFF 0xf4 + +#define SNSCB_QOFF 0xf6 + +#define SDSCB_QOFF 0xf8 + +#define QOFF_CTLSTA 0xfa +#define SCB_AVAIL 0x40 +#define SNSCB_ROLLOVER 0x20 +#define SDSCB_ROLLOVER 0x10 +#define SCB_QSIZE 0x07 +#define SCB_QSIZE_256 0x06 + +#define DFF_THRSH 0xfb +#define WR_DFTHRSH 0x70 +#define WR_DFTHRSH_MAX 0x70 +#define WR_DFTHRSH_90 0x60 +#define WR_DFTHRSH_85 0x50 +#define WR_DFTHRSH_75 0x40 +#define WR_DFTHRSH_63 0x30 +#define WR_DFTHRSH_50 0x20 +#define WR_DFTHRSH_25 0x10 +#define RD_DFTHRSH_MAX 0x07 +#define RD_DFTHRSH 0x07 +#define RD_DFTHRSH_90 0x06 +#define RD_DFTHRSH_85 0x05 +#define RD_DFTHRSH_75 0x04 +#define RD_DFTHRSH_63 0x03 +#define RD_DFTHRSH_50 0x02 +#define RD_DFTHRSH_25 0x01 +#define WR_DFTHRSH_MIN 0x00 +#define RD_DFTHRSH_MIN 0x00 + +#define SG_CACHEPTR 0xfc +#define SG_USER_DATA 0xfc +#define LAST_SEG 0x02 +#define LAST_SEG_DONE 0x01 + -#define CMD_GROUP_CODE_SHIFT 0x05 -#define BUS_8_BIT 0x00 -#define QOUTFIFO_OFFSET 0x01 #define CMD_GROUP2_BYTE_DELTA 0xfa #define MAX_OFFSET_8BIT 0x0f #define BUS_16_BIT 0x01 #define QINFIFO_OFFSET 0x02 #define CMD_GROUP5_BYTE_DELTA 0x0b +#define CMD_GROUP_CODE_SHIFT 0x05 +#define MAX_OFFSET_ULTRA2 0x7f #define MAX_OFFSET_16BIT 0x08 +#define BUS_8_BIT 0x00 +#define QOUTFIFO_OFFSET 0x01 #define UNTAGGEDSCB_OFFSET 0x00 +#define CCSGRAM_MAXSEGS 0x10 #define SCB_LIST_NULL 0xff #define SG_SIZEOF 0x08 #define CMD_GROUP4_BYTE_DELTA 0x04 #define CMD_GROUP0_BYTE_DELTA 0xfc #define HOST_MSG 0xff #define BUS_32_BIT 0x02 +#define CCSGADDR_MAX 0x80 /* Downloaded Constant Definitions */ -#define TMODE_NUMCMDS 0x01 -#define QCNTMASK 0x00 +#define TMODE_NUMCMDS 0x00 diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_seq.c --- v2.1.124/linux/drivers/scsi/aic7xxx_seq.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx_seq.c Thu Oct 8 08:07:34 1998 @@ -0,0 +1,769 @@ +/* + * DO NOT EDIT - This file is automatically generated. + */ +static unsigned char seqprog[] = { + 0xff, 0x6a, 0x06, 0x08, + 0x32, 0x6a, 0x00, 0x00, + 0x12, 0x6a, 0x00, 0x00, + 0xff, 0x6a, 0xd6, 0x09, + 0xff, 0x6a, 0xdc, 0x09, + 0x00, 0x65, 0x38, 0x59, + 0xf7, 0x01, 0x02, 0x08, + 0xff, 0x4e, 0xc8, 0x08, + 0xbf, 0x60, 0xc0, 0x08, + 0x60, 0x0b, 0x7c, 0x68, + 0x40, 0x00, 0x0e, 0x68, + 0x08, 0x1f, 0x3e, 0x10, + 0x60, 0x0b, 0x7c, 0x68, + 0x40, 0x00, 0x0e, 0x68, + 0x08, 0x1f, 0x3e, 0x10, + 0xff, 0x3e, 0x3e, 0x60, + 0x40, 0xfa, 0x10, 0x78, + 0xff, 0xf6, 0xd4, 0x08, + 0x01, 0x4e, 0x9c, 0x18, + 0x40, 0x60, 0xc0, 0x00, + 0x00, 0x4d, 0x10, 0x70, + 0x01, 0x4e, 0x9c, 0x18, + 0xbf, 0x60, 0xc0, 0x08, + 0x00, 0x6a, 0x72, 0x5c, + 0xff, 0x4e, 0xc8, 0x18, + 0x02, 0x6a, 0x88, 0x5b, + 0xff, 0x52, 0x20, 0x09, + 0x0d, 0x6a, 0x6a, 0x00, + 0x00, 0x52, 0xfe, 0x5b, + 0xff, 0x3e, 0x74, 0x09, + 0xff, 0x90, 0x7c, 0x08, + 0xff, 0x3e, 0x20, 0x09, + 0x00, 0x65, 0x44, 0x58, + 0x00, 0x65, 0x0e, 0x40, + 0xf7, 0x1f, 0xca, 0x08, + 0x08, 0xa1, 0xc8, 0x08, + 0x00, 0x65, 0xca, 0x00, + 0xff, 0x65, 0x3e, 0x08, + 0xf0, 0xa1, 0xc8, 0x08, + 0x0f, 0x0f, 0x1e, 0x08, + 0x00, 0x0f, 0x1e, 0x00, + 0xf0, 0xa1, 0xc8, 0x08, + 0x0f, 0x05, 0x0a, 0x08, + 0x00, 0x05, 0x0a, 0x00, + 0x5a, 0x6a, 0x00, 0x04, + 0x12, 0x65, 0xc8, 0x00, + 0x00, 0x01, 0x02, 0x00, + 0x31, 0x6a, 0xca, 0x00, + 0x80, 0x37, 0x64, 0x68, + 0xff, 0x65, 0xca, 0x18, + 0xff, 0x37, 0xdc, 0x08, + 0xff, 0x6e, 0xc8, 0x08, + 0x00, 0x6c, 0x6c, 0x78, + 0x20, 0x01, 0x02, 0x00, + 0x4c, 0x37, 0xc8, 0x28, + 0x08, 0x1f, 0x74, 0x78, + 0x08, 0x37, 0x6e, 0x00, + 0x08, 0x64, 0xc8, 0x00, + 0x70, 0x64, 0xca, 0x18, + 0xff, 0x6c, 0x0a, 0x08, + 0x20, 0x64, 0xca, 0x18, + 0xff, 0x6c, 0x08, 0x0c, + 0x40, 0x0b, 0x04, 0x69, + 0x80, 0x0b, 0xf6, 0x78, + 0xa4, 0x6a, 0x06, 0x00, + 0x40, 0x6a, 0x16, 0x00, + 0x10, 0x03, 0xf2, 0x78, + 0xff, 0x50, 0xc8, 0x08, + 0x88, 0x6a, 0xcc, 0x00, + 0x49, 0x6a, 0xee, 0x5b, + 0x01, 0x6a, 0x26, 0x01, + 0xff, 0x6a, 0xca, 0x08, + 0x08, 0x01, 0x02, 0x00, + 0x02, 0x0b, 0x92, 0x78, + 0xf7, 0x01, 0x02, 0x08, + 0xff, 0x06, 0xcc, 0x08, + 0xff, 0x66, 0x32, 0x09, + 0x01, 0x65, 0xca, 0x18, + 0x80, 0x66, 0xa0, 0x78, + 0xff, 0x66, 0xa2, 0x08, + 0x10, 0x03, 0x90, 0x68, + 0xfc, 0x65, 0xc8, 0x18, + 0x00, 0x65, 0xa8, 0x48, + 0xff, 0x6a, 0x32, 0x01, + 0x01, 0x64, 0x18, 0x19, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x84, 0x6a, 0x06, 0x00, + 0x08, 0x01, 0x02, 0x00, + 0x02, 0x0b, 0xb2, 0x78, + 0xff, 0x06, 0xc8, 0x08, + 0xff, 0x64, 0x32, 0x09, + 0xff, 0x6a, 0xca, 0x08, + 0x5b, 0x64, 0xc8, 0x28, + 0x00, 0x62, 0xc4, 0x18, + 0xfc, 0x65, 0xca, 0x18, + 0xff, 0x6a, 0xd4, 0x08, + 0xfa, 0x65, 0xca, 0x18, + 0xff, 0x6a, 0xd4, 0x08, + 0x04, 0x65, 0xca, 0x18, + 0x0b, 0x65, 0xca, 0x18, + 0xff, 0x65, 0xc8, 0x08, + 0x00, 0x8c, 0x18, 0x19, + 0x02, 0x0b, 0xce, 0x78, + 0x01, 0x65, 0xd4, 0x60, + 0xf7, 0x01, 0x02, 0x08, + 0xff, 0x06, 0x32, 0x09, + 0xff, 0x65, 0xca, 0x18, + 0xff, 0x65, 0xce, 0x68, + 0x0a, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0x40, 0x51, 0xe6, 0x78, + 0xe4, 0x6a, 0x06, 0x00, + 0x08, 0x01, 0x02, 0x00, + 0x04, 0x6a, 0x18, 0x5b, + 0x01, 0x50, 0xa0, 0x18, + 0x00, 0x50, 0xec, 0xe0, + 0xff, 0x6a, 0xa0, 0x08, + 0xff, 0x6a, 0x3a, 0x01, + 0x02, 0x6a, 0x22, 0x01, + 0x40, 0x51, 0xf2, 0x68, + 0xff, 0x6a, 0x06, 0x08, + 0x00, 0x65, 0x0e, 0x40, + 0x20, 0x6a, 0x16, 0x00, + 0xf0, 0x19, 0x6e, 0x08, + 0x08, 0x6a, 0x18, 0x00, + 0x08, 0x11, 0x22, 0x00, + 0x08, 0x6a, 0x5a, 0x58, + 0x08, 0x6a, 0x68, 0x00, + 0x00, 0x65, 0x18, 0x41, + 0x12, 0x6a, 0x00, 0x00, + 0x40, 0x6a, 0x16, 0x00, + 0xff, 0x3e, 0x20, 0x09, + 0xff, 0xba, 0x7c, 0x08, + 0xff, 0xa1, 0x6e, 0x08, + 0x08, 0x6a, 0x18, 0x00, + 0x08, 0x11, 0x22, 0x00, + 0x08, 0x6a, 0x5a, 0x58, + 0x80, 0x6a, 0x68, 0x00, + 0x80, 0x36, 0x6c, 0x00, + 0x00, 0x65, 0xd2, 0x5b, + 0xff, 0x3d, 0xc8, 0x08, + 0xbf, 0x64, 0x48, 0x79, + 0x80, 0x64, 0xf0, 0x71, + 0xa0, 0x64, 0x0e, 0x72, + 0xc0, 0x64, 0x08, 0x72, + 0xe0, 0x64, 0x52, 0x72, + 0x01, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0xf7, 0x11, 0x22, 0x08, + 0x00, 0x65, 0x38, 0x59, + 0xff, 0x06, 0xd4, 0x08, + 0xf7, 0x01, 0x02, 0x08, + 0x09, 0x0c, 0x32, 0x79, + 0x08, 0x0c, 0x0e, 0x68, + 0x01, 0x6a, 0x22, 0x01, + 0xff, 0x6a, 0x26, 0x09, + 0xff, 0x6a, 0x08, 0x08, + 0xdf, 0x01, 0x02, 0x08, + 0x01, 0x6a, 0x7a, 0x00, + 0x03, 0x36, 0x6c, 0x0c, + 0x08, 0x6a, 0xcc, 0x00, + 0xa9, 0x6a, 0xe8, 0x5b, + 0x00, 0x65, 0x66, 0x41, + 0xa8, 0x6a, 0x6a, 0x00, + 0x79, 0x6a, 0x6a, 0x00, + 0x40, 0x3d, 0x50, 0x69, + 0x04, 0x35, 0x6a, 0x00, + 0x00, 0x65, 0x3a, 0x5b, + 0x80, 0x6a, 0xd4, 0x01, + 0x10, 0x36, 0x42, 0x69, + 0x10, 0x36, 0x6c, 0x00, + 0x07, 0xac, 0x10, 0x31, + 0x88, 0x6a, 0xcc, 0x00, + 0xac, 0x6a, 0xe0, 0x5b, + 0x00, 0x65, 0xda, 0x5b, + 0xff, 0xa3, 0x70, 0x08, + 0x39, 0x6a, 0xcc, 0x00, + 0xa4, 0x6a, 0xe6, 0x5b, + 0xff, 0x38, 0x74, 0x69, + 0x80, 0x02, 0x04, 0x00, + 0xe7, 0x35, 0x6a, 0x08, + 0x03, 0x69, 0x18, 0x31, + 0xff, 0x6a, 0x10, 0x00, + 0xff, 0x6a, 0x12, 0x00, + 0xff, 0x6a, 0x14, 0x00, + 0x01, 0x38, 0x7a, 0x61, + 0x02, 0xfc, 0xf8, 0x01, + 0xbf, 0x35, 0x6a, 0x08, + 0xff, 0x69, 0xca, 0x08, + 0xff, 0x35, 0x26, 0x09, + 0x04, 0x0b, 0x7e, 0x69, + 0x04, 0x0b, 0x8a, 0x69, + 0x10, 0x0c, 0x80, 0x79, + 0x04, 0x0b, 0x88, 0x69, + 0xff, 0x6a, 0xca, 0x08, + 0x00, 0x35, 0x22, 0x5b, + 0x80, 0x02, 0xd6, 0x69, + 0xff, 0x65, 0xc8, 0x79, + 0xff, 0x38, 0x70, 0x18, + 0xff, 0x38, 0xc8, 0x79, + 0x80, 0xea, 0xaa, 0x61, + 0xef, 0x38, 0xc8, 0x18, + 0x80, 0x6a, 0xc8, 0x00, + 0x00, 0x65, 0x9c, 0x49, + 0x33, 0x38, 0xc8, 0x28, + 0xff, 0x64, 0xd0, 0x09, + 0x04, 0x39, 0xc0, 0x31, + 0x09, 0x6a, 0xd6, 0x01, + 0x80, 0xeb, 0xa2, 0x79, + 0xf7, 0xeb, 0xd6, 0x09, + 0x08, 0xeb, 0xa6, 0x69, + 0x01, 0x6a, 0xd6, 0x01, + 0x08, 0xe9, 0x10, 0x31, + 0x88, 0x6a, 0xcc, 0x00, + 0x39, 0x6a, 0xe6, 0x5b, + 0x08, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x0d, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0x88, 0x6a, 0x54, 0x5c, + 0x00, 0x65, 0xda, 0x5b, + 0xff, 0x6a, 0xc8, 0x08, + 0x08, 0x39, 0x72, 0x18, + 0x00, 0x3a, 0x74, 0x20, + 0x10, 0x0c, 0x66, 0x79, + 0x80, 0x93, 0x26, 0x01, + 0x00, 0x65, 0xe0, 0x59, + 0xff, 0x08, 0x52, 0x09, + 0xff, 0x09, 0x54, 0x09, + 0xff, 0x0a, 0x56, 0x09, + 0xff, 0x38, 0x50, 0x09, + 0x12, 0x01, 0x02, 0x00, + 0x00, 0x65, 0x18, 0x41, + 0x00, 0x65, 0xe0, 0x59, + 0x12, 0x01, 0x02, 0x00, + 0x7f, 0x02, 0x04, 0x08, + 0xe1, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0x04, 0x93, 0xea, 0x69, + 0xdf, 0x93, 0x26, 0x09, + 0x20, 0x93, 0xe4, 0x69, + 0x02, 0x93, 0x26, 0x01, + 0x01, 0x94, 0xe6, 0x79, + 0xd7, 0x93, 0x26, 0x09, + 0x08, 0x93, 0xec, 0x69, + 0xff, 0x6a, 0xd4, 0x0c, + 0x00, 0x65, 0x3a, 0x5b, + 0x02, 0xfc, 0xf8, 0x01, + 0x05, 0xb4, 0x10, 0x31, + 0x02, 0x6a, 0x1a, 0x31, + 0x88, 0x6a, 0xcc, 0x00, + 0xb4, 0x6a, 0xe4, 0x5b, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x00, 0x65, 0xda, 0x5b, + 0x3d, 0x6a, 0x22, 0x5b, + 0xac, 0x6a, 0x22, 0x5b, + 0x00, 0x65, 0x18, 0x41, + 0x00, 0x65, 0x3a, 0x5b, + 0xff, 0x06, 0x44, 0x09, + 0x00, 0x65, 0x18, 0x41, + 0xff, 0x34, 0xca, 0x08, + 0x80, 0x65, 0x32, 0x62, + 0x0f, 0xa1, 0xca, 0x08, + 0x07, 0xa1, 0xca, 0x08, + 0x40, 0xa0, 0xc8, 0x08, + 0x00, 0x65, 0xca, 0x00, + 0x80, 0x65, 0xca, 0x00, + 0x80, 0xa0, 0x22, 0x7a, + 0xff, 0x65, 0x0c, 0x08, + 0x00, 0x65, 0x34, 0x42, + 0x20, 0xa0, 0x3a, 0x7a, + 0xff, 0x65, 0x0c, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xa0, 0x3d, 0x46, 0x62, + 0x23, 0xa0, 0x0c, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xa0, 0x3d, 0x46, 0x62, + 0x00, 0xb9, 0x3a, 0x42, + 0xff, 0x65, 0x3a, 0x62, + 0xa1, 0x6a, 0x22, 0x01, + 0xff, 0x6a, 0xd4, 0x08, + 0x10, 0x51, 0x46, 0x72, + 0x40, 0x6a, 0x18, 0x00, + 0xff, 0x65, 0x0c, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xa0, 0x3d, 0x46, 0x62, + 0x10, 0x3d, 0x06, 0x00, + 0x00, 0x65, 0x0e, 0x42, + 0x40, 0x6a, 0x18, 0x00, + 0xff, 0x34, 0xa6, 0x08, + 0x80, 0x34, 0x4e, 0x62, + 0x7f, 0xa0, 0x40, 0x09, + 0x08, 0x6a, 0x68, 0x00, + 0x00, 0x65, 0x18, 0x41, + 0x64, 0x6a, 0x12, 0x5b, + 0x80, 0x64, 0xbe, 0x6a, + 0x04, 0x64, 0xa4, 0x72, + 0x02, 0x64, 0xaa, 0x72, + 0x00, 0x6a, 0x6c, 0x72, + 0x03, 0x64, 0xba, 0x72, + 0x01, 0x64, 0xa0, 0x72, + 0x07, 0x64, 0x00, 0x73, + 0x08, 0x64, 0x68, 0x72, + 0x11, 0x6a, 0x22, 0x01, + 0x07, 0x6a, 0x04, 0x5b, + 0xff, 0x06, 0xd4, 0x08, + 0x00, 0x65, 0x18, 0x41, + 0xff, 0xa8, 0x70, 0x6a, + 0xff, 0xa2, 0x88, 0x7a, + 0x01, 0x6a, 0x6a, 0x00, + 0x00, 0xb9, 0xfe, 0x5b, + 0xff, 0xa2, 0x88, 0x7a, + 0x71, 0x6a, 0x22, 0x01, + 0xff, 0x6a, 0xd4, 0x08, + 0x40, 0x51, 0x88, 0x62, + 0x0d, 0x6a, 0x6a, 0x00, + 0x00, 0xb9, 0xfe, 0x5b, + 0xff, 0x3e, 0x74, 0x09, + 0xff, 0x90, 0x7c, 0x08, + 0x00, 0x65, 0x44, 0x58, + 0x00, 0x65, 0x2a, 0x41, + 0x20, 0xa0, 0x90, 0x6a, + 0xff, 0x37, 0xc8, 0x08, + 0x00, 0x6a, 0xa8, 0x5b, + 0xff, 0x6a, 0xbe, 0x5b, + 0xff, 0xf8, 0xc8, 0x08, + 0xff, 0x4f, 0xc8, 0x08, + 0x01, 0x6a, 0xa8, 0x5b, + 0x00, 0xb9, 0xbe, 0x5b, + 0x01, 0x4f, 0x9e, 0x18, + 0x02, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x6c, 0x5c, + 0x00, 0x65, 0x2a, 0x41, + 0x41, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0x04, 0xa0, 0x40, 0x01, + 0x00, 0x65, 0x84, 0x5c, + 0x00, 0x65, 0x2a, 0x41, + 0x10, 0x36, 0x68, 0x7a, + 0xff, 0x38, 0x46, 0x09, + 0xa4, 0x6a, 0xcc, 0x00, + 0x39, 0x6a, 0xe6, 0x5b, + 0xac, 0x6a, 0xcc, 0x00, + 0x14, 0x6a, 0xe6, 0x5b, + 0xa9, 0x6a, 0xe8, 0x5b, + 0x00, 0x65, 0x68, 0x42, + 0xef, 0x36, 0x6c, 0x08, + 0x00, 0x65, 0x68, 0x42, + 0x0f, 0x64, 0xc8, 0x08, + 0x07, 0x64, 0xc8, 0x08, + 0x00, 0x37, 0x6e, 0x00, + 0x00, 0x65, 0x78, 0x5b, + 0xff, 0x51, 0xce, 0x72, + 0x20, 0x36, 0xde, 0x7a, + 0x00, 0x90, 0x5c, 0x5b, + 0x00, 0x65, 0xe0, 0x42, + 0xff, 0x06, 0xd4, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xe0, 0x3d, 0xfa, 0x62, + 0x20, 0x12, 0xfa, 0x62, + 0x51, 0x6a, 0x08, 0x5b, + 0xff, 0x51, 0x20, 0x09, + 0x20, 0xa0, 0xfa, 0x7a, + 0x00, 0x90, 0x5c, 0x5b, + 0x00, 0x65, 0x56, 0x5b, + 0xff, 0x37, 0xc8, 0x08, + 0x00, 0xa1, 0xf2, 0x62, + 0x04, 0xa0, 0xf2, 0x7a, + 0xfb, 0xa0, 0x40, 0x09, + 0x80, 0x36, 0x6c, 0x00, + 0x80, 0xa0, 0x68, 0x7a, + 0x7f, 0xa0, 0x40, 0x09, + 0xff, 0x6a, 0x04, 0x5b, + 0x00, 0x65, 0x68, 0x42, + 0x04, 0xa0, 0xf8, 0x7a, + 0x00, 0x65, 0x84, 0x5c, + 0x00, 0x65, 0xfa, 0x42, + 0x00, 0x65, 0x6c, 0x5c, + 0x31, 0x6a, 0x22, 0x01, + 0x0c, 0x6a, 0x04, 0x5b, + 0x00, 0x65, 0x68, 0x42, + 0x61, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x68, 0x42, + 0x10, 0x3d, 0x06, 0x00, + 0xff, 0x65, 0x68, 0x0c, + 0xff, 0x06, 0xd4, 0x08, + 0x01, 0x0c, 0x0a, 0x7b, + 0x04, 0x0c, 0x0a, 0x6b, + 0xe0, 0x03, 0x7a, 0x08, + 0xe0, 0x3d, 0x1e, 0x63, + 0xff, 0x65, 0xcc, 0x08, + 0xff, 0x12, 0xda, 0x0c, + 0xff, 0x06, 0xd4, 0x0c, + 0xff, 0x65, 0x0c, 0x08, + 0x02, 0x0b, 0x1a, 0x7b, + 0xff, 0x6a, 0xd4, 0x0c, + 0xd1, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0xff, 0x65, 0x26, 0x09, + 0x01, 0x0b, 0x32, 0x6b, + 0x10, 0x0c, 0x24, 0x7b, + 0x04, 0x0b, 0x2c, 0x6b, + 0xff, 0x6a, 0xca, 0x08, + 0x04, 0x93, 0x30, 0x6b, + 0x01, 0x94, 0x2e, 0x7b, + 0x10, 0x94, 0x30, 0x6b, + 0xc7, 0x93, 0x26, 0x09, + 0xff, 0x99, 0xd4, 0x08, + 0x08, 0x93, 0x34, 0x6b, + 0xff, 0x6a, 0xd4, 0x0c, + 0x80, 0x36, 0x38, 0x6b, + 0x21, 0x6a, 0x22, 0x05, + 0xff, 0x65, 0x20, 0x09, + 0xff, 0x51, 0x46, 0x63, + 0xff, 0x37, 0xc8, 0x08, + 0xa1, 0x6a, 0x50, 0x43, + 0xff, 0x51, 0xc8, 0x08, + 0xb9, 0x6a, 0x50, 0x43, + 0xff, 0xba, 0x54, 0x73, + 0xff, 0xba, 0x20, 0x09, + 0xff, 0x65, 0xca, 0x18, + 0x00, 0x6c, 0x4a, 0x63, + 0xff, 0x90, 0xca, 0x0c, + 0xff, 0x6a, 0xca, 0x04, + 0x20, 0x36, 0x72, 0x7b, + 0x00, 0x90, 0x3e, 0x5b, + 0xff, 0x65, 0x72, 0x73, + 0xff, 0xba, 0x66, 0x73, + 0xff, 0xbb, 0xcc, 0x08, + 0xff, 0xba, 0x20, 0x09, + 0xff, 0x66, 0x76, 0x09, + 0xff, 0x65, 0x20, 0x09, + 0xff, 0xbb, 0x70, 0x73, + 0xff, 0xba, 0xcc, 0x08, + 0xff, 0xbb, 0x20, 0x09, + 0xff, 0x66, 0x74, 0x09, + 0xff, 0x65, 0x20, 0x0d, + 0xff, 0xba, 0x7e, 0x0c, + 0x00, 0x6a, 0x72, 0x5c, + 0x0d, 0x6a, 0x6a, 0x00, + 0x00, 0x51, 0xfe, 0x43, + 0xff, 0x3f, 0xcc, 0x73, + 0xff, 0x6a, 0xa2, 0x00, + 0x00, 0x3f, 0x3e, 0x5b, + 0xff, 0x65, 0xcc, 0x73, + 0x20, 0x36, 0x6c, 0x00, + 0x20, 0xa0, 0x86, 0x6b, + 0xff, 0xb9, 0xa2, 0x0c, + 0xff, 0x6a, 0xa2, 0x04, + 0xff, 0x65, 0xa4, 0x08, + 0xe0, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0xd0, 0x01, + 0x09, 0x6a, 0xd6, 0x01, + 0x80, 0xeb, 0x92, 0x7b, + 0x01, 0x6a, 0xd6, 0x01, + 0x01, 0xe9, 0xa4, 0x34, + 0x88, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x0d, 0x6a, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0xff, 0x99, 0xa4, 0x0c, + 0xff, 0x65, 0xa4, 0x08, + 0xe0, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0xd0, 0x01, + 0x01, 0x6a, 0xdc, 0x05, + 0x88, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x01, 0x6a, 0x26, 0x05, + 0x01, 0x65, 0xd8, 0x31, + 0x09, 0xee, 0xdc, 0x01, + 0x80, 0xee, 0xc2, 0x7b, + 0xff, 0x6a, 0xdc, 0x0d, + 0xff, 0x65, 0x32, 0x09, + 0x0a, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x44, + 0xff, 0x37, 0xc8, 0x08, + 0x00, 0x6a, 0x88, 0x5b, + 0xff, 0x52, 0xa2, 0x0c, + 0x01, 0x0c, 0xd2, 0x7b, + 0x04, 0x0c, 0xd2, 0x6b, + 0xe0, 0x03, 0x7a, 0x08, + 0xff, 0x3d, 0x06, 0x0c, + 0xff, 0x8c, 0x10, 0x08, + 0xff, 0x8d, 0x12, 0x08, + 0xff, 0x8e, 0x14, 0x0c, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x0c, + 0x3d, 0x64, 0xa4, 0x28, + 0x55, 0x64, 0xc8, 0x28, + 0x00, 0x6c, 0xda, 0x18, + 0xff, 0x52, 0xc8, 0x08, + 0x00, 0x6c, 0xda, 0x20, + 0xff, 0x6a, 0xc8, 0x08, + 0x00, 0x6c, 0xda, 0x20, + 0x00, 0x6c, 0xda, 0x24, + 0xff, 0x65, 0xc8, 0x08, + 0xe0, 0x6a, 0xcc, 0x00, + 0x41, 0x6a, 0xee, 0x5b, + 0xff, 0x90, 0xe2, 0x09, + 0x20, 0x6a, 0xd0, 0x01, + 0x04, 0x35, 0x10, 0x7c, + 0x1d, 0x6a, 0xdc, 0x01, + 0xdc, 0xee, 0x0c, 0x64, + 0x00, 0x65, 0x1c, 0x44, + 0x01, 0x6a, 0xdc, 0x01, + 0x20, 0xa0, 0xd8, 0x31, + 0x09, 0xee, 0xdc, 0x01, + 0x80, 0xee, 0x16, 0x7c, + 0x19, 0x6a, 0xdc, 0x01, + 0xd8, 0xee, 0x1a, 0x64, + 0xff, 0x6a, 0xdc, 0x09, + 0x18, 0xee, 0x1e, 0x6c, + 0xff, 0x6a, 0xd4, 0x0c, + 0x88, 0x6a, 0xcc, 0x00, + 0x41, 0x6a, 0xee, 0x5b, + 0x20, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0xff, 0x35, 0x26, 0x09, + 0x04, 0x35, 0x48, 0x6c, + 0xa0, 0x6a, 0xca, 0x00, + 0x20, 0x65, 0xc8, 0x18, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0x00, 0x65, 0x34, 0x64, + 0x0a, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0x04, 0x35, 0x38, 0x7b, + 0xa0, 0x6a, 0x54, 0x5c, + 0x00, 0x65, 0x56, 0x5c, + 0x00, 0x65, 0x56, 0x5c, + 0x00, 0x65, 0x56, 0x44, + 0xff, 0x65, 0xcc, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x0c, + 0x08, 0x94, 0x64, 0x7c, + 0xf7, 0x93, 0x26, 0x09, + 0x08, 0x93, 0x68, 0x6c, + 0xff, 0x6a, 0xd4, 0x0c, + 0xff, 0x40, 0x74, 0x09, + 0xff, 0x90, 0x80, 0x08, + 0xff, 0x6a, 0x72, 0x05, + 0xff, 0x40, 0x80, 0x64, + 0xff, 0x3f, 0x78, 0x64, + 0xff, 0x6a, 0xca, 0x04, + 0xff, 0x3f, 0x20, 0x09, + 0x01, 0x6a, 0x6a, 0x00, + 0x00, 0xb9, 0xfe, 0x5b, + 0x00, 0x90, 0x5c, 0x43, + 0xff, 0x40, 0x20, 0x09, + 0xff, 0xba, 0x80, 0x0c, + 0xff, 0x6a, 0x76, 0x01, + 0xff, 0x3f, 0x74, 0x09, + 0xff, 0x90, 0x7e, 0x08, + 0xff, 0xba, 0x38, 0x73, + 0xff, 0xba, 0x20, 0x09, + 0xff, 0x3f, 0x76, 0x09, + 0xff, 0x3f, 0x20, 0x0d, +}; + +static int aic7xxx_patch12_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch12_func(struct aic7xxx_host *p) +{ + return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895); +} + +static int aic7xxx_patch11_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch11_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_WIDE) != 0); +} + +static int aic7xxx_patch10_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch10_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_ULTRA2) == 0); +} + +static int aic7xxx_patch9_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch9_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_ULTRA) != 0); +} + +static int aic7xxx_patch8_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch8_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_ULTRA2) != 0); +} + +static int aic7xxx_patch7_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch7_func(struct aic7xxx_host *p) +{ + return ((p->flags & AHC_PAGESCBS) == 0); +} + +static int aic7xxx_patch6_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch6_func(struct aic7xxx_host *p) +{ + return ((p->flags & AHC_PAGESCBS) != 0); +} + +static int aic7xxx_patch5_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch5_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_QUEUE_REGS) != 0); +} + +static int aic7xxx_patch4_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch4_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_TWIN) != 0); +} + +static int aic7xxx_patch3_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch3_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_QUEUE_REGS) == 0); +} + +static int aic7xxx_patch2_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch2_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_CMD_CHAN) != 0); +} + +static int aic7xxx_patch1_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch1_func(struct aic7xxx_host *p) +{ + return ((p->flags & AHC_TARGETMODE) != 0); +} + +static int aic7xxx_patch0_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch0_func(struct aic7xxx_host *p) +{ + return (0); +} + +struct sequencer_patch { + int (*patch_func)(struct aic7xxx_host *); + unsigned int begin :10, + skip_instr :10, + skip_patch :12; +} sequencer_patches[] = { + { aic7xxx_patch1_func, 1, 1, 2 }, + { aic7xxx_patch0_func, 2, 1, 1 }, + { aic7xxx_patch2_func, 3, 2, 1 }, + { aic7xxx_patch3_func, 7, 1, 1 }, + { aic7xxx_patch3_func, 8, 1, 1 }, + { aic7xxx_patch4_func, 11, 4, 1 }, + { aic7xxx_patch5_func, 16, 3, 2 }, + { aic7xxx_patch0_func, 19, 4, 1 }, + { aic7xxx_patch6_func, 23, 1, 1 }, + { aic7xxx_patch7_func, 26, 1, 1 }, + { aic7xxx_patch4_func, 34, 4, 1 }, + { aic7xxx_patch8_func, 38, 3, 2 }, + { aic7xxx_patch0_func, 41, 3, 1 }, + { aic7xxx_patch9_func, 47, 7, 1 }, + { aic7xxx_patch4_func, 55, 3, 1 }, + { aic7xxx_patch8_func, 58, 2, 1 }, + { aic7xxx_patch1_func, 63, 60, 1 }, + { aic7xxx_patch8_func, 164, 1, 2 }, + { aic7xxx_patch0_func, 165, 1, 1 }, + { aic7xxx_patch2_func, 169, 1, 1 }, + { aic7xxx_patch2_func, 172, 1, 2 }, + { aic7xxx_patch0_func, 173, 2, 1 }, + { aic7xxx_patch10_func, 175, 1, 1 }, + { aic7xxx_patch8_func, 182, 1, 2 }, + { aic7xxx_patch0_func, 183, 3, 1 }, + { aic7xxx_patch8_func, 187, 1, 2 }, + { aic7xxx_patch0_func, 188, 1, 1 }, + { aic7xxx_patch8_func, 189, 7, 2 }, + { aic7xxx_patch0_func, 196, 1, 1 }, + { aic7xxx_patch2_func, 201, 13, 2 }, + { aic7xxx_patch0_func, 214, 8, 1 }, + { aic7xxx_patch10_func, 222, 1, 1 }, + { aic7xxx_patch8_func, 227, 1, 1 }, + { aic7xxx_patch8_func, 228, 1, 1 }, + { aic7xxx_patch8_func, 233, 1, 1 }, + { aic7xxx_patch8_func, 235, 2, 1 }, + { aic7xxx_patch8_func, 240, 8, 1 }, + { aic7xxx_patch8_func, 249, 1, 1 }, + { aic7xxx_patch2_func, 250, 2, 2 }, + { aic7xxx_patch0_func, 252, 4, 1 }, + { aic7xxx_patch10_func, 256, 2, 2 }, + { aic7xxx_patch0_func, 258, 1, 1 }, + { aic7xxx_patch11_func, 265, 1, 2 }, + { aic7xxx_patch0_func, 266, 1, 1 }, + { aic7xxx_patch5_func, 328, 1, 2 }, + { aic7xxx_patch0_func, 329, 1, 1 }, + { aic7xxx_patch3_func, 332, 1, 1 }, + { aic7xxx_patch11_func, 351, 1, 2 }, + { aic7xxx_patch0_func, 352, 1, 1 }, + { aic7xxx_patch6_func, 356, 1, 1 }, + { aic7xxx_patch7_func, 364, 3, 2 }, + { aic7xxx_patch0_func, 367, 1, 1 }, + { aic7xxx_patch1_func, 396, 3, 1 }, + { aic7xxx_patch10_func, 410, 1, 1 }, + { aic7xxx_patch2_func, 453, 7, 2 }, + { aic7xxx_patch0_func, 460, 8, 1 }, + { aic7xxx_patch2_func, 469, 4, 2 }, + { aic7xxx_patch0_func, 473, 6, 1 }, + { aic7xxx_patch2_func, 479, 4, 2 }, + { aic7xxx_patch0_func, 483, 3, 1 }, + { aic7xxx_patch2_func, 512, 17, 4 }, + { aic7xxx_patch12_func, 520, 4, 2 }, + { aic7xxx_patch0_func, 524, 2, 1 }, + { aic7xxx_patch0_func, 529, 33, 1 }, + { aic7xxx_patch6_func, 566, 2, 1 }, + { aic7xxx_patch6_func, 569, 9, 1 }, + +}; diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h --- v2.1.124/linux/drivers/scsi/aic7xxx_seq.h Sun Jun 7 11:16:34 1998 +++ linux/drivers/scsi/aic7xxx_seq.h Wed Dec 31 16:00:00 1969 @@ -1,524 +0,0 @@ -/* - * DO NOT EDIT - This file is automatically generated. - */ -static unsigned char seqprog[] = { - 0xff, 0x6a, 0x03, 0x02, - 0x32, 0x6a, 0x00, 0x00, - 0x12, 0x6a, 0x00, 0x00, - 0x00, 0x65, 0x92, 0x16, - 0xf7, 0x01, 0x01, 0x02, - 0xff, 0x4e, 0x64, 0x02, - 0xbf, 0x60, 0x60, 0x02, - 0x60, 0x0b, 0x37, 0x1a, - 0x40, 0x00, 0x05, 0x1a, - 0x08, 0x1f, 0x1f, 0x04, - 0x60, 0x0b, 0x37, 0x1a, - 0x40, 0x00, 0x05, 0x1a, - 0x08, 0x1f, 0x1f, 0x04, - 0xff, 0x3e, 0x1d, 0x18, - 0x40, 0x60, 0x60, 0x00, - 0x00, 0x4d, 0x06, 0x1c, - 0x01, 0x4e, 0x4e, 0x06, - 0xbf, 0x60, 0x60, 0x02, - 0x00, 0x6a, 0xd8, 0x17, - 0xff, 0x4e, 0x64, 0x06, - 0x02, 0x6a, 0x93, 0x17, - 0x0d, 0x6a, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0xff, 0x99, 0x65, 0x02, - 0xff, 0x65, 0x90, 0x02, - 0x0d, 0x6a, 0x35, 0x00, - 0x00, 0x65, 0xb3, 0x17, - 0xff, 0x3e, 0xba, 0x02, - 0xff, 0x90, 0x3e, 0x02, - 0xff, 0x3e, 0x90, 0x02, - 0x00, 0x65, 0x20, 0x16, - 0x00, 0x65, 0x05, 0x10, - 0xf7, 0x1f, 0x65, 0x02, - 0x08, 0xa1, 0x64, 0x02, - 0x00, 0x65, 0x65, 0x00, - 0xff, 0x65, 0x1f, 0x02, - 0xf0, 0xa1, 0x64, 0x02, - 0x0f, 0x05, 0x05, 0x02, - 0x00, 0x05, 0x05, 0x00, - 0x5a, 0x6a, 0x00, 0x01, - 0x12, 0x65, 0x64, 0x00, - 0x00, 0x01, 0x01, 0x00, - 0x31, 0x6a, 0x65, 0x00, - 0x80, 0x37, 0x2d, 0x1a, - 0xff, 0x65, 0x65, 0x06, - 0xff, 0x37, 0x6e, 0x02, - 0xff, 0x6e, 0x64, 0x02, - 0x00, 0x6c, 0x31, 0x1e, - 0x20, 0x01, 0x01, 0x00, - 0x4c, 0x37, 0x64, 0x0a, - 0x08, 0x1f, 0x35, 0x1e, - 0x08, 0x37, 0x37, 0x00, - 0x08, 0x64, 0x64, 0x00, - 0x20, 0x64, 0x65, 0x06, - 0xff, 0x6c, 0x04, 0x03, - 0x40, 0x0b, 0x78, 0x1a, - 0x80, 0x0b, 0x71, 0x1e, - 0xa4, 0x6a, 0x03, 0x00, - 0x40, 0x6a, 0x0b, 0x00, - 0x10, 0x03, 0x6f, 0x1e, - 0xff, 0x50, 0x64, 0x02, - 0x49, 0x6a, 0xa9, 0x17, - 0x01, 0x6a, 0x93, 0x00, - 0xff, 0x6a, 0x65, 0x02, - 0x08, 0x01, 0x01, 0x00, - 0x02, 0x0b, 0x41, 0x1e, - 0xf7, 0x01, 0x01, 0x02, - 0xff, 0x06, 0x66, 0x02, - 0xff, 0x66, 0x99, 0x02, - 0x01, 0x65, 0x65, 0x06, - 0x80, 0x66, 0x48, 0x1e, - 0xff, 0x66, 0x51, 0x02, - 0x10, 0x03, 0x40, 0x1a, - 0xfc, 0x65, 0x64, 0x06, - 0x00, 0x65, 0x4c, 0x12, - 0xff, 0x6a, 0x99, 0x00, - 0x01, 0x64, 0x8c, 0x06, - 0x84, 0x6a, 0x03, 0x00, - 0x08, 0x01, 0x01, 0x00, - 0x02, 0x0b, 0x4f, 0x1e, - 0xff, 0x06, 0x64, 0x02, - 0xff, 0x64, 0x99, 0x02, - 0xff, 0x6a, 0x65, 0x02, - 0x5b, 0x64, 0x64, 0x0a, - 0x00, 0x62, 0x62, 0x06, - 0xfc, 0x65, 0x65, 0x06, - 0xff, 0x6a, 0x6a, 0x02, - 0xfa, 0x65, 0x65, 0x06, - 0xff, 0x6a, 0x6a, 0x02, - 0x04, 0x65, 0x65, 0x06, - 0x0b, 0x65, 0x65, 0x06, - 0xff, 0x65, 0x64, 0x02, - 0x00, 0x8c, 0x8c, 0x06, - 0x02, 0x0b, 0x5d, 0x1e, - 0x01, 0x65, 0x60, 0x18, - 0xf7, 0x01, 0x01, 0x02, - 0xff, 0x06, 0x99, 0x02, - 0xff, 0x65, 0x65, 0x06, - 0xff, 0x65, 0x5d, 0x1a, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x40, 0x51, 0x69, 0x1e, - 0xe4, 0x6a, 0x03, 0x00, - 0x08, 0x01, 0x01, 0x00, - 0x04, 0x6a, 0x5c, 0x17, - 0x01, 0x50, 0x50, 0x06, - 0x01, 0x50, 0x6c, 0x98, - 0xff, 0x6a, 0x50, 0x02, - 0xff, 0x6a, 0x9d, 0x00, - 0x02, 0x6a, 0x91, 0x00, - 0x40, 0x51, 0x6f, 0x1a, - 0xff, 0x6a, 0x03, 0x02, - 0x00, 0x65, 0x05, 0x10, - 0x20, 0x6a, 0x0b, 0x00, - 0xf0, 0x19, 0x37, 0x02, - 0x08, 0x6a, 0x0c, 0x00, - 0x08, 0x11, 0x11, 0x00, - 0x08, 0x6a, 0x28, 0x16, - 0x08, 0x6a, 0x34, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x12, 0x6a, 0x00, 0x00, - 0x40, 0x6a, 0x0b, 0x00, - 0xff, 0x3e, 0x90, 0x02, - 0xff, 0xba, 0x3e, 0x02, - 0xff, 0xa1, 0x37, 0x02, - 0x08, 0x6a, 0x0c, 0x00, - 0x08, 0x11, 0x11, 0x00, - 0x08, 0x6a, 0x28, 0x16, - 0x80, 0x6a, 0x34, 0x00, - 0x80, 0x36, 0x36, 0x00, - 0x00, 0x65, 0x9b, 0x17, - 0xff, 0x3d, 0x64, 0x02, - 0xbf, 0x64, 0x9a, 0x1e, - 0x80, 0x64, 0xc9, 0x1c, - 0xa0, 0x64, 0xd4, 0x1c, - 0xc0, 0x64, 0xd1, 0x1c, - 0xe0, 0x64, 0xf5, 0x1c, - 0x01, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0xf7, 0x11, 0x11, 0x02, - 0x00, 0x65, 0x92, 0x16, - 0xff, 0x06, 0x6a, 0x02, - 0xf7, 0x01, 0x01, 0x02, - 0x09, 0x0c, 0x8f, 0x1e, - 0x08, 0x0c, 0x05, 0x1a, - 0x01, 0x6a, 0x91, 0x00, - 0xff, 0x6a, 0x93, 0x02, - 0xff, 0x6a, 0x04, 0x02, - 0xdf, 0x01, 0x01, 0x02, - 0x01, 0x6a, 0x3d, 0x00, - 0x03, 0x36, 0x36, 0x03, - 0x08, 0x6a, 0x66, 0x00, - 0xa9, 0x6a, 0xa6, 0x17, - 0x00, 0x65, 0xa5, 0x10, - 0x79, 0x6a, 0x35, 0x00, - 0x40, 0x3d, 0x9d, 0x1a, - 0x04, 0x35, 0x35, 0x00, - 0x00, 0x65, 0x6c, 0x17, - 0x10, 0x36, 0x97, 0x1a, - 0x88, 0x6a, 0x66, 0x00, - 0xac, 0x6a, 0xa2, 0x17, - 0x00, 0x65, 0x9f, 0x17, - 0xff, 0xa3, 0x38, 0x02, - 0x39, 0x6a, 0x66, 0x00, - 0xa4, 0x6a, 0xa5, 0x17, - 0xff, 0x38, 0xac, 0x1a, - 0x80, 0x02, 0x02, 0x00, - 0xff, 0x6a, 0x8c, 0x00, - 0xff, 0x6a, 0x8d, 0x00, - 0xff, 0x6a, 0x8e, 0x00, - 0x00, 0x65, 0x9f, 0x17, - 0xe7, 0x35, 0x35, 0x02, - 0x01, 0x38, 0xae, 0x18, - 0xbf, 0x35, 0x35, 0x02, - 0x00, 0x35, 0x61, 0x17, - 0x80, 0x02, 0xc6, 0x1a, - 0xff, 0x65, 0xc0, 0x1e, - 0xff, 0x38, 0x38, 0x06, - 0xff, 0x38, 0xc0, 0x1e, - 0xff, 0x6a, 0x64, 0x02, - 0x08, 0x39, 0x39, 0x06, - 0x00, 0x3a, 0x3a, 0x08, - 0x88, 0x6a, 0x66, 0x00, - 0x39, 0x6a, 0xa5, 0x17, - 0x08, 0x6a, 0x8c, 0x00, - 0xff, 0x6a, 0x8d, 0x02, - 0xff, 0x6a, 0x8e, 0x02, - 0x0d, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x88, 0x6a, 0xc9, 0x17, - 0x00, 0x65, 0x9f, 0x17, - 0x10, 0x0c, 0xa5, 0x1e, - 0xff, 0x08, 0xa9, 0x02, - 0xff, 0x09, 0xaa, 0x02, - 0xff, 0x0a, 0xab, 0x02, - 0xff, 0x38, 0xa8, 0x02, - 0x10, 0x36, 0x36, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x7f, 0x02, 0x02, 0x02, - 0xe1, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x00, 0x65, 0x6c, 0x17, - 0x88, 0x6a, 0x66, 0x00, - 0xb4, 0x6a, 0xa4, 0x17, - 0xff, 0x6a, 0x8d, 0x02, - 0xff, 0x6a, 0x8e, 0x02, - 0x00, 0x65, 0x9f, 0x17, - 0x3d, 0x6a, 0x61, 0x17, - 0x00, 0x65, 0x82, 0x10, - 0x00, 0x65, 0x6c, 0x17, - 0xff, 0x06, 0xa2, 0x02, - 0x00, 0x65, 0x82, 0x10, - 0xff, 0x34, 0x65, 0x02, - 0x80, 0x65, 0xe6, 0x18, - 0x0f, 0xa1, 0x65, 0x02, - 0x07, 0xa1, 0x65, 0x02, - 0x40, 0xa0, 0x64, 0x02, - 0x00, 0x65, 0x65, 0x00, - 0x80, 0x65, 0x65, 0x00, - 0x80, 0xa0, 0xde, 0x1e, - 0xff, 0x65, 0x06, 0x02, - 0x00, 0x65, 0xe7, 0x10, - 0x20, 0xa0, 0xe9, 0x1e, - 0xff, 0x65, 0x06, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xa0, 0x3d, 0xef, 0x18, - 0x23, 0xa0, 0x06, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xa0, 0x3d, 0xef, 0x18, - 0x00, 0xb9, 0xe9, 0x10, - 0xff, 0x65, 0xe9, 0x18, - 0xa1, 0x6a, 0x91, 0x00, - 0x10, 0x51, 0xef, 0x1c, - 0x40, 0x6a, 0x0c, 0x00, - 0xff, 0x65, 0x06, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xa0, 0x3d, 0xef, 0x18, - 0x10, 0x3d, 0x03, 0x00, - 0x00, 0x65, 0xd4, 0x10, - 0x40, 0x6a, 0x0c, 0x00, - 0xff, 0x34, 0x52, 0x02, - 0x80, 0x34, 0xf3, 0x18, - 0x7f, 0xa0, 0xa0, 0x02, - 0x08, 0x6a, 0x34, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x64, 0x6a, 0x59, 0x17, - 0x80, 0x64, 0x2f, 0x1b, - 0x04, 0x64, 0x22, 0x1d, - 0x02, 0x64, 0x25, 0x1d, - 0x00, 0x6a, 0x02, 0x1d, - 0x03, 0x64, 0x2d, 0x1d, - 0x01, 0x64, 0x20, 0x1d, - 0x07, 0x64, 0x50, 0x1d, - 0x08, 0x64, 0x00, 0x1d, - 0x11, 0x6a, 0x91, 0x00, - 0x07, 0x6a, 0x52, 0x17, - 0xff, 0x06, 0x6a, 0x02, - 0x00, 0x65, 0x82, 0x10, - 0xff, 0xa8, 0x04, 0x1b, - 0xff, 0xa2, 0x0f, 0x1f, - 0x01, 0x6a, 0x35, 0x00, - 0x00, 0xb9, 0xb3, 0x17, - 0xff, 0xa2, 0x0f, 0x1f, - 0x71, 0x6a, 0x91, 0x00, - 0x40, 0x51, 0x0f, 0x19, - 0x0d, 0x6a, 0x35, 0x00, - 0x00, 0xb9, 0xb3, 0x17, - 0xff, 0x3e, 0xba, 0x02, - 0xff, 0x90, 0x3e, 0x02, - 0x00, 0x65, 0x20, 0x16, - 0x00, 0x65, 0x8b, 0x10, - 0x20, 0xa0, 0x16, 0x1b, - 0xff, 0x37, 0x64, 0x02, - 0x00, 0x6a, 0x93, 0x17, - 0x01, 0x6a, 0x93, 0x00, - 0xff, 0x6a, 0x99, 0x00, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0xff, 0x4f, 0x64, 0x02, - 0x01, 0x6a, 0x93, 0x17, - 0x01, 0x6a, 0x93, 0x00, - 0xff, 0xb9, 0x99, 0x02, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x01, 0x4f, 0x4f, 0x06, - 0x02, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0xd5, 0x17, - 0x00, 0x65, 0x8b, 0x10, - 0x41, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x04, 0xa0, 0xa0, 0x00, - 0x00, 0x65, 0xe1, 0x17, - 0x00, 0x65, 0x8b, 0x10, - 0x10, 0x36, 0x00, 0x1f, - 0xff, 0x38, 0xa3, 0x02, - 0xa4, 0x6a, 0x66, 0x00, - 0x39, 0x6a, 0xa5, 0x17, - 0xac, 0x6a, 0x66, 0x00, - 0x14, 0x6a, 0xa5, 0x17, - 0xa9, 0x6a, 0xa6, 0x17, - 0x00, 0x65, 0x00, 0x11, - 0xef, 0x36, 0x36, 0x02, - 0x00, 0x65, 0x00, 0x11, - 0x0f, 0x64, 0x64, 0x02, - 0x07, 0x64, 0x64, 0x02, - 0x00, 0x37, 0x37, 0x00, - 0x00, 0x65, 0x8b, 0x17, - 0xff, 0x51, 0x37, 0x1d, - 0x20, 0x36, 0x3f, 0x1f, - 0x00, 0x90, 0x7d, 0x17, - 0x00, 0x65, 0x40, 0x11, - 0xff, 0x06, 0x6a, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xe0, 0x3d, 0x4d, 0x19, - 0x20, 0x12, 0x4d, 0x19, - 0x51, 0x6a, 0x54, 0x17, - 0xff, 0x51, 0x90, 0x02, - 0x20, 0xa0, 0x4d, 0x1f, - 0x00, 0x90, 0x7d, 0x17, - 0x00, 0x65, 0x7a, 0x17, - 0xff, 0x37, 0x64, 0x02, - 0x00, 0xa1, 0x49, 0x19, - 0x04, 0xa0, 0x49, 0x1f, - 0xfb, 0xa0, 0xa0, 0x02, - 0x80, 0x36, 0x36, 0x00, - 0x80, 0xa0, 0x00, 0x1f, - 0x7f, 0xa0, 0xa0, 0x02, - 0xff, 0x6a, 0x52, 0x17, - 0x00, 0x65, 0x00, 0x11, - 0x04, 0xa0, 0x4c, 0x1f, - 0x00, 0x65, 0xe1, 0x17, - 0x00, 0x65, 0x4d, 0x11, - 0x00, 0x65, 0xd5, 0x17, - 0x31, 0x6a, 0x91, 0x00, - 0x0c, 0x6a, 0x52, 0x17, - 0x00, 0x65, 0x00, 0x11, - 0x61, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x00, 0x11, - 0x10, 0x3d, 0x03, 0x00, - 0xff, 0x65, 0x34, 0x03, - 0xff, 0x06, 0x6a, 0x02, - 0x01, 0x0c, 0x55, 0x1f, - 0x04, 0x0c, 0x55, 0x1b, - 0xe0, 0x03, 0x3d, 0x02, - 0xe0, 0x3d, 0x5f, 0x19, - 0xff, 0x65, 0x66, 0x02, - 0xff, 0x12, 0x6d, 0x03, - 0xff, 0x06, 0x6a, 0x03, - 0xff, 0x65, 0x06, 0x02, - 0x02, 0x0b, 0x5d, 0x1f, - 0xff, 0x6a, 0x6a, 0x03, - 0xd1, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0xff, 0x65, 0x93, 0x02, - 0x01, 0x0b, 0x69, 0x1b, - 0x10, 0x0c, 0x62, 0x1f, - 0x04, 0x0b, 0x66, 0x1b, - 0xff, 0x6a, 0x65, 0x02, - 0x04, 0x93, 0x68, 0x1b, - 0x01, 0x94, 0x67, 0x1f, - 0x10, 0x94, 0x68, 0x1b, - 0xc7, 0x93, 0x93, 0x02, - 0x38, 0x93, 0x6a, 0x1b, - 0xff, 0x6a, 0x6a, 0x03, - 0x80, 0x36, 0x6b, 0x1b, - 0x21, 0x6a, 0x91, 0x01, - 0xff, 0x65, 0x90, 0x02, - 0xff, 0x51, 0x72, 0x19, - 0xff, 0x37, 0x64, 0x02, - 0xa1, 0x6a, 0x77, 0x11, - 0xff, 0x51, 0x64, 0x02, - 0xb9, 0x6a, 0x77, 0x11, - 0xff, 0xba, 0x79, 0x1d, - 0xff, 0xba, 0x90, 0x02, - 0xff, 0x65, 0x65, 0x06, - 0x00, 0x6c, 0x74, 0x19, - 0xff, 0x90, 0x65, 0x03, - 0xff, 0x6a, 0x65, 0x01, - 0x20, 0x36, 0x88, 0x1f, - 0x00, 0x90, 0x6e, 0x17, - 0xff, 0x65, 0x88, 0x1d, - 0xff, 0xba, 0x82, 0x1d, - 0xff, 0xbb, 0x66, 0x02, - 0xff, 0xba, 0x90, 0x02, - 0xff, 0x66, 0xbb, 0x02, - 0xff, 0x65, 0x90, 0x02, - 0xff, 0xbb, 0x87, 0x1d, - 0xff, 0xba, 0x66, 0x02, - 0xff, 0xbb, 0x90, 0x02, - 0xff, 0x66, 0xba, 0x02, - 0xff, 0x65, 0x90, 0x03, - 0xff, 0xba, 0x3f, 0x03, - 0x00, 0x6a, 0xd8, 0x17, - 0x0d, 0x6a, 0x35, 0x00, - 0x00, 0x51, 0xb3, 0x11, - 0xff, 0x3f, 0x96, 0x1d, - 0xff, 0x6a, 0x51, 0x00, - 0x00, 0x3f, 0x6e, 0x17, - 0xff, 0x65, 0x96, 0x1d, - 0x20, 0x36, 0x36, 0x00, - 0x20, 0xa0, 0x92, 0x1b, - 0xff, 0xb9, 0x51, 0x03, - 0xff, 0x6a, 0x51, 0x01, - 0xff, 0x65, 0x66, 0x02, - 0x45, 0x6a, 0xab, 0x17, - 0x01, 0x6a, 0x8c, 0x01, - 0xff, 0x37, 0x64, 0x02, - 0x00, 0x6a, 0x93, 0x17, - 0x0d, 0x6a, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0xff, 0x99, 0x51, 0x03, - 0x01, 0x0c, 0x9b, 0x1f, - 0x04, 0x0c, 0x9b, 0x1b, - 0xe0, 0x03, 0x3d, 0x02, - 0xff, 0x3d, 0x03, 0x03, - 0xff, 0x8c, 0x08, 0x02, - 0xff, 0x8d, 0x09, 0x02, - 0xff, 0x8e, 0x0a, 0x03, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x03, - 0x3d, 0x64, 0x66, 0x0a, - 0x55, 0x64, 0x64, 0x0a, - 0x00, 0x6c, 0x88, 0x06, - 0xff, 0x66, 0x64, 0x02, - 0x00, 0x6c, 0x89, 0x08, - 0xff, 0x6a, 0x64, 0x02, - 0x00, 0x6c, 0x8a, 0x08, - 0x00, 0x6c, 0x8b, 0x08, - 0xff, 0x6a, 0x8d, 0x02, - 0xff, 0x6a, 0x8e, 0x03, - 0xff, 0x65, 0x64, 0x02, - 0x41, 0x6a, 0xa9, 0x17, - 0x1c, 0x6a, 0x8c, 0x00, - 0xff, 0x35, 0x93, 0x02, - 0x04, 0x35, 0xc3, 0x1b, - 0xa0, 0x6a, 0x65, 0x00, - 0x1c, 0x65, 0x64, 0x06, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0x00, 0x65, 0xba, 0x19, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x04, 0x35, 0x6b, 0x1f, - 0xa0, 0x6a, 0xc9, 0x17, - 0x00, 0x65, 0xca, 0x17, - 0x00, 0x65, 0xca, 0x17, - 0x00, 0x65, 0xca, 0x11, - 0xff, 0x65, 0x66, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x03, - 0x08, 0x94, 0xd1, 0x1f, - 0xf7, 0x93, 0x93, 0x02, - 0x08, 0x93, 0xd3, 0x1b, - 0xff, 0x6a, 0x6a, 0x03, - 0xff, 0x40, 0xba, 0x02, - 0xff, 0x90, 0x40, 0x02, - 0xff, 0x6a, 0xb9, 0x01, - 0xff, 0x40, 0xdf, 0x19, - 0xff, 0x3f, 0xdb, 0x19, - 0xff, 0x6a, 0x65, 0x01, - 0xff, 0x3f, 0x90, 0x02, - 0x01, 0x6a, 0x35, 0x00, - 0x00, 0xb9, 0xb3, 0x17, - 0x00, 0x90, 0x7d, 0x11, - 0xff, 0x40, 0x90, 0x02, - 0xff, 0xba, 0x40, 0x03, - 0xff, 0x6a, 0xbb, 0x00, - 0xff, 0x3f, 0xba, 0x02, - 0xff, 0x90, 0x3f, 0x02, - 0xff, 0xba, 0x6b, 0x1d, - 0xff, 0xba, 0x90, 0x02, - 0xff, 0x3f, 0xbb, 0x02, - 0xff, 0x3f, 0x90, 0x03, -}; -#define WIDE 0x20 -#define ULTRA 0x10 -#define SCB_PAGING 0x8 -#define TWIN_CHANNEL 0x4 -#define TARGET_MODE 0x2 -struct sequencer_patch { - int options; - int negative; - int begin; - int end; -} sequencer_patches[] = { - { 0x00000002, 0, 0x001, 0x002 }, - { 0x00000002, 1, 0x002, 0x003 }, - { 0x00000004, 0, 0x009, 0x00d }, - { 0x00000008, 0, 0x012, 0x013 }, - { 0x00000008, 1, 0x018, 0x019 }, - { 0x00000004, 0, 0x020, 0x024 }, - { 0x00000010, 0, 0x02a, 0x031 }, - { 0x00000002, 0, 0x038, 0x071 }, - { 0x00000020, 0, 0x0d6, 0x0d7 }, - { 0x00000020, 1, 0x0d7, 0x0d8 }, - { 0x00000020, 0, 0x12f, 0x130 }, - { 0x00000020, 1, 0x130, 0x131 }, - { 0x00000008, 0, 0x134, 0x135 }, - { 0x00000008, 1, 0x13c, 0x13f }, - { 0x00000008, 0, 0x13f, 0x140 }, - { 0x00000002, 0, 0x15c, 0x15f }, - { 0x00000008, 0, 0x1d5, 0x1d7 }, - { 0x00000008, 0, 0x1d8, 0x1e1 }, - { 0x00000000, 0, 0x000, 0x000 } -}; diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/amiga7xx.c linux/drivers/scsi/amiga7xx.c --- v2.1.124/linux/drivers/scsi/amiga7xx.c Mon Aug 3 12:45:46 1998 +++ linux/drivers/scsi/amiga7xx.c Mon Oct 5 13:43:31 1998 @@ -50,6 +50,28 @@ tpnt->proc_dir = &proc_scsi_amiga7xx; +#ifdef CONFIG_BLZ603EPLUS_SCSI + if ((key = zorro_find(ZORRO_PROD_PHASE5_BLIZZARD_603E_PLUS, 0, 0))) + { + cd = zorro_get_board(key); +/* + unsigned long address; + address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, + cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); +*/ + options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; + + clock = 50000000; /* 50MHz SCSI Clock */ + + ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(0x80f40000), + 0, IRQ_AMIGA_PORTS, DMA_NONE, + options, clock); + + zorro_config_board(key, 0); + num++; + } +#endif + #ifdef CONFIG_WARPENGINE_SCSI if ((key = zorro_find(ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx, 0, 0))) { diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/blz1230.c linux/drivers/scsi/blz1230.c --- v2.1.124/linux/drivers/scsi/blz1230.c Tue Jun 23 10:01:23 1998 +++ linux/drivers/scsi/blz1230.c Mon Oct 5 13:43:31 1998 @@ -131,7 +131,7 @@ /* Set the command buffer */ esp->esp_command = (volatile unsigned char*) cmd_buffer; - esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer); + esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer); esp->irq = IRQ_AMIGA_PORTS; request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/blz2060.c linux/drivers/scsi/blz2060.c --- v2.1.124/linux/drivers/scsi/blz2060.c Tue Jun 23 10:01:23 1998 +++ linux/drivers/scsi/blz2060.c Mon Oct 5 13:43:31 1998 @@ -107,7 +107,7 @@ /* Set the command buffer */ esp->esp_command = (volatile unsigned char*) cmd_buffer; - esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer); + esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer); esp->irq = IRQ_AMIGA_PORTS; request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/cyberstorm.c linux/drivers/scsi/cyberstorm.c --- v2.1.124/linux/drivers/scsi/cyberstorm.c Tue Jun 23 10:01:23 1998 +++ linux/drivers/scsi/cyberstorm.c Mon Oct 5 13:43:31 1998 @@ -127,7 +127,7 @@ /* Set the command buffer */ esp->esp_command = (volatile unsigned char*) cmd_buffer; - esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer); + esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer); esp->irq = IRQ_AMIGA_PORTS; request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/cyberstormII.c linux/drivers/scsi/cyberstormII.c --- v2.1.124/linux/drivers/scsi/cyberstormII.c Tue Jun 23 10:01:23 1998 +++ linux/drivers/scsi/cyberstormII.c Mon Oct 5 13:43:31 1998 @@ -118,7 +118,7 @@ /* Set the command buffer */ esp->esp_command = (volatile unsigned char*) cmd_buffer; - esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer); + esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer); esp->irq = IRQ_AMIGA_PORTS; request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/fastlane.c linux/drivers/scsi/fastlane.c --- v2.1.124/linux/drivers/scsi/fastlane.c Mon Aug 3 12:45:46 1998 +++ linux/drivers/scsi/fastlane.c Mon Oct 5 13:43:31 1998 @@ -163,7 +163,7 @@ /* Set the command buffer */ esp->esp_command = (volatile unsigned char*) cmd_buffer; - esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer); + esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer); esp->irq = IRQ_AMIGA_PORTS; request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v2.1.124/linux/drivers/scsi/hosts.c Thu Sep 17 17:53:36 1998 +++ linux/drivers/scsi/hosts.c Mon Oct 5 13:47:23 1998 @@ -41,7 +41,15 @@ #include "hosts.h" -#if defined(CONFIG_A4000T_SCSI) || defined(CONFIG_WARPENGINE_SCSI) || defined(CONFIG_A4091_SCSI) || defined (CONFIG_GVP_TURBO_SCSI) +#if defined(CONFIG_A4000T_SCSI) || \ + defined(CONFIG_WARPENGINE_SCSI) || \ + defined(CONFIG_A4091_SCSI) || \ + defined (CONFIG_GVP_TURBO_SCSI) || \ + defined (CONFIG_BLZ603E) +#define AMIGA7XXCONFIG +#endif + +#ifdef AMIGA7XXCONFIG #include "amiga7xx.h" #endif @@ -317,7 +325,7 @@ static Scsi_Host_Template builtin_scsi_hosts[] = { #ifdef CONFIG_AMIGA -#if defined(CONFIG_WARPENGINE_SCSI) || defined(CONFIG_A4000T_SCSI) || defined(CONFIG_A4091_SCSI) || defined (CONFIG_GVP_TURBO_SCSI) +#ifdef AMIGA7XXCONFIG AMIGA7XX_SCSI, #endif #ifdef CONFIG_A3000_SCSI diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c --- v2.1.124/linux/drivers/scsi/ncr53c8xx.c Wed Sep 9 14:51:08 1998 +++ linux/drivers/scsi/ncr53c8xx.c Mon Oct 5 13:40:23 1998 @@ -73,7 +73,7 @@ */ /* -** 16 July 1998, version 3.0g +** October 4 1998, version 3.0i ** ** Supported SCSI-II features: ** Synchronous negotiation @@ -584,6 +584,7 @@ #endif #ifdef __sparc__ +#include #define remap_pci_mem(base, size) ((vm_offset_t) __va(base)) #define unmap_pci_mem(vaddr, size) #define pcivtophys(p) ((p) & pci_dvma_mask) @@ -2011,9 +2012,9 @@ ncrcmd start [ 5]; ncrcmd startpos [ 1]; ncrcmd select [ 6]; - ncrcmd select2 [ 7]; + ncrcmd select2 [ 9]; ncrcmd loadpos [ 4]; - ncrcmd send_ident [ 7]; + ncrcmd send_ident [ 9]; ncrcmd prepare [ 6]; ncrcmd prepare2 [ 7]; #ifdef SCSI_NCR_PROFILE_SUPPORT @@ -2340,6 +2341,15 @@ */ /* + ** The M_REJECT problem seems to be due to a selection + ** timing problem. + ** Wait immediately for the selection to complete. + ** (2.5x behaves so) + */ + SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)), + 0, + + /* ** Next time use the next slot. */ SCR_COPY (4), @@ -2384,8 +2394,10 @@ offsetof (struct dsb, smsg), SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), PADDRH (resend_ident), + SCR_LOAD_REG (scratcha, 0x80), + 0, SCR_COPY (1), - RADDR (sfbr), + RADDR (scratcha), NADDR (lastmsg), }/*-------------------------< PREPARE >----------------------*/,{ /* @@ -2818,7 +2830,7 @@ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, NADDR (msgout), SCR_COPY (1), - RADDR (sfbr), + NADDR (msgout), NADDR (lastmsg), /* ** If it was no ABORT message ... @@ -3343,7 +3355,7 @@ SCR_MOVE_ABS (4) ^ SCR_MSG_OUT, NADDR (msgout), SCR_COPY (1), - RADDR (sfbr), + NADDR (msgout), NADDR (lastmsg), SCR_JUMP, PADDR (msg_out_done), @@ -3399,7 +3411,7 @@ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, NADDR (msgout), SCR_COPY (1), - RADDR (sfbr), + NADDR (msgout), NADDR (lastmsg), SCR_JUMP, PADDR (msg_out_done), @@ -3504,7 +3516,7 @@ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, NADDR (msgout), SCR_COPY (1), - RADDR (sfbr), + NADDR (msgout), NADDR (lastmsg), SCR_CLR (SCR_ACK|SCR_ATN), 0, @@ -4492,9 +4504,9 @@ int i; #ifdef __sparc__ -printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=0x%x\n", +printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%s\n", device->chip.name, unit, device->chip.revision_id, device->slot.base, - device->slot.io_port, device->slot.irq); + device->slot.io_port, __irq_itoa(device->slot.irq)); #else printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n", device->chip.name, unit, device->chip.revision_id, device->slot.base, @@ -4696,15 +4708,25 @@ #ifdef SCSI_NCR_SHARE_IRQ #define NCR_SA_INTERRUPT_FLAGS (SA_INTERRUPT | SA_SHIRQ) if (bootverbose > 1) +#ifdef __sparc__ + printk(KERN_INFO "%s: requesting shared irq %s (dev_id=0x%lx)\n", + ncr_name(np), __irq_itoa(device->slot.irq), (u_long) np); +#else printk(KERN_INFO "%s: requesting shared irq %d (dev_id=0x%lx)\n", ncr_name(np), device->slot.irq, (u_long) np); +#endif #else #define NCR_SA_INTERRUPT_FLAGS SA_INTERRUPT #endif if (request_irq(device->slot.irq, ncr53c8xx_intr, NCR_SA_INTERRUPT_FLAGS, "ncr53c8xx", np)) { +#ifdef __sparc__ + printk(KERN_ERR "%s: request irq %s failure\n", + ncr_name(np), __irq_itoa(device->slot.irq)); +#else printk(KERN_ERR "%s: request irq %d failure\n", ncr_name(np), device->slot.irq); +#endif goto attach_error; } np->irq = device->slot.irq; @@ -4794,7 +4816,8 @@ if (np->irq) { #ifdef DEBUG_NCR53C8XX #ifdef __sparc__ - printk(KERN_INFO "%s: freeing irq 0x%x\n", ncr_name(np), np->irq); + printk(KERN_INFO "%s: freeing irq %s\n", ncr_name(np), + __irq_itoa(np->irq)); #else printk(KERN_INFO "%s: freeing irq %d\n", ncr_name(np), np->irq); #endif @@ -4951,7 +4974,7 @@ nego = 0; - if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done) { + if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) { /* ** negotiate wide transfers ? @@ -5610,8 +5633,12 @@ */ #ifdef DEBUG_NCR53C8XX +#ifdef __sparc__ + printk("%s: freeing irq %s\n", ncr_name(np), __irq_itoa(np->irq)); +#else printk("%s: freeing irq %d\n", ncr_name(np), np->irq); #endif +#endif free_irq(np->irq, np); /* @@ -5866,7 +5893,8 @@ if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) { u_char * p = (u_char*) & cmd->sense_buffer; int i; - printk ("\n%s: sense data:", ncr_name (np)); + PRINT_ADDR(cmd); + printk ("sense data:"); for (i=0; i<14; i++) printk (" %x", *p++); printk (".\n"); } @@ -9792,7 +9820,7 @@ } if ((chip->features & FE_CLSE) && !cache_line_size) { - cache_line_size = 16; + cache_line_size = CACHE_LINE_SIZE; if (initverbose >= 2) printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size); pcibios_write_config_byte(bus, device_fn, @@ -10715,7 +10743,11 @@ copy_info(&info, "revision id 0x%x\n", np->revision_id); copy_info(&info, " IO port address 0x%lx, ", (u_long) np->port); +#ifdef __sparc__ + copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq)); +#else copy_info(&info, "IRQ number %d\n", (int) np->irq); +#endif #ifndef NCR_IOMAPPED if (np->reg) diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h --- v2.1.124/linux/drivers/scsi/ncr53c8xx.h Fri Jul 31 17:07:58 1998 +++ linux/drivers/scsi/ncr53c8xx.h Thu Oct 8 21:32:26 1998 @@ -45,7 +45,7 @@ /* ** Name and revision of the driver */ -#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 3.0g" +#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 3.0i" /* ** Check supported Linux versions diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.1.124/linux/drivers/scsi/scsi.c Thu Sep 17 17:53:36 1998 +++ linux/drivers/scsi/scsi.c Thu Oct 8 08:07:34 1998 @@ -1476,6 +1476,7 @@ SCpnt->internal_timeout = NORMAL_TIMEOUT; SCpnt->abort_reason = 0; + SCpnt->result = 0; internal_cmnd (SCpnt); SCSI_LOG_MLQUEUE(3,printk ("Leaving scsi_do_cmd()\n")); @@ -1704,6 +1705,8 @@ SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->result = 0; + memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); return internal_cmnd (SCpnt); } diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/scsi_obsolete.c linux/drivers/scsi/scsi_obsolete.c --- v2.1.124/linux/drivers/scsi/scsi_obsolete.c Wed Jul 1 19:38:55 1998 +++ linux/drivers/scsi/scsi_obsolete.c Thu Oct 8 08:07:34 1998 @@ -219,6 +219,8 @@ memcpy ((void *) SCpnt->cmnd , (void *) generic_sense, sizeof(generic_sense)); + memset ((void *) SCpnt->sense_buffer, 0, + sizeof(SCpnt->sense_buffer)); SCpnt->cmnd[1] = SCpnt->lun << 5; SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); @@ -227,6 +229,7 @@ SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); SCpnt->use_sg = 0; SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->result = 0; internal_cmnd (SCpnt); } @@ -375,7 +378,8 @@ SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->lun); scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); - return; + status = REDO; + break; } else { @@ -491,12 +495,8 @@ printk("scsi%d, channel %d : RESERVATION CONFLICT performing" " reset.\n", SCpnt->host->host_no, SCpnt->channel); scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); - return; -#if 0 - exit = DRIVER_SOFT | SUGGEST_ABORT; - status = MAYREDO; + status = REDO; break; -#endif default: printk ("Internal error %s %d \n" "status byte = %d \n", __FILE__, @@ -634,10 +634,13 @@ memcpy ((void *) SCpnt->cmnd, (void*) SCpnt->data_cmnd, sizeof(SCpnt->data_cmnd)); + memset ((void *) SCpnt->sense_buffer, 0, + sizeof(SCpnt->sense_buffer)); SCpnt->request_buffer = SCpnt->buffer; SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->result = 0; internal_cmnd (SCpnt); } break; diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c --- v2.1.124/linux/drivers/scsi/scsicam.c Sun Dec 21 17:04:49 1997 +++ linux/drivers/scsi/scsicam.c Tue Oct 6 08:56:36 1998 @@ -114,6 +114,8 @@ end_head = largest->end_head; end_sector = largest->end_sector & 0x3f; + if( end_head + 1 == 0 || end_sector == 0 ) return -1; + #ifdef DEBUG printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n", end_head, end_cyl, end_sector); diff -u --recursive --new-file v2.1.124/linux/drivers/video/amifb.c linux/drivers/video/amifb.c --- v2.1.124/linux/drivers/video/amifb.c Mon Oct 5 13:13:41 1998 +++ linux/drivers/video/amifb.c Mon Oct 5 12:24:34 1998 @@ -61,6 +61,7 @@ #include #include +#include