S: Trafford, Pennsylvania 15085
S: USA
+N: Dag Brattli
+E: dagb@cs.uit.no
+W: http://www.cs.uit.no/~dagb
+D: IrDA Subsystem
+S: 19. Wellington Road
+S: Lancaster, LA1 4DN
+S: UK, England
+
N: Andries Brouwer
E: aeb@cwi.nl
D: random Linux hacker
S: USA
N: Juan Jose Ciarlante
+W: http://juanjox.linuxhq.com/
E: jjciarla@raiz.uncu.edu.ar
-E: juanjo@irriga.uncu.edu.ar
+E: jjo@mendoza.gov.ar
D: Network driver alias support
D: IP masq hashing and app modules
+D: IP masq 2.1 features and bugs
S: Las Cuevas 2385 - Bo Guemes
S: Las Heras, Mendoza CP 5539
S: Argentina
S: Socorro, New Mexico 87801
S: USA
+N: Oleg Drokin
+E: green@ccssu.crimea.ua
+W: http://www.ccssu.crimea.ua/~green
+D: Cleaning up sound drivers.
+S: Skvoznoy per., 14a
+S: Evpatoria
+S: Crimea
+S: UKRAINE, 334320
+
N: Thomas Dunbar
E: tdunbar@vtaix.cc.vt.edu
D: TeX & METAFONT hacking/maintenance
S: Sterling Heights, Michigan 48313
S: USA
+N: Tristan Greaves
+E: Tristan.Greaves@icl.com
+E: tmg296@ecs.soton.ac.uk
+W: http://www.ecs.soton.ac.uk/~tmg296
+D: Miscellaneous ipv4 sysctl patches
+S: 15 Little Mead
+S: Denmead
+S: Hampshire
+S: PO7 6HS
+S: United Kingdom
+
N: Michael A. Griffith
E: grif@cs.ucr.edu
W: http://www.cs.ucr.edu/~grif
S: 160 00 Praha 6
S: Czech Republic
+N: Niels Kristian Bech Jensen
+E: nkbj@image.dk
+W: http://www.image.dk/~nkbj
+D: 4.4BSD and NeXTstep support in read-only ufs
+S: Dr. Holsts Vej 34, lejl. 164
+S: DK-8230 Åbyhøj
+S: Denmark
+
N: Michael K. Johnson
E: johnsonm@redhat.com
W: http://www.redhat.com/~johnsonm
S: 8103 Rein
S: Austria
+N: Jan "Yenya" Kasprzak
+E: kas@fi.muni.cz
+D: Author of the COSA/SRP sync serial board driver.
+D: Port of the syncppp.c from the 2.0 to the 2.1 kernel.
+P: 1024/D3498839 0D 99 A7 FB 20 66 05 D7 8B 35 FC DE 05 B1 8A 5E
+W: http://www.fi.muni.cz/~kas/
+S: c/o Faculty of Informatics, Masaryk University
+S: Botanicka' 68a
+S: 602 00 Brno
+S: Czech Republic
+
N: Fred N. van Kempen
E: waltje@uwalt.nl.mugnet.org
D: NET-2
S: San Jose, California 95161-1311
S: USA
+N: Thorsten Knabe
+E: Thorsten Knabe <tek@rbg.informatik.tu-darmstadt.de>
+E: Thorsten Knabe <tek01@hrzpub.tu-darmstadt.de>
+W: http://www.student.informatik.tu-darmstadt.de/~tek
+W: http://www.tu-darmstadt.de/~tek01
+P: 1024/3BC8D885 8C 29 C5 0A C0 D1 D6 F4 20 D4 2D AB 29 F6 D0 60
+D: AD1816 sound driver
+S: Am Bergfried 10
+S: 63225 Langen
+S: Germany
+
N: Alain L. Knaff
E: Alain.Knaff@poboxes.com
D: floppy driver
S: The Netherlands
N: Volker Lendecke
-E: lendecke@namu01.Num.Math.Uni-Goettingen.de
+E: vl@kki.org
D: Kernel smbfs (to mount WfW, NT and OS/2 network drives.)
D: NCP filesystem support (to mount NetWare volumes)
-S: Innersteweg 11
-S: 37081 Goettingen
+S: Von Ossietzky Str. 12
+S: 37085 Goettingen
S: Germany
N: Kevin Lentin
N: Paul Russell
E: Paul.Russell@rustcorp.com.au
-W: http://www.adelaide.net.au/~rustcorp
+W: http://www.rustcorp.com
D: Ruggedly handsome.
D: Developed Generic IP Firewalling Chains with Michael Neuling.
S: B2240 Zandhoven
S: Belgium
+N: Henning P. Schmiedehausen
+E: hps@tanstaafl.de
+D: added PCI support to the serial driver
+S: Buckenhof, Germany
+
N: Martin Schulze
E: joey@linux.de
W: http://home.pages.de/~joey/
N: Steven Whitehouse
E: SteveW@ACM.org
-D: Linux DECnet project: http://eeshack3.swan.ac.uk/~gw7rrm/DECnet/index.html
+W: http://www-sigproc.eng.cam.ac.uk/~sjw44/
+D: Linux DECnet project: http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html
D: Minor debugging of other networking protocols.
N: Hans-Joachim Widmaier
- how to set up linux with a serial line console as the default.
smart-config.txt
- description of the Smart Config makefile feature.
-smp
- - how to setup the kernel for SMP
smp.tex
- TeX document describing implementation of Multiprocessor Linux
+smp.txt
+ - a few more notes on symmetric multi-processing
sound/
- directory with info on sound card support
specialix.txt
letters that were missing in Latin 4 to cover the entire Nordic
area.
+nls iso8859-15
+CONFIG_NLS_ISO8859_15
+ If you want to display filenames with native language characters
+ from the Microsoft fat filesystem family or from JOLIET CDROMs
+ correctly on the screen, you need to include the appropriate
+ input/output character sets. Say Y here for the Latin 9 character
+ set, which covers most West European languages such as Albanian,
+ Catalan, Danish, Dutch, English, Estonian, Faeroese, Finnish,
+ French, German, Galician, Irish, Icelandic, Italian, Norwegian,
+ Portuguese, Spanish, Swedish, and Valencian. Latin 9 is an update to
+ Latin 1 (ISO 8859-1) that removes a handful of rarely used
+ characters and instead adds support for Estonian, corrects the
+ support for French and Finnish, and adds the new Euro character. If
+ unsure, say Y.
+
nls koi8-r
CONFIG_NLS_KOI8_R
If you want to display filenames with native language characters
0 to disable any limiting, otherwise the maximal rate in jiffies(1)
See the source for more information.
+icmp_ignore_bogus_error_responses - BOOLEAN
+ Some routers violate RFC 1122 by sending bogus responses to broadcast
+ frames. Such violations are normally logged via a kernel warning.
+ If this is set to TRUE, the kernel will not give such warnings, which
+ will avoid log file clutter.
+ Default: FALSE
(1) Jiffie: internal timeunit for the kernel. On the i386 1/100s, on the
Alpha 1/1024s. See the HZ define in /usr/include/asm/param.h for the exact
+++ /dev/null
-To set up SMP
-
-Edit linux/Makefile and uncomment SMP=1, then compile and install
-as usual.
-
-If you are using LILO, it is handy to have both SMP and non-SMP
-kernel images on hand. Edit /etc/lilo.conf to create an entry
-for another kernel image called "linux-smp" or something.
-
-The next time you compile the kernel, when running a SMP kernel,
-edit linux/Makefile and change "MAKE=make" to "MAKE=make -jN"
-(where N = number of CPU + 1, or if you have tons of memory/swap
- you can just use "-j" without a number). Feel free to experiment
-with this one.
-
-Of course you should time how long each build takes :-)
-Example:
- make config
- time -v sh -c 'make dep ; make clean install modules modules_install'
-
-If you are using some Compaq MP compliant machines you will need to set
-the operating system in the BIOS settings to "Unixware" - don't ask me
-why Compaqs don't work otherwise.
--- /dev/null
+To set up SMP
+
+Configure the kernel and answer Y to CONFIG_SMP.
+
+If you are using LILO, it is handy to have both SMP and non-SMP
+kernel images on hand. Edit /etc/lilo.conf to create an entry
+for another kernel image called "linux-smp" or something.
+
+The next time you compile the kernel, when running a SMP kernel,
+edit linux/Makefile and change "MAKE=make" to "MAKE=make -jN"
+(where N = number of CPU + 1, or if you have tons of memory/swap
+ you can just use "-j" without a number). Feel free to experiment
+with this one.
+
+Of course you should time how long each build takes :-)
+Example:
+ make config
+ time -v sh -c 'make dep ; make clean install modules modules_install'
+
+If you are using some Compaq MP compliant machines you will need to set
+the operating system in the BIOS settings to "Unixware" - don't ask me
+why Compaqs don't work otherwise.
--- /dev/null
+Documentation for the OPL3-SA2, SA3, and SAx driver (opl3sa2.o)
+---------------------------------------------------------------
+
+Scott Murray, scottm@interlog.com
+December, 1998
+
+NOTE: All trademarked terms mentioned below are properties of their
+ respective owners.
+
+This driver is for PnP soundcards based on the following Yamaha audio
+controller chipsets:
+
+YMF711 aka OPL3-SA2
+YMF715 aka OPL3-SA3
+YMF719 aka OPL3-SAx (?)
+
+I'm a little fuzzy on what is classified a SAx, as I've seen the label
+used to refer to the whole 7xx family and as a specific identifier for
+the 719 on my no-name soundcard. To make matters worse, there seem to
+be several reversions of the 715 chipset.
+
+Anyways, all of these chipsets implement the following devices:
+
+OPL3 FM synthesizer
+Soundblaster Pro
+Microsoft/Windows Sound System
+MPU401 MIDI interface
+
+Note that this driver uses the MSS device, and to my knowledge these
+chipsets enforce an either/or situation with the Soundblaster Pro
+device and the MSS device. Since the MSS device has better
+capabilities, I have implemented the driver to use it.
+
+Being PnP cards, some configuration is required. There are two ways
+of doing this. The most common is to use the isapnptools package to
+initialize the card, and use the kernel module form of the sound
+subsystem and sound drivers. Alternatively, some BIOS's allow manual
+configuration of installed PnP devices in the BIOS menus, which should
+allow using the non-modular sound drivers, i.e. built into the kernel.
+
+I personally use isapnp and modules, and do not have access to a PnP
+BIOS machine to test. If you have such a beast, try building both the
+MSS driver and this driver into the kernel (appropiately configured,
+of course) and let me know if it works. If it does not, then email me
+if you are willing to experiment in an effort to make it work.
+
+If you are using isapnp, follow the directions in its documentation to
+produce a configuration file. Here is the relevant excerpt for my SAx
+card from my isapnp.conf:
+
+(CONFIGURE YMH0800/-1 (LD 0
+
+# Instead of (IO 0 (BASE 0x0220)), disable SB:
+(IO 0 (BASE 0x0000))
+(IO 1 (BASE 0x0530))
+(IO 2 (BASE 0x0388))
+(IO 3 (BASE 0x0330))
+(IO 4 (BASE 0x0370))
+(INT 0 (IRQ 7 (MODE +E)))
+(DMA 0 (CHANNEL 0))
+(DMA 1 (CHANNEL 3))
+
+Here, note that:
+
+Port Acceptable Range Purpose
+---- ---------------- -------
+IO 0 0x0220 - 0x0280 SB base address, I set to 0 just to be safe.
+IO 1 0x0530 - 0x0F48 MSS base address
+IO 2 0x0388 - 0x03F8 OPL3 base address
+IO 3 0x0300 - 0x0334 MPU base address
+IO 4 0x0100 - 0x0FFE card's own base address for its control I/O ports
+
+The IRQ and DMA values can be any that considered acceptable for a
+MSS. Assuming you've got isapnp all happy, then you should be able to
+do something like the following (which matches up with the isapnp
+configuration above):
+
+insmod mpu401
+insmod ad1848
+insmod opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3
+insmod opl3 io=0x388
+
+Remeber that the opl3sa2 module's io argument is for it's own control
+port, which handles the card's master mixer for volume (on all cards),
+and bass and treble (on SA3 and SAx).
+
+If all goes well an you see no error messages, you should be able to
+start using the sound capabilities of your system. If you get an
+error message while trying to insert the opl3sa2 module, then make
+sure that the values of the various arguments match what you specified
+in your isapnp configuration file, and that there is no conflict with
+another device for an I/O port or interrupt. Checking the contents of
+/proc/ioports and /proc/interrupts can be useful to see if you're
+butting heads with another device.
+
+If you still cannot get the module to load, look at the contents of
+your system log file, usually /var/log/messages. If you see the
+message "No Yamaha audio controller found", then you have a different
+chipset than I've encountered so far. Look for a line in the log file
+that says "opl3sa2.c: chipset version = <some number>". If you want
+me to add support for your card, send me the number from this line and
+any information you have on the make and chipset of your sound card,
+and I may be able to work up something. If you do not see these
+messages, and any of the other messages present in the log are not
+helpful, email me some details and I'll try my best to help.
+
+To set up automatic module loading with kmod, the kernel module loader,
+I currently use the following section in my conf.modules file:
+
+# Sound
+alias char-major-14 opl3sa2
+pre-install opl3sa2 modprobe "-k" "ad1848"
+post-install opl3sa2 modprobe "-k" "opl3"
+options opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3
+options opl3 io=0x388
+
+That's all it currently takes to get an OPL3-SAx card working on my
+system. Once again, if you have any other problems, email me at the
+address listed above.
+
+Scott
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
-#
-# For SMP kernels, set this. We don't want to have this in the config file
-# because it makes re-config very ugly and too many fundamental files depend
-# on "CONFIG_SMP"
-#
-# For UP operations COMMENT THIS OUT, simply setting SMP = 0 won't work
-#
-SMP = 1
-
.EXPORT_ALL_VARIABLES:
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
-ifdef SMP
+ifdef CONFIG_SMP
CFLAGS += -D__SMP__
AFLAGS += -D__SMP__
endif
include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion
@echo -n \#define UTS_VERSION \"\#`cat .version` > .ver
- @if [ -n "$(SMP)" ] ; then echo -n " SMP" >> .ver; fi
+ @if [ -n "$(CONFIG_SMP)" ] ; then echo -n " SMP" >> .ver; fi
@if [ -f .name ]; then echo -n \-`cat .name` >> .ver; fi
@echo ' '`date`'"' >> .ver
@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> .ver
checkconfig:
perl -w scripts/checkconfig.pl `find * -name '*.[hcS]' -print | sort`
+checkhelp:
+ perl -w scripts/checkhelp.pl `find * -name [cC]onfig.in -print`
+
ifdef CONFIGURATION
..$(CONFIGURATION):
@echo
# and SMP Intel boxes - AC - from bits by Michael Chastain
#
-ifdef SMP
+ifdef CONFIG_SMP
genksyms_smp_prefix := -p smp_
else
genksyms_smp_prefix :=
define_bool CONFIG_ALPHA_AVANTI y
fi
+bool 'Symmetric multi-processing support' CONFIG_SMP
+
if [ "$CONFIG_PCI" = "y" ]; then
bool 'PCI quirks' CONFIG_PCI_QUIRKS
if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
endif # GENERIC
-ifdef SMP
+ifdef CONFIG_SMP
O_OBJS += smp.o
endif
EXPORT_SYMBOL(enable_irq);
EXPORT_SYMBOL(disable_irq);
EXPORT_SYMBOL(screen_info);
+EXPORT_SYMBOL(perf_irq);
/* platform dependent support */
EXPORT_SYMBOL(_inb);
}
}
+
+
+static void dummy_perf(unsigned long vector, struct pt_regs *regs)
+{
+ printk(KERN_CRIT "Performance counter interrupt!\n");
+}
+
+void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf;
+
/*
* Dispatch device interrupts.
*/
__restore_flags(flags);
return;
case 4:
- printk("Performance counter interrupt\n");
- break;
+ perf_irq(vector, ®s);
+ return;
default:
printk("Hardware intr %ld %lx? Huh?\n", type, vector);
}
* selected... :-(
*/
layout_all_busses(DEFAULT_IO_BASE, APECS_AND_LCA_DEFAULT_MEM_BASE);
- sio_pci_fixup(noname_map_irq, 0x0b0a0f09);
+ sio_pci_fixup(noname_map_irq, 0x0b0a0f0e);
sio_fixup_irq_levels(sio_collect_irq_levels());
enable_ide(0x26e);
}
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
fi
+bool 'Symmetric multi-processing support' CONFIG_SMP
endmenu
mainmenu_option next_comment
CONFIG_M586=y
# CONFIG_M686 is not set
# CONFIG_MATH_EMULATION is not set
+CONFIG_SMP=y
#
# Loadable module support
# SCSI low-level drivers
#
# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AHA152X is not set
# CONFIG_SCSI_AHA1542 is not set
# CONFIG_SCSI_AHA1740 is not set
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_IN2000 is not set
# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_DMA is not set
# CONFIG_SCSI_EATA_PIO is not set
-# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
CONFIG_SCSI_NCR53C8XX=y
# CONFIG_SCSI_PSI240I is not set
# CONFIG_SCSI_QLOGIC_FAS is not set
# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
# CONFIG_SCSI_SEAGATE is not set
# CONFIG_SCSI_DC390T is not set
# CONFIG_SCSI_T128 is not set
OX_OBJS += apm.o
endif
-ifdef SMP
+ifdef CONFIG_SMP
O_OBJS += io_apic.o smp.o trampoline.o
endif
int pirq_entries [MAX_PIRQS];
int pirqs_enabled;
+void __init ioapic_setup(char *str, int *ints)
+{
+ extern int skip_ioapic_setup; /* defined in arch/i386/kernel/smp.c */
+
+ skip_ioapic_setup = 1;
+}
+
void __init ioapic_pirq_setup(char *str, int *ints)
{
int i, max;
int i;
unsigned long *stack;
int cpu = smp_processor_id();
+ extern char *get_options(char *str, int *ints);
printk("\n%s, CPU %d:\n", str, cpu);
printk("irq: %d [%d %d]\n",
atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]);
printk("bh: %d [%d %d]\n",
atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]);
- stack = (unsigned long *) &str;
+ stack = (unsigned long *) &stack;
for (i = 40; i ; i--) {
unsigned long x = *++stack;
- if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) {
+ if (x > (unsigned long) &get_options && x < (unsigned long) &vsprintf) {
printk("<[%08lx]> ", x);
}
}
unsigned long flags;
spin_lock_irqsave(&irq_controller_lock, flags);
- if (!--irq_desc[irq].depth) {
+ switch (irq_desc[irq].depth) {
+ case 1:
irq_desc[irq].status &= ~IRQ_DISABLED;
irq_desc[irq].handler->enable(irq);
+ /* fall throught */
+ default:
+ irq_desc[irq].depth--;
+ break;
+ case 0:
+ printk("enable_irq() unbalanced from %p\n",
+ __builtin_return_address(0));
}
spin_unlock_irqrestore(&irq_controller_lock, flags);
}
struct hw_interrupt_type *handler; /* handle/enable/disable functions */
struct irqaction *action; /* IRQ action list */
unsigned int depth; /* Disable depth for nested irq disables */
- unsigned int unused[2];
} irq_desc_t;
#define IRQ0_TRAP_VECTOR 0x51
*/
#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
#include <linux/mc146818rtc.h>
#include <asm/i82489.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <asm/pgtable.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
-#include <asm/smp.h>
#include <asm/io.h>
#ifdef CONFIG_MTRR
int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, };
int mp_current_pci_id = 0;
unsigned long mp_lapic_addr = 0;
+int skip_ioapic_setup = 0; /* 1 if "noapic" boot option passed */
/* #define SMP_DEBUG */
}
}
if (ioapics > 1)
+ {
printk("Warning: Multiple IO-APICs not yet supported.\n");
+ printk("Warning: switching to non APIC mode.\n");
+ skip_ioapic_setup=1;
+ }
return num_processors;
}
* Here we can be sure that there is an IO-APIC in the system. Let's
* go and set it up:
*/
- setup_IO_APIC();
+ if (!skip_ioapic_setup)
+ setup_IO_APIC();
smp_done:
}
* (C. Scott Ananian <cananian@alumni.princeton.edu>, Andrew D.
* Balsa <andrebalsa@altern.org>, Philip Gladstone <philip@raptor.com>;
* ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>).
+ * 1998-12-16 Andrea Arcangeli
+ * Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy
+ * because was not accounting lost_ticks. I also removed some ugly
+ * not needed global cli() and where needed I used a disable_irq(0).
*/
/* What about the "updated NTP code" stuff in 2.0 time.c? It's not in
eax -= last_tsc_low; /* tsc_low delta */
/*
- * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient.
- * = (tsc_low delta) / (clocks_per_usec)
- * = (tsc_low delta) / (clocks_per_jiffy / usecs_per_jiffy)
+ * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient
+ * = (tsc_low delta) * (usecs_per_clock)
+ * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy)
*
* Using a mull instead of a divl saves up to 31 clock cycles
* in the critical path.
*/
void do_gettimeofday(struct timeval *tv)
{
+ extern volatile unsigned long lost_ticks;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
*tv = xtime;
tv->tv_usec += do_gettimeoffset();
- if (tv->tv_usec >= 1000000) {
+ if (lost_ticks)
+ tv->tv_usec += lost_ticks * (1000000/HZ);
+ restore_flags(flags);
+ while (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000;
tv->tv_sec++;
}
- restore_flags(flags);
}
void do_settimeofday(struct timeval *tv)
*/
tv->tv_usec -= do_gettimeoffset();
- if (tv->tv_usec < 0) {
+ while (tv->tv_usec < 0) {
tv->tv_usec += 1000000;
tv->tv_sec--;
}
*/
static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- int count, flags;
+ int count;
/* It is important that these two operations happen almost at the
* same time. We do the RDTSC stuff first, since it's faster. To
- * avoid any inconsistencies, we disable interrupts locally.
+ * avoid any inconsistencies, we need interrupts disabled locally.
*/
+
+ /*
+ * Interrupts are just disabled locally since the timer irq has the
+ * SA_INTERRUPT flag set. -arca
+ */
- __save_flags(flags);
- __cli();
/* read Pentium cycle counter */
- __asm__("rdtsc"
- :"=a" (last_tsc_low):: "eax", "edx");
+ __asm__("rdtsc" : "=a" (last_tsc_low) : : "edx");
outb_p(0x00, 0x43); /* latch the count ASAP */
count = ((LATCH-1) - count) * TICK_SIZE;
delay_at_last_interrupt = (count + LATCH/2) / LATCH;
- __restore_flags(flags);
-
+
timer_interrupt(irq, NULL, regs);
}
* misc. keyboard stuff (everything not in adb-bus.c or keyb_m68k.c)
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/kd.h>
if [ "$CONFIG_ALL_PPC" != "y" ];then
define_bool CONFIG_MACH_SPECIFIC y
fi
+
+bool 'Symmetric multi-processing support' CONFIG_SMP
+
endmenu
if [ "$CONFIG_MBX" = "y" ];then
# CONFIG_ALL_PPC is not set
# CONFIG_APUS is not set
# CONFIG_MBX is not set
+# CONFIG_SMP is not set
CONFIG_MACH_SPECIFIC=y
#
endif
endif
-ifdef SMP
+ifdef CONFIG_SMP
O_OBJS += smp.o
endif
O_TARGET = lib.o
O_OBJS = checksum.o string.o strcase.o
-ifdef SMP
+ifdef CONFIG_SMP
O_OBJS += locks.o
endif
# CONFIG_ALL_PPC is not set
# CONFIG_APUS is not set
CONFIG_MBX=y
+# CONFIG_SMP is not set
CONFIG_SERIAL_CONSOLE=y
CONFIG_MACH_SPECIFIC=y
# CONFIG_ALL_PPC is not set
# CONFIG_APUS is not set
# CONFIG_MBX is not set
+# CONFIG_SMP is not set
CONFIG_MACH_SPECIFIC=y
#
# CONFIG_ALL_PPC is not set
# CONFIG_APUS is not set
# CONFIG_MBX is not set
+# CONFIG_SMP is not set
CONFIG_MACH_SPECIFIC=y
#
define_bool CONFIG_VT_CONSOLE y
bool 'Support for AP1000 multicomputer' CONFIG_AP1000
+bool 'Symmetric multi-processing support' CONFIG_SMP
if [ "$CONFIG_AP1000" = "y" ]; then
define_bool CONFIG_NO_KEYBOARD y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_AP1000 is not set
+# CONFIG_SMP is not set
# CONFIG_SUN4 is not set
# CONFIG_PCI is not set
O_OBJS += sun4setup.o
endif
-ifdef SMP
+ifdef CONFIG_SMP
O_OBJS += trampoline.o smp.o sun4m_smp.o sun4d_smp.o
endif
@echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h
@echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h
@echo "" >> asm_offsets.h
- @echo "#ifndef __SMP__" >> asm_offsets.h
+ @echo "#include <linux/config.h>" >> asm_offsets.h
@echo "" >> asm_offsets.h
- @echo "#include <linux/sched.h>" > tmp.c
+ @echo "#ifndef CONFIG_SMP" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#include <linux/config.h>" > tmp.c
+ @echo "#undef CONFIG_SMP" >> tmp.c
+ @echo "#include <linux/sched.h>" >> tmp.c
$(CC) -E tmp.c -o tmp.i
@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/config.h>" >> check_asm.c
+ @echo "#undef CONFIG_SMP" >> check_asm.c
@echo "#include <linux/sched.h>" >> check_asm.c
@echo 'struct task_struct _task;' >> check_asm.c
@echo 'struct mm_struct _mm;' >> check_asm.c
./check_asm >> asm_offsets.h
@rm -f check_asm check_asm.c
@echo "" >> asm_offsets.h
- @echo "#else /* __SMP__ */" >> asm_offsets.h
+ @echo "#else /* CONFIG_SMP */" >> asm_offsets.h
@echo "" >> asm_offsets.h
- @echo "#include <linux/sched.h>" > tmp.c
+ @echo "#include <linux/config.h>" > tmp.c
+ @echo "#undef CONFIG_SMP" >> tmp.c
+ @echo "#define CONFIG_SMP 1" >> tmp.c
+ @echo "#include <linux/sched.h>" >> tmp.c
$(CC) -D__SMP__ -E tmp.c -o tmp.i
@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/config.h>" >> check_asm.c
+ @echo "#undef CONFIG_SMP" >> check_asm.c
+ @echo "#define CONFIG_SMP 1" >> check_asm.c
@echo "#include <linux/sched.h>" >> check_asm.c
@echo 'struct task_struct _task;' >> check_asm.c
@echo 'struct mm_struct _mm;' >> check_asm.c
./check_asm >> asm_offsets.h
@rm -f check_asm check_asm.c
@echo "" >> asm_offsets.h
- @echo "#endif /* __SMP__ */" >> asm_offsets.h
+ @echo "#endif /* CONFIG_SMP */" >> asm_offsets.h
@echo "" >> asm_offsets.h
@echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h
@if test -r $(HPATH)/asm/asm_offsets.h; then \
strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
copy_user.o locks.o atomic.o bitops.o debuglocks.o
-ifdef SMP
+ifdef CONFIG_SMP
OBJS += irqlock.o
endif
bitops.o: bitops.S
$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o bitops.o bitops.S
-ifdef SMP
+ifdef CONFIG_SMP
irqlock.o: irqlock.S
$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o irqlock.o irqlock.S
endif
else
O_OBJS += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o
endif
-ifdef SMP
+ifdef CONFIG_SMP
O_OBJS += nosun4c.o
else
O_OBJS += sun4c.o
# Uncomment this to get spinlock/rwlock debugging on SMP.
# DEBUG_SPINLOCK = 1
-ifdef SMP
+ifdef CONFIG_SMP
ifdef DEBUG_SPINLOCK
CFLAGS += -DSPIN_LOCK_DEBUG
AFLAGS += -DSPIN_LOCK_DEBUG
define_bool CONFIG_VT_CONSOLE y
bool 'Support for AP1000 multicomputer' CONFIG_AP1000
+bool 'Symmetric multi-processing support' CONFIG_SMP
mainmenu_option next_comment
comment 'Console drivers'
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_AP1000 is not set
+# CONFIG_SMP is not set
#
# Console drivers
O_OBJS += ebus.o
endif
-ifdef SMP
+ifdef CONFIG_SMP
O_OBJS += smp.o trampoline.o
endif
@echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h
@echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h
@echo "" >> asm_offsets.h
- @echo "#ifndef __SMP__" >> asm_offsets.h
+ @echo "#include <linux/config.h>" >> asm_offsets.h
@echo "" >> asm_offsets.h
- @echo "#include <linux/sched.h>" > tmp.c
+ @echo "#ifndef CONFIG_SMP" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#include <linux/config.h>" > tmp.c
+ @echo "#undef CONFIG_SMP" >> tmp.c
+ @echo "#include <linux/sched.h>" >> tmp.c
$(CC) -E tmp.c -o tmp.i
@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/config.h>" >> check_asm.c
+ @echo "#undef CONFIG_SMP" >> check_asm.c
@echo "#include <linux/sched.h>" >> check_asm.c
@echo 'struct task_struct _task;' >> check_asm.c
@echo 'struct mm_struct _mm;' >> check_asm.c
./check_asm >> asm_offsets.h
@rm -f check_asm check_asm.c
@echo "" >> asm_offsets.h
- @echo "#else /* __SMP__ */" >> asm_offsets.h
+ @echo "#else /* CONFIG_SMP */" >> asm_offsets.h
@echo "" >> asm_offsets.h
@echo "#ifndef SPIN_LOCK_DEBUG" >>asm_offsets.h
@echo "" >> asm_offsets.h
- @echo "#include <linux/sched.h>" > tmp.c
+ @echo "#include <linux/config.h>" > tmp.c
+ @echo "#undef CONFIG_SMP" >> tmp.c
+ @echo "#define CONFIG_SMP 1" >> tmp.c
+ @echo "#include <linux/sched.h>" >> tmp.c
$(CC) -D__SMP__ -E tmp.c -o tmp.i
@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/config.h>" >> check_asm.c
+ @echo "#undef CONFIG_SMP" >> check_asm.c
+ @echo "#define CONFIG_SMP 1" >> check_asm.c
@echo "#include <linux/sched.h>" >> check_asm.c
@echo 'struct task_struct _task;' >> check_asm.c
@echo 'struct mm_struct _mm;' >> check_asm.c
@echo "#include <linux/sched.h>" > tmp.c
$(CC) -D__SMP__ -DSPIN_LOCK_DEBUG -E tmp.c -o tmp.i
@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/config.h>" >> check_asm.c
+ @echo "#undef CONFIG_SMP" >> check_asm.c
+ @echo "#define CONFIG_SMP 1" >> check_asm.c
@echo "#include <linux/sched.h>" >> check_asm.c
@echo 'struct task_struct _task;' >> check_asm.c
@echo 'struct mm_struct _mm;' >> check_asm.c
@rm -f check_asm check_asm.c
@echo "#endif /* SPIN_LOCK_DEBUG */" >> asm_offsets.h
@echo "" >> asm_offsets.h
- @echo "#endif /* __SMP__ */" >> asm_offsets.h
+ @echo "#endif /* CONFIG_SMP */" >> asm_offsets.h
@echo "" >> asm_offsets.h
@echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h
@if test -r $(HPATH)/asm/asm_offsets.h; then \
** 37 0 Use Zorro II and Chip ram
** 37 1 Use only Zorro II ram
** 37 2 Use only Chip ram
+** 37 4-7 Use memory list entry 1-4 (first is 0)
+** ++jskov: support for 1-4th memory list entry.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
#define MAJOR_NR Z2RAM_MAJOR
+#include <linux/config.h>
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/blk.h>
#include <asm/setup.h>
#include <asm/bitops.h>
#include <asm/amigahw.h>
+#ifdef CONFIG_APUS
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#endif
#include <linux/zorro.h>
+
+extern int num_memory;
+extern struct mem_info memory[NUM_MEMINFO];
+
#define TRUE (1)
#define FALSE (0)
#define Z2MINOR_COMBINED (0)
#define Z2MINOR_Z2ONLY (1)
#define Z2MINOR_CHIPONLY (2)
+#define Z2MINOR_MEMLIST1 (4)
+#define Z2MINOR_MEMLIST2 (5)
+#define Z2MINOR_MEMLIST3 (6)
+#define Z2MINOR_MEMLIST4 (7)
+#define Z2MINOR_COUNT (8) /* Move this down when adding a new minor */
#define Z2RAM_CHUNK1024 ( Z2RAM_CHUNKSIZE >> 10 )
static u_long *z2ram_map = NULL;
static u_long z2ram_size = 0;
-static int z2_blocksizes[3] = { 1024, 1024, 1024 };
-static int z2_sizes[3] = { 0, 0, 0 };
+static int z2_blocksizes[Z2MINOR_COUNT];
+static int z2_sizes[Z2MINOR_COUNT];
static int z2_count = 0;
static int chip_count = 0;
+static int list_count = 0;
static int current_device = -1;
static void
{
z2_count = 0;
chip_count = 0;
+ list_count = 0;
z2ram_size = 0;
+ /* Use a specific list entry. */
+ if (device >= Z2MINOR_MEMLIST1 && device <= Z2MINOR_MEMLIST4) {
+ int index = device - Z2MINOR_MEMLIST1 + 1;
+ unsigned long size, paddr, vaddr;
+
+ if (index >= num_memory) {
+ printk( KERN_ERR DEVICE_NAME
+ ": no such entry in z2ram_map\n" );
+ return -ENOMEM;
+ }
+
+ paddr = memory[index].addr;
+ size = memory[index].size & ~(Z2RAM_CHUNKSIZE-1);
+
+#ifdef __powerpc__
+ /* FIXME: ioremap doesn't build correct memory tables. */
+ {
+ extern void* vmalloc (unsigned long);
+ extern void vfree (void*);
+ vfree(vmalloc (size));
+ }
+
+ vaddr = (unsigned long) __ioremap (paddr, size,
+ _PAGE_WRITETHRU);
+
+#else
+ vaddr = kernel_map (paddr, size, KERNELMAP_FULL_CACHING,
+ NULL);
+#endif
+ z2ram_map =
+ kmalloc((size/Z2RAM_CHUNKSIZE)*sizeof(z2ram_map[0]),
+ GFP_KERNEL);
+ if ( z2ram_map == NULL )
+ {
+ printk( KERN_ERR DEVICE_NAME
+ ": cannot get mem for z2ram_map\n" );
+ return -ENOMEM;
+ }
+
+ while (size) {
+ z2ram_map[ z2ram_size++ ] = vaddr;
+ size -= Z2RAM_CHUNKSIZE;
+ vaddr += Z2RAM_CHUNKSIZE;
+ list_count++;
+ }
+
+ if ( z2ram_size != 0 )
+ printk( KERN_INFO DEVICE_NAME
+ ": using %iK List Entry %d Memory\n",
+ list_count * Z2RAM_CHUNK1024, index );
+ } else
+
switch ( device )
{
case Z2MINOR_COMBINED:
return 0;
}
-static void
+static int
z2_release( struct inode *inode, struct file *filp )
{
-
if ( current_device == -1 )
- return;
+ return 0;
sync_dev( inode->i_rdev );
MOD_DEC_USE_COUNT;
#endif
- return;
+ return 0;
}
static struct file_operations z2_fops =
z2_open, /* open */
NULL, /* flush */
z2_release, /* release */
- block_fsync /* fsync */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
};
__initfunc(int
MAJOR_NR );
return -EBUSY;
}
+
+ {
+ /* Initialize size arrays. */
+ int i;
+
+ for (i = 0; i < Z2MINOR_COUNT; i++) {
+ z2_blocksizes[ i ] = 1024;
+ z2_sizes[ i ] = 0;
+ }
+ }
blk_dev[ MAJOR_NR ].request_fn = DEVICE_REQUEST;
blksize_size[ MAJOR_NR ] = z2_blocksizes;
* the following:
*
* retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
- * while ((retry_count > jiffies) && (! <some condition to wait for))
+ * while (time_before(jiffies, retry_count) && (! <some condition to wait for))
* {
* while (handle_sony_cd_attention())
* ;
reset_drive();
retry_count = jiffies + SONY_RESET_TIMEOUT;
- while ((retry_count > jiffies) && (!is_attention()))
+ while (time_before(jiffies, retry_count) && (!is_attention()))
{
sony_sleep();
}
printk("cdu31a: Resetting drive on error\n");
reset_drive();
retry_count = jiffies + SONY_RESET_TIMEOUT;
- while ((retry_count > jiffies) && (!is_attention()))
+ while (time_before(jiffies, retry_count) && (!is_attention()))
{
sony_sleep();
}
;
/* Wait for the result data to be ready */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
+ while (time_before(jiffies, retry_count) && (is_busy() || (!(is_result_ready()))))
{
sony_sleep();
sti();
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && (is_busy()))
+ while (time_before(jiffies, retry_count) && (is_busy()))
{
sony_sleep();
;
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && (is_busy()))
+ while (time_before(jiffies, retry_count) && (is_busy()))
{
sony_sleep();
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && !(is_data_ready()))
+ while (time_before(jiffies, retry_count) && !(is_data_ready()))
{
while (handle_sony_cd_attention())
;
/* Wait for the status from the drive. */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && !(is_result_ready()))
+ while (time_before(jiffies, retry_count) && !(is_result_ready()))
{
while (handle_sony_cd_attention())
;
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
continue_read_audio_wait:
- while ( (retry_count > jiffies)
- && !(is_data_ready())
+ while (time_before(jiffies, retry_count) && !(is_data_ready())
&& !(is_result_ready() || result_read))
{
while (handle_sony_cd_attention())
{
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && !(is_result_ready()))
+ while (time_before(jiffies, retry_count) && !(is_result_ready()))
{
while (handle_sony_cd_attention())
;
*/
reset_drive();
retry_count = jiffies + SONY_RESET_TIMEOUT;
- while ((retry_count > jiffies) && (!is_attention()))
+ while (time_before(jiffies, retry_count) && (!is_attention()))
{
sony_sleep();
}
if (!buf) buf = &c;
while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) {
- if (jiffies > timeout) return -1;
+ if (time_after(jiffies, timeout)) return -1;
mcdx_delay(stuffp, delay);
}
{
case 4:
sbp_sleep(HZ);
- if (jiffies > timeout_4) gear++;
+ if (time_after(jiffies, timeout_4)) gear++;
msg(DBG_TEA, "CDi_stat_loop_T: long sleep active.\n");
break;
case 3:
sbp_sleep(HZ/10);
- if (jiffies > timeout_3) gear++;
+ if (time_after(jiffies, timeout_3)) gear++;
break;
case 2:
sbp_sleep(HZ/100);
- if (jiffies > timeout_2) gear++;
+ if (time_after(jiffies, timeout_2)) gear++;
break;
case 1:
sbp_sleep(0);
- if (jiffies > timeout_1) gear++;
+ if (time_after(jiffies, timeout_1)) gear++;
}
} while (gear < 5);
return -1;
{
int i,j;
- for(timeout = jiffies + 10*HZ, i=maxtim_data; timeout > jiffies; )
+ for(timeout = jiffies + 10*HZ, i=maxtim_data; time_before(jiffies, timeout); )
{
for ( ;i!=0;i--)
{
st=inb(CDi_status);
if (!(st&s_not_result_ready)) break;
}
- if ((j!=0)||(timeout<=jiffies)) break;
+ if ((j!=0)||time_after_eq(jiffies, timeout)) break;
sbp_sleep(1);
j = 1;
}
- if (timeout<=jiffies) break;
+ if (time_after_eq(jiffies, timeout)) break;
infobuf[i]=inb(CDi_info);
}
#if 000
i=inb(CDi_status);
if (!(i&s_not_result_ready)) break;
}
- if ((j!=0)||(timeout<jiffies)) break;
+ if ((j!=0)||time_after(jiffies, timeout)) break;
sbp_sleep(1);
j = 1;
}
if (!(j&s_not_result_ready)) break;
if (fam0L_drive) if (j&s_attention) break;
}
- if (try != 0 || timeout <= jiffies) break;
+ if (try != 0 || time_after_eq(jiffies, timeout)) break;
if (data_retrying == 0) data_waits++;
data_retrying = 1;
sbp_sleep(1);
if (fam0L_drive)
{
i=maxtim_data;
- for (timeout=jiffies+9*HZ; timeout > jiffies; timeout--)
+ for (timeout=jiffies+9*HZ; time_before(jiffies, timeout); timeout--)
{
for ( ;i!=0;i--)
{
if (!(j&s_not_result_ready)) break;
if (j&s_attention) break;
}
- if (i != 0 || timeout <= jiffies) break;
+ if (i != 0 || time_after_eq(jiffies, timeout)) break;
sbp_sleep(0);
i = 1;
}
{
SBPCD_CLI;
i=maxtim_data;
- for (timeout=jiffies+HZ; timeout > jiffies; timeout--)
+ for (timeout=jiffies+HZ; time_before(jiffies, timeout); timeout--)
{
for ( ;i!=0;i--)
{
if (!(j&s_not_result_ready)) break;
if (j&s_attention) break;
}
- if (i != 0 || timeout <= jiffies) break;
+ if (i != 0 || time_after_eq(jiffies, timeout)) break;
sbp_sleep(0);
i = 1;
}
if (i<0) break;
if (!st_caddy_in) break;
}
- while ((!st_diskok)||(timeout<jiffies));
+ while ((!st_diskok)||time_after(jiffies, timeout));
}
i=SetSpeed();
if (i>=0) D_S[j].CD_changed=1;
/*
* Wait 10ms approx.
*/
- for( timer = jiffies; jiffies <= timer; );
+ for( timer = jiffies; time_before_eq(jiffies, timer); );
if ( (i % 100) == 0 ) printk( "." );
( void )sjcd_check_status();
}
/*
* Wait 10ms approx.
*/
- for( timer = jiffies; jiffies <= timer; );
+ for( timer = jiffies; time_before_eq(jiffies, timer); );
if ( (i % 100) == 0 ) printk( "." );
( void )sjcd_check_status();
}
/*
* Wait 10ms approx.
*/
- for( timer = jiffies; jiffies <= timer; );
+ for( timer = jiffies; time_before_eq(jiffies, timer); );
if ( (i % 100) == 0 ) printk( "." );
( void )sjcd_check_status();
}
SCC_check_open
};
-/*
- * tmp_buf is used as a temporary buffer by serial_write. We need to
- * lock it in case the memcpy_fromfs 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[4096]; /* This is cheating */
-static struct semaphore tmp_buf_sem = MUTEX;
-
/*
* This is used to figure out the divisor speeds and the timeouts
*/
/* If console serial line, then enable interrupts. */
if (info->private->is_cons) {
- printk("mac_SCC: console line %lx; enabling interrupt!\n", info);
+ printk("mac_SCC: console line %d; enabling interrupt!\n", info->line);
write_zsreg(info->private->zs_channel, R1,
(EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
write_zsreg(info->private->zs_channel, R9, (NV | MIE));
* client attached to us asynchronously.
*/
if (info->private->kgdb_channel) {
- printk("mac_SCC: kgdb line %lx; enabling interrupt!\n", info);
+ printk("mac_SCC: kgdb line %d; enabling interrupt!\n", info->line);
kgdb_chaninit(info, 1, info->private->zs_baud);
}
/* Report settings (in m68kserial.c) */
static void SCC_get_serial_info(struct m68k_async_struct * info,
struct serial_struct * retinfo)
{
- struct serial_struct tmp;
-
retinfo->baud_base = info->baud_base;
retinfo->custom_divisor = info->custom_divisor;
}
static int SCC_set_modem_info(struct m68k_async_struct *info,
int new_dtr, int new_rts)
{
- int error;
- unsigned int arg, bits;
+ unsigned int bits;
bits = (new_rts ? RTS: 0) + (new_dtr ? DTR: 0);
info->private->curregs[5] = (info->private->curregs[5] & ~(DTR | RTS)) | bits;
unsigned long arg)
{
int error;
- int retval;
switch (cmd) {
case TIOCSERGETLSR: /* Get line status register */
ZS_CONTROL+ZS_MOVE*n;
zs_channels[n].data = (volatile unsigned char *)ZS_DATA+ZS_MOVE*n;
#else
- zs_channels[n].control = (volatile unsigned char *)
+ zs_channels[n].control = (volatile unsigned char *) /* 2, 0 */
(mac_bi_data.sccbase+ZS_CH_A_FIRST)+ZS_MOVE*n;
- zs_channels[n].data = (volatile unsigned char *)
+ zs_channels[n].data = (volatile unsigned char *) /* 6, 4 */
(mac_bi_data.sccbase+ZS_CH_A_FIRST+ZS_DATA_MOVE)+ZS_MOVE*n;
#endif
zs_soft[n].private = &zs_soft_private[n];
rs_cons_check(struct m68k_async_struct *ss, int channel)
{
int i, o, io;
- static consout_registered = 0;
- static msg_printed = 0;
+ static int consout_registered = 0;
+ static int msg_printed = 0;
i = o = io = 0;
/* rs_init inits the driver */
int mac_SCC_init(void)
{
- int channel, line, nr = 0, i;
+ int channel, line, nr = 0;
unsigned long flags;
struct serial_struct req;
- struct m68k_async_struct *info;
printk("Mac68K Z8530 serial driver version 1.01\n");
for (channel = 0; channel < zs_channels_found; ++channel) {
req.line = channel;
req.type = SER_SCC_MAC;
- req.port = zs_soft[channel].private->zs_channel->control;
+ req.port = (int) zs_soft[channel].private->zs_channel->control;
if ((line = register_serial( &req )) >= 0) {
SCC_init_port( &rs_table[line], req.type, line );
state->flags = ((state->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
info->flags = ((info->flags & ~ASYNC_USR_MASK) |
- (info->flags & ASYNC_USR_MASK));
+ (new_serial.flags & ASYNC_USR_MASK));
state->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
ifeq ($(CONFIG_LANCE),y)
L_OBJS += lance.o
+else
+ ifeq ($(CONFIG_LANCE),m)
+ M_OBJS += lance.o
+ endif
endif
ifeq ($(CONFIG_PCNET32),y)
endif
endif
+ifeq ($(CONFIG_RCPCI),y)
+L_OBJS += rcpci.o
+else
+ ifeq ($(CONFIG_RCPCI),m)
+ M_OBJS += rcpci.o
+ endif
+endif
+
ifeq ($(CONFIG_MACE),y)
L_OBJS += mace.o
endif
wanpipe.o: $(WANPIPE_OBJS)
ld -r -o $@ $(WANPIPE_OBJS)
+
+rcpci.o: rcpci45.o rcmtl.o
+ $(LD) -r -o rcpci.o rcpci45.o rcmtl.o
extern int hplance_probe(struct device *dev);
extern int via_rhine_probe(struct device *dev);
extern int tc515_probe(struct device *dev);
+extern int lance_probe(struct device *dev);
+extern int rcpci_probe(struct device *);
/* Gigabit Ethernet adapters */
extern int yellowfin_probe(struct device *dev);
#ifdef CONFIG_DGRS
{dgrs_probe, 0},
#endif
+#ifdef CONFIG_RCPCI
+ {rcpci_probe, 0},
+#endif
#ifdef CONFIG_VORTEX
{tc59x_probe, 0},
#endif
#ifdef CONFIG_NE2000 /* ISA (use ne2k-pci for PCI cards) */
{ne_probe, 0},
#endif
+#ifdef CONFIG_LANCE /* ISA/VLB (use pcnet32 for PCI cards) */
+ {lance_probe, 0},
+#endif
#ifdef CONFIG_SMC9194
{smc_init, 0},
#endif
#endif
#ifdef CONFIG_SKTR
&& sktr_probe(dev)
+#endif
+#ifdef CONFIG_SMCTR
+ && smctr_probe(dev)
#endif
&& 1 ) {
return 1; /* -ENODEV or -EAGAIN would be more accurate. */
static int max_tx_desc[8] = {0, };
static int max_rx_desc[8] = {0, };
-static const char *version = "acenic.c: v0.18 12/16/98 Jes Sorensen (Jes.Sorensen@cern.ch)\n";
+static const char *version = "acenic.c: v0.19 12/17/98 Jes Sorensen (Jes.Sorensen@cern.ch)\n";
static struct device *root_dev = NULL;
{
long myjif = jiffies + HZ;
- while (myjif > jiffies);
+ while (time_before(jiffies, myjif));
}
#endif
* Wait for the firmware to spin up - max 3 seconds.
*/
myjif = jiffies + 3 * HZ;
- while ((myjif > jiffies) && !ap->fw_running);
+ while (time_before(jiffies, myjif) && !ap->fw_running);
if (!ap->fw_running){
printk(KERN_ERR "%s: firmware NOT running!\n", dev->name);
ace_dump_trace(ap);
ap->tx_full = 0;
dev->tbusy = 0;
mark_bh(NET_BH);
+
+ /*
+ * TX ring is no longer full, aka the
+ * transmitter is working fine - kill timer.
+ */
+ del_timer(&ap->timer);
}
ap->tx_ret_csm = txcsm;
- mod_timer(&ap->timer, jiffies + (5/2*HZ));
}
rxretprd = ap->rx_ret_prd;
#if 0
{ long myjif = jiffies + HZ;
- while (jiffies < myjif);
+ while (time_before(jiffies, myjif));
}
cmd.evt = C_LNK_NEGOTIATION;
* Setup the timer
*/
init_timer(&ap->timer);
- ap->timer.expires = jiffies + (5/2 * HZ);
ap->timer.data = (unsigned long)dev;
ap->timer.function = ace_timer;
- add_timer(&ap->timer);
return 0;
}
if ((idx + 1) % TX_RING_ENTRIES == ap->tx_ret_csm){
ap->tx_full = 1;
set_bit(0, (void*)&dev->tbusy);
+ /*
+ * Queue is full, add timer to detect whether the
+ * transmitter is stuck.
+ */
+ ap->timer.expires = jiffies + (3 * HZ);
+ add_timer(&ap->timer);
}
spin_unlock_irqrestore(&ap->lock, flags);
#define TX_FIFO_THRESH 256 /* Rounded down to 4 byte units. */
#define RX_FIFO_THRESH 1 /* 0-3, 0==32, 64,96, or 3==128 bytes */
-#include <linux/config.h>
#include <linux/version.h> /* Evil, but neccessary */
#ifdef MODULE
#ifdef MODVERSIONS
-#define RCS_ID "$Id: scc.c,v 1.73 1998/01/29 17:38:51 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $"
#define VERSION "3.0"
#define BANNER "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
Incomplete history of z8530drv:
-------------------------------
- 940913 - started to write the driver, rescued most of my own
- code (and Hans Alblas' memory buffer pool concept) from
- an earlier project "sccdrv" which was initiated by
- Guido ten Dolle. Not much of the old driver survived,
- though. The first version I put my hands on was sccdrv1.3
- from August 1993. The memory buffer pool concept
- appeared in an unauthorized sccdrv version (1.5) from
- August 1994.
+ 1994-09-13 started to write the driver, rescued most of my own
+ code (and Hans Alblas' memory buffer pool concept) from
+ an earlier project "sccdrv" which was initiated by
+ Guido ten Dolle. Not much of the old driver survived,
+ though. The first version I put my hands on was sccdrv1.3
+ from August 1993. The memory buffer pool concept
+ appeared in an unauthorized sccdrv version (1.5) from
+ August 1994.
- 950131 - changed copyright notice to GPL without limitations.
+ 1995-01-31 changed copyright notice to GPL without limitations.
.
. <SNIP>
.
- 961005 - New semester, new driver...
+ 1996-10-05 New semester, new driver...
* KISS TNC emulator removed (TTY driver)
* Source moved to drivers/net/
The move to version number 3.0 reflects theses changes.
You can use 'kissbridge' if you need a KISS TNC emulator.
- 961213 - Fixed for Linux networking changes. (G4KLX)
- 970108 - Fixed the remaining problems.
- 970402 - Hopefully fixed the problems with the new *_timer()
- routines, added calibration code.
- 971012 - Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
- 980129 - Small fix to avoid lock-up on initialization
+ 1996-12-13 Fixed for Linux networking changes. (G4KLX)
+ 1997-01-08 Fixed the remaining problems.
+ 1997-04-02 Hopefully fixed the problems with the new *_timer()
+ routines, added calibration code.
+ 1997-10-12 Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
+ 1998-01-29 Small fix to avoid lock-up on initialization
+ 1998-09-29 Fixed the "grouping" bugs, tx_inhibit works again,
+ using dev->tx_queue_len now instead of MAXQUEUE now.
+ 1998-10-21 Postponed the spinlock changes, would need a lot of
+ testing I currently don't have the time to. Softdcd doesn't
+ work.
+ 1998-11-04 Softdcd does not work correctly in DPLL mode, in fact it
+ never did. The DPLL locks on noise, the SYNC unit sees
+ flags that aren't... Restarting the DPLL does not help
+ either, it resynchronizes too slow and the first received
+ frame gets lost.
Thanks to all who contributed to this driver with ideas and bug
reports!
Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org
AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU
Internet: jreuter@poboxes.com
- www : http://www.rat.de/jr
+ www : http://poboxes.com/jreuter/
*/
/* ----------------------------------------------------------------------- */
#undef SCC_LDELAY 1 /* slow it even a bit more down */
-#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+#undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */
-#define MAXSCC 4 /* number of max. supported chips */
-#define BUFSIZE 384 /* must not exceed 4096 */
-#define MAXQUEUE 8 /* number of buffers we queue ourself */
-#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */
+#define SCC_MAXCHIPS 4 /* number of max. supported chips */
+#define SCC_BUFSIZE 384 /* must not exceed 4096 */
+#undef SCC_DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */
/* enable_irq()/disable_irq() */
#undef SCC_DEBUG
-#define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */
+#define SCC_DEFAULT_CLOCK 4915200
+ /* default pclock if nothing is specified */
/* ----------------------------------------------------------------------- */
static struct irqflags { unsigned char used : 1; } Ivec[16];
-static struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */
+static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */
static struct scc_ctrl {
io_port chan_A;
io_port chan_B;
int irq;
-} SCC_ctrl[MAXSCC+1];
+} SCC_ctrl[SCC_MAXCHIPS+1];
static unsigned char Driver_Initialized = 0;
static int Nchips = 0;
/* These provide interrupt save 2-step access to the Z8530 registers */
-extern __inline__ unsigned char InReg(io_port port, unsigned char reg)
+static inline unsigned char InReg(io_port port, unsigned char reg)
{
unsigned long flags;
unsigned char r;
return r;
}
-extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val)
+static inline void OutReg(io_port port, unsigned char reg, unsigned char val)
{
unsigned long flags;
restore_flags(flags);
}
-extern __inline__ void wr(struct scc_channel *scc, unsigned char reg,
+static inline void wr(struct scc_channel *scc, unsigned char reg,
unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
}
-extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
+static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
}
-extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
+static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
}
-#ifdef DISABLE_ALL_INTS
-extern __inline__ void scc_cli(int irq)
+#ifdef SCC_DISABLE_ALL_INTS
+static inline void scc_cli(int irq)
{ cli(); }
-extern __inline__ void scc_sti(int irq)
+static inline void scc_sti(int irq)
{ sti(); }
#else
-static __inline__ void scc_cli(int irq)
+static inline void scc_cli(int irq)
{ disable_irq(irq); }
-static __inline__ void scc_sti(int irq)
+static inline void scc_sti(int irq)
{ enable_irq(irq); }
#endif
/* ******************************************************************** */
-extern __inline__ void scc_lock_dev(struct scc_channel *scc)
+static inline void scc_lock_dev(struct scc_channel *scc)
{
scc->dev->tbusy = 1;
}
-extern __inline__ void scc_unlock_dev(struct scc_channel *scc)
+static inline void scc_unlock_dev(struct scc_channel *scc)
{
scc->dev->tbusy = 0;
}
-extern __inline__ void scc_discard_buffers(struct scc_channel *scc)
+static inline void scc_discard_buffers(struct scc_channel *scc)
{
unsigned long flags;
/* ----> subroutines for the interrupt handlers <---- */
-extern __inline__ void scc_notify(struct scc_channel *scc, int event)
+static inline void scc_notify(struct scc_channel *scc, int event)
{
struct sk_buff *skb;
char *bp;
scc->stat.nospace++;
}
-extern __inline__ void flush_rx_FIFO(struct scc_channel *scc)
+static inline void flush_rx_FIFO(struct scc_channel *scc)
{
int k;
}
}
+static void start_hunt(struct scc_channel *scc)
+{
+ if ((scc->modem.clocksrc != CLK_EXTERNAL))
+ OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+}
/* ----> four different interrupt handlers for Tx, Rx, changing of */
/* DCD/CTS and Rx/Tx errors */
/* Transmitter interrupt handler */
-extern __inline__ void scc_txint(struct scc_channel *scc)
+static inline void scc_txint(struct scc_channel *scc)
{
struct sk_buff *skb;
/* External/Status interrupt handler */
-extern __inline__ void scc_exint(struct scc_channel *scc)
+static inline void scc_exint(struct scc_channel *scc)
{
unsigned char status,changes,chg_and_stat;
if (chg_and_stat & BRK_ABRT) /* Received an ABORT */
flush_rx_FIFO(scc);
+ /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+
+ if ((changes & SYNC_HUNT) && scc->kiss.softdcd)
+ {
+ if (status & SYNC_HUNT)
+ {
+ scc->dcd = 0;
+ flush_rx_FIFO(scc);
+ if ((scc->modem.clocksrc != CLK_EXTERNAL))
+ OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+ } else {
+ scc->dcd = 1;
+ }
+
+ scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON);
+ }
/* DCD: on = start to receive packet, off = ABORT condition */
/* (a successfully received packet generates a special condition int) */
- if(changes & DCD) /* DCD input changed state */
+ if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */
{
if(status & DCD) /* DCD is now ON */
{
- if (scc->modem.clocksrc != CLK_EXTERNAL)
- OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
-
- or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+ start_hunt(scc);
+ scc->dcd = 1;
} else { /* DCD is now OFF */
cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
flush_rx_FIFO(scc);
+ scc->dcd = 0;
}
- if (!scc->kiss.softdcd)
- scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF);
- }
-
- /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
-
- if (changes & SYNC_HUNT)
- {
- if (scc->kiss.softdcd)
- scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON);
- else
- cl(scc,R15,SYNCIE); /* oops, we were too lazy to disable this? */
+ scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
}
#ifdef notdef
/* Receiver interrupt handler */
-extern __inline__ void scc_rxint(struct scc_channel *scc)
+static inline void scc_rxint(struct scc_channel *scc)
{
struct sk_buff *skb;
/* Receive Special Condition interrupt handler */
-extern __inline__ void scc_spint(struct scc_channel *scc)
+static inline void scc_spint(struct scc_channel *scc)
{
unsigned char status;
struct sk_buff *skb;
struct scc_ctrl *ctrl;
int k;
- scc_cli(irq);
-
if (Vector_Latch)
{
for(k=0; k < SCC_IRQTIMEOUT; k++)
OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */
}
- scc_sti(irq);
if (k == SCC_IRQTIMEOUT)
printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
} else
ctrl++;
}
-
- scc_sti(irq);
}
/* ----> set SCC channel speed <---- */
-extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc)
+static inline void set_brg(struct scc_channel *scc, unsigned int tc)
{
cl(scc,R14,BRENABL); /* disable baudrate generator */
wr(scc,R12,tc & 255); /* brg rate LOW */
or(scc,R14,BRENABL); /* enable baudrate generator */
}
-extern __inline__ void set_speed(struct scc_channel *scc)
+static inline void set_speed(struct scc_channel *scc)
{
disable_irq(scc->irq);
/* ----> initialize a SCC channel <---- */
-extern __inline__ void init_brg(struct scc_channel *scc)
+static inline void init_brg(struct scc_channel *scc)
{
wr(scc, R14, BRSRC); /* BRG source = PCLK */
OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */
wr(scc,R7,AUTOEOM);
}
- if((InReg(scc->ctrl,R0)) & DCD) /* DCD is now ON */
+ if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD))
+ /* DCD is now ON */
{
- if (scc->modem.clocksrc != CLK_EXTERNAL)
- or(scc,R14, SEARCH);
-
- or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+ start_hunt(scc);
}
/* enable ABORT, DCD & SYNC/HUNT interrupts */
- wr(scc,R15, BRKIE|TxUIE|DCDIE);
- if (scc->kiss.softdcd)
- or(scc,R15, SYNCIE);
+ wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE));
Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */
{
#ifdef CONFIG_SCC_TRXECHO
cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */
- cl(scc, R15, DCDIE); /* No DCD changes, please */
+ cl(scc, R15, DCDIE|SYNCIE); /* No DCD changes, please */
#endif
set_brg(scc, time_const); /* reprogram baudrate generator */
/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
- or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */
+ /* By popular demand: tx_inhibit */
+ if (scc->kiss.tx_inhibit)
+ {
+ or(scc,R5, TxENAB);
+ scc->wreg[R5] |= RTS;
+ } else {
+ or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */
+ }
} else {
cl(scc,R5,RTS|TxENAB);
/* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
-#ifdef CONFIG_SCC_TRXECHO
- or(scc,R3,RxENABLE|ENT_HM);
- or(scc,R15, DCDIE);
+
+#ifndef CONFIG_SCC_TRXECHO
+ if (scc->kiss.softdcd)
#endif
+ {
+ or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE);
+ start_hunt(scc);
+ }
}
} else {
if (tx)
if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
{
cl(scc, R3, RxENABLE);
- cl(scc, R15, DCDIE);
+ cl(scc, R15, DCDIE|SYNCIE);
}
#endif
-
- or(scc,R5,RTS|TxENAB); /* enable tx */
+ if (scc->kiss.tx_inhibit)
+ {
+ or(scc,R5, TxENAB);
+ scc->wreg[R5] |= RTS;
+ } else {
+ or(scc,R5,RTS|TxENAB); /* enable tx */
+ }
} else {
cl(scc,R5,RTS|TxENAB); /* disable tx */
-#ifdef CONFIG_SCC_TRXECHO
- if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) &&
+#ifndef CONFIG_SCC_TRXECHO
+ scc->kiss.softdcd)
+#else
+ 1)
+#endif
{
- or(scc, R3, RxENABLE|ENT_HM);
- or(scc, R15, DCDIE);
+ or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE);
+ start_hunt(scc);
}
-#endif
}
}
static unsigned char Rand = 17;
-extern __inline__ int is_grouped(struct scc_channel *scc)
+static inline int is_grouped(struct scc_channel *scc)
{
int k;
struct scc_channel *scc2;
if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
return 1;
- if ( (grp1 & RXGROUP) && (scc2->status & DCD) )
+ if ( (grp1 & RXGROUP) && scc2->dcd )
return 1;
}
}
{
Rand = Rand * 17 + 31;
- if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
+ if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
{
scc_start_defer(scc);
scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
{
- int dcd;
-
switch (cmd)
{
case PARAM_TXDELAY: scc->kiss.txdelay=arg; break;
case PARAM_SOFTDCD:
scc->kiss.softdcd=arg;
if (arg)
+ {
or(scc, R15, SYNCIE);
- else
+ cl(scc, R15, DCDIE);
+ start_hunt(scc);
+ } else {
+ or(scc, R15, DCDIE);
cl(scc, R15, SYNCIE);
+ }
break;
case PARAM_SPEED:
break;
case PARAM_HWEVENT:
- dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD));
- scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+ scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
break;
default: return -EINVAL;
scc->tx_wdog.data = (unsigned long) scc;
scc->tx_wdog.function = scc_stop_calibrate;
- scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+ scc->tx_wdog.expires = jiffies + HZ*duration;
add_timer(&scc->tx_wdog);
-
+
+ /* This doesn't seem to work. Why not? */
wr(scc, R6, 0);
wr(scc, R7, pattern);
skb->dev = scc->dev;
skb->protocol = htons(ETH_P_AX25);
skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
netif_rx(skb);
return;
save_flags(flags);
cli();
- if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1)
+ if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len)
{
struct sk_buff *skb_del;
skb_del = __skb_dequeue(&scc->tx_queue);
if (!suser()) return -EPERM;
if (!arg) return -EFAULT;
- if (Nchips >= MAXSCC)
+ if (Nchips >= SCC_MAXCHIPS)
return -EINVAL;
if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
Vector_Latch = hwcfg.vector_latch;
if (hwcfg.clock == 0)
- hwcfg.clock = DEFAULT_CLOCK;
+ hwcfg.clock = SCC_DEFAULT_CLOCK;
-#ifndef DONT_CHECK
+#ifndef SCC_DONT_CHECK
disable_irq(hwcfg.irq);
check_region(scc->ctrl, 1);
Outb(hwcfg.ctrl_a, 0);
- udelay(5);
+ OutReg(hwcfg.ctrl_a, R9, FHWRES);
+ udelay(100);
OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */
udelay(5);
SCC_Info[2*Nchips+chan].option = hwcfg.option;
SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
-#ifdef DONT_CHECK
+#ifdef SCC_DONT_CHECK
printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n",
device_name,
SCC_Info[2*Nchips+chan].data,
if (!suser()) return -EPERM;
if (!arg) return -EINVAL;
- scc->stat.bufsize = BUFSIZE;
+ scc->stat.bufsize = SCC_BUFSIZE;
if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
return -EINVAL;
case SIOCSCCCAL:
if (!suser()) return -EPERM;
- if (!arg || copy_from_user(&cal, arg, sizeof(cal)))
+ if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
return -EINVAL;
scc_start_calibrate(scc, cal.time, cal.pattern);
/* pre-init channel information */
- for (chip = 0; chip < MAXSCC; chip++)
+ for (chip = 0; chip < SCC_MAXCHIPS; chip++)
{
memset((char *) &SCC_Info[2*chip ], 0, sizeof(struct scc_channel));
memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
{
struct tok_info *ti;
short PIOaddr;
- int i;
+ unsigned long i;
PIOaddr = dev->base_addr;
ti=(struct tok_info *) dev->priv;
#endif
outb(0, PIOaddr+ADAPTRESET);
- for (i=jiffies+TR_RESET_INTERVAL; jiffies<=i;); /* wait 50ms */
+ for (i=jiffies+TR_RESET_INTERVAL; time_before_eq(jiffies, i);); /* wait 50ms */
outb(0,PIOaddr+ADAPTRESETREL);
#if !TR_NEWFORMAT
#include <linux/delay.h>
#include <linux/tty.h>
#include <linux/sched.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <asm/ioctls.h>
#include <linux/delay.h>
#include <linux/tty.h>
#include <linux/sched.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <asm/ioctls.h>
#include <linux/malloc.h>
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/config.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/delay.h>
#include <linux/tty.h>
#include <linux/sched.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <asm/ioctls.h>
#include <linux/delay.h>
#include <linux/malloc.h>
#include <linux/init.h>
-#include <linux/config.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/dma.h>
-/* lance.c: An AMD LANCE ethernet driver for linux. */
+/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */
/*
- Written 1993,1994,1995 by Donald Becker.
+ Written/copyright 1993-1998 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
of the GNU Public License, incorporated herein by reference.
This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
- with most other LANCE-based bus-master (NE2100 clone) ethercards.
+ with most other LANCE-based bus-master (NE2100/NE2500) ethercards.
The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
-
Fixing alignment problem with 1.3.* kernel and some minor changes
by Andrey V. Savochkin, 1996.
But I should to inform you that I'm not an expert in the LANCE card
and it may occurs that you will receive no answer on your mail
to Donald Becker. I didn't receive any answer on all my letters
- to him. Who knows why... But may be you are more lucky? ;-)
+ to him. Who knows why... But may be you are more lucky? ;->
SAW
- Fixed 7990 autoIRQ failure and reversed unneeded alignment. 8/20/96 djb
+
+ Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
+ - added support for Linux/Alpha, but removed most of it, because
+ it worked only for the PCI chip.
+ - added hook for the 32bit lance driver
+ - added PCnetPCI II (79C970A) to chip table
+ Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
+ - hopefully fix above so Linux/Alpha can use ISA cards too.
+ 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb
+ v1.12 10/27/97 Module support -djb
+ v1.14 2/3/98 Module support modified, made PCI support optional -djb
+
+ Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from
+ the 2.1 version of the old driver - Alan Cox
*/
-static const char *version = "lance.c:v1.09 Aug 20 1996 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
+static const char *version = "lance.c:v1.14ac 1998/11/20 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-static unsigned int lance_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0};
-void lance_probe1(int ioaddr);
-
-#ifdef HAVE_DEVLIST
-struct netdev_entry lance_drv =
-{"lance", lance_probe1, LANCE_TOTAL_SIZE, lance_portlist};
-#endif
+static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0};
+int lance_probe(struct device *dev);
+int lance_probe1(struct device *dev, int ioaddr, int irq, int options);
#ifdef LANCE_DEBUG
int lance_debug = LANCE_DEBUG;
probed for by enabling each free DMA channel in turn and checking if
initialization succeeds.
-The HP-J2405A board is an exception: with this board it's easy to read the
+The HP-J2405A board is an exception: with this board it is easy to read the
EEPROM-set values for the base, IRQ, and DMA. (Of course you must already
_know_ the base address -- that field is for writing the EEPROM.)
the 'lp->tx_full' flag.
The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
we can't avoid the interrupt overhead by having the Tx routine reap the Tx
stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
+the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
tx_full and tbusy flags.
*/
-/* Memory accessed from LANCE card must be aligned on 8-byte boundaries.
- But we can't believe that kmalloc()'ed memory satisfies it. -- SAW */
-#define LANCE_KMALLOC(x) \
- ((void *) (((unsigned long)kmalloc((x)+7, GFP_DMA | GFP_KERNEL)+7) & ~7))
-
-/*
- * Changes:
- * Thomas Bogendoerfer (tsbogend@alpha.franken.de):
- * - added support for Linux/Alpha, but removed most of it, because
- * it worked only for the PCI chip.
- * - added hook for the 32bit lance driver
- * - added PCnetPCI II (79C970A) to chip table
- * - made 32bit driver standalone
- * - changed setting of autoselect bit
- *
- * Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
- * - hopefully fix above so Linux/Alpha can use ISA cards too.
- */
-
/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
Reasonable default values are 16 Tx buffers, and 16 Rx buffers.
- That translates to 4 and 4 (16 == 2^^4). */
+ That translates to 4 and 4 (16 == 2^^4).
+ This is a compile-time option for efficiency.
+ */
#ifndef LANCE_LOG_TX_BUFFERS
#define LANCE_LOG_TX_BUFFERS 4
#define LANCE_LOG_RX_BUFFERS 4
const char *name;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ /* The addresses of receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
unsigned long rx_buffs; /* Address of Rx and Tx buffers. */
/* Tx low-memory "bounce buffer" address. */
char (*tx_bounce_buffs)[PKT_BUF_SZ];
#define LANCE_MUST_REINIT_RING 0x00000004
#define LANCE_MUST_UNRESET 0x00000008
#define LANCE_HAS_MISSED_FRAME 0x00000010
-#define PCNET32_POSSIBLE 0x00000020
/* A mapping from the chip ID number to the part number and features.
These are from the datasheets -- in real life the '970 version
LANCE_HAS_MISSED_FRAME},
{0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
+ LANCE_HAS_MISSED_FRAME},
/* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
it the PCnet32. */
{0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
+ LANCE_HAS_MISSED_FRAME},
{0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */
LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
- {0x2623, "PCnet/FAST 79C971", /* 79C971 PCInetFAST. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
- {0x2624, "PCnet/FAST+ 79C972", /* 79C972 PCInetFAST+. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
+ LANCE_HAS_MISSED_FRAME},
{0x0, "PCnet (unknown)",
LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
LANCE_HAS_MISSED_FRAME},
static int lance_open(struct device *dev);
static int lance_open_fail(struct device *dev);
-static void lance_init_ring(struct device *dev);
+static void lance_init_ring(struct device *dev, int mode);
static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
static int lance_rx(struct device *dev);
static void lance_interrupt(int irq, void *dev_id, struct pt_regs *regs);
\f
-/* This lance probe is unlike the other board probes in 1.0.*. The LANCE may
- have to allocate a contiguous low-memory region for bounce buffers.
- This requirement is satisfied by having the lance initialization occur
- before the memory management system is started, and thus well before the
- other probes. */
+#ifdef MODULE
+#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */
+#define IF_NAMELEN 8 /* # of chars for storing dev->name */
+
+static int io[MAX_CARDS] = { 0, };
+static int dma[MAX_CARDS] = { 0, };
+static int irq[MAX_CARDS] = { 0, };
-__initfunc(int lance_init(void))
+static char ifnames[MAX_CARDS][IF_NAMELEN] = { {0, }, };
+static struct device dev_lance[MAX_CARDS] =
+{{
+ 0, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL}};
+
+int init_module(void)
{
- int *port;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
+ struct device *dev = &dev_lance[this_dev];
+ dev->name = ifnames[this_dev];
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->dma = dma[this_dev];
+ dev->init = lance_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only complain once */
+ printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
+ return -EPERM;
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "lance.c: No PCnet/LANCE card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
- if (virt_to_bus(high_memory) <= 16*1024*1024)
+void cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
+ struct device *dev = &dev_lance[this_dev];
+ if (dev->priv != NULL) {
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_dma(dev->dma);
+ release_region(dev->base_addr, LANCE_TOTAL_SIZE);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other
+ board probes now that kmalloc() can allocate ISA DMA-able regions.
+ This also allows the LANCE driver to be used as a module.
+ */
+int lance_probe(struct device *dev)
+{
+ int *port, result;
+
+ if (high_memory <= 16*1024*1024)
lance_need_isa_bounce_buffers = 0;
-#if defined(CONFIG_PCI) && !(defined(CONFIG_PCNET32) || defined(CONFIG_PCNET32_MODULE))
+#if defined(CONFIG_PCI)
if (pci_present()) {
- struct pci_dev *pdev = NULL;
+ struct pci_dev *pdev = NULL;
if (lance_debug > 1)
- printk("lance.c: PCI is present, checking for devices...\n");
+ printk("lance.c: PCI bios is present, checking for devices...\n");
+
while ((pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, pdev))) {
unsigned int pci_ioaddr;
unsigned short pci_command;
}
printk("Found PCnet/PCI at %#x, irq %d.\n",
pci_ioaddr, pci_irq_line);
- lance_probe1(pci_ioaddr);
+ result = lance_probe1(dev, pci_ioaddr, pci_irq_line, 0);
pci_irq_line = 0;
+ if (!result) return 0;
}
}
#endif /* defined(CONFIG_PCI) */
char offset15, offset14 = inb(ioaddr + 14);
if ((offset14 == 0x52 || offset14 == 0x57) &&
- ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44))
- lance_probe1(ioaddr);
+ ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44)) {
+ result = lance_probe1(dev, ioaddr, 0, 0);
+ if ( !result ) return 0;
+ }
}
}
- return 0;
+ return -ENODEV;
}
-__initfunc(void lance_probe1(int ioaddr))
+__initfunc(int lance_probe1(struct device *dev, int ioaddr, int irq, int options))
{
- struct device *dev;
struct lance_private *lp;
short dma_channels; /* Mark spuriously-busy DMA channels */
int i, reset_val, lance_version;
outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
if (inw(ioaddr+LANCE_DATA) != 0x0004)
- return;
+ return -ENODEV;
/* Get the version of the chip. */
outw(88, ioaddr+LANCE_ADDR);
if (lance_debug > 2)
printk(" LANCE chip version is %#x.\n", chip_version);
if ((chip_version & 0xfff) != 0x003)
- return;
+ return -ENODEV;
chip_version = (chip_version >> 12) & 0xffff;
for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) {
if (chip_table[lance_version].id_number == chip_version)
break;
}
}
-
-#if defined(CONFIG_PCNET32) || defined (CONFIG_PCNET32_MODULE)
- /*
- * if pcnet32 is configured and the chip is capable of 32bit mode
- * leave the card alone
- */
- if (chip_table[lance_version].flags & PCNET32_POSSIBLE)
- return;
-#endif
- dev = init_etherdev(0, 0);
+ /* We can't use init_etherdev() to allocate dev->priv because it must
+ a ISA DMA-able region. */
+ dev = init_etherdev(dev, 0);
dev->open = lance_open_fail;
chipname = chip_table[lance_version].name;
printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name);
/* Make certain the data structures used by the LANCE are aligned and DMAble. */
+
lp = (struct lance_private *)(((unsigned long)kmalloc(sizeof(*lp)+7,
GFP_DMA | GFP_KERNEL)+7) & ~7);
if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
outw(0x0000, ioaddr+LANCE_ADDR);
inw(ioaddr+LANCE_ADDR);
- if (pci_irq_line) {
+ if (irq) { /* Set iff PCI card. */
dev->dma = 4; /* Native bus-master, no DMA channel needed. */
- dev->irq = pci_irq_line;
+ dev->irq = irq;
} else if (hp_builtin) {
static const char dma_tbl[4] = {3, 5, 6, 0};
static const char irq_tbl[4] = {3, 4, 5, 9};
printk(", probed IRQ %d", dev->irq);
else {
printk(", failed to detect IRQ line.\n");
- return;
+ return -ENODEV;
}
/* Check for the initialization done bit, 0x0100, which means
} else if (dev->dma) {
if (request_dma(dev->dma, chipname)) {
printk("DMA %d allocation failed.\n", dev->dma);
- return;
+ return -ENODEV;
} else
printk(", assigned DMA %d.\n", dev->dma);
} else { /* OK, we have to auto-DMA. */
}
if (i == 4) { /* Failure: bail. */
printk("DMA detection failed.\n");
- return;
+ return -ENODEV;
}
}
dev->irq = autoirq_report(4);
if (dev->irq == 0) {
printk(" Failed to detect the 7990 IRQ line.\n");
- return;
+ return -ENODEV;
}
printk(" Auto-IRQ detected IRQ%d.\n", dev->irq);
}
/* Turn on auto-select of media (10baseT or BNC) so that the user
can watch the LEDs even if the board isn't opened. */
outw(0x0002, ioaddr+LANCE_ADDR);
- /* set autoselect and clean xmausel */
- outw((inw(ioaddr+LANCE_BUS_IF) & 0xfffe) | 0x0002, ioaddr+LANCE_BUS_IF);
+ /* Don't touch 10base2 power bit. */
+ outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
}
if (lance_debug > 0 && did_version++ == 0)
dev->get_stats = lance_get_stats;
dev->set_multicast_list = set_multicast_list;
- return;
+ return 0;
}
static int
return -EAGAIN;
}
+ MOD_INC_USE_COUNT;
+
/* We used to allocate DMA here, but that was silly.
DMA lines can't be shared! We now permanently allocate them. */
if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
/* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
outw(0x0002, ioaddr+LANCE_ADDR);
- /* set autoselect and clean xmausel */
- outw((inw(ioaddr+LANCE_BUS_IF) & 0xfffe) | 0x0002, ioaddr+LANCE_BUS_IF);
- }
+ /* Only touch autoselect bit. */
+ outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
+ }
if (lance_debug > 1)
printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
(u32) virt_to_bus(lp->rx_ring),
(u32) virt_to_bus(&lp->init_block));
- lance_init_ring(dev);
+ lance_init_ring(dev, GFP_KERNEL);
/* Re-initialize the LANCE, and start it when done. */
outw(0x0001, ioaddr+LANCE_ADDR);
outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
/* Initialize the LANCE Rx and Tx rings. */
static void
-lance_init_ring(struct device *dev)
+lance_init_ring(struct device *dev, int gfp)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int i;
lp->dirty_rx = lp->dirty_tx = 0;
for (i = 0; i < RX_RING_SIZE; i++) {
- lp->rx_ring[i].base = (u32)virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
+ struct sk_buff *skb;
+ void *rx_buff;
+
+ skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp);
+ lp->rx_skbuff[i] = skb;
+ if (skb) {
+ skb->dev = dev;
+ rx_buff = skb->tail;
+ } else
+ rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp);
+ if (rx_buff == NULL)
+ lp->rx_ring[i].base = 0;
+ else
+ lp->rx_ring[i].base = (u32)virt_to_bus(rx_buff) | 0x80000000;
lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
}
/* The Tx buffer address is filled in as needed, but we do need to clear
the upper ownership bit. */
for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_skbuff[i] = 0;
lp->tx_ring[i].base = 0;
}
if (must_reinit ||
(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
lance_purge_tx_ring(dev);
- lance_init_ring(dev);
+ lance_init_ring(dev, GFP_ATOMIC);
}
outw(0x0000, dev->base_addr + LANCE_ADDR);
outw(csr0_bits, dev->base_addr + LANCE_DATA);
memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len);
lp->tx_ring[entry].base =
((u32)virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
- dev_kfree_skb (skb);
+ dev_kfree_skb(skb);
} else {
lp->tx_skbuff[entry] = skb;
lp->tx_ring[entry].base = ((u32)virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
}
lp->cur_tx++;
- lp->stats.tx_bytes+=skb->len;
-
+ lp->stats.tx_bytes += skb->len;
+
/* Trigger an immediate send poll. */
outw(0x0000, ioaddr+LANCE_ADDR);
outw(0x0048, ioaddr+LANCE_DATA);
ioaddr = dev->base_addr;
lp = (struct lance_private *)dev->priv;
if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n", dev->name);
dev->interrupt = 1;
{
int ioaddr = dev->base_addr;
struct lance_private *lp = (struct lance_private *)dev->priv;
+ int i;
dev->start = 0;
dev->tbusy = 1;
disable_dma(dev->dma);
release_dma_lock(flags);
}
-
free_irq(dev->irq, dev);
+ /* Free all the skbuffs in the Rx and Tx queues. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = lp->rx_skbuff[i];
+ lp->rx_skbuff[i] = 0;
+ lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
+ if (skb)
+ dev_kfree_skb(skb);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i])
+ dev_kfree_skb(lp->tx_skbuff[i]);
+ lp->tx_skbuff[i] = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
return 0;
}
}
-\f
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c"
- * c-indent-level: 4
- * tab-width: 4
- * End:
- */
#endif
#ifdef CONFIG_HIPPI
+#define MAX_HIP_CARDS 4
+static struct device *hipdev_index[MAX_HIP_CARDS];
+
static int hippi_change_mtu(struct device *dev, int new_mtu)
{
/*
struct device *init_hippi_dev(struct device *dev, int sizeof_priv)
{
- struct device *tmp_dev; /* pointer to a device structure */
+ int new_device = 0;
+ int i;
- /* Find next free HIPPI entry */
+ /* Use an existing correctly named device in Space.c:dev_base. */
+ if (dev == NULL) {
+ int alloc_size = sizeof(struct device) + sizeof("hip%d ")
+ + sizeof_priv + 3;
+ struct device *cur_dev;
+ char pname[8];
- for (tmp_dev = dev; tmp_dev != NULL; tmp_dev = tmp_dev->next)
- if ((strncmp(tmp_dev->name, "hip", 3) == 0) &&
- (tmp_dev->base_addr == 0))
- break;
+ for (i = 0; i < MAX_HIP_CARDS; ++i)
+ if (hipdev_index[i] == NULL) {
+ sprintf(pname, "hip%d", i);
+ for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+ if (strcmp(pname, cur_dev->name) == 0) {
+ dev = cur_dev;
+ dev->init = NULL;
+ sizeof_priv = (sizeof_priv + 3) & ~3;
+ dev->priv = sizeof_priv
+ ? kmalloc(sizeof_priv, GFP_KERNEL)
+ : NULL;
+ if (dev->priv) memset(dev->priv, 0, sizeof_priv);
+ goto hipfound;
+ }
+ }
- if (tmp_dev == NULL)
- {
- printk("Could not find free HIPPI device structure.\n");
- return NULL;
+ alloc_size &= ~3; /* Round to dword boundary. */
+
+ dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL);
+ memset(dev, 0, alloc_size);
+ if (sizeof_priv)
+ dev->priv = (void *) (dev + 1);
+ dev->name = sizeof_priv + (char *)(dev + 1);
+ new_device = 1;
}
-
- tmp_dev->init = NULL;
- sizeof_priv = (sizeof_priv + 3) & ~3;
- tmp_dev->priv = sizeof_priv ? kmalloc(sizeof_priv, GFP_KERNEL) : NULL;
- if (tmp_dev->priv)
- memset(dev->priv, 0, sizeof_priv);
+hipfound: /* From the double loop above. */
- /* Initialize remaining device structure information */
+ if (dev->name &&
+ ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
+ for (i = 0; i < MAX_HIP_CARDS; ++i)
+ if (hipdev_index[i] == NULL) {
+ sprintf(dev->name, "hip%d", i);
+ hipdev_index[i] = dev;
+ break;
+ }
+ }
- hippi_setup(tmp_dev);
- return tmp_dev;
+ hippi_setup(dev);
+
+ if (new_device)
+ register_netdevice(dev);
+
+ return dev;
}
static int hippi_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
#ifdef CONFIG_HIPPI
void hippi_setup(struct device *dev)
{
+ int i;
+
+ if (dev->name && (strncmp(dev->name, "hip", 3) == 0)) {
+ i = simple_strtoul(dev->name + 3, NULL, 0);
+ if (hipdev_index[i] == NULL) {
+ hipdev_index[i] = dev;
+ }
+ else if (dev != hipdev_index[i]) {
+ printk("hippi_setup: Ouch! Someone else took %s\n",
+ dev->name);
+ }
+ }
+
dev->set_multicast_list = NULL;
dev->change_mtu = hippi_change_mtu;
dev->hard_header = hippi_header;
return 0;
new_count = slhc_uncompress(ppp->slcomp, skb->data + PPP_HDRLEN,
skb->len - PPP_HDRLEN);
- if (new_count < 0) {
+ if (new_count<=0) {
if (ppp->flags & SC_DEBUG)
printk(KERN_NOTICE
"ppp: error in VJ decompression\n");
--- /dev/null
+/*
+** *************************************************************************
+**
+**
+** R C I F . H
+**
+**
+** RedCreek InterFace include file.
+**
+** ---------------------------------------------------------------------
+** --- Copyright (c) 1998, RedCreek Communications Inc. ---
+** --- All rights reserved. ---
+** ---------------------------------------------------------------------
+**
+** File Description:
+**
+** Header file private ioctl commands.
+**
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+** *************************************************************************
+*/
+
+#ifndef RCIF_H
+#define RCIF_H
+
+/* The following protocol revision # should be incremented every time
+ a new protocol or new structures are used in this file. */
+int USER_PROTOCOL_REV = 1; /* used to track different protocol revisions */
+
+/* define a single TCB & buffer */
+typedef struct /* a single buffer */
+{
+ U32 context; /* context */
+ U32 scount; /* segment count */
+ U32 size; /* segment size */
+ U32 addr; /* segment physical address */
+}
+__attribute__((packed))
+singleB, *psingleB ;
+typedef struct /* a single TCB */
+{
+ /*
+ ** +-----------------------+
+ ** | 1 | one buffer in the TCB
+ ** +-----------------------+
+ ** | <user's Context> | user's buffer reference
+ ** +-----------------------+
+ ** | 1 | one segment buffer
+ ** +-----------------------+ _
+ ** | <buffer size> | size \
+ ** +-----------------------+ \ segment descriptor
+ ** | <physical address> | physical address of buffer /
+ ** +-----------------------+ _/
+ */
+ U32 bcount; /* buffer count */
+ singleB b; /* buffer */
+
+}
+__attribute__((packed))
+singleTCB, *psingleTCB;
+
+/*
+ When adding new entries, please add all 5 related changes, since
+ it helps keep everything consistent:
+ 1) User structure entry
+ 2) User data entry
+ 3) Structure short-cut entry
+ 4) Data short-cut entry
+ 5) Command identifier entry
+
+ For Example ("GETSPEED"):
+ 1) struct RCgetspeed_tag { U32 LinkSpeedCode; } RCgetspeed;
+ 2) struct RCgetspeed_tag *getspeed;
+ 3) #define RCUS_GETSPEED data.RCgetspeed;
+ 4) #define RCUD_GETSPEED _RC_user_data.getspeed
+ 5) #define RCUC_GETSPEED 0x02
+
+ Notes for the "GETSPEED" entry, above:
+ 1) RCgetspeed - RC{name}
+ RCgetspeed_tag - RC{name}_tag
+ LinkSpeedCode - create any structure format desired (not too large,
+ since memory will be unioned with all other entries)
+ 2) RCgetspeed_tag - RC{name}_tag chosen in #1
+ getspeed - arbitrary name (ptr to structure in #1)
+ 3) RCUS_GETSPEED - RCUS_{NAME} ("NAME" & "name" do not have to the same)
+ data.RCgetspeed - data.RC{name} ("RC{name}" from #1)
+ 4) RCUD_GETSPEED - _RC_user_data.getspeed ("getspeed" from #2)
+ 5) RCUC_GETSPEED - unique hex identifier entry.
+*/
+
+typedef struct RC_user_tag RCuser_struct;
+
+/* 1) User structure entry */
+struct RC_user_tag
+{
+ int cmd;
+ union
+ {
+ /* GETINFO structure */
+ struct RCgetinfo_tag {
+ unsigned long int mem_start;
+ unsigned long int mem_end;
+ unsigned long int base_addr;
+ unsigned char irq;
+ unsigned char dma;
+ unsigned char port;
+ } RCgetinfo; /* <---- RCgetinfo */
+
+ /* GETSPEED structure */
+ struct RCgetspeed_tag {
+ U32 LinkSpeedCode;
+ } RCgetspeed; /* <---- RCgetspeed */
+
+ /* GETFIRMWAREVER structure */
+ #define FirmStringLen 80
+ struct RCgetfwver_tag {
+ U8 FirmString[FirmStringLen];
+ } RCgetfwver; /* <---- RCgetfwver */
+
+ /* GETIPANDMASK structure */
+ struct RCgetipnmask_tag {
+ U32 IpAddr;
+ U32 NetMask;
+ } RCgetipandmask; /* <---- RCgetipandmask */
+
+ /* GETMAC structure */
+ #define MAC_SIZE 10
+ struct RCgetmac_tag {
+ U8 mac[MAC_SIZE];
+ } RCgetmac; /* <---- RCgetmac */
+
+ /* GETLINKSTATUS structure */
+ struct RCgetlnkstatus_tag {
+ U32 ReturnStatus;
+ } RCgetlnkstatus; /* <---- RCgetlnkstatus */
+
+ /* GETLINKSTATISTICS structure */
+ struct RCgetlinkstats_tag {
+ RCLINKSTATS StatsReturn;
+ } RCgetlinkstats; /* <---- RCgetlinkstats */
+
+ /* DEFAULT structure (when no command was recognized) */
+ struct RCdefault_tag {
+ int rc;
+ } RCdefault; /* <---- RCdefault */
+
+ } data;
+
+}; /* struct RC_user_tag { ... } */
+
+/* 2) User data entry */
+/* RCUD = RedCreek User Data */
+union RC_user_data_tag { /* structure tags used are taken from RC_user_tag structure above */
+ struct RCgetinfo_tag *getinfo;
+ struct RCgetspeed_tag *getspeed;
+ struct RCgetfwver_tag *getfwver;
+ struct RCgetipnmask_tag *getipandmask;
+ struct RCgetmac_tag *getmac;
+ struct RCgetlnkstatus_tag *getlinkstatus;
+ struct RCgetlinkstats_tag *getlinkstatistics;
+ struct RCdefault_tag *rcdefault;
+} _RC_user_data; /* declare as a global, so the defines below will work */
+
+/* 3) Structure short-cut entry */
+/* define structure short-cuts */ /* structure names are taken from RC_user_tag structure above */
+#define RCUS_GETINFO data.RCgetinfo;
+#define RCUS_GETSPEED data.RCgetspeed;
+#define RCUS_GETFWVER data.RCgetfwver;
+#define RCUS_GETIPANDMASK data.RCgetipandmask;
+#define RCUS_GETMAC data.RCgetmac;
+#define RCUS_GETLINKSTATUS data.RCgetlnkstatus;
+#define RCUS_GETLINKSTATISTICS data.RCgetlinkstats;
+#define RCUS_DEFAULT data.RCdefault;
+
+/* 4) Data short-cut entry */
+/* define data short-cuts */ /* pointer names are from RC_user_data_tag union (just below RC_user_tag) */
+#define RCUD_GETINFO _RC_user_data.getinfo
+#define RCUD_GETSPEED _RC_user_data.getspeed
+#define RCUD_GETFWVER _RC_user_data.getfwver
+#define RCUD_GETIPANDMASK _RC_user_data.getipandmask
+#define RCUD_GETMAC _RC_user_data.getmac
+#define RCUD_GETLINKSTATUS _RC_user_data.getlinkstatus
+#define RCUD_GETLINKSTATISTICS _RC_user_data.getlinkstatistics
+#define RCUD_DEFAULT _RC_user_data.rcdefault
+
+/* 5) Command identifier entry */
+/* define command identifiers */
+#define RCUC_GETINFO 0x01
+#define RCUC_GETSPEED 0x02
+#define RCUC_GETFWVER 0x03
+#define RCUC_GETIPANDMASK 0x04
+#define RCUC_GETMAC 0x05
+#define RCUC_GETLINKSTATUS 0x06
+#define RCUC_GETLINKSTATISTICS 0x07
+#define RCUC_DEFAULT 0xff
+
+/* define ioctl commands to use, when talking to RC 45/PCI driver */
+#define RCU_PROTOCOL_REV SIOCDEVPRIVATE
+#define RCU_COMMAND SIOCDEVPRIVATE+1
+
+/*
+ Intended use for the above defines is shown below (GETINFO, as this example):
+
+ RCuser_struct RCuser; // declare RCuser structure
+ struct ifreq ifr; // declare an interface request structure
+
+ RCuser.cmd = RCUC_GETINFO; // set user command to GETINFO
+ ifr->ifr_data = (caddr_t) &RCuser; // set point to user structure
+
+ sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); // get a socket
+ ioctl(sock, RCU_COMMAND, &ifr); // do ioctl on socket
+
+ RCUD_GETINFO = &RCuser.RCUS_GETINFO; // set data pointer for GETINFO
+
+ // print results
+ printf("memory 0x%lx-0x%lx, base address 0x%x, irq 0x%x\n",
+ RCUD_GETINFO->mem_start, RCUD_GETINFO->mem_end,
+ RCUD_GETINFO->base_addr, RCUD_GETINFO->irq);
+*/
+
+#endif /* RCIF_H */
+
--- /dev/null
+/*
+** *************************************************************************
+**
+**
+** R C M T L . C $Revision: 1.1 $
+**
+**
+** RedCreek Message Transport Layer program module.
+**
+** ---------------------------------------------------------------------
+** --- Copyright (c) 1997-1998, RedCreek Communications Inc. ---
+** --- All rights reserved. ---
+** ---------------------------------------------------------------------
+**
+** File Description:
+**
+** Host side message transport layer.
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+***************************************************************************/
+
+#undef DEBUG
+
+#define RC_LINUX_MODULE
+#include "rcmtl.h"
+
+#define dprintf kprintf
+
+extern int printk(const char * fmt, ...);
+
+ /* RedCreek LAN device Target ID */
+#define LAN_TARGET_ID 0x10
+ /* RedCreek's OSM default LAN receive Initiator */
+#define DEFAULT_RECV_INIT_CONTEXT 0xA17
+
+
+/*
+** message structures
+*/
+
+#define TID_SZ 12
+#define FUNCTION_SZ 8
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+
+#define TRL_SINGLE_FIXED_LENGTH 0x00
+#define TRL_SINGLE_VARIABLE_LENGTH 0x40
+#define TRL_MULTIPLE_FIXED_LENGTH 0x80
+
+/* LAN Class specific functions */
+
+#define LAN_PACKET_SEND 0x3B
+#define LAN_SDU_SEND 0x3D
+#define LAN_RECEIVE_POST 0x3E
+#define LAN_RESET 0x35
+#define LAN_SHUTDOWN 0x37
+
+/* Private Class specfic function */
+#define RC_PRIVATE 0xFF
+
+/* RC Executive Function Codes. */
+
+#define RC_CMD_ADAPTER_ASSIGN 0xB3
+#define RC_CMD_ADAPTER_READ 0xB2
+#define RC_CMD_ADAPTER_RELEASE 0xB5
+#define RC_CMD_BIOS_INFO_SET 0xA5
+#define RC_CMD_BOOT_DEVICE_SET 0xA7
+#define RC_CMD_CONFIG_VALIDATE 0xBB
+#define RC_CMD_CONN_SETUP 0xCA
+#define RC_CMD_DEVICE_ASSIGN 0xB7
+#define RC_CMD_DEVICE_RELEASE 0xB9
+#define RC_CMD_HRT_GET 0xA8
+#define RC_CMD_ADAPTER_CLEAR 0xBE
+#define RC_CMD_ADAPTER_CONNECT 0xC9
+#define RC_CMD_ADAPTER_RESET 0xBD
+#define RC_CMD_LCT_NOTIFY 0xA2
+#define RC_CMD_OUTBOUND_INIT 0xA1
+#define RC_CMD_PATH_ENABLE 0xD3
+#define RC_CMD_PATH_QUIESCE 0xC5
+#define RC_CMD_PATH_RESET 0xD7
+#define RC_CMD_STATIC_MF_CREATE 0xDD
+#define RC_CMD_STATIC_MF_RELEASE 0xDF
+#define RC_CMD_STATUS_GET 0xA0
+#define RC_CMD_SW_DOWNLOAD 0xA9
+#define RC_CMD_SW_UPLOAD 0xAB
+#define RC_CMD_SW_REMOVE 0xAD
+#define RC_CMD_SYS_ENABLE 0xD1
+#define RC_CMD_SYS_MODIFY 0xC1
+#define RC_CMD_SYS_QUIESCE 0xC3
+#define RC_CMD_SYS_TAB_SET 0xA3
+
+
+ /* Init Outbound Q status */
+#define RC_CMD_OUTBOUND_INIT_IN_PROGRESS 0x01
+#define RC_CMD_OUTBOUND_INIT_REJECTED 0x02
+#define RC_CMD_OUTBOUND_INIT_FAILED 0x03
+#define RC_CMD_OUTBOUND_INIT_COMPLETE 0x04
+
+
+#define UTIL_NOP 0x00
+
+
+/* RC Get Status State values */
+
+#define ADAPTER_STATE_INITIALIZING 0x01
+#define ADAPTER_STATE_RESET 0x02
+#define ADAPTER_STATE_HOLD 0x04
+#define ADAPTER_STATE_READY 0x05
+#define ADAPTER_STATE_OPERATIONAL 0x08
+#define ADAPTER_STATE_FAILED 0x10
+#define ADAPTER_STATE_FAULTED 0x11
+
+
+/* Defines for Request Status Codes: Table 3-1 Reply Status Codes. */
+
+#define RC_REPLY_STATUS_SUCCESS 0x00
+#define RC_REPLY_STATUS_ABORT_DIRTY 0x01
+#define RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02
+#define RC_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03
+#define RC_REPLY_STATUS_ERROR_DIRTY 0x04
+#define RC_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05
+#define RC_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06
+#define RC_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x07
+#define RC_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x08
+#define RC_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x09
+#define RC_REPLY_STATUS_TRANSACTION_ERROR 0x0A
+#define RC_REPLY_STATUS_PROGRESS_REPORT 0x80
+
+
+/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/
+
+#define RC_DS_SUCCESS 0x0000
+#define RC_DS_BAD_KEY 0x0001
+#define RC_DS_CHAIN_BUFFER_TOO_LARGE 0x0002
+#define RC_DS_DEVICE_BUSY 0x0003
+#define RC_DS_DEVICE_LOCKED 0x0004
+#define RC_DS_DEVICE_NOT_AVAILABLE 0x0005
+#define RC_DS_DEVICE_RESET 0x0006
+#define RC_DS_INAPPROPRIATE_FUNCTION 0x0007
+#define RC_DS_INSUFFICIENT_RESOURCE_HARD 0x0008
+#define RC_DS_INSUFFICIENT_RESOURCE_SOFT 0x0009
+#define RC_DS_INVALID_INITIATOR_ADDRESS 0x000A
+#define RC_DS_INVALID_MESSAGE_FLAGS 0x000B
+#define RC_DS_INVALID_OFFSET 0x000C
+#define RC_DS_INVALID_PARAMETER 0x000D
+#define RC_DS_INVALID_REQUEST 0x000E
+#define RC_DS_INVALID_TARGET_ADDRESS 0x000F
+#define RC_DS_MESSAGE_TOO_LARGE 0x0010
+#define RC_DS_MESSAGE_TOO_SMALL 0x0011
+#define RC_DS_MISSING_PARAMETER 0x0012
+#define RC_DS_NO_SUCH_PAGE 0x0013
+#define RC_DS_REPLY_BUFFER_FULL 0x0014
+#define RC_DS_TCL_ERROR 0x0015
+#define RC_DS_TIMEOUT 0x0016
+#define RC_DS_UNKNOWN_ERROR 0x0017
+#define RC_DS_UNKNOWN_FUNCTION 0x0018
+#define RC_DS_UNSUPPORTED_FUNCTION 0x0019
+#define RC_DS_UNSUPPORTED_VERSION 0x001A
+
+ /* msg header defines for VersionOffset */
+#define RCMSGVER_1 0x0001
+#define SGL_OFFSET_0 RCMSGVER_1
+#define SGL_OFFSET_4 (0x0040 | RCMSGVER_1)
+#define TRL_OFFSET_5 (0x0050 | RCMSGVER_1)
+#define TRL_OFFSET_6 (0x0060 | RCMSGVER_1)
+
+ /* msg header defines for MsgFlags */
+#define MSG_STATIC 0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL 0x2000
+#define MSG_LAST 0x4000
+#define MSG_REPLY 0x8000
+
+ /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
+#define LAN_MSG_REQST (MSG_MULTI_TRANS | SGL_OFFSET_4)
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE 0x00040000
+#define FIVE_WORD_MSG_SIZE 0x00050000
+#define SIX_WORD_MSG_SIZE 0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE 0x00090000
+
+/* Special TID Assignments */
+
+#define ADAPTER_TID 0
+#define HOST_TID 1
+
+ /* RedCreek private message codes */
+#define RC_PRIVATE_GET_MAC_ADDR 0x0001/**/ /* OBSOLETE */
+#define RC_PRIVATE_SET_MAC_ADDR 0x0002
+#define RC_PRIVATE_GET_LAN_STATS 0x0003
+#define RC_PRIVATE_GET_LINK_STATUS 0x0004
+#define RC_PRIVATE_SET_LINK_SPEED 0x0005
+#define RC_PRIVATE_SET_IP_AND_MASK 0x0006
+/* #define RC_PRIVATE_GET_IP_AND_MASK 0x0007 */ /* OBSOLETE */
+#define RC_PRIVATE_GET_LINK_SPEED 0x0008
+#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009
+/* #define RC_PRIVATE_GET_MAC_ADDR 0x000A *//**/
+#define RC_PRIVATE_GET_IP_AND_MASK 0x000B /**/
+#define RC_PRIVATE_DEBUG_MSG 0x000C
+#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY 0x000D
+
+#define RC_PRIVATE_REBOOT 0x00FF
+
+
+/* RC message header */
+typedef struct _RC_MSG_FRAME
+{
+ U8 VersionOffset;
+ U8 MsgFlags;
+ U16 MessageSize;
+ BF TargetAddress:TID_SZ;
+ BF InitiatorAddress:TID_SZ;
+ BF Function:FUNCTION_SZ;
+ U32 InitiatorContext;
+ /* SGL[] */
+}
+ RC_MSG_FRAME, *PRC_MSG_FRAME;
+
+
+ /* assumed a 16K minus 256 byte space for outbound queue message frames */
+#define MSG_FRAME_SIZE 512
+#define NMBR_MSG_FRAMES 30
+
+/*
+** Message Unit CSR definitions for RedCreek PCI45 board
+*/
+typedef struct tag_rcatu
+{
+ volatile unsigned long APICRegSel; /* APIC Register Select */
+ volatile unsigned long reserved0;
+ volatile unsigned long APICWinReg; /* APIC Window Register */
+ volatile unsigned long reserved1;
+ volatile unsigned long InMsgReg0; /* inbound message register 0 */
+ volatile unsigned long InMsgReg1; /* inbound message register 1 */
+ volatile unsigned long OutMsgReg0; /* outbound message register 0 */
+ volatile unsigned long OutMsgReg1; /* outbound message register 1 */
+ volatile unsigned long InDoorReg; /* inbound doorbell register */
+ volatile unsigned long InIntStat; /* inbound interrupt status register */
+ volatile unsigned long InIntMask; /* inbound interrupt mask register */
+ volatile unsigned long OutDoorReg; /* outbound doorbell register */
+ volatile unsigned long OutIntStat; /* outbound interrupt status register */
+ volatile unsigned long OutIntMask; /* outbound interrupt mask register */
+ volatile unsigned long reserved2;
+ volatile unsigned long reserved3;
+ volatile unsigned long InQueue; /* inbound queue port */
+ volatile unsigned long OutQueue; /* outbound queue port */
+ volatile unsigned long reserved4;
+ volatile unsigned long reserver5;
+ /* RedCreek extension */
+ volatile unsigned long EtherMacLow;
+ volatile unsigned long EtherMacHi;
+ volatile unsigned long IPaddr;
+ volatile unsigned long IPmask;
+}
+ ATU, *PATU;
+
+ /*
+ ** typedef PAB
+ **
+ ** PCI Adapter Block - holds instance specific information and is located
+ ** in a reserved space at the start of the message buffer allocated by user.
+ */
+typedef struct
+{
+ PATU p_atu; /* ptr to ATU register block */
+ PU8 pPci45LinBaseAddr;
+ PU8 pLinOutMsgBlock;
+ U32 outMsgBlockPhyAddr;
+ PFNTXCALLBACK pTransCallbackFunc;
+ PFNRXCALLBACK pRecvCallbackFunc;
+ PFNCALLBACK pRebootCallbackFunc;
+ PFNCALLBACK pCallbackFunc;
+ U16 ADAPTERState;
+ U16 InboundMFrameSize;
+}
+ PAB, *PPAB;
+
+ /*
+ ** in reserved space right after PAB in host memory is area for returning
+ ** values from card
+ */
+
+ /*
+ ** Array of pointers to PCI Adapter Blocks.
+ ** Indexed by a zero based (0-31) interface number.
+ */
+#define MAX_ADAPTERS 32
+static PPAB PCIAdapterBlock[MAX_ADAPTERS] =
+{
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+
+/*
+** typedef NICSTAT
+**
+** Data structure for NIC statistics retruned from PCI card. Data copied from
+** here to user allocated RCLINKSTATS (see rclanmtl.h) structure.
+*/
+typedef struct tag_NicStat
+{
+ unsigned long TX_good;
+ unsigned long TX_maxcol;
+ unsigned long TX_latecol;
+ unsigned long TX_urun;
+ unsigned long TX_crs; /* lost carrier sense */
+ unsigned long TX_def; /* transmit deferred */
+ unsigned long TX_singlecol; /* single collisions */
+ unsigned long TX_multcol;
+ unsigned long TX_totcol;
+ unsigned long Rcv_good;
+ unsigned long Rcv_CRCerr;
+ unsigned long Rcv_alignerr;
+ unsigned long Rcv_reserr; /* rnr'd pkts */
+ unsigned long Rcv_orun;
+ unsigned long Rcv_cdt;
+ unsigned long Rcv_runt;
+ unsigned long dump_status; /* last field directly from the chip */
+}
+ NICSTAT, *P_NICSTAT;
+
+
+#define DUMP_DONE 0x0000A005 /* completed statistical dump */
+#define DUMP_CLEAR 0x0000A007 /* completed stat dump and clear counters */
+
+
+static volatile int msgFlag;
+
+
+/* local function prototypes */
+static void ProcessOutboundAdapterMsg(PPAB pPab, U32 phyMsgAddr);
+static int FillAdapterMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
+static int GetAdapterStatus(PPAB pPab);
+static int SendAdapterOutboundQInitMsg(PPAB pPab);
+static int SendEnableSysMsg(PPAB pPab);
+
+
+ /* 1st 100h bytes of message block is reserved for messenger instance */
+#define ADAPTER_BLOCK_RESERVED_SPACE 0x100
+
+/*
+** =========================================================================
+** InitRCApiMsgLayer()
+**
+** Initialize the RedCreek API Module and adapter.
+**
+** Inputs: AdapterID - interface number from 0 to 15
+** pciBaseAddr - virual base address of PCI (set by BIOS)
+** p_msgbuf - virual address to private message block (min. 16K)
+** p_phymsgbuf - physical address of private message block
+** TransmitCallbackFunction - address of transmit callback function
+** ReceiveCallbackFunction - address of receive callback function
+**
+** private message block is allocated by user. It must be in locked pages.
+** p_msgbuf and p_phymsgbuf point to the same location. Must be contigous
+** memory block of a minimum of 16K byte and long word aligned.
+** =========================================================================
+*/
+RC_RETURN
+InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr,
+ PU8 p_msgbuf, PU8 p_phymsgbuf,
+ PFNTXCALLBACK TransmitCallbackFunction,
+ PFNRXCALLBACK ReceiveCallbackFunction,
+ PFNCALLBACK RebootCallbackFunction)
+{
+ int result;
+ PPAB pPab;
+
+#ifdef DEBUG
+ kprintf("InitAPI: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n"
+ "TransmitCallbackFunction:0x%08.8ulx ReceiveCallbackFunction:0x%08.8ulx\n",
+ AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction);
+#endif /* DEBUG */
+
+
+ /* Check if this interface already initialized - if so, shut it down */
+ if (PCIAdapterBlock[AdapterID] != NULL)
+ {
+ printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
+// RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+ PCIAdapterBlock[AdapterID] = NULL;
+ }
+
+ /*
+ ** store adapter instance values in adapter block.
+ ** Adapter block is at beginning of message buffer
+ */
+ pPab = (PPAB)p_msgbuf;
+
+ pPab->p_atu = (PATU)pciBaseAddr;
+ pPab->pPci45LinBaseAddr = (PU8)pciBaseAddr;
+
+ /* Set outbound message frame addr - skip over Adapter Block */
+ pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+ pPab->pLinOutMsgBlock = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+
+ /* store callback function addresses */
+ pPab->pTransCallbackFunc = TransmitCallbackFunction;
+ pPab->pRecvCallbackFunc = ReceiveCallbackFunction;
+ pPab->pRebootCallbackFunc = RebootCallbackFunction;
+ pPab->pCallbackFunc = (PFNCALLBACK)NULL;
+
+ /*
+ ** Initialize API
+ */
+ result = GetAdapterStatus(pPab);
+
+ if (result != RC_RTN_NO_ERROR)
+ return result;
+
+ if (pPab->ADAPTERState == ADAPTER_STATE_OPERATIONAL)
+ {
+ printk("pPab->ADAPTERState == op: resetting adapter\n");
+ RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+ }
+
+ result = SendAdapterOutboundQInitMsg(pPab);
+
+ if (result != RC_RTN_NO_ERROR)
+ return result;
+
+ result = SendEnableSysMsg(pPab);
+
+ if (result != RC_RTN_NO_ERROR)
+ return result;
+
+ PCIAdapterBlock[AdapterID] = pPab;
+ return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** Disable and Enable Adapter interrupts. Adapter interrupts are enabled at Init time
+** but can be disabled and re-enabled through these two function calls.
+** Packets will still be put into any posted received buffers and packets will
+** be sent through RCSendPacket() functions. Disabling Adapter interrupts
+** will prevent hardware interrupt to host even though the outbound Adapter msg
+** queue is not emtpy.
+** =========================================================================
+*/
+#define i960_OUT_POST_Q_INT_BIT 0x0008 /* bit set masks interrupts */
+
+RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID)
+{
+ PPAB pPab;
+
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT;
+
+ return RC_RTN_NO_ERROR;
+}
+
+RC_RETURN RCEnableAdapterInterrupts(U16 AdapterID)
+{
+ PPAB pPab;
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT;
+
+ return RC_RTN_NO_ERROR;
+
+}
+
+
+/*
+** =========================================================================
+** RCSendPacket()
+** =========================================================================
+*/
+RC_RETURN
+RCSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
+{
+ U32 msgOffset;
+ PU32 pMsg;
+ int size;
+ PPAB pPab;
+
+#ifdef DEBUG
+kprintf("RCSendPacket()...\n");
+#endif /* DEBUG */
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ /* get Inbound free Q entry - reading from In Q gets free Q entry */
+ /* offset to Msg Frame in PCI msg block */
+
+ msgOffset = pPab->p_atu->InQueue;
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+#ifdef DEBUG
+ kprintf("RCSendPacket(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virual address of msg - virual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+ size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+ if (size == -1) /* error processing TCB - send NOP msg */
+ {
+#ifdef DEBUG
+ kprintf("RCSendPacket(): Error Rrocess TCB!\n");
+#endif /* DEBUG */
+ pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ return RC_RTN_TCB_ERROR;
+ }
+ else /* send over msg header */
+ {
+ pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+ pMsg[1] = LAN_PACKET_SEND << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = InitiatorContext;
+ pMsg[3] = 0; /* batch reply */
+ /* post to Inbound Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+ return RC_RTN_NO_ERROR;
+ }
+}
+
+
+/*
+** =========================================================================
+** RCPostRecvBuffer()
+**
+** inputs: pBufrCntrlBlock - pointer to buffer control block
+**
+** returns TRUE if successful in sending message, else FALSE.
+** =========================================================================
+*/
+RC_RETURN
+RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
+{
+ U32 msgOffset;
+ PU32 pMsg;
+ int size;
+ PPAB pPab;
+
+#ifdef DEBUG
+kprintf("RCPostRecvBuffers()...\n");
+#endif /* DEBUG */
+
+ /* search for DeviceHandle */
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+
+ /* get Inbound free Q entry - reading from In Q gets free Q entry */
+ /* offset to Msg Frame in PCI msg block */
+ msgOffset = pPab->p_atu->InQueue;
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+#ifdef DEBUG
+ kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+ return RC_RTN_FREE_Q_EMPTY;
+
+ }
+ /* calc virual address of msg - virual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+ size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+ if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */
+ {
+#ifdef DEBUG
+ kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size);
+#endif /* DEBUG */
+ pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ /* post to Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+ return RC_RTN_TCB_ERROR;
+ }
+ else /* send over size msg header */
+ {
+ pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+ pMsg[1] = LAN_RECEIVE_POST << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+ pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
+ /* post to Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+ return RC_RTN_NO_ERROR;
+ }
+}
+
+
+/*
+** =========================================================================
+** RCProcMsgQ()
+**
+** Process outbound message queue until empty.
+** =========================================================================
+*/
+void
+RCProcMsgQ(U16 AdapterID)
+{
+ U32 phyAddrMsg;
+ PU8 p8Msg;
+ PU32 p32;
+ U16 count;
+ PPAB pPab;
+ unsigned char debug_msg[20];
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return;
+
+ phyAddrMsg = pPab->p_atu->OutQueue;
+
+ while (phyAddrMsg != 0xFFFFFFFF)
+ {
+ p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+ p32 = (PU32)p8Msg;
+
+ //printk(" msg: 0x%x 0x%x \n", p8Msg[7], p32[5]);
+
+ /*
+ ** Send Packet Reply Msg
+ */
+ if (LAN_PACKET_SEND == p8Msg[7]) /* function code byte */
+ {
+ count = *(PU16)(p8Msg+2);
+ count -= p8Msg[0] >> 4;
+ /* status, count, context[], adapter */
+ (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
+ }
+ /*
+ ** Receive Packet Reply Msg */
+ else if (LAN_RECEIVE_POST == p8Msg[7])
+ {
+#ifdef DEBUG
+ kprintf("RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32);
+ kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+ p32[0], p32[1], p32[2], p32[3]);
+ kprintf(" 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+ p32[4], p32[5], p32[6], p32[7]);
+ kprintf(" 0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+ p32[8], p32[9], p32[10], p32[11]);
+#endif
+ /* status, count, buckets remaining, packetParmBlock, adapter */
+ (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID);
+
+
+ }
+ else if (LAN_RESET == p8Msg[7] || LAN_SHUTDOWN == p8Msg[7])
+ {
+ if (pPab->pCallbackFunc)
+ {
+ (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID);
+ }
+ else
+ {
+ pPab->pCallbackFunc = (PFNCALLBACK) 1;
+ }
+ //PCIAdapterBlock[AdapterID] = 0;
+ }
+ else if (RC_PRIVATE == p8Msg[7])
+ {
+ //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]);
+ switch (p32[5])
+ {
+ case RC_PRIVATE_DEBUG_MSG:
+ msgFlag = 1;
+ /*printk("Received RC_PRIVATE msg\n");*/
+ debug_msg[15] = (p32[6]&0xff000000) >> 24;
+ debug_msg[14] = (p32[6]&0x00ff0000) >> 16;
+ debug_msg[13] = (p32[6]&0x0000ff00) >> 8;
+ debug_msg[12] = (p32[6]&0x000000ff);
+
+ debug_msg[11] = (p32[7]&0xff000000) >> 24;
+ debug_msg[10] = (p32[7]&0x00ff0000) >> 16;
+ debug_msg[ 9] = (p32[7]&0x0000ff00) >> 8;
+ debug_msg[ 8] = (p32[7]&0x000000ff);
+
+ debug_msg[ 7] = (p32[8]&0xff000000) >> 24;
+ debug_msg[ 6] = (p32[8]&0x00ff0000) >> 16;
+ debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8;
+ debug_msg[ 4] = (p32[8]&0x000000ff);
+
+ debug_msg[ 3] = (p32[9]&0xff000000) >> 24;
+ debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16;
+ debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8;
+ debug_msg[ 0] = (p32[9]&0x000000ff);
+
+ debug_msg[16] = '\0';
+ printk (debug_msg);
+ break;
+ case RC_PRIVATE_REBOOT:
+ printk("Adapter reboot initiated...\n");
+ if (pPab->pRebootCallbackFunc)
+ {
+ (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID);
+ }
+ break;
+ default:
+ printk("Unknown private msg received: 0x%x\n",
+ p32[5]);
+ break;
+ }
+ }
+
+ /*
+ ** Process other Msg's
+ */
+ else
+ {
+ ProcessOutboundAdapterMsg(pPab, phyAddrMsg);
+ }
+
+ /* return MFA to outbound free Q*/
+ pPab->p_atu->OutQueue = phyAddrMsg;
+
+ /* any more msgs? */
+ phyAddrMsg = pPab->p_atu->OutQueue;
+ }
+}
+
+
+/*
+** =========================================================================
+** Returns LAN interface statistical counters to space provided by caller at
+** StatsReturnAddr. Returns 0 if success, else RC_RETURN code.
+** This function will call the WaitCallback function provided by
+** user while waiting for card to respond.
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatistics(U16 AdapterID,
+ P_RCLINKSTATS StatsReturnAddr,
+ PFNWAITCALLBACK WaitCallback)
+{
+ U32 msgOffset;
+ volatile U32 timeout;
+ volatile PU32 pMsg;
+ volatile PU32 p32, pReturnAddr;
+ P_NICSTAT pStats;
+ int i;
+ PPAB pPab;
+
+/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ msgOffset = pPab->p_atu->InQueue;
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+ #ifdef DEBUG
+ kprintf("Get8255XStats(): Inbound Free Q empty!\n");
+ #endif
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virual address of msg - virual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+ pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+ pMsg[3] = 0x112; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LAN_STATS;
+ pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+
+ p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+
+ pStats = (P_NICSTAT)p32;
+ pStats->dump_status = 0xFFFFFFFF;
+
+ /* post to Inbound Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+
+ timeout = 100000;
+ while (1)
+ {
+ if (WaitCallback)
+ (*WaitCallback)();
+
+ for (i = 0; i < 1000; i++)
+ ;
+
+ if (pStats->dump_status != 0xFFFFFFFF)
+ break;
+
+ if (!timeout--)
+ {
+ #ifdef DEBUG
+ kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n");
+ #endif
+ return RC_RTN_MSG_REPLY_TIMEOUT;
+ }
+ }
+
+ pReturnAddr = (PU32)StatsReturnAddr;
+
+ /* copy Nic stats to user's structure */
+ for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
+ pReturnAddr[i] = p32[i];
+
+ return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** Get82558LinkStatus()
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
+{
+ U32 msgOffset;
+ volatile U32 timeout;
+ volatile PU32 pMsg;
+ volatile PU32 p32;
+ PPAB pPab;
+
+/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ msgOffset = pPab->p_atu->InQueue;
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+ #ifdef DEBUG
+ dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n");
+ #endif
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virual address of msg - virual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+ pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+ pMsg[3] = 0x112; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
+ pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+
+ p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+ *p32 = 0xFFFFFFFF;
+
+ /* post to Inbound Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+
+ timeout = 100000;
+ while (1)
+ {
+ U32 i;
+
+ if (WaitCallback)
+ (*WaitCallback)();
+
+ for (i = 0; i < 1000; i++)
+ ;
+
+ if (*p32 != 0xFFFFFFFF)
+ break;
+
+ if (!timeout--)
+ {
+ #ifdef DEBUG
+ kprintf("Timeout waiting for link status\n");
+ #endif
+ return RC_RTN_MSG_REPLY_TIMEOUT;
+ }
+ }
+
+ *ReturnAddr = *p32; /* 1 = up 0 = down */
+
+ return RC_RTN_NO_ERROR;
+
+}
+
+/*
+** =========================================================================
+** RCGetMAC()
+**
+** get the MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
+{
+ unsigned i, timeout;
+ U32 off;
+ PU32 p;
+ U32 temp[2];
+ PPAB pPab;
+ PATU p_atu;
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ p_atu = pPab->p_atu;
+
+ p_atu->EtherMacLow = 0; /* first zero return data */
+ p_atu->EtherMacHi = 0;
+
+ off = p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ p = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef RCDEBUG
+ printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n",
+ (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+ /* setup private message */
+ p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ p[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ p[2] = 0; /* initiator context */
+ p[3] = 0x218; /* transaction context */
+ p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR;
+
+
+ p_atu->InQueue = off; /* send it to the device */
+#ifdef RCDEBUG
+ printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n",
+ (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+
+ /* wait for the rcpci45 board to update the info */
+ timeout = 1000000;
+ while (0 == p_atu->EtherMacLow)
+ {
+ if (WaitCallback)
+ (*WaitCallback)();
+
+ for (i = 0; i < 1000; i++)
+ ;
+
+ if (!timeout--)
+ {
+ printk("rc_getmac: Timeout\n");
+ return RC_RTN_MSG_REPLY_TIMEOUT;
+ }
+ }
+
+ /* read the mac address */
+ temp[0] = p_atu->EtherMacLow;
+ temp[1] = p_atu->EtherMacHi;
+ memcpy((char *)mac, (char *)temp, 6);
+
+
+#ifdef RCDEBUG
+// printk("rc_getmac: 0x%X\n", ptr);
+#endif /* RCDEBUG */
+
+ return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** RCSetMAC()
+**
+** set MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCSetMAC(U16 AdapterID, PU8 mac)
+{
+ U32 off;
+ PU32 pMsg;
+ PPAB pPab;
+
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ off = pPab->p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+ /* setup private message */
+ pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = 0; /* initiator context */
+ pMsg[3] = 0x219; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR;
+ pMsg[5] = *(unsigned *)mac; /* first four bytes */
+ pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */
+
+ pPab->p_atu->InQueue = off; /* send it to the device */
+
+ return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetLinkSpeed()
+**
+** set ethernet link speed.
+** input: speedControl - determines action to take as follows
+** 0 = reset and auto-negotiate (NWay)
+** 1 = Full Duplex 100BaseT
+** 2 = Half duplex 100BaseT
+** 3 = Full Duplex 10BaseT
+** 4 = Half duplex 10BaseT
+** all other values are ignore (do nothing)
+** =========================================================================
+*/
+RC_RETURN
+RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
+{
+ U32 off;
+ PU32 pMsg;
+ PPAB pPab;
+
+
+ pPab =PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ off = pPab->p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+ /* setup private message */
+ pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = 0; /* initiator context */
+ pMsg[3] = 0x219; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED;
+ pMsg[5] = LinkSpeedCode; /* link speed code */
+
+ pPab->p_atu->InQueue = off; /* send it to the device */
+
+ return RC_RTN_NO_ERROR ;
+}
+
+/*
+** =========================================================================
+** RCGetLinkSpeed()
+**
+** get ethernet link speed.
+**
+** 0 = Unknown
+** 1 = Full Duplex 100BaseT
+** 2 = Half duplex 100BaseT
+** 3 = Full Duplex 10BaseT
+** 4 = Half duplex 10BaseT
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
+{
+ U32 msgOffset, timeout;
+ PU32 pMsg;
+ volatile PU32 p32;
+ U8 AdapterLinkSpeed;
+ PPAB pPab;
+
+ pPab =PCIAdapterBlock[AdapterID];
+
+
+ msgOffset = pPab->p_atu->InQueue;
+
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+ kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virtual address of msg - virtual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+ /* virtual pointer to return buffer - clear first two dwords */
+ p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+ p32[0] = 0xff;
+
+ /* setup private message */
+ pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = 0; /* initiator context */
+ pMsg[3] = 0x219; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED;
+ /* phys address to return status - area right after PAB */
+ pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+
+ /* post to Inbound Post Q */
+
+ pPab->p_atu->InQueue = msgOffset;
+
+ /* wait for response */
+ timeout = 1000000;
+ while(1)
+ {
+ int i;
+
+ if (WaitCallback)
+ (*WaitCallback)();
+
+ for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */
+ ;
+
+ if (p32[0] != 0xff)
+ break;
+
+ if (!timeout--)
+ {
+ kprintf("Timeout waiting for link speed from adapter\n");
+ kprintf("0x%08.8ulx\n", p32[0]);
+ return RC_RTN_NO_LINK_SPEED;
+ }
+ }
+
+ /* get Link speed */
+ AdapterLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+ *pLinkSpeedCode= AdapterLinkSpeed;
+
+ return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE 0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability)
+{
+ U32 off;
+ PU32 pMsg;
+ PPAB pPab;
+
+ pPab =PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ off = pPab->p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+ /* setup private message */
+ pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = 0; /* initiator context */
+ pMsg[3] = 0x219; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY;
+ pMsg[5] = capability;
+
+ pPab->p_atu->InQueue = off; /* send it to the device */
+
+ return RC_RTN_NO_ERROR ;
+}
+
+/*
+** =========================================================================
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
+{
+ U32 msgOffset, timeout;
+ PU32 pMsg;
+ volatile PU32 p32;
+ PPAB pPab;
+
+ pPab =PCIAdapterBlock[AdapterID];
+
+ msgOffset = pPab->p_atu->InQueue;
+
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+ kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n");
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virtual address of msg - virtual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+ /* virtual pointer to return buffer - clear first two dwords */
+ p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+ p32[0] = 0xff;
+
+ /* setup private message */
+ pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = 0; /* initiator context */
+ pMsg[3] = 0x219; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV;
+ /* phys address to return status - area right after PAB */
+ pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+
+
+
+ /* post to Inbound Post Q */
+
+ pPab->p_atu->InQueue = msgOffset;
+
+
+ /* wait for response */
+ timeout = 1000000;
+ while(1)
+ {
+ int i;
+
+ if (WaitCallback)
+ (*WaitCallback)();
+
+ for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */
+ ;
+
+ if (p32[0] != 0xff)
+ break;
+
+ if (!timeout--)
+ {
+ kprintf("Timeout waiting for link speed from adapter\n");
+ return RC_RTN_NO_FIRM_VER;
+ }
+ }
+
+ strcpy(pFirmString, (PU8)p32);
+ return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCResetLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when
+** reset is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** reset is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN
+RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+ unsigned long off;
+ unsigned long *pMsg;
+ PPAB pPab;
+ int i;
+ long timeout = 0;
+
+
+ pPab =PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ off = pPab->p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ pPab->pCallbackFunc = CallbackFunction;
+
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+ /* setup message */
+ pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = LAN_RESET << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+ pMsg[3] = ResourceFlags << 16; /* resource flags */
+
+ pPab->p_atu->InQueue = off; /* send it to the device */
+
+ if (CallbackFunction == (PFNCALLBACK)NULL)
+ {
+ /* call RCProcMsgQ() until something in pPab->pCallbackFunc
+ or until timer goes off */
+ while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+ {
+ RCProcMsgQ(AdapterID);
+ for (i = 0; i < 100000; i++) /* please don't hog the bus!!! */
+ ;
+ timeout++;
+ if (timeout > 10000)
+ {
+ break;
+ }
+ }
+ if (ReturnAddr != (PU32)NULL)
+ *ReturnAddr = (U32)pPab->pCallbackFunc;
+ }
+
+ return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCResetAdapter()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+RC_RETURN
+RCResetAdapter(U16 AdapterID)
+{
+ U32 msgOffset, timeout;
+ PU32 pMsg;
+ PPAB pPab;
+ volatile PU32 p32;
+
+ pPab = PCIAdapterBlock[AdapterID];
+ msgOffset = pPab->p_atu->InQueue;
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virtual address of msg - virtual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+ pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID;
+ pMsg[2] = 0; /* universal context */
+ pMsg[3] = 0; /* universal context */
+ pMsg[4] = 0; /* universal context */
+ pMsg[5] = 0; /* universal context */
+ /* phys address to return status - area right after PAB */
+ pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+ pMsg[7] = 0;
+ pMsg[8] = 1; /* return 1 byte */
+
+ /* virual pointer to return buffer - clear first two dwords */
+ p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+ p32[0] = 0;
+ p32[1] = 0;
+
+ /* post to Inbound Post Q */
+
+ pPab->p_atu->InQueue = msgOffset;
+
+ /* wait for response */
+ timeout = 1000000;
+ while(1)
+ {
+ int i;
+
+ for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */
+ ;
+
+ if (p32[0] || p32[1])
+ break;
+
+ if (!timeout--)
+ {
+ printk("RCResetAdapter timeout\n");
+ return RC_RTN_MSG_REPLY_TIMEOUT;
+ }
+ }
+ return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCShutdownLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when
+** shutdown is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** shutdown is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+ volatile PU32 pMsg;
+ U32 off;
+ PPAB pPab;
+ int i;
+ long timeout = 0;
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ off = pPab->p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ pPab->pCallbackFunc = CallbackFunction;
+
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+ /* setup message */
+ pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = LAN_SHUTDOWN << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+ pMsg[3] = ResourceFlags << 16; /* resource flags */
+
+ pPab->p_atu->InQueue = off; /* send it to the device */
+
+ if (CallbackFunction == (PFNCALLBACK)NULL)
+ {
+ /* call RCProcMsgQ() until something in pPab->pCallbackFunc
+ or until timer goes off */
+ while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+ {
+ RCProcMsgQ(AdapterID);
+ for (i = 0; i < 100000; i++) /* please don't hog the bus!!! */
+ ;
+ timeout++;
+ if (timeout > 10000)
+ {
+ break;
+ }
+ }
+ if (ReturnAddr != (PU32)NULL)
+ *ReturnAddr = (U32)pPab->pCallbackFunc;
+ }
+ return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetRavlinIPandMask()
+**
+** Set the Ravlin 45/PCI cards IP address and network mask.
+**
+** IP address and mask must be in network byte order.
+** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+** 0x04030201 and 0x00FFFFFF on a little endian machine.
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
+{
+ volatile PU32 pMsg;
+ U32 off;
+ PPAB pPab;
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ off = pPab->p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+ /* setup private message */
+ pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = 0; /* initiator context */
+ pMsg[3] = 0x219; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK;
+ pMsg[5] = ipAddr;
+ pMsg[6] = netMask;
+
+
+ pPab->p_atu->InQueue = off; /* send it to the device */
+ return RC_RTN_NO_ERROR ;
+
+}
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask,
+ PFNWAITCALLBACK WaitCallback)
+{
+ unsigned i, timeout;
+ U32 off;
+ PU32 pMsg, p32;
+ PPAB pPab;
+ PATU p_atu;
+
+#ifdef DEBUG
+ kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+ pPab = PCIAdapterBlock[AdapterID];
+
+ if (pPab == NULL)
+ return RC_RTN_ADPTR_NOT_REGISTERED;
+
+ p_atu = pPab->p_atu;
+ off = p_atu->InQueue; /* get addresss of message */
+
+ if (0xFFFFFFFF == off)
+ return RC_RTN_FREE_Q_EMPTY;
+
+ p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+ *p32 = 0xFFFFFFFF;
+
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef DEBUG
+ kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+ /* setup private message */
+ pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+ pMsg[2] = 0; /* initiator context */
+ pMsg[3] = 0x218; /* transaction context */
+ pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK;
+ pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+
+ p_atu->InQueue = off; /* send it to the device */
+#ifdef DEBUG
+ kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+
+ /* wait for the rcpci45 board to update the info */
+ timeout = 100000;
+ while (0xffffffff == *p32)
+ {
+ if (WaitCallback)
+ (*WaitCallback)();
+
+ for (i = 0; i < 1000; i++)
+ ;
+
+ if (!timeout--)
+ {
+ #ifdef DEBUG
+ kprintf("RCGetRavlinIPandMask: Timeout\n");
+ #endif /* DEBUG */
+ return RC_RTN_MSG_REPLY_TIMEOUT;
+ }
+ }
+
+#ifdef DEBUG
+ kprintf("RCGetRavlinIPandMask: after time out\n", \
+ "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]);
+#endif /* DEBUG */
+
+ /* send IP and mask to user's space */
+ *pIpAddr = p32[0];
+ *pNetMask = p32[1];
+
+
+#ifdef DEBUG
+ kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+ return RC_RTN_NO_ERROR;
+}
+
+/*
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+**
+** local functions
+**
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+*/
+
+/*
+** =========================================================================
+** SendAdapterOutboundQInitMsg()
+**
+** =========================================================================
+*/
+static int
+SendAdapterOutboundQInitMsg(PPAB pPab)
+{
+ U32 msgOffset, timeout, phyOutQFrames, i;
+ volatile PU32 pMsg;
+ volatile PU32 p32;
+
+
+
+ msgOffset = pPab->p_atu->InQueue;
+
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+#ifdef DEBUG
+ kprintf("SendAdapterOutboundQInitMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+
+ /* calc virual address of msg - virual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+kprintf("SendAdapterOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+ pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
+ pMsg[1] = RC_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID;
+ pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+ pMsg[3] = 0x106; /* transaction context */
+ pMsg[4] = 4096; /* Host page frame size */
+ pMsg[5] = MSG_FRAME_SIZE << 16 | 0x80; /* outbound msg frame size and Initcode */
+ pMsg[6] = 0xD0000004; /* simple sgl element LE, EOB */
+ /* phys address to return status - area right after PAB */
+ pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+
+ /* virual pointer to return buffer - clear first two dwords */
+ p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+ p32[0] = 0;
+
+ /* post to Inbound Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+
+ /* wait for response */
+ timeout = 100000;
+ while(1)
+ {
+ for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */
+ ;
+
+ if (p32[0])
+ break;
+
+ if (!timeout--)
+ {
+#ifdef DEBUG
+ kprintf("Timeout wait for InitOutQ InPrgress status from adapter\n");
+#endif /* DEBUG */
+ return RC_RTN_NO_STATUS;
+ }
+ }
+
+ timeout = 100000;
+ while(1)
+ {
+ for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */
+ ;
+
+ if (p32[0] == RC_CMD_OUTBOUND_INIT_COMPLETE)
+ break;
+
+ if (!timeout--)
+ {
+#ifdef DEBUG
+ kprintf("Timeout wait for InitOutQ Complete status from adapter\n");
+#endif /* DEBUG */
+ return RC_RTN_NO_STATUS;
+ }
+ }
+
+ /* load PCI outbound free Q with MF physical addresses */
+ phyOutQFrames = pPab->outMsgBlockPhyAddr;
+
+ for (i = 0; i < NMBR_MSG_FRAMES; i++)
+ {
+ pPab->p_atu->OutQueue = phyOutQFrames;
+ phyOutQFrames += MSG_FRAME_SIZE;
+ }
+ return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** GetAdapterStatus()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+static int
+GetAdapterStatus(PPAB pPab)
+{
+ U32 msgOffset, timeout;
+ PU32 pMsg;
+ volatile PU32 p32;
+
+
+ msgOffset = pPab->p_atu->InQueue;
+ printk("GetAdapterStatus: msg offset = 0x%x\n", msgOffset);
+ if (msgOffset == 0xFFFFFFFF)
+ {
+#ifdef DEBUG
+ kprintf("GetAdapterStatus(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virual address of msg - virual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+ pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID;
+ pMsg[2] = 0; /* universal context */
+ pMsg[3] = 0; /* universal context */
+ pMsg[4] = 0; /* universal context */
+ pMsg[5] = 0; /* universal context */
+ /* phys address to return status - area right after PAB */
+ pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB);
+ pMsg[7] = 0;
+ pMsg[8] = 88; /* return 88 bytes */
+
+ /* virual pointer to return buffer - clear first two dwords */
+ p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+ p32[0] = 0;
+ p32[1] = 0;
+
+#ifdef DEBUG
+kprintf("GetAdapterStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n",
+ pMsg, msgOffset, pMsg[1], pMsg[6]);
+#endif /* DEBUG */
+
+ /* post to Inbound Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+
+#ifdef DEBUG
+kprintf("Return status to p32 = 0x%08.8ulx\n", p32);
+#endif /* DEBUG */
+
+ /* wait for response */
+ timeout = 1000000;
+ while(1)
+ {
+ int i;
+
+ for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */
+ ;
+
+ if (p32[0] && p32[1])
+ break;
+
+ if (!timeout--)
+ {
+#ifdef DEBUG
+ kprintf("Timeout waiting for status from adapter\n");
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+ return RC_RTN_NO_STATUS;
+ }
+ }
+
+#ifdef DEBUG
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+ /* get adapter state */
+ pPab->ADAPTERState = ((volatile PU8)p32)[10];
+ pPab->InboundMFrameSize = ((volatile PU16)p32)[6];
+
+#ifdef DEBUG
+ kprintf("adapter state 0x%02.2x InFrameSize = 0x%04.4x\n",
+ pPab->ADAPTERState, pPab->InboundMFrameSize);
+#endif /* DEBUG */
+ return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** SendEnableSysMsg()
+**
+**
+** =========================================================================
+*/
+static int
+SendEnableSysMsg(PPAB pPab)
+{
+ U32 msgOffset; // timeout;
+ volatile PU32 pMsg;
+
+ msgOffset = pPab->p_atu->InQueue;
+
+ if (msgOffset == 0xFFFFFFFF)
+ {
+#ifdef DEBUG
+ kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+ return RC_RTN_FREE_Q_EMPTY;
+ }
+
+ /* calc virual address of msg - virual already mapped to physical */
+ pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+ pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+ pMsg[1] = RC_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID;
+ pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+ pMsg[3] = 0x110; /* transaction context */
+ pMsg[4] = 0x50657465; /* RedCreek Private */
+
+ /* post to Inbound Post Q */
+ pPab->p_atu->InQueue = msgOffset;
+
+ return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** FillI12OMsgFromTCB()
+**
+** inputs pMsgU32 - virual pointer (mapped to physical) of message frame
+** pXmitCntrlBlock - pointer to caller buffer control block.
+**
+** fills in LAN SGL after Transaction Control Word or Bucket Count.
+** =========================================================================
+*/
+static int
+FillAdapterMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
+{
+ unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
+ PU32 pTCB, pMsg;
+
+ /* SGL element flags */
+#define EOB 0x40000000
+#define LE 0x80000000
+#define SIMPLE_SGL 0x10000000
+#define BC_PRESENT 0x01000000
+
+ pTCB = (PU32)pTransCtrlBlock;
+ pMsg = pMsgFrame;
+ nmbrDwords = 0;
+
+#ifdef DEBUG
+ kprintf("FillAdapterMsgSGLFromTCBX\n");
+kprintf("TCB 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+ pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
+kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg);
+#endif /* DEBUG */
+
+ nmbrBuffers = *pTCB++;
+
+ if (!nmbrBuffers)
+ {
+ return -1;
+ }
+
+ do
+ {
+ context = *pTCB++; /* buffer tag (context) */
+ nmbrSeg = *pTCB++; /* number of segments */
+
+ if (!nmbrSeg)
+ {
+ return -1;
+ }
+
+ flags = SIMPLE_SGL | BC_PRESENT;
+
+ if (1 == nmbrSeg)
+ {
+ flags |= EOB;
+
+ if (1 == nmbrBuffers)
+ flags |= LE;
+ }
+
+ /* 1st SGL buffer element has context */
+ pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */
+ pMsg[1] = context;
+ pMsg[2] = pTCB[1]; /* send buffer segment physical address */
+ nmbrDwords += 3;
+ pMsg += 3;
+ pTCB += 2;
+
+
+ if (--nmbrSeg)
+ {
+ do
+ {
+ flags = SIMPLE_SGL;
+
+ if (1 == nmbrSeg)
+ {
+ flags |= EOB;
+
+ if (1 == nmbrBuffers)
+ flags |= LE;
+ }
+
+ pMsg[0] = pTCB[0] | flags; /* send over count */
+ pMsg[1] = pTCB[1]; /* send buffer segment physical address */
+ nmbrDwords += 2;
+ pTCB += 2;
+ pMsg += 2;
+
+ } while (--nmbrSeg);
+ }
+
+ } while (--nmbrBuffers);
+
+ return nmbrDwords;
+}
+
+
+/*
+** =========================================================================
+** ProcessOutboundAdapterMsg()
+**
+** process reply message
+** * change to msg structure *
+** =========================================================================
+*/
+static void
+ProcessOutboundAdapterMsg(PPAB pPab, U32 phyAddrMsg)
+{
+ PU8 p8Msg;
+ PU32 p32;
+ // U16 count;
+
+
+ p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+ p32 = (PU32)p8Msg;
+
+#ifdef DEBUG
+ kprintf("VXD: ProcessOutboundAdapterMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
+ kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+ kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+#endif /* DEBUG */
+
+ if (p32[4] >> 24 != RC_REPLY_STATUS_SUCCESS)
+ {
+#ifdef DEBUG
+ kprintf("Message reply status not success\n");
+#endif /* DEBUG */
+ return;
+ }
+
+ switch (p8Msg[7] ) /* function code byte */
+ {
+ case RC_CMD_SYS_TAB_SET:
+ msgFlag = 1;
+#ifdef DEBUG
+ kprintf("Received RC_CMD_SYS_TAB_SET reply\n");
+#endif /* DEBUG */
+ break;
+
+ case RC_CMD_HRT_GET:
+ msgFlag = 1;
+#ifdef DEBUG
+ kprintf("Received RC_CMD_HRT_GET reply\n");
+#endif /* DEBUG */
+ break;
+
+ case RC_CMD_LCT_NOTIFY:
+ msgFlag = 1;
+#ifdef DEBUG
+ kprintf("Received RC_CMD_LCT_NOTIFY reply\n");
+#endif /* DEBUG */
+ break;
+
+ case RC_CMD_SYS_ENABLE:
+ msgFlag = 1;
+#ifdef DEBUG
+ kprintf("Received RC_CMD_SYS_ENABLE reply\n");
+#endif /* DEBUG */
+ break;
+
+ default:
+#ifdef DEBUG
+ kprintf("Received UNKNOWN reply\n");
+#endif /* DEBUG */
+ break;
+ }
+}
--- /dev/null
+/*
+** *************************************************************************
+**
+**
+** R C M T L . H $Revision: 3 $
+**
+**
+** RedCreek Message Transport Layer header file.
+**
+** ---------------------------------------------------------------------
+** --- Copyright (c) 1997-1998, RedCreek Communications Inc. ---
+** --- All rights reserved. ---
+** ---------------------------------------------------------------------
+**
+** File Description:
+**
+** Header file for host message transport layer API and data types.
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** *************************************************************************
+*/
+
+#ifndef RCMTL_H
+#define RCMTL_H
+
+/* Linux specific includes */
+#define kprintf printk
+#ifdef RC_LINUX_MODULE /* linux modules need non-library version of string functions */
+#include <linux/string.h>
+#else
+#include <string.h>
+#endif
+
+/* PCI/45 Configuration space values */
+#define RC_PCI45_VENDOR_ID 0x4916
+#define RC_PCI45_DEVICE_ID 0x1960
+
+
+ /* RedCreek API function return values */
+#define RC_RTN_NO_ERROR 0
+#define RC_RTN_NOT_INIT 1
+#define RC_RTN_FREE_Q_EMPTY 2
+#define RC_RTN_TCB_ERROR 3
+#define RC_RTN_TRANSACTION_ERROR 4
+#define RC_RTN_ADAPTER_ALREADY_INIT 5
+#define RC_RTN_MALLOC_ERROR 6
+#define RC_RTN_ADPTR_NOT_REGISTERED 7
+#define RC_RTN_MSG_REPLY_TIMEOUT 8
+#define RC_RTN_NO_STATUS 9
+#define RC_RTN_NO_FIRM_VER 10
+#define RC_RTN_NO_LINK_SPEED 11
+
+/* Driver capability flags */
+#define WARM_REBOOT_CAPABLE 0x01
+
+ /* scalar data types */
+typedef unsigned char U8;
+typedef unsigned char* PU8;
+typedef unsigned short U16;
+typedef unsigned short* PU16;
+typedef unsigned long U32;
+typedef unsigned long* PU32;
+typedef unsigned long BF;
+typedef int RC_RETURN;
+
+
+ /*
+ ** type PFNWAITCALLBACK
+ **
+ ** pointer to void function - type used for WaitCallback in some functions
+ */
+typedef void (*PFNWAITCALLBACK)(void); /* void argument avoids compiler complaint */
+
+ /*
+ ** type PFNTXCALLBACK
+ **
+ ** Pointer to user's transmit callback function. This user function is
+ ** called from RCProcMsgQ() when packet have been transmitted from buffers
+ ** given in the RCSendPacket() function. BufferContext is a pointer to
+ ** an array of 32 bit context values. These are the values the user assigned
+ ** and passed in the TCB to the RCSendPacket() function. PcktCount
+ ** indicates the number of buffer context values in the BufferContext[] array.
+ ** The User's TransmitCallbackFunction should recover (put back in free queue)
+ ** the packet buffers associated with the buffer context values.
+ */
+typedef void (*PFNTXCALLBACK)(U32 Status,
+ U16 PcktCount,
+ PU32 BufferContext,
+ U16 AdaterID);
+
+ /*
+ ** type PFNRXCALLBACK
+ **
+ ** Pointer to user's receive callback function. This user function
+ ** is called from RCProcMsgQ() when packets have been received into
+ ** previously posted packet buffers throught the RCPostRecvBuffers() function.
+ ** The received callback function should process the Packet Descriptor Block
+ ** pointed to by PacketDescBlock. See Packet Decription Block below.
+ */
+typedef void (*PFNRXCALLBACK)(U32 Status,
+ U8 PktCount,
+ U32 BucketsRemain,
+ PU32 PacketDescBlock,
+ U16 AdapterID);
+
+ /*
+ ** type PFNCALLBACK
+ **
+ ** Pointer to user's generic callback function. This user function
+ ** can be passed to LANReset or LANShutdown and is called when the
+ ** the reset or shutdown is complete.
+ ** Param1 and Param2 are invalid for LANReset and LANShutdown.
+ */
+typedef void (*PFNCALLBACK)(U32 Status,
+ U32 Param1,
+ U32 Param2,
+ U16 AdapterID);
+
+/*
+** Status - Transmit and Receive callback status word
+**
+** A 32 bit Status is returned to the TX and RX callback functions. This value
+** contains both the reply status and the detailed status as follows:
+**
+** 32 24 16 0
+** +------+------+------------+
+** | Reply| | Detailed |
+** |Status| 0 | Status |
+** +------+------+------------+
+**
+** Reply Status and Detailed Status of zero indicates No Errors.
+*/
+ /* reply message status defines */
+#define RC_REPLY_STATUS_SUCCESS 0x00
+#define RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02
+#define RC_REPLY_STATUS_TRANSACTION_ERROR 0x0A
+
+
+/* DetailedStatusCode defines */
+#define RC_DSC_SUCCESS 0x0000
+#define RC_DSC_DEVICE_FAILURE 0x0001
+#define RC_DSC_DESTINATION_NOT_FOUND 0x0002
+#define RC_DSC_TRANSMIT_ERROR 0x0003
+#define RC_DSC_TRANSMIT_ABORTED 0x0004
+#define RC_DSC_RECEIVE_ERROR 0x0005
+#define RC_DSC_RECEIVE_ABORTED 0x0006
+#define RC_DSC_DMA_ERROR 0x0007
+#define RC_DSC_BAD_PACKET_DETECTED 0x0008
+#define RC_DSC_OUT_OF_MEMORY 0x0009
+#define RC_DSC_BUCKET_OVERRUN 0x000A
+#define RC_DSC_IOP_INTERNAL_ERROR 0x000B
+#define RC_DSC_CANCELED 0x000C
+#define RC_DSC_INVALID_TRANSACTION_CONTEXT 0x000D
+#define RC_DSC_DESTINATION_ADDRESS_DETECTED 0x000E
+#define RC_DSC_DESTINATION_ADDRESS_OMITTED 0x000F
+#define RC_DSC_PARTIAL_PACKET_RETURNED 0x0010
+
+
+/*
+** Packet Description Block (Received packets)
+**
+** A pointer to this block structure is returned to the ReceiveCallback
+** function. It contains the list of packet buffers which have either been
+** filled with a packet or returned to host due to a LANReset function.
+** Currently there will only be one packet per receive bucket (buffer) posted.
+**
+** 32 24 0
+** +-----------------------+ -\
+** | Buffer 1 Context | \
+** +-----------------------+ \
+** | 0xC0000000 | / First Bucket Descriptor
+** +-----+-----------------+ /
+** | 0 | packet 1 length | /
+** +-----------------------+ -\
+** | Buffer 2 Context | \
+** +-----------------------+ \
+** | 0xC0000000 | / Second Bucket Descriptor
+** +-----+-----------------+ /
+** | 0 | packet 2 length | /
+** +-----+-----------------+ -
+** | ... | ----- more bucket descriptors
+** +-----------------------+ -\
+** | Buffer n Context | \
+** +-----------------------+ \
+** | 0xC0000000 | / Last Bucket Descriptor
+** +-----+-----------------+ /
+** | 0 | packet n length | /
+** +-----+-----------------+ -
+**
+** Buffer Context values are those given to adapter in the TCB on calls to
+** RCPostRecvBuffers().
+**
+*/
+
+
+
+/*
+** Transaction Control Block (TCB) structure
+**
+** A structure like this is filled in by the user and passed by reference to
+** RCSendPacket() and RCPostRecvBuffers() functions. Minimum size is five
+** 32-bit words for one buffer with one segment descriptor.
+** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers
+** that can be described in a given TCB.
+**
+** 32 0
+** +-----------------------+
+** | Buffer Count | Number of buffers in the TCB
+** +-----------------------+
+** | Buffer 1 Context | first buffer reference
+** +-----------------------+
+** | Buffer 1 Seg Count | number of segments in buffer
+** +-----------------------+
+** | Buffer 1 Seg Desc 1 | first segment descriptor (size, physical address)
+** +-----------------------+
+** | ... | more segment descriptors (size, physical address)
+** +-----------------------+
+** | Buffer 1 Seg Desc n | last segment descriptor (size, physical address)
+** +-----------------------+
+** | Buffer 2 Context | second buffer reference
+** +-----------------------+
+** | Buffer 2 Seg Count | number of segments in buffer
+** +-----------------------+
+** | Buffer 2 Seg Desc 1 | segment descriptor (size, physical address)
+** +-----------------------+
+** | ... | more segment descriptors (size, physical address)
+** +-----------------------+
+** | Buffer 2 Seg Desc n |
+** +-----------------------+
+** | ... | more buffer descriptor blocks ...
+** +-----------------------+
+** | Buffer n Context |
+** +-----------------------+
+** | Buffer n Seg Count |
+** +-----------------------+
+** | Buffer n Seg Desc 1 |
+** +-----------------------+
+** | ... |
+** +-----------------------+
+** | Buffer n Seg Desc n |
+** +-----------------------+
+**
+**
+** A TCB for one contigous packet buffer would look like the following:
+**
+** 32 0
+** +-----------------------+
+** | 1 | one buffer in the TCB
+** +-----------------------+
+** | <user's Context> | user's buffer reference
+** +-----------------------+
+** | 1 | one segment buffer
+** +-----------------------+ _
+** | <buffer size> | size \
+** +-----------------------+ \ segment descriptor
+** | <physical address> | physical address of buffer /
+** +-----------------------+ _/
+**
+*/
+
+ /* Buffer Segment Descriptor */
+typedef struct
+{
+ U32 size;
+ U32 phyAddress;
+}
+ BSD, *PBSD;
+
+typedef PU32 PRCTCB;
+/*
+** -------------------------------------------------------------------------
+** Exported functions comprising the API to the message transport layer
+** -------------------------------------------------------------------------
+*/
+
+
+ /*
+ ** InitRCApiMsgLayer()
+ **
+ ** Called once prior to using the API message transport layer. User
+ ** provides both the physical and virual address of a locked page buffer
+ ** that is used as a private buffer for the RedCreek API message
+ ** transport layer. This buffer must be a contigous memory block of a
+ ** minimum of 16K bytes and long word aligned. The user also must provide
+ ** the base address of the RedCreek PCI adapter assigned by BIOS or operating
+ ** system. The user provided value AdapterID is a zero based index of the
+ ** Ravlin 45/PCI adapter. This interface number is used in all subsequent API
+ ** calls to identify which adpapter for which the function is intended.
+ ** Up to sixteen interfaces are supported with this API.
+ **
+ ** Inputs: AdapterID - interface number from 0 to 15
+ ** pciBaseAddr - virual base address of PCI (set by BIOS)
+ ** p_msgbuf - virual address to private message block (min. 16K)
+ ** p_phymsgbuf - physical address of private message block
+ ** TransmitCallbackFunction - address of user's TX callback function
+ ** ReceiveCallbackFunction - address of user's RX callback function
+ **
+ */
+RC_RETURN InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr,
+ PU8 p_msgbuf, PU8 p_phymsgbuf,
+ PFNTXCALLBACK TransmitCallbackFunction,
+ PFNRXCALLBACK ReceiveCallbackFunction,
+ PFNCALLBACK RebootCallbackFunction);
+
+ /*
+ ** RCSetRavlinIPandMask()
+ **
+ ** Set the Ravlin 45/PCI cards IP address and network mask.
+ **
+ ** IP address and mask must be in network byte order.
+ ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+ ** 0x04030201 and 0x00FFFFFF on a little endian machine.
+ **
+ */
+RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask);
+
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask,
+ PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCProcMsgQ()
+ **
+ ** Called from user's polling loop or Interrupt Service Routine for a PCI
+ ** interrupt from the RedCreek PCI adapter. User responsible for determining
+ ** and hooking the PCI interrupt. This function will call the registered
+ ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction,
+ ** if a TX or RX transaction has completed.
+ */
+void RCProcMsgQ(U16 AdapterID);
+
+
+ /*
+ ** Disable and Enable Adapter interrupts. Adapter interrupts are enabled at
+ ** Init time but can be disabled and re-enabled through these two function calls.
+ ** Packets will still be put into any posted recieved buffers and packets will
+ ** be sent through RCSendPacket() functions. Disabling Adapter interrupts
+ ** will prevent hardware interrupt to host even though the outbound msg
+ ** queue is not emtpy.
+ */
+RC_RETURN RCEnableAdapterInterrupts(U16 adapterID);
+RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID);
+
+
+ /*
+ ** RCPostRecvBuffers()
+ **
+ ** Post user's page locked buffers for use by the PCI adapter to
+ ** return ethernet packets received from the LAN. Transaction Control Block,
+ ** provided by user, contains buffer descriptor(s) which includes a buffer
+ ** context number along with buffer size and physical address. See TCB above.
+ ** The buffer context and actual packet length are returned to the
+ ** ReceiveCallbackFunction when packets have been received. Buffers posted
+ ** to the RedCreek adapter are considered owned by the adapter until the
+ ** context is return to user through the ReceiveCallbackFunction.
+ */
+RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock);
+#define MAX_NMBR_POST_BUFFERS_PER_MSG 32
+
+ /*
+ ** RCSendPacket()
+ **
+ ** Send user's ethernet packet from a locked page buffer.
+ ** Packet must have full MAC header, however without a CRC.
+ ** Initiator context is a user provided value that is returned
+ ** to the TransmitCallbackFunction when packet buffer is free.
+ ** Transmit buffer are considered owned by the adapter until context's
+ ** returned to user through the TransmitCallbackFunction.
+ */
+RC_RETURN RCSendPacket(U16 AdapterID,
+ U32 context,
+ PRCTCB pTransactionCtrlBlock);
+
+
+ /* Ethernet Link Statistics structure */
+typedef struct tag_RC_link_stats
+{
+ U32 TX_good; /* good transmit frames */
+ U32 TX_maxcol; /* frames not TX due to MAX collisions */
+ U32 TX_latecol; /* frames not TX due to late collisions */
+ U32 TX_urun; /* frames not TX due to DMA underrun */
+ U32 TX_crs; /* frames TX with lost carrier sense */
+ U32 TX_def; /* frames deferred due to activity on link */
+ U32 TX_singlecol; /* frames TX with one and only on collision */
+ U32 TX_multcol; /* frames TX with more than one collision */
+ U32 TX_totcol; /* total collisions detected during TX */
+ U32 Rcv_good; /* good frames received */
+ U32 Rcv_CRCerr; /* frames RX and discarded with CRC errors */
+ U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
+ U32 Rcv_reserr; /* good frames discarded due to no RX buffer */
+ U32 Rcv_orun; /* RX frames lost due to FIFO overrun */
+ U32 Rcv_cdt; /* RX frames with collision during RX */
+ U32 Rcv_runt; /* RX frames shorter than 64 bytes */
+}
+ RCLINKSTATS, *P_RCLINKSTATS;
+
+ /*
+ ** RCGetLinkStatistics()
+ **
+ ** Returns link statistics in user's structure at address StatsReturnAddr
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatistics(U16 AdapterID,
+ P_RCLINKSTATS StatsReturnAddr,
+ PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCGetLinkStatus()
+ **
+ ** Return link status, up or down, to user's location addressed by ReturnAddr.
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatus(U16 AdapterID,
+ PU32 pReturnStatus,
+ PFNWAITCALLBACK WaitCallback);
+
+ /* Link Status defines - value returned in pReturnStatus */
+#define LAN_LINK_STATUS_DOWN 0
+#define LAN_LINK_STATUS_UP 1
+
+ /*
+ ** RCGetMAC()
+ **
+ ** Get the current MAC address assigned to user. RedCreek Ravlin 45/PCI
+ ** has two MAC addresses. One which is private to the PCI Card, and
+ ** another MAC which is given to the user as its link layer MAC address. The
+ ** adapter runs in promiscous mode because of the dual address requirement.
+ ** The MAC address is returned to the unsigned char array pointer to by mac.
+ */
+RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCSetMAC()
+ **
+ ** Set a new user port MAC address. This address will be returned on
+ ** subsequent RCGetMAC() calls.
+ */
+RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac);
+
+ /*
+ ** RCSetLinkSpeed()
+ **
+ ** set adapter's link speed based on given input code.
+ */
+RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode);
+ /* Set link speed codes */
+#define LNK_SPD_AUTO_NEG_NWAY 0
+#define LNK_SPD_100MB_FULL 1
+#define LNK_SPD_100MB_HALF 2
+#define LNK_SPD_10MB_FULL 3
+#define LNK_SPD_10MB_HALF 4
+
+
+
+
+ /*
+ ** RCGetLinkSpeed()
+ **
+ ** Return link speed code.
+ */
+ /* Return link speed codes */
+#define LNK_SPD_UNKNOWN 0
+#define LNK_SPD_100MB_FULL 1
+#define LNK_SPD_100MB_HALF 2
+#define LNK_SPD_10MB_FULL 3
+#define LNK_SPD_10MB_HALF 4
+
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
+
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE 0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability);
+
+/*
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** WARNING: user's space pointed to by pFirmString should be at least 60 bytes.
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback);
+
+/*
+** ----------------------------------------------
+** LAN adapter Reset and Shutdown functions
+** ----------------------------------------------
+*/
+ /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */
+#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS 0x0001
+#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS 0x0002
+
+ /*
+ ** RCResetLANCard()
+ **
+ ** Reset LAN card operation. Causes a software reset of the ethernet
+ ** controller and restarts the command and receive units. Depending on
+ ** the ResourceFlags given, the buffers are either returned to the
+ ** host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and
+ ** detailed status of RC_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete. If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset
+ ** to complete (please disable adapter interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** A call to RCPostRecvBuffers() is needed to return the ethernet to full
+ ** operation if the receive buffers were returned during LANReset.
+ ** Note: The IOP status is not affected by a LAN reset.
+ */
+RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+
+ /*
+ ** RCShutdownLANCard()
+ **
+ ** Shutdown LAN card operation and put into an idle (suspended) state.
+ ** The LAN card is restarted with RCResetLANCard() function.
+ ** Depending on the ResourceFlags given, the buffers are either returned
+ ** to the host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER
+ ** and detailed status of RC_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete. If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset
+ ** to complete (please disable adapter interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** Note: The IOP status is not affected by a LAN shutdown.
+ */
+RC_RETURN
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+ /*
+ ** RCResetAdapter();
+ ** Initializes ADAPTERState to ADAPTER_STATE_RESET.
+ ** Stops access to outbound message Q.
+ ** Discards any outstanding transmit or posted receive buffers.
+ ** Clears outbound message Q.
+ */
+RC_RETURN
+RCResetAdapter(U16 AdapterID);
+
+#endif /* RCMTL_H */
--- /dev/null
+/*
+** RCpci45.c
+**
+**
+**
+** ---------------------------------------------------------------------
+** --- Copyright (c) 1998, RedCreek Communications Inc. ---
+** --- All rights reserved. ---
+** ---------------------------------------------------------------------
+**
+** Written by Pete Popov and Brian Moyle.
+**
+** Known Problems
+**
+** Billions and Billions...
+**
+** ... apparently added by Brian. Pete knows of no bugs.
+**
+** TODO:
+** -Get rid of the wait loops in the API and replace them
+** with system independent delays ...something like
+** "delayms(2)".
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**
+** Ported to 2.1.x by Alan Cox 1998/12/9. If this doesnt work try 2.0.x
+** and let me know.
+**
+***************************************************************************/
+
+static char *version =
+"RedCreek Communications PCI linux driver version 1.32 Beta\n";
+
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/timer.h>
+#include <asm/irq.h> /* For NR_IRQS only. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#define RC_LINUX_MODULE
+#include "rcmtl.h"
+#include "rcif.h"
+
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+
+#define NEW_MULTICAST
+#include <linux/delay.h>
+
+/* PCI/45 Configuration space values */
+#define RC_PCI45_VENDOR_ID 0x4916
+#define RC_PCI45_DEVICE_ID 0x1960
+
+#define MAX_ETHER_SIZE 1520
+#define MAX_NMBR_RCV_BUFFERS 96
+#define RC_POSTED_BUFFERS_LOW_MARK MAX_NMBR_RCV_BUFFERS-16
+#define BD_SIZE 3 /* Bucket Descriptor size */
+#define BD_LEN_OFFSET 2 /* Bucket Descriptor offset to length field */
+
+
+/* RedCreek LAN device Target ID */
+#define RC_LAN_TARGET_ID 0x10
+/* RedCreek's OSM default LAN receive Initiator */
+#define DEFAULT_RECV_INIT_CONTEXT 0xA17
+
+
+static U32 DriverControlWord = 0;
+
+static void rc_timer(unsigned long);
+
+/*
+ * Driver Private Area, DPA.
+ */
+typedef struct
+{
+
+ /*
+ * pointer to the device structure which is part
+ * of the interface to the Linux kernel.
+ */
+ struct device *dev;
+
+ char devname[8]; /* "ethN" string */
+ U8 id; /* the AdapterID */
+ U32 pci_addr; /* the pci address of the adapter */
+ U32 bus;
+ U32 function;
+ struct timer_list timer; /* timer */
+ struct enet_statistics stats; /* the statistics structure */
+ struct device *next; /* points to the next RC adapter */
+ unsigned long numOutRcvBuffers;/* number of outstanding receive buffers*/
+ unsigned char shutdown;
+ unsigned char reboot;
+ unsigned char nexus;
+ PU8 PLanApiPA; /* Pointer to Lan Api Private Area */
+
+}
+DPA, *PDPA;
+
+#define MAX_ADAPTERS 32
+
+static PDPA PCIAdapters[MAX_ADAPTERS] =
+{
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+
+static int RCscan(void);
+static struct device
+*RCfound_device(struct device *, int, int, int, int, int, int);
+
+static int RCprobe1(struct device *);
+static int RCopen(struct device *);
+static int RC_xmit_packet(struct sk_buff *, struct device *);
+static void RCinterrupt(int, void *, struct pt_regs *);
+static int RCclose(struct device *dev);
+static struct enet_statistics *RCget_stats(struct device *);
+static int RCioctl(struct device *, struct ifreq *, int);
+static int RCconfig(struct device *, struct ifmap *);
+static void RCxmit_callback(U32, U16, PU32, U16);
+static void RCrecv_callback(U32, U8, U32, PU32, U16);
+static void RCreset_callback(U32, U32, U32, U16);
+static void RCreboot_callback(U32, U32, U32, U16);
+static int RC_allocate_and_post_buffers(struct device *, int);
+
+
+/* A list of all installed RC devices, for removing the driver module. */
+static struct device *root_RCdev = NULL;
+
+#ifdef MODULE
+int init_module(void)
+#else
+int rcpci_probe(struct netdevice *dev)
+#endif
+{
+ int cards_found;
+
+ printk(version);
+
+ root_RCdev = NULL;
+ cards_found = RCscan();
+#ifdef MODULE
+ return cards_found ? 0 : -ENODEV;
+#else
+ return -1;
+#endif
+}
+
+static int RCscan(void)
+{
+ int cards_found = 0;
+ struct device *dev = 0;
+
+ if (pcibios_present())
+ {
+ static int pci_index = 0;
+ unsigned char pci_bus, pci_device_fn;
+ int scan_status;
+ int board_index = 0;
+
+ for (;pci_index < 0xff; pci_index++)
+ {
+ unsigned char pci_irq_line;
+ unsigned short pci_command, vendor, device, class;
+ unsigned int pci_ioaddr;
+
+
+ scan_status =
+ (pcibios_find_device (RC_PCI45_VENDOR_ID,
+ RC_PCI45_DEVICE_ID,
+ pci_index,
+ &pci_bus,
+ &pci_device_fn));
+#ifdef RCDEBUG
+ printk("rc scan_status = 0x%X\n", scan_status);
+#endif
+ if (scan_status != 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);
+ pcibios_read_config_byte(pci_bus,
+ pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus,
+ pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ pcibios_read_config_word(pci_bus,
+ pci_device_fn,
+ PCI_CLASS_DEVICE, &class);
+
+ pci_ioaddr &= ~0xf;
+
+#ifdef RCDEBUG
+ printk("rc: Found RedCreek PCI adapter\n");
+ printk("rc: pci class = 0x%x 0x%x \n", class, class>>8);
+ printk("rc: pci_bus = %d, pci_device_fn = %d\n", pci_bus, pci_device_fn);
+ printk("rc: pci_irq_line = 0x%x \n", pci_irq_line);
+ printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr);
+#endif
+
+#if 0
+ if (check_region(pci_ioaddr, 32768))
+ {
+ printk("rc: check_region failed\n");
+ continue;
+ }
+ else
+ {
+ printk("rc: check_region passed\n");
+ }
+#endif
+
+ /*
+ * Get and check the bus-master and latency values.
+ * Some PCI BIOSes fail to set the master-enable bit.
+ */
+
+ pcibios_read_config_word(pci_bus,
+ pci_device_fn,
+ PCI_COMMAND,
+ &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk("rc: PCI Master Bit has not been set!\n");
+
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus,
+ pci_device_fn,
+ PCI_COMMAND,
+ pci_command);
+ }
+ if ( ! (pci_command & PCI_COMMAND_MEMORY)) {
+ /*
+ * If the BIOS did not set the memory enable bit, what else
+ * did it not initialize? Skip this adapter.
+ */
+ printk("rc: Adapter %d, PCI Memory Bit has not been set!\n",
+ cards_found);
+ printk("rc: Bios problem? \n");
+ continue;
+ }
+
+ dev = RCfound_device(dev, pci_ioaddr, pci_irq_line,
+ pci_bus, pci_device_fn,
+ board_index++, cards_found);
+
+ if (dev) {
+ dev = 0;
+ cards_found++;
+ }
+ }
+ }
+ printk("rc: found %d cards \n", cards_found);
+ return cards_found;
+}
+
+static struct device *
+RCfound_device(struct device *dev, int memaddr, int irq,
+ int bus, int function, int product_index, int card_idx)
+{
+ int dev_size = 32768;
+ unsigned long *vaddr=0;
+ PDPA pDpa;
+ int init_status;
+
+ /*
+ * Allocate and fill new device structure.
+ * We need enough for struct device plus DPA plus the LAN API private
+ * area, which requires a minimum of 16KB. The top of the allocated
+ * area will be assigned to struct device; the next chunk will be
+ * assigned to DPA; and finally, the rest will be assigned to the
+ * the LAN API layer.
+ */
+ dev = (struct device *) kmalloc(dev_size, GFP_DMA | GFP_KERNEL |GFP_ATOMIC);
+ memset(dev, 0, dev_size);
+#ifdef RCDEBUG
+ printk("rc: dev = 0x%08X\n", (uint)dev);
+#endif
+
+ /*
+ * dev->priv will point to the start of DPA.
+ */
+ dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
+ pDpa = dev->priv;
+ dev->name = pDpa->devname;
+
+ pDpa->dev = dev; /* this is just for easy reference */
+ pDpa->function = function;
+ pDpa->bus = bus;
+ pDpa->id = card_idx; /* the device number */
+ pDpa->pci_addr = memaddr;
+ PCIAdapters[card_idx] = pDpa;
+#ifdef RCDEBUG
+ printk("rc: pDpa = 0x%x, id = %d \n", (uint)pDpa, (uint)pDpa->id);
+#endif
+
+ /*
+ * Save the starting address of the LAN API private area. We'll
+ * pass that to InitRCApiMsgLayer().
+ */
+ pDpa->PLanApiPA = (void *)(((long)pDpa + sizeof(DPA) + 0xff) & ~0xff);
+#ifdef RCDEBUG
+ printk("rc: pDpa->PLanApiPA = 0x%x\n", (uint)pDpa->PLanApiPA);
+#endif
+
+ /* The adapter is accessable through memory-access read/write, not
+ * I/O read/write. Thus, we need to map it to some virtual address
+ * area in order to access the registers are normal memory.
+ */
+ vaddr = (ulong *) ioremap(memaddr, 32768);
+#ifdef RCDEBUG
+ printk("rc: RCfound_device: 0x%x, priv = 0x%x, vaddr = 0x%x\n",
+ (uint)dev, (uint)dev->priv, (uint)vaddr);
+#endif
+ dev->base_addr = (unsigned long)vaddr;
+ dev->irq = irq;
+ dev->interrupt = 0;
+
+ /*
+ * Request a shared interrupt line.
+ */
+ if ( request_irq(dev->irq, (void *)RCinterrupt,
+ SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) )
+ {
+ printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq );
+ iounmap(vaddr);
+ kfree(dev);
+ return 0;
+ }
+
+ init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr,
+ pDpa->PLanApiPA, pDpa->PLanApiPA,
+ (PFNTXCALLBACK)RCxmit_callback,
+ (PFNRXCALLBACK)RCrecv_callback,
+ (PFNCALLBACK)RCreboot_callback);
+#ifdef RCDEBUG
+ printk("rc: msg initted: status = 0x%x\n", init_status);
+#endif
+ if (init_status)
+ {
+ printk("rc: Unable to initialize msg layer\n");
+ free_irq(dev->irq, dev);
+ vfree(vaddr);
+ kfree(dev);
+ return 0;
+ }
+ if (RCGetMAC(pDpa->id, dev->dev_addr, NULL))
+ {
+ printk("rc: Unable to get adapter MAC\n");
+ free_irq(dev->irq, dev);
+ vfree(vaddr);
+ kfree(dev);
+ return 0;
+ }
+
+ DriverControlWord |= WARM_REBOOT_CAPABLE;
+ RCReportDriverCapability(pDpa->id, DriverControlWord);
+
+ dev->init = RCprobe1;
+ ether_setup(dev); /* linux kernel interface */
+
+ pDpa->next = root_RCdev;
+ root_RCdev = dev;
+
+ if (register_netdev(dev) != 0) /* linux kernel interface */
+ {
+ printk("rc: unable to register device \n");
+ free_irq(dev->irq, dev);
+ vfree(vaddr);
+ kfree(dev);
+ return 0;
+ }
+ return dev;
+}
+
+static int RCprobe1(struct device *dev)
+{
+ dev->open = RCopen;
+ dev->hard_start_xmit = RC_xmit_packet;
+ dev->stop = RCclose;
+ dev->get_stats = RCget_stats;
+ dev->do_ioctl = RCioctl;
+ dev->set_config = RCconfig;
+ return 0;
+}
+
+static int
+RCopen(struct device *dev)
+{
+ int post_buffers = MAX_NMBR_RCV_BUFFERS;
+ PDPA pDpa = (PDPA) dev->priv;
+ int count = 0;
+ int requested = 0;
+
+#ifdef RCDEBUG
+ printk("rc: RCopen\n");
+#endif
+ RCEnableAdapterInterrupts(pDpa->id);
+
+ if (pDpa->nexus)
+ {
+ /* This is not the first time RCopen is called. Thus,
+ * the interface was previously opened and later closed
+ * by RCclose(). RCclose() does a Shutdown; to wake up
+ * the adapter, a reset is mandatory before we can post
+ * receive buffers. However, if the adapter initiated
+ * a reboot while the interface was closed -- and interrupts
+ * were turned off -- we need will need to reinitialize
+ * the adapter, rather than simply waking it up.
+ */
+ printk("rc: Waking up adapter...\n");
+ RCResetLANCard(pDpa->id,0,0,0);
+ }
+ else
+ {
+ pDpa->nexus = 1;
+ }
+
+ while(post_buffers)
+ {
+ if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+ requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
+ else
+ requested = post_buffers;
+ count = RC_allocate_and_post_buffers(dev, requested);
+
+ if ( count < requested )
+ {
+ /*
+ * Check to see if we were able to post any buffers at all.
+ */
+ if (post_buffers == MAX_NMBR_RCV_BUFFERS)
+ {
+ printk("rc: Error RCopen: not able to allocate any buffers\r\n");
+ return(-ENOMEM);
+ }
+ printk("rc: Warning RCopen: not able to allocate all requested buffers\r\n");
+ break; /* we'll try to post more buffers later */
+ }
+ else
+ post_buffers -= count;
+ }
+ pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers;
+ pDpa->shutdown = 0; /* just in case */
+#ifdef RCDEBUG
+ printk("rc: RCopen: posted %d buffers\n", (uint)pDpa->numOutRcvBuffers);
+#endif
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+RC_xmit_packet(struct sk_buff *skb, struct device *dev)
+{
+
+ PDPA pDpa = (PDPA) dev->priv;
+ singleTCB tcb;
+ psingleTCB ptcb = &tcb;
+ RC_RETURN status = 0;
+
+ if (dev->tbusy || pDpa->shutdown || pDpa->reboot)
+ {
+#ifdef RCDEBUG
+ printk("rc: RC_xmit_packet: tbusy!\n");
+#endif
+ return 1;
+ }
+
+ if ( skb->len <= 0 )
+ {
+ printk("RC_xmit_packet: skb->len less than 0!\n");
+ return 0;
+ }
+
+ /*
+ * The user is free to reuse the TCB after RCSendPacket() returns, since
+ * the function copies the necessary info into its own private space. Thus,
+ * our TCB can be a local structure. The skb, on the other hand, will be
+ * freed up in our interrupt handler.
+ */
+ ptcb->bcount = 1;
+ /*
+ * we'll get the context when the adapter interrupts us to tell us that
+ * the transmision is done. At that time, we can free skb.
+ */
+ ptcb->b.context = (U32)skb;
+ ptcb->b.scount = 1;
+ ptcb->b.size = skb->len;
+ ptcb->b.addr = (U32)skb->data;
+
+#ifdef RCDEBUG
+ printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n",
+ (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb);
+#endif
+ if ( (status = RCSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb))
+ != RC_RTN_NO_ERROR)
+ {
+#ifdef RCDEBUG
+ printk("rc: RC send error 0x%x\n", (uint)status);
+#endif
+ dev->tbusy = 1;
+ }
+ else
+ {
+ dev->trans_start = jiffies;
+ // dev->tbusy = 0;
+ }
+ /*
+ * That's it!
+ */
+ return 0;
+}
+
+/*
+ * RCxmit_callback()
+ *
+ * The transmit callback routine. It's called by RCProcMsgQ()
+ * because the adapter is done with one or more transmit buffers and
+ * it's returning them to us, or we asked the adapter to return the
+ * outstanding transmit buffers by calling RCResetLANCard() with
+ * RC_RESOURCE_RETURN_PEND_TX_BUFFERS flag.
+ * All we need to do is free the buffers.
+ */
+static void
+RCxmit_callback(U32 Status,
+ U16 PcktCount,
+ PU32 BufferContext,
+ U16 AdapterID)
+{
+ struct sk_buff *skb;
+ PDPA pDpa;
+ struct device *dev;
+
+ pDpa = PCIAdapters[AdapterID];
+ if (!pDpa)
+ {
+ printk("rc: Fatal error: xmit callback, !pDpa\n");
+ return;
+ }
+ dev = pDpa->dev;
+
+ // printk("xmit_callback: Status = 0x%x\n", (uint)Status);
+ if (Status != RC_REPLY_STATUS_SUCCESS)
+ {
+ printk("rc: xmit_callback: Status = 0x%x\n", (uint)Status);
+ }
+#ifdef RCDEBUG
+ if (pDpa->shutdown || pDpa->reboot)
+ printk("rc: xmit callback: shutdown||reboot\n");
+#endif
+
+#ifdef RCDEBUG
+ printk("rc: xmit_callback: PcktCount = %d, BC = 0x%x\n",
+ (uint)PcktCount, (uint)BufferContext);
+#endif
+ while (PcktCount--)
+ {
+ skb = (struct sk_buff *)(BufferContext[0]);
+#ifdef RCDEBUG
+ printk("rc: skb = 0x%x\n", (uint)skb);
+#endif
+ BufferContext++;
+ dev_kfree_skb(skb);
+ }
+ dev->tbusy = 0;
+
+}
+
+static void
+RCreset_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
+{
+ PDPA pDpa;
+ struct device *dev;
+
+ pDpa = PCIAdapters[AdapterID];
+ dev = pDpa->dev;
+#ifdef RCDEBUG
+ printk("rc: RCreset_callback Status 0x%x\n", (uint)Status);
+#endif
+ /*
+ * Check to see why we were called.
+ */
+ if (pDpa->shutdown)
+ {
+ printk("rc: Shutting down interface\n");
+ pDpa->shutdown = 0;
+ pDpa->reboot = 0;
+ MOD_DEC_USE_COUNT;
+ }
+ else if (pDpa->reboot)
+ {
+ printk("rc: reboot, shutdown adapter\n");
+ /*
+ * We don't set any of the flags in RCShutdownLANCard()
+ * and we don't pass a callback routine to it.
+ * The adapter will have already initiated the reboot by
+ * the time the function returns.
+ */
+ RCDisableAdapterInterrupts(pDpa->id);
+ RCShutdownLANCard(pDpa->id,0,0,0);
+ printk("rc: scheduling timer...\n");
+ init_timer(&pDpa->timer);
+ pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+ pDpa->timer.data = (unsigned long)dev;
+ pDpa->timer.function = &rc_timer; /* timer handler */
+ add_timer(&pDpa->timer);
+ }
+
+
+
+}
+
+static void
+RCreboot_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
+{
+ PDPA pDpa;
+
+ pDpa = PCIAdapters[AdapterID];
+#ifdef RCDEBUG
+ printk("rc: RCreboot: rcv buffers outstanding = %d\n",
+ (uint)pDpa->numOutRcvBuffers);
+#endif
+ if (pDpa->shutdown)
+ {
+ printk("rc: skipping reboot sequence -- shutdown already initiated\n");
+ return;
+ }
+ pDpa->reboot = 1;
+ /*
+ * OK, we reset the adapter and ask it to return all
+ * outstanding transmit buffers as well as the posted
+ * receive buffers. When the adapter is done returning
+ * those buffers, it will call our RCreset_callback()
+ * routine. In that routine, we'll call RCShutdownLANCard()
+ * to tell the adapter that it's OK to start the reboot and
+ * schedule a timer callback routine to execute 3 seconds
+ * later; this routine will reinitialize the adapter at that time.
+ */
+ RCResetLANCard(pDpa->id,
+ RC_RESOURCE_RETURN_POSTED_RX_BUCKETS |
+ RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+ (PFNCALLBACK)RCreset_callback);
+}
+
+
+int broadcast_packet(unsigned char * address)
+{
+ int i;
+ for (i=0; i<6; i++)
+ if (address[i] != 0xff) return 0;
+
+ return 1;
+}
+
+/*
+ * RCrecv_callback()
+ *
+ * The receive packet callback routine. This is called by
+ * RCProcMsgQ() after the adapter posts buffers which have been
+ * filled (one ethernet packet per buffer).
+ */
+static void
+RCrecv_callback(U32 Status,
+ U8 PktCount,
+ U32 BucketsRemain,
+ PU32 PacketDescBlock,
+ U16 AdapterID)
+{
+
+ U32 len, count;
+ PDPA pDpa;
+ struct sk_buff *skb;
+ struct device *dev;
+ singleTCB tcb;
+ psingleTCB ptcb = &tcb;
+
+
+ pDpa = PCIAdapters[AdapterID];
+ dev = pDpa->dev;
+
+ ptcb->bcount = 1;
+
+#ifdef RCDEBUG
+ printk("rc: RCrecv_callback: 0x%x, 0x%x, 0x%x\n",
+ (uint)PktCount, (uint)BucketsRemain, (uint)PacketDescBlock);
+#endif
+
+#ifdef RCDEBUG
+ if ((pDpa->shutdown || pDpa->reboot) && !Status)
+ printk("shutdown||reboot && !Status: PktCount = %d\n",PktCount);
+#endif
+
+ if ( (Status != RC_REPLY_STATUS_SUCCESS) || pDpa->shutdown)
+ {
+ /*
+ * Free whatever buffers the adapter returned, but don't
+ * pass them to the kernel.
+ */
+
+ if (!pDpa->shutdown && !pDpa->reboot)
+ printk("rc: RCrecv error: status = 0x%x\n", (uint)Status);
+ else
+ printk("rc: Returning %d buffers, status = 0x%x\n",
+ PktCount, (uint)Status);
+ /*
+ * TO DO: check the nature of the failure and put the adapter in
+ * failed mode if it's a hard failure. Send a reset to the adapter
+ * and free all outstanding memory.
+ */
+ if (Status == RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER)
+ {
+#ifdef RCDEBUG
+ printk("RCrecv status ABORT NO DATA TRANSFER\n");
+#endif
+ }
+ /* check for reset status: RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */
+ if (PacketDescBlock)
+ {
+ while(PktCount--)
+ {
+ skb = (struct sk_buff *)PacketDescBlock[0];
+#ifdef RCDEBUG
+ printk("free skb 0x%p\n", skb);
+#endif
+ dev_kfree_skb(skb);
+ pDpa->numOutRcvBuffers--;
+ PacketDescBlock += BD_SIZE; /* point to next context field */
+ }
+ }
+ return;
+ }
+ else
+ {
+ while(PktCount--)
+ {
+ skb = (struct sk_buff *)PacketDescBlock[0];
+#ifdef RCDEBUG
+ if (pDpa->shutdown)
+ printk("shutdown: skb=0x%x\n", (uint)skb);
+
+ printk("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", (uint)skb,
+ (uint)skb->data[0], (uint)skb->data[1], (uint)skb->data[2],
+ (uint)skb->data[3], (uint)skb->data[4], (uint)skb->data[5]);
+#endif
+ if ( (memcmp(dev->dev_addr, skb->data, 6)) &&
+ (!broadcast_packet(skb->data)))
+ {
+ /*
+ * Re-post the buffer to the adapter. Since the adapter usually
+ * return 1 to 2 receive buffers at a time, it's not too inefficient
+ * post one buffer at a time but ... may be that should be
+ * optimized at some point.
+ */
+ ptcb->b.context = (U32)skb;
+ ptcb->b.scount = 1;
+ ptcb->b.size = MAX_ETHER_SIZE;
+ ptcb->b.addr = (U32)skb->data;
+
+ if ( RCPostRecvBuffers(pDpa->id, (PRCTCB)ptcb ) != RC_RTN_NO_ERROR)
+ {
+ printk("rc: RCrecv_callback: post buffer failed!\n");
+ dev_kfree_skb(skb);
+ }
+ else
+ {
+ pDpa->numOutRcvBuffers++;
+ }
+ }
+ else
+ {
+ len = PacketDescBlock[2];
+ skb->dev = dev;
+ skb_put( skb, len ); /* adjust length and tail */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb); /* send the packet to the kernel */
+ dev->last_rx = jiffies;
+ }
+ pDpa->numOutRcvBuffers--;
+ PacketDescBlock += BD_SIZE; /* point to next context field */
+ }
+ }
+
+ /*
+ * Replenish the posted receive buffers.
+ * DO NOT replenish buffers if the driver has already
+ * initiated a reboot or shutdown!
+ */
+
+ if (!pDpa->shutdown && !pDpa->reboot)
+ {
+ count = RC_allocate_and_post_buffers(dev,
+ MAX_NMBR_RCV_BUFFERS-pDpa->numOutRcvBuffers);
+ pDpa->numOutRcvBuffers += count;
+ }
+
+}
+
+/*
+ * RCinterrupt()
+ *
+ * Interrupt handler.
+ * This routine sets up a couple of pointers and calls
+ * RCProcMsgQ(), which in turn process the message and
+ * calls one of our callback functions.
+ */
+static void
+RCinterrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+
+ PDPA pDpa;
+ struct device *dev = (struct device *)(dev_id);
+
+ pDpa = (PDPA) (dev->priv);
+
+ if (pDpa->shutdown)
+ printk("rc: shutdown: service irq\n");
+
+#ifdef RCDEBUG
+ printk("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n",
+ (uint)pDpa, (uint)dev, (uint)pDpa->id);
+ printk("dev = 0x%x\n", (uint)dev);
+#endif
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ RCProcMsgQ(pDpa->id);
+ dev->interrupt = 0;
+
+ return;
+}
+
+#define REBOOT_REINIT_RETRY_LIMIT 10
+static void rc_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ PDPA pDpa = (PDPA) (dev->priv);
+ int init_status;
+ static int retry = 0;
+ int post_buffers = MAX_NMBR_RCV_BUFFERS;
+ int count = 0;
+ int requested = 0;
+
+ if (pDpa->reboot)
+ {
+
+ init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr,
+ pDpa->PLanApiPA, pDpa->PLanApiPA,
+ (PFNTXCALLBACK)RCxmit_callback,
+ (PFNRXCALLBACK)RCrecv_callback,
+ (PFNCALLBACK)RCreboot_callback);
+
+ switch(init_status)
+ {
+ case RC_RTN_NO_ERROR:
+
+ pDpa->reboot = 0;
+ pDpa->shutdown = 0; /* just in case */
+ RCReportDriverCapability(pDpa->id, DriverControlWord);
+ RCEnableAdapterInterrupts(pDpa->id);
+
+ if (dev->flags & IFF_UP)
+ {
+ while(post_buffers)
+ {
+ if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+ requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
+ else
+ requested = post_buffers;
+ count = RC_allocate_and_post_buffers(dev, requested);
+ post_buffers -= count;
+ if ( count < requested )
+ break;
+ }
+ pDpa->numOutRcvBuffers =
+ MAX_NMBR_RCV_BUFFERS - post_buffers;
+ printk("rc: posted %d buffers \r\n",
+ (uint)pDpa->numOutRcvBuffers);
+ }
+ printk("rc: Initialization done.\n");
+ return;
+ case RC_RTN_FREE_Q_EMPTY:
+ retry++;
+ printk("rc: inbound free q emtpy\n");
+ break;
+ default:
+ retry++;
+ printk("rc: unexpected bad status after reboot\n");
+ break;
+ }
+
+ if (retry > REBOOT_REINIT_RETRY_LIMIT)
+ {
+ printk("rc: unable to reinitialize adapter after reboot\n");
+ printk("rc: decrementing driver and closing interface\n");
+ RCDisableAdapterInterrupts(pDpa->id);
+ dev->flags &= ~IFF_UP;
+ MOD_DEC_USE_COUNT;
+ }
+ else
+ {
+ printk("rc: rescheduling timer...\n");
+ init_timer(&pDpa->timer);
+ pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+ pDpa->timer.data = (unsigned long)dev;
+ pDpa->timer.function = &rc_timer; /* timer handler */
+ add_timer(&pDpa->timer);
+ }
+ }
+ else
+ {
+ printk("rc: timer??\n");
+ }
+}
+
+static int
+RCclose(struct device *dev)
+{
+
+ PDPA pDpa = (PDPA) dev->priv;
+
+#ifdef RCDEBUG
+ printk("rc: RCclose\r\n");
+#endif
+ if (pDpa->reboot)
+ {
+ printk("rc: skipping reset -- adapter already in reboot mode\n");
+ dev->flags &= ~IFF_UP;
+ pDpa->shutdown = 1;
+ return 0;
+ }
+#ifdef RCDEBUG
+ printk("rc: receive buffers outstanding: %d\n",
+ (uint)pDpa->numOutRcvBuffers);
+#endif
+
+ pDpa->shutdown = 1;
+
+ /*
+ * We can't allow the driver to be unloaded until the adapter returns
+ * all posted receive buffers. It doesn't hurt to tell the adapter
+ * to return all posted receive buffers and outstanding xmit buffers,
+ * even if there are none.
+ */
+
+ RCShutdownLANCard(pDpa->id,
+ RC_RESOURCE_RETURN_POSTED_RX_BUCKETS |
+ RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+ (PFNCALLBACK)RCreset_callback);
+
+ dev->flags &= ~IFF_UP;
+ return 0;
+}
+
+static struct enet_statistics *
+RCget_stats(struct device *dev)
+{
+ RCLINKSTATS RCstats;
+
+ PDPA pDpa = dev->priv;
+
+ if (!pDpa)
+ {
+ printk("rc: RCget_stats: !pDpa\n");
+ return 0;
+ }
+ else if (!(dev->flags & IFF_UP))
+ {
+#ifdef RCDEBUG
+ printk("rc: RCget_stats: device down\n");
+#endif
+ return 0;
+ }
+
+ memset(&RCstats, 0, sizeof(RCLINKSTATS));
+ if ( (RCGetLinkStatistics(pDpa->id, &RCstats, (void *)0)) == RC_RTN_NO_ERROR )
+ {
+#ifdef RCDEBUG
+ printk("rc: TX_good 0x%x\n", (uint)RCstats.TX_good);
+ printk("rc: TX_maxcol 0x%x\n", (uint)RCstats.TX_maxcol);
+ printk("rc: TX_latecol 0x%x\n", (uint)RCstats.TX_latecol);
+ printk("rc: TX_urun 0x%x\n", (uint)RCstats.TX_urun);
+ printk("rc: TX_crs 0x%x\n", (uint)RCstats.TX_crs);
+ printk("rc: TX_def 0x%x\n", (uint)RCstats.TX_def);
+ printk("rc: TX_singlecol 0x%x\n", (uint)RCstats.TX_singlecol);
+ printk("rc: TX_multcol 0x%x\n", (uint)RCstats.TX_multcol);
+ printk("rc: TX_totcol 0x%x\n", (uint)RCstats.TX_totcol);
+
+ printk("rc: Rcv_good 0x%x\n", (uint)RCstats.Rcv_good);
+ printk("rc: Rcv_CRCerr 0x%x\n", (uint)RCstats.Rcv_CRCerr);
+ printk("rc: Rcv_alignerr 0x%x\n", (uint)RCstats.Rcv_alignerr);
+ printk("rc: Rcv_reserr 0x%x\n", (uint)RCstats.Rcv_reserr);
+ printk("rc: Rcv_orun 0x%x\n", (uint)RCstats.Rcv_orun);
+ printk("rc: Rcv_cdt 0x%x\n", (uint)RCstats.Rcv_cdt);
+ printk("rc: Rcv_runt 0x%x\n", (uint)RCstats.Rcv_runt);
+#endif
+
+ pDpa->stats.rx_packets = RCstats.Rcv_good; /* total packets received */
+ pDpa->stats.tx_packets = RCstats.TX_good; /* total packets transmitted */
+
+ pDpa->stats.rx_errors =
+ RCstats.Rcv_CRCerr +
+ RCstats.Rcv_alignerr +
+ RCstats.Rcv_reserr +
+ RCstats.Rcv_orun +
+ RCstats.Rcv_cdt +
+ RCstats.Rcv_runt; /* bad packets received */
+
+ pDpa->stats.tx_errors =
+ RCstats.TX_urun +
+ RCstats.TX_crs +
+ RCstats.TX_def +
+ RCstats.TX_totcol; /* packet transmit problems */
+
+ /*
+ * This needs improvement.
+ */
+ pDpa->stats.rx_dropped = 0; /* no space in linux buffers */
+ pDpa->stats.tx_dropped = 0; /* no space available in linux */
+ pDpa->stats.multicast = 0; /* multicast packets received */
+ pDpa->stats.collisions = RCstats.TX_totcol;
+
+ /* detailed rx_errors: */
+ pDpa->stats.rx_length_errors = 0;
+ pDpa->stats.rx_over_errors = RCstats.Rcv_orun; /* receiver ring buff overflow */
+ pDpa->stats.rx_crc_errors = RCstats.Rcv_CRCerr; /* recved pkt with crc error */
+ pDpa->stats.rx_frame_errors = 0; /* recv'd frame alignment error */
+ pDpa->stats.rx_fifo_errors = 0; /* recv'r fifo overrun */
+ pDpa->stats.rx_missed_errors = 0; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ pDpa->stats.tx_aborted_errors = 0;
+ pDpa->stats.tx_carrier_errors = 0;
+ pDpa->stats.tx_fifo_errors = 0;
+ pDpa->stats.tx_heartbeat_errors = 0;
+ pDpa->stats.tx_window_errors = 0;
+
+ return ((struct enet_statistics *)&(pDpa->stats));
+ }
+ return 0;
+}
+
+static int RCioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ RCuser_struct RCuser;
+ PDPA pDpa = dev->priv;
+
+#if RCDEBUG
+ printk("RCioctl: cmd = 0x%x\n", cmd);
+#endif
+
+ switch (cmd) {
+
+ case RCU_PROTOCOL_REV:
+ /*
+ * Assign user protocol revision, to tell user-level
+ * controller program whether or not it's in sync.
+ */
+ rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV;
+ break;
+
+
+ case RCU_COMMAND:
+ {
+ if(copy_from_user(&RCuser, rq->ifr_data, sizeof(RCuser)))
+ return -EFAULT;
+
+#ifdef RCDEBUG
+ printk("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd);
+#endif
+
+ switch(RCuser.cmd)
+ {
+ case RCUC_GETFWVER:
+ printk("RC GETFWVER\n");
+ RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
+ RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL);
+ break;
+ case RCUC_GETINFO:
+ printk("RC GETINFO\n");
+ RCUD_GETINFO = &RCuser.RCUS_GETINFO;
+ RCUD_GETINFO -> mem_start = dev->base_addr;
+ RCUD_GETINFO -> mem_end = dev->base_addr + 32768;
+ RCUD_GETINFO -> base_addr = pDpa->pci_addr;
+ RCUD_GETINFO -> irq = dev->irq;
+ break;
+ case RCUC_GETIPANDMASK:
+ printk("RC GETIPANDMASK\n");
+ RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
+ RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr,
+ (PU32) &RCUD_GETIPANDMASK->NetMask, NULL);
+ break;
+ case RCUC_GETLINKSTATISTICS:
+ printk("RC GETLINKSTATISTICS\n");
+ RCUD_GETLINKSTATISTICS = &RCuser.RCUS_GETLINKSTATISTICS;
+ RCGetLinkStatistics(pDpa->id, (P_RCLINKSTATS) &RCUD_GETLINKSTATISTICS->StatsReturn, NULL);
+ break;
+ case RCUC_GETLINKSTATUS:
+ printk("RC GETLINKSTATUS\n");
+ RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
+ RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
+ break;
+ case RCUC_GETMAC:
+ printk("RC GETMAC\n");
+ RCUD_GETMAC = &RCuser.RCUS_GETMAC;
+ RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL);
+ break;
+ case RCUC_GETSPEED:
+ printk("RC GETSPEED\n");
+ if (!(dev->flags & IFF_UP))
+ {
+ printk("RCioctl, GETSPEED error: interface down\n");
+ return -ENODATA;
+ }
+ RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
+ RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL);
+ printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode);
+ break;
+ default:
+ printk("RC command default\n");
+ RCUD_DEFAULT = &RCuser.RCUS_DEFAULT;
+ RCUD_DEFAULT -> rc = 0x11223344;
+ break;
+ }
+ copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+ break;
+ } /* RCU_COMMAND */
+
+ default:
+ printk("RC default\n");
+ rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+ break;
+ }
+ return 0;
+}
+
+static int RCconfig(struct device *dev, struct ifmap *map)
+{
+ /*
+ * To be completed ...
+ */
+ printk("rc: RCconfig\n");
+ return 0;
+ if (dev->flags & IFF_UP) /* can't act on a running interface */
+ return -EBUSY;
+
+ /* Don't allow changing the I/O address */
+ if (map->base_addr != dev->base_addr) {
+ printk(KERN_WARNING "RC pci45: Change I/O address not implemented\n");
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ PDPA pDpa;
+ struct device *next;
+
+
+#ifdef RCDEBUG
+ printk("rc: RC cleanup_module\n");
+ printk("rc: root_RCdev = 0x%x\n", (uint)root_RCdev);
+#endif
+
+
+ while (root_RCdev)
+ {
+ pDpa = (PDPA) root_RCdev->priv;
+#ifdef RCDEBUG
+ printk("rc: cleanup 0x%08X\n", (uint)root_RCdev);
+#endif
+ printk("Adapter reset: 0x%x\n", RCResetAdapter(pDpa->id));
+ unregister_netdev(root_RCdev);
+ next = pDpa->next;
+
+ vfree((unsigned long *)root_RCdev->base_addr);
+ free_irq( root_RCdev->irq, root_RCdev );
+ kfree(root_RCdev);
+ root_RCdev = next;
+ }
+}
+#endif
+
+
+static int
+RC_allocate_and_post_buffers(struct device *dev, int numBuffers)
+{
+
+ int i;
+ PDPA pDpa = (PDPA)dev->priv;
+ PU32 p;
+ psingleB pB;
+ struct sk_buff *skb;
+ RC_RETURN status;
+
+ if (!numBuffers)
+ return 0;
+ else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+ {
+#ifdef RCDEBUG
+ printk("rc: Too many buffers requested!\n");
+ printk("rc: attempting to allocate only 32 buffers\n");
+#endif
+ numBuffers = 32;
+ }
+
+ p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
+
+#ifdef RCDEBUG
+ printk("rc: TCB = 0x%x\n", (uint)p);
+#endif
+
+ if (!p)
+ {
+ printk("rc: RCopen: unable to allocate TCB\n");
+ return 0;
+ }
+
+ p[0] = 0; /* Buffer Count */
+ pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+
+#ifdef RCDEBUG
+ printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB);
+ printk("rc: pB = 0x%x\n", (uint)pB);
+#endif
+
+ for (i=0; i<numBuffers; i++)
+ {
+ skb = dev_alloc_skb(MAX_ETHER_SIZE+2);
+ if (!skb)
+ {
+ printk("rc: Doh! RCopen: unable to allocate enough skbs!\n");
+ if (*p != 0) /* did we allocate any buffers at all? */
+ {
+#ifdef RCDEBUG
+ printk("rc: will post only %d buffers \n", (uint)(*p));
+#endif
+ break;
+ }
+ else
+ {
+ kfree(p); /* Free the TCB */
+ return 0;
+ }
+ }
+#ifdef RCDEBUG
+ printk("post 0x%x\n", (uint)skb);
+#endif
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ pB->context = (U32)skb;
+ pB->scount = 1; /* segment count */
+ pB->size = MAX_ETHER_SIZE;
+ pB->addr = (U32)skb->data;
+ p[0]++;
+ pB++;
+ }
+
+ if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR)
+ {
+ printk("rc: Post buffer failed with error code 0x%x!\n", status);
+ pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+ while(p[0])
+ {
+ skb = (struct sk_buff *)pB->context;
+#ifdef RCDEBUG
+ printk("rc: freeing 0x%x\n", (uint)skb);
+#endif
+ dev_kfree_skb(skb);
+ p[0]--;
+ pB++;
+ }
+#ifdef RCDEBUG
+ printk("rc: freed all buffers, p[0] = %ld\n", p[0]);
+#endif
+ }
+ kfree(p);
+ return(p[0]); /* return the number of posted buffers */
+}
*/
#include <linux/config.h>
-#ifdef CONFIG_INET
-/* Entire module is for IP only */
#include <linux/module.h>
-
#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+
+#ifdef CONFIG_INET
+/* Entire module is for IP only */
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/termios.h>
#include <net/tcp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
-#include <linux/errno.h>
#include <linux/timer.h>
#include <asm/system.h>
#include <asm/uaccess.h>
}
#endif /* MODULE */
+#else /* CONFIG_INET */
+EXPORT_SYMBOL(slhc_init);
+EXPORT_SYMBOL(slhc_free);
+EXPORT_SYMBOL(slhc_remember);
+EXPORT_SYMBOL(slhc_compress);
+EXPORT_SYMBOL(slhc_uncompress);
+EXPORT_SYMBOL(slhc_toss);
+
+int
+slhc_toss(struct slcompress *comp)
+{
+ printk(KERN_DEBUG "Called IP function on non IP-system: slhc_toss");
+ return -EINVAL;
+}
+int
+slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
+{
+ printk(KERN_DEBUG "Called IP function on non IP-system: slhc_uncompress");
+ return -EINVAL;
+}
+int
+slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
+ unsigned char *ocp, unsigned char **cpp, int compress_cid)
+{
+ printk(KERN_DEBUG "Called IP function on non IP-system: slhc_compress");
+ return -EINVAL;
+}
+
+int
+slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
+{
+ printk(KERN_DEBUG "Called IP function on non IP-system: slhc_remember");
+ return -EINVAL;
+}
+
+void
+slhc_free(struct slcompress *comp)
+{
+ printk(KERN_DEBUG "Called IP function on non IP-system: slhc_free");
+ return;
+}
+struct slcompress *
+slhc_init(int rslots, int tslots)
+{
+ printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init");
+ return NULL;
+}
+
#endif /* CONFIG_INET */
#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 <linux/version.h>
-#ifdef MODULE
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
#include <linux/module.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#define RUN_AT(x) (jiffies + (x))
#if (LINUX_VERSION_CODE >= 0x20100)
-char kernel_version[] = UTS_RELEASE;
#else
#ifndef __alpha__
#define ioremap vremap
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT ((2000*HZ)/1000)
-#ifdef MODULE
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/skbuff.h>
/* Kernel compatibility defines, most common to the PCCard package. */
-#include <linux/version.h> /* Evil, but neccessary */
+#include <linux/version.h> /* Evil and unneccessary */
#define RUN_AT(x) (jiffies + (x))
-#if (LINUX_VERSION_CODE >= 0x20100)
-char kernel_version[] = UTS_RELEASE;
-#endif
#if (LINUX_VERSION_CODE < 0x20123)
#define test_and_set_bit(val, addr) set_bit(val, addr)
#endif
static void yellowfin_timer(unsigned long data);
enum capability_flags {HasMII=1, FullTxStatus=2};
-struct chip_info {
+static struct chip_info {
u16 vendor_id, device_id, device_id_mask, pci_flags;
const char *name;
void (*media_timer)(unsigned long data);
unsigned int count = 0;
unsigned char z=0;
unsigned char Byte=0;
+ unsigned long igiveupat=jiffies+5*HZ;
- for (i=0; ; i++) {
+ for (i=0; time_before(jiffies, igiveupat); i++) {
+ /* if(current->need_resched) schedule(); */
parport_write_control(port, parport_read_control(port) | 2); /* AutoFeed high */
if (parport_wait_peripheral(port, 0x40, 0)) {
#ifdef DEBUG_PROBE
comment 'SCSI low-level drivers'
dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
+dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI
dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
+dep_tristate 'AMI MegaRAID support' CONFIG_SCSI_MEGARAID $CONFIG_SCSI
+
dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
bool ' Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
fi
dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
-dep_tristate 'EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
-dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI
if [ "$CONFIG_SCSI_EATA" != "n" ]; then
bool ' enable tagged command queueing' CONFIG_SCSI_EATA_TAGGED_QUEUE
bool ' enable elevator sorting' CONFIG_SCSI_EATA_LINKED_COMMANDS
int ' maximum number of queued commands' CONFIG_SCSI_EATA_MAX_TAGS 16
fi
+dep_tristate 'EATA-DMA [Obsolete] (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
+dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
dep_tristate 'Future Domain 16xx SCSI/AHA 2920 support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
if [ "$CONFIG_MCA" = "y" ]; then
if [ "$CONFIG_SCSI" = "y" ]; then
"Port CONFIG_SCSI_G_NCR5380_PORT \
Memory CONFIG_SCSI_G_NCR5380_MEM" Port
fi
+if [ "$CONFIG_PCI" = "y" ]; then
+ dep_tristate 'Initio 9100U(W) support' CONFIG_SCSI_INITIO $CONFIG_SCSI
+fi
dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI
bool ' Reset SCSI-devices at boottime' CONFIG_IBMMCA_SCSI_DEV_RESET
fi
fi
+if [ "$CONFIG_MCA" = "y" ]; then
+ dep_tristate 'NCR MCA 53C9x SCSI support' CONFIG_SCSI_MCA_53C9X $CONFIG_SCSI
+fi
dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
dep_tristate 'PCI2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI
dep_tristate 'PCI2220i support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI
dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
+ dep_tristate 'Qlogic ISP FC SCSI support' CONFIG_SCSI_QLOGIC_FC $CONFIG_SCSI
fi
dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
if [ "$CONFIG_PCI" = "y" ]; then
endif
endif
+ifeq ($(CONFIG_SCSI_MCA_53C9X),y)
+L_OBJS += NCR53C9x.o mca_53c9x.o
+else
+ ifeq ($(CONFIG_SCSI_MCA_53C9X),m)
+ M_OBJS += NCR53C9x.o mca_53c9x.o
+ endif
+endif
+
ifeq ($(CONFIG_CYBERSTORM_SCSI),y)
L_OBJS += NCR53C9x.o cyberstorm.o
else
endif
endif
+ifeq ($(CONFIG_SCSI_ACARD),y)
+L_OBJS += atp870u.o
+else
+ ifeq ($(CONFIG_SCSI_ACARD),m)
+ M_OBJS += atp870u.o
+ endif
+endif
+
+ifeq ($(CONFIG_SCSI_INITIO),y)
+L_OBJS += initio.o
+else
+ ifeq ($(CONFIG_SCSI_INITIO),m)
+ M_OBJS += initio.o
+ endif
+endif
+
+ifeq ($(CONFIG_SCSI_QLOGIC_FC),y)
+L_OBJS += qlogicfc.o
+else
+ ifeq ($(CONFIG_SCSI_QLOGIC_FC),m)
+ M_OBJS += qlogicfc.o
+ endif
+endif
ifeq ($(CONFIG_SCSI_AHA152X),y)
L_OBJS += aha152x.o
endif
endif
+ifeq ($(CONFIG_SCSI_MEGARAID),y)
+L_OBJS += megaraid.o
+else
+ ifeq ($(CONFIG_SCSI_MEGARAID),m)
+ M_OBJS += megaraid.o
+ endif
+endif
+
ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
L_OBJS += ide-scsi.o
else
53c7xx.o : 53c7xx_d.h 53c7xx.c
$(CC) $(CFLAGS) -c 53c7xx.c
+initio.o: ini9100u.c i91uscsi.c
+ $(CC) $(CFLAGS) -c ini9100u.c -o ini9100u.o
+ $(CC) $(CFLAGS) -c i91uscsi.c -o i91uscsi.o
+ $(LD) -r -o initio.o ini9100u.o i91uscsi.o
+ rm -f ini9100u.o i91uscsi.o
+
+megaraid.o: megaraid.c
+ $(CC) $(CFLAGS) -c megaraid.c
+
scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
scsicam.o scsi_proc.o scsi_error.o scsi_obsolete.o scsi_queue.o
$(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o \
spin_lock_irq(&io_request_lock);
if (time_after_eq(jiffies, timeout)) {
- printk("scsi%d: timeout at NCR5380.c:%d\n", __LINE__);
+ printk("scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
return -1;
}
*
* Most DMA dependencies put in driver specific files by
* Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * Set up to use GETREG/SETREG (preprocessor macros in NCR53c9x.h) by
+ * Tymm Twillman (tymm@coe.missouri.edu)
*/
/* TODO:
#include <linux/blk.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
+
#include <linux/init.h>
#include "scsi.h"
esp->espcmdlog[esp->espcmdent] = cmd;
esp->espcmdent = (esp->espcmdent + 1) & 31;
#endif
- eregs->esp_cmd = cmd;
+ SETREG(eregs->esp_cmnd, cmd);
}
/* How we use the various Linux SCSI data structures for operation.
*/
esp->max_period = ((35 * esp->ccycle) / 1000);
if(esp->erev == fast) {
- version = eregs->esp_uid;
+ version = GETREG(eregs->esp_uid);
family_code = (version & 0xf8) >> 3;
#ifdef SYMBIOS_HACK
if (version == 0 && family_code == 0)
}
/* Reload the configuration registers */
- eregs->esp_cfact = esp->cfact;
- eregs->esp_stp = 0;
- eregs->esp_soff = 0;
- eregs->esp_timeo = esp->neg_defp;
+ SETREG(eregs->esp_cfact, esp->cfact);
+ SETREG(eregs->esp_stp, 0);
+ SETREG(eregs->esp_soff, 0);
+ SETREG(eregs->esp_timeo, esp->neg_defp);
esp->max_period = (esp->max_period + 3)>>2;
esp->min_period = (esp->min_period + 3)>>2;
- eregs->esp_cfg1 = esp->config1;
+ SETREG(eregs->esp_cfg1, esp->config1);
switch(esp->erev) {
case esp100:
/* nothing to do */
break;
case esp100a:
- eregs->esp_cfg2 = esp->config2;
+ SETREG(eregs->esp_cfg2, esp->config2);
break;
case esp236:
/* Slow 236 */
- eregs->esp_cfg2 = esp->config2;
- eregs->esp_cfg3 = esp->config3[0];
+ SETREG(eregs->esp_cfg2, esp->config2);
+ SETREG(eregs->esp_cfg3, esp->config3[0]);
break;
case fashme:
esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
case fas216:
case fas236:
/* Fast 236 or HME */
- eregs->esp_cfg2 = esp->config2;
+ SETREG(eregs->esp_cfg2, esp->config2);
for(i=0; i<8; i++) {
if(esp->erev == fashme)
esp->config3[i] |=
else
esp->config3[i] |= ESP_CONFIG3_FCLK;
}
- eregs->esp_cfg3 = esp->config3[0];
+ SETREG(eregs->esp_cfg3, esp->config3[0]);
if(esp->erev == fashme) {
esp->radelay = 80;
} else {
break;
case fas100a:
/* Fast 100a */
- eregs->esp_cfg2 = esp->config2;
+ SETREG(eregs->esp_cfg2, esp->config2);
for(i=0; i<8; i++)
esp->config3[i] |= ESP_CONFIG3_FCLOCK;
- eregs->esp_cfg3 = esp->config3[0];
+ SETREG(eregs->esp_cfg3, esp->config3[0]);
esp->radelay = 32;
break;
default:
};
/* Eat any bitrot in the chip */
- trash = eregs->esp_intrpt;
+ trash = GETREG(eregs->esp_intrpt);
udelay(100);
}
esp_reset_esp(esp, eregs);
/* Reset the SCSI bus, but tell ESP not to generate an irq */
- eregs->esp_cfg1 |= ESP_CONFIG1_SRRDISAB;
+ SETREG(eregs->esp_cfg1, GETREG(eregs->esp_cfg1) | ESP_CONFIG1_SRRDISAB);
esp_cmd(esp, eregs, ESP_CMD_RS);
udelay(400);
- eregs->esp_cfg1 = esp->config1;
+ SETREG(eregs->esp_cfg1, esp->config1);
/* Eat any bitrot in the chip and we are done... */
- trash = eregs->esp_intrpt;
+ trash = GETREG(eregs->esp_intrpt);
}
/* Allocate structure and insert basic data such as SCSI chip frequency
/* Probe the revision of this esp */
esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
- eregs->esp_cfg2 = esp->config2;
+ SETREG(eregs->esp_cfg2, esp->config2);
#ifndef SYMBIOS_HACK
- if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
+ if((GETREG(eregs->esp_cfg2) & ~(ESP_CONFIG2_MAGIC)) !=
(ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
printk("NCR53C90(esp100) detected\n");
esp->erev = esp100;
} else {
#endif
- eregs->esp_cfg2 = esp->config2 = 0;
- eregs->esp_cfg3 = 0;
- eregs->esp_cfg3 = esp->config3[0] = 5;
+ esp->config2 = 0;
+ SETREG(eregs->esp_cfg2, esp->config2);
+ SETREG(eregs->esp_cfg3, 0);
+ esp->config3[0] = 5;
+ SETREG(eregs->esp_cfg3, esp->config3[0]);
#ifndef SYMBIOS_HACK
- if(eregs->esp_cfg3 != 5) {
+ if(GETREG(eregs->esp_cfg3) != 5) {
printk("NCR53C90A(esp100a) detected\n");
esp->erev = esp100a;
} else {
for(target=0; target<8; target++)
esp->config3[target] = 0;
- eregs->esp_cfg3 = 0;
+ SETREG(eregs->esp_cfg3, 0);
#ifndef SYMBIOS_HACK
if(ccf > ESP_CCF_F5) {
#endif
printk("NCR53C9XF(espfast) detected\n");
esp->erev = fast;
- eregs->esp_cfg2 = esp->config2 = 0;
+ esp->config2 = 0;
+ SETREG(eregs->esp_cfg2, esp->config2);
esp->sync_defp = SYNC_DEFP_FAST;
#ifndef SYMBIOS_HACK
} else {
printk("NCR53C9x(esp236) detected\n");
esp->erev = esp236;
- eregs->esp_cfg2 = esp->config2 = 0;
+ esp->config2 = 0;
+ SETREG(eregs->esp_cfg2, esp->config2);
}
}
#endif
/* HME sucks... */
if(esp->erev == fashme)
- eregs->esp_busid = (target & 0xf) |
- (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT);
+ SETREG(eregs->esp_busid, (target & 0xf) |
+ (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT));
else
- eregs->esp_busid = (target & 7);
- eregs->esp_soff = SDptr->sync_max_offset;
- eregs->esp_stp = SDptr->sync_min_period;
+ SETREG(eregs->esp_busid, (target & 7));
+ SETREG(eregs->esp_soff, SDptr->sync_max_offset);
+ SETREG(eregs->esp_stp, SDptr->sync_min_period);
if(esp->erev > esp100a)
- eregs->esp_cfg3 = esp->config3[target];
+ SETREG(eregs->esp_cfg3, esp->config3[target]);
i = (cmdp - esp->esp_command);
int j = 0;
for(;j<i;j++)
- eregs->esp_fdata = esp->esp_command[j];
+ SETREG(eregs->esp_fdata, esp->esp_command[j]);
the_esp_command &= ~ESP_CMD_DMA;
/* Tell ESP to "go". */
esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Grrr! */
/* Set up the HME counters */
- eregs->esp_tclow = i;
- eregs->esp_tcmed = 0;
- eregs->fas_rlo = 0;
- eregs->fas_rhi = 0;
+ SETREG(eregs->esp_tclow, i);
+ SETREG(eregs->esp_tcmed, 0);
+ SETREG(eregs->fas_rlo, 0);
+ SETREG(eregs->fas_rhi, 0);
esp_cmd(esp, eregs, the_esp_command);
esp->dma_init_write(esp, esp->esp_command_dvma, 16);
} else {
/* Set up the ESP counters */
- eregs->esp_tclow = i;
- eregs->esp_tcmed = 0;
+ SETREG(eregs->esp_tclow, i);
+ SETREG(eregs->esp_tcmed, 0);
esp->dma_init_write(esp, esp->esp_command_dvma, i);
/* Tell ESP to "go". */
ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
- esp->esp_id, eregs->esp_status, eregs->esp_sstep, eregs->esp_intrpt));
+ esp->esp_id, GETREG(eregs->esp_status),
+ GETREG(eregs->esp_sstep), GETREG(eregs->esp_intrpt)));
#ifdef DEBUG_ESP_CMDS
printk("esp%d: last ESP cmds [", esp->esp_id);
i = (esp->espcmdent - 1) & 31;
return;
} else {
unsigned long count = 0;
- unsigned long fcnt = eregs->esp_fflags & ESP_FF_FBYTES;
+ unsigned long fcnt = GETREG(eregs->esp_fflags) & ESP_FF_FBYTES;
/* The HME stores bytes in multiples of 2 in the fifo. */
ESPHME(("hme_fifo[fcnt=%d", (int)fcnt));
while(fcnt) {
- esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata;
- esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata;
+ esp->hme_fifo_workaround_buffer[count++] =
+ GETREG(eregs->esp_fdata);
+ esp->hme_fifo_workaround_buffer[count++] =
+ GETREG(eregs->esp_fdata);
ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1]));
fcnt--;
}
- if(eregs->esp_status2 & ESP_STAT2_F1BYTE) {
+ if(GETREG(eregs->esp_status2) & ESP_STAT2_F1BYTE) {
ESPHME(("<poke_byte>"));
- eregs->esp_fdata = 0;
- esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata;
+ SETREG(eregs->esp_fdata, 0);
+ esp->hme_fifo_workaround_buffer[count++] =
+ GETREG(eregs->esp_fdata);
ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1]));
ESPHME(("CMD_FLUSH"));
esp_cmd(esp, eregs, ESP_CMD_FLUSH);
{
esp_cmd(esp, eregs, ESP_CMD_FLUSH);
while(count) {
- eregs->esp_fdata = *bytes++;
- eregs->esp_fdata = 0;
+ SETREG(eregs->esp_fdata, *bytes++);
+ SETREG(eregs->esp_fdata, 0);
count--;
}
}
if(esp->dma_irq_p(esp)) {
/* Yes, we are able to save an interrupt. */
- esp->sreg = eregs->esp_status;
+ esp->sreg = GETREG(eregs->esp_status);
if(esp->erev == fashme) {
/* This chip is really losing. */
ESPHME(("HME["));
ESPHME(("fifo_workaround]"));
hme_fifo_hwbug_workaround(esp, eregs);
}
- esp->ireg = eregs->esp_intrpt;
+ esp->ireg = GETREG(eregs->esp_intrpt);
esp->sreg &= ~(ESP_STAT_INTR);
if(!(esp->ireg & ESP_INTR_SR))
return 0;
return 0;
if(esp->dma_irq_p(esp)) {
/* Yes, we are able to save an interrupt. */
- esp->sreg = eregs->esp_status;
+ esp->sreg = GETREG(eregs->esp_status);
if(esp->erev == fashme) {
/* This chip is really losing. */
ESPHME(("HME["));
ESPHME(("fifo_workaround]"));
hme_fifo_hwbug_workaround(esp, eregs);
}
- esp->ireg = eregs->esp_intrpt;
+ esp->ireg = GETREG(eregs->esp_intrpt);
esp->sreg &= ~(ESP_STAT_INTR);
if(!(esp->ireg & ESP_INTR_SR))
return 0;
/* Misc. esp helper routines. */
static inline void esp_setcount(struct ESP_regs *eregs, int cnt, int hme)
{
- eregs->esp_tclow = (cnt & 0xff);
- eregs->esp_tcmed = ((cnt >> 8) & 0xff);
+ SETREG(eregs->esp_tclow, (cnt & 0xff));
+ SETREG(eregs->esp_tcmed, ((cnt >> 8) & 0xff));
if(hme) {
- eregs->fas_rlo = 0;
- eregs->fas_rhi = 0;
+ SETREG(eregs->fas_rlo, 0);
+ SETREG(eregs->fas_rhi, 0);
}
}
static inline int esp_getcount(struct ESP_regs *eregs)
{
- return (((eregs->esp_tclow)&0xff) |
- (((eregs->esp_tcmed)&0xff) << 8));
+ return (((GETREG(eregs->esp_tclow))&0xff) |
+ (((GETREG(eregs->esp_tcmed))&0xff) << 8));
}
static inline int fcount(struct NCR_ESP *esp, struct ESP_regs *eregs)
if(esp->erev == fashme)
return esp->hme_fifo_workaround_count;
else
- return eregs->esp_fflags & ESP_FF_FBYTES;
+ return GETREG(eregs->esp_fflags) & ESP_FF_FBYTES;
}
static inline int fnzero(struct NCR_ESP *esp, struct ESP_regs *eregs)
if(esp->erev == fashme)
return 0;
else
- return eregs->esp_fflags & ESP_FF_ONOTZERO;
+ return GETREG(eregs->esp_fflags) & ESP_FF_ONOTZERO;
}
/* XXX speculative nops unnecessary when continuing amidst a data phase
{
/* Do not touch this piece of code. */
if((!(esp->erev == esp100)) ||
- (!(sreg_datainp((esp->sreg = eregs->esp_status)) && !fifocnt) &&
- !(sreg_dataoutp(esp->sreg) && !fnzero(esp, eregs)))) {
+ (!(sreg_datainp((esp->sreg = GETREG(eregs->esp_status))) && !fifocnt)
+ && !(sreg_dataoutp(esp->sreg) && !fnzero(esp, eregs)))) {
if(sp->SCp.phase == in_dataout)
esp_cmd(esp, eregs, ESP_CMD_FLUSH);
return 0;
if(esp->erev != esp100)
return 0;
- junk = eregs->esp_intrpt;
+ junk = GETREG(eregs->esp_intrpt);
if(junk & ESP_INTR_SR)
return 1;
*/
targ = esp->hme_fifo_workaround_buffer[0];
} else {
- it = eregs->esp_fdata;
+ it = GETREG(eregs->esp_fdata);
if(!(it & me))
return -1;
it &= ~me;
if(esp->erev == fashme)
lun = esp->hme_fifo_workaround_buffer[1];
else
- lun = eregs->esp_fdata;
+ lun = GETREG(eregs->esp_fdata);
if(esp->sreg & ESP_STAT_PERR)
return 0;
if((lun & 0x40) || !(lun & 0x80))
Scsi_Cmnd *sp)
{
Scsi_Device *dp = sp->device;
- eregs->esp_soff = dp->sync_max_offset;
- eregs->esp_stp = dp->sync_min_period;
+ SETREG(eregs->esp_soff, dp->sync_max_offset);
+ SETREG(eregs->esp_stp, dp->sync_min_period);
if(esp->erev > esp100a)
- eregs->esp_cfg3 = esp->config3[sp->target];
+ SETREG(eregs->esp_cfg3, esp->config3[sp->target]);
if(esp->erev == fashme)
- eregs->esp_busid = (sp->target & 0xf) |
- (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT);
+ SETREG(eregs->esp_busid, (sp->target & 0xf) |
+ (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT));
esp->current_SC = sp;
}
static inline int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs)
{
/* Must be very careful with the fifo on the HME */
- if((esp->erev != fashme) || !(eregs->esp_status2 & ESP_STAT2_FEMPTY))
+ if((esp->erev != fashme) || !(GETREG(eregs->esp_status2) &
+ ESP_STAT2_FEMPTY))
esp_cmd(esp, eregs, ESP_CMD_FLUSH);
esp_maybe_nop(esp, eregs);
esp_cmd(esp, eregs, ESP_CMD_TI);
* will move and count 16-bit quantities during wide data.
* SMCC _and_ Qlogic can both bite me.
*/
- fifocnt = eregs->esp_fflags & ESP_FF_FBYTES;
+ fifocnt = GETREG(eregs->esp_fflags) & ESP_FF_FBYTES;
if(esp->erev != fashme)
ecount = esp_getcount(eregs);
bytes_sent = esp->current_transfer_size;
if(esp->do_pio_cmds){
esp_advance_phase(SCptr, in_status);
esp_cmd(esp, eregs, ESP_CMD_ICCSEQ);
- while(!(esp->eregs->esp_status & ESP_STAT_INTR));
- esp->esp_command[0] = eregs->esp_fdata;
- while(!(esp->eregs->esp_status & ESP_STAT_INTR));
- esp->esp_command[1] = eregs->esp_fdata;
+ while(!(GETREG(esp->eregs->esp_status)
+ & ESP_STAT_INTR));
+ esp->esp_command[0] = GETREG(eregs->esp_fdata);
+ while(!(GETREG(esp->eregs->esp_status)
+ & ESP_STAT_INTR));
+ esp->esp_command[1] = GETREG(eregs->esp_fdata);
} else {
if(esp->erev != fashme) {
esp->esp_command[0] = 0xff;
esp->esp_command[1] = 0xff;
- eregs->esp_tclow = 2;
- eregs->esp_tcmed = 0;
+ SETREG(eregs->esp_tclow, 2);
+ SETREG(eregs->esp_tcmed, 0);
esp->dma_init_read(esp, esp->esp_command_dvma, 2);
esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ);
} else {
int cmd_bytes_sent, fcnt;
if(esp->erev != fashme)
- esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS);
+ esp->seqreg = (GETREG(eregs->esp_sstep) & ESP_STEP_VBITS);
if(esp->erev == fashme)
fcnt = esp->hme_fifo_workaround_count;
else
- fcnt = (eregs->esp_fflags & ESP_FF_FBYTES);
+ fcnt = (GETREG(eregs->esp_fflags) & ESP_FF_FBYTES);
cmd_bytes_sent = esp->dma_bytes_sent(esp, fcnt);
if(esp->dma_invalidate)
esp->dma_invalidate(esp);
esp_print_seqreg(esp->seqreg);
printk("\n");
printk("esp%d: New -- ", esp->esp_id);
- esp->sreg = eregs->esp_status;
- esp->seqreg = eregs->esp_sstep;
- esp->ireg = eregs->esp_intrpt;
+ esp->sreg = GETREG(eregs->esp_status);
+ esp->seqreg = GETREG(eregs->esp_sstep);
+ esp->ireg = GETREG(eregs->esp_intrpt);
esp_print_ireg(esp->ireg);
printk(" ");
esp_print_statreg(esp->sreg);
esp->config3[SCptr->target] |= bit;
else
esp->config3[SCptr->target] &= ~bit;
- eregs->esp_cfg3 = esp->config3[SCptr->target];
+ SETREG(eregs->esp_cfg3,
+ esp->config3[SCptr->target]);
}
- eregs->esp_soff = SDptr->sync_min_period;
- eregs->esp_stp = SDptr->sync_max_offset;
+ SETREG(eregs->esp_soff, SDptr->sync_min_period);
+ SETREG(eregs->esp_stp, SDptr->sync_max_offset);
ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
SDptr->sync_max_offset,
ESPSDTR(("unaccaptable sync nego, forcing async\n"));
SDptr->sync_max_offset = 0;
SDptr->sync_min_period = 0;
- eregs->esp_soff = 0;
- eregs->esp_stp = 0;
+ SETREG(eregs->esp_soff, 0);
+ SETREG(eregs->esp_stp, 0);
if((esp->erev == fas100a || esp->erev == fas216 || esp->erev == fas236 || esp->erev == fashme)) {
if((esp->erev == fas100a) || (esp->erev == fashme))
bit = ESP_CONFIG3_FAST;
else
bit = ESP_CONFIG3_FSCSI;
esp->config3[SCptr->target] &= ~bit;
- eregs->esp_cfg3 = esp->config3[SCptr->target];
+ SETREG(eregs->esp_cfg3,
+ esp->config3[SCptr->target]);
}
}
/* Pure paranoia. */
esp->config3[SCptr->target] &= ~(ESP_CONFIG3_EWIDE);
}
- eregs->esp_cfg3 = esp->config3[SCptr->target];
+ SETREG(eregs->esp_cfg3, esp->config3[SCptr->target]);
/* Regardless, next try for sync transfers. */
build_sync_nego_msg(esp, esp->sync_defp, 15);
message_out = MSG_PARITY_ERROR;
esp_cmd(esp, eregs, ESP_CMD_FLUSH);
} else if(esp->erev != fashme &&
- (it = (eregs->esp_fflags & ESP_FF_FBYTES))!=1) {
+ (it = (GETREG(eregs->esp_fflags)
+ & ESP_FF_FBYTES))!=1) {
/* We certainly dropped the ball somewhere. */
message_out = INITIATOR_ERROR;
esp_cmd(esp, eregs, ESP_CMD_FLUSH);
if(esp->erev == fashme)
it = esp->hme_fifo_workaround_buffer[0];
else
- it = eregs->esp_fdata;
+ it = GETREG(eregs->esp_fdata);
esp_advance_phase(SCptr, in_msgincont);
} else {
/* it is ok and we want it */
esp->hme_fifo_workaround_buffer[0];
else
it = esp->cur_msgin[esp->msgin_ctr] =
- eregs->esp_fdata;
+ GETREG(eregs->esp_fdata);
esp->msgin_ctr++;
}
} else {
esp_advance_phase(SCptr, in_the_dark);
esp->msgin_len = 0;
}
- esp->sreg = eregs->esp_status;
+ esp->sreg = GETREG(eregs->esp_status);
esp->sreg &= ~(ESP_STAT_INTR);
if((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
esp_cmd(esp, eregs, ESP_CMD_MOK);
esp->dma_init_write(esp, esp->esp_command_dvma, i);
} else {
esp_cmd(esp, eregs, ESP_CMD_FLUSH);
- eregs->esp_fdata = *esp->esp_scmdp++;
+ SETREG(eregs->esp_fdata, *esp->esp_scmdp++);
esp->esp_scmdleft--;
esp_cmd(esp, eregs, ESP_CMD_TI);
}
if(esp->erev == fashme)
hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1);
else
- eregs->esp_fdata = esp->cur_msgout[0];
+ SETREG(eregs->esp_fdata, esp->cur_msgout[0]);
esp_cmd(esp, eregs, ESP_CMD_TI);
break;
case 2:
if(esp->do_pio_cmds){
- eregs->esp_fdata = esp->cur_msgout[0];
- eregs->esp_fdata = esp->cur_msgout[1];
+ SETREG(eregs->esp_fdata, esp->cur_msgout[0]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[1]);
esp_cmd(esp, eregs, ESP_CMD_TI);
} else {
esp->esp_command[0] = esp->cur_msgout[0];
case 4:
esp->snip = 1;
if(esp->do_pio_cmds){
- eregs->esp_fdata = esp->cur_msgout[0];
- eregs->esp_fdata = esp->cur_msgout[1];
- eregs->esp_fdata = esp->cur_msgout[2];
- eregs->esp_fdata = esp->cur_msgout[3];
+ SETREG(eregs->esp_fdata, esp->cur_msgout[0]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[1]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[2]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[3]);
esp_cmd(esp, eregs, ESP_CMD_TI);
} else {
esp->esp_command[0] = esp->cur_msgout[0];
case 5:
esp->snip = 1;
if(esp->do_pio_cmds){
- eregs->esp_fdata = esp->cur_msgout[0];
- eregs->esp_fdata = esp->cur_msgout[1];
- eregs->esp_fdata = esp->cur_msgout[2];
- eregs->esp_fdata = esp->cur_msgout[3];
- eregs->esp_fdata = esp->cur_msgout[4];
+ SETREG(eregs->esp_fdata, esp->cur_msgout[0]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[1]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[2]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[3]);
+ SETREG(eregs->esp_fdata, esp->cur_msgout[4]);
esp_cmd(esp, eregs, ESP_CMD_TI);
} else {
- esp->esp_command[0] = esp->cur_msgout[0];
- esp->esp_command[1] = esp->cur_msgout[1];
- esp->esp_command[2] = esp->cur_msgout[2];
- esp->esp_command[3] = esp->cur_msgout[3];
- esp->esp_command[4] = esp->cur_msgout[4];
+ SETREG(esp->esp_command[0], esp->cur_msgout[0]);
+ SETREG(esp->esp_command[1], esp->cur_msgout[1]);
+ SETREG(esp->esp_command[2], esp->cur_msgout[2]);
+ SETREG(esp->esp_command[3], esp->cur_msgout[3]);
+ SETREG(esp->esp_command[4], esp->cur_msgout[4]);
if(esp->erev == fashme) {
hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 5);
esp_cmd(esp, eregs, ESP_CMD_TI);
if(esp->erev == fashme) {
hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1);
} else {
- eregs->esp_fdata = esp->cur_msgout[0];
+ SETREG(eregs->esp_fdata, esp->cur_msgout[0]);
}
esp->msgout_len = 1;
esp_cmd(esp, eregs, ESP_CMD_TI);
esp->dma_irq_entry(esp);
/* Check for errors. */
- esp->sreg = eregs->esp_status;
+ esp->sreg = GETREG(eregs->esp_status);
esp->sreg &= (~ESP_STAT_INTR);
if(esp->erev == fashme) {
- esp->sreg2 = eregs->esp_status2;
- esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS);
+ esp->sreg2 = GETREG(eregs->esp_status2);
+ esp->seqreg = (GETREG(eregs->esp_sstep) & ESP_STEP_VBITS);
}
if(esp->sreg & (ESP_STAT_SPAM)) {
/* Gross error, could be due to one of:
}
}
- esp->ireg = eregs->esp_intrpt; /* Unlatch intr and stat regs */
+ esp->ireg = GETREG(eregs->esp_intrpt); /* Unlatch intr and stat regs */
/* This cannot be done until this very moment. -DaveM */
synchronize_irq();
}
#else
/* For SMP we only service one ESP on the list list at our IRQ level! */
-static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
{
struct NCR_ESP *esp;
-/* NCR53C9x.c: Defines and structures for the NCR53C9x generic driver.
+/* NCR53C9x.h: Defines and structures for the NCR53C9x generic driver.
*
* Originaly esp.h: Defines and structures for the Sparc ESP
* (Enhanced SCSI Processor) driver under Linux.
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*
* Generalization by Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * More generalization (for i386 stuff) by Tymm Twillman (tymm@computer.org)
*/
#ifndef NCR53C9X_H
/* All the ESP registers are one byte each and are accessed longwords
* apart with a big-endian ordering to the bytes.
*/
+
+/*
+ * On intel, we must use inb() and outb() for register access, and the registers
+ * are consecutive; no padding.
+ */
+
+#ifndef __i386__
+#define SETREG(reg, val) (reg = val)
+#define GETREG(reg) (reg)
struct ESP_regs {
/* Access Description Offset */
EREGS_PAD(fdpad);
volatile unchar esp_fdata; /* rw FIFO data bits 0x08 */
EREGS_PAD(cbpad);
- volatile unchar esp_cmd; /* rw SCSI command bits 0x0c */
+ volatile unchar esp_cmnd; /* rw SCSI command bits 0x0c */
EREGS_PAD(stpad);
volatile unchar esp_status; /* ro ESP status register 0x10 */
#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */
#define fas_rhi esp_fgrnd /* rw HME extended counter 0x3c */
};
+#else
+#define SETREG(reg, val) outb(val, reg)
+#define GETREG(reg) inb(reg)
+
+struct ESP_regs {
+#ifdef CONFIG_MCA
+ unsigned int slot;
+#endif
+ unsigned int io_addr;
+ /* Access Description Offset */
+#define esp_tclow io_addr /* rw Low bits of the transfer count 0x00 */
+#define esp_tcmed io_addr + 1 /* rw Mid bits of the transfer count 0x04 */
+#define esp_fdata io_addr + 2 /* rw FIFO data bits 0x08 */
+#define esp_cmnd io_addr + 3 /* rw SCSI command bits 0x0c */
+#define esp_status io_addr + 4 /* ro ESP status register 0x10 */
+#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */
+#define esp_intrpt io_addr + 5 /* ro Kind of interrupt 0x14 */
+#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */
+#define esp_sstep io_addr + 6 /* ro Sequence step register 0x18 */
+#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */
+#define esp_fflags io_addr + 7 /* ro Bits of current FIFO info 0x1c */
+#define esp_soff esp_fflags /* wo Sync offset 0x1c */
+#define esp_cfg1 io_addr + 8 /* rw First configuration register 0x20 */
+#define esp_cfact io_addr + 9 /* wo Clock conversion factor 0x24 */
+#define esp_status2 esp_cfact /* ro HME status2 register 0x24 */
+#define esp_ctest io_addr + 10 /* wo Chip test register 0x28 */
+#define esp_cfg2 io_addr + 11 /* rw Second configuration register 0x2c */
+
+ /* The following is only found on the 53C9X series SCSI chips */
+#define esp_cfg3 io_addr + 12 /* rw Third configuration register 0x30 */
+#define esp_hole io_addr + 13 /* hole in register map 0x34 */
+
+ /* The following is found on all chips except the NCR53C90 (ESP100) */
+#define esp_tchi io_addr + 14 /* rw High bits of transfer count 0x38 */
+#define esp_uid esp_tchi /* ro Unique ID code 0x38 */
+#define fas_rlo esp_tchi /* rw HME extended counter 0x38 */
+#define esp_fgrnd io_addr + 15 /* rw Data base for fifo 0x3c */
+#define fas_rhi esp_fgrnd /* rw HME extended counter 0x3c */
+};
+
+#ifndef save_and_cli
+#define save_and_cli(flags) save_flags(flags); cli();
+#endif
+
+#endif
+
/* Various revisions of the ESP board. */
enum esp_rev {
esp100 = 0x00, /* NCR53C90 - very broken */
struct NCR_ESP {
struct NCR_ESP *next; /* Next ESP on probed or NULL */
struct ESP_regs *eregs; /* All esp registers */
+#ifndef __i386__
struct Linux_DMA *dma; /* Who I do transfers with. */
+#else
+ int dma;
+#endif
void *dregs; /* And his registers. */
struct Scsi_Host *ehost; /* Backpointer to SCSI Host */
int diff; /* Differential SCSI bus? */
int bursts; /* Burst sizes our DVMA supports */
+#ifdef CONFIG_MCA
+ int slot; /* MCA slot the adapter occupies */
+#endif
+
/* Our command queues, only one cmd lives in the current_SC queue. */
Scsi_Cmnd *issue_SC; /* Commands to be issued */
Scsi_Cmnd *current_SC; /* Who is currently working the bus */
0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
-#define AIC7XXX_C_VERSION "5.1.4"
+#define AIC7XXX_C_VERSION "5.1.6"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
"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 */
+ "Adaptec AHA-394X Ultra2 SCSI host adapter", /* AIC_7897 */
+ "Adaptec PCMCIA SCSI controller", /* card bus stuff */
};
/*
struct aic7xxx_scb *q_next; /* next scb in queue */
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]; /*
* don't have to calculate anything
* during underflow/overflow/stat code
*/
+ void *kmalloc_ptr;
};
/*
* This is the first 64 bytes in the host struct
*/
- struct Scsi_Host *host; /* pointer to scsi host */
- struct aic7xxx_host *next; /* allow for multiple IRQs */
- int host_no; /* SCSI host number */
- unsigned long base; /* card base address */
- volatile unsigned char *maddr; /* memory mapped address */
- unsigned long mbase; /* I/O memory address */
+ /*
+ * We are grouping things here....first, items that get either read or
+ * written with nearly every interrupt
+ */
volatile ahc_flag_type flags;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
- spinlock_t spin_lock;
-#endif
- volatile unsigned char cpu_lock_count[NR_CPUS];
- ahc_chip chip; /* chip type */
ahc_feature features; /* chip features */
- unsigned long last_reset;
+ unsigned long base; /* card base address */
+ volatile unsigned char *maddr; /* memory mapped address */
unsigned long isr_count; /* Interrupt count */
unsigned long spurious_int;
- struct target_cmd *targetcmds;
- unsigned int num_targetcmds;
+ scb_data_type *scb_data;
+ struct aic7xxx_cmd_queue {
+ Scsi_Cmnd *head;
+ Scsi_Cmnd *tail;
+ } completeq;
+
+ /*
+ * Things read/written on nearly every entry into aic7xxx_queue()
+ */
+ volatile scb_queue_type waiting_scbs;
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 */
- volatile unsigned char max_activescbs;
unsigned char unpause; /* unpause value for HCNTRL */
unsigned char pause; /* pause value for HCNTRL */
volatile unsigned char qoutfifonext;
+ volatile unsigned char activescbs; /* active scbs */
+ volatile unsigned char max_activescbs;
volatile unsigned char qinfifonext;
- /*
- * MAX_TARGETS is currently == 16, so that makes these entries the next
- * 64 bytes
- */
-
#define DEVICE_PRESENT 0x01
#define BUS_DEVICE_RESET_PENDING 0x02
-#define DEVICE_TIMEOUT 0x04
+#define DEVICE_RESET_DELAY 0x04
#define DEVICE_PRINT_SDTR 0x08
#define DEVICE_PRINT_WDTR 0x10
-#define DEVICE_SUCCESS 0x20
-#define DEVICE_TAGGED_SUCCESS 0x40
+#define DEVICE_WAS_BUSY 0x20
#define DEVICE_SCANNED 0x80
volatile unsigned char dev_flags[MAX_TARGETS];
volatile unsigned char dev_active_cmds[MAX_TARGETS];
volatile unsigned char dev_temp_queue_depth[MAX_TARGETS];
unsigned char dev_commands_sent[MAX_TARGETS];
- /*
- * The next 128 (or 256 on 64 bit machines)....
- */
- Scsi_Cmnd *dev_wdtr_cmnd[MAX_TARGETS];
- Scsi_Cmnd *dev_sdtr_cmnd[MAX_TARGETS];
-
- /*
- * The next 64....
- */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+ spinlock_t spin_lock;
+ volatile unsigned char cpu_lock_count[NR_CPUS];
+#endif
- long dev_last_reset[MAX_TARGETS];
+ unsigned short dev_timer_active; /* Which devs have a timer set */
- /*
- * The next 64....
- */
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+ Scsi_Cmnd *dev_wdtr_cmnd[MAX_TARGETS];
+ Scsi_Cmnd *dev_sdtr_cmnd[MAX_TARGETS];
+#endif
- unsigned char dev_mid_level_queue_depth[MAX_TARGETS];
unsigned char dev_last_queue_full[MAX_TARGETS];
unsigned char dev_last_queue_full_count[MAX_TARGETS];
unsigned char dev_max_queue_depth[MAX_TARGETS];
- /*
- * The next 128....
- */
-
volatile scb_queue_type delayed_scbs[MAX_TARGETS];
- /*
- *
- */
-
- struct timer_list dev_timer[MAX_TARGETS];
+ unsigned long dev_expires[MAX_TARGETS];
+ struct timer_list dev_timer;
/*
* The next 64....
unsigned char msg_len; /* Length of message */
unsigned char msg_index; /* Index into msg_buf array */
transinfo_type transinfo[MAX_TARGETS];
- volatile scb_queue_type waiting_scbs; /*
- * SCBs waiting for space in
- * the QINFIFO.
- */
- scb_data_type *scb_data;
-
- struct aic7xxx_cmd_queue {
- Scsi_Cmnd *head;
- Scsi_Cmnd *tail;
- } completeq;
/*
struct seeprom_config sc;
unsigned short sc_type;
unsigned short sc_size;
+ struct aic7xxx_host *next; /* allow for multiple IRQs */
+ struct Scsi_Host *host; /* pointer to scsi host */
+ int host_no; /* SCSI host number */
+ unsigned long mbase; /* I/O memory address */
+ unsigned long last_reset;
+ ahc_chip chip; /* chip type */
/*
* Statistics Kept:
* 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 r_total; /* total reads */
- long r_total512; /* 512 byte blocks read */
#ifdef AIC7XXX_PROC_STATS
+ long xfers; /* total xfer count */
+ long w_total512; /* 512 byte blocks written */
+ long r_total512; /* 512 byte blocks read */
long w_bins[10]; /* binned write */
long r_bins[10]; /* binned reads */
#endif /* AIC7XXX_PROC_STATS */
} stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */
+
+#if 0
+ struct target_cmd *targetcmds;
+ unsigned int num_targetcmds;
+#endif
+
};
/*
*/
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
+ * Should we force EXTENDED translation on a controller.
+ * 0 == Use whatever is in the SEEPROM or default to off
+ * 1 == Use whatever is in the SEEPROM or default to on
*/
static unsigned int aic7xxx_extended = 0;
/*
for(i=0; i<milliseconds; i++)
udelay(1000);
}
+
+static inline int
+time_after_eq(unsigned long a, unsigned long b)
+{
+ return((long)((a) - (b)) >= 0L);
+}
+
+static inline int
+timer_pending(struct timer_list *timer)
+{
+ return( timer->prev != NULL );
+}
+
+#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075
+
#endif
static inline unsigned char
{
printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
}
+#if 0
download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
+#endif
+ download_consts[TMODE_NUMCMDS] = 0;
cur_patch = &sequencer_patches[0];
downloaded = 0;
skip_addr = 0;
{
char *buffer;
+ p->dev_flags[tindex] |= DEVICE_PRESENT;
if(cmd->use_sg)
{
struct scatterlist *sg;
p->needsdtr_copy |= (1<<tindex);
if (p->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;
+ p->transinfo[tindex].goal_offset = p->transinfo[tindex].user_offset;
+ }
else
- p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+ {
+ 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
{
#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
(cmd->data_cmnd[0] == WRITE_FILEMARKS) )
{
sp->w_total++;
- sp->w_total512 += (actual >> 9);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if ( (sp->w_total > 16) && (aic7xxx_verbose > 0xffff) )
+ aic7xxx_verbose &= 0xffff;
+#endif
#ifdef AIC7XXX_PROC_STATS
+ sp->xfers++;
+ sp->w_total512 += (actual >> 9);
ptr = sp->w_bins;
#endif /* AIC7XXX_PROC_STATS */
}
else
{
sp->r_total++;
- sp->r_total512 += (actual >> 9);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if ( (sp->r_total > 16) && (aic7xxx_verbose > 0xffff) )
+ aic7xxx_verbose &= 0xffff;
+#endif
#ifdef AIC7XXX_PROC_STATS
+ sp->xfers++;
+ sp->r_total512 += (actual >> 9);
ptr = sp->r_bins;
#endif /* AIC7XXX_PROC_STATS */
}
for (i = min_target; i <= max_target; i++)
{
+ if ( i == p->scsi_id )
+ {
+ continue;
+ }
if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
printk(INFO_LEAD "Cleaning up status information "
"and delayed_scbs.\n", p->host_no, channel, i, lun);
- if ( !(p->dev_flags[i] & DEVICE_TAGGED_SUCCESS) &&
- (p->dev_active_cmds[i]) &&
- (p->tagenable & (0x01 << i)) )
- {
- printk(INFO_LEAD "Device appears to be choking on tagged commands.\n",
- p->host_no, channel, i, lun);
- printk(INFO_LEAD "Will use untagged I/O instead.\n", p->host_no,
- channel, i, lun);
- p->dev_max_queue_depth[i] = 1;
- p->dev_temp_queue_depth[i] = 1;
- p->tagenable &= ~(0x01 << i);
- p->orderedtag &= ~(0x01 << i);
- }
p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
if ( tag == SCB_LIST_NULL )
{
- p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
- p->dev_last_reset[i] = jiffies;
+ p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR |
+ DEVICE_RESET_DELAY;
+ p->dev_expires[i] = jiffies + (4 * HZ);
+ p->dev_timer_active |= (0x01 << i);
p->dev_last_queue_full_count[i] = 0;
p->dev_last_queue_full[i] = 0;
p->dev_temp_queue_depth[i] =
p->dev_max_queue_depth[i];
- /*
- * In case this isn't a full bus reset, we want to add a 4 second timer in
- * here so that we can delay all re-sent commands for this device for the
- * 4 seconds and then have our timer routine pick them back up.
- */
- del_timer(&p->dev_timer[i]);
- p->dev_timer[i].expires = jiffies + (4 * HZ);
- add_timer(&p->dev_timer[i]);
}
for(j=0; j<MAX_LUNS; j++)
{
"delayed_scbs queue!\n", p->host_no, channel, i, lun);
scbq_init(&p->delayed_scbs[i]);
}
- if ( p->delayed_scbs[i].head == NULL )
- del_timer(&p->dev_timer[i]);
+ if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) ||
+ time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
+ {
+ del_timer(&p->dev_timer);
+ p->dev_timer.expires = p->dev_expires[i];
+ add_timer(&p->dev_timer);
+ p->dev_timer_active |= (0x01 << p->scsi_id);
+ }
}
}
tindex = TARGET_INDEX(scb->cmd);
if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
{
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 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]) ||
- time_after_eq(p->dev_last_reset[tindex], jiffies - 4 * HZ) )
+ (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) )
{
-#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 ( !timer_pending(&p->dev_timer[tindex]) &&
- !(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) )
}
if (sent)
{
-#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
#else
spin_lock_irqsave(&io_request_lock, cpu_flags);
#endif
+ p->dev_timer_active &= ~(0x01 << p->scsi_id);
for(i=0; i<MAX_TARGETS; i++)
{
- if ( timer_pending(&p->dev_timer[i]) &&
- time_before_eq(p->dev_timer[i].expires, jiffies) )
+ if ( i == p->scsi_id )
{
- del_timer(&p->dev_timer[i]);
+ continue;
+ }
+ if ( (p->dev_timer_active & (0x01 << i)) &&
+ time_after_eq(jiffies, p->dev_expires[i]) )
+ {
+ p->dev_timer_active &= ~(0x01 << i);
+ p->dev_flags[i] &= ~(DEVICE_RESET_DELAY|DEVICE_WAS_BUSY);
p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i];
j = 0;
while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
*/
}
}
+ else if ( p->dev_timer_active & (0x01 << i) )
+ {
+ if ( p->dev_timer_active & (0x01 << p->scsi_id) )
+ {
+ if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
+ {
+ p->dev_timer.expires = p->dev_expires[i];
+ }
+ }
+ else
+ {
+ p->dev_timer.expires = p->dev_expires[i];
+ p->dev_timer_active |= (0x01 << p->scsi_id);
+ }
+ }
+ }
+ if ( p->dev_timer_active & (0x01 << p->scsi_id) )
+ {
+ add_timer(&p->dev_timer);
}
+
aic7xxx_run_waiting_queues(p);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
DRIVER_UNLOCK
(scb->tag_action) &&
!(scb->flags & SCB_MSGOUT_BITS) )
{
- if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
- (p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS))
+ if (scb->tag_action == MSG_ORDERED_Q_TAG)
{
/*
* OK...the device seems able to accept tagged commands, but
aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
}
- else if ( (scb->tag_action == MSG_SIMPLE_Q_TAG) &&
- !(p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS) )
+ else if (scb->tag_action == MSG_SIMPLE_Q_TAG)
{
unsigned char i, reset = 0;
struct aic7xxx_scb *scbp;
p->activescbs--;
scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
- if ( !timer_pending(&p->dev_timer[tindex]) )
+ if ( !(p->dev_timer_active & (0x01 << tindex)) )
{
+ p->dev_timer_active |= (0x01 << tindex);
if ( p->dev_active_cmds[tindex] )
{
- p->dev_timer[tindex].expires = jiffies + (HZ * 2);
- add_timer(&p->dev_timer[tindex]);
+ p->dev_expires[tindex] = jiffies + HZ;
}
else
{
- p->dev_timer[tindex].expires = jiffies + (HZ / 2);
- add_timer(&p->dev_timer[tindex]);
+ p->dev_expires[tindex] = jiffies + (HZ / 10);
+ }
+ if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) )
+ {
+ p->dev_timer.expires = p->dev_expires[tindex];
+ p->dev_timer_active |= (0x01 << p->scsi_id);
+ add_timer(&p->dev_timer);
+ }
+ else if ( time_after_eq(p->dev_timer.expires,
+ p->dev_expires[tindex]) )
+ {
+ del_timer(&p->dev_timer);
+ p->dev_timer.expires = p->dev_expires[tindex];
+ add_timer(&p->dev_timer);
}
}
#ifdef AIC7XXX_VERBOSE_DEBUGGING
p->dev_last_queue_full[tindex] = 0;
p->dev_last_queue_full_count[tindex] = 0;
}
+ else
+ {
+ p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
+ }
}
break;
}
{
maxsync = AHC_SYNCRATE_FAST;
}
-
-#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
+ /*
+ * We might have a device that is starting negotiation with us
+ * before we can start up negotiation with it....be prepared to
+ * have a device ask for a higher speed then we want to give it
+ * in that case
+ */
+ if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+ (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
{
- 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);
+ if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+ {
+ /*
+ * Not only is the device starting this up, but it also hasn't
+ * been scanned yet, so this would likely be our TUR or our
+ * INQUIRY command at scan time, so we need to use the
+ * settings from the SEEPROM if they existed. Of course, even
+ * if we didn't find a SEEPROM, we stuffed default values into
+ * the user settings anyway, so use those in all cases.
+ */
+ p->transinfo[tindex].goal_period =
+ p->transinfo[tindex].user_period;
+ p->transinfo[tindex].goal_offset =
+ p->transinfo[tindex].user_offset;
+ p->needsdtr_copy |= target_mask;
+ }
+ if ( !p->transinfo[tindex].goal_offset )
+ period = 255;
+ if ( p->transinfo[tindex].goal_period > period )
+ period = p->transinfo[tindex].goal_period;
}
-#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)
+ /*
+ * Did we drop to async? If so, are we sending a reply? If we are,
+ * then we have to make sure that the reply value reflects the proper
+ * settings so we need to set the goal values according to what
+ * we need to send.
+ */
+ if ( (offset == 0) || (offset != saved_offset) ||
+ ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+ (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) )
{
- /*
- * 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;
+ offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+ if ( offset == 0 )
+ {
+ 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
}
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;
aic_outb(p, HOST_MSG, MSG_OUT);
* 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->transinfo[tindex].goal_width =
+ p->transinfo[tindex].user_width;
p->needwdtr_copy |= target_mask;
p->needsdtr_copy |= target_mask;
}
{
p->needwdtr_copy &= ~target_mask;
bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ aic7xxx_set_width(p, target, channel, lun, bus_width,
+ AHC_TRANS_GOAL|AHC_TRANS_QUITE);
break;
}
}
aic7xxx_calculate_residual(p, scb);
}
cmd->result |= (aic7xxx_error(cmd) << 16);
- if (scb->tag_action)
- p->dev_flags[TARGET_INDEX(cmd)] |=
- DEVICE_TAGGED_SUCCESS | DEVICE_SUCCESS | DEVICE_PRESENT;
- else
- p->dev_flags[TARGET_INDEX(cmd)] |=
- DEVICE_SUCCESS | DEVICE_PRESENT;
aic7xxx_done(p, scb);
break;
}
target_mask = (1 << tindex);
device->queue_depth = default_depth;
- p->dev_mid_level_queue_depth[tindex] = 3;
p->dev_temp_queue_depth[tindex] = 1;
p->dev_max_queue_depth[tindex] = 1;
p->tagenable &= ~target_mask;
}
p->dev_max_queue_depth[tindex] = device->queue_depth;
p->dev_temp_queue_depth[tindex] = device->queue_depth;
- p->dev_mid_level_queue_depth[tindex] = device->queue_depth;
p->tagenable |= target_mask;
p->orderedtag |= target_mask;
device->tagged_queue = 1;
p->completeq.tail = NULL;
scbq_init(&p->scb_data->free_scbs);
scbq_init(&p->waiting_scbs);
+ init_timer(&p->dev_timer);
+ p->dev_timer.data = (unsigned long)p;
+ p->dev_timer.function = (void *)aic7xxx_timer;
+ p->dev_timer_active = 0;
for (i = 0; i < NUMBER(p->untagged_scbs); i++)
{
p->dev_commands_sent[i] = 0;
p->dev_flags[i] = 0;
p->dev_active_cmds[i] = 0;
- p->dev_last_reset[i] = jiffies;
p->dev_last_queue_full[i] = 0;
p->dev_last_queue_full_count[i] = 0;
p->dev_max_queue_depth[i] = 1;
p->dev_temp_queue_depth[i] = 1;
- p->dev_mid_level_queue_depth[i] = 3;
+ p->dev_expires[i] = 0;
scbq_init(&p->delayed_scbs[i]);
- init_timer(&p->dev_timer[i]);
- p->dev_timer[i].data = (unsigned long)p;
- p->dev_timer[i].function = (void *)aic7xxx_timer;
}
printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
scarray, p->sc_size, C46);
}
+ if (!have_seeprom)
+ {
+ p->sc_size = 128;
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, p->sc_type);
+ if (!have_seeprom)
+ {
+ 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;
}
sc->adapter_control &= ~CFAUTOTERM;
sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
}
- p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
+ if (aic7xxx_extended)
+ p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
}
else
{
}
else if (sc->device_flags[i] & CFNEWULTRAFORMAT)
{
- if ( (sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03 )
+ if ( ((sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03) &&
+ !(p->features & AHC_ULTRA2) )
{
sc->device_flags[i] &= ~CFXFER;
sc->device_flags[i] |= CFSYNCHISULTRA;
current_p = current_p->next;
current_p->next = temp_p;
}
- if (aic7xxx_extended)
- {
- temp_p->flags |= AHC_EXTEND_TRANS_A;
- if (temp_p->flags & AHC_MULTI_CHANNEL)
- temp_p->flags |= AHC_EXTEND_TRANS_B;
- }
switch (type)
{
AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
AHC_AIC7896_FE, 23,
32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 24,
+ 32, C46 },
};
unsigned short command;
aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
}
- if (aic7xxx_extended)
- temp_p->flags |= AHC_EXTEND_TRANS_A;
-
if ( list_p == NULL )
{
list_p = current_p = temp_p;
"message buffer\n", p->host_no, CTL_OF_SCB(scb));
scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
aic7xxx_error(scb->cmd) = DID_RESET;
- p->dev_flags[TARGET_INDEX(scb->cmd)] &=
- ~DEVICE_SUCCESS;
p->dev_flags[TARGET_INDEX(scb->cmd)] |=
BUS_DEVICE_RESET_PENDING;
/* Send the abort message to the active SCB. */
"in use\n", p->host_no, CTL_OF_SCB(scb));
scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
aic7xxx_error(scb->cmd) = DID_RESET;
- p->dev_flags[TARGET_INDEX(scb->cmd)] &=
- ~DEVICE_SUCCESS;
p->dev_flags[TARGET_INDEX(scb->cmd)] |=
BUS_DEVICE_RESET_PENDING;
return(SCSI_RESET_ERROR);
*/
scb->hscb->control |= MK_MESSAGE;
scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
- p->dev_flags[TARGET_INDEX(scb->cmd)] &= ~DEVICE_SUCCESS;
p->dev_flags[TARGET_INDEX(scb->cmd)] |=
BUS_DEVICE_RESET_PENDING;
if (hscb_index != SCB_LIST_NULL)
{
mask = (0x01 << i);
printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c,"
- " q_depth=%d:%d:%d\n",
+ " q_depth=%d:%d\n",
p->host_no, 0, i, 0, p->dev_flags[i],
(p->wdtr_pending & mask) ? 'Y' : 'N',
(p->needwdtr & 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]);
+ p->dev_max_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)
{
action = HOST_RESET;
}
- if ( ((jiffies - p->dev_last_reset[tindex]) < (HZ * 3)) &&
+ if ( (p->dev_flags[tindex] & DEVICE_RESET_DELAY) &&
!(action & (HOST_RESET | BUS_RESET)))
{
if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
p->msg_len = 0;
}
aic7xxx_run_done_queue(p, TRUE);
+ /*
+ * If this a SCSI_RESET_SYNCHRONOUS then the command we were given is
+ * in need of being re-started, so send it on through to aic7xxx_queue
+ * and let it set until the delay is over. This keeps it from dying
+ * entirely and avoids getting a bogus dead command back through the
+ * mid-level code due to too many retries.
+ */
+ if ( flags & SCSI_RESET_SYNCHRONOUS )
+ {
+ cmd->result = DID_RESET << 16;
+ cmd->done(cmd);
+ }
p->flags &= ~AHC_IN_RESET;
- /* We can't rely on run_waiting_queues to unpause the sequencer for
- * PCI based controllers since we use AAP */
+ /*
+ * We can't rely on run_waiting_queues to unpause the sequencer for
+ * PCI based controllers since we use AAP. NOTE: this also sets
+ * the timer for the one command we might have queued in the case
+ * of a synch reset.
+ */
aic7xxx_run_waiting_queues(p);
unpause_sequencer(p, FALSE);
DRIVER_UNLOCK
{
for (lun = 0; lun < MAX_LUNS; lun++)
{
- if (p->stats[target][lun].xfers != 0)
+ if (p->stats[target][lun].r_total != 0)
#ifdef AIC7XXX_PROC_STATS
size += 512;
#else
for (lun = 0; lun < MAX_LUNS; lun++)
{
sp = &p->stats[target][lun];
- if (sp->xfers == 0)
+ if (sp->r_total == 0)
{
continue;
}
p->transinfo[target].cur_period,
p->transinfo[target].cur_offset,
p->transinfo[target].cur_width);
+#ifdef AIC7XXX_PROC_STATS
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",
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++)
{
size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
}
+#else
+ size += sprintf(BLS, " Total transfers: %ld/%ld read/written)\n",
+ sp->r_total, sp->w_total);
#endif /* AIC7XXX_PROC_STATS */
size += sprintf(BLS, "\n\n");
}
--- /dev/null
+/* $Id: atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $
+ * linux/kernel/atp870u.c
+ *
+ * Copyright (C) 1997 Wu Ching Chen
+ * 2.1.x update (C) 1998 Krzysztof G. Baranowski
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/pci.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+
+#include "atp870u.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_atp870u = {
+ PROC_SCSI_ATP870U, 7, "atp870u",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+void mydlyu(unsigned int);
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $";
+*/
+
+static unsigned char admaxu=1,host_idu[2],chip_veru[2],scam_on[2],global_map[2];
+static unsigned short int active_idu[2],wide_idu[2],sync_idu,ultra_map[2];
+static int workingu[2]={0,0};
+static Scsi_Cmnd *querequ[2][qcnt],*curr_req[2][16];
+static unsigned char devspu[2][16] = {{0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20},
+ {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}};
+static unsigned char dirctu[2][16],last_cmd[2],in_snd[2],in_int[2];
+static unsigned char ata_cdbu[2][16];
+static unsigned int ioportu[2]={0,0};
+static unsigned int irqnumu[2]={0,0};
+static unsigned short int pciportu[2];
+static unsigned long prdaddru[2][16],tran_lenu[2][16],last_lenu[2][16];
+static unsigned char prd_tableu[2][16][1024];
+static unsigned char *prd_posu[2][16];
+static unsigned char quhdu[2],quendu[2];
+static unsigned char devtypeu[2][16] = {{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+static struct Scsi_Host * atp_host[2]={NULL,NULL};
+
+static void atp870u_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned short int tmpcip,id;
+ unsigned char i,j,h,tarid,lun;
+ unsigned char *prd;
+ Scsi_Cmnd *workrequ;
+ unsigned int workportu,tmport;
+ unsigned long adrcntu,k;
+ int errstus;
+
+ for ( h=0; h < 2; h++ )
+ {
+ if ( ( irq & 0x0f ) == irqnumu[h] )
+ {
+ goto irq_numok;
+ }
+ }
+ return;
+irq_numok:
+ in_int[h]=1;
+ workportu=ioportu[h];
+ tmport=workportu;
+
+ if ( workingu[h] != 0 )
+ {
+ tmport += 0x1f;
+ j=inb(tmport);
+ tmpcip=pciportu[h];
+ if ((inb(tmpcip) & 0x08) != 0)
+ {
+ tmpcip += 0x2;
+ while((inb(tmpcip) & 0x08) != 0);
+ }
+ tmpcip=pciportu[h];
+ outb(0x00,tmpcip);
+ tmport -=0x08;
+ i=inb(tmport);
+ if ((j & 0x40) == 0)
+ {
+ if ((last_cmd[h] & 0x40) == 0)
+ {
+ last_cmd[h]=0xff;
+ }
+ }
+ else
+ {
+ last_cmd[h] |= 0x40;
+ }
+ tmport -= 0x02;
+ tarid=inb(tmport);
+ tmport += 0x02;
+ if ((tarid & 0x40) != 0)
+ {
+ tarid=(tarid & 0x07) | 0x08;
+ }
+ else
+ {
+ tarid &= 0x07;
+ }
+ if ( i == 0x85 )
+ {
+ if (wide_idu[h] != 0)
+ {
+ tmport=workportu+0x1b;
+ j=inb(tmport) & 0x0e;
+ j |= 0x01;
+ outb(j,tmport);
+ }
+ if (((quhdu[h] != quendu[h]) || (last_cmd[h] != 0xff)) &&
+ (in_snd[h] == 0))
+ {
+ send_s870(h);
+ }
+ in_int[h]=0;
+ return;
+ }
+ if ( i == 0x21 )
+ {
+ tmport -= 0x05;
+ adrcntu=0;
+ ((unsigned char *)&adrcntu)[2]=inb(tmport++);
+ ((unsigned char *)&adrcntu)[1]=inb(tmport++);
+ ((unsigned char *)&adrcntu)[0]=inb(tmport);
+ k=last_lenu[h][tarid];
+ k -= adrcntu;
+ tran_lenu[h][tarid]= k;
+ last_lenu[h][tarid]=adrcntu;
+ tmport -= 0x04;
+ outb(0x41,tmport);
+ tmport += 0x08;
+ outb(0x08,tmport);
+ in_int[h]=0;
+ return ;
+ }
+
+ if ((i == 0x80) || (i == 0x8f))
+ {
+ lun=0;
+ tmport -= 0x07;
+ j=inb(tmport);
+ if ( j == 0x44 )
+ {
+ tmport += 0x0d;
+ lun=inb(tmport) & 0x07;
+ }
+ else
+ {
+ if ( j == 0x41 )
+ {
+ tmport += 0x02;
+ adrcntu=0;
+ ((unsigned char *)&adrcntu)[2]=inb(tmport++);
+ ((unsigned char *)&adrcntu)[1]=inb(tmport++);
+ ((unsigned char *)&adrcntu)[0]=inb(tmport);
+ k=last_lenu[h][tarid];
+ k -= adrcntu;
+ tran_lenu[h][tarid]= k;
+ last_lenu[h][tarid]=adrcntu;
+ tmport += 0x04;
+ outb(0x08,tmport);
+ in_int[h]=0;
+ return ;
+ }
+ else
+ {
+ outb(0x46,tmport);
+ dirctu[h][tarid]=0x00;
+ tmport += 0x02;
+ outb(0x00,tmport++);
+ outb(0x00,tmport++);
+ outb(0x00,tmport++);
+ tmport+=0x03;
+ outb(0x08,tmport);
+ in_int[h]=0;
+ return;
+ }
+ }
+ tmport=workportu + 0x10;
+ outb(0x45,tmport);
+ tmport += 0x06;
+ tarid=inb(tmport);
+ if ((tarid & 0x10) != 0)
+ {
+ tarid=(tarid & 0x07) | 0x08;
+ }
+ else
+ {
+ tarid &= 0x07;
+ }
+ workrequ=curr_req[h][tarid];
+ tmport=workportu + 0x0f;
+ outb(lun,tmport);
+ tmport += 0x02;
+ outb(devspu[h][tarid],tmport++);
+ adrcntu=tran_lenu[h][tarid];
+ k=last_lenu[h][tarid];
+ outb(((unsigned char *)&k)[2],tmport++);
+ outb(((unsigned char *)&k)[1],tmport++);
+ outb(((unsigned char *)&k)[0],tmport++);
+ j=tarid;
+ if ( tarid > 7 )
+ {
+ j = (j & 0x07) | 0x40;
+ }
+ j |= dirctu[h][tarid];
+ outb(j,tmport++);
+ outb(0x80,tmport);
+ tmport=workportu + 0x1b;
+ j=inb(tmport) & 0x0e;
+ id=1;
+ id=id << tarid;
+ if ((id & wide_idu[h]) != 0)
+ {
+ j |= 0x01;
+ }
+ outb(j,tmport);
+ if ( last_lenu[h][tarid] == 0 )
+ {
+ tmport=workportu + 0x18;
+ outb(0x08,tmport);
+ in_int[h]=0;
+ return ;
+ }
+ prd=prd_posu[h][tarid];
+ while ( adrcntu != 0 )
+ {
+ id=((unsigned short int *)(prd))[2];
+ if ( id == 0 )
+ {
+ k=0x10000;
+ }
+ else
+ {
+ k=id;
+ }
+ if ( k > adrcntu )
+ {
+ ((unsigned short int *)(prd))[2] =(unsigned short int)
+ (k - adrcntu);
+ ((unsigned long *)(prd))[0] += adrcntu;
+ adrcntu=0;
+ prd_posu[h][tarid]=prd;
+ }
+ else
+ {
+ adrcntu -= k;
+ prdaddru[h][tarid] += 0x08;
+ prd += 0x08;
+ if ( adrcntu == 0 )
+ {
+ prd_posu[h][tarid]=prd;
+ }
+ }
+ }
+ tmpcip=pciportu[h] + 0x04;
+ outl(prdaddru[h][tarid],tmpcip);
+ tmpcip -= 0x02;
+ outb(0x06,tmpcip);
+ outb(0x00,tmpcip);
+ tmpcip -= 0x02;
+ tmport=workportu + 0x18;
+ if ( dirctu[h][tarid] != 0 )
+ {
+ outb(0x08,tmport);
+ outb(0x01,tmpcip);
+ in_int[h]=0;
+ return;
+ }
+ outb(0x08,tmport);
+ outb(0x09,tmpcip);
+ in_int[h]=0;
+ return;
+ }
+
+ workrequ=curr_req[h][tarid];
+ if ( i == 0x42 )
+ {
+ errstus=0x02;
+ workrequ->result=errstus;
+ goto go_42;
+ }
+ if ( i == 0x16 )
+ {
+ errstus=0;
+ tmport -= 0x08;
+ errstus=inb(tmport);
+ workrequ->result=errstus;
+/* if ( errstus == 0x02 )
+ {
+ tmport +=0x10;
+ if ((inb(tmport) & 0x80) != 0)
+ {
+ printk(" autosense ");
+ }
+ tmport -=0x09;
+ outb(0,tmport);
+ tmport=workportu+0x3a;
+ outb((unsigned char)(inb(tmport) | 0x10),tmport);
+ tmport -= 0x39;
+
+ outb(0x08,tmport++);
+ outb(0x7f,tmport++);
+ outb(0x03,tmport++);
+ outb(0x00,tmport++);
+ outb(0x00,tmport++);
+ outb(0x00,tmport++);
+ outb(0x0e,tmport++);
+ outb(0x00,tmport);
+ tmport+=0x07;
+ outb(0x00,tmport++);
+ tmport++;
+ outb(devspu[h][workrequ->target],tmport++);
+ outb(0x00,tmport++);
+ outb(0x00,tmport++);
+ outb(0x0e,tmport++);
+ tmport+=0x03;
+ outb(0x09,tmport);
+ tmport+=0x07;
+ i=0;
+ adrcntu=(unsigned long)(&workrequ->sense_buffer[0]);
+get_sens:
+ j=inb(tmport);
+ if ((j & 0x01) != 0)
+ {
+ tmport-=0x06;
+ (unsigned char)(((caddr_t) adrcntu)[i++])=inb(tmport);
+ tmport+=0x06;
+ goto get_sens;
+ }
+ if ((j & 0x80) == 0)
+ {
+ goto get_sens;
+ }
+ if ((j & 0x40) == 0)
+ {
+ tmport-=0x08;
+ i=inb(tmport);
+ }
+ tmport=workportu+0x3a;
+ outb((unsigned char)(inb(tmport) & 0xef),tmport);
+ tmport=workportu+0x01;
+ outb(0x2c,tmport);
+ tmport += 0x15;
+ outb(0x80,tmport);
+ } */
+go_42:
+ (*workrequ->scsi_done)(workrequ);
+ curr_req[h][tarid]=0;
+ workingu[h]--;
+ if (wide_idu[h] != 0)
+ {
+ tmport=workportu+0x1b;
+ j=inb(tmport) & 0x0e;
+ j |= 0x01;
+ outb(j,tmport);
+ }
+ if (((last_cmd[h] != 0xff) || (quhdu[h] != quendu[h])) &&
+ (in_snd[h] == 0))
+ {
+ send_s870(h);
+ }
+ in_int[h]=0;
+ return;
+ }
+ if ( i == 0x4f )
+ {
+ i=0x89;
+ }
+ i &= 0x0f;
+ if ( i == 0x09 )
+ {
+ tmpcip=tmpcip+4;
+ outl(prdaddru[h][tarid],tmpcip);
+ tmpcip=tmpcip-2;
+ outb(0x06,tmpcip);
+ outb(0x00,tmpcip);
+ tmpcip=tmpcip-2;
+ tmport=workportu+0x10;
+ outb(0x41,tmport);
+ dirctu[h][tarid]=0x00;
+ tmport += 0x08;
+ outb(0x08,tmport);
+ outb(0x09,tmpcip);
+ in_int[h]=0;
+ return;
+ }
+ if ( i == 0x08 )
+ {
+ tmpcip=tmpcip+4;
+ outl(prdaddru[h][tarid],tmpcip);
+ tmpcip=tmpcip-2;
+ outb(0x06,tmpcip);
+ outb(0x00,tmpcip);
+ tmpcip=tmpcip-2;
+ tmport=workportu+0x10;
+ outb(0x41,tmport);
+ tmport += 0x05;
+ outb((unsigned char)(inb(tmport) | 0x20),tmport);
+ dirctu[h][tarid]=0x20;
+ tmport += 0x03;
+ outb(0x08,tmport);
+ outb(0x01,tmpcip);
+ in_int[h]=0;
+ return;
+ }
+ tmport -= 0x07;
+ if ( i == 0x0a )
+ {
+ outb(0x30,tmport);
+ }
+ else
+ {
+ outb(0x46,tmport);
+ }
+ dirctu[h][tarid]=0x00;
+ tmport += 0x02;
+ outb(0x00,tmport++);
+ outb(0x00,tmport++);
+ outb(0x00,tmport++);
+ tmport+=0x03;
+ outb(0x08,tmport);
+ in_int[h]=0;
+ return;
+ }
+ else
+ {
+ tmport=workportu+0x17;
+ inb(tmport);
+ workingu[h]=0;
+ in_int[h]=0;
+ return;
+ }
+}
+
+int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done)(Scsi_Cmnd *))
+{
+ unsigned char i,h;
+ unsigned long flags;
+ unsigned short int m;
+ unsigned int tmport;
+
+ for( h=0; h <= admaxu; h++ )
+ {
+ if ( req_p->host == atp_host[h] )
+ {
+ goto host_ok;
+ }
+ }
+ return 0;
+host_ok:
+ if ( req_p->channel != 0 )
+ {
+ req_p->result = 0x00040000;
+ done(req_p);
+ return 0;
+ }
+ m=1;
+ m= m << req_p->target;
+ if ( ( m & active_idu[h] ) == 0 )
+ {
+ req_p->result = 0x00040000;
+ done(req_p);
+ return 0;
+ }
+ if (done)
+ {
+ req_p->scsi_done = done;
+ }
+ else
+ {
+ printk("atp870u_queuecommand: done can't be NULL\n");
+ req_p->result = 0;
+ done(req_p);
+ return 0;
+ }
+ quendu[h]++;
+ if ( quendu[h] >= qcnt )
+ {
+ quendu[h]=0;
+ }
+ wait_que_empty:
+ if ( quhdu[h] == quendu[h] )
+ {
+ goto wait_que_empty;
+ }
+ save_flags(flags);
+ cli();
+ querequ[h][quendu[h]]=req_p;
+ if ( quendu[h] == 0 )
+ {
+ i=qcnt-1;
+ }
+ else
+ {
+ i=quendu[h]-1;
+ }
+ tmport = ioportu[h]+0x1c;
+ restore_flags(flags);
+ if ((inb(tmport) == 0) && (in_int[h] == 0) && (in_snd[h] == 0))
+ {
+ send_s870(h);
+ }
+ return 0;
+}
+
+void mydlyu(unsigned int dlycnt )
+{
+ unsigned int i ;
+ for ( i = 0 ; i < dlycnt ; i++ )
+ {
+ inb(0x80);
+ }
+}
+
+void send_s870(unsigned char h)
+{
+ unsigned int tmport;
+ Scsi_Cmnd *workrequ;
+ unsigned long flags;
+ unsigned int i;
+ unsigned char j,tarid;
+ unsigned char *prd;
+ unsigned short int tmpcip,w;
+ unsigned long l,bttl;
+ unsigned int workportu;
+ struct scatterlist * sgpnt;
+
+ save_flags(flags);
+ cli();
+ if ( in_snd[h] != 0 )
+ {
+ restore_flags(flags);
+ return;
+ }
+ in_snd[h]=1;
+ if ((last_cmd[h] != 0xff) && ((last_cmd[h] & 0x40) != 0))
+ {
+ last_cmd[h] &= 0x0f;
+ workrequ=curr_req[h][last_cmd[h]];
+ goto cmd_subp;
+ }
+ workingu[h]++;
+ j=quhdu[h];
+ quhdu[h]++;
+ if ( quhdu[h] >= qcnt )
+ {
+ quhdu[h]=0;
+ }
+ workrequ=querequ[h][quhdu[h]];
+ if ( curr_req[h][workrequ->target] == 0 )
+ {
+ curr_req[h][workrequ->target]=workrequ;
+ last_cmd[h]=workrequ->target;
+ goto cmd_subp;
+ }
+ quhdu[h]=j;
+ workingu[h]--;
+ in_snd[h]=0;
+ restore_flags(flags);
+ return ;
+cmd_subp:
+ workportu=ioportu[h];
+ tmport=workportu+0x1f;
+ if ((inb(tmport) & 0xb0) != 0)
+ {
+ goto abortsnd;
+ }
+ tmport=workportu+0x1c;
+ if ( inb(tmport) == 0 )
+ {
+ goto oktosend;
+ }
+abortsnd:
+ last_cmd[h] |= 0x40;
+ in_snd[h]=0;
+ restore_flags(flags);
+ return;
+oktosend:
+ memcpy(&ata_cdbu[h][0], &workrequ->cmnd[0], workrequ->cmd_len);
+ if ( ata_cdbu[h][0] == 0x25 )
+ {
+ if ( workrequ->request_bufflen > 8 )
+ {
+ workrequ->request_bufflen=0x08;
+ }
+ }
+ if ( ata_cdbu[h][0] == 0x12 )
+ {
+ if ( workrequ->request_bufflen > 0x24 )
+ {
+ workrequ->request_bufflen = 0x24;
+ ata_cdbu[h][4]=0x24;
+ }
+ }
+
+ tmport=workportu+0x1b;
+ j=inb(tmport) & 0x0e;
+ tarid=workrequ->target;
+ w=1;
+ w = w << tarid;
+ if ((w & wide_idu[h]) != 0)
+ {
+ j |= 0x01;
+ }
+ outb(j,tmport);
+ tmport=workportu;
+ outb(workrequ->cmd_len,tmport++);
+ outb(0x2c,tmport++);
+ outb(0xcf,tmport++);
+ for ( i=0 ; i < workrequ->cmd_len ; i++ )
+ {
+ outb(ata_cdbu[h][i],tmport++);
+ }
+ tmport=workportu+0x0f;
+ outb(0x00,tmport);
+ tmport+=0x02;
+ outb(devspu[h][tarid],tmport++);
+ if (workrequ->use_sg)
+ {
+
+ l=0;
+ sgpnt = (struct scatterlist *) workrequ->request_buffer;
+ for(i=0; i<workrequ->use_sg; i++)
+ {
+ if(sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER)
+ {
+ panic("Foooooooood fight!");
+ }
+ l += sgpnt[i].length;
+ }
+ }
+ else
+ {
+ l=workrequ->request_bufflen;
+ }
+ outb((unsigned char)(((unsigned char *)(&l))[2]),tmport++);
+ outb((unsigned char)(((unsigned char *)(&l))[1]),tmport++);
+ outb((unsigned char)(((unsigned char *)(&l))[0]),tmport++);
+ j=tarid;
+ last_lenu[h][j]=l;
+ tran_lenu[h][j]=0;
+ if ((j & 0x08) != 0)
+ {
+ j=(j & 0x07) | 0x40;
+ }
+ if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) ||
+ (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15))
+ {
+ outb((unsigned char)(j | 0x20),tmport++);
+ }
+ else
+ {
+ outb(j,tmport++);
+ }
+ outb(0x80,tmport);
+ tmport=workportu + 0x1c;
+ dirctu[h][tarid]=0;
+ if ( l == 0 )
+ {
+ if ( inb(tmport) == 0 )
+ {
+ tmport=workportu+0x18;
+ outb(0x08,tmport);
+ }
+ else
+ {
+ last_cmd[h] |= 0x40;
+ }
+ in_snd[h]=0;
+ restore_flags(flags);
+ return;
+ }
+ tmpcip=pciportu[h];
+ prd=&prd_tableu[h][tarid][0];
+ prd_posu[h][tarid]=prd;
+ if (workrequ->use_sg)
+ {
+ sgpnt = (struct scatterlist *) workrequ->request_buffer;
+ i=0;
+ for(j=0; j<workrequ->use_sg; j++)
+ {
+ (unsigned long)(((unsigned long *)(prd))[i >> 1])=(unsigned long)sgpnt[j].address;
+ (unsigned short int)(((unsigned short int *)(prd))[i+2])=sgpnt[j].length;
+ (unsigned short int)(((unsigned short int *)(prd))[i+3])=0;
+ i +=0x04;
+ }
+ (unsigned short int)(((unsigned short int *)(prd))[i-1])=0x8000;
+ }
+ else
+ {
+ bttl=(unsigned long)workrequ->request_buffer;
+ l=workrequ->request_bufflen;
+ i=0;
+ while ( l > 0x10000 )
+ {
+ (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x0000;
+ (unsigned short int)(((unsigned short int *)(prd))[i+2])=0x0000;
+ (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl;
+ l -= 0x10000;
+ bttl += 0x10000;
+ i += 0x04;
+ }
+ (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x8000;
+ (unsigned short int)(((unsigned short int *)(prd))[i+2])=l;
+ (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl;
+ }
+ tmpcip=tmpcip+4;
+ prdaddru[h][tarid]=(unsigned long)&prd_tableu[h][tarid][0];
+ outl(prdaddru[h][tarid],tmpcip);
+ tmpcip=tmpcip-2;
+ outb(0x06,tmpcip);
+ outb(0x00,tmpcip);
+ tmpcip=tmpcip-2;
+ if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) ||
+ (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15))
+ {
+ dirctu[h][tarid]=0x20;
+ if ( inb(tmport) == 0 )
+ {
+ tmport=workportu+0x18;
+ outb(0x08,tmport);
+ outb(0x01,tmpcip);
+ }
+ else
+ {
+ last_cmd[h] |= 0x40;
+ }
+ in_snd[h]=0;
+ restore_flags(flags);
+ return;
+ }
+ if ( inb(tmport) == 0 )
+ {
+ tmport=workportu+0x18;
+ outb(0x08,tmport);
+ outb(0x09,tmpcip);
+ }
+ else
+ {
+ last_cmd[h] |= 0x40;
+ }
+ in_snd[h]=0;
+ restore_flags(flags);
+ return;
+
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ SCpnt->SCp.Status++;
+}
+
+int atp870u_command(Scsi_Cmnd * SCpnt)
+{
+
+ atp870u_queuecommand(SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier();
+ return SCpnt->result;
+}
+
+unsigned char fun_scam ( unsigned char host,unsigned short int * val )
+{
+ unsigned int tmport ;
+ unsigned short int i,k;
+ unsigned char j;
+
+ tmport = ioportu[host]+0x1c;
+ outw(*val,tmport);
+FUN_D7:
+ for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */
+ {
+ k=inw(tmport);
+ j= (unsigned char)(k >> 8);
+ if ((k & 0x8000) != 0) /* DB7 all release? */
+ {
+ goto FUN_D7;
+ }
+ }
+ *val |= 0x4000; /* assert DB6 */
+ outw(*val,tmport);
+ *val &= 0xdfff; /* assert DB5 */
+ outw(*val,tmport);
+FUN_D5:
+ for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */
+ {
+ if ((inw(tmport) & 0x2000) != 0) /* DB5 all release? */
+ {
+ goto FUN_D5;
+ }
+ }
+ *val |= 0x8000; /* no DB4-0, assert DB7 */
+ *val &= 0xe0ff;
+ outw(*val,tmport);
+ *val &= 0xbfff; /* release DB6 */
+ outw(*val,tmport);
+FUN_D6:
+ for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */
+ {
+ if ((inw(tmport) & 0x4000) != 0) /* DB6 all release? */
+ {
+ goto FUN_D6;
+ }
+ }
+
+ return j;
+}
+
+void tscam( unsigned char host )
+{
+
+ unsigned int tmport ;
+ unsigned char i,j,k;
+ unsigned long n;
+ unsigned short int m,assignid_map,val;
+ unsigned char mbuf[33],quintet[2];
+ static unsigned char g2q_tab[8]={ 0x38,0x31,0x32,0x2b,0x34,0x2d,0x2e,0x27 };
+
+
+ for ( i=0; i < 0x10; i++ )
+ {
+ mydlyu(0xffff);
+ }
+
+ tmport = ioportu[host]+1;
+ outb(0x08,tmport++);
+ outb(0x7f,tmport);
+ tmport = ioportu[host]+0x11;
+ outb(0x20,tmport);
+
+ if ((scam_on[host] & 0x40) == 0)
+ {
+ return;
+ }
+
+ m=1;
+ m <<= host_idu[host];
+ j=16;
+ if ( chip_veru[host] < 4 )
+ {
+ m |= 0xff00;
+ j=8;
+ }
+ assignid_map=m;
+ tmport = ioportu[host]+0x02;
+ outb(0x02,tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */
+ outb(0,tmport++);
+ outb(0,tmport++);
+ outb(0,tmport++);
+ outb(0,tmport++);
+ outb(0,tmport++);
+ outb(0,tmport++);
+
+ for ( i = 0 ; i < j ; i ++ )
+ {
+ m=1;
+ m=m<<i;
+ if ( ( m & assignid_map ) != 0 )
+ {
+ continue;
+ }
+ tmport = ioportu[host]+0x0f;
+ outb(0,tmport++);
+ tmport += 0x02;
+ outb(0,tmport++);
+ outb(0,tmport++);
+ outb(0,tmport++);
+ if ( i > 7 )
+ {
+ k=(i & 0x07) | 0x40;
+ }
+ else
+ {
+ k=i;
+ }
+ outb(k,tmport++);
+ tmport = ioportu[host]+0x1b;
+ if ( chip_veru[host] == 4 )
+ {
+ outb((unsigned char)((inb(tmport) & 0x0e) | 0x01),tmport);
+ }
+ else
+ {
+ outb((unsigned char)(inb(tmport) & 0x0e),tmport);
+ }
+wait_rdyok:
+ tmport = ioportu[host]+0x18;
+ outb(0x09,tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport -= 0x08;
+ k=inb(tmport);
+ if ( k != 0x16 )
+ {
+ if ((k == 0x85) || (k == 0x42))
+ {
+ continue;
+ }
+ tmport = ioportu[host]+0x10;
+ outb(0x41,tmport);
+ goto wait_rdyok;
+ }
+ assignid_map |= m;
+
+ }
+ tmport = ioportu[host]+0x02;
+ outb(0x7f,tmport);
+ tmport = ioportu[host]+0x1b;
+ outb(0x02,tmport);
+
+ outb(0,0x80);
+
+ val=0x0080; /* bsy */
+ tmport = ioportu[host]+0x1c;
+ outw(val,tmport);
+ val |=0x0040; /* sel */
+ outw(val,tmport);
+ val |=0x0004; /* msg */
+ outw(val,tmport);
+ inb(0x80); /* 2 deskew delay(45ns*2=90ns) */
+ val &=0x007f; /* no bsy */
+ outw(val,tmport);
+ mydlyu(0xffff); /* recommanded SCAM selection response time */
+ mydlyu(0xffff);
+ val &=0x00fb; /* after 1ms no msg */
+ outw(val,tmport);
+wait_nomsg:
+ if ((inb(tmport) & 0x04) != 0)
+ {
+ goto wait_nomsg;
+ }
+ outb(1,0x80);
+ mydlyu(100);
+ for ( n=0; n < 0x30000; n++ )
+ {
+ if ((inb(tmport) & 0x80) != 0) /* bsy ? */
+ {
+ goto wait_io;
+ }
+ }
+ goto TCM_SYNC;
+wait_io:
+ for ( n=0; n < 0x30000; n++ )
+ {
+ if ((inb(tmport) & 0x81) == 0x0081)
+ {
+ goto wait_io1;
+ }
+ }
+ goto TCM_SYNC;
+wait_io1:
+ inb(0x80);
+ val |=0x8003; /* io,cd,db7 */
+ outw(val,tmport);
+ inb(0x80);
+ val &=0x00bf; /* no sel */
+ outw(val,tmport);
+ outb(2,0x80);
+TCM_SYNC:
+ mydlyu(0x800);
+ if ((inb(tmport) & 0x80) == 0x00) /* bsy ? */
+ {
+ outw(0,tmport--);
+ outb(0,tmport);
+ tmport=ioportu[host] + 0x15;
+ outb(0,tmport);
+ tmport += 0x03;
+ outb(0x09,tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0);
+ tmport -= 0x08;
+ inb(tmport);
+ return;
+ }
+
+ val &= 0x00ff; /* synchronization */
+ val |= 0x3f00;
+ fun_scam(host,&val);
+ outb(3,0x80);
+ val &= 0x00ff; /* isolation */
+ val |= 0x2000;
+ fun_scam(host,&val);
+ outb(4,0x80);
+ i=8;
+ j=0;
+TCM_ID:
+ if ((inw(tmport) & 0x2000) == 0)
+ {
+ goto TCM_ID;
+ }
+ outb(5,0x80);
+ val &= 0x00ff; /* get ID_STRING */
+ val |= 0x2000;
+ k=fun_scam(host,&val);
+ if ((k & 0x03) == 0)
+ {
+ goto TCM_5;
+ }
+ mbuf[j] <<= 0x01;
+ mbuf[j] &= 0xfe;
+ if ((k & 0x02) != 0)
+ {
+ mbuf[j] |= 0x01;
+ }
+ i--;
+ if ( i > 0 )
+ {
+ goto TCM_ID;
+ }
+ j++;
+ i=8;
+ goto TCM_ID;
+
+TCM_5: /* isolation complete.. */
+/* mbuf[32]=0;
+ printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */
+ i=15;
+ j=mbuf[0];
+ if ((j & 0x20) != 0) /* bit5=1:ID upto 7 */
+ {
+ i=7;
+ }
+ if ((j & 0x06) == 0) /* IDvalid? */
+ {
+ goto G2Q5;
+ }
+ k=mbuf[1];
+small_id:
+ m=1;
+ m <<= k;
+ if ((m & assignid_map) == 0)
+ {
+ goto G2Q_QUIN;
+ }
+ if ( k > 0 )
+ {
+ k--;
+ goto small_id;
+ }
+G2Q5: /* srch from max acceptable ID# */
+ k=i; /* max acceptable ID# */
+G2Q_LP:
+ m=1;
+ m <<= k;
+ if ((m & assignid_map) == 0)
+ {
+ goto G2Q_QUIN;
+ }
+ if ( k > 0 )
+ {
+ k--;
+ goto G2Q_LP;
+ }
+G2Q_QUIN: /* k=binID#, */
+ assignid_map |= m;
+ if ( k < 8 )
+ {
+ quintet[0]=0x38; /* 1st dft ID<8 */
+ }
+ else
+ {
+ quintet[0]=0x31; /* 1st ID>=8 */
+ }
+ k &= 0x07;
+ quintet[1]=g2q_tab[k];
+
+ val &= 0x00ff; /* AssignID 1stQuintet,AH=001xxxxx */
+ m=quintet[0] << 8;
+ val |= m;
+ fun_scam(host,&val);
+ val &= 0x00ff; /* AssignID 2ndQuintet,AH=001xxxxx */
+ m=quintet[1] << 8;
+ val |= m;
+ fun_scam(host,&val);
+
+ goto TCM_SYNC;
+
+}
+
+void is870(unsigned long host,unsigned int wkport )
+{
+ unsigned int tmport ;
+ unsigned char i,j,k,rmb;
+ unsigned short int m;
+ static unsigned char mbuf[512];
+ static unsigned char satn[9] = { 0,0,0,0,0,0,0,6,6 };
+ static unsigned char inqd[9] = { 0x12,0,0,0,0x24,0,0,0x24,6 };
+ static unsigned char synn[6] = { 0x80,1,3,1,0x19,0x0e };
+ static unsigned char synu[6] = { 0x80,1,3,1,0x0c,0x0e };
+ static unsigned char synw[6] = { 0x80,1,3,1,0x0c,0x07 };
+ static unsigned char wide[6] = { 0x80,1,2,3,1,0 };
+
+ sync_idu=0;
+ tmport=wkport+0x3a;
+ outb((unsigned char)(inb(tmport) | 0x10),tmport);
+
+ for ( i = 0 ; i < 16 ; i ++ )
+ {
+ if ((chip_veru[host] != 4) && (i > 7))
+ {
+ break;
+ }
+ m=1;
+ m=m<<i;
+ if ( ( m & active_idu[host] ) != 0 )
+ {
+ continue;
+ }
+ if ( i == host_idu[host] )
+ {
+ printk(" ID: %2d Host Adapter\n",host_idu[host]);
+ continue;
+ }
+ if ( chip_veru[host] == 4 )
+ {
+ tmport=wkport+0x1b;
+ j=(inb(tmport) & 0x0e) | 0x01;
+ outb(j,tmport);
+ }
+ tmport=wkport+1;
+ outb(0x08,tmport++);
+ outb(0x7f,tmport++);
+ outb(satn[0],tmport++);
+ outb(satn[1],tmport++);
+ outb(satn[2],tmport++);
+ outb(satn[3],tmport++);
+ outb(satn[4],tmport++);
+ outb(satn[5],tmport++);
+ tmport+=0x06;
+ outb(0,tmport);
+ tmport+=0x02;
+ outb(devspu[host][i],tmport++);
+ outb(0,tmport++);
+ outb(satn[6],tmport++);
+ outb(satn[7],tmport++);
+ j=i;
+ if ((j & 0x08) != 0)
+ {
+ j=(j & 0x07) | 0x40;
+ }
+ outb(j,tmport);
+ tmport+=0x03;
+ outb(satn[8],tmport);
+ tmport+=0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+ {
+ continue;
+ }
+ while ( inb(tmport) != 0x8e );
+ active_idu[host] |= m;
+
+ tmport=wkport+0x10;
+ outb(0x30,tmport);
+ tmport=wkport+0x04;
+ outb(0x00,tmport);
+
+phase_cmd:
+ tmport=wkport+0x18;
+ outb(0x08,tmport);
+ tmport+=0x07;
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ j=inb(tmport);
+ if ( j != 0x16 )
+ {
+ tmport=wkport+0x10;
+ outb(0x41,tmport);
+ goto phase_cmd;
+ }
+sel_ok:
+ tmport=wkport+3;
+ outb(inqd[0],tmport++);
+ outb(inqd[1],tmport++);
+ outb(inqd[2],tmport++);
+ outb(inqd[3],tmport++);
+ outb(inqd[4],tmport++);
+ outb(inqd[5],tmport);
+ tmport+=0x07;
+ outb(0,tmport);
+ tmport+=0x02;
+ outb(devspu[host][i],tmport++);
+ outb(0,tmport++);
+ outb(inqd[6],tmport++);
+ outb(inqd[7],tmport++);
+ tmport+=0x03;
+ outb(inqd[8],tmport);
+ tmport+=0x07;
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+ {
+ continue;
+ }
+ while ( inb(tmport) != 0x8e );
+ if ( chip_veru[host] == 4 )
+ {
+ tmport=wkport+0x1b;
+ j=inb(tmport) & 0x0e;
+ outb(j,tmport);
+ }
+ tmport=wkport+0x18;
+ outb(0x08,tmport);
+ tmport += 0x07;
+ j=0;
+rd_inq_data:
+ k=inb(tmport);
+ if ((k & 0x01) != 0 )
+ {
+ tmport-=0x06;
+ mbuf[j++]=inb(tmport);
+ tmport+=0x06;
+ goto rd_inq_data;
+ }
+ if ((k & 0x80) == 0 )
+ {
+ goto rd_inq_data;
+ }
+ tmport-=0x08;
+ j=inb(tmport);
+ if ( j == 0x16 )
+ {
+ goto inq_ok;
+ }
+ tmport=wkport+0x10;
+ outb(0x46,tmport);
+ tmport+=0x02;
+ outb(0,tmport++);
+ outb(0,tmport++);
+ outb(0,tmport++);
+ tmport+=0x03;
+ outb(0x08,tmport);
+ tmport+=0x07;
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ if (inb(tmport) != 0x16)
+ {
+ goto sel_ok;
+ }
+inq_ok:
+ mbuf[36]=0;
+ printk(" ID: %2d %s\n",i,&mbuf[8]);
+ devtypeu[host][i]=mbuf[0];
+ rmb=mbuf[1];
+ if ( chip_veru[host] != 4 )
+ {
+ goto not_wide;
+ }
+ if ((mbuf[7] & 0x60) == 0)
+ {
+ goto not_wide;
+ }
+ if ((global_map[host] & 0x20) == 0)
+ {
+ goto not_wide;
+ }
+ tmport=wkport+0x1b;
+ j=(inb(tmport) & 0x0e) | 0x01;
+ outb(j,tmport);
+ tmport=wkport+3;
+ outb(satn[0],tmport++);
+ outb(satn[1],tmport++);
+ outb(satn[2],tmport++);
+ outb(satn[3],tmport++);
+ outb(satn[4],tmport++);
+ outb(satn[5],tmport++);
+ tmport+=0x06;
+ outb(0,tmport);
+ tmport+=0x02;
+ outb(devspu[host][i],tmport++);
+ outb(0,tmport++);
+ outb(satn[6],tmport++);
+ outb(satn[7],tmport++);
+ tmport+=0x03;
+ outb(satn[8],tmport);
+ tmport+=0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+ {
+ continue;
+ }
+ while ( inb(tmport) != 0x8e );
+try_wide:
+ j=0;
+ tmport=wkport+0x14;
+ outb(0x05,tmport);
+ tmport += 0x04;
+ outb(0x20,tmport);
+ tmport+=0x07;
+
+ while ((inb(tmport) & 0x80) == 0 )
+ {
+ if ((inb(tmport) & 0x01) != 0 )
+ {
+ tmport-=0x06;
+ outb(wide[j++],tmport);
+ tmport+=0x06;
+ }
+ }
+ tmport-=0x08;
+ while ((inb(tmport) & 0x80) == 0x00);
+ j=inb(tmport) & 0x0f;
+ if ( j == 0x0f )
+ {
+ goto widep_in;
+ }
+ if ( j == 0x0a )
+ {
+ goto widep_cmd;
+ }
+ if ( j == 0x0e )
+ {
+ goto try_wide;
+ }
+ continue;
+widep_out:
+ tmport=wkport+0x18;
+ outb(0x20,tmport);
+ tmport+=0x07;
+ while ((inb(tmport) & 0x80) == 0 )
+ {
+ if ((inb(tmport) & 0x01) != 0 )
+ {
+ tmport-=0x06;
+ outb(0,tmport);
+ tmport+=0x06;
+ }
+ }
+ tmport-=0x08;
+ j=inb(tmport) & 0x0f;
+ if ( j == 0x0f )
+ {
+ goto widep_in;
+ }
+ if ( j == 0x0a )
+ {
+ goto widep_cmd;
+ }
+ if ( j == 0x0e )
+ {
+ goto widep_out;
+ }
+ continue;
+widep_in:
+ tmport=wkport+0x14;
+ outb(0xff,tmport);
+ tmport += 0x04;
+ outb(0x20,tmport);
+ tmport+=0x07;
+ k=0;
+widep_in1:
+ j=inb(tmport);
+ if ((j & 0x01) != 0)
+ {
+ tmport-=0x06;
+ mbuf[k++]=inb(tmport);
+ tmport+=0x06;
+ goto widep_in1;
+ }
+ if ((j & 0x80) == 0x00)
+ {
+ goto widep_in1;
+ }
+ tmport-=0x08;
+ j=inb(tmport) & 0x0f;
+ if ( j == 0x0f )
+ {
+ goto widep_in;
+ }
+ if ( j == 0x0a )
+ {
+ goto widep_cmd;
+ }
+ if ( j == 0x0e )
+ {
+ goto widep_out;
+ }
+ continue;
+widep_cmd:
+ tmport=wkport+0x10;
+ outb(0x30,tmport);
+ tmport=wkport+0x14;
+ outb(0x00,tmport);
+ tmport+=0x04;
+ outb(0x08,tmport);
+ tmport+=0x07;
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ j=inb(tmport);
+ if ( j != 0x16 )
+ {
+ if ( j == 0x4e )
+ {
+ goto widep_out;
+ }
+ continue;
+ }
+ if ( mbuf[0] != 0x01 )
+ {
+ goto not_wide;
+ }
+ if ( mbuf[1] != 0x02 )
+ {
+ goto not_wide;
+ }
+ if ( mbuf[2] != 0x03 )
+ {
+ goto not_wide;
+ }
+ if ( mbuf[3] != 0x01 )
+ {
+ goto not_wide;
+ }
+ m=1;
+ m = m << i;
+ wide_idu[host] |= m;
+not_wide:
+ if ((devtypeu[host][i] == 0x00) || (devtypeu[host][i] == 0x07))
+ {
+ goto set_sync;
+ }
+ continue;
+set_sync:
+ tmport=wkport+0x1b;
+ j=inb(tmport) & 0x0e;
+ if ((m & wide_idu[host]) != 0 )
+ {
+ j |= 0x01;
+ }
+ outb(j,tmport);
+ tmport=wkport+3;
+ outb(satn[0],tmport++);
+ outb(satn[1],tmport++);
+ outb(satn[2],tmport++);
+ outb(satn[3],tmport++);
+ outb(satn[4],tmport++);
+ outb(satn[5],tmport++);
+ tmport+=0x06;
+ outb(0,tmport);
+ tmport+=0x02;
+ outb(devspu[host][i],tmport++);
+ outb(0,tmport++);
+ outb(satn[6],tmport++);
+ outb(satn[7],tmport++);
+ tmport+=0x03;
+ outb(satn[8],tmport);
+ tmport+=0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+ {
+ continue;
+ }
+ while ( inb(tmport) != 0x8e);
+try_sync:
+ j=0;
+ tmport=wkport+0x14;
+ outb(0x06,tmport);
+ tmport += 0x04;
+ outb(0x20,tmport);
+ tmport+=0x07;
+
+ while ((inb(tmport) & 0x80) == 0 )
+ {
+ if ((inb(tmport) & 0x01) != 0 )
+ {
+ tmport-=0x06;
+ if ( rmb != 0 )
+ {
+ outb(synn[j++],tmport);
+ }
+ else
+ {
+ if ((m & wide_idu[host]) != 0)
+ {
+ outb(synw[j++],tmport);
+ }
+ else
+ {
+ if ((m & ultra_map[host]) != 0)
+ {
+ outb(synu[j++],tmport);
+ }
+ else
+ {
+ outb(synn[j++],tmport);
+ }
+ }
+ }
+ tmport+=0x06;
+ }
+ }
+ tmport-=0x08;
+ while ((inb(tmport) & 0x80) == 0x00);
+ j=inb(tmport) & 0x0f;
+ if ( j == 0x0f )
+ {
+ goto phase_ins;
+ }
+ if ( j == 0x0a )
+ {
+ goto phase_cmds;
+ }
+ if ( j == 0x0e )
+ {
+ goto try_sync;
+ }
+ continue;
+phase_outs:
+ tmport=wkport+0x18;
+ outb(0x20,tmport);
+ tmport+=0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ {
+ if ((inb(tmport) & 0x01) != 0x00)
+ {
+ tmport-=0x06;
+ outb(0x00,tmport);
+ tmport+=0x06;
+ }
+ }
+ tmport-=0x08;
+ j=inb(tmport);
+ if ( j == 0x85 )
+ {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if ( j == 0x0f )
+ {
+ goto phase_ins;
+ }
+ if ( j == 0x0a )
+ {
+ goto phase_cmds;
+ }
+ if ( j == 0x0e )
+ {
+ goto phase_outs;
+ }
+ continue;
+phase_ins:
+ tmport=wkport+0x14;
+ outb(0xff,tmport);
+ tmport += 0x04;
+ outb(0x20,tmport);
+ tmport+=0x07;
+ k=0;
+phase_ins1:
+ j=inb(tmport);
+ if ((j & 0x01) != 0x00)
+ {
+ tmport-=0x06;
+ mbuf[k++]=inb(tmport);
+ tmport+=0x06;
+ goto phase_ins1;
+ }
+ if ((j & 0x80) == 0x00)
+ {
+ goto phase_ins1;
+ }
+ tmport-=0x08;
+ while ((inb(tmport) & 0x80) == 0x00);
+ j=inb(tmport);
+ if ( j == 0x85 )
+ {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if ( j == 0x0f )
+ {
+ goto phase_ins;
+ }
+ if ( j == 0x0a )
+ {
+ goto phase_cmds;
+ }
+ if ( j == 0x0e )
+ {
+ goto phase_outs;
+ }
+ continue;
+phase_cmds:
+ tmport=wkport+0x10;
+ outb(0x30,tmport);
+tar_dcons:
+ tmport=wkport+0x14;
+ outb(0x00,tmport);
+ tmport+=0x04;
+ outb(0x08,tmport);
+ tmport+=0x07;
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport-=0x08;
+ j=inb(tmport);
+ if ( j != 0x16 )
+ {
+ continue;
+ }
+ if ( mbuf[0] != 0x01 )
+ {
+ continue;
+ }
+ if ( mbuf[1] != 0x03 )
+ {
+ continue;
+ }
+ if ( mbuf[4] == 0x00 )
+ {
+ continue;
+ }
+ if ( mbuf[3] > 0x64 )
+ {
+ continue;
+ }
+ if ( mbuf[4] > 0x0c )
+ {
+ mbuf[4]=0x0c;
+ }
+ devspu[host][i] = mbuf[4];
+ if ((mbuf[3] < 0x0d) && (rmb == 0))
+ {
+ j=0xa0;
+ goto set_syn_ok;
+ }
+ if ( mbuf[3] < 0x1a )
+ {
+ j=0x20;
+ goto set_syn_ok;
+ }
+ if ( mbuf[3] < 0x33 )
+ {
+ j=0x40;
+ goto set_syn_ok;
+ }
+ if ( mbuf[3] < 0x4c )
+ {
+ j=0x50;
+ goto set_syn_ok;
+ }
+ j=0x60;
+set_syn_ok:
+ devspu[host][i] = (devspu[host][i] & 0x0f) | j;
+ }
+ tmport=wkport+0x3a;
+ outb((unsigned char)(inb(tmport) & 0xef),tmport);
+}
+
+/* return non-zero on detection */
+int atp870u_detect(Scsi_Host_Template * tpnt)
+{
+ unsigned char irq,h,k;
+ unsigned long flags;
+ unsigned int base_io,error,tmport;
+ unsigned short index = 0;
+ unsigned char pci_bus[3], pci_device_fn[3], chip_ver[3],host_id;
+ struct Scsi_Host * shpnt = NULL;
+ int count = 0;
+ static unsigned short devid[7]={0x8002,0x8010,0x8020,0x8030,0x8040,0x8050,0};
+ static struct pci_dev *pdev = NULL;
+
+ printk("aec671x_detect: \n");
+ if (!pci_present())
+ {
+ printk(" NO BIOS32 SUPPORT.\n");
+ return count;
+ }
+
+ tpnt->proc_dir = &proc_scsi_atp870u;
+
+ for ( h = 0 ; h < 2 ; h++ )
+ {
+ active_idu[h]=0;
+ wide_idu[h]=0;
+ host_idu[h]=0x07;
+ quhdu[h]=0;
+ quendu[h]=0;
+ pci_bus[h]=0;
+ pci_device_fn[h]=0xff;
+ chip_ver[h]=0;
+ last_cmd[h]=0xff;
+ in_snd[h]=0;
+ in_int[h]=0;
+ for ( k = 0 ; k < qcnt ; k++ )
+ {
+ querequ[h][k]=0;
+ }
+ for ( k = 0 ; k < 16 ; k++ )
+ {
+ curr_req[h][k]=0;
+ }
+ }
+ h=0;
+ while ( devid[h] != 0 )
+ {
+ pci_find_device(0x1191,devid[h],pdev);
+ if (pdev == NULL); {
+ h++;
+ index=0;
+ continue;
+ }
+ chip_ver[2]=0;
+
+ /* To avoid messing with the things below... */
+ pci_device_fn[2] = pdev->devfn;
+ pci_bus[2] = pdev->bus->number;
+
+ if ( devid[h] == 0x8002 )
+ {
+ error = pci_read_config_byte(pdev,0x08,&chip_ver[2]);
+ if ( chip_ver[2] < 2 )
+ {
+ goto nxt_devfn;
+ }
+ }
+ if ( devid[h] == 0x8010 )
+ {
+ chip_ver[2]=0x04;
+ }
+ if ( pci_device_fn[2] < pci_device_fn[0] )
+ {
+ pci_bus[1]=pci_bus[0];
+ pci_device_fn[1]=pci_device_fn[0];
+ chip_ver[1]=chip_ver[0];
+ pci_bus[0]=pci_bus[2];
+ pci_device_fn[0]=pci_device_fn[2];
+ chip_ver[0]=chip_ver[2];
+ }
+ else if ( pci_device_fn[2] < pci_device_fn[1] )
+ {
+ pci_bus[1]=pci_bus[2];
+ pci_device_fn[1]=pci_device_fn[2];
+ chip_ver[1]=chip_ver[2];
+ }
+nxt_devfn:
+ index++;
+ if ( index > 3 )
+ {
+ index=0;
+ h++;
+ }
+ }
+ for ( h=0; h < 2; h++ )
+ {
+ if ( pci_device_fn[h] == 0xff )
+ {
+ return count;
+ }
+
+ pdev->devfn = pci_device_fn[h];
+ pdev->bus->number = pci_bus[h];
+
+ /* Found an atp870u/w. */
+ error = pci_read_config_dword(pdev,0x10,&base_io);
+ error += pci_read_config_byte(pdev,0x3c,&irq);
+ error += pci_read_config_byte(pdev,0x49,&host_id);
+
+ base_io &= 0xfffffff8;
+ printk(" ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d IO:%x, IRQ:%d.\n"
+ ,h,base_io,irq);
+ ioportu[h]=base_io;
+ pciportu[h]=base_io + 0x20;
+ irqnumu[h]=irq;
+ host_id &= 0x07;
+ host_idu[h]=host_id;
+ chip_veru[h]=chip_ver[h];
+
+ tmport=base_io+0x22;
+ scam_on[h]=inb(tmport);
+ tmport += 0x0b;
+ global_map[h]=inb(tmport++);
+ ultra_map[h]=inw(tmport);
+ if ( ultra_map[h] == 0 )
+ {
+ scam_on[h]=0x00;
+ global_map[h]=0x20;
+ ultra_map[h]=0xffff;
+ }
+
+ shpnt = scsi_register(tpnt,4);
+
+ save_flags(flags);
+ cli();
+ if (request_irq(irq,atp870u_intr_handle, 0, "atp870u", NULL))
+ {
+ printk("Unable to allocate IRQ for Acard controller.\n");
+ goto unregister;
+ }
+
+ tmport=base_io+0x3a;
+ k=(inb(tmport) & 0xf3) | 0x10;
+ outb(k,tmport);
+ outb((k & 0xdf),tmport);
+ mydlyu(0x8000);
+ outb(k,tmport);
+ mydlyu(0x8000);
+ tmport=base_io;
+ outb((host_id | 0x08),tmport);
+ tmport += 0x18;
+ outb(0,tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0);
+ tmport -= 0x08;
+ inb(tmport);
+ tmport = base_io +1;
+ outb(8,tmport++);
+ outb(0x7f,tmport);
+ tmport = base_io + 0x11;
+ outb(0x20,tmport);
+
+ tscam(h);
+ is870(h,base_io);
+ tmport=base_io+0x3a;
+ outb((inb(tmport) & 0xef),tmport);
+
+ atp_host[h] = shpnt;
+ if ( chip_ver[h] == 4 )
+ {
+ shpnt->max_id = 16;
+ }
+ shpnt->this_id = host_id;
+ shpnt->unique_id = base_io;
+ shpnt->io_port = base_io;
+ shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */
+ shpnt->irq = irq;
+ restore_flags(flags);
+ request_region(base_io, 0x40,"atp870u"); /* Register the IO ports that we use */
+ count++;
+ index++;
+ continue;
+unregister:
+ scsi_unregister(shpnt);
+ restore_flags(flags);
+ index++;
+ continue;
+ }
+
+ return count;
+}
+
+/* The abort command does not leave the device in a clean state where
+ it is available to be used again. Until this gets worked out, we will
+ leave it commented out. */
+
+int atp870u_abort(Scsi_Cmnd * SCpnt)
+{
+ unsigned char h,j;
+ unsigned int tmport;
+/* printk(" atp870u_abort: \n"); */
+ for ( h=0; h <= admaxu; h++ )
+ {
+ if ( SCpnt->host == atp_host[h] )
+ {
+ goto find_adp;
+ }
+ }
+ panic("Abort host not found !");
+find_adp:
+ printk(" workingu=%x last_cmd=%x ",workingu[h],last_cmd[h]);
+ printk(" quhdu=%x quendu=%x ",quhdu[h],quendu[h]);
+ tmport=ioportu[h];
+ for ( j=0; j < 0x17; j++)
+ {
+ printk(" r%2x=%2x",j,inb(tmport++));
+ }
+ tmport += 0x05;
+ printk(" r1c=%2x",inb(tmport));
+ tmport += 0x03;
+ printk(" r1f=%2x in_snd=%2x ",inb(tmport),in_snd[h]);
+ tmport++;
+ printk(" r20=%2x",inb(tmport));
+ tmport += 0x02;
+ printk(" r22=%2x \n",inb(tmport));
+ return (SCSI_ABORT_SNOOZE);
+}
+
+int atp870u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+ unsigned char h;
+ /*
+ * See if a bus reset was suggested.
+ */
+/* printk("atp870u_reset: \n"); */
+ for( h=0; h <= admaxu; h++ )
+ {
+ if ( SCpnt->host == atp_host[h] )
+ {
+ goto find_host;
+ }
+ }
+ panic("Reset bus host not found !");
+find_host:
+/* SCpnt->result = 0x00080000;
+ SCpnt->scsi_done(SCpnt);
+ workingu[h]=0;
+ quhdu[h]=0;
+ quendu[h]=0;
+ return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET); */
+ return (SCSI_RESET_SNOOZE);
+}
+
+const char *
+atp870u_info(struct Scsi_Host *notused)
+{
+ static char buffer[128];
+
+ strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V1.0 ");
+
+ return buffer;
+}
+
+int
+atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
+{
+ return (-ENOSYS); /* Currently this is a no-op */
+}
+
+#define BLS buffer + len + size
+int
+atp870u_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ struct Scsi_Host *HBAptr;
+ static u8 buff[512];
+ int i;
+ int size = 0;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ HBAptr = NULL;
+ for (i = 0; i < 2; i++)
+ {
+ if ((HBAptr = atp_host[i]) != NULL)
+ {
+ if (HBAptr->host_no == hostno)
+ {
+ break;
+ }
+ HBAptr = NULL;
+ }
+ }
+
+ if (HBAptr == NULL)
+ {
+ size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno);
+ len += size; pos = begin + len; size = 0;
+ goto stop_output;
+ }
+
+ if (inout == TRUE) /* Has data been written to the file? */
+ {
+ return (atp870u_set_info(buffer, length, HBAptr));
+ }
+
+ if (offset == 0)
+ {
+ memset(buff, 0, sizeof(buff));
+ }
+
+ size += sprintf(BLS, "ACARD AEC-671X Driver Version: 1.0\n");
+ len += size; pos = begin + len; size = 0;
+
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Adapter Configuration:\n");
+ size += sprintf(BLS, " Base IO: %#.4lx\n", HBAptr->io_port);
+ size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
+ len += size; pos = begin + len; size = 0;
+
+stop_output:
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ {
+ len = length; /* Ending slop */
+ }
+
+ return (len);
+}
+
+#include "sd.h"
+
+int atp870u_biosparam(Scsi_Disk * disk, kdev_t dev, int * ip)
+{
+ int heads, sectors, cylinders;
+
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if ( cylinders > 1024 )
+ {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+
+ ip[0] = heads;
+ ip[1] = sectors;
+ ip[2] = cylinders;
+
+ return 0;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = ATP870U;
+
+#include "scsi_module.c"
+#endif
+
--- /dev/null
+#ifndef _ATP870U_H
+
+/* $Id: atp870u.h,v 1.0 1997/05/07 15:09:00 root Exp root $
+ *
+ * Header file for the ACARD 870U/W driver for Linux
+ *
+ * $Log: atp870u.h,v $
+ * Revision 1.0 1997/05/07 15:09:00 root
+ * Initial revision
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* I/O Port */
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+
+int atp870u_detect(Scsi_Host_Template *);
+int atp870u_command(Scsi_Cmnd *);
+int atp870u_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int atp870u_abort(Scsi_Cmnd *);
+int atp870u_reset(Scsi_Cmnd *, unsigned int);
+int atp870u_biosparam(Disk *, kdev_t, int*);
+void send_s870(unsigned char);
+
+#define qcnt 32
+#define ATP870U_SCATTER 127
+#define ATP870U_CMDLUN 1
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry proc_scsi_atp870u;
+
+extern const char *atp870u_info(struct Scsi_Host *);
+
+extern int atp870u_proc_info(char *, char **, off_t, int, int, int);
+
+#define ATP870U { \
+ next: NULL, \
+ module: NULL, \
+ proc_dir: &proc_scsi_atp870u, \
+ proc_info: atp870u_proc_info, \
+ name: NULL, \
+ detect: atp870u_detect, \
+ release: NULL, \
+ info: atp870u_info, \
+ command: atp870u_command, \
+ queuecommand: atp870u_queuecommand, \
+ eh_strategy_handler: NULL, \
+ eh_abort_handler: NULL, \
+ eh_device_reset_handler: NULL, \
+ eh_bus_reset_handler: NULL, \
+ eh_host_reset_handler: NULL, \
+ abort: atp870u_abort, \
+ reset: atp870u_reset, \
+ slave_attach: NULL, \
+ bios_param: atp870u_biosparam, \
+ can_queue: qcnt, \
+ this_id: 1, \
+ sg_tablesize: ATP870U_SCATTER, \
+ cmd_per_lun: ATP870U_CMDLUN, \
+ present: 0, \
+ unchecked_isa_dma: 0, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 0 \
+}
+#endif
/* *************************************************** */
#endif
-#if 0
/* IBM/ANSI scsi scan ordering */
/* Stick this back in when the scsi.c changes are there */
- shpnt->reverse_scan = 1;
-#endif
+ shpnt->reverse_ordering = 1;
/* saving info */
#include "atari_scsi.h"
#endif
+#ifdef CONFIG_MAC_SCSI_OLD
+#include "mac_scsi.h"
+#endif
+
+#ifdef CONFIG_MAC_SCSI
+#include "mac_scsinew.h"
+#endif
+
+#ifdef CONFIG_SCSI_MAC_ESP
+#include "mac_esp.h"
+#endif
+
#ifdef CONFIG_SCSI_ADVANSYS
#include "advansys.h"
#endif
#include "qlogicisp.h"
#endif
+#ifdef CONFIG_SCSI_QLOGIC_FC
+#include "qlogicfc.h"
+#endif
+
#ifdef CONFIG_SCSI_SEAGATE
#include "seagate.h"
#endif
#include "wd7000.h"
#endif
+#ifdef CONFIG_SCSI_MCA_53C9X
+#include "mca_53c9x.h"
+#endif
+
#ifdef CONFIG_SCSI_IBMMCA
#include "ibmmca.h"
#endif
#include "AM53C974.h"
#endif
+#ifdef CONFIG_SCSI_MEGARAID
+#include "megaraid.h"
+#endif
+
+#ifdef CONFIG_SCSI_ACARD
+#include "atp870u.h"
+#endif
+
#ifdef CONFIG_SCSI_SUNESP
#include "esp.h"
#endif
#include "pluto.h"
#endif
+#ifdef CONFIG_SCSI_INITIO
+#include "ini9100u.h"
+#endif
+
#ifdef CONFIG_SCSI_DEBUG
#include "scsi_debug.h"
#endif
#endif
#endif
+#ifdef CONFIG_MAC
+#ifdef CONFIG_MAC_SCSI_OLD
+ MAC_SCSI,
+#endif
+#ifdef CONFIG_SCSI_MAC_ESP
+ SCSI_MAC_ESP,
+#endif
+#ifdef CONFIG_MAC_SCSI
+ MAC_NCR5380,
+#endif
+#endif
+
#ifdef CONFIG_MVME16x_SCSI
MVME16x_SCSI,
#endif
#ifdef CONFIG_SCSI_QLOGIC_ISP
QLOGICISP,
#endif
+#ifdef CONFIG_SCSI_QLOGIC_FC
+ QLOGICFC,
+#endif
#ifdef CONFIG_SCSI_PAS16
MV_PAS16,
#endif
#ifdef CONFIG_SCSI_7000FASST
WD7000,
#endif
+#ifdef CONFIG_SCSI_MCA_53C9X
+ MCA_53C9X,
+#endif
#ifdef CONFIG_SCSI_IBMMCA
IBMMCA,
#endif
#ifdef CONFIG_SCSI_AM53C974
AM53C974,
#endif
+#ifdef CONFIG_SCSI_MEGARAID
+ MEGARAID,
+#endif
+#ifdef CONFIG_SCSI_ACARD
+ ATP870U,
+#endif
#ifdef CONFIG_SCSI_SUNESP
SCSI_SPARC_ESP,
#endif
#ifdef CONFIG_SCSI_GDTH
GDTH,
#endif
+#ifdef CONFIG_SCSI_INITIO
+ INI9100U,
+#endif
#ifdef CONFIG_SCSI_QLOGICPTI
QLOGICPTI,
#endif
* Host has rejected a command because it was busy.
*/
unsigned host_blocked:1;
+
+ /*
+ * Host uses correct SCSI ordering not PC ordering. The bit is
+ * set for the minority of drivers whose authors actually read the spec ;)
+ */
+ unsigned reverse_ordering:1;
+
void (*select_queue_depths)(struct Scsi_Host *, Scsi_Device *);
/*
--- /dev/null
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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
+ * 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
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ ************************************************************************
+ Module: i91uscsi.c
+ Description: PCI I/F for INI-910 SCSI Bus Master Controller
+ Revision History:
+ 11/09/94 Tim Chen, Initiali Version 0.90A
+ 01/17/95 TC, release ver 1.01
+ 02/09/95 TC modify ReadPCIConfig, try both mechanisms;
+ 02/15/95 TC add support for INI-9100W
+ 06/04/96 HC, Change to fit LINUX from jaspci.c
+ 11/18/96 HC, Port for tulip
+ 07/08/98 hc, Support 0002134A
+ 07/23/98 wh, Change the abort_srb routine.
+ 09/16/98 hl, Support ALPHA, Rewrite the returnNumberAdapters <01>
+ 12/09/98 bv, Removed unused code, changed tul_se2_wait to
+ use udelay(30) and tul_do_pause to enable
+ interrupts for >= 2.1.95
+ 12/13/98 bv, Use spinlocks instead of cli() for serialized
+ access to HCS_Semaph, HCS_FirstAvail and HCS_LastAvail
+ members of the HCS structure.
+**********************************************************************/
+
+#define DEBUG_INTERRUPT 0
+#define DEBUG_QUEUE 0
+#define DEBUG_STATE 0
+#define INT_DISC 0
+
+
+#ifndef CVT_LINUX_VERSION
+#define CVT_LINUX_VERSION(V,P,S) (V * 65536 + P * 256 + S)
+#endif
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/blk.h>
+#include <asm/io.h>
+
+#include "i91uscsi.h"
+
+/*--- external functions --*/
+static void tul_se2_wait(void);
+
+/*--- forward refrence ---*/
+static SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun);
+static SCB *tul_find_done_scb(HCS * pCurHcb);
+
+static int tulip_main(HCS * pCurHcb);
+
+static int tul_next_state(HCS * pCurHcb);
+static int tul_state_1(HCS * pCurHcb);
+static int tul_state_2(HCS * pCurHcb);
+static int tul_state_3(HCS * pCurHcb);
+static int tul_state_4(HCS * pCurHcb);
+static int tul_state_5(HCS * pCurHcb);
+static int tul_state_6(HCS * pCurHcb);
+static int tul_state_7(HCS * pCurHcb);
+static int tul_xfer_data_in(HCS * pCurHcb);
+static int tul_xfer_data_out(HCS * pCurHcb);
+static int tul_xpad_in(HCS * pCurHcb);
+static int tul_xpad_out(HCS * pCurHcb);
+static int tul_status_msg(HCS * pCurHcb);
+
+static int tul_msgin(HCS * pCurHcb);
+static int tul_msgin_sync(HCS * pCurHcb);
+static int tul_msgin_accept(HCS * pCurHcb);
+static int tul_msgout_reject(HCS * pCurHcb);
+static int tul_msgin_extend(HCS * pCurHcb);
+
+static int tul_msgout_ide(HCS * pCurHcb);
+static int tul_msgout_abort_targ(HCS * pCurHcb);
+static int tul_msgout_abort_tag(HCS * pCurHcb);
+
+static int tul_bus_device_reset(HCS * pCurHcb);
+static void tul_select_atn(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb);
+static int int_tul_busfree(HCS * pCurHcb);
+int int_tul_scsi_rst(HCS * pCurHcb);
+static int int_tul_bad_seq(HCS * pCurHcb);
+static int int_tul_resel(HCS * pCurHcb);
+static int tul_sync_done(HCS * pCurHcb);
+static int wdtr_done(HCS * pCurHcb);
+static int wait_tulip(HCS * pCurHcb);
+static int tul_wait_done_disc(HCS * pCurHcb);
+static int tul_wait_disc(HCS * pCurHcb);
+static void tulip_scsi(HCS * pCurHcb);
+static int tul_post_scsi_rst(HCS * pCurHcb);
+
+static void tul_se2_ew_en(WORD CurBase);
+static void tul_se2_ew_ds(WORD CurBase);
+static int tul_se2_rd_all(WORD CurBase);
+static void tul_se2_update_all(WORD CurBase); /* setup default pattern */
+static void tul_read_eeprom(WORD CurBase);
+
+ /* ---- EXTERNAL VARIABLES ---- */
+HCS tul_hcs[MAX_SUPPORTED_ADAPTERS];
+ /* ---- INTERNAL VARIABLES ---- */
+static INI_ADPT_STRUCT i91u_adpt[MAX_SUPPORTED_ADAPTERS];
+
+/*NVRAM nvram, *nvramp = &nvram; */
+static NVRAM i91unvram;
+static NVRAM *i91unvramp;
+
+
+
+static UCHAR i91udftNvRam[64] =
+{
+/*----------- header -----------*/
+ 0x25, 0xc9, /* Signature */
+ 0x40, /* Size */
+ 0x01, /* Revision */
+ /* -- Host Adapter Structure -- */
+ 0x95, /* ModelByte0 */
+ 0x00, /* ModelByte1 */
+ 0x00, /* ModelInfo */
+ 0x01, /* NumOfCh */
+ NBC1_DEFAULT, /* BIOSConfig1 */
+ 0, /* BIOSConfig2 */
+ 0, /* HAConfig1 */
+ 0, /* HAConfig2 */
+ /* SCSI channel 0 and target Structure */
+ 7, /* SCSIid */
+ NCC1_DEFAULT, /* SCSIconfig1 */
+ 0, /* SCSIconfig2 */
+ 0x10, /* NumSCSItarget */
+
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+
+ /* SCSI channel 1 and target Structure */
+ 7, /* SCSIid */
+ NCC1_DEFAULT, /* SCSIconfig1 */
+ 0, /* SCSIconfig2 */
+ 0x10, /* NumSCSItarget */
+
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0}; /* - CheckSum - */
+
+
+static UCHAR tul_rate_tbl[8] = /* fast 20 */
+{
+ /* nanosecond devide by 4 */
+ 12, /* 50ns, 20M */
+ 18, /* 75ns, 13.3M */
+ 25, /* 100ns, 10M */
+ 31, /* 125ns, 8M */
+ 37, /* 150ns, 6.6M */
+ 43, /* 175ns, 5.7M */
+ 50, /* 200ns, 5M */
+ 62 /* 250ns, 4M */
+};
+
+extern int tul_num_ch;
+
+
+static void tul_do_pause(unsigned amount)
+{ /* Pause for amount*10 milliseconds */
+ unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ /*
+ * We need to release the io_request_lock
+ * to make sure that the jiffies are updated
+ */
+ spin_unlock_irq(&io_request_lock);
+
+ while (time_before_eq(jiffies, the_time));
+
+ /*
+ * Acquire the io_request_lock again
+ */
+ spin_lock_irq(&io_request_lock);
+#else
+ while (jiffies < the_time);
+#endif
+}
+
+/*-- forward reference --*/
+
+/*******************************************************************
+ Use memeory refresh time ~ 15us * 2
+********************************************************************/
+void tul_se2_wait()
+{
+#if 1
+ udelay(30);
+#else
+ UCHAR readByte;
+
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10) {
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10)
+ break;
+ }
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) != 0x10)
+ break;
+ }
+ } else {
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10)
+ break;
+ }
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) != 0x10)
+ break;
+ }
+ }
+#endif
+}
+
+
+/******************************************************************
+ Input: instruction for Serial E2PROM
+
+ EX: se2_rd(0 call se2_instr() to send address and read command
+
+ StartBit OP_Code Address Data
+ --------- -------- ------------------ -------
+ 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0
+
+ +-----------------------------------------------------
+ |
+ CS -----+
+ +--+ +--+ +--+ +--+ +--+
+ ^ | ^ | ^ | ^ | ^ |
+ | | | | | | | | | |
+ CLK -------+ +--+ +--+ +--+ +--+ +--
+ (leading edge trigger)
+
+ +--1-----1--+
+ | SB OP | OP A5 A4
+ DI ----+ +--0------------------
+ (address and cmd sent to nvram)
+
+ -------------------------------------------+
+ |
+ DO +---
+ (data sent from nvram)
+
+
+******************************************************************/
+void tul_se2_instr(WORD CurBase, UCHAR instr)
+{
+ int i;
+ UCHAR b;
+
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* cs+start bit */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK | SE2DO); /* +CLK */
+ tul_se2_wait();
+
+ for (i = 0; i < 8; i++) {
+ if (instr & 0x80)
+ b = SE2CS | SE2DO; /* -CLK+dataBit */
+ else
+ b = SE2CS; /* -CLK */
+ TUL_WR(CurBase + TUL_NVRAM, b);
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, b | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ instr <<= 1;
+ }
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ return;
+}
+
+
+/******************************************************************
+ Function name : tul_se2_ew_en
+ Description : Enable erase/write state of serial EEPROM
+******************************************************************/
+void tul_se2_ew_en(WORD CurBase)
+{
+ tul_se2_instr(CurBase, 0x30); /* EWEN */
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+ return;
+}
+
+
+/************************************************************************
+ Disable erase/write state of serial EEPROM
+*************************************************************************/
+void tul_se2_ew_ds(WORD CurBase)
+{
+ tul_se2_instr(CurBase, 0); /* EWDS */
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+ return;
+}
+
+
+/******************************************************************
+ Input :address of Serial E2PROM
+ Output :value stored in Serial E2PROM
+*******************************************************************/
+USHORT tul_se2_rd(WORD CurBase, ULONG adr)
+{
+ UCHAR instr, readByte;
+ USHORT readWord;
+ int i;
+
+ instr = (UCHAR) (adr | 0x80);
+ tul_se2_instr(CurBase, instr); /* READ INSTR */
+ readWord = 0;
+
+ for (i = 15; i >= 0; i--) {
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+
+ /* sample data after the following edge of clock */
+ readByte = TUL_RD(CurBase, TUL_NVRAM);
+ readByte &= SE2DI;
+ readWord += (readByte << i);
+ tul_se2_wait(); /* 6/20/95 */
+ }
+
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* no chip select */
+ tul_se2_wait();
+ return readWord;
+}
+
+
+/******************************************************************
+ Input: new value in Serial E2PROM, address of Serial E2PROM
+*******************************************************************/
+void tul_se2_wr(WORD CurBase, UCHAR adr, USHORT writeWord)
+{
+ UCHAR readByte;
+ UCHAR instr;
+ int i;
+
+ instr = (UCHAR) (adr | 0x40);
+ tul_se2_instr(CurBase, instr); /* WRITE INSTR */
+ for (i = 15; i >= 0; i--) {
+ if (writeWord & 0x8000)
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* -CLK+dataBit 1 */
+ else
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK+dataBit 0 */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ writeWord <<= 1;
+ }
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* +CS */
+ tul_se2_wait();
+
+ for (;;) {
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ if ((readByte = TUL_RD(CurBase, TUL_NVRAM)) & SE2DI)
+ break; /* write complete */
+ }
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+int tul_se2_rd_all(WORD CurBase)
+{
+ int i;
+ ULONG chksum = 0;
+ USHORT *np;
+
+ i91unvramp = &i91unvram;
+ np = (USHORT *) i91unvramp;
+ for (i = 0; i < 32; i++) {
+ *np++ = tul_se2_rd(CurBase, i);
+ }
+
+/*--------------------Is signature "ini" ok ? ----------------*/
+ if (i91unvramp->NVM_Signature != INI_SIGNATURE)
+ return -1;
+/*---------------------- Is ckecksum ok ? ----------------------*/
+ np = (USHORT *) i91unvramp;
+ for (i = 0; i < 31; i++)
+ chksum += *np++;
+ if (i91unvramp->NVM_CheckSum != (USHORT) chksum)
+ return -1;
+ return 1;
+}
+
+
+/***********************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+void tul_se2_update_all(WORD CurBase)
+{ /* setup default pattern */
+ int i;
+ ULONG chksum = 0;
+ USHORT *np, *np1;
+
+ i91unvramp = &i91unvram;
+ /* Calculate checksum first */
+ np = (USHORT *) i91udftNvRam;
+ for (i = 0; i < 31; i++)
+ chksum += *np++;
+ *np = (USHORT) chksum;
+ tul_se2_ew_en(CurBase); /* Enable write */
+
+ np = (USHORT *) i91udftNvRam;
+ np1 = (USHORT *) i91unvramp;
+ for (i = 0; i < 32; i++, np++, np1++) {
+ if (*np != *np1) {
+ tul_se2_wr(CurBase, i, *np);
+ }
+ }
+
+ tul_se2_ew_ds(CurBase); /* Disable write */
+ return;
+}
+
+/*************************************************************************
+ Function name : read_eeprom
+**************************************************************************/
+void tul_read_eeprom(WORD CurBase)
+{
+ UCHAR gctrl;
+
+ i91unvramp = &i91unvram;
+/*------Enable EEProm programming ---*/
+ gctrl = TUL_RD(CurBase, TUL_GCTRL);
+ TUL_WR(CurBase + TUL_GCTRL, gctrl | TUL_GCTRL_EEPROM_BIT);
+ if (tul_se2_rd_all(CurBase) != 1) {
+ tul_se2_update_all(CurBase); /* setup default pattern */
+ tul_se2_rd_all(CurBase); /* load again */
+ }
+/*------ Disable EEProm programming ---*/
+ gctrl = TUL_RD(CurBase, TUL_GCTRL);
+ TUL_WR(CurBase + TUL_GCTRL, gctrl & ~TUL_GCTRL_EEPROM_BIT);
+} /* read_eeprom */
+
+int Addi91u_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt,
+ BYTE bBus, BYTE bDevice)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {
+ if (i91u_adpt[i].ADPT_BIOS < wBIOS)
+ continue;
+ if (i91u_adpt[i].ADPT_BIOS == wBIOS) {
+ if (i91u_adpt[i].ADPT_BASE == wBASE)
+ if (i91u_adpt[i].ADPT_Bus != 0xFF)
+ return (FAILURE);
+ else if (i91u_adpt[i].ADPT_BASE < wBASE)
+ continue;
+ }
+ for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
+ i91u_adpt[j].ADPT_BASE = i91u_adpt[j - 1].ADPT_BASE;
+ i91u_adpt[j].ADPT_INTR = i91u_adpt[j - 1].ADPT_INTR;
+ i91u_adpt[j].ADPT_BIOS = i91u_adpt[j - 1].ADPT_BIOS;
+ i91u_adpt[j].ADPT_Bus = i91u_adpt[j - 1].ADPT_Bus;
+ i91u_adpt[j].ADPT_Device = i91u_adpt[j - 1].ADPT_Device;
+ }
+ i91u_adpt[i].ADPT_BASE = wBASE;
+ i91u_adpt[i].ADPT_INTR = bInterrupt;
+ i91u_adpt[i].ADPT_BIOS = wBIOS;
+ i91u_adpt[i].ADPT_Bus = bBus;
+ i91u_adpt[i].ADPT_Device = bDevice;
+ return (SUCCESSFUL);
+ }
+ return (FAILURE);
+}
+
+void init_i91uAdapter_table(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) { /* Initialize adapter structure */
+ i91u_adpt[i].ADPT_BIOS = 0xffff;
+ i91u_adpt[i].ADPT_BASE = 0xffff;
+ i91u_adpt[i].ADPT_INTR = 0xff;
+ i91u_adpt[i].ADPT_Bus = 0xff;
+ i91u_adpt[i].ADPT_Device = 0xff;
+ }
+ return;
+}
+
+void tul_stop_bm(HCS * pCurHcb)
+{
+
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+ /* wait Abort DMA xfer done */
+ while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+}
+
+/***************************************************************************/
+void get_tulipPCIConfig(HCS * pCurHcb, int ch_idx)
+{
+ pCurHcb->HCS_Base = i91u_adpt[ch_idx].ADPT_BASE; /* Supply base address */
+ pCurHcb->HCS_BIOS = i91u_adpt[ch_idx].ADPT_BIOS; /* Supply BIOS address */
+ pCurHcb->HCS_Intr = i91u_adpt[ch_idx].ADPT_INTR; /* Supply interrupt line */
+ return;
+}
+
+/***************************************************************************/
+int tul_reset_scsi(HCS * pCurHcb, int seconds)
+{
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_BUS);
+
+ while (!((pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt)) & TSS_SCSIRST_INT));
+ /* reset tulip chip */
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, 0);
+
+ /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */
+ /* SONY 5200 tape drive won't work if only stall for 1 sec */
+ tul_do_pause(seconds * 100);
+
+ TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+ return (SCSI_RESET_SUCCESS);
+}
+
+/***************************************************************************/
+int init_tulip(HCS * pCurHcb, SCB * scbp, int tul_num_scb, BYTE * pbBiosAdr, int seconds)
+{
+ int i;
+ WORD *pwFlags;
+ BYTE *pbHeads;
+ SCB *pTmpScb, *pPrevScb = NULL;
+
+ pCurHcb->HCS_NumScbs = tul_num_scb;
+ pCurHcb->HCS_Semaph = 1;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ pCurHcb->HCS_SemaphLock = SPIN_LOCK_UNLOCKED;
+#endif
+ pCurHcb->HCS_JSStatus0 = 0;
+ pCurHcb->HCS_Scb = scbp;
+ pCurHcb->HCS_NxtPend = scbp;
+ pCurHcb->HCS_NxtAvail = scbp;
+ for (i = 0, pTmpScb = scbp; i < tul_num_scb; i++, pTmpScb++) {
+ pTmpScb->SCB_TagId = i;
+ if (i != 0)
+ pPrevScb->SCB_NxtScb = pTmpScb;
+ pPrevScb = pTmpScb;
+ }
+ pPrevScb->SCB_NxtScb = NULL;
+ pCurHcb->HCS_ScbEnd = pTmpScb;
+ pCurHcb->HCS_FirstAvail = scbp;
+ pCurHcb->HCS_LastAvail = pPrevScb;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ pCurHcb->HCS_AvailLock = SPIN_LOCK_UNLOCKED;
+#endif
+ pCurHcb->HCS_FirstPend = NULL;
+ pCurHcb->HCS_LastPend = NULL;
+ pCurHcb->HCS_FirstBusy = NULL;
+ pCurHcb->HCS_LastBusy = NULL;
+ pCurHcb->HCS_FirstDone = NULL;
+ pCurHcb->HCS_LastDone = NULL;
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+
+ tul_read_eeprom(pCurHcb->HCS_Base);
+/*---------- get H/A configuration -------------*/
+ if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)
+ pCurHcb->HCS_MaxTar = 8;
+ else
+ pCurHcb->HCS_MaxTar = 16;
+
+ pCurHcb->HCS_Config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;
+
+ pCurHcb->HCS_SCSI_ID = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;
+ pCurHcb->HCS_IdMask = ~(1 << pCurHcb->HCS_SCSI_ID);
+
+#if CHK_PARITY
+ /* Enable parity error response */
+ TUL_WR(pCurHcb->HCS_Base + TUL_PCMD, TUL_RD(pCurHcb->HCS_Base, TUL_PCMD) | 0x40);
+#endif
+
+ /* Mask all the interrupt */
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+
+ tul_stop_bm(pCurHcb);
+ /* --- Initialize the tulip --- */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_CHIP);
+
+ /* program HBA's SCSI ID */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId, pCurHcb->HCS_SCSI_ID << 4);
+
+ /* Enable Initiator Mode ,phase latch,alternate sync period mode,
+ disable SCSI reset */
+ if (pCurHcb->HCS_Config & HCC_EN_PAR)
+ pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);
+ else
+ pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_SConf1);
+
+ /* Enable HW reselect */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, 0);
+
+ /* selection time out = 250 ms */
+ TUL_WR(pCurHcb->HCS_Base + TUL_STimeOut, 153);
+
+/*--------- Enable SCSI terminator -----*/
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, (pCurHcb->HCS_Config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)));
+ TUL_WR(pCurHcb->HCS_Base + TUL_GCTRL1,
+ ((pCurHcb->HCS_Config & HCC_AUTO_TERM) >> 4) | (TUL_RD(pCurHcb->HCS_Base, TUL_GCTRL1) & 0xFE));
+
+ for (i = 0,
+ pwFlags = (WORD *) & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),
+ pbHeads = pbBiosAdr + 0x180;
+ i < pCurHcb->HCS_MaxTar;
+ i++, pwFlags++) {
+ pCurHcb->HCS_Tcs[i].TCS_Flags = *pwFlags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ if (pCurHcb->HCS_Tcs[i].TCS_Flags & TCF_EN_255)
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+ else
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+ pCurHcb->HCS_Tcs[i].TCS_JS_Period = 0;
+ pCurHcb->HCS_Tcs[i].TCS_SConfig0 = pCurHcb->HCS_SConf1;
+ pCurHcb->HCS_Tcs[i].TCS_DrvHead = *pbHeads++;
+ if (pCurHcb->HCS_Tcs[i].TCS_DrvHead == 255)
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+ else
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+ pCurHcb->HCS_Tcs[i].TCS_DrvSector = *pbHeads++;
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY;
+ pCurHcb->HCS_ActTags[i] = 0;
+ pCurHcb->HCS_MaxTags[i] = 0xFF;
+ } /* for */
+ printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",
+ pCurHcb->HCS_Base, pCurHcb->HCS_Intr,
+ pCurHcb->HCS_BIOS, pCurHcb->HCS_SCSI_ID);
+/*------------------- reset SCSI Bus ---------------------------*/
+ if (pCurHcb->HCS_Config & HCC_SCSI_RESET) {
+ printk("i91u: Reset SCSI Bus ... \n");
+ tul_reset_scsi(pCurHcb, seconds);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCFG1, 0x17);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SIntEnable, 0xE9);
+ return (0);
+}
+
+/***************************************************************************/
+SCB *tul_alloc_scb(HCS * hcsp)
+{
+ SCB *pTmpScb;
+ ULONG flags;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+ if ((pTmpScb = hcsp->HCS_FirstAvail) != NULL) {
+#if DEBUG_QUEUE
+ printk("find scb at %08lx\n", (ULONG) pTmpScb);
+#endif
+ if ((hcsp->HCS_FirstAvail = pTmpScb->SCB_NxtScb) == NULL)
+ hcsp->HCS_LastAvail = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ pTmpScb->SCB_Status = SCB_RENT;
+ }
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_release_scb(HCS * hcsp, SCB * scbp)
+{
+ ULONG flags;
+
+#if DEBUG_QUEUE
+ printk("Release SCB %lx; ", (ULONG) scbp);
+#endif
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+ scbp->SCB_Srb = 0;
+ scbp->SCB_Status = 0;
+ scbp->SCB_NxtScb = NULL;
+ if (hcsp->HCS_LastAvail != NULL) {
+ hcsp->HCS_LastAvail->SCB_NxtScb = scbp;
+ hcsp->HCS_LastAvail = scbp;
+ } else {
+ hcsp->HCS_FirstAvail = scbp;
+ hcsp->HCS_LastAvail = scbp;
+ }
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+#else
+ restore_flags(flags);
+#endif
+}
+
+/***************************************************************************/
+void tul_append_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("Append pend SCB %lx; ", (ULONG) scbp);
+#endif
+ scbp->SCB_Status = SCB_PEND;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastPend != NULL) {
+ pCurHcb->HCS_LastPend->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ } else {
+ pCurHcb->HCS_FirstPend = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ }
+}
+
+/***************************************************************************/
+void tul_push_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("Push pend SCB %lx; ", (ULONG) scbp);
+#endif
+ scbp->SCB_Status = SCB_PEND;
+ if ((scbp->SCB_NxtScb = pCurHcb->HCS_FirstPend) != NULL) {
+ pCurHcb->HCS_FirstPend = scbp;
+ } else {
+ pCurHcb->HCS_FirstPend = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_find_first_pend_scb(HCS * pCurHcb)
+{
+ SCB *pFirstPend;
+
+
+ pFirstPend = pCurHcb->HCS_FirstPend;
+ while (pFirstPend != NULL) {
+ if (pFirstPend->SCB_Opcode != ExecSCSI) {
+ return (pFirstPend);
+ }
+ if (pFirstPend->SCB_TagMsg == 0) {
+ if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] == 0) &&
+ !(pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+ return (pFirstPend);
+ }
+ } else {
+ if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] >=
+ pCurHcb->HCS_MaxTags[pFirstPend->SCB_Target]) |
+ (pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+ pFirstPend = pFirstPend->SCB_NxtScb;
+ continue;
+ }
+ return (pFirstPend);
+ }
+ pFirstPend = pFirstPend->SCB_NxtScb;
+ }
+
+
+ return (pFirstPend);
+}
+/***************************************************************************/
+SCB *tul_pop_pend_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+ if ((pTmpScb = pCurHcb->HCS_FirstPend) != NULL) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ }
+#if DEBUG_QUEUE
+ printk("Pop pend SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+
+/***************************************************************************/
+void tul_unlink_pend_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+ printk("unlink pend SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend;
+ while (pTmpScb != NULL) {
+ if (pCurScb == pTmpScb) { /* Unlink this SCB */
+ if (pTmpScb == pCurHcb->HCS_FirstPend) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastPend)
+ pCurHcb->HCS_LastPend = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+ return;
+}
+/***************************************************************************/
+void tul_append_busy_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("append busy SCB %lx; ", (ULONG) scbp);
+#endif
+ if (scbp->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[scbp->SCB_Target]++;
+ else
+ pCurHcb->HCS_Tcs[scbp->SCB_Target].TCS_Flags |= TCF_BUSY;
+ scbp->SCB_Status = SCB_BUSY;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastBusy != NULL) {
+ pCurHcb->HCS_LastBusy->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastBusy = scbp;
+ } else {
+ pCurHcb->HCS_FirstBusy = scbp;
+ pCurHcb->HCS_LastBusy = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_pop_busy_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+
+ if ((pTmpScb = pCurHcb->HCS_FirstBusy) != NULL) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ if (pTmpScb->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ else
+ pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+ }
+#if DEBUG_QUEUE
+ printk("Pop busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_unlink_busy_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+ printk("unlink busy SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+ while (pTmpScb != NULL) {
+ if (pCurScb == pTmpScb) { /* Unlink this SCB */
+ if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastBusy)
+ pCurHcb->HCS_LastBusy = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+ if (pTmpScb->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ else
+ pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+ return;
+}
+
+/***************************************************************************/
+SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun)
+{
+ SCB *pTmpScb, *pPrevScb;
+ WORD scbp_tarlun;
+
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+ while (pTmpScb != NULL) {
+ scbp_tarlun = (pTmpScb->SCB_Lun << 8) | (pTmpScb->SCB_Target);
+ if (scbp_tarlun == tarlun) { /* Unlink this SCB */
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+#if DEBUG_QUEUE
+ printk("find busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_append_done_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("append done SCB %lx; ", (ULONG) scbp);
+#endif
+
+ scbp->SCB_Status = SCB_DONE;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastDone != NULL) {
+ pCurHcb->HCS_LastDone->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastDone = scbp;
+ } else {
+ pCurHcb->HCS_FirstDone = scbp;
+ pCurHcb->HCS_LastDone = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_find_done_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+
+ if ((pTmpScb = pCurHcb->HCS_FirstDone) != NULL) {
+ if ((pCurHcb->HCS_FirstDone = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastDone = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ }
+#if DEBUG_QUEUE
+ printk("find done SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+int tul_abort_srb(HCS * pCurHcb, ULONG srbp)
+{
+ ULONG flags;
+ SCB *pTmpScb, *pPrevScb;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+
+ if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+ tulip_main(pCurHcb);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+
+ return SCSI_ABORT_SNOOZE;
+ }
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend; /* Check Pend queue */
+ while (pTmpScb != NULL) {
+ /* 07/27/98 */
+ if (pTmpScb->SCB_Srb == (unsigned char *) srbp) {
+ if (pTmpScb == pCurHcb->HCS_ActScb) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_ABORT_BUSY;
+ } else if (pTmpScb == pCurHcb->HCS_FirstPend) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastPend)
+ pCurHcb->HCS_LastPend = pPrevScb;
+ }
+ pTmpScb->SCB_HaStat = HOST_ABORTED;
+ pTmpScb->SCB_Flags |= SCF_DONE;
+ if (pTmpScb->SCB_Flags & SCF_POST)
+ (*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_ABORT_SUCCESS;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */
+ while (pTmpScb != NULL) {
+
+ if (pTmpScb->SCB_Srb == (unsigned char *) srbp) {
+
+ if (pTmpScb == pCurHcb->HCS_ActScb) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_ABORT_BUSY;
+ } else if (pTmpScb->SCB_TagMsg == 0) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_ABORT_BUSY;
+ } else {
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastBusy)
+ pCurHcb->HCS_LastBusy = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+
+
+ pTmpScb->SCB_HaStat = HOST_ABORTED;
+ pTmpScb->SCB_Flags |= SCF_DONE;
+ if (pTmpScb->SCB_Flags & SCF_POST)
+ (*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_ABORT_SUCCESS;
+ }
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return (SCSI_ABORT_NOT_RUNNING);
+}
+
+/***************************************************************************/
+int tul_bad_seq(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+
+ printk("tul_bad_seg c=%d\n", pCurHcb->HCS_Index);
+
+ if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+ tul_unlink_busy_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ pCurScb->SCB_TaStat = 0;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ tul_stop_bm(pCurHcb);
+
+ tul_reset_scsi(pCurHcb, 8); /* 7/29/98 */
+
+ return (tul_post_scsi_rst(pCurHcb));
+}
+
+/************************************************************************/
+int tul_device_reset(HCS * pCurHcb, ULONG pSrb, unsigned int target, unsigned int ResetFlags)
+{
+ ULONG flags;
+ SCB *pScb;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+
+ if (ResetFlags & SCSI_RESET_ASYNCHRONOUS) {
+
+ if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+ tulip_main(pCurHcb);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+
+ return SCSI_RESET_SNOOZE;
+ }
+ pScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */
+ while (pScb != NULL) {
+ if (pScb->SCB_Srb == (unsigned char *) pSrb)
+ break;
+ pScb = pScb->SCB_NxtScb;
+ }
+ if (pScb == NULL) {
+ printk("Unable to Reset - No SCB Found\n");
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ }
+ if ((pScb = tul_alloc_scb(pCurHcb)) == NULL) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ pScb->SCB_Opcode = BusDevRst;
+ pScb->SCB_Flags = SCF_POST;
+ pScb->SCB_Target = target;
+ pScb->SCB_Mode = 0;
+
+ pScb->SCB_Srb = 0;
+ if (ResetFlags & SCSI_RESET_SYNCHRONOUS) {
+ pScb->SCB_Srb = (unsigned char *) pSrb;
+ }
+ tul_push_pend_scb(pCurHcb, pScb); /* push this SCB to Pending queue */
+
+ if (pCurHcb->HCS_Semaph == 1) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+ pCurHcb->HCS_Semaph = 0;
+
+ tulip_main(pCurHcb);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+ }
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return SCSI_RESET_PENDING;
+}
+
+int tul_reset_scsi_bus(HCS * pCurHcb)
+{
+ ULONG flags;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ pCurHcb->HCS_Semaph = 0;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+
+ tul_stop_bm(pCurHcb);
+
+ tul_reset_scsi(pCurHcb, 2); /* 7/29/98 */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+ tul_post_scsi_rst(pCurHcb);
+
+ tulip_main(pCurHcb);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return (SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET);
+}
+
+/************************************************************************/
+void tul_exec_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ ULONG flags;
+
+ pCurScb->SCB_Mode = 0;
+
+ pCurScb->SCB_SGIdx = 0;
+ pCurScb->SCB_SGMax = pCurScb->SCB_SGLen;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+
+ tul_append_pend_scb(pCurHcb, pCurScb); /* Append this SCB to Pending queue */
+
+/* VVVVV 07/21/98 */
+ if (pCurHcb->HCS_Semaph == 1) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+ pCurHcb->HCS_Semaph = 0;
+
+ tulip_main(pCurHcb);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+ }
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return;
+}
+
+/***************************************************************************/
+int tul_isr(HCS * pCurHcb)
+{
+ /* Enter critical section */
+
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_Int) & TSS_INT_PENDING) {
+ if (pCurHcb->HCS_Semaph == 1) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* Disable Tulip SCSI Int */
+ pCurHcb->HCS_Semaph = 0;
+
+ tulip_main(pCurHcb);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/***************************************************************************/
+int tulip_main(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+
+ for (;;) {
+
+ tulip_scsi(pCurHcb); /* Call tulip_scsi */
+
+ while ((pCurScb = tul_find_done_scb(pCurHcb)) != NULL) { /* find done entry */
+ if (pCurScb->SCB_TaStat == QUEUE_FULL) {
+ pCurHcb->HCS_MaxTags[pCurScb->SCB_Target] =
+ pCurHcb->HCS_ActTags[pCurScb->SCB_Target] - 1;
+ pCurScb->SCB_TaStat = 0;
+ tul_append_pend_scb(pCurHcb, pCurScb);
+ continue;
+ }
+ if (!(pCurScb->SCB_Mode & SCM_RSENS)) { /* not in auto req. sense mode */
+ if (pCurScb->SCB_TaStat == 2) {
+
+ /* clr sync. nego flag */
+
+ if (pCurScb->SCB_Flags & SCF_SENSE) {
+ BYTE len;
+ len = pCurScb->SCB_SenseLen;
+ if (len == 0)
+ len = 1;
+ pCurScb->SCB_BufLen = pCurScb->SCB_SenseLen;
+ pCurScb->SCB_BufPtr = pCurScb->SCB_SensePtr;
+ pCurScb->SCB_Flags &= ~(SCF_SG | SCF_DIR); /* for xfer_data_in */
+/* pCurScb->SCB_Flags |= SCF_NO_DCHK; */
+ /* so, we won't report worng direction in xfer_data_in,
+ and won't report HOST_DO_DU in state_6 */
+ pCurScb->SCB_Mode = SCM_RSENS;
+ pCurScb->SCB_Ident &= 0xBF; /* Disable Disconnect */
+ pCurScb->SCB_TagMsg = 0;
+ pCurScb->SCB_TaStat = 0;
+ pCurScb->SCB_CDBLen = 6;
+ pCurScb->SCB_CDB[0] = SCSICMD_RequestSense;
+ pCurScb->SCB_CDB[1] = 0;
+ pCurScb->SCB_CDB[2] = 0;
+ pCurScb->SCB_CDB[3] = 0;
+ pCurScb->SCB_CDB[4] = len;
+ pCurScb->SCB_CDB[5] = 0;
+ tul_push_pend_scb(pCurHcb, pCurScb);
+ break;
+ }
+ }
+ } else { /* in request sense mode */
+
+ if (pCurScb->SCB_TaStat == 2) { /* check contition status again after sending
+ requset sense cmd 0x3 */
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ }
+ pCurScb->SCB_TaStat = 2;
+ }
+ pCurScb->SCB_Flags |= SCF_DONE;
+ if (pCurScb->SCB_Flags & SCF_POST) {
+ (*pCurScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pCurScb);
+ }
+ } /* while */
+
+ /* find_active: */
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0) & TSS_INT_PENDING)
+ continue;
+
+ if (pCurHcb->HCS_ActScb) { /* return to OS and wait for xfer_done_ISR/Selected_ISR */
+ return 1; /* return to OS, enable interrupt */
+ }
+ /* Check pending SCB */
+ if (tul_find_first_pend_scb(pCurHcb) == NULL) {
+ return 1; /* return to OS, enable interrupt */
+ }
+ } /* End of for loop */
+ /* statement won't reach here */
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/***************************************************************************/
+void tulip_scsi(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ TCS *pCurTcb;
+
+ /* make sure to service interrupt asap */
+
+ if ((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0)) & TSS_INT_PENDING) {
+
+ pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+ pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* SCSI bus reset detected */
+ int_tul_scsi_rst(pCurHcb);
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) { /* if selected/reselected interrupt */
+ if (int_tul_resel(pCurHcb) == 0)
+ tul_next_state(pCurHcb);
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) {
+ int_tul_busfree(pCurHcb);
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ int_tul_busfree(pCurHcb); /* unexpected bus free or sel timeout */
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) { /* func complete or Bus service */
+ if ((pCurScb = pCurHcb->HCS_ActScb) != NULL)
+ tul_next_state(pCurHcb);
+ return;
+ }
+ }
+ if (pCurHcb->HCS_ActScb != NULL)
+ return;
+
+ if ((pCurScb = tul_find_first_pend_scb(pCurHcb)) == NULL)
+ return;
+
+ /* program HBA's SCSI ID & target SCSI ID */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId,
+ (pCurHcb->HCS_SCSI_ID << 4) | (pCurScb->SCB_Target & 0x0F));
+ if (pCurScb->SCB_Opcode == ExecSCSI) {
+ pCurTcb = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+
+ if (pCurScb->SCB_TagMsg)
+ pCurTcb->TCS_DrvFlags |= TCF_DRV_EN_TAG;
+ else
+ pCurTcb->TCS_DrvFlags &= ~TCF_DRV_EN_TAG;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+ if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) { /* do wdtr negotiation */
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ } else {
+ if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { /* do sync negotiation */
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ } else {
+ if (pCurScb->SCB_TagMsg)
+ tul_select_atn3(pCurHcb, pCurScb);
+ else
+ tul_select_atn(pCurHcb, pCurScb);
+ }
+ }
+ if (pCurScb->SCB_Flags & SCF_POLL) {
+ while (wait_tulip(pCurHcb) != -1) {
+ if (tul_next_state(pCurHcb) == -1)
+ break;
+ }
+ }
+ } else if (pCurScb->SCB_Opcode == BusDevRst) {
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ pCurScb->SCB_NxtStat = 8;
+ if (pCurScb->SCB_Flags & SCF_POLL) {
+ while (wait_tulip(pCurHcb) != -1) {
+ if (tul_next_state(pCurHcb) == -1)
+ break;
+ }
+ }
+ } else if (pCurScb->SCB_Opcode == AbortCmd) {
+ ULONG srbp;
+
+ srbp = (ULONG) pCurScb->SCB_Srb;
+/* 08/03/98 */
+ if (tul_abort_srb(pCurHcb, srbp) != 0) {
+
+
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+
+ tul_release_scb(pCurHcb, pCurScb);
+ } else {
+ pCurScb->SCB_Opcode = BusDevRst;
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ pCurScb->SCB_NxtStat = 8;
+ }
+
+/* 08/03/98 */
+ } else {
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = 0x16; /* bad command */
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ return;
+}
+
+
+/***************************************************************************/
+int tul_next_state(HCS * pCurHcb)
+{
+ int next;
+
+ next = pCurHcb->HCS_ActScb->SCB_NxtStat;
+ for (;;) {
+ switch (next) {
+ case 1:
+ next = tul_state_1(pCurHcb);
+ break;
+ case 2:
+ next = tul_state_2(pCurHcb);
+ break;
+ case 3:
+ next = tul_state_3(pCurHcb);
+ break;
+ case 4:
+ next = tul_state_4(pCurHcb);
+ break;
+ case 5:
+ next = tul_state_5(pCurHcb);
+ break;
+ case 6:
+ next = tul_state_6(pCurHcb);
+ break;
+ case 7:
+ next = tul_state_7(pCurHcb);
+ break;
+ case 8:
+ return (tul_bus_device_reset(pCurHcb));
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ if (next <= 0)
+ return next;
+ }
+}
+
+
+/***************************************************************************/
+/* sTate after selection with attention & stop */
+int tul_state_1(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+ printk("-s1-");
+#endif
+
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ tul_append_busy_scb(pCurHcb, pCurScb);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+ /* ATN on */
+ if (pCurHcb->HCS_Phase == MSG_OUT) {
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, (TSC_EN_BUS_IN | TSC_HW_RESELECT));
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+
+ if (pCurScb->SCB_TagMsg) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+ }
+ if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {
+
+ pCurTcb->TCS_Flags |= TCF_WDTR_DONE;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2); /* Extended msg length */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3); /* Sync request */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1); /* Start from 16 bits */
+ } else if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {
+
+ pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3); /* extended msg length */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1); /* sync request */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET); /* REQ/ACK offset */
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+ return (3);
+}
+
+
+/***************************************************************************/
+/* state after selection with attention */
+/* state after selection with attention3 */
+int tul_state_2(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+ printk("-s2-");
+#endif
+
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ tul_append_busy_scb(pCurHcb, pCurScb);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+
+ if (pCurHcb->HCS_JSStatus1 & TSS_CMD_PH_CMP) {
+ return (4);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+ return (3);
+}
+
+/***************************************************************************/
+/* state before CDB xfer is done */
+int tul_state_3(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+ int i;
+
+#if DEBUG_STATE
+ printk("-s3-");
+#endif
+ for (;;) {
+ switch (pCurHcb->HCS_Phase) {
+ case CMD_OUT: /* Command out phase */
+ for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase == CMD_OUT) {
+ return (tul_bad_seq(pCurHcb));
+ }
+ return (4);
+
+ case MSG_IN: /* Message in phase */
+ pCurScb->SCB_NxtStat = 3;
+ if (tul_msgin(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case STATUS_IN: /* Status phase */
+ if (tul_status_msg(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case MSG_OUT: /* Message out phase */
+ if (pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) {
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP); /* msg nop */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+
+ } else {
+ pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3); /* ext. msg len */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1); /* sync request */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET); /* REQ/ACK offset */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7));
+
+ }
+ break;
+
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ }
+}
+
+
+/***************************************************************************/
+int tul_state_4(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+ printk("-s4-");
+#endif
+ if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_NO_XF) {
+ return (6); /* Go to state 6 */
+ }
+ for (;;) {
+ if (pCurScb->SCB_BufLen == 0)
+ return (6); /* Go to state 6 */
+
+ switch (pCurHcb->HCS_Phase) {
+
+ case STATUS_IN: /* Status phase */
+ if ((pCurScb->SCB_Flags & SCF_DIR) != 0) { /* if direction bit set then report data underrun */
+ pCurScb->SCB_HaStat = HOST_DO_DU;
+ }
+ if ((tul_status_msg(pCurHcb)) == -1)
+ return (-1);
+ break;
+
+ case MSG_IN: /* Message in phase */
+ pCurScb->SCB_NxtStat = 0x4;
+ if (tul_msgin(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case MSG_OUT: /* Message out phase */
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+ pCurScb->SCB_BufLen = 0;
+ pCurScb->SCB_HaStat = HOST_DO_DU;
+ if (tul_msgout_ide(pCurHcb) == -1)
+ return (-1);
+ return (6); /* Go to state 6 */
+ } else {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP); /* msg nop */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ }
+ break;
+
+ case DATA_IN: /* Data in phase */
+ return (tul_xfer_data_in(pCurHcb));
+
+ case DATA_OUT: /* Data out phase */
+ return (tul_xfer_data_out(pCurHcb));
+
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ }
+}
+
+
+/***************************************************************************/
+/* state after dma xfer done or phase change before xfer done */
+int tul_state_5(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ long cnt, xcnt; /* cannot use unsigned !! code: if (xcnt < 0) */
+
+#if DEBUG_STATE
+ printk("-s5-");
+#endif
+/*------ get remaining count -------*/
+
+ cnt = TUL_RDLONG(pCurHcb->HCS_Base, TUL_SCnt0) & 0x0FFFFFF;
+
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XCmd) & 0x20) {
+ /* ----------------------- DATA_IN ----------------------------- */
+ /* check scsi parity error */
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+ pCurScb->SCB_HaStat = HOST_DO_DU;
+ }
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) { /* DMA xfer pending, Send STOP */
+ /* tell Hardware scsi xfer has been terminated */
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, TUL_RD(pCurHcb->HCS_Base, TUL_XCtrl) | 0x80);
+ /* wait until DMA xfer not pending */
+ while (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND);
+ }
+ } else {
+/*-------- DATA OUT -----------*/
+ if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0) {
+ if (pCurHcb->HCS_ActTcs->TCS_JS_Period & TSC_WIDE_SCSI)
+ cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F) << 1;
+ else
+ cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F);
+ }
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT);
+ /* wait Abort DMA xfer done */
+ while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+ }
+ if ((cnt == 1) && (pCurHcb->HCS_Phase == DATA_OUT)) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1) {
+ return (-1);
+ }
+ cnt = 0;
+ } else {
+ if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ }
+ }
+
+ if (cnt == 0) {
+ pCurScb->SCB_BufLen = 0;
+ return (6); /* Go to state 6 */
+ }
+ /* Update active data pointer */
+ xcnt = (long) pCurScb->SCB_BufLen - cnt; /* xcnt== bytes already xferred */
+ pCurScb->SCB_BufLen = (U32) cnt; /* cnt == bytes left to be xferred */
+ if (pCurScb->SCB_Flags & SCF_SG) {
+ register SG *sgp;
+ ULONG i;
+
+ sgp = &pCurScb->SCB_SGList[pCurScb->SCB_SGIdx];
+ for (i = pCurScb->SCB_SGIdx; i < pCurScb->SCB_SGMax; sgp++, i++) {
+ xcnt -= (long) sgp->SG_Len;
+ if (xcnt < 0) { /* this sgp xfer half done */
+ xcnt += (long) sgp->SG_Len; /* xcnt == bytes xferred in this sgp */
+ sgp->SG_Ptr += (U32) xcnt; /* new ptr to be xfer */
+ sgp->SG_Len -= (U32) xcnt; /* new len to be xfer */
+ pCurScb->SCB_BufPtr += ((U32) (i - pCurScb->SCB_SGIdx) << 3);
+ /* new SG table ptr */
+ pCurScb->SCB_SGLen = (BYTE) (pCurScb->SCB_SGMax - i);
+ /* new SG table len */
+ pCurScb->SCB_SGIdx = (WORD) i;
+ /* for next disc and come in this loop */
+ return (4); /* Go to state 4 */
+ }
+ /* else (xcnt >= 0 , i.e. this sgp already xferred */
+ } /* for */
+ return (6); /* Go to state 6 */
+ } else {
+ pCurScb->SCB_BufPtr += (U32) xcnt;
+ }
+ return (4); /* Go to state 4 */
+}
+
+/***************************************************************************/
+/* state after Data phase */
+int tul_state_6(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+ printk("-s6-");
+#endif
+ for (;;) {
+ switch (pCurHcb->HCS_Phase) {
+ case STATUS_IN: /* Status phase */
+ if ((tul_status_msg(pCurHcb)) == -1)
+ return (-1);
+ break;
+
+ case MSG_IN: /* Message in phase */
+ pCurScb->SCB_NxtStat = 6;
+ if ((tul_msgin(pCurHcb)) == -1)
+ return (-1);
+ break;
+
+ case MSG_OUT: /* Message out phase */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP); /* msg nop */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case DATA_IN: /* Data in phase */
+ return (tul_xpad_in(pCurHcb));
+
+ case DATA_OUT: /* Data out phase */
+ return (tul_xpad_out(pCurHcb));
+
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ }
+}
+
+/***************************************************************************/
+int tul_state_7(HCS * pCurHcb)
+{
+ int cnt, i;
+
+#if DEBUG_STATE
+ printk("-s7-");
+#endif
+ /* flush SCSI FIFO */
+ cnt = TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F;
+ if (cnt) {
+ for (i = 0; i < cnt; i++)
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ }
+ switch (pCurHcb->HCS_Phase) {
+ case DATA_IN: /* Data in phase */
+ case DATA_OUT: /* Data out phase */
+ return (tul_bad_seq(pCurHcb));
+ default:
+ return (6); /* Go to state 6 */
+ }
+}
+
+/***************************************************************************/
+int tul_xfer_data_in(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DOUT) {
+ return (6); /* wrong direction */
+ }
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_IN); /* 7/25/95 */
+
+ if (pCurScb->SCB_Flags & SCF_SG) { /* S/G xfer */
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_IN);
+ } else {
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_IN);
+ }
+ pCurScb->SCB_NxtStat = 0x5;
+ return (0); /* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xfer_data_out(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DIN) {
+ return (6); /* wrong direction */
+ }
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_OUT);
+
+ if (pCurScb->SCB_Flags & SCF_SG) { /* S/G xfer */
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_OUT);
+ } else {
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_OUT);
+ }
+
+ pCurScb->SCB_NxtStat = 0x5;
+ return (0); /* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xpad_in(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+ pCurScb->SCB_HaStat = HOST_DO_DU; /* over run */
+ }
+ for (;;) {
+ if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+ else
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1) {
+ return (-1);
+ }
+ if (pCurHcb->HCS_Phase != DATA_IN) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ return (6);
+ }
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ }
+}
+
+int tul_xpad_out(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+ pCurScb->SCB_HaStat = HOST_DO_DU; /* over run */
+ }
+ for (;;) {
+ if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+ else
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if ((wait_tulip(pCurHcb)) == -1) {
+ return (-1);
+ }
+ if (pCurHcb->HCS_Phase != DATA_OUT) { /* Disable wide CPU to allow read 16 bits */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ return (6);
+ }
+ }
+}
+
+
+/***************************************************************************/
+int tul_status_msg(HCS * pCurHcb)
+{ /* status & MSG_IN */
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ BYTE msg;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_CMD_COMP);
+ if ((wait_tulip(pCurHcb)) == -1) {
+ return (-1);
+ }
+ /* get status */
+ pCurScb->SCB_TaStat = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+
+ if (pCurHcb->HCS_Phase == MSG_OUT) {
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+ } else {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ if (pCurHcb->HCS_Phase == MSG_IN) {
+ msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) { /* Parity error */
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_OUT)
+ return (tul_bad_seq(pCurHcb));
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ if (msg == 0) { /* Command complete */
+
+ if ((pCurScb->SCB_TaStat & 0x18) == 0x10) { /* No link support */
+ return (tul_bad_seq(pCurHcb));
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+ return tul_wait_done_disc(pCurHcb);
+
+ }
+ if ((msg == MSG_LINK_COMP) || (msg == MSG_LINK_FLAG)) {
+ if ((pCurScb->SCB_TaStat & 0x18) == 0x10)
+ return (tul_msgin_accept(pCurHcb));
+ }
+ }
+ return (tul_bad_seq(pCurHcb));
+}
+
+
+/***************************************************************************/
+/* scsi bus free */
+int int_tul_busfree(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+ if (pCurScb != NULL) {
+ if (pCurScb->SCB_Status & SCB_SELECT) { /* selection timeout */
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = HOST_SEL_TOUT;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ } else { /* Unexpected bus free */
+ tul_unlink_busy_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = HOST_BUS_FREE;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi bus reset */
+int int_tul_scsi_rst(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ int i;
+
+ /* if DMA xfer is pending, abort DMA xfer */
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & 0x01) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+ /* wait Abort DMA xfer done */
+ while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & 0x04) == 0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ }
+ /* Abort all active & disconnected scb */
+ while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+
+ /* clr sync nego. done flag */
+ for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ }
+ return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi reselection */
+int int_tul_resel(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ TCS *pCurTcb;
+ BYTE tag, msg = 0;
+ BYTE tar, lun;
+
+ if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+ if (pCurScb->SCB_Status & SCB_SELECT) { /* if waiting for selection complete */
+ pCurScb->SCB_Status &= ~SCB_SELECT;
+ }
+ pCurHcb->HCS_ActScb = NULL;
+ }
+ /* --------- get target id---------------------- */
+ tar = TUL_RD(pCurHcb->HCS_Base, TUL_SBusId);
+ /* ------ get LUN from Identify message----------- */
+ lun = TUL_RD(pCurHcb->HCS_Base, TUL_SIdent) & 0x0F;
+ /* 07/22/98 from 0x1F -> 0x0F */
+ pCurTcb = &pCurHcb->HCS_Tcs[tar];
+ pCurHcb->HCS_ActTcs = pCurTcb;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+
+
+ /* ------------- tag queueing ? ------------------- */
+ if (pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG) {
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_IN)
+ goto no_tag;
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1)
+ return (-1);
+ msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* Read Tag Message */
+
+ if ((msg < MSG_STAG) || (msg > MSG_OTAG)) /* Is simple Tag */
+ goto no_tag;
+
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+
+ if (pCurHcb->HCS_Phase != MSG_IN)
+ goto no_tag;
+
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1)
+ return (-1);
+ tag = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* Read Tag ID */
+ pCurScb = pCurHcb->HCS_Scb + tag;
+ if ((pCurScb->SCB_Target != tar) || (pCurScb->SCB_Lun != lun)) {
+ return tul_msgout_abort_tag(pCurHcb);
+ }
+ if (pCurScb->SCB_Status != SCB_BUSY) { /* 03/24/95 */
+ return tul_msgout_abort_tag(pCurHcb);
+ }
+ pCurHcb->HCS_ActScb = pCurScb;
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ } else { /* No tag */
+ no_tag:
+ if ((pCurScb = tul_find_busy_scb(pCurHcb, tar | (lun << 8))) == NULL) {
+ return tul_msgout_abort_targ(pCurHcb);
+ }
+ pCurHcb->HCS_ActScb = pCurScb;
+ if (!(pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG)) {
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ }
+ }
+ return 0;
+}
+
+
+/***************************************************************************/
+int int_tul_bad_seq(HCS * pCurHcb)
+{ /* target wrong phase */
+ SCB *pCurScb;
+ int i;
+
+ tul_reset_scsi(pCurHcb, 10);
+
+ while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);;
+ }
+ return (-1);
+}
+
+
+/***************************************************************************/
+int tul_msgout_abort_targ(HCS * pCurHcb)
+{
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ if (tul_msgin_accept(pCurHcb) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_OUT)
+ return (tul_bad_seq(pCurHcb));
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+ return tul_wait_disc(pCurHcb);
+}
+
+/***************************************************************************/
+int tul_msgout_abort_tag(HCS * pCurHcb)
+{
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ if (tul_msgin_accept(pCurHcb) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_OUT)
+ return (tul_bad_seq(pCurHcb));
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT_TAG);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+ return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin(HCS * pCurHcb)
+{
+ TCS *pCurTcb;
+
+ for (;;) {
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1)
+ return (-1);
+
+ switch (TUL_RD(pCurHcb->HCS_Base, TUL_SFifo)) {
+ case MSG_DISC: /* Disconnect msg */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+
+ return tul_wait_disc(pCurHcb);
+
+ case MSG_SDP:
+ case MSG_RESTORE:
+ case MSG_NOP:
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ case MSG_REJ: /* Clear ATN first */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal,
+ (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+ pCurTcb = pCurHcb->HCS_ActTcs;
+ if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { /* do sync nego */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ }
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ case MSG_EXTEND: /* extended msg */
+ tul_msgin_extend(pCurHcb);
+ break;
+
+ case MSG_IGNOREWIDE:
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ /* get */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if (wait_tulip(pCurHcb) == -1)
+ return -1;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0); /* put pad */
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* get IGNORE field */
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* get pad */
+
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ case MSG_COMP:
+ {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+ return tul_wait_done_disc(pCurHcb);
+ }
+ default:
+ tul_msgout_reject(pCurHcb);
+ break;
+ }
+ if (pCurHcb->HCS_Phase != MSG_IN)
+ return (pCurHcb->HCS_Phase);
+ }
+ /* statement won't reach here */
+}
+
+
+
+
+/***************************************************************************/
+int tul_msgout_reject(HCS * pCurHcb)
+{
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+
+ if (pCurHcb->HCS_Phase == MSG_OUT) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_REJ); /* Msg reject */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ return (pCurHcb->HCS_Phase);
+}
+
+
+
+/***************************************************************************/
+int tul_msgout_ide(HCS * pCurHcb)
+{
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_IDE); /* Initiator Detected Error */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+}
+
+
+/***************************************************************************/
+int tul_msgin_extend(HCS * pCurHcb)
+{
+ BYTE len, idx;
+
+ if (tul_msgin_accept(pCurHcb) != MSG_IN)
+ return (pCurHcb->HCS_Phase);
+
+ /* Get extended msg length */
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+
+ len = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ pCurHcb->HCS_Msg[0] = len;
+ for (idx = 1; len != 0; len--) {
+
+ if ((tul_msgin_accept(pCurHcb)) != MSG_IN)
+ return (pCurHcb->HCS_Phase);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ pCurHcb->HCS_Msg[idx++] = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ }
+ if (pCurHcb->HCS_Msg[1] == 1) { /* if it's synchronous data transfer request */
+ if (pCurHcb->HCS_Msg[0] != 3) /* if length is not right */
+ return (tul_msgout_reject(pCurHcb));
+ if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_SYNC_NEGO) { /* Set OFFSET=0 to do async, nego back */
+ pCurHcb->HCS_Msg[3] = 0;
+ } else {
+ if ((tul_msgin_sync(pCurHcb) == 0) &&
+ (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SYNC_DONE)) {
+ tul_sync_done(pCurHcb);
+ return (tul_msgin_accept(pCurHcb));
+ }
+ }
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ if ((tul_msgin_accept(pCurHcb)) != MSG_OUT)
+ return (pCurHcb->HCS_Phase);
+ /* sync msg out */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+ tul_sync_done(pCurHcb);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[3]);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ if ((pCurHcb->HCS_Msg[0] != 2) || (pCurHcb->HCS_Msg[1] != 3))
+ return (tul_msgout_reject(pCurHcb));
+ /* if it's WIDE DATA XFER REQ */
+ if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) {
+ pCurHcb->HCS_Msg[2] = 0;
+ } else {
+ if (pCurHcb->HCS_Msg[2] > 2) /* > 32 bits */
+ return (tul_msgout_reject(pCurHcb));
+ if (pCurHcb->HCS_Msg[2] == 2) { /* == 32 */
+ pCurHcb->HCS_Msg[2] = 1;
+ } else {
+ if ((pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) == 0) {
+ wdtr_done(pCurHcb);
+ if ((pCurHcb->HCS_ActTcs->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ return (tul_msgin_accept(pCurHcb));
+ }
+ }
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+ if (tul_msgin_accept(pCurHcb) != MSG_OUT)
+ return (pCurHcb->HCS_Phase);
+ /* WDTR msg out */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_msgin_sync(HCS * pCurHcb)
+{
+ char default_period;
+
+ default_period = tul_rate_tbl[pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SCSI_RATE];
+ if (pCurHcb->HCS_Msg[3] > MAX_OFFSET) {
+ pCurHcb->HCS_Msg[3] = MAX_OFFSET;
+ if (pCurHcb->HCS_Msg[2] < default_period) {
+ pCurHcb->HCS_Msg[2] = default_period;
+ return 1;
+ }
+ if (pCurHcb->HCS_Msg[2] >= 59) { /* Change to async */
+ pCurHcb->HCS_Msg[3] = 0;
+ }
+ return 1;
+ }
+ /* offset requests asynchronous transfers ? */
+ if (pCurHcb->HCS_Msg[3] == 0) {
+ return 0;
+ }
+ if (pCurHcb->HCS_Msg[2] < default_period) {
+ pCurHcb->HCS_Msg[2] = default_period;
+ return 1;
+ }
+ if (pCurHcb->HCS_Msg[2] >= 59) {
+ pCurHcb->HCS_Msg[3] = 0;
+ return 1;
+ }
+ return 0;
+}
+
+
+/***************************************************************************/
+int wdtr_done(HCS * pCurHcb)
+{
+ pCurHcb->HCS_ActTcs->TCS_Flags &= ~TCF_SYNC_DONE;
+ pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_WDTR_DONE;
+
+ pCurHcb->HCS_ActTcs->TCS_JS_Period = 0;
+ if (pCurHcb->HCS_Msg[2]) { /* if 16 bit */
+ pCurHcb->HCS_ActTcs->TCS_JS_Period |= TSC_WIDE_SCSI;
+ }
+ pCurHcb->HCS_ActTcs->TCS_SConfig0 &= ~TSC_ALT_PERIOD;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+ return 1;
+}
+
+/***************************************************************************/
+int tul_sync_done(HCS * pCurHcb)
+{
+ int i;
+
+ pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_SYNC_DONE;
+
+ if (pCurHcb->HCS_Msg[3]) {
+ pCurHcb->HCS_ActTcs->TCS_JS_Period |= pCurHcb->HCS_Msg[3];
+ for (i = 0; i < 8; i++) {
+ if (tul_rate_tbl[i] >= pCurHcb->HCS_Msg[2]) /* pick the big one */
+ break;
+ }
+ pCurHcb->HCS_ActTcs->TCS_JS_Period |= (i << 4);
+ pCurHcb->HCS_ActTcs->TCS_SConfig0 |= TSC_ALT_PERIOD;
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+ return (-1);
+}
+
+
+int tul_post_scsi_rst(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ TCS *pCurTcb;
+ int i;
+
+ pCurHcb->HCS_ActScb = 0;
+ pCurHcb->HCS_ActTcs = 0;
+ pCurHcb->HCS_Flags = 0;
+
+ while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ /* clear sync done flag */
+ pCurTcb = &pCurHcb->HCS_Tcs[0];
+ for (i = 0; i < pCurHcb->HCS_MaxTar; pCurTcb++, i++) {
+ pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ /* Initialize the sync. xfer register values to an asyn xfer */
+ pCurTcb->TCS_JS_Period = 0;
+ pCurTcb->TCS_SConfig0 = pCurHcb->HCS_SConf1;
+ pCurHcb->HCS_ActTags[0] = 0; /* 07/22/98 */
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY; /* 07/22/98 */
+ } /* for */
+
+ return (-1);
+}
+
+/***************************************************************************/
+void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb)
+{
+ pCurScb->SCB_Status |= SCB_SELECT;
+ pCurScb->SCB_NxtStat = 0x1;
+ pCurHcb->HCS_ActScb = pCurScb;
+ pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SELATNSTOP);
+ return;
+}
+
+
+/***************************************************************************/
+void tul_select_atn(HCS * pCurHcb, SCB * pCurScb)
+{
+ int i;
+
+ pCurScb->SCB_Status |= SCB_SELECT;
+ pCurScb->SCB_NxtStat = 0x2;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+ for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+ pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+ pCurHcb->HCS_ActScb = pCurScb;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN);
+ return;
+}
+
+/***************************************************************************/
+void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb)
+{
+ int i;
+
+ pCurScb->SCB_Status |= SCB_SELECT;
+ pCurScb->SCB_NxtStat = 0x2;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+ for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+ pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+ pCurHcb->HCS_ActScb = pCurScb;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN3);
+ return;
+}
+
+/***************************************************************************/
+/* SCSI Bus Device Reset */
+int tul_bus_device_reset(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+ SCB *pTmpScb, *pPrevScb;
+ BYTE tar;
+
+ if (pCurHcb->HCS_Phase != MSG_OUT) {
+ return (int_tul_bad_seq(pCurHcb)); /* Unexpected phase */
+ }
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ tul_release_scb(pCurHcb, pCurScb);
+
+
+ tar = pCurScb->SCB_Target; /* target */
+ pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE | TCF_BUSY);
+ /* clr sync. nego & WDTR flags 07/22/98 */
+
+ /* abort all SCB with same target */
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */
+ while (pTmpScb != NULL) {
+
+ if (pTmpScb->SCB_Target == tar) {
+ /* unlink it */
+ if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastBusy)
+ pCurHcb->HCS_LastBusy = pPrevScb;
+ }
+ pTmpScb->SCB_HaStat = HOST_ABORTED;
+ tul_append_done_scb(pCurHcb, pTmpScb);
+ }
+ /* Previous haven't change */
+ else {
+ pPrevScb = pTmpScb;
+ }
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_DEVRST);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+ return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin_accept(HCS * pCurHcb)
+{
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+ return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int wait_tulip(HCS * pCurHcb)
+{
+
+ while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+ & TSS_INT_PENDING));
+
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+ pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+ pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+
+ if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) { /* if SCSI bus reset detected */
+ return (int_tul_resel(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) { /* if selected/reselected timeout interrupt */
+ return (int_tul_busfree(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* if SCSI bus reset detected */
+ return (int_tul_scsi_rst(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ if (pCurHcb->HCS_Flags & HCF_EXPECT_DONE_DISC) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+ pCurHcb->HCS_ActScb->SCB_HaStat = 0;
+ tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+ pCurHcb->HCS_Flags &= ~HCF_EXPECT_DONE_DISC;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ return (-1);
+ }
+ if (pCurHcb->HCS_Flags & HCF_EXPECT_DISC) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+ pCurHcb->HCS_Flags &= ~HCF_EXPECT_DISC;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ return (-1);
+ }
+ return (int_tul_busfree(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) {
+ return (pCurHcb->HCS_Phase);
+ }
+ return (pCurHcb->HCS_Phase);
+}
+/***************************************************************************/
+int tul_wait_disc(HCS * pCurHcb)
+{
+
+ while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+ & TSS_INT_PENDING));
+
+
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* if SCSI bus reset detected */
+ return (int_tul_scsi_rst(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ pCurHcb->HCS_ActScb = NULL;
+ return (-1);
+ }
+ return (tul_bad_seq(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_wait_done_disc(HCS * pCurHcb)
+{
+
+
+ while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+ & TSS_INT_PENDING));
+
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* if SCSI bus reset detected */
+ return (int_tul_scsi_rst(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+
+ tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+ pCurHcb->HCS_ActScb = NULL;
+ return (-1);
+ }
+ return (tul_bad_seq(pCurHcb));
+}
+
+/**************************** EOF *********************************/
--- /dev/null
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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
+ * 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
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ **************************************************************************/
+
+#include <linux/config.h>
+
+#define ULONG unsigned long
+#define USHORT unsigned short
+#define UCHAR unsigned char
+#define BYTE unsigned char
+#define WORD unsigned short
+#define DWORD unsigned long
+#define UBYTE unsigned char
+#define UWORD unsigned short
+#define UDWORD unsigned long
+#ifdef ALPHA
+#define U32 unsigned int
+#else
+#define U32 unsigned long
+#endif
+
+#ifndef NULL
+#define NULL 0 /* zero */
+#endif
+#ifndef TRUE
+#define TRUE (1) /* boolean true */
+#endif
+#ifndef FALSE
+#define FALSE (0) /* boolean false */
+#endif
+#ifndef FAILURE
+#define FAILURE (-1)
+#endif
+
+#define TOTAL_SG_ENTRY 32
+#define MAX_SUPPORTED_ADAPTERS 8
+#define MAX_OFFSET 15
+#define MAX_TARGETS 16
+
+#define INI_VENDOR_ID 0x1101 /* Initio's PCI vendor ID */
+#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */
+#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */
+#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */
+
+#define _I91USCSI_H
+
+typedef struct {
+ unsigned short base;
+ unsigned short vec;
+} i91u_config;
+
+/***************************************/
+/* Tulip Configuration Register Set */
+/***************************************/
+#define TUL_PVID 0x00 /* Vendor ID */
+#define TUL_PDID 0x02 /* Device ID */
+#define TUL_PCMD 0x04 /* Command */
+#define TUL_PSTUS 0x06 /* Status */
+#define TUL_PRID 0x08 /* Revision number */
+#define TUL_PPI 0x09 /* Programming interface */
+#define TUL_PSC 0x0A /* Sub Class */
+#define TUL_PBC 0x0B /* Base Class */
+#define TUL_PCLS 0x0C /* Cache line size */
+#define TUL_PLTR 0x0D /* Latency timer */
+#define TUL_PHDT 0x0E /* Header type */
+#define TUL_PBIST 0x0F /* BIST */
+#define TUL_PBAD 0x10 /* Base address */
+#define TUL_PBAD1 0x14 /* Base address */
+#define TUL_PBAD2 0x18 /* Base address */
+#define TUL_PBAD3 0x1C /* Base address */
+#define TUL_PBAD4 0x20 /* Base address */
+#define TUL_PBAD5 0x24 /* Base address */
+#define TUL_PRSVD 0x28 /* Reserved */
+#define TUL_PRSVD1 0x2C /* Reserved */
+#define TUL_PRAD 0x30 /* Expansion ROM base address */
+#define TUL_PRSVD2 0x34 /* Reserved */
+#define TUL_PRSVD3 0x38 /* Reserved */
+#define TUL_PINTL 0x3C /* Interrupt line */
+#define TUL_PINTP 0x3D /* Interrupt pin */
+#define TUL_PIGNT 0x3E /* MIN_GNT */
+#define TUL_PMGNT 0x3F /* MAX_GNT */
+
+/************************/
+/* Jasmin Register Set */
+/************************/
+#define TUL_HACFG0 0x40 /* H/A Configuration Register 0 */
+#define TUL_HACFG1 0x41 /* H/A Configuration Register 1 */
+#define TUL_HACFG2 0x42 /* H/A Configuration Register 2 */
+
+#define TUL_SDCFG0 0x44 /* SCSI Device Configuration 0 */
+#define TUL_SDCFG1 0x45 /* SCSI Device Configuration 1 */
+#define TUL_SDCFG2 0x46 /* SCSI Device Configuration 2 */
+#define TUL_SDCFG3 0x47 /* SCSI Device Configuration 3 */
+
+#define TUL_GINTS 0x50 /* Global Interrupt Status Register */
+#define TUL_GIMSK 0x52 /* Global Interrupt MASK Register */
+#define TUL_GCTRL 0x54 /* Global Control Register */
+#define TUL_GCTRL_EEPROM_BIT 0x04
+#define TUL_GCTRL1 0x55 /* Global Control Register */
+#define TUL_DMACFG 0x5B /* DMA configuration */
+#define TUL_NVRAM 0x5D /* Non-volatile RAM port */
+
+#define TUL_SCnt0 0x80 /* 00 R/W Transfer Counter Low */
+#define TUL_SCnt1 0x81 /* 01 R/W Transfer Counter Mid */
+#define TUL_SCnt2 0x82 /* 02 R/W Transfer Count High */
+#define TUL_SFifoCnt 0x83 /* 03 R FIFO counter */
+#define TUL_SIntEnable 0x84 /* 03 W Interrupt enble */
+#define TUL_SInt 0x84 /* 04 R Interrupt Register */
+#define TUL_SCtrl0 0x85 /* 05 W Control 0 */
+#define TUL_SStatus0 0x85 /* 05 R Status 0 */
+#define TUL_SCtrl1 0x86 /* 06 W Control 1 */
+#define TUL_SStatus1 0x86 /* 06 R Status 1 */
+#define TUL_SConfig 0x87 /* 07 W Configuration */
+#define TUL_SStatus2 0x87 /* 07 R Status 2 */
+#define TUL_SPeriod 0x88 /* 08 W Sync. Transfer Period & Offset */
+#define TUL_SOffset 0x88 /* 08 R Offset */
+#define TUL_SScsiId 0x89 /* 09 W SCSI ID */
+#define TUL_SBusId 0x89 /* 09 R SCSI BUS ID */
+#define TUL_STimeOut 0x8A /* 0A W Sel/Resel Time Out Register */
+#define TUL_SIdent 0x8A /* 0A R Identify Message Register */
+#define TUL_SAvail 0x8A /* 0A R Availiable Counter Register */
+#define TUL_SData 0x8B /* 0B R/W SCSI data in/out */
+#define TUL_SFifo 0x8C /* 0C R/W FIFO */
+#define TUL_SSignal 0x90 /* 10 R/W SCSI signal in/out */
+#define TUL_SCmd 0x91 /* 11 R/W Command */
+#define TUL_STest0 0x92 /* 12 R/W Test0 */
+#define TUL_STest1 0x93 /* 13 R/W Test1 */
+#define TUL_SCFG1 0x94 /* 14 R/W Configuration */
+
+#define TUL_XAddH 0xC0 /*DMA Transfer Physical Address */
+#define TUL_XAddW 0xC8 /*DMA Current Transfer Physical Address */
+#define TUL_XCntH 0xD0 /*DMA Transfer Counter */
+#define TUL_XCntW 0xD4 /*DMA Current Transfer Counter */
+#define TUL_XCmd 0xD8 /*DMA Command Register */
+#define TUL_Int 0xDC /*Interrupt Register */
+#define TUL_XStatus 0xDD /*DMA status Register */
+#define TUL_Mask 0xE0 /*Interrupt Mask Register */
+#define TUL_XCtrl 0xE4 /*DMA Control Register */
+#define TUL_XCtrl1 0xE5 /*DMA Control Register 1 */
+#define TUL_XFifo 0xE8 /*DMA FIFO */
+
+#define TUL_WCtrl 0xF7 /*Bus master wait state control */
+#define TUL_DCtrl 0xFB /*DMA delay control */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Command register of Configuration Space Header */
+/*----------------------------------------------------------------------*/
+#define BUSMS 0x04 /* BUS MASTER Enable */
+#define IOSPA 0x01 /* IO Space Enable */
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip SCSI Command register */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_RESEL 0x80 /* Enable Reselection */
+#define TSC_CMD_COMP 0x84 /* Command Complete Sequence */
+#define TSC_SEL 0x01 /* Select Without ATN Sequence */
+#define TSC_SEL_ATN 0x11 /* Select With ATN Sequence */
+#define TSC_SEL_ATN_DMA 0x51 /* Select With ATN Sequence with DMA */
+#define TSC_SEL_ATN3 0x31 /* Select With ATN3 Sequence */
+#define TSC_SEL_ATNSTOP 0x12 /* Select With ATN and Stop Sequence */
+#define TSC_SELATNSTOP 0x1E /* Select With ATN and Stop Sequence */
+
+#define TSC_SEL_ATN_DIRECT_IN 0x95 /* Select With ATN Sequence */
+#define TSC_SEL_ATN_DIRECT_OUT 0x15 /* Select With ATN Sequence */
+#define TSC_SEL_ATN3_DIRECT_IN 0xB5 /* Select With ATN3 Sequence */
+#define TSC_SEL_ATN3_DIRECT_OUT 0x35 /* Select With ATN3 Sequence */
+#define TSC_XF_DMA_OUT_DIRECT 0x06 /* DMA Xfer Infomation out */
+#define TSC_XF_DMA_IN_DIRECT 0x86 /* DMA Xfer Infomation in */
+
+#define TSC_XF_DMA_OUT 0x43 /* DMA Xfer Infomation out */
+#define TSC_XF_DMA_IN 0xC3 /* DMA Xfer Infomation in */
+#define TSC_XF_FIFO_OUT 0x03 /* FIFO Xfer Infomation out */
+#define TSC_XF_FIFO_IN 0x83 /* FIFO Xfer Infomation in */
+
+#define TSC_MSG_ACCEPT 0x0F /* Message Accept */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 0 Register */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_SEQ 0x20 /* Reset sequence counter */
+#define TSC_FLUSH_FIFO 0x10 /* Flush FIFO */
+#define TSC_ABT_CMD 0x04 /* Abort command (sequence) */
+#define TSC_RST_CHIP 0x02 /* Reset SCSI Chip */
+#define TSC_RST_BUS 0x01 /* Reset SCSI Bus */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 1 Register */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_SCAM 0x80 /* Enable SCAM */
+#define TSC_TIMER 0x40 /* Select timeout unit */
+#define TSC_EN_SCSI2 0x20 /* SCSI-2 mode */
+#define TSC_PWDN 0x10 /* Power down mode */
+#define TSC_WIDE_CPU 0x08 /* Wide CPU */
+#define TSC_HW_RESELECT 0x04 /* Enable HW reselect */
+#define TSC_EN_BUS_OUT 0x02 /* Enable SCSI data bus out latch */
+#define TSC_EN_BUS_IN 0x01 /* Enable SCSI data bus in latch */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Configuration Register */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_LATCH 0x80 /* Enable phase latch */
+#define TSC_INITIATOR 0x40 /* Initiator mode */
+#define TSC_EN_SCSI_PAR 0x20 /* Enable SCSI parity */
+#define TSC_DMA_8BIT 0x10 /* Alternate dma 8-bits mode */
+#define TSC_DMA_16BIT 0x08 /* Alternate dma 16-bits mode */
+#define TSC_EN_WDACK 0x04 /* Enable DACK while wide SCSI xfer */
+#define TSC_ALT_PERIOD 0x02 /* Alternate sync period mode */
+#define TSC_DIS_SCSIRST 0x01 /* Disable SCSI bus reset us */
+
+#define TSC_INITDEFAULT (TSC_INITIATOR | TSC_EN_LATCH | TSC_ALT_PERIOD | TSC_DIS_SCSIRST)
+
+#define TSC_WIDE_SCSI 0x80 /* Enable Wide SCSI */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI signal Register */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_ACK 0x00 /* Release ACK signal */
+#define TSC_RST_ATN 0x00 /* Release ATN signal */
+#define TSC_RST_BSY 0x00 /* Release BSY signal */
+
+#define TSC_SET_ACK 0x40 /* ACK signal */
+#define TSC_SET_ATN 0x08 /* ATN signal */
+
+#define TSC_REQI 0x80 /* REQ signal */
+#define TSC_ACKI 0x40 /* ACK signal */
+#define TSC_BSYI 0x20 /* BSY signal */
+#define TSC_SELI 0x10 /* SEL signal */
+#define TSC_ATNI 0x08 /* ATN signal */
+#define TSC_MSGI 0x04 /* MSG signal */
+#define TSC_CDI 0x02 /* C/D signal */
+#define TSC_IOI 0x01 /* I/O signal */
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 0 Register */
+/*----------------------------------------------------------------------*/
+#define TSS_INT_PENDING 0x80 /* Interrupt pending */
+#define TSS_SEQ_ACTIVE 0x40 /* Sequencer active */
+#define TSS_XFER_CNT 0x20 /* Transfer counter zero */
+#define TSS_FIFO_EMPTY 0x10 /* FIFO empty */
+#define TSS_PAR_ERROR 0x08 /* SCSI parity error */
+#define TSS_PH_MASK 0x07 /* SCSI phase mask */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 1 Register */
+/*----------------------------------------------------------------------*/
+#define TSS_STATUS_RCV 0x08 /* Status received */
+#define TSS_MSG_SEND 0x40 /* Message sent */
+#define TSS_CMD_PH_CMP 0x20 /* command phase done */
+#define TSS_DATA_PH_CMP 0x10 /* Data phase done */
+#define TSS_STATUS_SEND 0x08 /* Status sent */
+#define TSS_XFER_CMP 0x04 /* Transfer completed */
+#define TSS_SEL_CMP 0x02 /* Selection completed */
+#define TSS_ARB_CMP 0x01 /* Arbitration completed */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 2 Register */
+/*----------------------------------------------------------------------*/
+#define TSS_CMD_ABTED 0x80 /* Command aborted */
+#define TSS_OFFSET_0 0x40 /* Offset counter zero */
+#define TSS_FIFO_FULL 0x20 /* FIFO full */
+#define TSS_TIMEOUT_0 0x10 /* Timeout counter zero */
+#define TSS_BUSY_RLS 0x08 /* Busy release */
+#define TSS_PH_MISMATCH 0x04 /* Phase mismatch */
+#define TSS_SCSI_BUS_EN 0x02 /* SCSI data bus enable */
+#define TSS_SCSIRST 0x01 /* SCSI bus reset in progress */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Interrupt Register */
+/*----------------------------------------------------------------------*/
+#define TSS_RESEL_INT 0x80 /* Reselected interrupt */
+#define TSS_SEL_TIMEOUT 0x40 /* Selected/reselected timeout */
+#define TSS_BUS_SERV 0x20
+#define TSS_SCSIRST_INT 0x10 /* SCSI bus reset detected */
+#define TSS_DISC_INT 0x08 /* Disconnected interrupt */
+#define TSS_SEL_INT 0x04 /* Select interrupt */
+#define TSS_SCAM_SEL 0x02 /* SCAM selected */
+#define TSS_FUNC_COMP 0x01
+
+/*----------------------------------------------------------------------*/
+/* SCSI Phase Codes. */
+/*----------------------------------------------------------------------*/
+#define DATA_OUT 0
+#define DATA_IN 1 /* 4 */
+#define CMD_OUT 2
+#define STATUS_IN 3 /* 6 */
+#define MSG_OUT 6 /* 3 */
+#define MSG_IN 7
+
+
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip xfer Command register */
+/*----------------------------------------------------------------------*/
+#define TAX_X_FORC 0x02
+#define TAX_X_ABT 0x04
+#define TAX_X_CLR_FIFO 0x08
+
+#define TAX_X_IN 0x21
+#define TAX_X_OUT 0x01
+#define TAX_SG_IN 0xA1
+#define TAX_SG_OUT 0x81
+
+/*----------------------------------------------------------------------*/
+/* Tulip Interrupt Register */
+/*----------------------------------------------------------------------*/
+#define XCMP 0x01
+#define FCMP 0x02
+#define XABT 0x04
+#define XERR 0x08
+#define SCMP 0x10
+#define IPEND 0x80
+
+/*----------------------------------------------------------------------*/
+/* Tulip DMA Status Register */
+/*----------------------------------------------------------------------*/
+#define XPEND 0x01 /* Transfer pending */
+#define FEMPTY 0x02 /* FIFO empty */
+
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_GCTRL */
+/*----------------------------------------------------------------------*/
+#define EXTSG 0x80
+#define EXTAD 0x60
+#define SEG4K 0x08
+#define EEPRG 0x04
+#define MRMUL 0x02
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_NVRAM */
+/*----------------------------------------------------------------------*/
+#define SE2CS 0x08
+#define SE2CLK 0x04
+#define SE2DO 0x02
+#define SE2DI 0x01
+
+
+/************************************************************************/
+/* Scatter-Gather Element Structure */
+/************************************************************************/
+typedef struct SG_Struc {
+ U32 SG_Ptr; /* Data Pointer */
+ U32 SG_Len; /* Data Length */
+} SG;
+
+/***********************************************************************
+ SCSI Control Block
+************************************************************************/
+typedef struct Scsi_Ctrl_Blk {
+ struct Scsi_Ctrl_Blk *SCB_NxtScb;
+ UBYTE SCB_Status; /*4 */
+ UBYTE SCB_NxtStat; /*5 */
+ UBYTE SCB_Mode; /*6 */
+ UBYTE SCB_Msgin; /*7 SCB_Res0 */
+ UWORD SCB_SGIdx; /*8 */
+ UWORD SCB_SGMax; /*A */
+#ifdef ALPHA
+ U32 SCB_Reserved[2]; /*C */
+#else
+ U32 SCB_Reserved[3]; /*C */
+#endif
+
+ U32 SCB_XferLen; /*18 Current xfer len */
+ U32 SCB_TotXLen; /*1C Total xfer len */
+ U32 SCB_PAddr; /*20 SCB phy. Addr. */
+
+ UBYTE SCB_Opcode; /*24 SCB command code */
+ UBYTE SCB_Flags; /*25 SCB Flags */
+ UBYTE SCB_Target; /*26 Target Id */
+ UBYTE SCB_Lun; /*27 Lun */
+ U32 SCB_BufPtr; /*28 Data Buffer Pointer */
+ U32 SCB_BufLen; /*2C Data Allocation Length */
+ UBYTE SCB_SGLen; /*30 SG list # */
+ UBYTE SCB_SenseLen; /*31 Sense Allocation Length */
+ UBYTE SCB_HaStat; /*32 */
+ UBYTE SCB_TaStat; /*33 */
+ UBYTE SCB_CDBLen; /*34 CDB Length */
+ UBYTE SCB_Ident; /*35 Identify */
+ UBYTE SCB_TagMsg; /*36 Tag Message */
+ UBYTE SCB_TagId; /*37 Queue Tag */
+ UBYTE SCB_CDB[12]; /*38 */
+ U32 SCB_SGPAddr; /*44 SG List/Sense Buf phy. Addr. */
+ U32 SCB_SensePtr; /*48 Sense data pointer */
+ void (*SCB_Post) (BYTE *, BYTE *); /*4C POST routine */
+ unsigned char *SCB_Srb; /*50 SRB Pointer */
+ SG SCB_SGList[TOTAL_SG_ENTRY]; /*54 Start of SG list */
+} SCB;
+
+/* Bit Definition for SCB_Status */
+#define SCB_RENT 0x01
+#define SCB_PEND 0x02
+#define SCB_CONTIG 0x04 /* Contigent Allegiance */
+#define SCB_SELECT 0x08
+#define SCB_BUSY 0x10
+#define SCB_DONE 0x20
+
+
+/* Opcodes of SCB_Opcode */
+#define ExecSCSI 0x1
+#define BusDevRst 0x2
+#define AbortCmd 0x3
+
+
+/* Bit Definition for SCB_Mode */
+#define SCM_RSENS 0x01 /* request sense mode */
+
+
+/* Bit Definition for SCB_Flags */
+#define SCF_DONE 0x01
+#define SCF_POST 0x02
+#define SCF_SENSE 0x04
+#define SCF_DIR 0x18
+#define SCF_NO_DCHK 0x00
+#define SCF_DIN 0x08
+#define SCF_DOUT 0x10
+#define SCF_NO_XF 0x18
+#define SCF_WR_VF 0x20 /* Write verify turn on */
+#define SCF_POLL 0x40
+#define SCF_SG 0x80
+
+/* Error Codes for SCB_HaStat */
+#define HOST_SEL_TOUT 0x11
+#define HOST_DO_DU 0x12
+#define HOST_BUS_FREE 0x13
+#define HOST_BAD_PHAS 0x14
+#define HOST_INV_CMD 0x16
+#define HOST_ABORTED 0x1A /* 07/21/98 */
+#define HOST_SCSI_RST 0x1B
+#define HOST_DEV_RST 0x1C
+
+/* Error Codes for SCB_TaStat */
+#define TARGET_CHKCOND 0x02
+#define TARGET_BUSY 0x08
+#define QUEUE_FULL 0x28
+
+/* SCSI MESSAGE */
+#define MSG_COMP 0x00
+#define MSG_EXTEND 0x01
+#define MSG_SDP 0x02
+#define MSG_RESTORE 0x03
+#define MSG_DISC 0x04
+#define MSG_IDE 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJ 0x07
+#define MSG_NOP 0x08
+#define MSG_PARITY 0x09
+#define MSG_LINK_COMP 0x0A
+#define MSG_LINK_FLAG 0x0B
+#define MSG_DEVRST 0x0C
+#define MSG_ABORT_TAG 0x0D
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG 0x20
+#define MSG_HTAG 0x21
+#define MSG_OTAG 0x22
+
+#define MSG_IGNOREWIDE 0x23
+
+#define MSG_IDENT 0x80
+
+/***********************************************************************
+ Target Device Control Structure
+**********************************************************************/
+
+typedef struct Tar_Ctrl_Struc {
+ UWORD TCS_Flags; /* 0 */
+ UBYTE TCS_JS_Period; /* 2 */
+ UBYTE TCS_SConfig0; /* 3 */
+
+ UWORD TCS_DrvFlags; /* 4 */
+ UBYTE TCS_DrvHead; /* 6 */
+ UBYTE TCS_DrvSector; /* 7 */
+} TCS;
+
+/***********************************************************************
+ Target Device Control Structure
+**********************************************************************/
+
+/* Bit Definition for TCF_Flags */
+#define TCF_SCSI_RATE 0x0007
+#define TCF_EN_DISC 0x0008
+#define TCF_NO_SYNC_NEGO 0x0010
+#define TCF_NO_WDTR 0x0020
+#define TCF_EN_255 0x0040
+#define TCF_EN_START 0x0080
+#define TCF_WDTR_DONE 0x0100
+#define TCF_SYNC_DONE 0x0200
+#define TCF_BUSY 0x0400
+
+
+/* Bit Definition for TCF_DrvFlags */
+#define TCF_DRV_BUSY 0x01 /* Indicate target busy(driver) */
+#define TCF_DRV_EN_TAG 0x0800
+#define TCF_DRV_255_63 0x0400
+
+typedef struct I91u_Adpt_Struc {
+ UWORD ADPT_BIOS; /* 0 */
+ UWORD ADPT_BASE; /* 1 */
+ UBYTE ADPT_Bus; /* 2 */
+ UBYTE ADPT_Device; /* 3 */
+ UBYTE ADPT_INTR; /* 4 */
+} INI_ADPT_STRUCT;
+
+
+/***********************************************************************
+ Host Adapter Control Structure
+************************************************************************/
+typedef struct Ha_Ctrl_Struc {
+ UWORD HCS_Base; /* 00 */
+ UWORD HCS_BIOS; /* 02 */
+ UBYTE HCS_Intr; /* 04 */
+ UBYTE HCS_SCSI_ID; /* 05 */
+ UBYTE HCS_MaxTar; /* 06 */
+ UBYTE HCS_NumScbs; /* 07 */
+
+ UBYTE HCS_Flags; /* 08 */
+ UBYTE HCS_Index; /* 09 */
+ UBYTE HCS_HaId; /* 0A */
+ UBYTE HCS_Config; /* 0B */
+ UWORD HCS_IdMask; /* 0C */
+ UBYTE HCS_Semaph; /* 0E */
+ UBYTE HCS_Phase; /* 0F */
+ UBYTE HCS_JSStatus0; /* 10 */
+ UBYTE HCS_JSInt; /* 11 */
+ UBYTE HCS_JSStatus1; /* 12 */
+ UBYTE HCS_SConf1; /* 13 */
+
+ UBYTE HCS_Msg[8]; /* 14 */
+ SCB *HCS_NxtAvail; /* 1C */
+ SCB *HCS_Scb; /* 20 */
+ SCB *HCS_ScbEnd; /* 24 */
+ SCB *HCS_NxtPend; /* 28 */
+ SCB *HCS_NxtContig; /* 2C */
+ SCB *HCS_ActScb; /* 30 */
+ TCS *HCS_ActTcs; /* 34 */
+
+ SCB *HCS_FirstAvail; /* 38 */
+ SCB *HCS_LastAvail; /* 3C */
+ SCB *HCS_FirstPend; /* 40 */
+ SCB *HCS_LastPend; /* 44 */
+ SCB *HCS_FirstBusy; /* 48 */
+ SCB *HCS_LastBusy; /* 4C */
+ SCB *HCS_FirstDone; /* 50 */
+ SCB *HCS_LastDone; /* 54 */
+ UBYTE HCS_MaxTags[16]; /* 58 */
+ UBYTE HCS_ActTags[16]; /* 68 */
+ TCS HCS_Tcs[MAX_TARGETS]; /* 78 */
+ ULONG pSRB_head; /* SRB save queue header */
+ ULONG pSRB_tail; /* SRB save queue tail */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spinlock_t HCS_AvailLock;
+ spinlock_t HCS_SemaphLock;
+ spinlock_t pSRB_lock; /* SRB queue lock */
+#endif
+} HCS;
+
+/* Bit Definition for HCB_Config */
+#define HCC_SCSI_RESET 0x01
+#define HCC_EN_PAR 0x02
+#define HCC_ACT_TERM1 0x04
+#define HCC_ACT_TERM2 0x08
+#define HCC_AUTO_TERM 0x10
+#define HCC_EN_PWR 0x80
+
+/* Bit Definition for HCB_Flags */
+#define HCF_EXPECT_DISC 0x01
+#define HCF_EXPECT_SELECT 0x02
+#define HCF_EXPECT_RESET 0x10
+#define HCF_EXPECT_DONE_DISC 0x20
+
+/******************************************************************
+ Serial EEProm
+*******************************************************************/
+
+typedef struct _NVRAM_SCSI { /* SCSI channel configuration */
+ UCHAR NVM_ChSCSIID; /* 0Ch -> Channel SCSI ID */
+ UCHAR NVM_ChConfig1; /* 0Dh -> Channel config 1 */
+ UCHAR NVM_ChConfig2; /* 0Eh -> Channel config 2 */
+ UCHAR NVM_NumOfTarg; /* 0Fh -> Number of SCSI target */
+ /* SCSI target configuration */
+ UCHAR NVM_Targ0Config; /* 10h -> Target 0 configuration */
+ UCHAR NVM_Targ1Config; /* 11h -> Target 1 configuration */
+ UCHAR NVM_Targ2Config; /* 12h -> Target 2 configuration */
+ UCHAR NVM_Targ3Config; /* 13h -> Target 3 configuration */
+ UCHAR NVM_Targ4Config; /* 14h -> Target 4 configuration */
+ UCHAR NVM_Targ5Config; /* 15h -> Target 5 configuration */
+ UCHAR NVM_Targ6Config; /* 16h -> Target 6 configuration */
+ UCHAR NVM_Targ7Config; /* 17h -> Target 7 configuration */
+ UCHAR NVM_Targ8Config; /* 18h -> Target 8 configuration */
+ UCHAR NVM_Targ9Config; /* 19h -> Target 9 configuration */
+ UCHAR NVM_TargAConfig; /* 1Ah -> Target A configuration */
+ UCHAR NVM_TargBConfig; /* 1Bh -> Target B configuration */
+ UCHAR NVM_TargCConfig; /* 1Ch -> Target C configuration */
+ UCHAR NVM_TargDConfig; /* 1Dh -> Target D configuration */
+ UCHAR NVM_TargEConfig; /* 1Eh -> Target E configuration */
+ UCHAR NVM_TargFConfig; /* 1Fh -> Target F configuration */
+} NVRAM_SCSI;
+
+typedef struct _NVRAM {
+/*----------header ---------------*/
+ USHORT NVM_Signature; /* 0,1: Signature */
+ UCHAR NVM_Size; /* 2: Size of data structure */
+ UCHAR NVM_Revision; /* 3: Revision of data structure */
+ /* ----Host Adapter Structure ---- */
+ UCHAR NVM_ModelByte0; /* 4: Model number (byte 0) */
+ UCHAR NVM_ModelByte1; /* 5: Model number (byte 1) */
+ UCHAR NVM_ModelInfo; /* 6: Model information */
+ UCHAR NVM_NumOfCh; /* 7: Number of SCSI channel */
+ UCHAR NVM_BIOSConfig1; /* 8: BIOS configuration 1 */
+ UCHAR NVM_BIOSConfig2; /* 9: BIOS configuration 2 */
+ UCHAR NVM_HAConfig1; /* A: Hoat adapter configuration 1 */
+ UCHAR NVM_HAConfig2; /* B: Hoat adapter configuration 2 */
+ NVRAM_SCSI NVM_SCSIInfo[2];
+ UCHAR NVM_reserved[10];
+ /* ---------- CheckSum ---------- */
+ USHORT NVM_CheckSum; /* 0x3E, 0x3F: Checksum of NVRam */
+} NVRAM, *PNVRAM;
+
+/* Bios Configuration for nvram->BIOSConfig1 */
+#define NBC1_ENABLE 0x01 /* BIOS enable */
+#define NBC1_8DRIVE 0x02 /* Support more than 2 drives */
+#define NBC1_REMOVABLE 0x04 /* Support removable drive */
+#define NBC1_INT19 0x08 /* Intercept int 19h */
+#define NBC1_BIOSSCAN 0x10 /* Dynamic BIOS scan */
+#define NBC1_LUNSUPPORT 0x40 /* Support LUN */
+
+/* HA Configuration Byte 1 */
+#define NHC1_BOOTIDMASK 0x0F /* Boot ID number */
+#define NHC1_LUNMASK 0x70 /* Boot LUN number */
+#define NHC1_CHANMASK 0x80 /* Boot Channel number */
+
+/* Bit definition for nvram->SCSIconfig1 */
+#define NCC1_BUSRESET 0x01 /* Reset SCSI bus at power up */
+#define NCC1_PARITYCHK 0x02 /* SCSI parity enable */
+#define NCC1_ACTTERM1 0x04 /* Enable active terminator 1 */
+#define NCC1_ACTTERM2 0x08 /* Enable active terminator 2 */
+#define NCC1_AUTOTERM 0x10 /* Enable auto terminator */
+#define NCC1_PWRMGR 0x80 /* Enable power management */
+
+/* Bit definition for SCSI Target configuration byte */
+#define NTC_DISCONNECT 0x08 /* Enable SCSI disconnect */
+#define NTC_SYNC 0x10 /* SYNC_NEGO */
+#define NTC_NO_WDTR 0x20 /* SYNC_NEGO */
+#define NTC_1GIGA 0x40 /* 255 head / 63 sectors (64/32) */
+#define NTC_SPINUP 0x80 /* Start disk drive */
+
+/* Default NVRam values */
+#define INI_SIGNATURE 0xC925
+#define NBC1_DEFAULT (NBC1_ENABLE)
+#define NCC1_DEFAULT (NCC1_BUSRESET | NCC1_AUTOTERM | NCC1_PARITYCHK)
+#define NTC_DEFAULT (NTC_NO_WDTR | NTC_1GIGA | NTC_DISCONNECT)
+
+/* SCSI related definition */
+#define DISC_NOT_ALLOW 0x80 /* Disconnect is not allowed */
+#define DISC_ALLOW 0xC0 /* Disconnect is allowed */
+#define SCSICMD_RequestSense 0x03
+
+
+/*----------------------------------------------------------------------*/
+/* PCI */
+/*----------------------------------------------------------------------*/
+#define PCI_FUNCTION_ID 0xB1
+#define PCI_BIOS_PRESENT 0x01
+#define FIND_PCI_DEVICE 0x02
+#define FIND_PCI_CLASS_CODE 0x03
+#define GENERATE_SPECIAL_CYCLE 0x06
+#define READ_CONFIG_BYTE 0x08
+#define READ_CONFIG_WORD 0x09
+#define READ_CONFIG_DWORD 0x0A
+#define WRITE_CONFIG_BYTE 0x0B
+#define WRITE_CONFIG_WORD 0x0C
+#define WRITE_CONFIG_DWORD 0x0D
+
+#define SUCCESSFUL 0x00
+#define FUNC_NOT_SUPPORTED 0x81
+#define BAD_VENDOR_ID 0x83 /* Bad vendor ID */
+#define DEVICE_NOT_FOUND 0x86 /* PCI device not found */
+#define BAD_REGISTER_NUMBER 0x87
+
+#define MAX_PCI_DEVICES 21 /* Maximum devices supportted */
+
+#define MAX_PCI_CHANL 4
+
+typedef struct _BIOS32_ENTRY_STRUCTURE {
+ DWORD Signatures; /* Should be "_32_" */
+ DWORD BIOS32Entry; /* 32-bit physical address */
+ BYTE Revision; /* Revision level, should be 0 */
+ BYTE Length; /* Multiply of 16, should be 1 */
+ BYTE CheckSum; /* Checksum of whole structure */
+ BYTE Reserved[5]; /* Reserved */
+} BIOS32_ENTRY_STRUCTURE, *PBIOS32_ENTRY_STRUCTURE;
+
+typedef struct {
+ union {
+ unsigned int eax;
+ struct {
+ unsigned short ax;
+ } word;
+ struct {
+ unsigned char al;
+ unsigned char ah;
+ } byte;
+ } eax;
+ union {
+ unsigned int ebx;
+ struct {
+ unsigned short bx;
+ } word;
+ struct {
+ unsigned char bl;
+ unsigned char bh;
+ } byte;
+ } ebx;
+ union {
+ unsigned int ecx;
+ struct {
+ unsigned short cx;
+ } word;
+ struct {
+ unsigned char cl;
+ unsigned char ch;
+ } byte;
+ } ecx;
+ union {
+ unsigned int edx;
+ struct {
+ unsigned short dx;
+ } word;
+ struct {
+ unsigned char dl;
+ unsigned char dh;
+ } byte;
+ } edx;
+ union {
+ unsigned int edi;
+ struct {
+ unsigned short di;
+ } word;
+ } edi;
+ union {
+ unsigned int esi;
+ struct {
+ unsigned short si;
+ } word;
+ } esi;
+} REGS;
+
+typedef union { /* Union define for mechanism 1 */
+ struct {
+ unsigned char RegNum;
+ unsigned char FcnNum:3;
+ unsigned char DeviceNum:5;
+ unsigned char BusNum;
+ unsigned char Reserved:7;
+ unsigned char Enable:1;
+ } sConfigAdr;
+ unsigned long lConfigAdr;
+} CONFIG_ADR;
+
+typedef union { /* Union define for mechanism 2 */
+ struct {
+ unsigned char RegNum;
+ unsigned char DeviceNum;
+ unsigned short Reserved;
+ } sHostAdr;
+ unsigned long lHostAdr;
+} HOST_ADR;
+
+typedef struct _HCSinfo {
+ ULONG base;
+ UCHAR vec;
+ UCHAR bios; /* High byte of BIOS address */
+ USHORT BaseAndBios; /* high byte: pHcsInfo->bios,low byte:pHcsInfo->base */
+} HCSINFO;
+
+#define TUL_RD(x,y) (UCHAR)(inb( (int)((ULONG)(x+y)) ))
+#define TUL_RDLONG(x,y) (ULONG)(inl((int)((ULONG)(x+y)) ))
+#define TUL_WR( adr,data) outb( (UCHAR)(data), (int)(adr))
+#define TUL_WRSHORT(adr,data) outw( (UWORD)(data), (int)(adr))
+#define TUL_WRLONG( adr,data) outl( (ULONG)(data), (int)(adr))
+
+#define SCSI_ABORT_SNOOZE 0
+#define SCSI_ABORT_SUCCESS 1
+#define SCSI_ABORT_PENDING 2
+#define SCSI_ABORT_BUSY 3
+#define SCSI_ABORT_NOT_RUNNING 4
+#define SCSI_ABORT_ERROR 5
+
+#define SCSI_RESET_SNOOZE 0
+#define SCSI_RESET_PUNT 1
+#define SCSI_RESET_SUCCESS 2
+#define SCSI_RESET_PENDING 3
+#define SCSI_RESET_WAKEUP 4
+#define SCSI_RESET_NOT_RUNNING 5
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS 0x01
+#define SCSI_RESET_ASYNCHRONOUS 0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET 0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET 0x08
+
+#define SCSI_RESET_BUS_RESET 0x100
+#define SCSI_RESET_HOST_RESET 0x200
+#define SCSI_RESET_ACTION 0xff
--- /dev/null
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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
+ * 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
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host
+ * adapters
+ *
+ * 08/06/97 hc - v1.01h
+ * - Support inic-940 and inic-935
+ * 09/26/97 hc - v1.01i
+ * - Make correction from J.W. Schultz suggestion
+ * 10/13/97 hc - Support reset function
+ * 10/21/97 hc - v1.01j
+ * - Support 32 LUN (SCSI 3)
+ * 01/14/98 hc - v1.01k
+ * - Fix memory allocation problem
+ * 03/04/98 hc - v1.01l
+ * - Fix tape rewind which will hang the system problem
+ * - Set can_queue to tul_num_scb
+ * 06/25/98 hc - v1.01m
+ * - Get it work for kernel version >= 2.1.75
+ * - Dynamic assign SCSI bus reset holding time in init_tulip()
+ * 07/02/98 hc - v1.01n
+ * - Support 0002134A
+ * 08/07/98 hc - v1.01o
+ * - Change the tul_abort_srb routine to use scsi_done. <01>
+ * 09/07/98 hl - v1.02
+ * - Change the INI9100U define and proc_dir_entry to
+ * reflect the newer Kernel 2.1.118, but the v1.o1o
+ * should work with Kernel 2.1.118.
+ * 09/20/98 wh - v1.02a
+ * - Support Abort command.
+ * - Handle reset routine.
+ * 09/21/98 hl - v1.03
+ * - remove comments.
+ * 12/09/98 bv - v1.03a
+ * - Removed unused code
+ * 12/13/98 bv - v1.03b
+ * - Remove cli() locking for kernels >= 2.1.95. This uses
+ * spinlocks to serialize access to the pSRB_head and
+ * pSRB_tail members of the HCS structure.
+ **************************************************************************/
+
+#define CVT_LINUX_VERSION(V,P,S) (V * 65536 + P * 256 + S)
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+#include <stdarg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92)
+#include <linux/bios32.h>
+#endif
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23)
+#include <linux/init.h>
+#endif
+#include <linux/blk.h>
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+#include <asm/spinlock.h>
+#endif
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "ini9100u.h"
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/config.h>
+
+#else
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include <linux/malloc.h>
+#include "ini9100u.h"
+#endif
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93)
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+#endif
+
+#ifdef DEBUG_i91u
+unsigned int i91u_debug = DEBUG_DEFAULT;
+#endif
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = INI9100U;
+#include "scsi_module.c"
+#endif
+
+char *i91uCopyright = "Copyright (C) 1996-98";
+char *i91uInitioName = "by Initio Corporation";
+char *i91uProductName = "INI-9X00U/UW";
+char *i91uVersion = "v1.03b";
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+struct proc_dir_entry proc_scsi_ini9100u =
+{
+ PROC_SCSI_INI9100U, 7, "INI9100U",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2,
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+#define TULSZ(sz) (sizeof(sz) / sizeof(sz[0]))
+#define TUL_RDWORD(x,y) (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+/* set by i91_setup according to the command line */
+static int setup_called = 0;
+
+static int tul_num_ch = 4; /* Maximum 4 adapters */
+static int tul_num_scb;
+static int tul_tag_enable = 1;
+static SCB *tul_scb;
+
+#ifdef DEBUG_i91u
+static int setup_debug = 0;
+#endif
+
+static char *setup_str = (char *) NULL;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr0(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr1(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr2(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr3(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr4(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr5(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr6(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr7(int irq, void *dev_id, struct pt_regs *);
+#else
+static void i91u_intr0(int irq, struct pt_regs *);
+static void i91u_intr1(int irq, struct pt_regs *);
+static void i91u_intr2(int irq, struct pt_regs *);
+static void i91u_intr3(int irq, struct pt_regs *);
+static void i91u_intr4(int irq, struct pt_regs *);
+static void i91u_intr5(int irq, struct pt_regs *);
+static void i91u_intr6(int irq, struct pt_regs *);
+static void i91u_intr7(int irq, struct pt_regs *);
+#endif
+
+static void i91u_panic(char *msg);
+
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb);
+
+ /* ---- EXTERNAL FUNCTIONS ---- */
+ /* Get total number of adapters */
+extern void init_i91uAdapter_table(void);
+extern int Addi91u_into_Adapter_table(WORD, WORD, BYTE, BYTE, BYTE);
+extern int tul_ReturnNumberOfAdapters(void);
+extern void get_tulipPCIConfig(HCS * pHCB, int iChannel_index);
+extern int init_tulip(HCS * pHCB, SCB * pSCB, int tul_num_scb, BYTE * pbBiosAdr, int reset_time);
+extern SCB *tul_alloc_scb(HCS * pHCB);
+extern int tul_abort_srb(HCS * pHCB, Scsi_Cmnd * pSRB);
+extern void tul_exec_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_release_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_stop_bm(HCS * pHCB);
+extern int tul_reset_scsi(HCS * pCurHcb, int seconds);
+extern int tul_isr(HCS * pHCB);
+extern int tul_reset(HCS * pHCB, Scsi_Cmnd * pSRB, unsigned char target);
+extern int tul_reset_scsi_bus(HCS * pCurHcb);
+extern int tul_device_reset(HCS * pCurHcb, ULONG pSrb, unsigned int target, unsigned int ResetFlags);
+ /* ---- EXTERNAL VARIABLES ---- */
+extern HCS tul_hcs[];
+
+/*
+ * queue services:
+ */
+/*****************************************************************************
+ Function name : i91uAppendSRBToQueue
+ Description : This function will push current request into save list
+ Input : pSRB - Pointer to SCSI request block.
+ pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : None.
+*****************************************************************************/
+static void i91uAppendSRBToQueue(HCS * pHCB, Scsi_Cmnd * pSRB)
+{
+ ULONG flags;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(pHCB->pSRB_lock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+
+ pSRB->next = NULL; /* Pointer to next */
+
+ if (pHCB->pSRB_head == NULL)
+ pHCB->pSRB_head = pSRB;
+ else
+ pHCB->pSRB_tail->next = pSRB; /* Pointer to next */
+ pHCB->pSRB_tail = pSRB;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pHCB->pSRB_lock), flags);
+#else
+ restore_flags(flags);
+#endif
+ return;
+}
+
+/*****************************************************************************
+ Function name : i91uPopSRBFromQueue
+ Description : This function will pop current request from save list
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static Scsi_Cmnd *i91uPopSRBFromQueue(HCS * pHCB)
+{
+ Scsi_Cmnd *pSRB;
+ ULONG flags;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&(pHCB->pSRB_lock), flags);
+#else
+ save_flags(flags);
+ cli();
+#endif
+
+ if ((pSRB = pHCB->pSRB_head) != NULL) {
+ pHCB->pSRB_head = pHCB->pSRB_head->next;
+ pSRB->next = NULL;
+ }
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&(pHCB->pSRB_lock), flags);
+#else
+ restore_flags(flags);
+#endif
+
+ return (pSRB);
+}
+
+/* called from init/main.c */
+
+void i91u_setup(char *str, int *ints)
+{
+ if (setup_called)
+ i91u_panic("i91u: i91u_setup called twice.\n");
+
+ setup_called = ints[0];
+ setup_str = str;
+
+#ifdef DEBUG_i91u
+ setup_debug = ints[0] >= 1 ? ints[1] : DEBUG_DEFAULT;
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93)
+int tul_NewReturnNumberOfAdapters(void)
+{
+ struct pci_dev *pDev = NULL; /* Start from none */
+ int iAdapters = 0;
+ long dRegValue;
+ WORD wBIOS;
+
+ init_i91uAdapter_table();
+
+ while ((pDev = pci_find_device(INI_VENDOR_ID, I950_DEVICE_ID, pDev)) != NULL) {
+ pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
+ wBIOS = (UWORD) (dRegValue & 0xFF);
+ if (((dRegValue & 0xFF00) >> 8) == 0xFF)
+ dRegValue = 0;
+ wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8));
+ if (Addi91u_into_Adapter_table(wBIOS,
+ (pDev->base_address[0] & 0xFFFE),
+ pDev->irq,
+ pDev->bus->number,
+ (pDev->devfn >> 3)
+ ) == 0)
+ iAdapters++;
+ }
+ while ((pDev = pci_find_device(INI_VENDOR_ID, I940_DEVICE_ID, pDev)) != NULL) {
+ pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
+ wBIOS = (UWORD) (dRegValue & 0xFF);
+ if (((dRegValue & 0xFF00) >> 8) == 0xFF)
+ dRegValue = 0;
+ wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8));
+ if (Addi91u_into_Adapter_table(wBIOS,
+ (pDev->base_address[0] & 0xFFFE),
+ pDev->irq,
+ pDev->bus->number,
+ (pDev->devfn >> 3)
+ ) == 0)
+ iAdapters++;
+ }
+ while ((pDev = pci_find_device(INI_VENDOR_ID, I935_DEVICE_ID, pDev)) != NULL) {
+ pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
+ wBIOS = (UWORD) (dRegValue & 0xFF);
+ if (((dRegValue & 0xFF00) >> 8) == 0xFF)
+ dRegValue = 0;
+ wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8));
+ if (Addi91u_into_Adapter_table(wBIOS,
+ (pDev->base_address[0] & 0xFFFE),
+ pDev->irq,
+ pDev->bus->number,
+ (pDev->devfn >> 3)
+ ) == 0)
+ iAdapters++;
+ }
+ while ((pDev = pci_find_device(INI_VENDOR_ID, 0x0002, pDev)) != NULL) {
+ pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
+ wBIOS = (UWORD) (dRegValue & 0xFF);
+ if (((dRegValue & 0xFF00) >> 8) == 0xFF)
+ dRegValue = 0;
+ wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8));
+ if (Addi91u_into_Adapter_table(wBIOS,
+ (pDev->base_address[0] & 0xFFFE),
+ pDev->irq,
+ pDev->bus->number,
+ (pDev->devfn >> 3)
+ ) == 0)
+ iAdapters++;
+ }
+
+ return (iAdapters);
+}
+
+#else /* <01> */
+
+/*****************************************************************************
+ Function name : tul_ReturnNumberOfAdapters
+ Description : This function will scan PCI bus to get all Orchid card
+ Input : None.
+ Output : None.
+ Return : SUCCESSFUL - Successful scan
+ ohterwise - No drives founded
+*****************************************************************************/
+int tul_ReturnNumberOfAdapters(void)
+{
+ unsigned int i, iAdapters;
+ unsigned int dRegValue;
+ unsigned short command;
+ WORD wBIOS, wBASE;
+ BYTE bPCIBusNum, bInterrupt, bPCIDeviceNum;
+ struct {
+ unsigned short vendor_id;
+ unsigned short device_id;
+ } const i91u_pci_devices[] =
+ {
+ {INI_VENDOR_ID, I935_DEVICE_ID},
+ {INI_VENDOR_ID, I940_DEVICE_ID},
+ {INI_VENDOR_ID, I950_DEVICE_ID},
+ {INI_VENDOR_ID, I920_DEVICE_ID}
+ };
+
+
+ iAdapters = 0;
+ /*
+ * PCI-bus probe.
+ */
+ if (pcibios_present()) {
+#ifdef MMAPIO
+ unsigned long page_offset, base;
+#endif
+
+#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92)
+ struct pci_dev *pdev = NULL;
+#else
+ int index;
+ unsigned char pci_bus, pci_devfn;
+#endif
+
+ bPCIBusNum = 0;
+ bPCIDeviceNum = 0;
+ init_i91uAdapter_table();
+ for (i = 0; i < TULSZ(i91u_pci_devices); i++) {
+#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92)
+ pdev = NULL;
+ while ((pdev = pci_find_device(i91u_pci_devices[i].vendor_id,
+ i91u_pci_devices[i].device_id,
+ pdev)))
+#else
+ index = 0;
+ while (!(pcibios_find_device(i91u_pci_devices[i].vendor_id,
+ i91u_pci_devices[i].device_id,
+ index++, &pci_bus, &pci_devfn)))
+#endif
+ {
+ if (i == 0) {
+ /*
+ printk("i91u: The RAID controller is not supported by\n");
+ printk("i91u: this driver, we are ignoring it.\n");
+ */
+ } else {
+ /*
+ * Read sundry information from PCI BIOS.
+ */
+#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92)
+ bPCIBusNum = pdev->bus->number;
+ bPCIDeviceNum = pdev->devfn;
+ dRegValue = pdev->base_address[0];
+ if (dRegValue == -1) { /* Check return code */
+ printk("\n\ri91u: tulip read configuration error.\n");
+ return (0); /* Read configuration space error */
+ }
+ /* <02> read from base address + 0x50 offset to get the wBIOS balue. */
+ wBASE = (WORD) dRegValue;
+
+ /* Now read the interrupt line */
+ dRegValue = pdev->irq;
+ bInterrupt = dRegValue & 0xFF; /* Assign interrupt line */
+ pci_read_config_word(pdev, PCI_COMMAND, &command);
+ pci_write_config_word(pdev, PCI_COMMAND,
+ command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+
+#else
+ bPCIBusNum = pci_bus;
+ bPCIDeviceNum = pci_devfn;
+ pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
+ &dRegValue);
+ if (dRegValue == -1) { /* Check return code */
+ printk("\n\ri91u: tulip read configuration error.\n");
+ return (0); /* Read configuration space error */
+ }
+ /* <02> read from base address + 0x50 offset to get the wBIOS balue. */
+ wBASE = (WORD) dRegValue;
+
+ /* Now read the interrupt line */
+ pcibios_read_config_dword(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
+ &dRegValue);
+ bInterrupt = dRegValue & 0xFF; /* Assign interrupt line */
+ pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command);
+ pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND,
+ command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+#endif
+ wBASE &= PCI_BASE_ADDRESS_IO_MASK;
+ wBIOS = TUL_RDWORD(wBASE, 0x50);
+
+#ifdef MMAPIO
+ base = wBASE & PAGE_MASK;
+ page_offset = wBASE - base;
+
+ /*
+ * replace the next line with this one if you are using 2.1.x:
+ * temp_p->maddr = ioremap(base, page_offset + 256);
+ */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,0)
+ wBASE = ioremap(base, page_offset + 256);
+#else
+ wBASE = (WORD) vremap(base, page_offset + 256);
+#endif
+ if (wBASE) {
+ wBASE += page_offset;
+ }
+#endif
+
+ if (Addi91u_into_Adapter_table(wBIOS, wBASE, bInterrupt, bPCIBusNum,
+ bPCIDeviceNum) == 0x0)
+ iAdapters++;
+ }
+ } /* while(pdev=....) */
+ } /* for PCI_DEVICES */
+ } /* PCI BIOS present */
+ return (iAdapters);
+}
+#endif
+
+int i91u_detect(Scsi_Host_Template * tpnt)
+{
+ SCB *pSCB;
+ HCS *pHCB;
+ struct Scsi_Host *hreg;
+ unsigned long i; /* 01/14/98 */
+ int ok = 0, iAdapters;
+ ULONG dBiosAdr;
+ BYTE *pbBiosAdr;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ tpnt->proc_dir = &proc_scsi_ini9100u;
+#endif
+
+ if (setup_called) { /* Setup by i91u_setup */
+ printk("i91u: processing commandline: ");
+
+#ifdef DEBUG_i91u
+ if (setup_called > 1) {
+ printk("\ni91u: %s\n", setup_str);
+ printk("i91u: usage: i91u[=<DEBUG>]\n");
+ i91u_panic("i91u panics in line %d", __LINE__);
+ }
+ i91u_debug = setup_debug;
+#endif
+ }
+ /* Get total number of adapters in the motherboard */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93)
+#ifdef CONFIG_PCI
+ iAdapters = tul_NewReturnNumberOfAdapters();
+#else
+ iAdapters = tul_ReturnNumberOfAdapters();
+#endif
+#else
+ iAdapters = tul_ReturnNumberOfAdapters();
+#endif
+
+ if (iAdapters == 0) /* If no tulip founded, return */
+ return (0);
+
+ tul_num_ch = (iAdapters > tul_num_ch) ? tul_num_ch : iAdapters;
+ /* Update actually channel number */
+ if (tul_tag_enable) { /* 1.01i */
+ tul_num_scb = MAX_TARGETS * i91u_MAXQUEUE;
+ } else {
+ tul_num_scb = MAX_TARGETS + 3; /* 1-tape, 1-CD_ROM, 1- extra */
+ } /* Update actually SCBs per adapter */
+
+ /* Get total memory needed for HCS */
+ i = tul_num_ch * sizeof(HCS);
+ memset((unsigned char *) &tul_hcs[0], 0, i); /* Initialize tul_hcs 0 */
+ /* Get total memory needed for SCB */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ for (; tul_num_scb >= MAX_TARGETS + 3; tul_num_scb--) {
+ i = tul_num_ch * tul_num_scb * sizeof(SCB);
+ if ((tul_scb = (SCB *) kmalloc(i, GFP_ATOMIC | GFP_DMA)) != NULL)
+ break;
+ }
+#else
+ i = tul_num_ch * tul_num_scb * sizeof(SCB);
+ tul_scb = (SCB *) scsi_init_malloc(i, GFP_ATOMIC | GFP_DMA);
+#endif
+ if (tul_scb == NULL) {
+ printk("i91u: SCB memory allocation error\n");
+ return (0);
+ }
+ memset((unsigned char *) tul_scb, 0, i);
+
+ pSCB = tul_scb;
+ for (i = 0; i < tul_num_ch * tul_num_scb; i++, pSCB++) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ pSCB->SCB_SGPAddr = (U32) VIRT_TO_BUS(&pSCB->SCB_SGList[0]);
+#else
+ pSCB->SCB_SGPAddr = (U32) (&pSCB->SCB_SGList[0]);
+#endif
+ }
+
+ for (i = 0, pHCB = &tul_hcs[0]; /* Get pointer for control block */
+ i < tul_num_ch;
+ i++, pHCB++) {
+ pHCB->pSRB_head = NULL; /* Initial SRB save queue */
+ pHCB->pSRB_tail = NULL; /* Initial SRB save queue */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ pHCB->pSRB_lock = SPIN_LOCK_UNLOCKED; /* SRB save queue lock */
+#endif
+ request_region(pHCB->HCS_Base, 0x100, "i91u"); /* Register */
+
+ get_tulipPCIConfig(pHCB, i);
+
+ dBiosAdr = pHCB->HCS_BIOS;
+ dBiosAdr = (dBiosAdr << 4);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ pbBiosAdr = phys_to_virt(dBiosAdr);
+#endif
+
+ init_tulip(pHCB, tul_scb + (i * tul_num_scb), tul_num_scb, pbBiosAdr, 10);
+ pHCB->HCS_Index = i; /* 7/29/98 */
+ hreg = scsi_register(tpnt, sizeof(HCS));
+ hreg->io_port = pHCB->HCS_Base;
+ hreg->n_io_port = 0xff;
+ hreg->can_queue = tul_num_scb; /* 03/05/98 */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ hreg->unique_id = pHCB->HCS_Base;
+ hreg->max_id = pHCB->HCS_MaxTar;
+#endif
+ hreg->max_lun = 32; /* 10/21/97 */
+ hreg->irq = pHCB->HCS_Intr;
+ hreg->this_id = pHCB->HCS_SCSI_ID; /* Assign HCS index */
+ hreg->base = (UCHAR *) pHCB;
+ hreg->sg_tablesize = TOTAL_SG_ENTRY; /* Maximun support is 32 */
+
+ /* Initial tulip chip */
+ switch (i) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ case 0:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr0, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ case 1:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr1, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ case 2:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr2, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ case 3:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr3, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ case 4:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr4, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ case 5:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr5, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ case 6:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr6, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ case 7:
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr7, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+ break;
+ default:
+ i91u_panic("i91u: Too many host adapters\n");
+ break;
+ }
+ if (ok < 0) {
+ if (ok == -EINVAL) {
+ printk("i91u: bad IRQ %d.\n", pHCB->HCS_Intr);
+ printk(" Contact author.\n");
+ } else if (ok == -EBUSY)
+ printk("i91u: IRQ %d already in use. Configure another.\n",
+ pHCB->HCS_Intr);
+ else {
+ printk("\ni91u: Unexpected error code on requesting IRQ %d.\n",
+ pHCB->HCS_Intr);
+ printk(" Contact author.\n");
+ }
+ i91u_panic("i91u: driver needs an IRQ.\n");
+ }
+#endif
+ }
+
+ tpnt->this_id = -1;
+ tpnt->can_queue = 1;
+
+ return 1;
+}
+
+static void i91uBuildSCB(HCS * pHCB, SCB * pSCB, Scsi_Cmnd * SCpnt)
+{ /* Create corresponding SCB */
+ struct scatterlist *pSrbSG;
+ SG *pSG; /* Pointer to SG list */
+ int i;
+ long TotalLen;
+
+ pSCB->SCB_Post = i91uSCBPost; /* i91u's callback routine */
+ pSCB->SCB_Srb = SCpnt;
+ pSCB->SCB_Opcode = ExecSCSI;
+ pSCB->SCB_Flags = SCF_POST; /* After SCSI done, call post routine */
+ pSCB->SCB_Target = SCpnt->target;
+ pSCB->SCB_Lun = SCpnt->lun;
+ pSCB->SCB_Ident = SCpnt->lun | DISC_ALLOW;
+ pSCB->SCB_Flags |= SCF_SENSE; /* Turn on auto request sense */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ pSCB->SCB_SensePtr = (U32) VIRT_TO_BUS(SCpnt->sense_buffer);
+#else
+ pSCB->SCB_SensePtr = (U32) (SCpnt->sense_buffer);
+#endif
+
+ pSCB->SCB_SenseLen = SENSE_SIZE;
+
+ pSCB->SCB_CDBLen = SCpnt->cmd_len;
+ pSCB->SCB_HaStat = 0;
+ pSCB->SCB_TaStat = 0;
+ memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, SCpnt->cmd_len);
+
+ if (SCpnt->device->tagged_supported) { /* Tag Support */
+ pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG; /* Do simple tag only */
+ } else {
+ pSCB->SCB_TagMsg = 0; /* No tag support */
+ }
+
+ if (SCpnt->use_sg) {
+ pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
+ if (SCpnt->use_sg == 1) { /* If only one entry in the list *//* treat it as regular I/O */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ pSCB->SCB_BufPtr = (U32) VIRT_TO_BUS(pSrbSG->address);
+#else
+ pSCB->SCB_BufPtr = (U32) (pSrbSG->address);
+#endif
+ TotalLen = pSrbSG->length;
+ pSCB->SCB_SGLen = 0;
+ } else { /* Assign SG physical address */
+ pSCB->SCB_BufPtr = pSCB->SCB_SGPAddr;
+ pSCB->SCB_Flags |= SCF_SG; /* Turn on SG list flag */
+ for (i = 0, TotalLen = 0, pSG = &pSCB->SCB_SGList[0]; /* 1.01g */
+ i < SCpnt->use_sg;
+ i++, pSG++, pSrbSG++) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ pSG->SG_Ptr = (U32) VIRT_TO_BUS(pSrbSG->address);
+#else
+ pSG->SG_Ptr = (U32) (pSrbSG->address);
+#endif
+ TotalLen += pSG->SG_Len = pSrbSG->length;
+ }
+ pSCB->SCB_SGLen = i;
+ }
+ pSCB->SCB_BufLen = (SCpnt->request_bufflen > TotalLen) ?
+ TotalLen : SCpnt->request_bufflen;
+ } else { /* Non SG */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+ pSCB->SCB_BufPtr = (U32) VIRT_TO_BUS(SCpnt->request_buffer);
+#else
+ pSCB->SCB_BufPtr = (U32) (SCpnt->request_buffer);
+#endif
+ pSCB->SCB_BufLen = SCpnt->request_bufflen;
+ pSCB->SCB_SGLen = 0;
+ }
+
+ return;
+}
+
+/*
+ * Queue a command and setup interrupts for a free bus.
+ */
+int i91u_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+ register SCB *pSCB;
+ HCS *pHCB; /* Point to Host adapter control block */
+
+ if (SCpnt->lun > 16) { /* 07/22/98 */
+
+ SCpnt->result = (DID_TIME_OUT << 16);
+ done(SCpnt); /* Notify system DONE */
+ return (0);
+ }
+ pHCB = (HCS *) SCpnt->host->base;
+
+ SCpnt->scsi_done = done;
+ /* Get free SCSI control block */
+ if ((pSCB = tul_alloc_scb(pHCB)) == NULL) {
+ i91uAppendSRBToQueue(pHCB, SCpnt); /* Buffer this request */
+ return (0);
+ }
+ i91uBuildSCB(pHCB, pSCB, SCpnt);
+ tul_exec_scb(pHCB, pSCB); /* Start execute SCB */
+ return (0);
+}
+
+/*
+ * We only support command in interrupt-driven fashion
+ */
+int i91u_command(Scsi_Cmnd * SCpnt)
+{
+ printk("i91u: interrupt driven driver; use i91u_queue()\n");
+ return -1;
+}
+
+/*
+ * Abort a queued command
+ * (commands that are on the bus can't be aborted easily)
+ */
+int i91u_abort(Scsi_Cmnd * SCpnt)
+{
+ HCS *pHCB;
+
+ pHCB = (HCS *) SCpnt->host->base;
+ return tul_abort_srb(pHCB, SCpnt);
+}
+
+/*
+ * Reset registers, reset a hanging bus and
+ * kill active and disconnected commands for target w/o soft reset
+ */
+int i91u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{ /* I need Host Control Block Information */
+ HCS *pHCB;
+
+ pHCB = (HCS *) SCpnt->host->base;
+
+ if (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
+ return tul_reset_scsi_bus(pHCB);
+ else
+ return tul_device_reset(pHCB, (ULONG) SCpnt, SCpnt->target, reset_flags);
+}
+
+/*
+ * Return the "logical geometry"
+ */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+int i91u_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array)
+#else
+int i91u_biosparam(Scsi_Disk * disk, int dev, int *info_array)
+#endif
+{
+ HCS *pHcb; /* Point to Host adapter control block */
+ TCS *pTcb;
+
+ pHcb = (HCS *) disk->device->host->base;
+ pTcb = &pHcb->HCS_Tcs[disk->device->id];
+
+ if (pTcb->TCS_DrvHead) {
+ info_array[0] = pTcb->TCS_DrvHead;
+ info_array[1] = pTcb->TCS_DrvSector;
+ info_array[2] = disk->capacity / pTcb->TCS_DrvHead / pTcb->TCS_DrvSector;
+ } else {
+ if (pTcb->TCS_DrvFlags & TCF_DRV_255_63) {
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = disk->capacity / 255 / 63;
+ } else {
+ info_array[0] = 64;
+ info_array[1] = 32;
+ info_array[2] = disk->capacity >> 11;
+ }
+ }
+
+#if defined(DEBUG_BIOSPARAM)
+ if (i91u_debug & debug_biosparam) {
+ printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
+ info_array[0], info_array[1], info_array[2]);
+ printk("WARNING: check, if the bios geometry is correct.\n");
+ }
+#endif
+
+ return 0;
+}
+
+/*****************************************************************************
+ Function name : i91uSCBPost
+ Description : This is callback routine be called when tulip finish one
+ SCSI command.
+ Input : pHCB - Pointer to host adapter control block.
+ pSCB - Pointer to SCSI control block.
+ Output : None.
+ Return : None.
+*****************************************************************************/
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb)
+{
+ Scsi_Cmnd *pSRB; /* Pointer to SCSI request block */
+ HCS *pHCB;
+ SCB *pSCB;
+
+ pHCB = (HCS *) pHcb;
+ pSCB = (SCB *) pScb;
+ if ((pSRB = pSCB->SCB_Srb) == 0) {
+ printk("i91uSCBPost: SRB pointer is empty\n");
+
+ tul_release_scb(pHCB, pSCB); /* Release SCB for current channel */
+ return;
+ }
+ switch (pSCB->SCB_HaStat) {
+ case 0x0:
+ case 0xa: /* Linked command complete without error and linked normally */
+ case 0xb: /* Linked command complete without error interrupt generated */
+ pSCB->SCB_HaStat = 0;
+ break;
+
+ case 0x11: /* Selection time out-The initiator selection or target
+ reselection was not complete within the SCSI Time out period */
+ pSCB->SCB_HaStat = DID_TIME_OUT;
+ break;
+
+ case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
+ phase sequence was requested by the target. The host adapter
+ will generate a SCSI Reset Condition, notifying the host with
+ a SCRD interrupt */
+ pSCB->SCB_HaStat = DID_RESET;
+ break;
+
+ case 0x1a: /* SCB Aborted. 07/21/98 */
+ pSCB->SCB_HaStat = DID_ABORT;
+ break;
+
+ case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
+ than was allocated by the Data Length field or the sum of the
+ Scatter / Gather Data Length fields. */
+ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+ case 0x16: /* Invalid SCB Operation Code. */
+
+ default:
+ printk("ini9100u: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat);
+ pSCB->SCB_HaStat = DID_ERROR; /* Couldn't find any better */
+ break;
+ }
+
+ pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16);
+
+ if (pSRB == NULL) {
+ printk("pSRB is NULL\n");
+ }
+ pSRB->scsi_done(pSRB); /* Notify system DONE */
+ if ((pSRB = i91uPopSRBFromQueue(pHCB)) != NULL)
+ /* Find the next pending SRB */
+ { /* Assume resend will success */
+ /* Reuse old SCB */
+ i91uBuildSCB(pHCB, pSCB, pSRB); /* Create corresponding SCB */
+
+ tul_exec_scb(pHCB, pSCB); /* Start execute SCB */
+ } else { /* No Pending SRB */
+ tul_release_scb(pHCB, pSCB); /* Release SCB for current channel */
+ }
+ return;
+}
+
+/*
+ * Interrupts handler (main routine of the driver)
+ */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr0(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr0(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[0].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[0]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr1(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr1(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[1].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[1]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr2(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr2(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[2].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[2]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr3(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr3(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[3].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[3]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr4(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr4(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[4].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[4]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr5(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr5(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[5].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[5]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr6(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr6(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[6].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[6]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr7(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr7(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ unsigned long flags;
+#endif
+
+ if (tul_hcs[7].HCS_Intr != irqno)
+ return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ tul_isr(&tul_hcs[7]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+/*
+ * Dump the current driver status and panic...
+ */
+static void i91u_panic(char *msg)
+{
+ printk("\ni91u_panic: %s\n", msg);
+ panic("i91u panic");
+}
--- /dev/null
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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
+ * 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
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *************************************************************************
+ *
+ * Module: ini9100u.h
+ * Description: INI-9100U/UW LINUX device driver header
+ * Revision History:
+ * 06/18/96 Harry Chen, Initial Version 1.00A (Beta)
+ * 06/23/98 hc - v1.01k
+ * - Get it work for kernel version >= 2.1.75
+ * 12/09/98 bv - v1.03a
+ * - Removed unused code
+ * 12/13/98 bv - v1.03b
+ * - Add spinlocks to HCS structure.
+*******************************************************************************/
+
+#ifndef CVT_LINUX_VERSION
+#define CVT_LINUX_VERSION(V,P,S) (((V) * 65536) + ((P) * 256) + (S))
+#endif
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#include "sd.h"
+
+extern int i91u_detect(Scsi_Host_Template *);
+extern int i91u_command(Scsi_Cmnd *);
+extern int i91u_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+extern int i91u_abort(Scsi_Cmnd *);
+extern int i91u_reset(Scsi_Cmnd *, unsigned int);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1, 3, 0)
+extern int i91u_biosparam(Scsi_Disk *, kdev_t, int *); /*for linux v2.0 */
+extern struct proc_dir_entry proc_scsi_ini9100u;
+#else
+extern int i91u_biosparam(Disk *, int, int *); /*for linux v1.13 */
+#endif
+
+#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.03b"
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(1, 3, 0)
+#define INI9100U { \
+ NULL, \
+ NULL, \
+ i91u_REVID, \
+ i91u_detect, \
+ NULL, \
+ NULL, \
+ i91u_command, \
+ i91u_queue, \
+ i91u_abort, \
+ i91u_reset, \
+ NULL, \
+ i91u_biosparam, \
+ 1, \
+ 7, \
+ SG_ALL, \
+ 1, \
+ 0, \
+ 0, \
+ ENABLE_CLUSTERING \
+}
+#else
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2, 1, 75)
+#define INI9100U { \
+ NULL, \
+ NULL, \
+ &proc_scsi_ini9100u, \
+ NULL, \
+ i91u_REVID, \
+ i91u_detect, \
+ NULL, \
+ NULL, \
+ i91u_command, \
+ i91u_queue, \
+ i91u_abort, \
+ i91u_reset, \
+ NULL, \
+ i91u_biosparam, \
+ 1, \
+ 7, \
+ SG_ALL, \
+ 1, \
+ 0, \
+ 0, \
+ ENABLE_CLUSTERING \
+}
+#else /* Version >= 2.1.75 */
+#define INI9100U { \
+ next: NULL, \
+ module: NULL, \
+ proc_dir: &proc_scsi_ini9100u, \
+ proc_info: NULL, \
+ name: i91u_REVID, \
+ detect: i91u_detect, \
+ release: NULL, \
+ info: NULL, \
+ command: i91u_command, \
+ queuecommand: i91u_queue, \
+ eh_strategy_handler: NULL, \
+ eh_abort_handler: NULL, \
+ eh_device_reset_handler: NULL, \
+ eh_bus_reset_handler: NULL, \
+ eh_host_reset_handler: NULL, \
+ abort: i91u_abort, \
+ reset: i91u_reset, \
+ slave_attach: NULL, \
+ bios_param: i91u_biosparam, \
+ can_queue: 1, \
+ this_id: 1, \
+ sg_tablesize: SG_ALL, \
+ cmd_per_lun: 1, \
+ present: 0, \
+ unchecked_isa_dma: 0, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 0 \
+}
+#endif
+#endif
+
+
+#define VIRT_TO_BUS(i) (unsigned int) virt_to_bus((void *)(i))
+#define ULONG unsigned long
+#define USHORT unsigned short
+#define UCHAR unsigned char
+#define BYTE unsigned char
+#define WORD unsigned short
+#define DWORD unsigned long
+#define UBYTE unsigned char
+#define UWORD unsigned short
+#define UDWORD unsigned long
+#ifdef ALPHA
+#define U32 unsigned int
+#else
+#define U32 unsigned long
+#endif
+
+#ifndef NULL
+#define NULL 0 /* zero */
+#endif
+#ifndef TRUE
+#define TRUE (1) /* boolean true */
+#endif
+#ifndef FALSE
+#define FALSE (0) /* boolean false */
+#endif
+#ifndef FAILURE
+#define FAILURE (-1)
+#endif
+
+#define i91u_MAXQUEUE 2
+#define TOTAL_SG_ENTRY 32
+#define MAX_TARGETS 16
+#define SENSE_SIZE 14
+
+#define INI_VENDOR_ID 0x1101 /* Initio's PCI vendor ID */
+#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */
+#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */
+#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */
+#define I920_DEVICE_ID 0x0002 /* Initio's other product ID */
+
+/************************************************************************/
+/* Scatter-Gather Element Structure */
+/************************************************************************/
+typedef struct SG_Struc {
+ U32 SG_Ptr; /* Data Pointer */
+ U32 SG_Len; /* Data Length */
+} SG;
+
+/***********************************************************************
+ SCSI Control Block
+************************************************************************/
+typedef struct Scsi_Ctrl_Blk {
+ U32 SCB_InitioReserved[9]; /* 0 */
+
+ UBYTE SCB_Opcode; /*24 SCB command code */
+ UBYTE SCB_Flags; /*25 SCB Flags */
+ UBYTE SCB_Target; /*26 Target Id */
+ UBYTE SCB_Lun; /*27 Lun */
+ U32 SCB_BufPtr; /*28 Data Buffer Pointer */
+ U32 SCB_BufLen; /*2C Data Allocation Length */
+ UBYTE SCB_SGLen; /*30 SG list # */
+ UBYTE SCB_SenseLen; /*31 Sense Allocation Length */
+ UBYTE SCB_HaStat; /*32 */
+ UBYTE SCB_TaStat; /*33 */
+ UBYTE SCB_CDBLen; /*34 CDB Length */
+ UBYTE SCB_Ident; /*35 Identify */
+ UBYTE SCB_TagMsg; /*36 Tag Message */
+ UBYTE SCB_TagId; /*37 Queue Tag */
+ UBYTE SCB_CDB[12]; /*38 */
+ U32 SCB_SGPAddr; /*44 SG List/Sense Buf phy. Addr. */
+ U32 SCB_SensePtr; /*48 Sense data pointer */
+ void (*SCB_Post) (BYTE *, BYTE *); /*4C POST routine */
+ Scsi_Cmnd *SCB_Srb; /*50 SRB Pointer */
+ SG SCB_SGList[TOTAL_SG_ENTRY]; /*54 Start of SG list */
+} SCB;
+
+/* Opcodes of SCB_Opcode */
+#define ExecSCSI 0x1
+#define BusDevRst 0x2
+#define AbortCmd 0x3
+
+/* Bit Definition for SCB_Flags */
+#define SCF_DONE 0x01
+#define SCF_POST 0x02
+#define SCF_SENSE 0x04
+#define SCF_DIR 0x18
+#define SCF_NO_DCHK 0x00
+#define SCF_DIN 0x08
+#define SCF_DOUT 0x10
+#define SCF_NO_XF 0x18
+#define SCF_POLL 0x40
+#define SCF_SG 0x80
+
+/* Error Codes for SCB_HaStat */
+#define HOST_SEL_TOUT 0x11
+#define HOST_DO_DU 0x12
+#define HOST_BUS_FREE 0x13
+#define HOST_BAD_PHAS 0x14
+#define HOST_INV_CMD 0x16
+#define HOST_SCSI_RST 0x1B
+#define HOST_DEV_RST 0x1C
+
+/* Error Codes for SCB_TaStat */
+#define TARGET_CHKCOND 0x02
+#define TARGET_BUSY 0x08
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG 0x20
+#define MSG_HTAG 0x21
+#define MSG_OTAG 0x22
+
+/***********************************************************************
+ Target Device Control Structure
+**********************************************************************/
+
+typedef struct Tar_Ctrl_Struc {
+ ULONG TCS_InitioReserved; /* 0 */
+
+ UWORD TCS_DrvFlags; /* 4 */
+ UBYTE TCS_DrvHead; /* 6 */
+ UBYTE TCS_DrvSector; /* 7 */
+} TCS;
+
+/***********************************************************************
+ Target Device Control Structure
+**********************************************************************/
+/* Bit Definition for TCF_DrvFlags */
+#define TCF_DRV_255_63 0x0400
+
+/***********************************************************************
+ Host Adapter Control Structure
+************************************************************************/
+typedef struct Ha_Ctrl_Struc {
+ UWORD HCS_Base; /* 00 */
+ UWORD HCS_BIOS; /* 02 */
+ UBYTE HCS_Intr; /* 04 */
+ UBYTE HCS_SCSI_ID; /* 05 */
+ UBYTE HCS_MaxTar; /* 06 */
+ UBYTE HCS_NumScbs; /* 07 */
+
+ UBYTE HCS_Flags; /* 08 */
+ UBYTE HCS_Index; /* 09 */
+ UBYTE HCS_Reserved[2]; /* 0a */
+ ULONG HCS_InitioReserved[27]; /* 0C */
+ TCS HCS_Tcs[16]; /* 78 -> 16 Targets */
+ Scsi_Cmnd *pSRB_head; /* SRB save queue header */
+ Scsi_Cmnd *pSRB_tail; /* SRB save queue tail */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+ spinlock_t HCS_AvailLock;
+ spinlock_t HCS_SemaphLock;
+ spinlock_t pSRB_lock;
+#endif
+} HCS;
+
+/* Bit Definition for HCB_Flags */
+#define HCF_EXPECT_RESET 0x10
+
+/* SCSI related definition */
+#define DISC_NOT_ALLOW 0x80 /* Disconnect is not allowed */
+#define DISC_ALLOW 0xC0 /* Disconnect is allowed */
--- /dev/null
+/*
+ * 68k mac 53c9[46] scsi driver
+ *
+ * copyright (c) 1998, David Weis weisd3458@uni.edu
+ *
+ * debugging on Quadra 800 and 660AV Michael Schmitz, Dave Kilzer 7/98
+ *
+ * based loosely on cyber_esp.c
+ */
+
+/* these are unused for now */
+#define myreadl(addr) (*(volatile unsigned int *) (addr))
+#define mywritel(b, addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "NCR53C9x.h"
+#include "mac_esp.h"
+
+#include "../../arch/m68k/mac/via6522.h" /* huh? */
+
+#include <asm/io.h>
+
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+
+#include <asm/pgtable.h>
+
+#include <asm/macintosh.h>
+
+extern inline void esp_handle(struct NCR_ESP *esp);
+extern void mac_esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
+
+static int dma_bytes_sent(struct NCR_ESP * esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP * esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP * esp);
+static void dma_init_read(struct NCR_ESP * esp, char * vaddress, int length);
+static void dma_init_write(struct NCR_ESP * esp, char * vaddress, int length);
+static void dma_ints_off(struct NCR_ESP * esp);
+static void dma_ints_on(struct NCR_ESP * esp);
+static int dma_irq_p(struct NCR_ESP * esp);
+static int dma_irq_p_quick(struct NCR_ESP * esp);
+static void dma_led_off(struct NCR_ESP * esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP * esp, __u32 addr, int count, int write);
+static void dma_setup_quick(struct NCR_ESP * esp, __u32 addr, int count, int write);
+
+
+static int esp_dafb_dma_irq_p(struct NCR_ESP * espdev);
+static int esp_iosb_dma_irq_p(struct NCR_ESP * espdev);
+
+static int esp_initialized = 0;
+
+static int setup_num_esps = -1;
+static int setup_disconnect = -1;
+static int setup_nosync = -1;
+static int setup_can_queue = -1;
+static int setup_cmd_per_lun = -1;
+static int setup_sg_tablesize = -1;
+#ifdef SUPPORT_TAGS
+static int setup_use_tagged_queuing = -1;
+#endif
+static int setup_hostid = -1;
+
+/*
+ * Experimental ESP inthandler; check macints.c to make sure dev_id is
+ * set up properly!
+ */
+
+void mac_esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *) dev_id;
+ int irq_p = 0;
+
+ /* Handle the one ESP interrupt showing at this IRQ level. */
+ if(((esp)->irq & 0xff) == irq) {
+ /*
+ * Debug ..
+ */
+ irq_p = esp->dma_irq_p(esp);
+ printk("mac_esp: irq_p %x current %p disconnected %p\n",
+ irq_p, esp->current_SC, esp->disconnected_SC);
+
+ /*
+ * Mac: if we're here, it's an ESP interrupt for sure!
+ */
+ if((esp->current_SC || esp->disconnected_SC)) {
+ esp->dma_ints_off(esp);
+
+ ESPIRQ(("I%d(", esp->esp_id));
+ esp_handle(esp);
+ ESPIRQ((")"));
+
+ esp->dma_ints_on(esp);
+ }
+ }
+}
+
+/*
+ * Debug hooks; use for playing with the interrupt flag testing and interrupt
+ * acknowledge on the various machines
+ */
+
+void scsi_esp_polled(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ if (esp_initialized == 0)
+ return;
+
+ mac_esp_intr(irq, dev_id, pregs);
+}
+
+void fake_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: got irq\n");
+#endif
+
+ mac_esp_intr(irq, dev_id, pregs);
+}
+
+void fake_drq(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ printk("mac_esp: got drq\n");
+}
+
+#define DRIVER_SETUP
+
+/*
+ * Function : mac_scsi_setup(char *str, int *ints)
+ *
+ * Purpose : booter command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ * Currently unused in the new driver; need to add settable parameters to the
+ * detect function.
+ *
+ */
+
+void mac_esp_setup(char *str, int *ints) {
+#ifdef DRIVER_SETUP
+ /* Format of mac53c9x parameter is:
+ * mac53c9x=<num_esps>,<disconnect>,<nosync>,<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>
+ * Negative values mean don't change.
+ */
+
+ /* Grmbl... the standard parameter parsing can't handle negative numbers
+ * :-( So let's do it ourselves!
+ */
+
+ int i = ints[0]+1, fact;
+
+ while( str && (isdigit(*str) || *str == '-') && i <= 10) {
+ if (*str == '-')
+ fact = -1, ++str;
+ else
+ fact = 1;
+ ints[i++] = simple_strtoul( str, NULL, 0 ) * fact;
+ if ((str = strchr( str, ',' )) != NULL)
+ ++str;
+ }
+ ints[0] = i-1;
+
+ if (ints[0] < 1) {
+ printk( "mac_esp_setup: no arguments!\n" );
+ return;
+ }
+
+ if (ints[0] >= 1) {
+ if (ints[1] > 0)
+ /* no limits on this, just > 0 */
+ if (ints[1] >= 0 && ints[1] <= 2)
+ setup_num_esps = ints[1];
+ else if (ints[1] > 2)
+ printk( "mac_esp_setup: invalid number of hosts %d !\n", ints[1] );
+ }
+ if (ints[0] >= 2) {
+ if (ints[2] > 0)
+ setup_disconnect = ints[2];
+ }
+ if (ints[0] >= 3) {
+ if (ints[3] >= 0) {
+ setup_nosync = ints[3];
+ }
+ }
+ if (ints[0] >= 4) {
+ if (ints[4] > 0)
+ /* no limits on this, just > 0 */
+ setup_can_queue = ints[4];
+ }
+ if (ints[0] >= 5) {
+ if (ints[5] > 0)
+ setup_cmd_per_lun = ints[5];
+ }
+ if (ints[0] >= 6) {
+ if (ints[6] >= 0) {
+ setup_sg_tablesize = ints[6];
+ /* Must be <= SG_ALL (255) */
+ if (setup_sg_tablesize > SG_ALL)
+ setup_sg_tablesize = SG_ALL;
+ }
+ }
+ if (ints[0] >= 7) {
+ /* Must be between 0 and 7 */
+ if (ints[7] >= 0 && ints[7] <= 7)
+ setup_hostid = ints[7];
+ else if (ints[7] > 7)
+ printk( "mac_esp_setup: invalid host ID %d !\n", ints[7] );
+ }
+#ifdef SUPPORT_TAGS
+ if (ints[0] >= 8) {
+ if (ints[8] >= 0)
+ setup_use_tagged_queuing = !!ints[8];
+ }
+#endif
+#endif
+}
+
+/*
+ * ESP address 'detection'
+ */
+
+unsigned long get_base(int chip_num)
+{
+ /*
+ * using the chip_num and mac model, figure out where the
+ * chips are mapped
+ */
+
+ unsigned long io_base = 0x50f00000;
+ unsigned int second_offset = 0x402;
+ unsigned long scsi_loc = 0;
+
+ switch (macintosh_config->scsi_type) {
+
+ /* 950, 900, 700 */
+ case MAC_SCSI_QUADRA2:
+ scsi_loc = io_base + 0xf000 + ((chip_num == 0) ? 0 : second_offset);
+ break;
+
+ /* av's */
+ case MAC_SCSI_QUADRA3:
+ scsi_loc = io_base + 0x18000 + ((chip_num == 0) ? 0 : second_offset);
+ break;
+
+ /* most quadra/centris models are like this */
+ case MAC_SCSI_QUADRA:
+ scsi_loc = io_base + 0x10000;
+ break;
+
+ default:
+ printk("mac_esp: get_base: hit default!\n");
+ scsi_loc = io_base + 0x10000;
+ break;
+
+ } /* switch */
+
+ printk("mac_esp: io base at 0x%lx\n", scsi_loc);
+
+ return scsi_loc;
+}
+
+/*
+ * Model dependent ESP setup
+ */
+
+int mac_esp_detect(Scsi_Host_Template * tpnt)
+{
+ int quick = 0;
+ int chipnum, chipspresent = 0;
+#if 0
+ unsigned long timeout;
+#endif
+
+ /* what do we have in this machine... */
+ if (MACHW_PRESENT(MAC_SCSI_96)) {
+ chipspresent ++;
+ }
+
+ if (MACHW_PRESENT(MAC_SCSI_96_2)) {
+ chipspresent ++;
+ }
+
+ /* number of ESPs present ? */
+ if (setup_num_esps >= 0) {
+ if (chipspresent >= setup_num_esps)
+ chipspresent = setup_num_esps;
+ else
+ printk("mac_esp_detect: num_hosts detected %d setup %d \n",
+ chipspresent, setup_num_esps);
+ }
+
+ /* TODO: add disconnect / nosync flags */
+
+ /* setup variables */
+ tpnt->can_queue =
+ (setup_can_queue > 0) ? setup_can_queue : 7;
+ tpnt->cmd_per_lun =
+ (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : 1;
+ tpnt->sg_tablesize =
+ (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_ALL;
+
+ if (setup_hostid >= 0)
+ tpnt->this_id = setup_hostid;
+ else {
+ /* use 7 as default */
+ tpnt->this_id = 7;
+ }
+
+#ifdef SUPPORT_TAGS
+ if (setup_use_tagged_queuing < 0)
+ setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING;
+#endif
+
+ for (chipnum = 0; chipnum < chipspresent; chipnum ++) {
+ struct NCR_ESP * esp;
+
+ esp = esp_allocate(tpnt, (void *) NULL);
+ esp->eregs = (struct ESP_regs *) get_base(chipnum);
+
+ esp->dma_irq_p = &esp_dafb_dma_irq_p;
+ if (chipnum == 0) {
+
+ if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) {
+ /* most machines except those below :-) */
+ quick = 1;
+ esp->dma_irq_p = &esp_iosb_dma_irq_p;
+ } else if (macintosh_config->scsi_type == MAC_SCSI_QUADRA3) {
+ /* mostly av's */
+ quick = 0;
+ } else {
+ /* q950, 900, 700 */
+ quick = 1;
+ writel(0x1d1, 0xf9800024);
+ esp->dregs = (void *) 0xf9800024;
+ }
+
+ } else { /* chipnum */
+
+ quick = 1;
+ writel(0x1d1, 0xf9800028);
+ esp->dregs = (void *) 0xf9800028;
+
+ } /* chipnum == 0 */
+
+
+ /* use pio for command bytes; pio for message/data: TBI */
+ esp->do_pio_cmds = 1;
+
+ /* various functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = NULL;
+ esp->dma_init_write = NULL;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+
+ esp->dma_ports_p = &dma_ports_p;
+
+
+ /* Optional functions */
+ esp->dma_barrier = NULL;
+ esp->dma_drain = NULL;
+ esp->dma_invalidate = NULL;
+ esp->dma_irq_entry = NULL;
+ esp->dma_irq_exit = NULL;
+ esp->dma_led_on = NULL;
+ esp->dma_led_off = NULL;
+ esp->dma_poll = NULL;
+ esp->dma_reset = NULL;
+
+ /* SCSI chip speed */
+ /* below esp->cfreq = 40000000; */
+
+
+ if (quick) {
+ /* 'quick' means there's handshake glue logic like in the 5380 case */
+ esp->dma_setup = &dma_setup_quick;
+ } else {
+ esp->dma_setup = &dma_setup;
+ }
+
+ if (chipnum == 0) {
+
+ esp->irq = IRQ_MAC_SCSI;
+
+ request_irq(IRQ_MAC_SCSI, esp_intr, 0, "Mac ESP SCSI", esp);
+ request_irq(IRQ_MAC_SCSIDRQ, fake_drq, 0, "Mac ESP DRQ", esp);
+
+ if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) {
+ esp->cfreq = 16500000;
+ } else {
+ esp->cfreq = 25000000;
+ }
+
+
+ } else { /* chipnum == 1 */
+
+ esp->irq = IRQ_MAC_SCSIDRQ;
+
+ request_irq(IRQ_MAC_SCSIDRQ, esp_intr, 0, "Mac ESP SCSI 2", esp);
+
+ esp->cfreq = 25000000;
+
+ }
+
+ if (quick) {
+ printk("esp: using quick version\n");
+ }
+
+ printk("esp: addr at 0x%p\n", esp->eregs);
+
+ esp->scsi_id = 7;
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ } /* for chipnum */
+
+ if (chipspresent)
+ printk("\nmac_esp: %d esp controllers found\n", chipspresent);
+
+ esp_initialized = chipspresent;
+
+ return chipspresent;
+}
+
+/*
+ * I've been wondering what this is supposed to do, for some time. Talking
+ * to Allen Briggs: These machines have an extra register someplace where the
+ * DRQ pin of the ESP can be monitored. That isn't useful for determining
+ * anything else (such as reselect interrupt or other magic) though.
+ * Maybe make the semantics should be changed like
+ * if (esp->current_SC)
+ * ... check DRQ flag ...
+ * else
+ * ... disconnected, check pending VIA interrupt ...
+ *
+ * There's a problem with using the dabf flag or mac_irq_pending() here: both
+ * seem to return 1 even though no interrupt is currently pending, resulting
+ * in esp_exec_cmd() holding off the next command, and possibly infinite loops
+ * in esp_intr().
+ * Short term fix: just use esp_status & ESP_STAT_INTR here, as long as we
+ * use simple PIO. The DRQ status will be important when implementing pseudo
+ * DMA mode (set up ESP transfer count, return, do a batch of bytes in PIO or
+ * 'hardware handshake' mode upon DRQ).
+ * If you plan on changing this (i.e. to save the esp_status register access in
+ * favor of a VIA register access or a shadow register for the IFR), make sure
+ * to try a debug version of this first to monitor what registers would be a good
+ * indicator of the ESP interrupt.
+ */
+
+static int esp_dafb_dma_irq_p(struct NCR_ESP * esp)
+{
+ unsigned int ret;
+ int sreg = esp->eregs->esp_status;
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: esp_dafb_dma_irq_p dafb %d irq %d\n",
+ readl(esp->dregs), mac_irq_pending(IRQ_MAC_SCSI));
+#endif
+
+ sreg &= ESP_STAT_INTR;
+
+ /*
+ * maybe working; this is essentially what's used for iosb_dma_irq_p
+ */
+ if (sreg)
+ return 1;
+ else
+ return 0;
+
+ /*
+ * didn't work ...
+ */
+#if 0
+ if (esp->current_SC)
+ ret = readl(esp->dregs) & 0x200;
+ else if (esp->disconnected_SC)
+ ret = 1; /* sreg ?? */
+ else
+ ret = mac_irq_pending(IRQ_MAC_SCSI);
+
+ return(ret);
+#endif
+
+}
+
+/*
+ * See above: testing mac_irq_pending always returned 8 (SCSI IRQ) regardless
+ * of the actual ESP status.
+ */
+
+static int esp_iosb_dma_irq_p(struct NCR_ESP * esp)
+{
+ int ret = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ);
+ int sreg = esp->eregs->esp_status;
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n",
+ mac_irq_pending(IRQ_MAC_SCSIDRQ), mac_irq_pending(IRQ_MAC_SCSI),
+ sreg, esp->current_SC, esp->disconnected_SC);
+#endif
+
+ sreg &= ESP_STAT_INTR;
+
+ if (sreg)
+ return (sreg);
+ else
+ return 0;
+}
+
+/*
+ * This seems to be OK for PIO at least ... usually 0 after PIO.
+ */
+
+static int dma_bytes_sent(struct NCR_ESP * esp, int fifo_count)
+{
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma bytes sent = %x\n", fifo_count);
+#endif
+
+ return fifo_count;
+}
+
+/*
+ * dma_can_transfer is used to switch between DMA and PIO, if DMA (pseudo)
+ * is ever implemented. Returning 0 here will use PIO.
+ */
+
+static int dma_can_transfer(struct NCR_ESP * esp, Scsi_Cmnd * sp)
+{
+ unsigned long sz = sp->SCp.this_residual;
+#if 0 /* no DMA yet; make conditional */
+ if (sz > 0x10000000) {
+ sz = 0x10000000;
+ }
+ printk("mac_esp: dma can transfer = 0lx%x\n", sz);
+#else
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: pio to transfer = %ld\n", sz);
+#endif
+
+ sz = 0;
+#endif
+ return sz;
+}
+
+/*
+ * Not yet ...
+ */
+
+static void dma_dump_state(struct NCR_ESP * esp)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_dump_state: called\n");
+#endif
+#if 0
+ ESPLOG(("esp%d: dma -- cond_reg<%02x>\n",
+ esp->esp_id, ((struct mac_dma_registers *)
+ (esp->dregs))->cond_reg));
+#endif
+}
+
+/*
+ * DMA setup: should be used to set up the ESP transfer count for pseudo
+ * DMA transfers; need a DRQ transfer function to do the actual transfer
+ */
+
+static void dma_init_read(struct NCR_ESP * esp, char * vaddress, int length)
+{
+ printk("mac_esp: dma_init_read\n");
+}
+
+
+static void dma_init_write(struct NCR_ESP * esp, char * vaddress, int length)
+{
+ printk("mac_esp: dma_init_write\n");
+}
+
+
+static void dma_ints_off(struct NCR_ESP * esp)
+{
+ mac_turnoff_irq(esp->irq);
+}
+
+
+static void dma_ints_on(struct NCR_ESP * esp)
+{
+ mac_turnon_irq(esp->irq);
+}
+
+/*
+ * generic dma_irq_p(), unused
+ */
+
+static int dma_irq_p(struct NCR_ESP * esp)
+{
+ int i = esp->eregs->esp_status;
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_irq_p status %d\n", i);
+#endif
+
+ return (i & ESP_STAT_INTR);
+}
+
+static int dma_irq_p_quick(struct NCR_ESP * esp)
+{
+ /*
+ * Copied from iosb_dma_irq_p()
+ */
+ int ret = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ);
+ int sreg = esp->eregs->esp_status;
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n",
+ mac_irq_pending(IRQ_MAC_SCSIDRQ), mac_irq_pending(IRQ_MAC_SCSI),
+ sreg, esp->current_SC, esp->disconnected_SC);
+#endif
+
+ sreg &= ESP_STAT_INTR;
+
+ if (sreg)
+ return (sreg);
+ else
+ return 0;
+
+}
+
+static void dma_led_off(struct NCR_ESP * esp)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_led_off: called\n");
+#endif
+}
+
+
+static void dma_led_on(struct NCR_ESP * esp)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_led_on: called\n");
+#endif
+}
+
+
+static int dma_ports_p(struct NCR_ESP * esp)
+{
+ return 0;
+}
+
+
+static void dma_setup(struct NCR_ESP * esp, __u32 addr, int count, int write)
+{
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_setup\n");
+#endif
+
+ if (write) {
+ dma_init_read(esp, (char *) addr, count);
+ } else {
+ dma_init_write(esp, (char *) addr, count);
+ }
+}
+
+
+static void dma_setup_quick(struct NCR_ESP * esp, __u32 addr, int count, int write)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_setup_quick\n");
+#endif
+}
--- /dev/null
+
+/*
+mac_esp.h
+
+copyright 1997 David Weis, weisd3458@uni.edu
+*/
+
+
+#include "NCR53C9x.h"
+
+#ifndef MAC_ESP_H
+#define MAC_ESP_H
+
+/* #define DEBUG_MAC_ESP */
+
+extern int mac_esp_detect(struct SHT *);
+extern const char *esp_info(struct Scsi_Host *);
+extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int esp_command(Scsi_Cmnd *);
+extern int esp_abort(Scsi_Cmnd *);
+extern int esp_reset(Scsi_Cmnd *, unsigned int);
+
+
+#define SCSI_MAC_ESP { proc_dir: &proc_scsi_esp, \
+ name: "Mac 53C9x SCSI", \
+ detect: mac_esp_detect, \
+ release: NULL, \
+ info: esp_info, \
+ /* command: esp_command, */ \
+ queuecommand: esp_queue, \
+ abort: esp_abort, \
+ reset: esp_reset, \
+ can_queue: 7, \
+ this_id: 7, \
+ sg_tablesize: SG_ALL, \
+ cmd_per_lun: 1, \
+ use_clustering: DISABLE_CLUSTERING, \
+ use_new_eh_code: 0 }
+
+#endif /* MAC_ESP_H */
+
--- /dev/null
+/* mca_53c9x.c: Driver for the SCSI adapter found on NCR 35xx
+ * (and maybe some other) Microchannel machines
+ *
+ * Code taken mostly from Cyberstorm SCSI drivers
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * Hacked to work with the NCR MCA stuff by Tymm Twillman (tymm@computer.org)
+ *
+ * The CyberStorm SCSI driver (and this driver) is based on David S. Miller's
+ * ESP driver * for the Sparc computers.
+ *
+ * Special thanks to Ken Stewart at Symbios (LSI) for helping with info on
+ * the 86C01. I was on the brink of going ga-ga...
+ *
+ * Also thanks to Jesper Skov for helping me with info on how the Amiga
+ * does things...
+ */
+
+/*
+ * This is currently only set up to use one 53c9x card at a time; it could be
+ * changed fairly easily to detect/use more than one, but I'm not too sure how
+ * many cards that use the 53c9x on MCA systems there are (if, in fact, there
+ * are cards that use them, other than the one built into some NCR systems)...
+ * If anyone requests this, I'll throw it in, otherwise it's not worth the
+ * effort.
+ */
+
+/*
+ * Info on the 86C01 MCA interface chip at the bottom, if you care enough to
+ * look.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "NCR53C9x.h"
+#include "mca_53c9x.h"
+
+#include <asm/dma.h>
+#include <linux/mca.h>
+#include <asm/irq.h>
+#include <asm/mca_dma.h>
+
+#include <asm/pgtable.h>
+
+static int dma_bytes_sent(struct NCR_ESP *, int);
+static int dma_can_transfer(struct NCR_ESP *, Scsi_Cmnd *);
+static void dma_dump_state(struct NCR_ESP *);
+static void dma_init_read(struct NCR_ESP *, __u32, int);
+static void dma_init_write(struct NCR_ESP *, __u32, int);
+static void dma_ints_off(struct NCR_ESP *);
+static void dma_ints_on(struct NCR_ESP *);
+static int dma_irq_p(struct NCR_ESP *);
+static int dma_ports_p(struct NCR_ESP *);
+static void dma_setup(struct NCR_ESP *, __u32, int, int);
+static void dma_led_on(struct NCR_ESP *);
+static void dma_led_off(struct NCR_ESP *);
+
+/* This is where all commands are put before they are trasfered to the
+ * 53c9x via PIO.
+ */
+
+volatile unsigned char cmd_buffer[16];
+
+/*
+ * We keep the structure that is used to access the registers on the 53c9x
+ * here.
+ */
+
+static struct ESP_regs eregs;
+
+/***************************************************************** Detection */
+int mca_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ static int io_port_by_pos[] = MCA_53C9X_IO_PORTS;
+ int mca_53c9x_ids[] = MCA_53C9X_IDS;
+ int *id_to_check = mca_53c9x_ids;
+ int slot;
+ int pos[3];
+ unsigned int tmp_io_addr;
+ unsigned char tmp_byte;
+
+
+ if (!MCA_bus)
+ return 0;
+
+ while (*id_to_check) {
+ if ((slot = mca_find_adapter(*id_to_check, 0)) !=
+ MCA_NOTFOUND)
+ {
+ esp = esp_allocate(tpnt, (void *) NULL);
+
+ pos[0] = mca_read_stored_pos(slot, 2);
+ pos[1] = mca_read_stored_pos(slot, 3);
+ pos[2] = mca_read_stored_pos(slot, 4);
+
+ esp->eregs = &eregs;
+
+ /*
+ * IO port base is given in the first (non-ID) pos
+ * register, like so:
+ *
+ * Bits 3 2 1 IO base
+ * ----------------------------
+ * 0 0 0 <disabled>
+ * 0 0 1 0x0240
+ * 0 1 0 0x0340
+ * 0 1 1 0x0400
+ * 1 0 0 0x0420
+ * 1 0 1 0x3240
+ * 1 1 0 0x8240
+ * 1 1 1 0xA240
+ */
+
+ tmp_io_addr =
+ io_port_by_pos[(pos[0] & 0x0E) >> 1];
+
+ esp->eregs->io_addr = tmp_io_addr + 0x10;
+
+ if (esp->eregs->io_addr == 0x0000) {
+ printk("Adapter is disabled.\n");
+ break;
+ }
+
+ /*
+ * IRQ is specified in bits 4 and 5:
+ *
+ * Bits 4 5 IRQ
+ * -----------------------
+ * 0 0 3
+ * 0 1 5
+ * 1 0 7
+ * 1 1 9
+ */
+
+ esp->irq = ((pos[0] & 0x30) >> 3) + 3;
+
+ /*
+ * DMA channel is in the low 3 bits of the second
+ * POS register
+ */
+
+ esp->dma = pos[1] & 7;
+ esp->slot = slot;
+
+ if (request_irq(esp->irq, esp_intr, 0,
+ "NCR 53c9x SCSI", esp_intr))
+ {
+ printk("Unable to request IRQ %d.\n", esp->irq);
+ return 0;
+ }
+
+ if (request_dma(esp->dma, "NCR 53c9x SCSI")) {
+ printk("Unable to request DMA channel %d.\n",
+ esp->dma);
+ free_irq(esp->irq, esp_intr);
+ return 0;
+ }
+
+ request_region(tmp_io_addr, 32, "NCR 53c9x SCSI");
+
+ /*
+ * 86C01 handles DMA, IO mode, from address
+ * (base + 0x0a)
+ */
+
+ mca_disable_dma(esp->dma);
+ mca_set_dma_io(esp->dma, tmp_io_addr + 0x0a);
+ mca_enable_dma(esp->dma);
+
+ /* Tell the 86C01 to give us interrupts */
+
+ tmp_byte = inb(tmp_io_addr + 0x02) | 0x40;
+ outb(tmp_byte, tmp_io_addr + 0x02);
+
+ /*
+ * Scsi ID -- general purpose register, hi
+ * 2 bits; add 4 to this number to get the
+ * ID
+ */
+
+ esp->scsi_id = ((pos[2] & 0xC0) >> 6) + 4;
+
+ /* Do command transfer with programmed I/O */
+
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_led_on = dma_led_on;
+ esp->dma_led_off = dma_led_off;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+
+ /* Set the command buffer */
+
+ esp->esp_command = (volatile unsigned char*)
+ cmd_buffer;
+ esp->esp_command_dvma = virt_to_bus(cmd_buffer);
+
+ /* SCSI chip speed */
+
+ esp->cfreq = 25000000;
+
+ /* Differential SCSI? I think not. */
+
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk(" Adapter found in slot %2d: io port 0x%x "
+ "irq %d dma channel %d\n", slot + 1, tmp_io_addr,
+ esp->irq, esp->dma);
+
+ mca_set_adapter_name(slot, "NCR 53C9X SCSI Adapter");
+ mca_mark_as_used(slot);
+
+ break;
+ }
+
+ id_to_check++;
+ }
+
+ return esps_in_use;
+}
+
+
+/******************************************************************* Release */
+
+int mca_esp_release(struct Scsi_Host *host)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *)host->hostdata;
+ unsigned char tmp_byte;
+
+
+ /*
+ * Tell the 86C01 to stop sending interrupts
+ */
+
+ tmp_byte = inb(esp->eregs->io_addr - 0x0E);
+ tmp_byte &= ~0x40;
+ outb(tmp_byte, esp->eregs->io_addr - 0x0E);
+
+ free_irq(esp->irq, esp_intr);
+ free_dma(esp->dma);
+
+ mca_mark_as_unused(esp->eregs->slot);
+
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Ask the 53c9x. It knows. */
+
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ /*
+ * The MCA dma channels can only do up to 128K bytes at a time.
+ * (16 bit mode)
+ */
+
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0x20000)
+ sz = 0x20000;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ /*
+ * Doesn't quite match up to the other drivers, but we do what we
+ * can.
+ */
+
+ ESPLOG(("esp%d: dma channel <%d>\n", esp->esp_id, esp->dma));
+ ESPLOG(("bytes left to dma: %d\n", mca_get_dma_residue(esp->dma)));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ unsigned long flags;
+
+
+ save_flags(flags);
+ cli();
+
+ mca_disable_dma(esp->dma);
+ mca_set_dma_mode(esp->dma, MCA_DMA_MODE_XFER | MCA_DMA_MODE_16 |
+ MCA_DMA_MODE_IO);
+ mca_set_dma_addr(esp->dma, addr);
+ mca_set_dma_count(esp->dma, length / 2); /* !!! */
+ mca_enable_dma(esp->dma);
+
+ restore_flags(flags);
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ unsigned long flags;
+
+
+ save_flags(flags);
+ cli();
+
+ mca_disable_dma(esp->dma);
+ mca_set_dma_mode(esp->dma, MCA_DMA_MODE_XFER | MCA_DMA_MODE_WRITE |
+ MCA_DMA_MODE_16 | MCA_DMA_MODE_IO);
+ mca_set_dma_addr(esp->dma, addr);
+ mca_set_dma_count(esp->dma, length / 2); /* !!! */
+ mca_enable_dma(esp->dma);
+
+ restore_flags(flags);
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ /*
+ * Tell the 'C01 to shut up. All interrupts are routed through it.
+ */
+
+ outb(inb(esp->eregs->io_addr - 0x0E) & ~0x40,
+ esp->eregs->io_addr - 0x0E);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ /*
+ * Ok. You can speak again.
+ */
+
+ outb(inb(esp->eregs->io_addr - 0x0E) | 0x40,
+ esp->eregs->io_addr - 0x0E);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ /*
+ * DaveM says that this should return a "yes" if there is an interrupt
+ * or a DMA error occurred. I copied the Amiga driver's semantics,
+ * though, because it seems to work and we can't really tell if
+ * a DMA error happened. This gives the "yes" if the scsi chip
+ * is sending an interrupt and no DMA activity is taking place
+ */
+
+ return (!(inb(esp->eregs->io_addr - 0x04) & 1) &&
+ !(inb(esp->eregs->io_addr - 0x04) & 2) );
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ /*
+ * Check to see if interrupts are enabled on the 'C01 (in case abort
+ * is entered multiple times, so we only do the abort once)
+ */
+
+ return (inb(esp->eregs->io_addr - 0x0E) & 0x40) ? 1:0;
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ if(write){
+ dma_init_write(esp, addr, count);
+ } else {
+ dma_init_read(esp, addr, count);
+ }
+}
+
+/*
+ * These will not play nicely with other disk controllers that try to use the
+ * disk active LED... but what can you do? Don't answer that.
+ *
+ * Stolen shamelessly from ibmmca.c -- IBM Microchannel SCSI adapter driver
+ *
+ */
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+ outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR);
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+ outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR);
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = MCA_53C9X;
+#include "scsi_module.c"
+#endif
+
+/*
+ * OK, here's the goods I promised. The NCR 86C01 is an MCA interface chip
+ * that handles enabling/diabling IRQ, dma interfacing, IO port selection
+ * and other fun stuff. It takes up 16 addresses, and the chip it is
+ * connnected to gets the following 16. Registers are as follows:
+ *
+ * Offsets 0-1 : Card ID
+ *
+ * Offset 2 : Mode enable register --
+ * Bit 7 : Data Word width (1 = 16, 0 = 8)
+ * Bit 6 : IRQ enable (1 = enabled)
+ * Bits 5,4 : IRQ select
+ * 0 0 : IRQ 3
+ * 0 1 : IRQ 5
+ * 1 0 : IRQ 7
+ * 1 1 : IRQ 9
+ * Bits 3-1 : Base Address
+ * 0 0 0 : <disabled>
+ * 0 0 1 : 0x0240
+ * 0 1 0 : 0x0340
+ * 0 1 1 : 0x0400
+ * 1 0 0 : 0x0420
+ * 1 0 1 : 0x3240
+ * 1 1 0 : 0x8240
+ * 1 1 1 : 0xA240
+ * Bit 0 : Card enable (1 = enabled)
+ *
+ * Offset 3 : DMA control register --
+ * Bit 7 : DMA enable (1 = enabled)
+ * Bits 6,5 : Preemt Count Select (transfers to complete after
+ * 'C01 has been preempted on MCA bus)
+ * 0 0 : 0
+ * 0 1 : 1
+ * 1 0 : 3
+ * 1 1 : 7
+ * (all these wacky numbers; I'm sure there's a reason somewhere)
+ * Bit 4 : Fairness enable (1 = fair bus priority)
+ * Bits 3-0 : Arbitration level (0-15 consecutive)
+ *
+ * Offset 4 : General purpose register
+ * Bits 7-3 : User definable (here, 7,6 are SCSI ID)
+ * Bits 2-0 : reserved
+ *
+ * Offset 10 : DMA decode register (used for IO based DMA; also can do
+ * PIO through this port)
+ *
+ * Offset 12 : Status
+ * Bits 7-2 : reserved
+ * Bit 1 : DMA pending (1 = pending)
+ * Bit 0 : IRQ pending (0 = pending)
+ *
+ * Exciting, huh?
+ *
+ */
--- /dev/null
+/* mca_53c94.h: Defines and structures for the SCSI adapter found on NCR 35xx
+ * (and maybe some other) Microchannel machines.
+ *
+ * Code taken mostly from Cyberstorm SCSI drivers
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * Hacked to work with the NCR MCA stuff by Tymm Twillman (tymm@computer.org)
+ * 1998
+ */
+
+#include "NCR53C9x.h"
+
+#ifndef MCA_53C9X_H
+#define MCA_53C9X_H
+
+/*
+ * From ibmmca.c (IBM scsi controller card driver) -- used for turning PS2 disk
+ * activity LED on and off
+ */
+
+#define PS2_SYS_CTR 0x92
+
+extern int mca_esp_detect(struct SHT *);
+extern int mca_esp_release(struct Scsi_Host *);
+extern const char *esp_info(struct Scsi_Host *);
+extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int esp_command(Scsi_Cmnd *);
+extern int esp_abort(Scsi_Cmnd *);
+extern int esp_reset(Scsi_Cmnd *, unsigned int);
+extern int esp_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout);
+
+
+#define MCA_53C9X { proc_dir: &proc_scsi_esp, \
+ name: "NCR 53c9x SCSI", \
+ detect: mca_esp_detect, \
+ release: mca_esp_release, \
+ queuecommand: esp_queue, \
+ abort: esp_abort, \
+ reset: esp_reset, \
+ can_queue: 7, \
+ sg_tablesize: SG_ALL, \
+ cmd_per_lun: 1, \
+ unchecked_isa_dma: 1, \
+ use_clustering: DISABLE_CLUSTERING }
+
+/* Ports the ncr's 53c94 can be put at; indexed by pos register value */
+
+#define MCA_53C9X_IO_PORTS { \
+ 0x0000, 0x0240, 0x0340, 0x0400, \
+ 0x0420, 0x3240, 0x8240, 0xA240, \
+ }
+
+/*
+ * Supposedly there were some cards put together with the 'c9x and 86c01. If
+ * they have different ID's from the ones on the 3500 series machines,
+ * you can add them here and hopefully things will work out.
+ */
+
+#define MCA_53C9X_IDS { \
+ 0x7F4C, \
+ 0x0000, \
+ }
+
+#endif /* MCA_53C9X_H */
+
--- /dev/null
+/*===================================================================
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright 1998 American Megatrends Inc.
+ *
+ * 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.
+ *
+ * Version : 0.92
+ *
+ * Description: Linux device driver for AMI MegaRAID controller
+ *
+ * History:
+ *
+ * Version 0.90:
+ * Works and has been tested with the MegaRAID 428 controller, and
+ * the MegaRAID 438 controller. Probably works with the 466 also,
+ * but not tested.
+ *
+ * Version 0.91:
+ * Aligned mailbox area on 16-byte boundry.
+ * Added schedule() at the end to properly clean up.
+ * Made improvements for conformity to linux driver standards.
+ *
+ * Version 0.92:
+ * Added support for 2.1 kernels.
+ * Reads from pci_dev struct, so it's not dependent on pcibios.
+ * Added some missing virt_to_bus() translations.
+ * Added support for SMP.
+ * Changed global cli()'s to spinlocks for 2.1, and simulated
+ * spinlocks for 2.0.
+ * Removed setting of SA_INTERRUPT flag when requesting Irq.
+ *
+ * Version 0.92ac:
+ * Small changes to the comments/formatting. Plus a couple of
+ * added notes. Returned to the authors. No actual code changes
+ * save printk levels.
+ * 8 Oct 98 Alan Cox <alan.cox@linux.org>
+ *
+ * Merged with 2.1.131 source tree.
+ * 12 Dec 98 K. Baranowski <kgb@knm.org.pl>
+ *
+ * BUGS:
+ * Tested with 2.1.90, but unfortunately there is a bug in pci.c which
+ * fails to detect our controller. Does work with 2.1.118--don't know
+ * which kernel in between it was fixed in.
+ * With SMP enabled under 2.1.118 with more than one processor, gets an
+ * error message "scsi_end_request: buffer-list destroyed" under heavy
+ * IO, but doesn't seem to affect operation, or data integrity. The
+ * message doesn't occur without SMP enabled, or with one proccessor with
+ * SMP enabled, or under any combination under 2.0 kernels.
+ *
+ *===================================================================*/
+#define QISR 1
+
+#define CRLFSTR "\n"
+
+#define MULTIQ 1
+
+#include <linux/version.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+char kernel_version[] = UTS_RELEASE;
+
+/* originally ported by Dell Corporation; updated, released, and maintained by
+ American Megatrends */
+MODULE_AUTHOR("American Megatrends Inc.");
+MODULE_DESCRIPTION("AMI MegaRAID driver");
+#endif
+#endif
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include <linux/wait.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/malloc.h> /* for kmalloc() */
+#if LINUX_VERSION_CODE < 0x20100
+#include <linux/bios32.h>
+#else
+#include <asm/spinlock.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+
+#include "megaraid.h"
+
+/*================================================================
+ *
+ * #Defines
+ *
+ *================================================================*/
+
+#if LINUX_VERSION_CODE < 0x020100
+#define ioremap vremap
+#define iounmap vfree
+
+/* simulate spin locks */
+typedef struct {volatile char lock;} spinlock_t;
+#define spin_lock_init(x) { (x)->lock = 0;}
+#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\
+ (x)->lock=1; save_flags(flags);\
+ cli();}
+#define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);}
+
+#endif
+
+#if LINUX_VERSION_CODE >= 0x020100
+#define queue_task_irq(a,b) queue_task(a,b)
+#define queue_task_irq_off(a,b) queue_task(a,b)
+#endif
+
+#define MAX_SERBUF 160
+#define COM_BASE 0x2f8
+
+#define ENQUEUE(obj,type,list,next) \
+{ type **node; long cpuflag; \
+ spin_lock_irqsave(&mega_lock,cpuflag);\
+ for(node=&(list); *node; node=(type **)&(*node)->##next); \
+ (*node) = obj; \
+ (*node)->##next = NULL; \
+ spin_unlock_irqrestore(&mega_lock,cpuflag);\
+};
+
+#define DEQUEUE(obj,type,list,next) \
+{ long cpuflag; \
+ spin_lock_irqsave(&mega_lock,cpuflag);\
+ if ((obj=list) != NULL) {\
+ list = (type *)(list)->##next; \
+ } \
+ spin_unlock_irqrestore(&mega_lock,cpuflag);\
+};
+
+u_long RDINDOOR(mega_host_config *megaCfg)
+{
+ return readl(megaCfg->base + 0x20);
+}
+
+void WRINDOOR(mega_host_config *megaCfg, u_long value)
+{
+ writel(value,megaCfg->base+0x20);
+}
+
+u_long RDOUTDOOR(mega_host_config *megaCfg)
+{
+ return readl(megaCfg->base+0x2C);
+}
+
+void WROUTDOOR(mega_host_config *megaCfg, u_long value)
+{
+ writel(value,megaCfg->base+0x2C);
+}
+
+/*================================================================
+ *
+ * Function prototypes
+ *
+ *================================================================*/
+static int MegaIssueCmd(mega_host_config *megaCfg,
+ u_char *mboxData,
+ mega_scb *scb,
+ int intr);
+static int build_sglist(mega_host_config *megaCfg, mega_scb *scb,
+ u_long *buffer, u_long *length);
+
+static void mega_runque(void *);
+static void mega_rundoneq(void);
+static void mega_cmd_done(mega_host_config *,mega_scb *, int);
+
+/* set SERDEBUG to 1 to enable serial debugging */
+#define SERDEBUG 0
+#if SERDEBUG
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int ser_printk(const char *fmt, ...);
+#endif
+
+/*================================================================
+ *
+ * Global variables
+ *
+ *================================================================*/
+static int numCtlrs = 0;
+static mega_host_config *megaCtlrs[4] = { 0 };
+
+/* Change this to 0 if you want to see the raw drives */
+static int use_raid = 1;
+
+/* Queue of pending/completed SCBs */
+static mega_scb *qPending = NULL;
+static Scsi_Cmnd *qCompleted = NULL;
+
+volatile static spinlock_t mega_lock;
+static struct tq_struct runq = {0,0,mega_runque,NULL};
+
+struct proc_dir_entry proc_scsi_megaraid = {
+ PROC_SCSI_MEGARAID, 8, "megaraid",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#if SERDEBUG
+static char strbuf[MAX_SERBUF+1];
+
+static void ser_init()
+{
+ unsigned port=COM_BASE;
+
+ outb(0x80,port+3);
+ outb(0,port+1);
+ /* 9600 Baud, if 19200: outb(6,port) */
+ outb(12, port);
+ outb(3,port+3);
+ outb(0,port+1);
+}
+
+static void ser_puts(char *str)
+{
+ char *ptr;
+
+ ser_init();
+ for (ptr=str;*ptr;++ptr)
+ ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+ unsigned port=COM_BASE;
+
+ while ((inb(port+5) & 0x20)==0);
+ outb(c,port);
+ if (c==0x0a)
+ {
+ while ((inb(port+5) & 0x20)==0);
+ outb(0x0d,port);
+ }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ long flags;
+
+ spin_lock_irqsave(mega_lock,flags);
+ va_start(args,fmt);
+ i = vsprintf(strbuf,fmt,args);
+ ser_puts(strbuf);
+ va_end(args);
+ spin_unlock_irqrestore(&mega_lock,flags);
+
+ return i;
+}
+
+#define TRACE(a) { ser_printk a;}
+
+#else
+#define TRACE(A)
+#endif
+
+void callDone(Scsi_Cmnd *SCpnt)
+{
+ if (SCpnt->result) {
+ TRACE(("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number,
+ SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun,
+ SCpnt->result));
+ }
+ SCpnt->scsi_done(SCpnt);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Local functions
+ *
+ *-------------------------------------------------------------------------*/
+
+/*================================================
+ * Initialize SCB structures
+ *================================================*/
+static void initSCB(mega_host_config *megaCfg)
+{
+ int idx;
+
+ for(idx=0; idx<megaCfg->max_cmds; idx++) {
+ megaCfg->scbList[idx].idx = -1;
+ megaCfg->scbList[idx].flag = 0;
+ megaCfg->scbList[idx].sgList = NULL;
+ megaCfg->scbList[idx].SCpnt = NULL;
+ }
+}
+
+/*===========================
+ * Allocate a SCB structure
+ *===========================*/
+static mega_scb *allocateSCB(mega_host_config *megaCfg,Scsi_Cmnd *SCpnt)
+{
+ int idx;
+ long flags;
+
+ spin_lock_irqsave(&mega_lock,flags);
+ for(idx=0; idx<megaCfg->max_cmds; idx++) {
+ if (megaCfg->scbList[idx].idx < 0) {
+
+ /* Set Index and SCB pointer */
+ megaCfg->scbList[idx].flag = 0;
+ megaCfg->scbList[idx].idx = idx;
+ megaCfg->scbList[idx].SCpnt = SCpnt;
+ megaCfg->scbList[idx].next = NULL;
+ spin_unlock_irqrestore(&mega_lock,flags);
+
+ if (megaCfg->scbList[idx].sgList == NULL) {
+ megaCfg->scbList[idx].sgList =
+ kmalloc(sizeof(mega_sglist)*MAX_SGLIST,GFP_ATOMIC|GFP_DMA);
+ }
+
+ return &megaCfg->scbList[idx];
+ }
+ }
+ spin_unlock_irqrestore(&mega_lock,flags);
+
+ printk(KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n");
+
+ return NULL;
+}
+
+/*=======================
+ * Free a SCB structure
+ *=======================*/
+static void freeSCB(mega_scb *scb)
+{
+ long flags;
+
+ spin_lock_irqsave(&mega_lock,flags);
+ scb->flag = 0;
+ scb->idx = -1;
+ scb->next = NULL;
+ scb->SCpnt = NULL;
+ spin_unlock_irqrestore(&mega_lock,flags);
+}
+
+/* Run through the list of completed requests */
+static void mega_rundoneq()
+{
+ mega_host_config *megaCfg;
+ Scsi_Cmnd *SCpnt;
+ long islogical;
+
+ while(1) {
+ DEQUEUE(SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+ if (SCpnt == NULL) return;
+
+ megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+ /* Check if we're allowing access to RAID drives or physical
+ * if use_raid == 1 and this wasn't a disk on the max channel or
+ * if use_raid == 0 and this was a disk on the max channel
+ * then fail.
+ */
+ islogical = (SCpnt->channel == megaCfg->host->max_channel) ? 1 : 0;
+ if (SCpnt->cmnd[0] == INQUIRY &&
+ ((((u_char*)SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) &&
+ (islogical != use_raid)) {
+ SCpnt->result = 0xF0;
+ }
+
+ /* Convert result to error */
+ switch(SCpnt->result) {
+ case 0x00: case 0x02:
+ SCpnt->result |= (DID_OK << 16);
+ break;
+ case 0x8:
+ SCpnt->result |= (DID_BUS_BUSY << 16);
+ break;
+ default:
+ SCpnt->result |= (DID_BAD_TARGET << 16);
+ break;
+ }
+
+ /* Callback */
+ callDone(SCpnt);
+ }
+}
+
+/* Add command to the list of completed requests */
+static void mega_cmd_done(mega_host_config *megaCfg,mega_scb *pScb, int status)
+{
+ pScb->SCpnt->result = status;
+ ENQUEUE(pScb->SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+ freeSCB(pScb);
+}
+
+/*----------------------------------------------------
+ * Process pending queue list
+ *
+ * Run as a scheduled task
+ *----------------------------------------------------*/
+static void mega_runque(void *dummy)
+{
+ mega_host_config *megaCfg;
+ mega_scb *pScb;
+ long flags;
+
+ /* Take care of any completed requests */
+ mega_rundoneq();
+
+ DEQUEUE(pScb,mega_scb,qPending,next);
+
+ if (pScb) {
+ megaCfg = (mega_host_config *)pScb->SCpnt->host->hostdata;
+
+ if (megaCfg->mbox->busy || megaCfg->flag & (IN_ISR|PENDING)) {
+ TRACE(("%.08lx %.02x <%d.%d.%d> intr%d busy%d isr%d pending%d\n",
+ pScb->SCpnt->serial_number,
+ pScb->SCpnt->cmnd[0],
+ pScb->SCpnt->channel,
+ pScb->SCpnt->target,
+ pScb->SCpnt->lun,
+ intr_count,
+ megaCfg->mbox->busy,
+ (megaCfg->flag & IN_ISR) ? 1 : 0,
+ (megaCfg->flag & PENDING) ? 1 : 0));
+ }
+
+ if (MegaIssueCmd(megaCfg, pScb->mboxData, pScb, 1)) {
+ /* We're BUSY... come back later */
+ spin_lock_irqsave(&mega_lock,flags);
+ pScb->next = qPending;
+ qPending = pScb;
+ spin_unlock_irqrestore(&mega_lock,flags);
+
+ if (!(megaCfg->flag & PENDING)) { /* If PENDING, irq will schedule task */
+ queue_task(&runq, &tq_scheduler);
+ }
+ }
+ }
+}
+
+/*-------------------------------------------------------------------
+ *
+ * Build a SCB from a Scsi_Cmnd
+ *
+ * Returns a SCB pointer, or NULL
+ * If NULL is returned, the scsi_done function MUST have been called
+ *
+ *-------------------------------------------------------------------*/
+static mega_scb *mega_build_cmd(mega_host_config *megaCfg, Scsi_Cmnd *SCpnt)
+{
+ mega_scb *pScb;
+ mega_mailbox *mbox;
+ mega_passthru *pthru;
+ long seg;
+
+ /* We don't support multi-luns */
+ if (SCpnt->lun != 0) {
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone(SCpnt);
+ return NULL;
+ }
+
+ /*-----------------------------------------------------
+ *
+ * Logical drive commands
+ *
+ *-----------------------------------------------------*/
+ if (SCpnt->channel == megaCfg->host->max_channel) {
+ switch(SCpnt->cmnd[0]) {
+ case TEST_UNIT_READY:
+ memset(SCpnt->request_buffer, 0, SCpnt->request_bufflen);
+ SCpnt->result = (DID_OK << 16);
+ callDone(SCpnt);
+ return NULL;
+
+ case MODE_SENSE:
+ memset(SCpnt->request_buffer, 0, SCpnt->cmnd[4]);
+ SCpnt->result = (DID_OK << 16);
+ callDone(SCpnt);
+ return NULL;
+
+ case READ_CAPACITY:
+ case INQUIRY:
+ /* Allocate a SCB and initialize passthru */
+ if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+ SCpnt->result = (DID_ERROR << 16);
+ callDone(SCpnt);
+ return NULL;
+ }
+ pthru = &pScb->pthru;
+ mbox = (mega_mailbox *)&pScb->mboxData;
+
+ memset(mbox, 0, sizeof(pScb->mboxData));
+ memset(pthru, 0, sizeof(mega_passthru));
+ pthru->timeout = 0;
+ pthru->ars = 0;
+ pthru->islogical = 1;
+ pthru->logdrv = SCpnt->target;
+ pthru->cdblen = SCpnt->cmd_len;
+ pthru->dataxferaddr = virt_to_bus(SCpnt->request_buffer);
+ pthru->dataxferlen = SCpnt->request_bufflen;
+ memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ /* Initialize mailbox area */
+ mbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+ mbox->xferaddr = virt_to_bus(pthru);
+
+ return pScb;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ /* Allocate a SCB and initialize mailbox */
+ if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+ SCpnt->result = (DID_ERROR << 16);
+ callDone(SCpnt);
+ return NULL;
+ }
+ mbox = (mega_mailbox *)&pScb->mboxData;
+
+ memset(mbox, 0, sizeof(pScb->mboxData));
+ mbox->logdrv = SCpnt->target;
+ mbox->cmd = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ?
+ MEGA_MBOXCMD_LREAD : MEGA_MBOXCMD_LWRITE;
+
+ /* 6-byte */
+ if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) {
+ mbox->numsectors =
+ (u_long)SCpnt->cmnd[4];
+ mbox->lba =
+ ((u_long)SCpnt->cmnd[1] << 16) |
+ ((u_long)SCpnt->cmnd[2] << 8) |
+ (u_long)SCpnt->cmnd[3];
+ mbox->lba &= 0x1FFFFF;
+ }
+
+ /* 10-byte */
+ if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) {
+ mbox->numsectors =
+ (u_long)SCpnt->cmnd[8] |
+ ((u_long)SCpnt->cmnd[7] << 8);
+ mbox->lba =
+ ((u_long)SCpnt->cmnd[2] << 24) |
+ ((u_long)SCpnt->cmnd[3] << 16) |
+ ((u_long)SCpnt->cmnd[4] << 8) |
+ (u_long)SCpnt->cmnd[5];
+ }
+
+ /* Calculate Scatter-Gather info */
+ mbox->numsgelements = build_sglist(megaCfg, pScb,
+ (u_long*)&mbox->xferaddr,
+ (u_long*)&seg);
+
+ return pScb;
+
+ default:
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone(SCpnt);
+ return NULL;
+ }
+ }
+ /*-----------------------------------------------------
+ *
+ * Passthru drive commands
+ *
+ *-----------------------------------------------------*/
+ else {
+ /* Allocate a SCB and initialize passthru */
+ if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+ SCpnt->result = (DID_ERROR << 16);
+ callDone(SCpnt);
+ return NULL;
+ }
+ pthru = &pScb->pthru;
+ mbox = (mega_mailbox *)pScb->mboxData;
+
+ memset(mbox, 0, sizeof(pScb->mboxData));
+ memset(pthru, 0, sizeof(mega_passthru));
+ pthru->timeout = 0;
+ pthru->ars = 0;
+ pthru->islogical = 0;
+ pthru->channel = SCpnt->channel;
+ pthru->target = SCpnt->target;
+ pthru->cdblen = SCpnt->cmd_len;
+ memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ pthru->numsgelements = build_sglist(megaCfg, pScb,
+ (u_long *)&pthru->dataxferaddr,
+ (u_long *)&pthru->dataxferlen);
+
+ /* Initialize mailbox */
+ mbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+ mbox->xferaddr = virt_to_bus(pthru);
+
+ return pScb;
+ }
+ return NULL;
+}
+
+/*--------------------------------------------------------------------
+ * Interrupt service routine
+ *--------------------------------------------------------------------*/
+static void megaraid_isr(int irq, void *devp, struct pt_regs *regs)
+{
+ mega_host_config *megaCfg;
+ u_char byte, idx, sIdx;
+ u_long dword;
+ mega_mailbox *mbox;
+ mega_scb *pScb;
+ long flags;
+ int qCnt, qStatus;
+
+ megaCfg = (mega_host_config *)devp;
+ mbox = (mega_mailbox *)megaCfg->mbox;
+
+ if (megaCfg->host->irq == irq) {
+ spin_lock_irqsave(&mega_lock,flags);
+
+ if (megaCfg->flag & IN_ISR) {
+ TRACE(("ISR called reentrantly!!\n"));
+ }
+
+ megaCfg->flag |= IN_ISR;
+
+ /* Check if a valid interrupt is pending */
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ dword = RDOUTDOOR(megaCfg);
+ if (dword != 0x10001234) {
+ /* Spurious interrupt */
+ megaCfg->flag &= ~IN_ISR;
+ spin_unlock_irqrestore(&mega_lock,flags);
+ return;
+ }
+ WROUTDOOR(megaCfg,dword);
+ } else {
+ byte = READ_PORT(megaCfg->host->io_port, INTR_PORT);
+ if ((byte & VALID_INTR_BYTE) == 0) {
+ /* Spurious interrupt */
+ megaCfg->flag &= ~IN_ISR;
+ spin_unlock_irqrestore(&mega_lock,flags);
+ return;
+ }
+ WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
+ }
+
+ qCnt = mbox->numstatus;
+ qStatus = mbox->status;
+
+ if (qCnt > 1) {TRACE(("ISR: Received %d status\n", qCnt))
+ printk(KERN_DEBUG "Got numstatus = %d\n",qCnt);
+ }
+
+ for(idx=0; idx<qCnt; idx++) {
+ sIdx = mbox->completed[idx];
+ if (sIdx > 0) {
+ pScb = &megaCfg->scbList[sIdx-1];
+ spin_unlock_irqrestore(&mega_lock,flags); /* locks within cmd_done */
+ mega_cmd_done(megaCfg,&megaCfg->scbList[sIdx-1], qStatus);
+ spin_lock_irqsave(&mega_lock,flags);
+ }
+ }
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox)|0x2);
+ while (RDINDOOR(megaCfg) & 0x02);
+ } else {
+ CLEAR_INTR(megaCfg->host->io_port);
+ }
+
+ megaCfg->flag &= ~IN_ISR;
+ megaCfg->flag &= ~PENDING;
+
+ spin_unlock_irqrestore(&mega_lock,flags);
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ mega_runque(NULL);
+ spin_unlock_irqrestore(&io_request_lock,flags);
+
+#if 0
+ /* Queue as a delayed ISR routine */
+ queue_task_irq_off(&runq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ spin_unlock_irqrestore(&mega_lock,flags);
+#endif
+
+ }
+}
+
+/*==================================================*/
+/* Wait until the controller's mailbox is available */
+/*==================================================*/
+static int busyWaitMbox(mega_host_config *megaCfg)
+{
+ mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
+ long counter;
+
+ for(counter=0; counter<0xFFFFFF; counter++) {
+ if (!mbox->busy) return 0;
+ }
+ return -1;
+}
+
+/*=====================================================
+ * Post a command to the card
+ *
+ * Arguments:
+ * mega_host_config *megaCfg - Controller structure
+ * u_char *mboxData - Mailbox area, 16 bytes
+ * mega_scb *pScb - SCB posting (or NULL if N/A)
+ * int intr - if 1, interrupt, 0 is blocking
+ *=====================================================*/
+static int MegaIssueCmd(mega_host_config *megaCfg,
+ u_char *mboxData,
+ mega_scb *pScb,
+ int intr)
+{
+ mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
+ long flags;
+ u_char byte;
+ u_long cmdDone;
+
+ mboxData[0x1] = (pScb ? pScb->idx+1 : 0x00); /* Set cmdid */
+ mboxData[0xF] = 1; /* Set busy */
+
+ /* one bad report of problem when issuing a command while pending.
+ * Wasn't able to duplicate, but it doesn't really affect performance
+ * anyway, so don't allow command while PENDING
+ */
+ if (megaCfg->flag & PENDING) {
+ return -1;
+ }
+
+ /* Wait until mailbox is free */
+ if (busyWaitMbox(megaCfg)) {
+ if (pScb) {
+ TRACE(("Mailbox busy %.08lx <%d.%d.%d>\n", pScb->SCpnt->serial_number,
+ pScb->SCpnt->channel, pScb->SCpnt->target, pScb->SCpnt->lun));
+ }
+ return -1;
+ }
+
+ /* Copy mailbox data into host structure */
+ spin_lock_irqsave(&mega_lock,flags);
+ memset(mbox, 0, sizeof(mega_mailbox));
+ memcpy(mbox, mboxData, 16);
+ spin_unlock_irqrestore(&mega_lock,flags);
+
+ /* Kick IO */
+ megaCfg->flag |= PENDING;
+ if (intr) {
+ /* Issue interrupt (non-blocking) command */
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ mbox->mraid_poll = 0;
+ mbox->mraid_ack = 0;
+ WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
+ } else {
+ ENABLE_INTR(megaCfg->host->io_port);
+ ISSUE_COMMAND(megaCfg->host->io_port);
+ }
+ }
+ else { /* Issue non-ISR (blocking) command */
+
+ if (megaCfg->flag & BOARD_QUARTZ) {
+
+ mbox->mraid_poll = 0;
+ mbox->mraid_ack = 0;
+ WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
+
+ while((cmdDone=RDOUTDOOR(megaCfg)) != 0x10001234);
+ WROUTDOOR(megaCfg, cmdDone);
+
+ if (pScb) {
+ mega_cmd_done(megaCfg,pScb, mbox->status);
+ mega_rundoneq();
+ }
+
+ WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox) | 0x2);
+ while(RDINDOOR(megaCfg) & 0x2);
+
+ megaCfg->flag &= ~PENDING;
+ }
+ else {
+ DISABLE_INTR(megaCfg->host->io_port);
+ ISSUE_COMMAND(megaCfg->host->io_port);
+
+ while(!((byte=READ_PORT(megaCfg->host->io_port,INTR_PORT))&INTR_VALID));
+ WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
+
+ ENABLE_INTR(megaCfg->host->io_port);
+ CLEAR_INTR(megaCfg->host->io_port);
+
+ if (pScb) {
+ mega_cmd_done(megaCfg,pScb, mbox->status);
+ mega_rundoneq();
+ }
+ megaCfg->flag &= ~PENDING;
+ }
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------
+ * Copies data to SGLIST
+ *-------------------------------------------------------------------*/
+static int build_sglist(mega_host_config *megaCfg, mega_scb *scb,
+ u_long *buffer, u_long *length)
+{
+ struct scatterlist *sgList;
+ int idx;
+
+ /* Scatter-gather not used */
+ if (scb->SCpnt->use_sg == 0) {
+ *buffer = virt_to_bus(scb->SCpnt->request_buffer);
+ *length = (u_long)scb->SCpnt->request_bufflen;
+ return 0;
+ }
+
+ sgList = (struct scatterlist *)scb->SCpnt->buffer;
+ if (scb->SCpnt->use_sg == 1) {
+ *buffer = virt_to_bus(sgList[0].address);
+ *length = (u_long)sgList[0].length;
+ return 0;
+ }
+
+ /* Copy Scatter-Gather list info into controller structure */
+ for(idx=0; idx<scb->SCpnt->use_sg; idx++) {
+ scb->sgList[idx].address = virt_to_bus(sgList[idx].address);
+ scb->sgList[idx].length = (u_long)sgList[idx].length;
+ }
+
+ /* Reset pointer and length fields */
+ *buffer = virt_to_bus(scb->sgList);
+ *length = 0;
+
+ /* Return count of SG requests */
+ return scb->SCpnt->use_sg;
+}
+
+/*--------------------------------------------------------------------
+ * Initializes the adress of the controller's mailbox register
+ * The mailbox register is used to issue commands to the card.
+ * Format of the mailbox area:
+ * 00 01 command
+ * 01 01 command id
+ * 02 02 # of sectors
+ * 04 04 logical bus address
+ * 08 04 physical buffer address
+ * 0C 01 logical drive #
+ * 0D 01 length of scatter/gather list
+ * 0E 01 reserved
+ * 0F 01 mailbox busy
+ * 10 01 numstatus byte
+ * 11 01 status byte
+ *--------------------------------------------------------------------*/
+static int mega_register_mailbox(mega_host_config *megaCfg, u_long paddr)
+{
+ /* align on 16-byte boundry */
+ megaCfg->mbox = &megaCfg->mailbox;
+ megaCfg->mbox = (mega_mailbox *) ((((ulong)megaCfg->mbox) + 16)&0xfffffff0);
+ paddr = (paddr+16)&0xfffffff0;
+
+ /* Register mailbox area with the firmware */
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ }
+ else {
+ WRITE_PORT(megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF);
+ WRITE_PORT(megaCfg->host->io_port, MBOX_PORT1, (paddr >> 8) & 0xFF);
+ WRITE_PORT(megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF);
+ WRITE_PORT(megaCfg->host->io_port, MBOX_PORT3, (paddr >> 24) & 0xFF);
+ WRITE_PORT(megaCfg->host->io_port, ENABLE_MBOX_REGION, ENABLE_MBOX_BYTE);
+
+ CLEAR_INTR(megaCfg->host->io_port);
+ ENABLE_INTR(megaCfg->host->io_port);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------
+ * Issue an adapter info query to the controller
+ *-------------------------------------------------------------------*/
+static int mega_i_query_adapter(mega_host_config *megaCfg)
+{
+ mega_RAIDINQ *adapterInfo;
+ mega_mailbox *mbox;
+ u_char mboxData[16];
+ u_long paddr;
+
+ spin_lock_init(&mega_lock);
+ /* Initialize adapter inquiry */
+ paddr = virt_to_bus(megaCfg->mega_buffer);
+ mbox = (mega_mailbox *)mboxData;
+
+ memset((void *)megaCfg->mega_buffer, 0, sizeof(megaCfg->mega_buffer));
+ memset(mbox, 0, 16);
+
+ /* Initialize mailbox registers */
+ mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ;
+ mbox->xferaddr = paddr;
+
+ /* Issue a blocking command to the card */
+ MegaIssueCmd(megaCfg, mboxData, NULL, 0);
+
+ /* Initialize host/local structures with Adapter info */
+ adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+ megaCfg->host->max_channel = adapterInfo->AdpInfo.ChanPresent;
+ megaCfg->host->max_id = adapterInfo->AdpInfo.MaxTargPerChan;
+ megaCfg->numldrv = adapterInfo->LogdrvInfo.NumLDrv;
+
+#if 0
+ printk(KERN_DEBUG "---- Logical drive info ----\n");
+ for(i=0; i<megaCfg->numldrv; i++) {
+ printk(KERN_DEBUG "%d: size: %ld prop: %x state: %x\n",i,
+ adapterInfo->LogdrvInfo.LDrvSize[i],
+ adapterInfo->LogdrvInfo.LDrvProp[i],
+ adapterInfo->LogdrvInfo.LDrvState[i]);
+ }
+ printk(KERN_DEBUG "---- Physical drive info ----\n");
+ for(i=0; i<MAX_PHYSICAL_DRIVES; i++) {
+ if (i && !(i % 8)) printk("\n");
+ printk("%d: %x ", i, adapterInfo->PhysdrvInfo.PDrvState[i]);
+ }
+ printk("\n");
+#endif
+
+ megaCfg->max_cmds = adapterInfo->AdpInfo.MaxConcCmds;
+
+#ifdef HP /* use HP firmware and bios version encoding */
+ sprintf(megaCfg->fwVer,"%c%d%d.%d%d",
+ adapterInfo->AdpInfo.FwVer[2],
+ adapterInfo->AdpInfo.FwVer[1] >> 8,
+ adapterInfo->AdpInfo.FwVer[1] & 0x0f,
+ adapterInfo->AdpInfo.FwVer[2] >> 8,
+ adapterInfo->AdpInfo.FwVer[2] & 0x0f);
+ sprintf(megaCfg->biosVer,"%c%d%d.%d%d",
+ adapterInfo->AdpInfo.BiosVer[2],
+ adapterInfo->AdpInfo.BiosVer[1] >> 8,
+ adapterInfo->AdpInfo.BiosVer[1] & 0x0f,
+ adapterInfo->AdpInfo.BiosVer[2] >> 8,
+ adapterInfo->AdpInfo.BiosVer[2] & 0x0f);
+#else
+ memcpy(megaCfg->fwVer, adapterInfo->AdpInfo.FwVer, 4);
+ megaCfg->fwVer[4] = 0;
+
+ memcpy(megaCfg->biosVer, adapterInfo->AdpInfo.BiosVer, 4);
+ megaCfg->biosVer[4] = 0;
+#endif
+
+ printk(KERN_INFO "megaraid: [%s:%s] detected %d logical drives" CRLFSTR,
+ megaCfg->fwVer,
+ megaCfg->biosVer,
+ megaCfg->numldrv);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Driver interface functions
+ *
+ *-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------
+ * Returns data to be displayed in /proc/scsi/megaraid/X
+ *----------------------------------------------------------*/
+int megaraid_proc_info(char *buffer, char **start, off_t offset,
+ int length, int inode, int inout)
+{
+ *start = buffer;
+ return 0;
+}
+
+int findCard(Scsi_Host_Template *pHostTmpl,
+ u_short pciVendor, u_short pciDev,
+ long flag)
+{
+ mega_host_config *megaCfg;
+ struct Scsi_Host *host;
+ u_char pciBus, pciDevFun, megaIrq;
+ u_long megaBase;
+ u_short pciIdx = 0;
+
+#if LINUX_VERSION_CODE < 0x20100
+ while(!pcibios_find_device(pciVendor, pciDev, pciIdx,&pciBus,&pciDevFun)) {
+#else
+ struct pci_dev *pdev=pci_devices;
+
+ while((pdev = pci_find_device(pciVendor, pciDev, pdev))) {
+ pciBus = pdev->bus->number;
+ pciDevFun = pdev->devfn;
+#endif
+ printk(KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:fun %d\n",
+ pciVendor,
+ pciDev,
+ pciIdx, pciBus,
+ PCI_SLOT(pciDevFun),
+ PCI_FUNC(pciDevFun));
+
+ /* Read the base port and IRQ from PCI */
+#if LINUX_VERSION_CODE < 0x20100
+ pcibios_read_config_dword(pciBus, pciDevFun,
+ PCI_BASE_ADDRESS_0,
+ (u_int *)&megaBase);
+ pcibios_read_config_byte(pciBus, pciDevFun,
+ PCI_INTERRUPT_LINE,
+ &megaIrq);
+#else
+ megaBase = pdev->base_address[0];
+ megaIrq = pdev->irq;
+#endif
+ pciIdx++;
+
+ if (flag & BOARD_QUARTZ) {
+ megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
+ megaBase = (long) ioremap(megaBase,128);
+ }
+ else {
+ megaBase &= PCI_BASE_ADDRESS_IO_MASK;
+ megaBase += 0x10;
+ }
+
+ /* Initialize SCSI Host structure */
+ host = scsi_register(pHostTmpl, sizeof(mega_host_config));
+ megaCfg = (mega_host_config *)host->hostdata;
+ memset(megaCfg, 0, sizeof(mega_host_config));
+
+ printk(KERN_INFO " scsi%d: Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR,
+ host->host_no, (u_int)megaBase, megaIrq);
+
+ /* Copy resource info into structure */
+ megaCfg->flag = flag;
+ megaCfg->host = host;
+ megaCfg->base = megaBase;
+ megaCfg->host->irq = megaIrq;
+ megaCfg->host->io_port = megaBase;
+ megaCfg->host->n_io_port = 16;
+ megaCfg->host->unique_id = (pciBus << 8) | pciDevFun;
+ megaCtlrs[numCtlrs++] = megaCfg;
+
+ if (flag != BOARD_QUARTZ) {
+ /* Request our IO Range */
+ if (check_region(megaBase, 16)) {
+ printk(KERN_WARNING "megaraid: Couldn't register I/O range!" CRLFSTR);
+ scsi_unregister(host);
+ continue;
+ }
+ request_region(megaBase, 16, "megaraid");
+ }
+
+ /* Request our IRQ */
+ if (request_irq(megaIrq, megaraid_isr, SA_SHIRQ,
+ "megaraid", megaCfg)) {
+ printk(KERN_WARNING "megaraid: Couldn't register IRQ %d!" CRLFSTR,
+ megaIrq);
+ scsi_unregister(host);
+ continue;
+ }
+
+ mega_register_mailbox(megaCfg, virt_to_bus((void*)&megaCfg->mailbox));
+ mega_i_query_adapter(megaCfg);
+
+ /* Initialize SCBs */
+ initSCB(megaCfg);
+
+ }
+ return pciIdx;
+}
+
+/*---------------------------------------------------------
+ * Detects if a megaraid controller exists in this system
+ *---------------------------------------------------------*/
+int megaraid_detect(Scsi_Host_Template *pHostTmpl)
+{
+ int count = 0;
+
+ pHostTmpl->proc_dir = &proc_scsi_megaraid;
+
+#if LINUX_VERSION_CODE < 0x20100
+ if (!pcibios_present())
+ {
+ printk(KERN_WARNING "megaraid: PCI bios not present." CRLFSTR);
+ return 0;
+ }
+#endif
+
+ count += findCard(pHostTmpl, 0x101E, 0x9010, 0);
+ count += findCard(pHostTmpl, 0x101E, 0x9060, 0);
+ count += findCard(pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ);
+
+ return count;
+}
+
+/*---------------------------------------------------------------------
+ * Release the controller's resources
+ *---------------------------------------------------------------------*/
+int megaraid_release(struct Scsi_Host *pSHost)
+{
+ mega_host_config *megaCfg;
+ mega_mailbox *mbox;
+ u_char mboxData[16];
+
+ megaCfg = (mega_host_config*)pSHost->hostdata;
+ mbox = (mega_mailbox *)mboxData;
+
+ /* Flush cache to disk */
+ memset(mbox, 0, 16);
+ mboxData[0] = 0xA;
+
+ /* Issue a blocking (interrupts disabled) command to the card */
+ MegaIssueCmd(megaCfg, mboxData, NULL, 0);
+
+ schedule();
+
+ /* Free our resources */
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ iounmap((void *)megaCfg->base);
+ } else {
+ release_region(megaCfg->host->io_port, 16);
+ }
+ free_irq(megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise
+ extra interrupt is generated */
+ scsi_unregister(pSHost);
+
+ return 0;
+}
+
+/*----------------------------------------------
+ * Get information about the card/driver
+ *----------------------------------------------*/
+const char *megaraid_info(struct Scsi_Host *pSHost)
+{
+ static char buffer[512];
+ mega_host_config *megaCfg;
+ mega_RAIDINQ *adapterInfo;
+
+ megaCfg = (mega_host_config *)pSHost->hostdata;
+ adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+
+ sprintf(buffer, "AMI MegaRAID %s %d commands %d targs %d chans",
+ megaCfg->fwVer,
+ adapterInfo->AdpInfo.MaxConcCmds,
+ megaCfg->host->max_id,
+ megaCfg->host->max_channel);
+ return buffer;
+}
+
+/*-----------------------------------------------------------------
+ * Perform a SCSI command
+ * Mailbox area:
+ * 00 01 command
+ * 01 01 command id
+ * 02 02 # of sectors
+ * 04 04 logical bus address
+ * 08 04 physical buffer address
+ * 0C 01 logical drive #
+ * 0D 01 length of scatter/gather list
+ * 0E 01 reserved
+ * 0F 01 mailbox busy
+ * 10 01 numstatus byte
+ * 11 01 status byte
+ *-----------------------------------------------------------------*/
+int megaraid_queue(Scsi_Cmnd *SCpnt, void (*pktComp)(Scsi_Cmnd *))
+{
+ mega_host_config *megaCfg;
+ mega_scb *pScb;
+
+ megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+ if (!(megaCfg->flag & (1L << SCpnt->channel))) {
+ printk(KERN_INFO "scsi%d: scanning channel %c for devices.\n",
+ megaCfg->host->host_no,
+ SCpnt->channel + 'A');
+ megaCfg->flag |= (1L << SCpnt->channel);
+ }
+
+ SCpnt->scsi_done = pktComp;
+
+ /* Allocate and build a SCB request */
+ if ((pScb = mega_build_cmd(megaCfg, SCpnt)) != NULL) {
+ /* Add SCB to the head of the pending queue */
+ ENQUEUE(pScb, mega_scb, qPending, next);
+
+ /* Issue the command to the card */
+ mega_runque(NULL);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Issue a blocking command to the controller
+ *
+ * Note - this isnt 2.0.x SMP safe
+ *----------------------------------------------------------------------*/
+volatile static int internal_done_flag = 0;
+volatile static int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd *SCpnt)
+{
+ internal_done_errcode = SCpnt->result;
+ internal_done_flag++;
+}
+
+/*
+ * This seems dangerous in an SMP environment because
+ * while spinning on internal_done_flag in 2.0.x SMP
+ * no IRQ's will be taken, including those that might
+ * be needed to clear this.
+ *
+ * I think this should be using a wait queue ?
+ * -- AC
+ */
+
+int megaraid_command(Scsi_Cmnd *SCpnt)
+{
+ internal_done_flag = 0;
+
+ /* Queue command, and wait until it has completed */
+ megaraid_queue(SCpnt, internal_done);
+
+ while(!internal_done_flag)
+ barrier();
+
+ return internal_done_errcode;
+}
+
+/*---------------------------------------------------------------------
+ * Abort a previous SCSI request
+ *---------------------------------------------------------------------*/
+int megaraid_abort(Scsi_Cmnd *SCpnt)
+{
+ mega_host_config *megaCfg;
+ int idx;
+ long flags;
+
+ spin_lock_irqsave(&mega_lock,flags);
+
+ megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+ TRACE(("ABORT!!! %.08lx %.02x <%d.%d.%d>\n",
+ SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target,
+ SCpnt->lun));
+ /*
+ * Walk list of SCBs for any that are still outstanding
+ */
+ for(idx=0; idx<megaCfg->max_cmds; idx++) {
+ if (megaCfg->scbList[idx].idx >= 0) {
+ if (megaCfg->scbList[idx].SCpnt == SCpnt) {
+ freeSCB(&megaCfg->scbList[idx]);
+
+ SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
+ callDone(SCpnt);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&mega_lock,flags);
+ return SCSI_ABORT_SNOOZE;
+}
+
+/*---------------------------------------------------------------------
+ * Reset a previous SCSI request
+ *---------------------------------------------------------------------*/
+int megaraid_reset(Scsi_Cmnd *SCpnt, unsigned int rstflags)
+{
+ mega_host_config *megaCfg;
+ int idx;
+ long flags;
+
+ spin_lock_irqsave(&mega_lock,flags);
+
+ megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+ TRACE(("RESET: %.08lx %.02x <%d.%d.%d>\n",
+ SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target,
+ SCpnt->lun));
+
+ /*
+ * Walk list of SCBs for any that are still outstanding
+ */
+ for(idx=0; idx<megaCfg->max_cmds; idx++) {
+ if (megaCfg->scbList[idx].idx >= 0) {
+ SCpnt = megaCfg->scbList[idx].SCpnt;
+ freeSCB(&megaCfg->scbList[idx]);
+ SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
+ callDone(SCpnt);
+ }
+ }
+ spin_unlock_irqrestore(&mega_lock,flags);
+ return SCSI_RESET_PUNT;
+}
+
+/*-------------------------------------------------------------
+ * Return the disk geometry for a particular disk
+ * Input:
+ * Disk *disk - Disk geometry
+ * kdev_t dev - Device node
+ * int *geom - Returns geometry fields
+ * geom[0] = heads
+ * geom[1] = sectors
+ * geom[2] = cylinders
+ *-------------------------------------------------------------*/
+int megaraid_biosparam(Disk *disk, kdev_t dev, int *geom)
+{
+ int heads, sectors, cylinders;
+ mega_host_config *megaCfg;
+
+ /* Get pointer to host config structure */
+ megaCfg = (mega_host_config *)disk->device->host->hostdata;
+
+ /* Default heads (64) & sectors (32) */
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ /* Handle extended translation size for logical drives > 1Gb */
+ if (disk->capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+
+ /* return result */
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return 0;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = MEGARAID;
+
+#include "scsi_module.c"
+#endif
--- /dev/null
+#ifndef __MEGARAID_H__
+#define __MEGARAID_H__
+
+#define IN_ISR 0x80000000L
+#define NO_INTR 0x40000000L
+#define IN_TIMEOUT 0x20000000L
+#define PENDING 0x10000000L
+#define BOARD_QUARTZ 0x08000000L
+
+#define SCB_ACTIVE 0x1
+#define SCB_WAITQ 0x2
+#define SCB_ISSUED 0x4
+
+#define SCB_FREE -1
+#define SCB_RESET -2
+#define SCB_ABORT -3
+#define SCB_LOCKED -4
+
+#define MEGA_CMD_TIMEOUT 10
+
+#define MAX_SGLIST 20
+#define MAX_COMMANDS 254
+
+#define MAX_LOGICAL_DRIVES 8
+#define MAX_CHANNEL 5
+#define MAX_TARGET 15
+#define MAX_PHYSICAL_DRIVES MAX_CHANNEL*MAX_TARGET
+
+#define INQUIRY_DATA_SIZE 0x24
+#define MAX_CDB_LEN 0x0A
+#define MAX_REQ_SENSE_LEN 0x20
+
+#define INTR_VALID 0x40
+
+/* Mailbox commands */
+#define MEGA_MBOXCMD_LREAD 0x01
+#define MEGA_MBOXCMD_LWRITE 0x02
+#define MEGA_MBOXCMD_PASSTHRU 0x03
+#define MEGA_MBOXCMD_ADAPTERINQ 0x05
+
+/* Offsets into Mailbox */
+#define COMMAND_PORT 0x00
+#define COMMAND_ID_PORT 0x01
+#define SG_LIST_PORT0 0x08
+#define SG_LIST_PORT1 0x09
+#define SG_LIST_PORT2 0x0a
+#define SG_LIST_PORT3 0x0b
+#define SG_ELEMENT_PORT 0x0d
+#define NO_FIRED_PORT 0x0f
+
+/* I/O Port offsets */
+#define I_CMD_PORT 0x00
+#define I_ACK_PORT 0x00
+#define I_TOGGLE_PORT 0x01
+#define INTR_PORT 0x0a
+
+#define MAILBOX_SIZE 70
+#define MBOX_BUSY_PORT 0x00
+#define MBOX_PORT0 0x04
+#define MBOX_PORT1 0x05
+#define MBOX_PORT2 0x06
+#define MBOX_PORT3 0x07
+#define ENABLE_MBOX_REGION 0x0B
+
+/* I/O Port Values */
+#define ISSUE_BYTE 0x10
+#define ACK_BYTE 0x08
+#define ENABLE_INTR_BYTE 0xc0
+#define DISABLE_INTR_BYTE 0x00
+#define VALID_INTR_BYTE 0x40
+#define MBOX_BUSY_BYTE 0x10
+#define ENABLE_MBOX_BYTE 0x00
+
+/* Setup some port macros here */
+#define WRITE_MAILBOX(base,offset,value) *(base+offset)=value
+#define READ_MAILBOX(base,offset) *(base+offset)
+
+#define WRITE_PORT(base,offset,value) outb_p(value,base+offset)
+#define READ_PORT(base,offset) inb_p(base+offset)
+
+#define ISSUE_COMMAND(base) WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE)
+#define CLEAR_INTR(base) WRITE_PORT(base,I_ACK_PORT,ACK_BYTE)
+#define ENABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE)
+#define DISABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE)
+
+/* Define AMI's PCI codes */
+#undef PCI_VENDOR_ID_AMI
+#undef PCI_DEVICE_ID_AMI_MEGARAID
+
+#ifndef PCI_VENDOR_ID_AMI
+#define PCI_VENDOR_ID_AMI 0x101E
+#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
+#endif
+
+#define PCI_CONF_BASE_ADDR_OFFSET 0x10
+#define PCI_CONF_IRQ_OFFSET 0x3c
+
+#if LINUX_VERSION_CODE < 0x20100
+#define MEGARAID \
+ { NULL, /* Next */\
+ NULL, /* Usage Count Pointer */\
+ NULL, /* /proc Directory Entry */\
+ megaraid_proc_info, /* /proc Info Function */\
+ "MegaRAID", /* Driver Name */\
+ megaraid_detect, /* Detect Host Adapter */\
+ megaraid_release, /* Release Host Adapter */\
+ megaraid_info, /* Driver Info Function */\
+ megaraid_command, /* Command Function */\
+ megaraid_queue, /* Queue Command Function */\
+ megaraid_abort, /* Abort Command Function */\
+ megaraid_reset, /* Reset Command Function */\
+ NULL, /* Slave Attach Function */\
+ megaraid_biosparam, /* Disk BIOS Parameters */\
+ 1, /* # of cmds that can be\
+ outstanding at any time */\
+ 7, /* HBA Target ID */\
+ MAX_SGLIST, /* Scatter/Gather Table Size */\
+ 1, /* SCSI Commands per LUN */\
+ 0, /* Present */\
+ 0, /* Default Unchecked ISA DMA */\
+ ENABLE_CLUSTERING } /* Enable Clustering */
+#else
+#define MEGARAID \
+ {\
+ name: "MegaRAID", /* Driver Name */\
+ proc_info: megaraid_proc_info, /* /proc driver info */\
+ detect: megaraid_detect, /* Detect Host Adapter */\
+ release: megaraid_release, /* Release Host Adapter */\
+ info: megaraid_info, /* Driver Info Function */\
+ command: megaraid_command, /* Command Function */\
+ queuecommand: megaraid_queue, /* Queue Command Function */\
+ abort: megaraid_abort, /* Abort Command Function */\
+ reset: megaraid_reset, /* Reset Command Function */\
+ bios_param: megaraid_biosparam, /* Disk BIOS Parameters */\
+ can_queue: 255, /* Can Queue */\
+ this_id: 7, /* HBA Target ID */\
+ sg_tablesize: MAX_SGLIST, /* Scatter/Gather Table Size */\
+ cmd_per_lun: 1, /* SCSI Commands per LUN */\
+ present: 0, /* Present */\
+ unchecked_isa_dma:0, /* Default Unchecked ISA DMA */\
+ use_clustering: ENABLE_CLUSTERING /* Enable Clustering */\
+ }
+#endif
+
+/* Structures */
+typedef struct _mega_ADP_INFO
+{
+ u_char MaxConcCmds;
+ u_char RbldRate;
+ u_char MaxTargPerChan;
+ u_char ChanPresent;
+ u_char FwVer[4];
+ u_short AgeOfFlash;
+ u_char ChipSet;
+ u_char DRAMSize;
+ u_char CacheFlushInterval;
+ u_char BiosVer[4];
+ u_char resvd[7];
+} mega_ADP_INFO;
+
+typedef struct _mega_LDRV_INFO
+{
+ u_char NumLDrv;
+ u_char resvd[3];
+ u_long LDrvSize[MAX_LOGICAL_DRIVES];
+ u_char LDrvProp[MAX_LOGICAL_DRIVES];
+ u_char LDrvState[MAX_LOGICAL_DRIVES];
+} mega_LDRV_INFO;
+
+typedef struct _mega_PDRV_INFO
+{
+ u_char PDrvState[MAX_PHYSICAL_DRIVES];
+ u_char resvd;
+} mega_PDRV_INFO;
+
+// RAID inquiry: Mailbox command 0x5
+typedef struct _mega_RAIDINQ
+{
+ mega_ADP_INFO AdpInfo;
+ mega_LDRV_INFO LogdrvInfo;
+ mega_PDRV_INFO PhysdrvInfo;
+} mega_RAIDINQ;
+
+// Passthrough command: Mailbox command 0x3
+typedef struct mega_passthru
+{
+ u_char timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ u_char ars:1;
+ u_char reserved:3;
+ u_char islogical:1;
+ u_char logdrv; /* if islogical == 1 */
+ u_char channel; /* if islogical == 0 */
+ u_char target; /* if islogical == 0 */
+ u_char queuetag; /* unused */
+ u_char queueaction; /* unused */
+ u_char cdb[MAX_CDB_LEN];
+ u_char cdblen;
+ u_char reqsenselen;
+ u_char reqsensearea[MAX_REQ_SENSE_LEN];
+ u_char numsgelements;
+ u_char scsistatus;
+ u_long dataxferaddr;
+ u_long dataxferlen;
+} mega_passthru;
+
+typedef struct _mega_mailbox
+{
+ /* 0x0 */ u_char cmd;
+ /* 0x1 */ u_char cmdid;
+ /* 0x2 */ u_short numsectors;
+ /* 0x4 */ u_long lba;
+ /* 0x8 */ u_long xferaddr;
+ /* 0xC */ u_char logdrv;
+ /* 0xD */ u_char numsgelements;
+ /* 0xE */ u_char resvd;
+ /* 0xF */ u_char busy;
+ /* 0x10*/ u_char numstatus;
+ /* 0x11*/ u_char status;
+ /* 0x12*/ u_char completed[46];
+ u_char mraid_poll;
+ u_char mraid_ack;
+ u_char pad[16];
+} mega_mailbox;
+
+typedef struct _mega_sglist
+{
+ u_long address;
+ u_long length;
+} mega_sglist;
+
+/* Queued command data */
+typedef struct _mega_scb mega_scb;
+
+struct _mega_scb
+{
+ int idx;
+ u_long flag;
+ Scsi_Cmnd *SCpnt;
+ u_char mboxData[16];
+ mega_passthru pthru;
+ mega_sglist *sgList;
+ mega_scb *next;
+};
+
+/* Per-controller data */
+typedef struct _mega_host_config
+{
+ u_char numldrv;
+ u_long flag;
+ u_long base;
+
+ struct tq_struct megaTq;
+
+ /* Host adapter parameters */
+ u_char fwVer[7];
+ u_char biosVer[7];
+
+ struct Scsi_Host *host;
+
+ /* The following must be DMA-able!! */
+ volatile mega_mailbox *mbox;
+ volatile mega_mailbox mailbox;
+ volatile u_char mega_buffer[2*1024L];
+
+ u_char max_cmds;
+ mega_scb scbList[MAX_COMMANDS];
+} mega_host_config;
+
+extern struct proc_dir_entry proc_scsi_megaraid;
+
+const char *megaraid_info( struct Scsi_Host * );
+int megaraid_detect( Scsi_Host_Template * );
+int megaraid_release(struct Scsi_Host *);
+int megaraid_command( Scsi_Cmnd * );
+int megaraid_abort( Scsi_Cmnd * );
+int megaraid_reset( Scsi_Cmnd *, unsigned int);
+int megaraid_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
+int megaraid_biosparam( Disk *, kdev_t, int * );
+int megaraid_proc_info( char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout );
+
+#endif
release: ppa_release, \
command: ppa_command, \
queuecommand: ppa_queuecommand, \
- abort: ppa_abort, \
- reset: ppa_reset, \
+ eh_abort_handler: ppa_abort, \
+ eh_device_reset_handler: NULL, \
+ eh_bus_reset_handler: ppa_reset, \
+ eh_host_reset_handler: ppa_reset, \
bios_param: ppa_biosparam, \
this_id: -1, \
sg_tablesize: SG_ALL, \
}
else {
+ /* Actual LUN. PC ordering is 0->n IBM/spec ordering is n->0 */
+ int order_dev;
+
for (channel = 0; channel <= shpnt->max_channel; channel++) {
for (dev = 0; dev < shpnt->max_id; ++dev) {
- if (shpnt->this_id != dev) {
+ if( shpnt->reverse_ordering)
+ /* Shift to scanning 15,14,13... or 7,6,5,4, */
+ order_dev = shpnt->max_channel-dev-1;
+ else
+ order_dev = dev;
+
+ if (shpnt->this_id != order_dev) {
/*
* We need the for so our continue, etc. work fine. We put this in
max_scsi_luns : shpnt->max_lun);
sparse_lun = 0;
for (lun = 0; lun < max_dev_lun; ++lun) {
- if (!scan_scsis_single (channel, dev, lun, &max_dev_lun,
+ if (!scan_scsis_single (channel, order_dev, lun, &max_dev_lun,
&sparse_lun, &SDpnt, SCpnt, shpnt,
scsi_result)
&& !sparse_lun)
{
if(SDpnt->host->hostt == tpnt
&& SDpnt->host->hostt->module
- && SDpnt->host->hostt->module->usecount) return;
+ && GET_USE_COUNT(SDpnt->host->hostt->module)) return;
/*
* FIXME(eric) - We need to find a way to notify the
* low level driver that we are shutting down - via the
/*
* If we are busy, this is not going to fly.
*/
- if(tpnt->module->usecount != 0) return 0;
+ if(GET_USE_COUNT(tpnt->module) != 0) return 0;
/*
* Next, detach the devices from the driver.
the directory we remove is the same as parent (BUG: we
serve mountpoints later) or if it lives on a different
device.
- * sysvfs/namei.c (sysv_rmdir):
- Bugectomy: old check for victim being busy (inode->i_count)
- wasn't replaced (with checking dentry->d_count) and escaped
- Linus in the last round of changes. Shot and buried.
+ * sysv/namei.c (sysv_rmdir): See sysv/CHANGES
Fri Dec 4 00:54:12 1998 AV
* kernel/ksyms.c: Added VFS_rmdir to export list (for NFSD).
* nfsd/vfs.c: Fixed rmdir handling.
- Remaining problems with rmdir:
- UMSDOS_rename is broken. Call it with the dest. existing and
- being an empty directory and you've got EBUSY. At least it
- doesn't do any harm, so that will wait several days till rename
- cleanup.
- TODO: unlink handling in NFSD does no tests.
+Tue Dec 8 05:55:08 1998 AV
+ * vfat/namei.c: Fixed the bug in vfat_rename() introduced in the
+ first round of rmdir fixes.
+
+Wed Dec 9 03:06:10 1998 AV
+ * namei.c (do_rename): part of fs-independent checks had been moved
+ here (sticky bit handling, type mismatches). Cases of
+ the source or target being append-only or immutable also went
+ here - if we check it for parent we could as well do it for
+ children.
+ * {affs,ext2,minix,sysv,ufs}/namei.c (do_*_rename):
+ Removed tests that went to VFS, it simplified the code big way.
+ Fixed a race in check for empty target - we should check for
+ extra owners _before_ checking for emptiness, not after it.
+ * {ext2,ufs}/namei.c (do_*_rename):
+ VERY nasty bug shot: if somebody mkdired /tmp/cca01234, went
+ there, rmdired '.', waited till somebody created a file with
+ the same name and said mv . /tmp/goodbye_sticky_bit... Well,
+ goodbye sticky bit. Down, not across!
+ * {minix,sysv}/namei.c (do_*_rename):
+ Incorrect check for other owners (i_count instead of d_count).
+ Fixed.
+ * vfat: Looks like the changes above fixed a bug in VFAT - this beast
+ used to allow renaming file over directory and vice versa.
+
+Wed Dec 9 08:00:27 1998 AV
+ * namei.c (VFS_rename): New function. It gets the same arguments as
+ ->rename() method, does all checks and applies fs-specific
+ rmdir() method. It should be called with semaphores down
+ on both parents.
+ * include/linux/fs.h: Added VFS_rename
+ * kernel/ksyms.c: Added VFS_rename to export list (for NFSD).
+ * nfsd/vfs.c: Changed rename handling (switched to VFS_rename).
+
+Wed Dec 9 18:16:27 1998 AV
+ * namei.c (do_unlink): handling of sticky bit went here.
+ * {affs,ext2,minix,qnx4,sysv,ufs}/namei.c (*_unlink):
+ removed handling of sticky bit.
+ * qnx4/namei.c (qnx4_unlink):
+ Yet another inode leak. Fixed.
+
+Thu Dec 10 04:55:26 1998 AV
+ * {ext2,minix,sysv,ufs}/namei.c (*_mknod):
+ removed meaningless code handling attempts to mknod symlinks
+ and directories. VFS protects us from _that_ and if this code
+ would ever be called we'ld get a filesystem corruption.
+
+Thu Dec 10 16:58:50 1998 AV
+ * namei.c (do_rename): Fixed dentry leak that had been introduced by
+ the first round of rmdir fixes.
+
+Fri Dec 11 14:57:17 1998 AV
+ * msdos/namei.c (msdos_rmdir): Fixed race in emptiness check.
+
+Sat Dec 12 19:59:57 1998 AV
+ * msdos/namei.c (msdos_mkdir): Fixed the evil breakage introduced by
+ the changes of rmdir locking scheme. We shouldn't call
+ msdos_rmdir from there.
+
+Sun Dec 13 02:05:16 1998 AV
+ * namei.c (do_unlink):
+ Added new function: vfs_unlink, with the same arguments as
+ ->unlink() method.
+ * kernel/ksyms.c: Made it exported.
+ * include/linux/fs.h: Added prototype.
+ * nfsd/vfs.c: Changed handling of unlink (switched to vfs_unlink)
+ * {ext2,ufs}/namei.c (*_unlink): moved handling of imm./append-only to
+ VFS.
+
+Wed Dec 16 06:10:04 1998 AV
+ * namei.c (may_create, may_delete): New inline functions.
+ They check whether creation/deletion is permitted.
+ Checks from other places of namei.c went there.
+ Looks like originally I misread permission-related stuff
+ both here and in nfsd. In particular, checks for
+ immutable are done in permission(). D'oh.
+ * unlink on directory should return -EISDIR, not -EPERM as it used to
+ do. Fixed.
+ * rmdir of immutable/append-only directory shouldn't be allowed. Fixed.
+
+Remains unfixed:
+ * UMSDOS_rename is broken. Call it with the dest. existing and being an
+ empty directory and you've got EBUSY. At least it doesn't do
+ any harm, so that will wait several days till rename cleanup.
+ Sigh... It will wait a bit more. Problems with fat-derived
+ filesystems are much worse than I thought. Idea of changing
+ inode under dentry is broken by design - guess where the
+ semaphore sits, for one.
+ * umsdos: weird. rename() shouldn't return -EEXIST. BTW, manpage
+ for rename(2) is obviously bogus - it mentions EEXIST and
+ on the next line (correctly) says that EINVAL should be
+ returned. Under the same conditions.
+ * rename's handling of races is, erm, not optimal. Looks like I know
+ what to do, but this thing needs some more cleanup - we can
+ take care of almost all races in VFS and be much more graceful
+ wrt locking. Moreover, it would give strong lookup atomicity.
+ But it's a lot of changes to lookup and dcache code, so it will
+ go after the fs drivers' cleanup.
+ * hfs allows mknod. Only for regular files ;-/ IMHO it's bogus.
+ * affs allows HARD links to directories. VFS is, to put it politely,
+ not too ready to cope with _that_. And I'm not sure it should
+ be - looks like they are pretty much similar to symlinks.
+ * truncate doesn't give a damn about IO errors and disk overflows (on
+ braindead filesystems). I've submitted a patch to Linus, but
+ looks like it wasn't applied.
+ * msdos: shouldn't we treat SYS as IMMUTABLE? Makes sense, IMHO.
+ * minix, qnx and sysv do NOT allow to mkdir sticky directories.
+ * {minix,sysv}/namei.c (do_{minix,syv}_{rename,unlink}):
+ Stuff related to retries still needs cleanup/fixing.
+ Looks like I've found an extremely low-probability race
+ there...
goto unlink_done;
inode = dentry->d_inode;
- retval = -EPERM;
- if (current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid && !capable(CAP_FOWNER))
- goto unlink_done;
if ((retval = affs_remove_header(bh,inode)) < 0)
goto unlink_done;
retval = 0;
goto end_rename;
}
- if (new_inode && S_ISDIR(new_inode->i_mode)) {
- retval = -EISDIR;
- if (!S_ISDIR(old_inode->i_mode))
- goto end_rename;
- retval = -EINVAL;
- if (is_subdir(new_dentry, old_dentry))
- goto end_rename;
- if (new_dentry->d_count > 1)
- shrink_dcache_parent(new_dentry);
- retval = -ENOTEMPTY;
- if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
- goto end_rename;
- retval = -EBUSY;
- if (new_dentry->d_count > 1)
- goto end_rename;
- }
if (S_ISDIR(old_inode->i_mode)) {
- retval = -ENOTDIR;
- if (new_inode && !S_ISDIR(new_inode->i_mode))
- goto end_rename;
retval = -EINVAL;
if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ if (new_inode) {
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
+ retval = -EBUSY;
+ if (new_dentry->d_count > 1)
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
+ goto end_rename;
+ }
+
if (affs_parent_ino(old_inode) != old_dir->i_ino)
goto end_rename;
}
O_TARGET := coda.o
O_OBJS := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o coda_linux.o\
- symlink.o pioctl.o sysctl.o stats.o
+ symlink.o pioctl.o sysctl.o
M_OBJS := $(O_TARGET)
# If you want debugging output, please uncomment the following line.
return;
}
-void coda_purge_children(struct inode *inode)
+void coda_flag_inode_children(struct inode *inode, int flag)
{
struct list_head *alias;
struct dentry *alias_de;
+ ENTRY;
if ( !inode )
return;
alias = inode->i_dentry.next;
while ( alias != &inode->i_dentry ) {
alias_de = list_entry(alias, struct dentry, d_alias);
- coda_flag_children(alias_de, C_PURGE);
+ coda_flag_children(alias_de, flag);
shrink_dcache_parent(alias_de);
alias = alias->next;
}
else if (S_ISSOCK(inode->i_mode))
inode->i_op = NULL;
else {
- printk ("coda_read_inode: what's this? i_mode = %o\n",
+ printk ("coda_fill_inode: what's this? i_mode = %o\n",
inode->i_mode);
inode->i_op = NULL;
}
error = venus_getattr(sb, fid, &attr);
if ( error ) {
- CDEBUG(D_CNODE, "coda_cnode_make: coda_getvattr returned %d for %s.\n",
+ CDEBUG(D_CNODE,
+ "coda_cnode_make: coda_getvattr returned %d for %s.\n",
error, coda_f2s(fid));
*inode = NULL;
return error;
INIT_LIST_HEAD(&(cnp->c_volrootlist));
} else {
cnp->c_flags = 0;
- CDEBUG(D_CNODE, "coda_cnode make on initialized"
- "inode %ld, %s!\n",
+ printk("coda_cnode make on initialized inode %ld, %s!\n",
(*inode)->i_ino, coda_f2s(&cnp->c_fid));
}
/* recognize special .CONTROL name */
int coda_iscontrol(const char *name, size_t length)
{
- if ((CFS_CONTROLLEN == length) &&
- (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0))
+ if ((CODA_CONTROLLEN == length) &&
+ (strncmp(name, CODA_CONTROL, CODA_CONTROLLEN) == 0))
return 1;
return 0;
}
cred->cr_suid = (vuid_t) current->suid;
cred->cr_fsuid = (vuid_t) current->fsuid;
- cred->cr_gid = (vgid_t) current->gid;
+ cred->cr_groupid = (vgid_t) current->gid;
cred->cr_egid = (vgid_t) current->egid;
cred->cr_sgid = (vgid_t) current->sgid;
cred->cr_fsgid = (vgid_t) current->fsgid;
coda_flags |= C_O_TRUNC;
}
+ if ( flags & O_CREAT ) {
+ CDEBUG(D_FILE, "--> C_O_CREAT added\n");
+ coda_flags |= C_O_CREAT;
+ }
+
if ( flags & O_EXCL ) {
coda_flags |= C_O_EXCL;
CDEBUG(D_FILE, "--> C_O_EXCL added\n");
/* dentry ops */
static int coda_dentry_revalidate(struct dentry *de);
static void coda_dentry_delete(struct dentry *);
+
/* support routines */
static int coda_venus_readdir(struct file *filp, void *dirent,
filldir_t filldir);
int coda_fsync(struct file *, struct dentry *dentry);
-static int coda_refresh_inode(struct dentry *dentry);
int coda_crossvol_rename = 0;
int coda_hasmknod = 0;
-
struct dentry_operations coda_dentry_operations =
{
coda_dentry_revalidate, /* revalidate */
size_t length = entry->d_name.len;
ENTRY;
- CDEBUG(D_INODE, "name %s, len %d in ino %ld\n",
- name, length, dir->i_ino);
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("coda_lookup: inode is NULL or not a directory\n");
- return -ENOTDIR;
- }
dircnp = ITOC(dir);
- if ( length > CFS_MAXNAMLEN ) {
+ if ( length > CODA_MAXNAMLEN ) {
printk("name too long: lookup, %s (%*s)\n",
coda_f2s(&dircnp->c_fid), length, name);
return -ENAMETOOLONG;
}
-
- CDEBUG(D_INODE, "lookup: %*s in %s\n", length, name,
- coda_f2s(&dircnp->c_fid));
+
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ printk("coda_lookup: inode is NULL or not a directory\n");
+ return -ENOTDIR;
+ }
+
+
+ CDEBUG(D_INODE, "name %s, len %d in ino %ld, fid %s\n",
+ name, length, dir->i_ino, coda_f2s(&dircnp->c_fid));
/* control object, create inode on the fly */
if (coda_isroot(dir) && coda_iscontrol(name, length)) {
res_inode = NULL;
if (!error) {
- if (type & CFS_NOCACHE) {
- type &= (~CFS_NOCACHE);
+ if (type & CODA_NOCACHE) {
+ type &= (~CODA_NOCACHE);
CDEBUG(D_INODE, "dropme set for %s\n",
coda_f2s(&resfid));
dropme = 1;
int coda_permission(struct inode *inode, int mask)
{
- struct coda_inode_info *cp;
+ struct coda_inode_info *cp = ITOC(inode);
int error;
ENTRY;
coda_permission_stat.count++;
if ( mask == 0 ) {
- EXIT;
return 0;
}
}
cp = ITOC(inode);
- CHECK_CNODE(cp);
CDEBUG(D_INODE, "mask is %o\n", mask);
error = venus_access(inode->i_sb, &(cp->c_fid), mask);
return -EPERM;
dircnp = ITOC(dir);
- CHECK_CNODE(dircnp);
- if ( length > CFS_MAXNAMLEN ) {
+ if ( length > CODA_MAXNAMLEN ) {
printk("name too long: create, %s(%s)\n",
coda_f2s(&dircnp->c_fid), name);
return -ENAMETOOLONG;
return -EPERM;
dircnp = ITOC(dir);
- CHECK_CNODE(dircnp);
- if ( length > CFS_MAXNAMLEN ) {
+ if ( length > CODA_MAXNAMLEN ) {
printk("name too long: mknod, %s(%s)\n",
coda_f2s(&dircnp->c_fid), name);
return -ENAMETOOLONG;
return -ENOENT;
}
- if ( len > CFS_MAXNAMLEN )
+ if ( len > CODA_MAXNAMLEN )
return -ENAMETOOLONG;
if (coda_isroot(dir) && coda_iscontrol(name, len))
return -EPERM;
dircnp = ITOC(dir);
- CHECK_CNODE(dircnp);
CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n",
name, len, coda_f2s(&(dircnp->c_fid)), mode);
dir_cnp = ITOC(dir_inode);
cnp = ITOC(inode);
- CHECK_CNODE(cnp);
CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid)));
CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid)));
- if ( len > CFS_MAXNAMLEN ) {
+ if ( len > CODA_MAXNAMLEN ) {
printk("coda_link: name too long. \n");
return -ENAMETOOLONG;
}
++inode->i_count;
d_instantiate(de, inode);
inode->i_nlink++;
- } else {
+ } else {
d_drop(de);
+ return error;
}
CDEBUG(D_INODE, "link result %d\n",error);
if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
return -EPERM;
- if ( len > CFS_MAXNAMLEN )
+ if ( len > CODA_MAXNAMLEN )
return -ENAMETOOLONG;
symlen = strlen(symname);
- if ( symlen > CFS_MAXPATHLEN )
+ if ( symlen > CODA_MAXPATHLEN )
return -ENAMETOOLONG;
CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen);
* an inode for the entry we have to drop it.
*/
d_drop(de);
-
error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len,
symname, symlen);
+ /* mtime is no good anymore */
if ( !error ) {
dir_cnp->c_flags |= C_VATTR;
}
/* destruction routines: unlink, rmdir */
int coda_unlink(struct inode *dir, struct dentry *de)
{
- struct coda_inode_info *dircnp;
+ struct coda_inode_info *dircnp = ITOC(dir);
int error;
const char *name = de->d_name.name;
int len = de->d_name.len;
ENTRY;
coda_vfs_stat.unlink++;
- dircnp = ITOC(dir);
- CHECK_CNODE(dircnp);
-
- CDEBUG(D_INODE, " %s in %s, ino %ld\n", name ,
+ CDEBUG(D_INODE, " %s in %s, dirino %ld\n", name ,
coda_f2s(&(dircnp->c_fid)), dir->i_ino);
- /* this file should no longer be in the namecache! */
-
error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len);
-
if ( error ) {
CDEBUG(D_INODE, "upc returned error %d\n", error);
return error;
}
- /* cache management */
+ /* cache management: mtime has changed, ask Venus */
dircnp->c_flags |= C_VATTR;
de->d_inode->i_nlink--;
-
d_delete(de);
return 0;
return -ENOENT;
}
dircnp = ITOC(dir);
- CHECK_CNODE(dircnp);
- if (len > CFS_MAXNAMLEN)
+ if (len > CODA_MAXNAMLEN)
return -ENAMETOOLONG;
if (!list_empty(&de->d_hash))
return -EBUSY;
-
- /* update i_nlink and free the inode before unlinking;
- if rmdir fails a new lookup set i_nlink right.*/
- if (de->d_inode->i_nlink)
- de->d_inode->i_nlink --;
-
error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
if ( error ) {
return error;
}
+ if (de->d_inode->i_nlink)
+ de->d_inode->i_nlink --;
+
return 0;
}
ENTRY;
coda_vfs_stat.rename++;
- if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
+ if ( (old_length > CODA_MAXNAMLEN) || new_length > CODA_MAXNAMLEN ) {
return -ENAMETOOLONG;
}
}
cnp = ITOC(inode);
- CHECK_CNODE(cnp);
-
if ( !cnp->c_ovp ) {
CDEBUG(D_FILE, "open inode pointer = NULL.\n");
return -EIO;
result = open_file.f_op->readdir(&open_file, dirent, filldir);
}
coda_restore_codafile(inode, file, cnp->c_ovp, &open_file);
- return result;
EXIT;
+ return result;
}
/* ask venus to cache the file and return the inode of the container file,
struct inode *cont_inode = NULL;
unsigned short flags = f->f_flags & (~O_EXCL);
unsigned short coda_flags = coda_flags_to_cflags(flags);
+ struct coda_cred *cred;
ENTRY;
coda_vfs_stat.open++;
-
+
CDEBUG(D_SPECIAL, "OPEN inode number: %ld, count %d, flags %o.\n",
f->f_dentry->d_inode->i_ino, f->f_dentry->d_count, flags);
cnp = ITOC(i);
+
error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev);
if (error) {
CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n",
return error;
}
+ CODA_ALLOC(cred, struct coda_cred *, sizeof(*cred));
+ coda_load_creds(cred);
+ f->private_data = cred;
+
if ( cnp->c_ovp ) {
iput(cnp->c_ovp);
cnp->c_ovp = NULL;
int error;
unsigned short flags = (f->f_flags) & (~O_EXCL);
unsigned short cflags = coda_flags_to_cflags(flags);
+ struct coda_cred *cred;
ENTRY;
coda_vfs_stat.release++;
+ cred = (struct coda_cred *)f->private_data;
+
cnp =ITOC(i);
CHECK_CNODE(cnp);
CDEBUG(D_FILE,
--cnp->c_owrite;
}
- error = venus_release(i->i_sb, &(cnp->c_fid), cflags);
+ error = venus_release(i->i_sb, &(cnp->c_fid), cflags, cred);
+
+ CODA_FREE(cred, sizeof(*cred));
+ f->private_data = NULL;
CDEBUG(D_FILE, "coda_release: result: %d\n", error);
return error;
* beyond the current_dir pointer.
*/
-struct getdents_callback {
- struct linux_dirent * current_dir;
- struct linux_dirent * previous;
- int count;
- int error;
-};
+
+/* should be big enough to hold any single directory entry */
+#define DIR_BUFSIZE 2048
static int coda_venus_readdir(struct file *filp, void *getdent,
filldir_t filldir)
{
- int result = 0, offset, count, pos, error = 0;
+ int bufsize;
+ int offset = filp->f_pos; /* offset in the directory file */
+ int count = 0;
+ int pos = 0; /* offset in the block we read */
+ int result = 0; /* either an error or # of entries returned */
int errfill;
- caddr_t buff = NULL;
+ char *buff = NULL;
struct venus_dirent *vdirent;
- struct getdents_callback *dents_callback;
- int string_offset;
- int size;
- char debug[255];
+ int string_offset = (int) (&((struct venus_dirent *)(0))->d_name);
+ int i;
ENTRY;
- /* we also need the ofset of the string in the dirent struct */
- string_offset = sizeof ( char )* 2 + sizeof(unsigned int) +
- sizeof(unsigned short);
-
- dents_callback = (struct getdents_callback *) getdent;
-
- size = count = dents_callback->count;
- CODA_ALLOC(buff, void *, size);
- if ( ! buff ) {
+ CODA_ALLOC(buff, char *, DIR_BUFSIZE);
+ if ( !buff ) {
printk("coda_venus_readdir: out of memory.\n");
return -ENOMEM;
}
/* we use this routine to read the file into our buffer */
- result = read_exec(filp->f_dentry, filp->f_pos, buff, count, 1);
- if ( result < 0) {
+ bufsize = read_exec(filp->f_dentry, filp->f_pos, buff, DIR_BUFSIZE, 1);
+ if ( bufsize < 0) {
printk("coda_venus_readdir: cannot read directory %d.\n",
- result);
- error = result;
+ bufsize);
+ result = bufsize;
goto exit;
}
- if ( result == 0) {
- error = result;
+ if ( bufsize == 0) {
+ result = 0;
goto exit;
}
-
+
/* Parse and write into user space. Filldir tells us when done! */
- offset = filp->f_pos;
- pos = 0;
- CDEBUG(D_FILE, "offset %d, count %d.\n", offset, count);
+ CDEBUG(D_FILE, "buffsize: %d offset %d, count %d.\n",
+ bufsize, offset, count);
- while ( pos + string_offset < result ) {
+ i = 0;
+ result = 0;
+ while ( pos + string_offset < bufsize && i < 1024) {
vdirent = (struct venus_dirent *) (buff + pos);
/* test if the name is fully in the buffer */
- if ( pos + string_offset + (int) vdirent->d_namlen >= result ){
+ if ( pos + string_offset + (int) vdirent->d_namlen >= bufsize ){
+ if ( result == 0 )
+ printk("CODA: Invalid directory cfino: %ld\n",
+ filp->f_dentry->d_inode->i_ino);
break;
}
-
/* now we are certain that we can read the entry from buff */
- /* for debugging, get the string out */
- memcpy(debug, vdirent->d_name, vdirent->d_namlen);
- *(debug + vdirent->d_namlen) = '\0';
-
/* if we don't have a null entry, copy it */
- if ( vdirent->d_fileno ) {
+ if ( vdirent->d_fileno && vdirent->d_reclen ) {
int namlen = vdirent->d_namlen;
off_t offs = filp->f_pos;
ino_t ino = vdirent->d_fileno;
char *name = vdirent->d_name;
- /* adjust count */
- count = dents_callback->count;
- errfill = filldir(dents_callback, name, namlen,
+ errfill = filldir(getdent, name, namlen,
offs, ino);
-CDEBUG(D_FILE, "ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %s, offset %d, count %d.\n", vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos, string_offset, debug, (u_int) offs, dents_callback->count);
-
- /* errfill means no space for filling in this round */
- if ( errfill < 0 ) break;
+CDEBUG(D_FILE, "entry %d: ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %*s, offset %d, result: %d, errfill: %d.\n", i,vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos, string_offset, vdirent->d_namlen, vdirent->d_name, (u_int) offs, result, errfill);
+ /* errfill means no space for filling in this round */
+ if ( errfill < 0 ) {
+ result = 0;
+ break;
+ }
+ /* adjust count */
+ result++;
}
/* next one */
- filp->f_pos += (unsigned int) vdirent->d_reclen;
+ filp->f_pos += vdirent->d_reclen;
+ if ( filp->f_pos > filp->f_dentry->d_inode->i_size )
+ break;
+ if ( !vdirent->d_reclen ) {
+ printk("CODA: Invalid directory, cfino: %ld\n",
+ filp->f_dentry->d_inode->i_ino);
+ break;
+ }
pos += (unsigned int) vdirent->d_reclen;
+ i++;
}
exit:
- CODA_FREE(buff, size);
- return error;
+ CODA_FREE(buff, DIR_BUFSIZE);
+ return result;
}
/* called when a cache lookup succeeds */
cii = ITOC(de->d_inode);
if (cii->c_flags & C_PURGE)
valid = 0;
+ if (cii->c_flags & C_FLUSH) {
+ coda_flag_inode_children(inode, C_FLUSH);
+ valid = 0;
+ }
}
return valid || coda_isroot(de->d_inode);
}
}
-static int coda_refresh_inode(struct dentry *dentry)
+
+/*
+ * This is called when we want to check if the inode has
+ * changed on the server. Coda makes this easy since the
+ * cache manager Venus issues a downcall to the kernel when this
+ * happens
+ */
+
+int coda_revalidate_inode(struct dentry *dentry)
{
struct coda_vattr attr;
- int error;
+ int error = 0;
int old_mode;
ino_t old_ino;
struct inode *inode = dentry->d_inode;
struct coda_inode_info *cii = ITOC(inode);
ENTRY;
-
- error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
- if ( error ) {
- make_bad_inode(inode);
- return -EIO;
- }
+ CDEBUG(D_INODE, "revalidating: %*s/%*s\n",
+ dentry->d_name.len, dentry->d_name.name,
+ dentry->d_parent->d_name.len, dentry->d_parent->d_name.name);
- /* this inode may be lost if:
- - it's type changed
- - it's ino changed
- */
- old_mode = inode->i_mode;
- old_ino = inode->i_ino;
- coda_vattr_to_iattr(inode, &attr);
+ if ( cii->c_flags == 0 )
+ return 0;
- if ((inode->i_ino != old_ino) ||
- ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT))) {
+ /* Venus closed the device .... */
+ if ( cii->c_flags & C_DYING ) {
make_bad_inode(inode);
- inode->i_mode = old_mode;
return -EIO;
}
-
- cii->c_flags &= ~C_VATTR;
- return 0;
-}
+
+ if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) {
+ error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
+ if ( error ) {
+ make_bad_inode(inode);
+ return -EIO;
+ }
-/*
- * This is called when we want to check if the inode has
- * changed on the server. Coda makes this easy since the
- * cache manager Venus issues a downcall to the kernel when this
- * happens
- */
+ /* this inode may be lost if:
+ - it's ino changed
+ - type changes must be permitted for repair and
+ missing mount points.
+ */
+ old_mode = inode->i_mode;
+ old_ino = inode->i_ino;
+ coda_vattr_to_iattr(inode, &attr);
-int coda_revalidate_inode(struct dentry *dentry)
-{
- int error = 0;
- struct coda_inode_info *cii = ITOC(dentry->d_inode);
- ENTRY;
- CDEBUG(D_INODE, "revalidating: %*s/%*s\n",
- dentry->d_name.len, dentry->d_name.name,
- dentry->d_parent->d_name.len, dentry->d_parent->d_name.name);
+ if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) {
+ printk("Coda: inode %ld, fid %s changed type!\n",
+ inode->i_ino, coda_f2s(&(cii->c_fid)));
+ }
- if (cii->c_flags & (C_VATTR | C_PURGE)) {
- error = coda_refresh_inode(dentry);
+ /* the following can happen when a local fid is replaced
+ with a global one, here we lose and declar the inode bad */
+ if (inode->i_ino != old_ino) {
+ make_bad_inode(inode);
+ inode->i_mode = old_mode;
+ return -EIO;
+ }
+
+ if ( cii->c_flags )
+ coda_flag_inode_children(inode, C_FLUSH);
+
+ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
}
- return error;
+ return 0;
}
#include <linux/coda_fs_i.h>
#include <linux/coda_cache.h>
-
/* VFS super_block ops */
static struct super_block *coda_read_super(struct super_block *, void *, int);
static void coda_read_inode(struct inode *);
static int coda_statfs(struct super_block *sb, struct statfs *buf,
int bufsiz);
-/* helper functions */
-static struct vcomm *coda_psinode2vcomm(struct inode *inode);
-static int coda_get_psdev(void *, struct inode **);
-static struct coda_sb_info *coda_psinode2sbi(struct inode *inode);
-
/* exported operations */
struct super_operations coda_super_operations =
{
NULL /* remount_fs */
};
-/*
- * globals
- */
-struct coda_sb_info coda_super_info[MAX_CODADEVS];
-
-
static struct super_block * coda_read_super(struct super_block *sb,
void *data, int silent)
{
struct inode *psdev = 0, *root = 0;
struct coda_sb_info *sbi = NULL;
- struct vcomm *vc = NULL;
+ struct venus_comm *vc = NULL;
ViceFid fid;
kdev_t dev = sb->s_dev;
int error;
ENTRY;
MOD_INC_USE_COUNT;
- if (coda_get_psdev(data, &psdev))
- goto error;
-
- vc = coda_psinode2vcomm(psdev);
- if ( !vc )
- goto error;
- vc->vc_sb = sb;
- vc->vc_inuse = 1;
-
- sbi = coda_psinode2sbi(psdev);
- if ( !sbi )
- goto error;
+
+ vc = &coda_upc_comm;
+ sbi = &coda_super_info;
+
+ if ( sbi->sbi_sb ) {
+ printk("Already mounted\n");
+ return NULL;
+ }
+
+ sbi->sbi_sb = sb;
sbi->sbi_psdev = psdev;
sbi->sbi_vcomm = vc;
INIT_LIST_HEAD(&(sbi->sbi_cchead));
EXIT;
return sb;
-error:
-EXIT;
+ error:
+ EXIT;
MOD_DEC_USE_COUNT;
if (sbi) {
sbi->sbi_vcomm = NULL;
sbi->sbi_root = NULL;
- }
- if ( vc ) {
- vc->vc_sb = NULL;
- vc->vc_inuse = 0;
+ sbi->sbi_sb = NULL;
}
if (root) {
iput(root);
coda_cache_clear_all(sb);
sb_info = coda_sbp(sb);
sb_info->sbi_vcomm->vc_inuse = 0;
- sb_info->sbi_vcomm->vc_sb = NULL;
+ coda_super_info.sbi_sb = NULL;
printk("Coda: Bye bye.\n");
memset(sb_info, 0, sizeof(* sb_info));
return register_filesystem(&coda_fs_type);
}
-/* MODULE stuff is in psdev.c */
-/* helpers */
-static struct vcomm *coda_psinode2vcomm(struct inode *inode)
-{
-
- unsigned int minor = MINOR(inode->i_rdev);
- CDEBUG(D_PSDEV,"minor %d\n", minor);
- if ( minor < MAX_CODADEVS )
- return &(psdev_vcomm[minor]);
- else
- return NULL;
-}
-
-static struct coda_sb_info *coda_psinode2sbi(struct inode *inode)
-{
- unsigned int minor = MINOR(inode->i_rdev);
- CDEBUG(D_PSDEV,"minor %d\n", minor);
- if ( (minor >= 0) && (minor < MAX_CODADEVS))
- return &(coda_super_info[minor]);
- else
- return NULL;
-}
-
-/* name lookup for psdev passed in by mount */
-static int coda_get_psdev(void *data, struct inode **res_dev)
-{
- char **psdev_path;
- struct inode *psdev = 0;
- struct dentry *ent=NULL;
-
-
- if ( ! data ) {
- printk("coda_get_psdev: no data!\n");
- return 1;
- }
-
- psdev_path = data;
- ent = namei((char *) *psdev_path);
- if (IS_ERR(ent)) {
- printk("namei error %ld for %d\n", PTR_ERR(ent),
- (int) psdev_path);
- return 1;
- }
- psdev = ent->d_inode;
-
- if (!S_ISCHR(psdev->i_mode)) {
- printk("not a character device\n");
- return 1;
- }
- CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n",
- MAJOR(psdev->i_rdev),
- MINOR(psdev->i_rdev), psdev->i_count);
-
- if (MAJOR(psdev->i_rdev) != CODA_PSDEV_MAJOR) {
- printk("device %d not a Coda PSDEV device\n",
- MAJOR(psdev->i_rdev));
- return 1;
- }
-
- if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) {
- printk("minor %d not an allocated Coda PSDEV\n",
- psdev->i_rdev);
- return 1;
- }
-
- if (psdev->i_count < 1) {
- printk("coda device minor %d not open (i_count = %d)\n",
- MINOR(psdev->i_rdev), psdev->i_count);
- return 1;
- }
-
- *res_dev = psdev;
- EXIT;
- return 0;
-}
* Copyright (c) 1997 Carnegie-Mellon University
*/
-#include <linux/config.h> /* for CONFIG_PROC_FS */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/init.h>
+#include <linux/list.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/coda_cache.h>
#include <linux/coda_proc.h>
-extern struct proc_dir_entry proc_sys_root;
-
/*
* Coda stuff
*/
extern struct file_system_type coda_fs_type;
extern int init_coda_fs(void);
-extern int cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
-extern int cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy);
/* statistics */
-struct coda_upcallstats coda_callstats;
-int coda_hard = 0; /* allows signals during upcalls */
+int coda_hard = 0; /* allows signals during upcalls */
unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
-extern struct coda_sb_info coda_super_info[MAX_CODADEVS];
-struct vcomm psdev_vcomm[MAX_CODADEVS];
-
-/* queue stuff for the messages */
-static inline void init_queue(struct queue *head)
-{
- head->forw = head;
- head->back = head;
-}
-
-static inline struct vmsg *q_getnext(struct queue *elt)
-{
- return (struct vmsg *)(elt->forw);
-}
-
-static inline int q_end(struct vmsg *msg, struct queue *queue)
-{
- return (struct queue *)msg == queue;
-}
-static inline int q_empty(struct queue *queue)
-{
- return queue->forw == queue;
-}
-
-/* insert before head, ie. at the tail */
-void coda_q_insert(struct queue *el, struct queue *head)
-{
- el->forw = head->back->forw;
- el->back = head->back;
- head->back->forw = el;
- head->back = el;
-}
-
-void coda_q_remove(struct queue *el)
-{
- el->forw->back = el->back;
- el->back->forw = el->forw;
-}
-
-static struct vcomm *coda_psdev2vcomm(struct file *file)
-{
- unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
- struct vcomm *vcp = NULL;
-
- if ( (minor >= 0) && (minor < MAX_CODADEVS) )
- vcp = &psdev_vcomm[minor];
- return vcp;
-}
+struct coda_sb_info coda_super_info;
+struct venus_comm coda_upc_comm;
/*
* Device operations
*/
-
static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
{
- struct vcomm *vcp = coda_psdev2vcomm(file);
+ struct venus_comm *vcp = &coda_upc_comm;
unsigned int mask = POLLOUT | POLLWRNORM;
- if ( !vcp )
- return -ENXIO;
-
poll_wait(file, &(vcp->vc_waitq), wait);
- if (!q_empty(&(vcp->vc_pending)))
+ if (!list_empty(&vcp->vc_pending))
mask |= POLLIN | POLLRDNORM;
return mask;
static ssize_t coda_psdev_write(struct file *file, const char *buf,
size_t count, loff_t *off)
{
- struct vcomm *vcp = coda_psdev2vcomm(file);
- struct vmsg *vmp;
- int error = 0;
- int size;
- u_long uniq;
- u_long opcode;
- u_long opcodebuf[2];
-
- if (!vcp)
- return -ENXIO;
+ struct venus_comm *vcp = &coda_upc_comm;
+ struct upc_req *req = NULL;
+ struct upc_req *tmp;
+ struct list_head *lh;
+ struct coda_in_hdr hdr;
+ int error;
+
+ if ( !coda_upc_comm.vc_pid )
+ return -EIO;
/* Peek at the opcode, uniquefier */
- if (copy_from_user(opcodebuf, buf, 2 * sizeof(u_long)))
+ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
return -EFAULT;
- opcode = opcodebuf[0];
- uniq = opcodebuf[1];
- CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld)\n",
- current->pid, opcode, uniq);
+ CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), count %d\n",
+ current->pid, hdr.opcode, hdr.unique, count);
- if (DOWNCALL(opcode)) {
+ if (DOWNCALL(hdr.opcode)) {
struct super_block *sb = NULL;
union outputArgs *dcbuf;
- size = sizeof(*dcbuf);
+ int size = sizeof(*dcbuf);
- sb = vcp->vc_sb;
+ sb = coda_super_info.sbi_sb;
if ( !sb ) {
printk("coda_psdev_write: downcall, no SB!\n");
return count;
}
CDEBUG(D_PSDEV, "handling downcall\n");
- if ( count < sizeof(struct cfs_out_hdr) ) {
+ if ( count < sizeof(struct coda_out_hdr) ) {
printk("coda_downcall opc %ld uniq %ld, not enough!\n",
- opcode, uniq);
+ hdr.opcode, hdr.unique);
return count;
}
CODA_ALLOC(dcbuf, union outputArgs *, size);
if ( count > size ) {
printk("Coda: downcall opc %ld, uniq %ld, too much!",
- opcode, uniq);
+ hdr.opcode, hdr.unique);
count = size;
}
if (copy_from_user(dcbuf, buf, count))
return -EFAULT;
/* what downcall errors does Venus handle ? */
- error = coda_downcall(opcode, dcbuf, sb);
+ error = coda_downcall(hdr.opcode, dcbuf, sb);
if ( error) {
printk("psdev_write: coda_downcall error: %d\n",
/* Look for the message on the processing queue. */
- for (vmp = q_getnext(&(vcp->vc_processing));
- !q_end(vmp, &(vcp->vc_processing));
- vmp = q_getnext(&(vmp->vm_chain))) {
- if (vmp->vm_unique == uniq) {
+ lh = &vcp->vc_processing;
+ while ( (lh = lh->next) != &vcp->vc_processing ) {
+ tmp = list_entry(lh, struct upc_req , uc_chain);
+ if (tmp->uc_unique == hdr.unique) {
+ req = tmp;
+ list_del(&req->uc_chain);
+ CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n",
+ hdr.unique);
break;
- CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", uniq);
}
}
- if (q_end(vmp, &(vcp->vc_processing))) {
+ if (!req) {
printk("psdev_write: msg (%ld, %ld) not found\n",
- opcode, uniq);
+ hdr.opcode, hdr.unique);
return(-ESRCH);
}
- /* Remove the message from the processing queue */
- coda_q_remove(&(vmp->vm_chain));
-
/* move data into response buffer. */
- if (vmp->vm_outSize < count) {
+ if (req->uc_outSize < count) {
printk("psdev_write: too much cnt: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
- vmp->vm_outSize, count, opcode, uniq);
- count = vmp->vm_outSize; /* don't have more space! */
+ req->uc_outSize, count, hdr.opcode, hdr.unique);
+ count = req->uc_outSize; /* don't have more space! */
}
- if (copy_from_user(vmp->vm_data, buf, count))
+ if (copy_from_user(req->uc_data, buf, count))
return -EFAULT;
/* adjust outsize. is this usefull ?? */
- vmp->vm_outSize = count;
- vmp->vm_flags |= VM_WRITE;
+ req->uc_outSize = count;
+ req->uc_flags |= REQ_WRITE;
CDEBUG(D_PSDEV,
- "Found! Count %d for (opc,uniq)=(%ld,%ld), vmsg at %x\n",
- count, opcode, uniq, (int)&vmp);
+ "Found! Count %d for (opc,uniq)=(%ld,%ld), upc_req at %x\n",
+ count, hdr.opcode, hdr.unique, (int)&req);
- wake_up(&vmp->vm_sleep);
+ wake_up(&req->uc_sleep);
return(count);
}
static ssize_t coda_psdev_read(struct file * file, char * buf,
size_t count, loff_t *off)
{
- struct vcomm *vcp = coda_psdev2vcomm(file);
- struct vmsg *vmp;
+ struct venus_comm *vcp = &coda_upc_comm;
+ struct upc_req *req;
int result = count ;
- if (!vcp)
- return -ENXIO;
-
- /* Get message at head of request queue. */
- if (q_empty(&(vcp->vc_pending))) {
- return 0;
+ CDEBUG(D_PSDEV, "count %d\n", count);
+ if (list_empty(&(vcp->vc_pending))) {
+ return -1;
}
- vmp = q_getnext(&(vcp->vc_pending));
- coda_q_remove(&(vmp->vm_chain));
+ req = list_entry((vcp->vc_pending.next), struct upc_req, uc_chain);
+ list_del(&(req->uc_chain));
/* Move the input args into userspace */
- if (vmp->vm_inSize <= count)
- result = vmp->vm_inSize;
+ if (req->uc_inSize <= count)
+ result = req->uc_inSize;
- if (count < vmp->vm_inSize) {
+ if (count < req->uc_inSize) {
printk ("psdev_read: Venus read %d bytes of %d in message\n",
- count, vmp->vm_inSize);
+ count, req->uc_inSize);
}
- if ( copy_to_user(buf, vmp->vm_data, result))
+ if ( copy_to_user(buf, req->uc_data, result))
return -EFAULT;
- if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
- printk("coda_psdev_read: bad chain");
-
/* If request was a signal, don't enqueue */
- if (vmp->vm_opcode == CFS_SIGNAL) {
+ if (req->uc_opcode == CODA_SIGNAL) {
CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n",
- vmp->vm_opcode, vmp->vm_unique);
- CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr));
- CODA_FREE(vmp, sizeof(struct vmsg));
+ req->uc_opcode, req->uc_unique);
+ CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+ CODA_FREE(req, sizeof(struct upc_req));
return count;
}
- vmp->vm_flags |= VM_READ;
- coda_q_insert(&(vmp->vm_chain), &(vcp->vc_processing));
+ req->uc_flags |= REQ_READ;
+ list_add(&(req->uc_chain), vcp->vc_processing.prev);
return result;
}
static int coda_psdev_open(struct inode * inode, struct file * file)
{
- register struct vcomm *vcp = NULL;
+ struct venus_comm *vcp = &coda_upc_comm;
ENTRY;
-
- vcp =coda_psdev2vcomm(file);
-
- if (!vcp)
- return -ENODEV;
+
+ /* first opener: must be lento. Initialize & take its pid */
+ if ( file->f_flags == O_RDWR ) {
+ if ( vcp->vc_pid ) {
+ printk("Venus pid already set to %d!!\n", vcp->vc_pid);
+ return -1;
+ }
+ if ( vcp->vc_inuse ) {
+ printk("psdev_open: Cannot O_RDWR while open.\n");
+ return -1;
+ }
+ }
+
+ vcp->vc_inuse++;
+ MOD_INC_USE_COUNT;
- if (vcp->vc_inuse)
- return -EBUSY;
- memset(vcp, 0, sizeof(struct vcomm));
- vcp->vc_inuse = 1;
- MOD_INC_USE_COUNT;
+ if ( file->f_flags == O_RDWR ) {
+ vcp->vc_pid = current->pid;
+ vcp->vc_seq = 0;
+ INIT_LIST_HEAD(&vcp->vc_pending);
+ INIT_LIST_HEAD(&vcp->vc_processing);
+ }
- init_queue(&(vcp->vc_pending));
- init_queue(&(vcp->vc_processing));
+ CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n",
+ vcp->vc_inuse, vcp->vc_pid, current->pid);
- memset(&coda_callstats, 0, sizeof(struct coda_upcallstats));
EXIT;
return 0;
}
-static int
-coda_psdev_release(struct inode * inode, struct file * file)
+
+static int coda_psdev_release(struct inode * inode, struct file * file)
{
- struct vcomm *vcp;
- struct vmsg *vmp;
- unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct venus_comm *vcp = &coda_upc_comm;
+ struct upc_req *req;
+ struct list_head *lh, *next;
ENTRY;
- vcp = coda_psdev2vcomm(file);
+ if ( !vcp->vc_inuse ) {
+ printk("psdev_release: Not open.\n");
+ return -1;
+ }
+
+ vcp->vc_inuse--;
+ MOD_DEC_USE_COUNT;
+ CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n",
+ vcp->vc_inuse, vcp->vc_pid, current->pid);
+
+ if ( vcp->vc_pid != current->pid ) {
+ return 0;
+ }
- if ( !vcp || !vcomm_open(vcp) ) {
- printk("psdev_release: not open");
- return 0;
- }
-
-
- /* flush the name cache so that we can unmount */
- CDEBUG(D_PSDEV, "Flushing the cache.\n");
- /* cfsnc_flush(); */
- /* cfsnc_use = 0; */
- CDEBUG(D_PSDEV, "Done.\n");
-
- /* if operations are in progress perhaps the kernel
- can profit from setting the C_DYING flag on the root
- cnode of Coda filesystems */
- if (coda_super_info[minor].sbi_root) {
- struct coda_inode_info *cnp =
- ITOC(coda_super_info[minor].sbi_root);
- cnp->c_flags |= C_DYING;
- } else
- vcp->vc_inuse = 0;
-
-
+ vcp->vc_pid = 0;
/* Wakeup clients so they can return. */
- for (vmp = q_getnext(&(vcp->vc_pending));
- !q_end(vmp, &(vcp->vc_pending));
- vmp = q_getnext(&(vmp->vm_chain))) {
- /* Free signal request messages and don't wakeup cause
- no one is waiting. */
- if (vmp->vm_opcode == CFS_SIGNAL) {
- CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr));
- CODA_FREE(vmp, (u_int)sizeof(struct vmsg));
- continue;
- }
- wake_up(&vmp->vm_sleep);
+ CDEBUG(D_PSDEV, "wake up pending clients\n");
+ lh = vcp->vc_pending.next;
+ next = lh;
+ while ( (lh = next) != &vcp->vc_pending) {
+ next = lh->next;
+ req = list_entry(lh, struct upc_req, uc_chain);
+ /* Async requests need to be freed here */
+ if (req->uc_flags & REQ_ASYNC) {
+ CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+ CODA_FREE(req, (u_int)sizeof(struct upc_req));
+ continue;
+ }
+ wake_up(&req->uc_sleep);
}
- for (vmp = q_getnext(&(vcp->vc_processing));
- !q_end(vmp, &(vcp->vc_processing));
- vmp = q_getnext(&(vmp->vm_chain))) {
- wake_up(&vmp->vm_sleep);
+ lh = &vcp->vc_processing;
+ CDEBUG(D_PSDEV, "wake up processing clients\n");
+ while ( (lh = lh->next) != &vcp->vc_processing) {
+ req = list_entry(lh, struct upc_req, uc_chain);
+ wake_up(&req->uc_sleep);
}
-
- mark_vcomm_closed(vcp);
- MOD_DEC_USE_COUNT;
+ CDEBUG(D_PSDEV, "Done.\n");
+
EXIT;
return 0;
}
};
-#ifdef CONFIG_PROC_FS
-
-
-struct proc_dir_entry proc_sys_coda = {
- 0, 4, "coda",
- S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
- 0, &proc_dir_inode_operations,
- NULL, NULL,
- NULL,
- NULL, NULL
-};
-
-/*
- target directory structure:
- /proc/fs (see linux/fs/proc/root.c)
- /proc/fs/coda
- /proc/fs/coda/{vfs_stats,
-
-*/
-
-
-struct proc_dir_entry proc_fs_coda = {
- PROC_FS_CODA, 4, "coda",
- S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
- 0, &proc_dir_inode_operations,
- NULL, NULL,
- NULL,
- NULL, NULL
-};
-
-struct proc_dir_entry proc_coda_vfs = {
- PROC_VFS_STATS , 9, "vfs_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_vfs_stats_get_info
- };
-
-struct proc_dir_entry proc_coda_vfs_control = {
- 0 , 9, "vfs_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_vfs_stats_get_info
- };
-
-struct proc_dir_entry proc_coda_upcall = {
- PROC_UPCALL_STATS , 12, "upcall_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_upcall_stats_get_info
- };
-
-struct proc_dir_entry proc_coda_upcall_control = {
- 0 , 12, "upcall_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_upcall_stats_get_info
- };
-
-struct proc_dir_entry proc_coda_permission = {
- PROC_PERMISSION_STATS , 16, "permission_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_permission_stats_get_info
- };
-
-struct proc_dir_entry proc_coda_permission_control = {
- 0 , 16, "permission_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_permission_stats_get_info
- };
-
-struct proc_dir_entry proc_coda_cache_inv = {
- PROC_CACHE_INV_STATS , 15, "cache_inv_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_cache_inv_stats_get_info
- };
-
-struct proc_dir_entry proc_coda_cache_inv_control = {
- 0 , 15, "cache_inv_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- coda_cache_inv_stats_get_info
- };
-
-#endif
-
__initfunc(int init_coda(void))
{
CODA_PSDEV_MAJOR);
return -EIO;
}
- memset(psdev_vcomm, 0, sizeof(psdev_vcomm));
- memset(coda_super_info, 0, sizeof(coda_super_info));
- memset(&coda_callstats, 0, sizeof(coda_callstats));
-
- reset_coda_vfs_stats();
- reset_coda_upcall_stats();
- reset_coda_permission_stats();
- reset_coda_cache_inv_stats();
-
-#ifdef CONFIG_PROC_FS
- proc_register(&proc_root_fs,&proc_fs_coda);
- proc_register(&proc_fs_coda,&proc_coda_vfs);
- proc_register(&proc_fs_coda,&proc_coda_upcall);
- proc_register(&proc_fs_coda,&proc_coda_permission);
- proc_register(&proc_fs_coda,&proc_coda_cache_inv);
-#endif
-
-#ifdef CONFIG_SYSCTL
- proc_register(&proc_sys_root,&proc_sys_coda);
- proc_register(&proc_sys_coda,&proc_coda_vfs_control);
- proc_register(&proc_sys_coda,&proc_coda_upcall_control);
- proc_register(&proc_sys_coda,&proc_coda_permission_control);
- proc_register(&proc_sys_coda,&proc_coda_cache_inv_control);
+ memset(&coda_upc_comm, 0, sizeof(coda_upc_comm));
+ memset(&coda_super_info, 0, sizeof(coda_super_info));
coda_sysctl_init();
-#endif
+
return 0;
}
int init_module(void)
{
int status;
- printk(KERN_INFO "Coda Kernel/Venus communications (module), v4.7.1, braam@cs.cmu.edu.\n");
+ printk(KERN_INFO "Coda Kernel/Venus communications (module), v4.7.5, braam@cs.cmu.edu.\n");
status = init_coda_psdev();
if ( status ) {
printk("coda: failed to unregister filesystem\n");
}
unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
-
-#if CONFIG_PROC_FS
- coda_sysctl_clean();
-
- proc_unregister(&proc_sys_coda, proc_coda_cache_inv_control.low_ino);
- proc_unregister(&proc_sys_coda, proc_coda_permission_control.low_ino);
- proc_unregister(&proc_sys_coda, proc_coda_upcall_control.low_ino);
- proc_unregister(&proc_sys_coda, proc_coda_vfs_control.low_ino);
- proc_unregister(&proc_sys_root, proc_sys_coda.low_ino);
-#endif
-
-#ifdef CONFIG_SYSCTL
- proc_unregister(&proc_fs_coda, proc_coda_cache_inv.low_ino);
- proc_unregister(&proc_fs_coda, proc_coda_permission.low_ino);
- proc_unregister(&proc_fs_coda, proc_coda_upcall.low_ino);
- proc_unregister(&proc_fs_coda, proc_coda_vfs.low_ino);
- proc_unregister(&proc_root_fs, proc_fs_coda.low_ino);
-#endif
+ coda_sysctl_clean();
}
#endif
struct coda_vfs_stats coda_vfs_stat;
struct coda_permission_stats coda_permission_stat;
struct coda_cache_inv_stats coda_cache_inv_stat;
-struct coda_upcall_stats_entry coda_upcall_stat[CFS_NCALLS];
+struct coda_upcall_stats_entry coda_upcall_stat[CODA_NCALLS];
/* keep this in sync with coda.h! */
char *coda_upcall_names[] = {
{
struct coda_upcall_stats_entry * pentry;
- if ( opcode < 0 || opcode > CFS_NCALLS - 1) {
+ if ( opcode < 0 || opcode > CODA_NCALLS - 1) {
printk("Nasty opcode %d passed to coda_upcall_stats\n",
opcode);
return;
if ( offset < 320)
len += sprintf( buffer + len,"%-79s\n", "------\t\t -----\t------------\t-----------------");
pos = 320;
- for ( i = 0 ; i < CFS_NCALLS ; i++ ) {
+ for ( i = 0 ; i < CODA_NCALLS ; i++ ) {
tmplen += sprintf(tmpbuf,"%s\t%9d\t%10ld\t%10ld",
coda_upcall_names[i],
coda_upcall_stat[i].count,
#include <linux/coda_proc.h>
static int coda_readlink(struct dentry *de, char *buffer, int length);
-static struct dentry *coda_follow_link(struct dentry *, struct dentry *, unsigned int);
+static struct dentry *coda_follow_link(struct dentry *, struct dentry *,
+ unsigned int);
struct inode_operations coda_symlink_inode_operations = {
NULL, /* no file-operations */
coda_vfs_stat.readlink++;
/* the maximum length we receive is len */
- if ( length > CFS_MAXPATHLEN )
- len = CFS_MAXPATHLEN;
+ if ( length > CODA_MAXPATHLEN )
+ len = CODA_MAXPATHLEN;
else
len = length;
CODA_ALLOC(buf, char *, len);
return error;
}
-static struct dentry *coda_follow_link(struct dentry *de,
- struct dentry *base,
+static struct dentry *coda_follow_link(struct dentry *de, struct dentry *base,
unsigned int follow)
{
struct inode *inode = de->d_inode;
int error;
struct coda_inode_info *cnp;
unsigned int len;
- char mem[CFS_MAXPATHLEN];
+ char mem[CODA_MAXPATHLEN];
char *path;
ENTRY;
CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino);
cnp = ITOC(inode);
coda_vfs_stat.follow_link++;
- len = CFS_MAXPATHLEN;
+ len = CODA_MAXPATHLEN;
error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len);
if (error) {
*
* Carnegie Mellon encourages users to contribute improvements to
* the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ *
+ * CODA operation statistics
+ * (c) March, 1998 Zhanyong Wan <zhanyong.wan@yale.edu>
+ *
*/
-/* sysctl entries for Coda! */
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <linux/coda_psdev.h>
#include <linux/coda_cache.h>
#include <linux/coda_proc.h>
-extern int coda_debug;
-/* extern int cfsnc_use; */
-extern int coda_print_entry;
-/* extern int cfsnc_flushme; */
-extern int cfsnc_procsize;
-/* extern void cfsnc_flush(void); */
-void coda_sysctl_init(void);
-void coda_sysctl_clean(void);
-
-int coda_dointvec(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp);
-
-struct ctl_table_header *fs_table_header, *coda_table_header;
+
+static struct ctl_table_header *fs_table_header;
+
#define FS_CODA 1 /* Coda file system */
#define CODA_DEBUG 1 /* control debugging */
#define CODA_PERMISSION 8 /* permission statistics */
#define CODA_CACHE_INV 9 /* cache invalidation statistics */
-
-
static ctl_table coda_table[] = {
- {CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &coda_dointvec},
- {CODA_ENTRY, "printentry", &coda_print_entry, sizeof(int), 0644, NULL, &coda_dointvec},
- {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &coda_dointvec},
- {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &coda_dointvec},
- {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_ENTRY, "printentry", &coda_print_entry, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &proc_dointvec},
{CODA_VFS, "vfs_stats", NULL, 0, 0644, NULL, &do_reset_coda_vfs_stats},
{CODA_UPCALL, "upcall_stats", NULL, 0, 0644, NULL, &do_reset_coda_upcall_stats},
{CODA_PERMISSION, "permission_stats", NULL, 0, 0644, NULL, &do_reset_coda_permission_stats},
{ 0 }
};
-
static ctl_table fs_table[] = {
{FS_CODA, "coda", NULL, 0, 0555, coda_table},
{0}
};
+struct coda_vfs_stats coda_vfs_stat;
+struct coda_permission_stats coda_permission_stat;
+struct coda_cache_inv_stats coda_cache_inv_stat;
+struct coda_upcall_stats_entry coda_upcall_stat[CODA_NCALLS];
+struct coda_upcallstats coda_callstats;
+
+/* keep this in sync with coda.h! */
+char *coda_upcall_names[] = {
+ "totals ", /* 0 */
+ "noop ", /* 1 */
+ "root ", /* 2 */
+ "sync ", /* 3 */
+ "open ", /* 4 */
+ "close ", /* 5 */
+ "ioctl ", /* 6 */
+ "getattr ", /* 7 */
+ "setattr ", /* 8 */
+ "access ", /* 9 */
+ "lookup ", /* 10 */
+ "create ", /* 11 */
+ "remove ", /* 12 */
+ "link ", /* 13 */
+ "rename ", /* 14 */
+ "mkdir ", /* 15 */
+ "rmdir ", /* 16 */
+ "readdir ", /* 17 */
+ "symlink ", /* 18 */
+ "readlink ", /* 19 */
+ "fsync ", /* 20 */
+ "inactive ", /* 21 */
+ "vget ", /* 22 */
+ "signal ", /* 23 */
+ "replace ", /* 24 */
+ "flush ", /* 25 */
+ "purgeuser ", /* 26 */
+ "zapfile ", /* 27 */
+ "zapdir ", /* 28 */
+ "zapvnode ", /* 28 */
+ "purgefid ", /* 30 */
+ "open_by_path" /* 31 */
+};
+
+
+void reset_coda_vfs_stats( void )
+{
+ memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) );
+}
+
+void reset_coda_upcall_stats( void )
+{
+ memset( &coda_upcall_stat, 0, sizeof( coda_upcall_stat ) );
+}
+
+void reset_coda_permission_stats( void )
+{
+ memset( &coda_permission_stat, 0, sizeof( coda_permission_stat ) );
+}
+
+void reset_coda_cache_inv_stats( void )
+{
+ memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) );
+}
+
+
+void do_time_stats( struct coda_upcall_stats_entry * pentry,
+ unsigned long runtime )
+{
+
+ unsigned long time = runtime * 1000 /HZ; /* time in ms */
+ CDEBUG(D_SPECIAL, "time: %ld\n", time);
+
+ if ( pentry->count == 0 ) {
+ pentry->time_sum = pentry->time_squared_sum = 0;
+ }
+
+ pentry->count++;
+ pentry->time_sum += time;
+ pentry->time_squared_sum += time*time;
+}
+
+
+
+void coda_upcall_stats(int opcode, long unsigned runtime)
+{
+ struct coda_upcall_stats_entry * pentry;
+
+ if ( opcode < 0 || opcode > CODA_NCALLS - 1) {
+ printk("Nasty opcode %d passed to coda_upcall_stats\n",
+ opcode);
+ return;
+ }
+
+ pentry = &coda_upcall_stat[opcode];
+ do_time_stats(pentry, runtime);
+
+ /* fill in the totals */
+ pentry = &coda_upcall_stat[0];
+ do_time_stats(pentry, runtime);
+
+}
+
+unsigned long get_time_average( const struct coda_upcall_stats_entry * pentry )
+{
+ return ( pentry->count == 0 ) ? 0 : pentry->time_sum / pentry->count;
+}
+
+static inline unsigned long absolute( unsigned long x )
+{
+ return x >= 0 ? x : -x;
+}
+
+static unsigned long sqr_root( unsigned long x )
+{
+ unsigned long y = x, r;
+ int n_bit = 0;
+
+ if ( x == 0 )
+ return 0;
+ if ( x < 0)
+ x = -x;
+
+ while ( y ) {
+ y >>= 1;
+ n_bit++;
+ }
+
+ r = 1 << (n_bit/2);
+
+ while ( 1 ) {
+ r = (r + x/r)/2;
+ if ( r*r <= x && x < (r+1)*(r+1) )
+ break;
+ }
+
+ return r;
+}
+
+unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pentry )
+{
+ unsigned long time_avg;
+
+ if ( pentry->count <= 1 )
+ return 0;
+
+ time_avg = get_time_average( pentry );
+ return
+ sqr_root( (pentry->time_squared_sum / pentry->count) -
+ time_avg * time_avg );
+}
+
+int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp,
+ void * buffer, size_t * lenp )
+{
+ if ( write ) {
+ reset_coda_vfs_stats();
+ }
+
+ *lenp = 0;
+ return 0;
+}
+
+int do_reset_coda_upcall_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp )
+{
+ if ( write ) {
+ reset_coda_upcall_stats();
+ }
+
+ *lenp = 0;
+ return 0;
+}
+
+int do_reset_coda_permission_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp )
+{
+ if ( write ) {
+ reset_coda_permission_stats();
+ }
+
+ *lenp = 0;
+ return 0;
+}
+
+int do_reset_coda_cache_inv_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp )
+{
+ if ( write ) {
+ reset_coda_cache_inv_stats();
+ }
+
+ *lenp = 0;
+ return 0;
+}
+
+int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy )
+{
+ int len=0;
+ off_t begin;
+ struct coda_vfs_stats * ps = & coda_vfs_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda VFS statistics\n"
+ "===================\n\n"
+ "File Operations:\n"
+ "\tfile_read\t%9d\n"
+ "\tfile_write\t%9d\n"
+ "\tfile_mmap\t%9d\n"
+ "\topen\t\t%9d\n"
+ "\trelase\t\t%9d\n"
+ "\tfsync\t\t%9d\n\n"
+ "Dir Operations:\n"
+ "\treaddir\t\t%9d\n\n"
+ "Inode Operations\n"
+ "\tcreate\t\t%9d\n"
+ "\tlookup\t\t%9d\n"
+ "\tlink\t\t%9d\n"
+ "\tunlink\t\t%9d\n"
+ "\tsymlink\t\t%9d\n"
+ "\tmkdir\t\t%9d\n"
+ "\trmdir\t\t%9d\n"
+ "\trename\t\t%9d\n"
+ "\tpermission\t%9d\n"
+ "\treadpage\t%9d\n",
+
+ /* file operations */
+ ps->file_read,
+ ps->file_write,
+ ps->file_mmap,
+ ps->open,
+ ps->release,
+ ps->fsync,
+
+ /* dir operations */
+ ps->readdir,
+
+ /* inode operations */
+ ps->create,
+ ps->lookup,
+ ps->link,
+ ps->unlink,
+ ps->symlink,
+ ps->mkdir,
+ ps->rmdir,
+ ps->rename,
+ ps->permission,
+ ps->readpage );
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy )
+{
+ int len=0;
+ int i;
+ off_t begin;
+ off_t pos = 0;
+ char tmpbuf[80];
+ int tmplen = 0;
+
+ ENTRY;
+ /* this works as long as we are below 1024 characters! */
+ if ( offset < 80 )
+ len += sprintf( buffer,"%-79s\n", "Coda upcall statistics");
+ if ( offset < 160)
+ len += sprintf( buffer + len,"%-79s\n", "======================");
+ if ( offset < 240)
+ len += sprintf( buffer + len,"%-79s\n", "upcall\t\t count\tavg time(ms)\tstd deviation(ms)");
+ if ( offset < 320)
+ len += sprintf( buffer + len,"%-79s\n", "------\t\t -----\t------------\t-----------------");
+ pos = 320;
+ for ( i = 0 ; i < CODA_NCALLS ; i++ ) {
+ tmplen += sprintf(tmpbuf,"%s\t%9d\t%10ld\t%10ld",
+ coda_upcall_names[i],
+ coda_upcall_stat[i].count,
+ get_time_average(&coda_upcall_stat[i]),
+ coda_upcall_stat[i].time_squared_sum);
+ pos += 80;
+ if ( pos < offset )
+ continue;
+ len += sprintf(buffer + len, "%-79s\n", tmpbuf);
+ if ( len >= length )
+ break;
+ }
+
+ begin = len- (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+ EXIT;
+ return len;
+}
+
+int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy )
+{
+ int len=0;
+ off_t begin;
+ struct coda_permission_stats * ps = & coda_permission_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda permission statistics\n"
+ "==========================\n\n"
+ "count\t\t%9d\n"
+ "hit count\t%9d\n",
+
+ ps->count,
+ ps->hit_count );
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy )
+{
+ int len=0;
+ off_t begin;
+ struct coda_cache_inv_stats * ps = & coda_cache_inv_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda cache invalidation statistics\n"
+ "==================================\n\n"
+ "flush\t\t%9d\n"
+ "purge user\t%9d\n"
+ "zap_dir\t\t%9d\n"
+ "zap_file\t%9d\n"
+ "zap_vnode\t%9d\n"
+ "purge_fid\t%9d\n"
+ "replace\t\t%9d\n",
+ ps->flush,
+ ps->purge_user,
+ ps->zap_dir,
+ ps->zap_file,
+ ps->zap_vnode,
+ ps->purge_fid,
+ ps->replace );
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ target directory structure:
+ /proc/fs (see linux/fs/proc/root.c)
+ /proc/fs/coda
+ /proc/fs/coda/{vfs_stats,
+
+*/
+
+struct proc_dir_entry proc_fs_coda = {
+ PROC_FS_CODA, 4, "coda",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &proc_dir_inode_operations,
+ NULL, NULL,
+ NULL,
+ NULL, NULL
+};
+
+struct proc_dir_entry proc_coda_vfs = {
+ PROC_VFS_STATS , 9, "vfs_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ coda_vfs_stats_get_info
+ };
+
+struct proc_dir_entry proc_coda_upcall = {
+ PROC_UPCALL_STATS , 12, "upcall_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ coda_upcall_stats_get_info
+ };
+
+struct proc_dir_entry proc_coda_permission = {
+ PROC_PERMISSION_STATS , 16, "permission_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ coda_permission_stats_get_info
+ };
+
+
+struct proc_dir_entry proc_coda_cache_inv = {
+ PROC_CACHE_INV_STATS , 15, "cache_inv_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ coda_cache_inv_stats_get_info
+ };
+
+#endif
void coda_sysctl_init()
{
- fs_table_header = register_sysctl_table(fs_table, 0);
-/* coda_table_header = register_sysctl_table(coda_table, 0);*/
-}
-
-void coda_sysctl_clean() {
- /*unregister_sysctl_table(coda_table_header);*/
- unregister_sysctl_table(fs_table_header);
-}
-
-int coda_dointvec(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
-{
- int *i, vleft, first=1, len, left, neg, val;
- #define TMPBUFLEN 20
- char buf[TMPBUFLEN], *p;
-
- if (!table->data || !table->maxlen || !*lenp ||
- (filp->f_pos && !write)) {
- *lenp = 0;
- return 0;
- }
-
- i = (int *) table->data;
- vleft = table->maxlen / sizeof(int);
- left = *lenp;
-
- for (; left && vleft--; i++, first=0) {
- if (write) {
- while (left) {
- char c;
- if(get_user(c,(char *) buffer))
- return -EFAULT;
- if (!isspace(c))
- break;
- left--;
- ((char *) buffer)++;
- }
- if (!left)
- break;
- neg = 0;
- len = left;
- if (len > TMPBUFLEN-1)
- len = TMPBUFLEN-1;
- if (copy_from_user(buf, buffer, len))
- return -EFAULT;
- buf[len] = 0;
- p = buf;
- if (*p == '-' && left > 1) {
- neg = 1;
- left--, p++;
- }
- if (*p < '0' || *p > '9')
- break;
- val = simple_strtoul(p, &p, 0);
- len = p-buf;
- if ((len < left) && *p && !isspace(*p))
- break;
- if (neg)
- val = -val;
- buffer += len;
- left -= len;
- *i = val;
- } else {
- p = buf;
- if (!first)
- *p++ = '\t';
- sprintf(p, "%d", *i);
- len = strlen(buf);
- if (len > left)
- len = left;
- if (copy_to_user(buffer, buf, len))
- return -EFAULT;
- left -= len;
- buffer += len;
- }
- }
-
- if (!write && !first && left) {
- if(put_user('\n', (char *) buffer))
- return -EFAULT;
- left--, buffer++;
- }
- if (write) {
- p = (char *) buffer;
- while (left) {
- char c;
- if(get_user(c, p++))
- return -EFAULT;
- if (!isspace(c))
- break;
- left--;
- }
- }
- if (write && first)
- return -EINVAL;
- *lenp -= left;
- filp->f_pos += *lenp;
- return 0;
+ memset(&coda_callstats, 0, sizeof(coda_callstats));
+ reset_coda_vfs_stats();
+ reset_coda_upcall_stats();
+ reset_coda_permission_stats();
+ reset_coda_cache_inv_stats();
+
+#ifdef CONFIG_PROC_FS
+ proc_register(&proc_root_fs,&proc_fs_coda);
+ proc_register(&proc_fs_coda,&proc_coda_vfs);
+ proc_register(&proc_fs_coda,&proc_coda_upcall);
+ proc_register(&proc_fs_coda,&proc_coda_permission);
+ proc_register(&proc_fs_coda,&proc_coda_cache_inv);
+#endif
+
+#ifdef CONFIG_SYSCTL
+ if ( !fs_table_header )
+ fs_table_header = register_sysctl_table(fs_table, 0);
+#endif
}
+void coda_sysctl_clean()
+{
+
+#ifdef CONFIG_SYSCTL
+ if ( fs_table_header ) {
+ unregister_sysctl_table(fs_table_header);
+ fs_table_header = 0;
+ }
+#endif
+#if CONFIG_PROC_FS
+ proc_unregister(&proc_fs_coda, proc_coda_cache_inv.low_ino);
+ proc_unregister(&proc_fs_coda, proc_coda_permission.low_ino);
+ proc_unregister(&proc_fs_coda, proc_coda_upcall.low_ino);
+ proc_unregister(&proc_fs_coda, proc_coda_vfs.low_ino);
+ proc_unregister(&proc_root_fs, proc_fs_coda.low_ino);
+#endif
+}
return b;
}
-#define INSIZE(tag) sizeof(struct cfs_ ## tag ## _in)
-#define OUTSIZE(tag) sizeof(struct cfs_ ## tag ## _out)
+#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
+#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
#define SIZE(tag) max(INSIZE(tag), OUTSIZE(tag))
ENTRY;
insize = SIZE(root);
- UPARG(CFS_ROOT);
+ UPARG(CODA_ROOT);
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (error) {
printk("coda_get_rootfid: error %d\n", error);
} else {
- *fidp = (ViceFid) outp->cfs_root.VFid;
+ *fidp = (ViceFid) outp->coda_root.VFid;
CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n",
fidp->Volume, fidp->Vnode);
}
ENTRY;
insize = SIZE(getattr);
- UPARG(CFS_GETATTR);
- inp->cfs_getattr.VFid = *fid;
+ UPARG(CODA_GETATTR);
+ inp->coda_getattr.VFid = *fid;
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( !error )
- *attr = outp->cfs_getattr.attr;
+ *attr = outp->coda_getattr.attr;
if (inp)
CODA_FREE(inp, insize);
int insize, outsize, error;
insize= SIZE(setattr);
- UPARG(CFS_SETATTR);
+ UPARG(CODA_SETATTR);
- inp->cfs_setattr.VFid = *fid;
- inp->cfs_setattr.attr = *vattr;
+ inp->coda_setattr.VFid = *fid;
+ inp->coda_setattr.attr = *vattr;
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
offset = INSIZE(lookup);
insize = max(offset + length +1, OUTSIZE(lookup));
- UPARG(CFS_LOOKUP);
+ UPARG(CODA_LOOKUP);
- inp->cfs_lookup.VFid = *fid;
- inp->cfs_lookup.name = offset;
+ inp->coda_lookup.VFid = *fid;
+ inp->coda_lookup.name = offset;
+ inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
/* send Venus a null terminated string */
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( !error ) {
- *resfid = outp->cfs_lookup.VFid;
- *type = outp->cfs_lookup.vtype;
+ *resfid = outp->coda_lookup.VFid;
+ *type = outp->coda_lookup.vtype;
}
if (inp) CODA_FREE(inp, insize);
}
-int venus_release(struct super_block *sb, struct ViceFid *fid, int flags)
+int venus_release(struct super_block *sb, struct ViceFid *fid, int flags,
+ struct coda_cred *cred)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = SIZE(close);
- UPARG(CFS_CLOSE);
-
- inp->cfs_close.VFid = *fid;
- inp->cfs_close.flags = flags;
+ UPARG(CODA_CLOSE);
+
+ if ( cred ) {
+ memcpy(&(inp->ih.cred), cred, sizeof(*cred));
+ } else
+ printk("CODA: close without valid file creds.\n");
+
+ inp->coda_close.VFid = *fid;
+ inp->coda_close.flags = flags;
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
int insize, outsize, error;
insize = SIZE(open);
- UPARG(CFS_OPEN);
+ UPARG(CODA_OPEN);
- inp->cfs_open.VFid = *fid;
- inp->cfs_open.flags = flags;
+ inp->coda_open.VFid = *fid;
+ inp->coda_open.flags = flags;
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( !error ) {
- *ino = outp->cfs_open.inode;
- *dev = outp->cfs_open.dev;
+ *ino = outp->coda_open.inode;
+ *dev = outp->coda_open.dev;
} else {
*ino = 0;
*dev = 0;
offset = INSIZE(mkdir);
insize = max(offset + length + 1, OUTSIZE(mkdir));
- UPARG(CFS_MKDIR);
+ UPARG(CODA_MKDIR);
- inp->cfs_mkdir.VFid = *dirfid;
- inp->cfs_mkdir.attr = *attrs;
- inp->cfs_mkdir.name = offset;
+ inp->coda_mkdir.VFid = *dirfid;
+ inp->coda_mkdir.attr = *attrs;
+ inp->coda_mkdir.name = offset;
/* Venus must get null terminated string */
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- *attrs = outp->cfs_mkdir.attr;
- *newfid = outp->cfs_mkdir.VFid;
+ *attrs = outp->coda_mkdir.attr;
+ *newfid = outp->coda_mkdir.VFid;
if (inp)
CODA_FREE(inp, insize);
offset = INSIZE(rename);
insize = max(offset + new_length + old_length + 8,
OUTSIZE(rename));
- UPARG(CFS_RENAME);
+ UPARG(CODA_RENAME);
- inp->cfs_rename.sourceFid = *old_fid;
- inp->cfs_rename.destFid = *new_fid;
- inp->cfs_rename.srcname = offset;
+ inp->coda_rename.sourceFid = *old_fid;
+ inp->coda_rename.destFid = *new_fid;
+ inp->coda_rename.srcname = offset;
/* Venus must receive an null terminated string */
s = ( old_length & ~0x3) +4; /* round up to word boundary */
/* another null terminated string for Venus */
offset += s;
- inp->cfs_rename.destname = offset;
+ inp->coda_rename.destname = offset;
s = ( new_length & ~0x3) +4; /* round up to word boundary */
memcpy((char *)(inp) + offset, new_name, new_length);
*((char *)inp + offset + new_length) = '\0';
CDEBUG(D_INODE, "destname in packet: %s\n",
- (char *)inp + (int) inp->cfs_rename.destname);
+ (char *)inp + (int) inp->coda_rename.destname);
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (inp) CODA_FREE(inp, insize);
offset = INSIZE(create);
insize = max(offset + length + 1, OUTSIZE(create));
- UPARG(CFS_CREATE);
+ UPARG(CODA_CREATE);
- inp->cfs_create.VFid = *dirfid;
- inp->cfs_create.attr.va_mode = mode;
- inp->cfs_create.attr.va_rdev = rdev;
- inp->cfs_create.excl = excl;
- inp->cfs_create.mode = mode;
- inp->cfs_create.name = offset;
+ inp->coda_create.VFid = *dirfid;
+ inp->coda_create.attr.va_mode = mode;
+ inp->coda_create.attr.va_rdev = rdev;
+ inp->coda_create.excl = excl;
+ inp->coda_create.mode = mode;
+ inp->coda_create.name = offset;
/* Venus must get null terminated string */
memcpy((char *)(inp) + offset, name, length);
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- *attrs = outp->cfs_create.attr;
- *newfid = outp->cfs_create.VFid;
+ *attrs = outp->coda_create.attr;
+ *newfid = outp->coda_create.VFid;
if (inp)
CODA_FREE(inp, insize);
offset = INSIZE(rmdir);
insize = max(offset + length + 1, OUTSIZE(rmdir));
- UPARG(CFS_RMDIR);
+ UPARG(CODA_RMDIR);
- inp->cfs_rmdir.VFid = *dirfid;
- inp->cfs_rmdir.name = offset;
+ inp->coda_rmdir.VFid = *dirfid;
+ inp->coda_rmdir.name = offset;
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
offset = INSIZE(remove);
insize = max(offset + length + 1, OUTSIZE(remove));
- UPARG(CFS_REMOVE);
+ UPARG(CODA_REMOVE);
- inp->cfs_remove.VFid = *dirfid;
- inp->cfs_remove.name = offset;
+ inp->coda_remove.VFid = *dirfid;
+ inp->coda_remove.name = offset;
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
char *result;
insize = max(INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
- UPARG(CFS_READLINK);
+ UPARG(CODA_READLINK);
- inp->cfs_readlink.VFid = *fid;
+ inp->coda_readlink.VFid = *fid;
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (! error) {
- retlen = outp->cfs_readlink.count;
+ retlen = outp->coda_readlink.count;
if ( retlen > *length )
retlen = *length;
*length = retlen;
- result = (char *)outp + (int)outp->cfs_readlink.data;
+ result = (char *)outp + (int)outp->coda_readlink.data;
memcpy(buffer, result, retlen);
*(buffer + retlen) = '\0';
}
offset = INSIZE(link);
insize = max(offset + len + 1, OUTSIZE(link));
- UPARG(CFS_LINK);
+ UPARG(CODA_LINK);
- inp->cfs_link.sourceFid = *fid;
- inp->cfs_link.destFid = *dirfid;
- inp->cfs_link.tname = offset;
+ inp->coda_link.sourceFid = *fid;
+ inp->coda_link.destFid = *dirfid;
+ inp->coda_link.tname = offset;
/* make sure strings are null terminated */
memcpy((char *)(inp) + offset, name, len);
offset = INSIZE(symlink);
insize = max(offset + len + symlen + 8, OUTSIZE(symlink));
- UPARG(CFS_SYMLINK);
+ UPARG(CODA_SYMLINK);
- /* inp->cfs_symlink.attr = *tva; XXXXXX */
- inp->cfs_symlink.VFid = *fid;
+ /* inp->coda_symlink.attr = *tva; XXXXXX */
+ inp->coda_symlink.VFid = *fid;
/* Round up to word boundary and null terminate */
- inp->cfs_symlink.srcname = offset;
+ inp->coda_symlink.srcname = offset;
s = ( symlen & ~0x3 ) + 4;
memcpy((char *)(inp) + offset, symname, symlen);
*((char *)inp + offset + symlen) = '\0';
/* Round up to word boundary and null terminate */
offset += s;
- inp->cfs_symlink.tname = offset;
+ inp->coda_symlink.tname = offset;
s = (len & ~0x3) + 4;
memcpy((char *)(inp) + offset, name, len);
*((char *)inp + offset + len) = '\0';
int insize, outsize, error;
insize=SIZE(fsync);
- UPARG(CFS_FSYNC);
+ UPARG(CODA_FSYNC);
- inp->cfs_fsync.VFid = *fid;
+ inp->coda_fsync.VFid = *fid;
error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs),
&outsize, inp);
int insize, outsize, error;
insize = SIZE(access);
- UPARG(CFS_ACCESS);
+ UPARG(CODA_ACCESS);
- inp->cfs_access.VFid = *fid;
- inp->cfs_access.flags = mask;
+ inp->coda_access.VFid = *fid;
+ inp->coda_access.flags = mask;
error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
int iocsize;
insize = VC_MAXMSGSIZE;
- UPARG(CFS_IOCTL);
+ UPARG(CODA_IOCTL);
/* build packet for Venus */
if (data->vi.in_size > VC_MAXDATASIZE) {
goto exit;
}
- inp->cfs_ioctl.VFid = *fid;
+ inp->coda_ioctl.VFid = *fid;
/* the cmd field was mutated by increasing its size field to
* reflect the path and follow args. We need to subtract that
* out before sending the command to Venus. */
- inp->cfs_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
+ inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
- inp->cfs_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
+ inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
- /* in->cfs_ioctl.rwflag = flag; */
- inp->cfs_ioctl.len = data->vi.in_size;
- inp->cfs_ioctl.data = (char *)(INSIZE(ioctl));
+ /* in->coda_ioctl.rwflag = flag; */
+ inp->coda_ioctl.len = data->vi.in_size;
+ inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
/* get the data out of user space */
- if ( copy_from_user((char*)inp + (int)inp->cfs_ioctl.data,
+ if ( copy_from_user((char*)inp + (int)inp->coda_ioctl.data,
data->vi.in, data->vi.in_size) ) {
error = EINVAL;
goto exit;
}
/* Copy out the OUT buffer. */
- if (outp->cfs_ioctl.len > data->vi.out_size) {
+ if (outp->coda_ioctl.len > data->vi.out_size) {
CDEBUG(D_FILE, "return len %d <= request len %d\n",
- outp->cfs_ioctl.len,
+ outp->coda_ioctl.len,
data->vi.out_size);
error = EINVAL;
} else {
if ( error ) goto exit;
if (copy_to_user(data->vi.out,
- (char *)outp + (int)outp->cfs_ioctl.data,
+ (char *)outp + (int)outp->coda_ioctl.data,
data->vi.out_size)) {
error = EINVAL;
goto exit;
*
*/
-static inline unsigned long coda_waitfor_upcall(struct vmsg *vmp)
+static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp)
{
struct wait_queue wait = { current, NULL };
unsigned long posttime;
- vmp->vm_posttime = jiffies;
+ vmp->uc_posttime = jiffies;
posttime = jiffies;
- add_wait_queue(&vmp->vm_sleep, &wait);
+ add_wait_queue(&vmp->uc_sleep, &wait);
for (;;) {
if ( coda_hard == 0 )
current->state = TASK_INTERRUPTIBLE;
current->state = TASK_UNINTERRUPTIBLE;
/* got a reply */
- if ( vmp->vm_flags & VM_WRITE )
+ if ( vmp->uc_flags & REQ_WRITE )
break;
if ( !coda_hard && signal_pending(current) ) {
break;
/* signal is present: after timeout always return
really smart idea, probably useless ... */
- if ( jiffies > vmp->vm_posttime + coda_timeout * HZ )
+ if ( jiffies - vmp->uc_posttime > coda_timeout * HZ )
break;
}
schedule();
}
- remove_wait_queue(&vmp->vm_sleep, &wait);
+ remove_wait_queue(&vmp->uc_sleep, &wait);
current->state = TASK_RUNNING;
CDEBUG(D_SPECIAL, "posttime: %ld, returned: %ld\n", posttime, jiffies-posttime);
union inputArgs *buffer)
{
unsigned long runtime;
- struct vcomm *vcommp;
+ struct venus_comm *vcommp;
union outputArgs *out;
- struct vmsg *vmp;
+ struct upc_req *req;
int error = 0;
ENTRY;
- if (sbi->sbi_vcomm == NULL) {
- return -ENODEV;
- }
- vcommp = sbi->sbi_vcomm;
-
-
- if (!vcomm_open(vcommp))
+ vcommp = &coda_upc_comm;
+ if ( !vcommp->vc_pid ) {
+ printk("No pseudo device in upcall comms at %p\n", vcommp);
return -ENXIO;
+ }
/* Format the request message. */
- CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
- vmp->vm_data = (void *)buffer;
- vmp->vm_flags = 0;
- vmp->vm_inSize = inSize;
- vmp->vm_outSize = *outSize ? *outSize : inSize;
- vmp->vm_opcode = ((union inputArgs *)buffer)->ih.opcode;
- vmp->vm_unique = ++vcommp->vc_seq;
- vmp->vm_sleep = NULL;
+ CODA_ALLOC(req,struct upc_req *,sizeof(struct upc_req));
+ req->uc_data = (void *)buffer;
+ req->uc_flags = 0;
+ req->uc_inSize = inSize;
+ req->uc_outSize = *outSize ? *outSize : inSize;
+ req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
+ req->uc_unique = ++vcommp->vc_seq;
+ req->uc_sleep = NULL;
/* Fill in the common input args. */
- ((union inputArgs *)buffer)->ih.unique = vmp->vm_unique;
+ ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
/* Append msg to pending queue and poke Venus. */
- coda_q_insert(&(vmp->vm_chain), &(vcommp->vc_pending));
+ list_add(&(req->uc_chain), vcommp->vc_pending.prev);
CDEBUG(D_UPCALL,
"Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %x.zzz.\n",
- current->pid, vmp->vm_opcode, vmp->vm_unique, (int)vmp);
+ current->pid, req->uc_opcode, req->uc_unique, (int)req);
wake_up_interruptible(&vcommp->vc_waitq);
/* We can be interrupted while we wait for Venus to process
* ENODEV. */
/* Go to sleep. Wake up on signals only after the timeout. */
- runtime = coda_waitfor_upcall(vmp);
+ runtime = coda_waitfor_upcall(req);
coda_upcall_stats(((union inputArgs *)buffer)->ih.opcode, runtime);
CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n",
- vmp->vm_opcode, jiffies - vmp->vm_posttime,
- vmp->vm_unique, vmp->vm_outSize);
+ req->uc_opcode, jiffies - req->uc_posttime,
+ req->uc_unique, req->uc_outSize);
CDEBUG(D_UPCALL,
- "..process %d woken up by Venus for vmp at 0x%x, data at %x\n",
- current->pid, (int)vmp, (int)vmp->vm_data);
- if (vcomm_open(vcommp)) { /* i.e. Venus is still alive */
+ "..process %d woken up by Venus for req at 0x%x, data at %x\n",
+ current->pid, (int)req, (int)req->uc_data);
+ if (vcommp->vc_pid) { /* i.e. Venus is still alive */
/* Op went through, interrupt or not... */
- if (vmp->vm_flags & VM_WRITE) {
- out = (union outputArgs *)vmp->vm_data;
+ if (req->uc_flags & REQ_WRITE) {
+ out = (union outputArgs *)req->uc_data;
/* here we map positive Venus errors to kernel errors */
if ( out->oh.result < 0 ) {
printk("Tell Peter: Venus returns negative error %ld, for oc %ld!\n",
CDEBUG(D_UPCALL,
"upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n",
out->oh.unique, out->oh.opcode, out->oh.result, out);
- *outSize = vmp->vm_outSize;
+ *outSize = req->uc_outSize;
goto exit;
}
- if ( !(vmp->vm_flags & VM_READ) && signal_pending(current)) {
+ if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) {
/* Interrupted before venus read it. */
CDEBUG(D_UPCALL,
"Interrupted before read:(op,un) (%d.%d), flags = %x\n",
- vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
- coda_q_remove(&(vmp->vm_chain));
+ req->uc_opcode, req->uc_unique, req->uc_flags);
+ list_del(&(req->uc_chain));
/* perhaps the best way to convince the app to
give up? */
error = -EINTR;
goto exit;
}
- if ( (vmp->vm_flags & VM_READ) && signal_pending(current) ) {
+ if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) {
/* interrupted after Venus did its read, send signal */
- union inputArgs *dog;
- struct vmsg *svmp;
+ union inputArgs *sig_inputArgs;
+ struct upc_req *sig_req;
CDEBUG(D_UPCALL,
"Sending Venus a signal: op = %d.%d, flags = %x\n",
- vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+ req->uc_opcode, req->uc_unique, req->uc_flags);
- coda_q_remove(&(vmp->vm_chain));
+ list_del(&(req->uc_chain));
error = -EINTR;
- CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
- CODA_ALLOC((svmp->vm_data), char *, sizeof(struct cfs_in_hdr));
+ CODA_ALLOC(sig_req, struct upc_req *, sizeof (struct upc_req));
+ CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
- dog = (union inputArgs *)svmp->vm_data;
- dog->ih.opcode = CFS_SIGNAL;
- dog->ih.unique = vmp->vm_unique;
+ sig_inputArgs = (union inputArgs *)sig_req->uc_data;
+ sig_inputArgs->ih.opcode = CODA_SIGNAL;
+ sig_inputArgs->ih.unique = req->uc_unique;
- svmp->vm_flags = 0;
- svmp->vm_opcode = dog->ih.opcode;
- svmp->vm_unique = dog->ih.unique;
- svmp->vm_inSize = sizeof(struct cfs_in_hdr);
- svmp->vm_outSize = sizeof(struct cfs_in_hdr);
+ sig_req->uc_flags = REQ_ASYNC;
+ sig_req->uc_opcode = sig_inputArgs->ih.opcode;
+ sig_req->uc_unique = sig_inputArgs->ih.unique;
+ sig_req->uc_inSize = sizeof(struct coda_in_hdr);
+ sig_req->uc_outSize = sizeof(struct coda_in_hdr);
CDEBUG(D_UPCALL,
"coda_upcall: enqueing signal msg (%d, %d)\n",
- svmp->vm_opcode, svmp->vm_unique);
+ sig_req->uc_opcode, sig_req->uc_unique);
/* insert at head of queue! */
- coda_q_insert(&(svmp->vm_chain), vcommp->vc_pending.forw);
+ list_add(&(sig_req->uc_chain), &vcommp->vc_pending);
wake_up_interruptible(&vcommp->vc_waitq);
} else {
printk("Coda: Strange interruption..\n");
}
} else { /* If venus died i.e. !VC_OPEN(vcommp) */
printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n",
- vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+ req->uc_opcode, req->uc_unique, req->uc_flags);
error = -ENODEV;
}
exit:
- CODA_FREE(vmp, sizeof(struct vmsg));
+ CODA_FREE(req, sizeof(struct upc_req));
if (error)
badclstats();
return error;
* There are 7 cases where cache invalidations occur. The semantics
* of each is listed here:
*
- * CFS_FLUSH -- flush all entries from the name cache and the cnode cache.
- * CFS_PURGEUSER -- flush all entries from the name cache for a specific user
+ * CODA_FLUSH -- flush all entries from the name cache and the cnode cache.
+ * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
* This call is a result of token expiration.
*
* The next arise as the result of callbacks on a file or directory.
- * CFS_ZAPFILE -- flush the cached attributes for a file.
+ * CODA_ZAPFILE -- flush the cached attributes for a file.
- * CFS_ZAPDIR -- flush the attributes for the dir and
+ * CODA_ZAPDIR -- flush the attributes for the dir and
* force a new lookup for all the children
of this dir.
*
* The next is a result of Venus detecting an inconsistent file.
- * CFS_PURGEFID -- flush the attribute for the file
+ * CODA_PURGEFID -- flush the attribute for the file
* purge it and its children from the dcache
*
* The last allows Venus to replace local fids with global ones
* during reintegration.
*
- * CFS_REPLACE -- replace one ViceFid with another throughout the name cache */
+ * CODA_REPLACE -- replace one ViceFid with another throughout the name cache */
int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
{
switch (opcode) {
- case CFS_FLUSH : {
- clstats(CFS_FLUSH);
- CDEBUG(D_DOWNCALL, "CFS_FLUSH\n");
+ case CODA_FLUSH : {
+ clstats(CODA_FLUSH);
+ CDEBUG(D_DOWNCALL, "CODA_FLUSH\n");
coda_cache_clear_all(sb);
shrink_dcache_sb(sb);
+ coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
return(0);
}
- case CFS_PURGEUSER : {
- struct coda_cred *cred = &out->cfs_purgeuser.cred;
- CDEBUG(D_DOWNCALL, "CFS_PURGEUSER\n");
+ case CODA_PURGEUSER : {
+ struct coda_cred *cred = &out->coda_purgeuser.cred;
+ CDEBUG(D_DOWNCALL, "CODA_PURGEUSER\n");
if ( !cred ) {
printk("PURGEUSER: null cred!\n");
return 0;
}
- clstats(CFS_PURGEUSER);
+ clstats(CODA_PURGEUSER);
coda_cache_clear_cred(sb, cred);
return(0);
}
- case CFS_ZAPDIR : {
+ case CODA_ZAPDIR : {
struct inode *inode;
- ViceFid *fid = &out->cfs_zapdir.CodaFid;
+ ViceFid *fid = &out->coda_zapdir.CodaFid;
CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", coda_f2s(fid));
- clstats(CFS_ZAPDIR);
+ clstats(CODA_ZAPDIR);
inode = coda_fid_to_inode(fid, sb);
if (inode) {
CDEBUG(D_DOWNCALL, "zapdir: inode = %ld children flagged\n",
inode->i_ino);
- coda_purge_children(inode);
+ coda_flag_inode_children(inode, C_PURGE);
CDEBUG(D_DOWNCALL, "zapdir: inode = %ld cache cleared\n", inode->i_ino);
coda_flag_inode(inode, C_VATTR);
} else
return(0);
}
- case CFS_ZAPFILE : {
+ case CODA_ZAPFILE : {
struct inode *inode;
- struct ViceFid *fid = &out->cfs_zapfile.CodaFid;
- clstats(CFS_ZAPFILE);
+ struct ViceFid *fid = &out->coda_zapfile.CodaFid;
+ clstats(CODA_ZAPFILE);
CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid));
inode = coda_fid_to_inode(fid, sb);
if ( inode ) {
return 0;
}
- case CFS_PURGEFID : {
+ case CODA_PURGEFID : {
struct inode *inode;
- ViceFid *fid = &out->cfs_purgefid.CodaFid;
+ ViceFid *fid = &out->coda_purgefid.CodaFid;
CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid));
- clstats(CFS_PURGEFID);
+ clstats(CODA_PURGEFID);
inode = coda_fid_to_inode(fid, sb);
if ( inode ) {
CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n", inode->i_ino);
- coda_purge_children(inode);
+ coda_flag_inode_children(inode, C_PURGE);
coda_purge_dentries(inode);
}else
CDEBUG(D_DOWNCALL, "purgefid: no inode\n");
return 0;
}
- case CFS_REPLACE : {
+ case CODA_REPLACE : {
struct inode *inode;
- ViceFid *oldfid = &out->cfs_replace.OldFid;
- ViceFid *newfid = &out->cfs_replace.NewFid;
- clstats(CFS_REPLACE);
- CDEBUG(D_DOWNCALL, "CFS_REPLACE\n");
+ ViceFid *oldfid = &out->coda_replace.OldFid;
+ ViceFid *newfid = &out->coda_replace.NewFid;
+ clstats(CODA_REPLACE);
+ CDEBUG(D_DOWNCALL, "CODA_REPLACE\n");
inode = coda_fid_to_inode(oldfid, sb);
if ( inode ) {
CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", inode->i_ino);
if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
EXT2_FEATURE_INCOMPAT_FILETYPE))
de->file_type = EXT2_FT_REG_FILE;
- } else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &ext2_dir_inode_operations;
- if (dir->i_mode & S_ISGID)
- inode->i_mode |= S_ISGID;
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_DIR;
- }
- else if (S_ISLNK(inode->i_mode)) {
- inode->i_op = &ext2_symlink_inode_operations;
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_SYMLINK;
} else if (S_ISCHR(inode->i_mode)) {
inode->i_op = &chrdev_inode_operations;
if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
inode = dentry->d_inode;
DQUOT_INIT(inode);
- retval = -EPERM;
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- goto end_unlink;
- if ((dir->i_mode & S_ISVTX) &&
- current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid && !capable(CAP_FOWNER))
- goto end_unlink;
-
retval = -EIO;
if (le32_to_cpu(de->inode) != inode->i_ino)
goto end_unlink;
goto end_rename;
old_bh = ext2_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de);
- retval = -ENOENT;
- if (!old_bh)
- goto end_rename;
+ /*
+ * Check for inode number is _not_ due to possible IO errors.
+ * We might rmdir the source, keep it as pwd of some process
+ * and merrily kill the link to whatever was created under the
+ * same name. Goodbye sticky bit ;-<
+ */
old_inode = old_dentry->d_inode;
-
- retval = -EPERM;
- if ((old_dir->i_mode & S_ISVTX) &&
- current->fsuid != old_inode->i_uid &&
- current->fsuid != old_dir->i_uid && !capable(CAP_FOWNER))
- goto end_rename;
- if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode))
+ retval = -ENOENT;
+ if (!old_bh || le32_to_cpu(old_de->inode) != old_inode->i_ino)
goto end_rename;
new_inode = new_dentry->d_inode;
retval = 0;
if (new_inode == old_inode)
goto end_rename;
- if (new_inode && S_ISDIR(new_inode->i_mode)) {
- retval = -EISDIR;
- if (!S_ISDIR(old_inode->i_mode))
- goto end_rename;
- retval = -EINVAL;
- if (is_subdir(new_dentry, old_dentry))
- goto end_rename;
- /* Prune any children before testing for busy */
- if (new_dentry->d_count > 1)
- shrink_dcache_parent(new_dentry);
- retval = -ENOTEMPTY;
- if (!empty_dir (new_inode))
- goto end_rename;
- retval = -EBUSY;
- if (new_dentry->d_count > 1)
- goto end_rename;
- }
- retval = -EPERM;
- if (new_inode) {
- if ((new_dir->i_mode & S_ISVTX) &&
- current->fsuid != new_inode->i_uid &&
- current->fsuid != new_dir->i_uid && !capable(CAP_FOWNER))
- goto end_rename;
- if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode))
- goto end_rename;
- }
if (S_ISDIR(old_inode->i_mode)) {
- retval = -ENOTDIR;
- if (new_inode && !S_ISDIR(new_inode->i_mode))
- goto end_rename;
retval = -EINVAL;
if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ if (new_inode) {
+ /* Prune any children before testing for busy */
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
+ retval = -EBUSY;
+ if (new_dentry->d_count > 1)
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir (new_inode))
+ goto end_rename;
+ }
dir_bh = ext2_bread (old_inode, 0, 0, &retval);
if (!dir_bh)
goto end_rename;
inode->i_op = NULL;
if (S_ISREG(inode->i_mode))
inode->i_op = &minix_file_inode_operations;
- else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &minix_dir_inode_operations;
- if (dir->i_mode & S_ISGID)
- inode->i_mode |= S_ISGID;
- }
- else if (S_ISLNK(inode->i_mode))
- inode->i_op = &minix_symlink_inode_operations;
else if (S_ISCHR(inode->i_mode))
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
schedule();
goto repeat;
}
- if ((dir->i_mode & S_ISVTX) &&
- current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid && !capable(CAP_FOWNER))
- goto end_unlink;
if (de->inode != inode->i_ino) {
retval = -ENOENT;
goto end_unlink;
goto end_rename;
old_inode = old_dentry->d_inode;
retval = -EPERM;
- if ((old_dir->i_mode & S_ISVTX) &&
- current->fsuid != old_inode->i_uid &&
- current->fsuid != old_dir->i_uid && !capable(CAP_FOWNER))
- goto end_rename;
new_inode = new_dentry->d_inode;
new_bh = minix_find_entry(new_dir, new_dentry->d_name.name,
new_dentry->d_name.len, &new_de);
retval = 0;
goto end_rename;
}
- if (new_inode && S_ISDIR(new_inode->i_mode)) {
- retval = -EISDIR;
- if (!S_ISDIR(old_inode->i_mode))
- goto end_rename;
- retval = -EINVAL;
- if (is_subdir(new_dentry, old_dentry))
- goto end_rename;
- retval = -ENOTEMPTY;
- if (!empty_dir(new_inode))
- goto end_rename;
- retval = -EBUSY;
- if (new_inode->i_count > 1)
- goto end_rename;
- }
- retval = -EPERM;
- if (new_inode && (new_dir->i_mode & S_ISVTX) &&
- current->fsuid != new_inode->i_uid &&
- current->fsuid != new_dir->i_uid && !capable(CAP_FOWNER))
- goto end_rename;
if (S_ISDIR(old_inode->i_mode)) {
- retval = -ENOTDIR;
- if (new_inode && !S_ISDIR(new_inode->i_mode))
- goto end_rename;
retval = -EINVAL;
if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ if (new_inode) {
+ /* Prune any children before testing for busy */
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
+ retval = -EBUSY;
+ if (new_dentry->d_count > 1)
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_inode))
+ goto end_rename;
+ retval = -EBUSY;
+ }
retval = -EIO;
dir_bh = minix_bread(old_inode,0,0);
if (!dir_bh)
if (res < 0)
goto rmdir_done;
/*
- * Check whether the directory is empty, then prune
- * any child dentries and make sure it's not in use.
+ * Check whether the directory is not in use, then check
+ * whether it is empty.
*/
- res = msdos_empty(inode);
- if (res)
- goto rmdir_done;
res = -EBUSY;
if (!list_empty(&dentry->d_hash)) {
#ifdef MSDOS_DEBUG
#endif
goto rmdir_done;
}
+ res = msdos_empty(inode);
+ if (res)
+ goto rmdir_done;
inode->i_nlink = 0;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
- MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+
+#ifdef whatfor
/*
- * Instantiate the dentry now, in case we need to cleanup.
+ * He's dead, Jim. We don't d_instantiate anymore. Should do it
+ * from the very beginning, actually.
*/
- d_instantiate(dentry, inode);
+ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+#endif
if ((res = fat_add_cluster(inode)) < 0)
goto mkdir_error;
MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
dot->i_nlink = dir->i_nlink;
mark_inode_dirty(dot);
+#ifdef whatfor
MSDOS_I(inode)->i_busy = 0;
+#endif
iput(dot);
+ d_instantiate(dentry, inode);
res = 0;
out_unlock:
mkdir_error:
printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
- if (msdos_rmdir(dir,dentry) < 0)
- fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+ bh = NULL;
+ fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY);
+ inode->i_nlink = 0;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_nlink--;
+ mark_inode_dirty(inode);
+ mark_inode_dirty(dir);
+ iput(inode);
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_brelse(sb, bh);
goto out_unlock;
out_exist:
res = -EPERM;
if (!S_ISREG(inode->i_mode) && nospc)
goto unlink_done;
- if (IS_IMMUTABLE(inode))
- goto unlink_done;
/* N.B. check for busy files? */
inode->i_nlink = 0;
return dentry;
}
+/*
+ * It's inline, so penalty for filesystems that don't use sticky bit is
+ * minimal.
+ */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+ if (!(dir->i_mode & S_ISVTX))
+ return 0;
+ if (inode->i_uid == current->fsuid)
+ return 0;
+ if (dir->i_uid == current->fsuid)
+ return 0;
+ return !capable(CAP_FOWNER);
+}
+
+/*
+ * Check whether we can remove a link victim from directory dir, check
+ * whether the type of victim is right.
+ * 1. We can't do it if dir is read-only (done in permission())
+ * 2. We should have write and exec permissions on dir
+ * 3. We can't remove anything from append-only dir
+ * 4. We can't do anything with immutable dir (done in permission())
+ * 5. If the sticky bit on dir is set we should either
+ * a. be owner of dir, or
+ * b. be owner of victim, or
+ * c. have CAP_FOWNER capability
+ * 6. If the victim is append-only or immutable we can't do antyhing with
+ * links pointing to it.
+ * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
+ * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
+ * 9. We can't remove a root or mountpoint.
+ */
+static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir)
+{
+ int error;
+ if (!victim->d_inode || victim->d_parent->d_inode != dir)
+ return -ENOENT;
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ return error;
+ if (IS_APPEND(dir))
+ return -EPERM;
+ if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
+ IS_IMMUTABLE(victim->d_inode))
+ return -EPERM;
+ if (isdir) {
+ if (!S_ISDIR(victim->d_inode->i_mode))
+ return -ENOTDIR;
+ if (IS_ROOT(victim))
+ return -EBUSY;
+ if (victim->d_mounts != victim->d_covers)
+ return -EBUSY;
+ } else if (S_ISDIR(victim->d_inode->i_mode))
+ return -EISDIR;
+ return 0;
+}
+
+/* Check whether we can create an object with dentry child in directory
+ * dir.
+ * 1. We can't do it if child already exists (open has special treatment for
+ * this case, but since we are inlined it's OK)
+ * 2. We can't do it if dir is read-only (done in permission())
+ * 3. We should have write and exec permissions on dir
+ * 4. We can't do it if dir is immutable (done in permission())
+ */
+static inline int may_create(struct inode *dir, struct dentry *child) {
+ if (child->d_inode)
+ return -EEXIST;
+ return permission(dir,MAY_WRITE | MAY_EXEC);
+}
+
static inline struct dentry *get_parent(struct dentry *dentry)
{
return dget(dentry->d_parent);
error = 0;
if (flag & O_EXCL)
error = -EEXIST;
- } else if (IS_RDONLY(dir->d_inode))
- error = -EROFS;
- else if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
- error = -EACCES;
- else if ((error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC)) == 0) {
- DQUOT_INIT(dir->d_inode);
- error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);
- /* Don't check for write permission, don't truncate */
- acc_mode = 0;
- flag &= ~O_TRUNC;
+ } else if ((error = may_create(dir->d_inode, dentry)) == 0) {
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
+ error = -EACCES;
+ else {
+ DQUOT_INIT(dir->d_inode);
+ error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);
+ /* Don't check for write permission, don't truncate */
+ acc_mode = 0;
+ flag &= ~O_TRUNC;
+ }
}
unlock_dir(dir);
if (error)
if (IS_ERR(dir))
goto exit;
- retval = ERR_PTR(-EEXIST);
- if (dentry->d_inode)
- goto exit_lock;
-
- retval = ERR_PTR(-EROFS);
- if (IS_RDONLY(dir->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
- retval = ERR_PTR(error);
+ error = may_create(dir->d_inode, dentry);
if (error)
goto exit_lock;
- retval = ERR_PTR(-EPERM);
+ error = -EPERM;
if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod)
goto exit_lock;
DQUOT_INIT(dir->d_inode);
error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);
+exit_lock:
retval = ERR_PTR(error);
if (!error)
retval = dget(dentry);
-
-exit_lock:
unlock_dir(dir);
exit:
dput(dentry);
if (IS_ERR(dir))
goto exit_dput;
- error = -EEXIST;
- if (dentry->d_inode)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(dir->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
+ error = may_create(dir->d_inode, dentry);
if (error)
goto exit_lock;
dput(d2);
}
-/*
- * It's inline, so penalty for filesystems that don't use sticky bit is
- * minimal.
- */
-static inline int check_sticky(struct inode *dir, struct inode *inode)
-{
- if (!(dir->i_mode & S_ISVTX))
- return 0;
- if (inode->i_uid == current->fsuid)
- return 0;
- if (dir->i_uid == current->fsuid)
- return 0;
- return !capable(CAP_FOWNER);
-}
-int VFS_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
{
int error;
- if (IS_RDONLY(dir))
- return -EROFS;
-
- error = permission(dir,MAY_WRITE | MAY_EXEC);
+ error = may_delete(dir, dentry, 1);
if (error)
return error;
- /*
- * A subdirectory cannot be removed from an append-only directory.
- */
- if (IS_APPEND(dir))
- return -EPERM;
- /*
- * Check the sticky bit.
- */
- if (check_sticky(dir, dentry->d_inode))
- return -EPERM;
-
- /* Disallow removals of mountpoints. */
- if (dentry->d_mounts != dentry->d_covers)
- return -EBUSY;
-
if (!dir->i_op || !dir->i_op->rmdir)
return -EPERM;
- /*
- * I suspect that these two checks are atavisms copied from minixfs
- * and it looks like they can be dropped. Anyway, it will be simpler
- * to drop them from here and even if those checks are needed they
- * belong to VFS.
- */
- if (dir == dentry->d_inode)
- return -EPERM;
- if (dir->i_dev != dentry->d_inode->i_dev)
- return -EPERM;
- /*
- * Non-directories can't be rmdir'd. It may confuse the heck of
- * NFS and CODA. Testing it in VFS is the Right Thing (tm), anyway.
- */
- if (!S_ISDIR(dentry->d_inode->i_mode))
- return -ENOTDIR;
+
DQUOT_INIT(dir);
/*
error = -ENOENT;
if (!dentry->d_inode)
goto exit_dput;
+
+ dir = dget(dentry->d_parent);
+
/*
* The dentry->d_count stuff confuses d_delete() enough to
* not kill the inode from under us while it is locked. This
* in the inode, not in the dentry..
*/
dentry->d_count++;
- dir = dget(dentry->d_parent);
double_lock(dir, dentry);
- /*
- * Check that dentry still sits where it did and do the real stuff.
- */
- if (dentry->d_parent == dir)
- error = VFS_rmdir(dir->d_inode, dentry);
+ error = vfs_rmdir(dir->d_inode, dentry);
double_unlock(dentry, dir);
exit_dput:
return error;
}
+int vfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ error = may_delete(dir, dentry, 0);
+ if (error)
+ goto exit_lock;
+
+ if (!dir->i_op || !dir->i_op->unlink)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+
+ error = dir->i_op->unlink(dir, dentry);
+
+exit_lock:
+ return error;
+}
+
static inline int do_unlink(const char * name)
{
int error;
if (IS_ERR(dir))
goto exit_dput;
- error = -ENOENT;
- if (!dentry->d_inode)
- goto exit_lock;
-
- /* Mount point? */
- error = -EBUSY;
- if (dentry == dir)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(dir->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
- if (error)
- goto exit_lock;
-
- /*
- * A directory can't be unlink'ed.
- * A file cannot be removed from an append-only directory.
- */
- error = -EPERM;
- if (S_ISDIR(dentry->d_inode->i_mode))
- goto exit_lock;
-
- if (IS_APPEND(dir->d_inode))
- goto exit_lock;
-
- if (!dir->d_inode->i_op || !dir->d_inode->i_op->unlink)
- goto exit_lock;
+ error = vfs_unlink(dir->d_inode, dentry);
- DQUOT_INIT(dir->d_inode);
-
- error = dir->d_inode->i_op->unlink(dir->d_inode, dentry);
-
-exit_lock:
unlock_dir(dir);
exit_dput:
dput(dentry);
if (IS_ERR(dir))
goto exit_dput;
- error = -EEXIST;
- if (dentry->d_inode)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(dir->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
+ error = may_create(dir->d_inode, dentry);
if (error)
goto exit_lock;
if (!inode)
goto exit_lock;
- error = -EEXIST;
- if (new_dentry->d_inode)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(dir->d_inode))
+ error = may_create(dir->d_inode, new_dentry);
+ if (error)
goto exit_lock;
error = -EXDEV;
if (dir->d_inode->i_dev != inode->i_dev)
goto exit_lock;
- error = permission(dir->d_inode, MAY_WRITE | MAY_EXEC);
- if (error)
- goto exit_lock;
-
/*
* A link to an append-only or immutable file cannot be created.
*/
return error;
}
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+ int isdir;
+
+ isdir = S_ISDIR(old_dentry->d_inode->i_mode);
+
+ error = may_delete(old_dir, old_dentry, isdir); /* XXX */
+ if (error)
+ return error;
+
+ if (new_dir->i_dev != old_dir->i_dev)
+ return -EXDEV;
+
+ if (!new_dentry->d_inode)
+ error = may_create(new_dir, new_dentry);
+ else
+ error = may_delete(new_dir, new_dentry, isdir);
+ if (error)
+ return error;
+
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+
+ DQUOT_INIT(old_dir);
+ DQUOT_INIT(new_dir);
+ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+
+ return error;
+}
+
static inline int do_rename(const char * oldname, const char * newname)
{
int error;
double_lock(new_dir, old_dir);
- error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
- if (error)
- goto exit_lock;
- error = permission(new_dir->d_inode,MAY_WRITE | MAY_EXEC);
- if (error)
- goto exit_lock;
-
- /* Disallow moves of mountpoints. */
- error = -EBUSY;
- if (old_dir == old_dentry || new_dir == new_dentry)
- goto exit_lock;
-
- error = -EXDEV;
- if (new_dir->d_inode->i_dev != old_dir->d_inode->i_dev)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(new_dir->d_inode) || IS_RDONLY(old_dir->d_inode))
- goto exit_lock;
+ error = vfs_rename(old_dir->d_inode, old_dentry,
+ new_dir->d_inode, new_dentry);
- /*
- * A file cannot be removed from an append-only directory.
- */
- error = -EPERM;
- if (IS_APPEND(old_dir->d_inode))
- goto exit_lock;
-
- error = -EPERM;
- if (!old_dir->d_inode->i_op || !old_dir->d_inode->i_op->rename)
- goto exit_lock;
-
- DQUOT_INIT(old_dir->d_inode);
- DQUOT_INIT(new_dir->d_inode);
- error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry,
- new_dir->d_inode, new_dentry);
-
-exit_lock:
double_unlock(new_dir, old_dir);
dput(new_dentry);
exit_old:
(tlen == 1 || (tlen == 2 && tname[1] == '.'))))
goto out;
- err = -EXDEV;
- if (fdir->i_dev != tdir->i_dev)
- goto out_nfserr;
- err = -EPERM;
- if (!fdir->i_op || !fdir->i_op->rename)
- goto out_nfserr;
-
odentry = lookup_dentry(fname, dget(fdentry), 0);
err = PTR_ERR(odentry);
if (IS_ERR(odentry))
nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
/* N.B. check for parent changes after locking?? */
- DQUOT_INIT(fdir);
- DQUOT_INIT(tdir);
- err = fdir->i_op->rename(fdir, odentry, tdir, ndentry);
+ err = vfs_rename(fdir, odentry, tdir, ndentry);
if (!err && EX_ISSYNC(tfhp->fh_export)) {
write_inode_now(fdir);
write_inode_now(tdir);
if (IS_ERR(rdentry))
goto out_nfserr;
- /*
- * FIXME!!
- *
- * This should do a double-lock on both rdentry and the parent
- *
- * Moreover, it should do checks *both* for unlink and rmdir
- * cases. AV
- */
if (type != S_IFDIR) {
/* It's UNLINK */
err = fh_lock_parent(fhp, rdentry);
if (err)
goto out;
- DQUOT_INIT(dirp);
- err = -EPERM;
- if (dirp->i_op && dirp->i_op->unlink)
- err = dirp->i_op->unlink(dirp, rdentry);
+ err = vfs_unlink(dirp, rdentry);
+
DQUOT_DROP(dirp);
fh_unlock(fhp);
err = -ENOENT;
if (rdentry->d_parent->d_inode == dirp)
- err = VFS_rmdir(dirp, rdentry);
+ err = vfs_rmdir(dirp, rdentry);
rdentry->d_count--;
DQUOT_DROP(dirp);
tristate 'Codepage 866 (Cyrillic/Russian)' CONFIG_NLS_CODEPAGE_866
tristate 'Codepage 869 (Greek)' CONFIG_NLS_CODEPAGE_869
tristate 'Codepage 874 (Thai)' CONFIG_NLS_CODEPAGE_874
- tristate 'NLS ISO 8859-1 (Latin 1; Western European Languages)' CONFIG_NLS_ISO8859_1
- tristate 'NLS ISO 8859-2 (Latin 2; Slavic/Central European)' CONFIG_NLS_ISO8859_2
- tristate 'NLS ISO 8859-3 (Latin 3; Esperanto, Galician, Maltese, Turkish)' CONFIG_NLS_ISO8859_3
- tristate 'NLS ISO 8859-4 (Latin 4; Estonian, Latvian, Lithuanian)' CONFIG_NLS_ISO8859_4
- tristate 'NLS ISO 8859-5 (Cyrillic)' CONFIG_NLS_ISO8859_5
- tristate 'NLS ISO 8859-6 (Arabic)' CONFIG_NLS_ISO8859_6
- tristate 'NLS ISO 8859-7 (Modern Greek)' CONFIG_NLS_ISO8859_7
- tristate 'NLS ISO 8859-8 (Hebrew)' CONFIG_NLS_ISO8859_8
- tristate 'NLS ISO 8859-9 (Latin 5; Turkey)' CONFIG_NLS_ISO8859_9
- tristate 'NLS KOI8-R (Russian)' CONFIG_NLS_KOI8_R
+ tristate 'NLS ISO 8859-1 (Latin 1; Western European Languages)' CONFIG_NLS_ISO8859_1
+ tristate 'NLS ISO 8859-2 (Latin 2; Slavic/Central European)' CONFIG_NLS_ISO8859_2
+ tristate 'NLS ISO 8859-3 (Latin 3; Esperanto, Galician, Maltese, Turkish)' CONFIG_NLS_ISO8859_3
+ tristate 'NLS ISO 8859-4 (Latin 4; Estonian, Latvian, Lithuanian)' CONFIG_NLS_ISO8859_4
+ tristate 'NLS ISO 8859-5 (Cyrillic)' CONFIG_NLS_ISO8859_5
+ tristate 'NLS ISO 8859-6 (Arabic)' CONFIG_NLS_ISO8859_6
+ tristate 'NLS ISO 8859-7 (Modern Greek)' CONFIG_NLS_ISO8859_7
+ tristate 'NLS ISO 8859-8 (Hebrew)' CONFIG_NLS_ISO8859_8
+ tristate 'NLS ISO 8859-9 (Latin 5; Turkey)' CONFIG_NLS_ISO8859_9
+ tristate 'NLS ISO 8859-15 (Latin 9; Western European with Euro)' CONFIG_NLS_ISO8859_15
+ tristate 'NLS KOI8-R (Russian)' CONFIG_NLS_KOI8_R
endmenu
fi
endif
endif
+ifeq ($(CONFIG_NLS_ISO8859_15),y)
+NLS += nls_iso8859-15.o
+else
+ ifeq ($(CONFIG_NLS_ISO8859_15),m)
+ M_OBJS += nls_iso8859-15.o
+ endif
+endif
+
ifeq ($(CONFIG_NLS_KOI8_R),y)
NLS += nls_koi8-r.o
else
#ifdef CONFIG_NLS_ISO8859_9
init_nls_iso8859_9();
#endif
+#ifdef CONFIG_NLS_ISO8859_15
+ init_nls_iso8859_15();
+#endif
#ifdef CONFIG_NLS_CODEPAGE_437
init_nls_cp437();
#endif
--- /dev/null
+/*
+ * linux/fs/nls_iso8859-15.c
+ *
+ * Charset iso8859-15 translation tables.
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+
+static struct nls_unicode charset2uni[256] = {
+ /* 0x00*/
+ {0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00},
+ {0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00},
+ {0x08, 0x00}, {0x09, 0x00}, {0x0a, 0x00}, {0x0b, 0x00},
+ {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
+ /* 0x10*/
+ {0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00},
+ {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00},
+ {0x18, 0x00}, {0x19, 0x00}, {0x1a, 0x00}, {0x1b, 0x00},
+ {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
+ /* 0x20*/
+ {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
+ {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00},
+ {0x28, 0x00}, {0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00},
+ {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x2f, 0x00},
+ /* 0x30*/
+ {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+ {0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
+ {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0x00}, {0x3b, 0x00},
+ {0x3c, 0x00}, {0x3d, 0x00}, {0x3e, 0x00}, {0x3f, 0x00},
+ /* 0x40*/
+ {0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00},
+ {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
+ {0x48, 0x00}, {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00},
+ {0x4c, 0x00}, {0x4d, 0x00}, {0x4e, 0x00}, {0x4f, 0x00},
+ /* 0x50*/
+ {0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00},
+ {0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+ {0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00},
+ {0x5c, 0x00}, {0x5d, 0x00}, {0x5e, 0x00}, {0x5f, 0x00},
+ /* 0x60*/
+ {0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00},
+ {0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
+ {0x68, 0x00}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00},
+ {0x6c, 0x00}, {0x6d, 0x00}, {0x6e, 0x00}, {0x6f, 0x00},
+ /* 0x70*/
+ {0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00},
+ {0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
+ {0x78, 0x00}, {0x79, 0x00}, {0x7a, 0x00}, {0x7b, 0x00},
+ {0x7c, 0x00}, {0x7d, 0x00}, {0x7e, 0x00}, {0x7f, 0x00},
+ /* 0x80*/
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ /* 0x90*/
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+ /* 0xa0*/
+ {0xa0, 0x00}, {0xa1, 0x00}, {0xa2, 0x00}, {0xa3, 0x00},
+ {0xac, 0x20}, {0xa5, 0x00}, {0x60, 0x01}, {0xa7, 0x00},
+ {0x61, 0x01}, {0xa9, 0x00}, {0xaa, 0x00}, {0xab, 0x00},
+ {0xac, 0x00}, {0xad, 0x00}, {0xae, 0x00}, {0xaf, 0x00},
+ /* 0xb0*/
+ {0xb0, 0x00}, {0xb1, 0x00}, {0xb2, 0x00}, {0xb3, 0x00},
+ {0x7d, 0x01}, {0xb5, 0x00}, {0xb6, 0x00}, {0xb7, 0x00},
+ {0x7e, 0x01}, {0xb9, 0x00}, {0xba, 0x00}, {0xbb, 0x00},
+ {0x52, 0x01}, {0x53, 0x01}, {0x78, 0x01}, {0xbf, 0x00},
+ /* 0xc0*/
+ {0xc0, 0x00}, {0xc1, 0x00}, {0xc2, 0x00}, {0xc3, 0x00},
+ {0xc4, 0x00}, {0xc5, 0x00}, {0xc6, 0x00}, {0xc7, 0x00},
+ {0xc8, 0x00}, {0xc9, 0x00}, {0xca, 0x00}, {0xcb, 0x00},
+ {0xcc, 0x00}, {0xcd, 0x00}, {0xce, 0x00}, {0xcf, 0x00},
+ /* 0xd0*/
+ {0xd0, 0x00}, {0xd1, 0x00}, {0xd2, 0x00}, {0xd3, 0x00},
+ {0xd4, 0x00}, {0xd5, 0x00}, {0xd6, 0x00}, {0xd7, 0x00},
+ {0xd8, 0x00}, {0xd9, 0x00}, {0xda, 0x00}, {0xdb, 0x00},
+ {0xdc, 0x00}, {0xdd, 0x00}, {0xde, 0x00}, {0xdf, 0x00},
+ /* 0xe0*/
+ {0xe0, 0x00}, {0xe1, 0x00}, {0xe2, 0x00}, {0xe3, 0x00},
+ {0xe4, 0x00}, {0xe5, 0x00}, {0xe6, 0x00}, {0xe7, 0x00},
+ {0xe8, 0x00}, {0xe9, 0x00}, {0xea, 0x00}, {0xeb, 0x00},
+ {0xec, 0x00}, {0xed, 0x00}, {0xee, 0x00}, {0xef, 0x00},
+ /* 0xf0*/
+ {0xf0, 0x00}, {0xf1, 0x00}, {0xf2, 0x00}, {0xf3, 0x00},
+ {0xf4, 0x00}, {0xf5, 0x00}, {0xf6, 0x00}, {0xf7, 0x00},
+ {0xf8, 0x00}, {0xf9, 0x00}, {0xfa, 0x00}, {0xfb, 0x00},
+ {0xfc, 0x00}, {0xfd, 0x00}, {0xfe, 0x00}, {0xff, 0x00},
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0x00, 0xa5, 0x00, 0xa7, /* 0xa0-0xa7 */
+ 0x00, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0x00, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0x00, 0xb9, 0xba, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0xbc, 0xbd, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xa6, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0xbe, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xb8, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+
+ page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static void inc_use_count(void)
+{
+ MOD_INC_USE_COUNT;
+}
+
+static void dec_use_count(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static struct nls_table table = {
+ "iso8859-15",
+ page_uni2charset,
+ charset2uni,
+ inc_use_count,
+ dec_use_count,
+ NULL
+};
+
+int init_nls_iso8859_15(void)
+{
+ return register_nls(&table);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return init_nls_iso8859_15();
+}
+
+
+void cleanup_module(void)
+{
+ unregister_nls(&table);
+ return;
+}
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
+#include <linux/swapctl.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/signal.h>
i.freeswap >> 10);
}
+static int get_swapstats(char * buffer)
+{
+ unsigned long *w = swapstats.kswap_wakeups;
+
+ return sprintf(buffer,
+ "ProcFreeTry: %8lu\n"
+ "ProcFreeSucc: %8lu\n"
+ "ProcShrinkTry: %8lu\n"
+ "ProcShrinkSucc: %8lu\n"
+ "KswapFreeTry: %8lu\n"
+ "KswapFreeSucc: %8lu\n"
+ "KswapWakeups: %8lu %lu %lu %lu\n",
+ swapstats.gfp_freepage_attempts,
+ swapstats.gfp_freepage_successes,
+ swapstats.gfp_shrink_attempts,
+ swapstats.gfp_shrink_successes,
+ swapstats.kswap_freepage_attempts,
+ swapstats.kswap_freepage_successes,
+ w[0], w[1], w[2], w[3]
+ );
+}
+
static int get_version(char * buffer)
{
extern char *linux_banner;
case PROC_MEMINFO:
return get_meminfo(page);
+ case PROC_SWAPSTATS:
+ return get_swapstats(page);
+
#ifdef CONFIG_PCI_OLD_PROC
case PROC_PCI:
return get_pci_list(page);
static int process_unauthorized(int type, int pid)
{
struct task_struct *p;
- uid_t euid; /* Save the euid keep the lock short */
+ uid_t euid=0; /* Save the euid keep the lock short */
read_lock(&tasklist_lock);
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_array_inode_operations
};
+static struct proc_dir_entry proc_root_swapstats = {
+ PROC_SWAPSTATS, 9, "swapstats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
+};
static struct proc_dir_entry proc_root_kmsg = {
PROC_KMSG, 4, "kmsg",
S_IFREG | S_IRUSR, 1, 0, 0,
proc_register(&proc_root, &proc_root_loadavg);
proc_register(&proc_root, &proc_root_uptime);
proc_register(&proc_root, &proc_root_meminfo);
+ proc_register(&proc_root, &proc_root_swapstats);
proc_register(&proc_root, &proc_root_kmsg);
proc_register(&proc_root, &proc_root_version);
proc_register(&proc_root, &proc_root_cpuinfo);
if (bh == NULL) {
return -ENOENT;
}
- if ((inode = iget(dir->i_sb, ino)) == NULL) {
- QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
- retval = -EACCES;
+ inode = dentry->d_inode;
+ if (inode->i_ino != ino) {
+ retval = -EIO;
goto end_unlink;
}
retval = -EPERM;
- if ((dir->i_mode & S_ISVTX) &&
- current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) {
- goto end_unlink;
- }
if (!inode->i_nlink) {
QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n",
kdevname(inode->i_dev),
Wed Feb 4 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
* namei.c: removed static subdir(); is_subdir() from dcache.c
is used instead. Cosmetic changes.
+
+Thu Dec 3 1998 Al Viro (viro@math.psu.edu)
+ * namei.c (sysv_rmdir):
+ Bugectomy: old check for victim being busy
+ (inode->i_count) wasn't replaced (with checking
+ dentry->d_count) and escaped Linus in the last round
+ of changes. Shot and buried.
+
+Wed Dec 9 1998 AV
+ * namei.c (do_sysv_rename):
+ Fixed incorrect check for other owners + race.
+ Removed checks that went to VFS.
+ * namei.c (sysv_unlink):
+ Removed checks that went to VFS.
+
+Thu Dec 10 1998 AV
+ * namei.c (do_mknod):
+ Removed dead code - mknod is never asked to
+ create a symlink or directory. Incidentially,
+ it wouldn't do it right if it would be called.
inode->i_op = NULL;
if (S_ISREG(inode->i_mode))
inode->i_op = &sysv_file_inode_operations;
- else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &sysv_dir_inode_operations;
- if (dir->i_mode & S_ISGID)
- inode->i_mode |= S_ISGID;
- }
- else if (S_ISLNK(inode->i_mode))
- inode->i_op = &sysv_symlink_inode_operations;
else if (S_ISCHR(inode->i_mode))
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
schedule();
goto repeat;
}
- if ((dir->i_mode & S_ISVTX) &&
- current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid && !capable(CAP_FOWNER))
- goto end_unlink;
if (de->inode != inode->i_ino) {
retval = -ENOENT;
goto end_unlink;
goto end_rename;
old_inode = old_dentry->d_inode; /* don't cross mnt-points */
retval = -EPERM;
- if ((old_dir->i_mode & S_ISVTX) &&
- current->fsuid != old_inode->i_uid &&
- current->fsuid != old_dir->i_uid && !capable(CAP_FOWNER))
- goto end_rename;
new_inode = new_dentry->d_inode;
new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name,
new_dentry->d_name.len, &new_de);
retval = 0;
goto end_rename;
}
- if (new_inode && S_ISDIR(new_inode->i_mode)) {
- retval = -EISDIR;
- if (!S_ISDIR(old_inode->i_mode))
- goto end_rename;
- retval = -EINVAL;
- if (is_subdir(new_dentry, old_dentry))
- goto end_rename;
- retval = -ENOTEMPTY;
- if (!empty_dir(new_inode))
- goto end_rename;
- retval = -EBUSY;
- if (new_inode->i_count > 1)
- goto end_rename;
- }
- retval = -EPERM;
- if (new_inode && (new_dir->i_mode & S_ISVTX) &&
- current->fsuid != new_inode->i_uid &&
- current->fsuid != new_dir->i_uid && !capable(CAP_FOWNER))
- goto end_rename;
if (S_ISDIR(old_inode->i_mode)) {
- retval = -ENOTDIR;
- if (new_inode && !S_ISDIR(new_inode->i_mode))
- goto end_rename;
retval = -EINVAL;
if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ if (new_inode) {
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
+ retval = -EBUSY;
+ if (new_dentry->d_count > 1)
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_inode))
+ goto end_rename;
+ }
retval = -EIO;
dir_bh = sysv_file_bread(old_inode, 0, 0);
if (!dir_bh)
inode->i_op = NULL;
if (S_ISREG(inode->i_mode))
inode->i_op = &ufs_file_inode_operations;
- else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &ufs_dir_inode_operations;
- if (dir->i_mode & S_ISGID)
- inode->i_mode |= S_ISGID;
- }
- else if (S_ISLNK(inode->i_mode))
- inode->i_op = &ufs_symlink_inode_operations;
else if (S_ISCHR(inode->i_mode))
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
if (inode->i_sb->dq_op)
inode->i_sb->dq_op->initialize (inode, -1);
- retval = -EPERM;
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- goto end_unlink;
- if ((dir->i_mode & S_ISVTX) &&
- current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid && !fsuser())
- goto end_unlink;
-
retval = -EIO;
if (SWAB32(de->d_ino) != inode->i_ino)
goto end_unlink;
old_bh = ufs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de);
UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(old_de->d_ino),
SWAB16(old_de->d_reclen), ufs_get_de_namlen(old_de), old_de->d_name))
-
+
+ /* Arrrgh. See comments in ext2 */
retval = -ENOENT;
- if (!old_bh)
+ if (!old_bh || SWAB32(old_de->d_ino) != old_inode->i_ino)
goto end_rename;
old_inode = old_dentry->d_inode;
- retval = -EPERM;
- if ((old_dir->i_mode & S_ISVTX) &&
- current->fsuid != old_inode->i_uid &&
- current->fsuid != old_dir->i_uid && !fsuser())
- goto end_rename;
- if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode))
- goto end_rename;
-
new_inode = new_dentry->d_inode;
UFSD(("name %s, len %u\n", new_dentry->d_name.name, new_dentry->d_name.len))
new_bh = ufs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de);
retval = 0;
if (new_inode == old_inode)
goto end_rename;
- if (new_inode && S_ISDIR(new_inode->i_mode)) {
- retval = -EISDIR;
- if (!S_ISDIR(old_inode->i_mode))
- goto end_rename;
- retval = -EINVAL;
- if (is_subdir(new_dentry, old_dentry))
- goto end_rename;
- retval = -ENOTEMPTY;
- if (!ufs_empty_dir (new_inode))
- goto end_rename;
- retval = -EBUSY;
- if (new_dentry->d_count > 1)
- goto end_rename;
- }
- retval = -EPERM;
- if (new_inode) {
- if ((new_dir->i_mode & S_ISVTX) &&
- current->fsuid != new_inode->i_uid &&
- current->fsuid != new_dir->i_uid && !fsuser())
- goto end_rename;
- if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode))
- goto end_rename;
- }
if (S_ISDIR(old_inode->i_mode)) {
- retval = -ENOTDIR;
- if (new_inode && !S_ISDIR(new_inode->i_mode))
- goto end_rename;
retval = -EINVAL;
if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ if (new_inode) {
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
+ retval = -EBUSY;
+ if (new_dentry->d_count > 1)
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!ufs_empty_dir (new_inode))
+ goto end_rename;
+ }
+
dir_bh = ufs_bread (old_inode, 0, 0, &retval);
if (!dir_bh)
goto end_rename;
} else if (inode->i_op == &umsdos_symlink_inode_operations) {
printk (" (i_op is umsdos_symlink_inode_operations)\n");
} else {
- printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op));
+ printk (" (i_op is UNKNOWN: %p)\n", inode->i_op);
}
} else {
printk (KERN_DEBUG "* inode is NULL\n");
if (res == 0) {
drop_replace_inodes(old_dentry, new_inode);
+ list_del(&old_dentry->d_alias);
+ iput(old_dentry->d_inode);
+ d_instantiate(old_dentry, new_inode);
+
d_move(old_dentry, new_dentry);
put_new_inode = 0;
}
* processes are run.
*/
-#define PROC_CHANGE_PENALTY 15 /* Schedule penalty */
+#define PROC_CHANGE_PENALTY 10 /* Schedule penalty */
#define SMP_FROM_INT 1
#define SMP_FROM_SYSCALL 2
/* for downcalls and attributes and lookups */
void coda_flag_inode(struct inode *inode, int flag);
-void coda_flag_alias_children(struct inode *inode, int flag);
+void coda_flag_inode_children(struct inode *inode, int flag);
/*
};
/* flags */
-#define C_VATTR 0x1 /* Validity of vattr in the cnode */
-#define C_SYMLINK 0x2 /* Validity of symlink pointer in the cnode */
-#define C_DYING 0x4 /* Set for outstanding cnodes from venus (which died) */
-#define C_PURGE 0x8
+#define C_VATTR 0x1 /* Validity of vattr in inode */
+#define C_PURGE 0x8
#define C_ZAPDIR 0x10
+#define C_DYING 0x4 /* from venus (which died) */
#define C_INITED 0x20
+#define C_FLUSH 0x2 /* used after a flush */
int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
#define NB_SFS_SIZ 0x895440
/* cache.c */
-void coda_purge_children(struct inode *);
+void coda_purge_children(struct inode *, int);
void coda_purge_dentries(struct inode *);
+/* sysctl.h */
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+
/* debugging masks */
#define D_SUPER 1 /* print results returned by Venus */
#define CODA_PSDEV_MAJOR 67
#define MAX_CODADEVS 5 /* how many do we allow */
-extern struct vcomm psdev_vcomm[];
-
-/* queue stuff; the rest is static to psdev.c */
-struct queue {
- struct queue *forw, *back;
-};
-void coda_q_insert(struct queue *el, struct queue *q);
-void coda_q_remove(struct queue *q);
-
-
+extern struct venus_comm coda_upc_comm;
+extern struct coda_sb_info coda_super_info;
#define CODA_SUPER_MAGIC 0x73757245
struct coda_sb_info
struct inode * sbi_psdev; /* /dev/cfs? Venus/kernel device */
struct inode * sbi_ctlcp; /* control magic file */
int sbi_refct;
- struct vcomm * sbi_vcomm;
+ struct venus_comm * sbi_vcomm;
struct inode * sbi_root;
+ struct super_block *sbi_sb;
struct list_head sbi_cchead;
struct list_head sbi_volroothead;
};
/* communication pending/processing queues queues */
-struct vcomm {
+struct venus_comm {
u_long vc_seq;
struct wait_queue *vc_waitq; /* Venus wait queue */
- struct queue vc_pending;
- struct queue vc_processing;
- struct super_block *vc_sb;
+ struct list_head vc_pending;
+ struct list_head vc_processing;
int vc_inuse;
+ pid_t vc_pid; /* Venus pid */
};
-static inline int vcomm_open(struct vcomm *vcp)
-{
- return ((vcp)->vc_pending.forw != NULL);
-}
-
-static inline void mark_vcomm_closed(struct vcomm *vcp)
-{
- (vcp)->vc_pending.forw = NULL;
-}
static inline struct coda_sb_info *coda_sbp(struct super_block *sb)
{
/* messages between coda filesystem in kernel and Venus */
extern int coda_hard;
extern unsigned long coda_timeout;
-struct vmsg {
- struct queue vm_chain;
- caddr_t vm_data;
- u_short vm_flags;
- u_short vm_inSize; /* Size is at most 5000 bytes */
- u_short vm_outSize;
- u_short vm_opcode; /* copied from data to save lookup */
- int vm_unique;
- struct wait_queue *vm_sleep; /* process' wait queue */
- unsigned long vm_posttime;
+struct upc_req {
+ struct list_head uc_chain;
+ caddr_t uc_data;
+ u_short uc_flags;
+ u_short uc_inSize; /* Size is at most 5000 bytes */
+ u_short uc_outSize;
+ u_short uc_opcode; /* copied from data to save lookup */
+ int uc_unique;
+ struct wait_queue *uc_sleep; /* process' wait queue */
+ unsigned long uc_posttime;
};
+#define REQ_ASYNC 0x1
+#define REQ_READ 0x2
+#define REQ_WRITE 0x4
+
/*
* Statistics
} u;
};
+/*
+ * VFS helper functions..
+ */
+extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_unlink(struct inode *, struct dentry *);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
/*
* This is the "filldir" function type, used by readdir() to let
* the kernel specify what kind of dirent layout it wants to have.
extern void put_write_access(struct inode *inode);
extern struct dentry * open_namei(const char * pathname, int flag, int mode);
extern struct dentry * do_mknod(const char * filename, int mode, dev_t dev);
-extern int VFS_rmdir(struct inode *dir, struct dentry *dentry);
extern int do_pipe(int *);
/* fs/dcache.c -- generic fs support functions */
extern unsigned long max_mapnr;
extern unsigned long num_physpages;
extern void * high_memory;
+extern int page_cluster;
#include <asm/page.h>
#include <asm/atomic.h>
PROC_STRAM,
PROC_SOUND,
PROC_MTRR, /* whether enabled or not */
- PROC_FS
+ PROC_FS,
+ PROC_SWAPSTATS
};
enum pid_directory_inos {
PROC_SCSI_PAS16,
PROC_SCSI_QLOGICFAS,
PROC_SCSI_QLOGICISP,
+ PROC_SCSI_QLOGICFC,
PROC_SCSI_SEAGATE,
PROC_SCSI_T128,
PROC_SCSI_NCR53C7xx,
PROC_SCSI_AM53C974,
PROC_SCSI_SSC,
PROC_SCSI_NCR53C406A,
+ PROC_SCSI_MEGARAID,
PROC_SCSI_PPA,
+ PROC_SCSI_ATP870U,
PROC_SCSI_ESP,
PROC_SCSI_QLOGICPTI,
PROC_SCSI_AMIGA7XX,
PROC_SCSI_MESH,
PROC_SCSI_53C94,
PROC_SCSI_PLUTO,
+ PROC_SCSI_INI9100U,
PROC_SCSI_SCSI_DEBUG,
PROC_SCSI_NOT_PRESENT,
PROC_SCSI_FILE, /* I'm assuming here that we */
/* channel grouping */
-#define RXGROUP 0x100 /* if set, only tx when all channels clear */
-#define TXGROUP 0x200 /* if set, don't transmit simultaneously */
+#define RXGROUP 0100 /* if set, only tx when all channels clear */
+#define TXGROUP 0200 /* if set, don't transmit simultaneously */
/* Tx/Rx clock sources */
unsigned char wreg[16]; /* Copy of last written value in WRx */
unsigned char status; /* Copy of R0 at last external interrupt */
+ unsigned char dcd; /* DCD status */
struct scc_kiss kiss; /* control structure for KISS params */
struct scc_stat stat; /* statistical information */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
+ unsigned short shm_unused; /* compatibility */
+ void *shm_unused2; /* ditto - used by DIPC */
+ void *shm_unused3; /* unused */
};
struct shmid_kernel
extern unsigned long page_cache_size;
extern int buffermem;
+struct swap_stats
+{
+ long proc_freepage_attempts;
+ long proc_freepage_successes;
+ long kswap_freepage_attempts;
+ long kswap_freepage_successes;
+};
+extern struct swap_stats swap_stats;
+
/* Incomplete types for prototype declarations: */
struct task_struct;
struct vm_area_struct;
/* linux/ipc/shm.c */
extern int shm_swap (int, int);
+/* linux/mm/swap.c */
+extern void swap_setup (void);
+
/* linux/mm/vmscan.c */
extern int try_to_free_pages(unsigned int gfp_mask, int count);
extern int add_to_swap_cache(struct page *, unsigned long);
extern int swap_duplicate(unsigned long);
extern int swap_check_entry(unsigned long);
+struct page * lookup_swap_cache(unsigned long);
extern struct page * read_swap_cache_async(unsigned long, int);
#define read_swap_cache(entry) read_swap_cache_async(entry, 1);
extern int FASTCALL(swap_count(unsigned long));
typedef struct swapstat_v1
{
- unsigned int wakeups;
- unsigned int pages_reclaimed;
- unsigned int pages_shm;
- unsigned int pages_mmap;
- unsigned int pages_swap;
+ unsigned long wakeups;
+ unsigned long pages_reclaimed;
+ unsigned long pages_shm;
+ unsigned long pages_mmap;
+ unsigned long pages_swap;
+
+ unsigned long gfp_freepage_attempts;
+ unsigned long gfp_freepage_successes;
+ unsigned long gfp_shrink_attempts;
+ unsigned long gfp_shrink_successes;
+ unsigned long kswap_freepage_attempts;
+ unsigned long kswap_freepage_successes;
+ unsigned long kswap_wakeups[4];
} swapstat_v1;
typedef swapstat_v1 swapstat_t;
extern swapstat_t swapstats;
CTL_PROC=4, /* Process info */
CTL_FS=5, /* Filesystems */
CTL_DEBUG=6, /* Debugging */
- CTL_DEV=7, /* Devices */
+ CTL_DEV=7 /* Devices */
};
KERN_SG_BIG_BUFF=29,
KERN_ACCT=30, /* BSD process accounting parameters */
KERN_PPC_L2CR=31, /* l2cr register on PPC */
+
+ KERN_RTSIGNR=32, /* Number of rt sigs queued */
+ KERN_RTSIGMAX=33, /* Max queuable */
+
+ KERN_SHMMAX=34, /* int: Maximum shared memory segment */
+ KERN_MSGMAX=35, /* int: Maximum size of a messege */
+ KERN_MSGMNB=36, /* int: Maximum message queue size */
+ KERN_MSGPOOL=37 /* int: Maximum system message pool size */
};
VM_BUFFERMEM=6, /* struct: Set buffer memory thresholds */
VM_PAGECACHE=7, /* struct: Set cache memory thresholds */
VM_PAGERDAEMON=8, /* struct: Control kswapd behaviour */
- VM_PGT_CACHE=9 /* struct: Set page table cache parameters */
+ VM_PGT_CACHE=9, /* struct: Set page table cache parameters */
+ VM_PAGE_CLUSTER=10 /* int: set log2 number of pages to swap together */
};
NET_CORE_FASTROUTE=7,
NET_CORE_MSG_COST=8,
NET_CORE_MSG_BURST=9,
- NET_CORE_OPTMEM_MAX=10,
+ NET_CORE_OPTMEM_MAX=10
};
/* /proc/sys/net/ethernet */
enum
{
NET_UNIX_DESTROY_DELAY=1,
- NET_UNIX_DELETE_DELAY=2,
+ NET_UNIX_DELETE_DELAY=2
};
/* /proc/sys/net/ipv4 */
NET_IPV4_ICMP_TIMEEXCEED_RATE=61,
NET_IPV4_ICMP_PARAMPROB_RATE=62,
NET_IPV4_ICMP_ECHOREPLY_RATE=63,
+ NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES=64
};
enum {
NET_IPV4_ROUTE_REDIRECT_SILENCE=11,
NET_IPV4_ROUTE_ERROR_COST=12,
NET_IPV4_ROUTE_ERROR_BURST=13,
- NET_IPV4_ROUTE_GC_ELASTICITY=14,
+ NET_IPV4_ROUTE_GC_ELASTICITY=14
};
enum
{
NET_PROTO_CONF_ALL=-2,
- NET_PROTO_CONF_DEFAULT=-3,
+ NET_PROTO_CONF_DEFAULT=-3
/* And device ifindices ... */
};
NET_IPV4_CONF_RP_FILTER=8,
NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE=9,
NET_IPV4_CONF_BOOTP_RELAY=10,
- NET_IPV4_CONF_LOG_MARTIANS=11,
+ NET_IPV4_CONF_LOG_MARTIANS=11
};
/* /proc/sys/net/ipv6 */
enum {
NET_IPV6_CONF=16,
NET_IPV6_NEIGH=17,
- NET_IPV6_ROUTE=18,
+ NET_IPV6_ROUTE=18
};
enum {
NET_IPV6_ROUTE_GC_MIN_INTERVAL=4,
NET_IPV6_ROUTE_GC_TIMEOUT=5,
NET_IPV6_ROUTE_GC_INTERVAL=6,
- NET_IPV6_ROUTE_GC_ELASTICITY=7,
+ NET_IPV6_ROUTE_GC_ELASTICITY=7
};
enum {
NET_IPV6_DAD_TRANSMITS=7,
NET_IPV6_RTR_SOLICITS=8,
NET_IPV6_RTR_SOLICIT_INTERVAL=9,
- NET_IPV6_RTR_SOLICIT_DELAY=10,
+ NET_IPV6_RTR_SOLICIT_DELAY=10
};
/* /proc/sys/net/<protocol>/neigh/<dev> */
NET_ATALK_AARP_EXPIRY_TIME=1,
NET_ATALK_AARP_TICK_TIME=2,
NET_ATALK_AARP_RETRANSMIT_LIMIT=3,
- NET_ATALK_AARP_RESOLVE_TIME=4,
+ NET_ATALK_AARP_RESOLVE_TIME=4
};
NET_ROSE_LINK_FAIL_TIMEOUT=7,
NET_ROSE_MAX_VCS=8,
NET_ROSE_WINDOW_SIZE=9,
- NET_ROSE_NO_ACTIVITY_TIMEOUT=10,
+ NET_ROSE_NO_ACTIVITY_TIMEOUT=10
};
/* /proc/sys/net/x25 */
/* /proc/sys/net/decnet */
enum {
- NET_DECNET_DEF_T3_BROADCAST=1,
- NET_DECNET_DEF_T3_POINTTOPOINT=2,
- NET_DECNET_DEF_T1=3,
- NET_DECNET_DEF_BCT1=4,
- NET_DECNET_CACHETIMEOUT=5,
- NET_DECNET_DEBUG_LEVEL=6
+ NET_DECNET_NODE_TYPE=1,
+ NET_DECNET_NODE_ADDRESS=2,
+ NET_DECNET_NODE_NAME=3,
+ NET_DECNET_DEFAULT_DEVICE=4,
+ NET_DECNET_DEBUG_LEVEL=255
};
/* CTL_PROC names: */
FS_MAXFILE=7, /* int:maximum number of filedescriptors that can be allocated */
FS_DENTRY=8,
FS_NRSUPER=9, /* int:current number of allocated super_blocks */
- FS_MAXSUPER=10, /* int:maximum number of super_blocks that can be allocated */
+ FS_MAXSUPER=10 /* int:maximum number of super_blocks that can be allocated */
};
/* CTL_DEBUG names: */
/* CTL_DEV names: */
enum {
DEV_CDROM=1,
- DEV_HWMON=2,
+ DEV_HWMON=2
};
/* /proc/sys/dev/cdrom */
enum {
- DEV_CDROM_INFO=1,
+ DEV_CDROM_INFO=1
};
#ifdef __KERNEL__
#define NR_CPUS 1
#endif
-#define NR_TASKS 512
+#define NR_TASKS 512 /* On x86 Max 4092, or 4090 w/APM configured. */
#define MAX_TASKS_PER_USER (NR_TASKS/2)
#define MIN_TASKS_LEFT_FOR_ROOT 4
extern void ax25_kick(ax25_cb *);
extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int);
extern void ax25_queue_xmit(struct sk_buff *);
-extern void ax25_check_iframes_acked(ax25_cb *, unsigned short);
+extern int ax25_check_iframes_acked(ax25_cb *, unsigned short);
/* ax25_route.c */
extern void ax25_rt_device_down(struct device *);
extern void smp_setup(char *str, int *ints);
#ifdef __i386__
extern void ioapic_pirq_setup(char *str, int *ints);
+extern void ioapic_setup(char *str, int *ints);
#endif
extern void no_scroll(char *str, int *ints);
extern void kbd_reset_setup(char *str, int *ints);
{ "nosmp", smp_setup },
{ "maxcpus=", smp_setup },
#ifdef __i386__
+ { "noapic", ioapic_setup },
{ "pirq=", ioapic_pirq_setup },
#endif
#endif
return (unsigned int) shp->u.shm_perm.seq * SHMMNI + id;
}
+int shmmax = SHMMAX;
+
asmlinkage int sys_shmget (key_t key, int size, int shmflg)
{
struct shmid_kernel *shp;
down(¤t->mm->mmap_sem);
lock_kernel();
- if (size < 0 || size > SHMMAX) {
+ if (size < 0 || size > shmmax) {
err = -EINVAL;
} else if (key == IPC_PRIVATE) {
err = newseg(key, shmflg, size);
if (!buf)
goto out;
shminfo.shmmni = SHMMNI;
- shminfo.shmmax = SHMMAX;
+ shminfo.shmmax = shmmax;
shminfo.shmmin = SHMMIN;
shminfo.shmall = SHMALL;
shminfo.shmseg = SHMSEG;
EXPORT_SYMBOL(find_inode_number);
EXPORT_SYMBOL(is_subdir);
EXPORT_SYMBOL(get_unused_fd);
-EXPORT_SYMBOL(VFS_rmdir);
+EXPORT_SYMBOL(vfs_rmdir);
+EXPORT_SYMBOL(vfs_unlink);
+EXPORT_SYMBOL(vfs_rename);
#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
EXPORT_SYMBOL(do_nfsservctl);
static kmem_cache_t *signal_queue_cachep;
-static int nr_queued_signals;
-static int max_queued_signals = 1024;
+int nr_queued_signals;
+int max_queued_signals = 1024;
void __init signals_init(void)
{
&pager_daemon, sizeof(pager_daemon_t), 0644, NULL, &proc_dointvec},
{VM_PGT_CACHE, "pagetable_cache",
&pgt_cache_water, 2*sizeof(int), 0600, NULL, &proc_dointvec},
+ {VM_PAGE_CLUSTER, "page-cluster",
+ &page_cluster, sizeof(int), 0600, NULL, &proc_dointvec},
{0}
};
struct file * file = area->vm_file;
struct dentry * dentry = file->f_dentry;
struct inode * inode = dentry->d_inode;
- unsigned long offset;
+ unsigned long offset, reada, i;
struct page * page, **hash;
unsigned long old_page, new_page;
return new_page;
no_cached_page:
- new_page = __get_free_page(GFP_USER);
+ /*
+ * Try to read in an entire cluster at once.
+ */
+ reada = offset;
+ reada >>= PAGE_SHIFT + page_cluster;
+ reada <<= PAGE_SHIFT + page_cluster;
+
+ for (i = 1 << page_cluster; i > 0; --i, reada += PAGE_SIZE)
+ new_page = try_to_read_ahead(file, reada, new_page);
+
+ if (!new_page)
+ new_page = __get_free_page(GFP_USER);
if (!new_page)
goto no_page;
if (inode->i_op->readpage(file, page) != 0)
goto failure;
- /*
- * Do a very limited read-ahead if appropriate
- */
- if (PageLocked(page))
- new_page = try_to_read_ahead(file, offset + PAGE_SIZE, 0);
goto found_page;
page_locked_wait:
if (!page) {
if (!new)
goto out;
- page_cache = get_free_page(GFP_KERNEL);
+ page_cache = get_free_page(GFP_USER);
if (!page_cache)
goto out;
page = mem_map + MAP_NR(page_cache);
return start_mem;
}
+/*
+ * Primitive swap readahead code. We simply read an aligned block of
+ * (1 << page_cluster) entries in the swap area. This method is chosen
+ * because it doesn't cost us any seek time. We also make sure to queue
+ * the 'original' request together with the readahead ones...
+ */
+void swapin_readahead(unsigned long entry)
+{
+ int i;
+ struct page *new_page;
+ unsigned long offset = SWP_OFFSET(entry);
+ struct swap_info_struct *swapdev = SWP_TYPE(entry) + swap_info;
+
+ offset = (offset >> page_cluster) << page_cluster;
+
+ for (i = 1 << page_cluster; i > 0; i--) {
+ if (offset >= swapdev->max
+ || nr_free_pages - atomic_read(&nr_async_pages) <
+ (freepages.high + freepages.low)/2)
+ return;
+ if (!swapdev->swap_map[offset] ||
+ swapdev->swap_map[offset] == SWAP_MAP_BAD ||
+ test_bit(offset, swapdev->swap_lockmap))
+ continue;
+ new_page = read_swap_cache_async(SWP_ENTRY(SWP_TYPE(entry), offset), 0);
+ if (new_page != NULL)
+ __free_page(new_page);
+ offset++;
+ }
+ return;
+}
+
/*
* The tests may look silly, but it essentially makes sure that
* no other process did a swap-in on us just as we were waiting.
pte_t * page_table, unsigned long entry, int write_access)
{
unsigned long page;
- struct page *page_map;
-
- page_map = read_swap_cache(entry);
+ struct page *page_map = lookup_swap_cache(entry);
+ if (!page_map) {
+ swapin_readahead(entry);
+ page_map = read_swap_cache(entry);
+ }
if (pte_val(*page_table) != entry) {
if (page_map)
free_page_and_swap_cache(page_address(page_map));
}
/* Don't allow too many pending pages in flight.. */
- if (atomic_read(&nr_async_pages) > SWAP_CLUSTER_MAX)
+ if (atomic_read(&nr_async_pages) > pager_daemon.swap_cluster)
wait = 1;
p = &swap_info[type];
144 /* freepages.high */
};
+/* How many pages do we try to swap or page in/out together? */
+int page_cluster = 4; /* Default value modified in swap_setup() */
+
/* We track the number of pages currently being asynchronously swapped
out, so that we don't try to swap TOO many pages out at once */
atomic_t nr_async_pages = ATOMIC_INIT(0);
SWAP_CLUSTER_MAX, /* minimum number of tries */
SWAP_CLUSTER_MAX, /* do swap I/O in clusters of this size */
};
+
+
+/*
+ * Perform any setup for the swap system
+ */
+
+void __init swap_setup(void)
+{
+ /* Use a smaller cluster for memory <16MB or <32MB */
+ if (num_physpages < ((16 * 1024 * 1024) >> PAGE_SHIFT))
+ page_cluster = 2;
+ else if (num_physpages < ((32 * 1024 * 1024) >> PAGE_SHIFT))
+ page_cluster = 3;
+ else
+ page_cluster = 4;
+}
* incremented.
*/
-static struct page * lookup_swap_cache(unsigned long entry)
+struct page * lookup_swap_cache(unsigned long entry)
{
struct page *found;
if (found_page)
goto out;
- new_page_addr = __get_free_page(GFP_KERNEL);
+ new_page_addr = __get_free_page(GFP_USER);
if (!new_page_addr)
goto out; /* Out of memory */
new_page = mem_map + MAP_NR(new_page_addr);
return 0;
}
-/*
- * We are much more aggressive about trying to swap out than we used
- * to be. This works out OK, because we now do proper aging on page
- * contents.
- */
-static int do_try_to_free_page(int gfp_mask)
-{
- int i=6;
-
- /* Always trim SLAB caches when memory gets low. */
- kmem_cache_reap(gfp_mask);
-
- do {
- if (shrink_mmap(i, gfp_mask))
- return 1;
- if (shm_swap(i, gfp_mask))
- return 1;
- if (swap_out(i, gfp_mask))
- return 1;
- shrink_dcache_memory(i, gfp_mask);
- i--;
- } while (i >= 0);
- return 0;
-}
-
/*
* Before we start the kernel thread, print out the
* kswapd initialization message (otherwise the init message
int i;
char *revision="$Revision: 1.5 $", *s, *e;
+ swap_setup();
+
if ((s = strchr(revision, ':')) &&
(e = strchr(s, '$')))
s++, i = e - s;
printk ("Starting kswapd v%.*s\n", i, s);
}
+#define free_memory(fn) \
+ count++; do { if (!--count) goto done; } while (fn)
+
+static int kswapd_free_pages(int kswapd_state)
+{
+ unsigned long end_time;
+
+ /* Always trim SLAB caches when memory gets low. */
+ kmem_cache_reap(0);
+
+ /* max one hundreth of a second */
+ end_time = jiffies + (HZ-1)/100;
+ do {
+ int priority = 7;
+ int count = pager_daemon.swap_cluster;
+
+ switch (kswapd_state) {
+ do {
+ default:
+ free_memory(shrink_mmap(priority, 0));
+ kswapd_state++;
+ case 1:
+ free_memory(shm_swap(priority, 0));
+ kswapd_state++;
+ case 2:
+ free_memory(swap_out(priority, 0));
+ shrink_dcache_memory(priority, 0);
+ kswapd_state = 0;
+ } while (--priority >= 0);
+ return kswapd_state;
+ }
+done:
+ if (nr_free_pages > freepages.high + pager_daemon.swap_cluster)
+ break;
+ } while (time_before_eq(jiffies,end_time));
+ return kswapd_state;
+}
+
/*
* The background pageout daemon.
* Started as a kernel thread from the init process.
init_swap_timer();
kswapd_task = current;
while (1) {
- unsigned long end_time;
+ int state = 0;
current->state = TASK_INTERRUPTIBLE;
flush_signals(current);
run_task_queue(&tq_disk);
schedule();
swapstats.wakeups++;
-
- /* max one hundreth of a second */
- end_time = jiffies + (HZ-1)/100;
- do {
- if (!do_try_to_free_page(0))
- break;
- if (nr_free_pages > freepages.high + SWAP_CLUSTER_MAX)
- break;
- } while (time_before_eq(jiffies,end_time));
+ state = kswapd_free_pages(state);
}
/* As if we could ever get here - maybe we want to make this killable */
kswapd_task = NULL;
* if we need more memory as part of a swap-out effort we
* will just silently return "success" to tell the page
* allocator to accept the allocation.
+ *
+ * We want to try to free "count" pages, and we need to
+ * cluster them so that we get good swap-out behaviour. See
+ * the "free_memory()" macro for details.
*/
int try_to_free_pages(unsigned int gfp_mask, int count)
{
- int retval = 1;
+ int retval;
lock_kernel();
+
+ /* Always trim SLAB caches when memory gets low. */
+ kmem_cache_reap(gfp_mask);
+
+ retval = 1;
if (!(current->flags & PF_MEMALLOC)) {
+ int priority;
+
current->flags |= PF_MEMALLOC;
+
+ priority = 8;
do {
- retval = do_try_to_free_page(gfp_mask);
- if (!retval)
- break;
- count--;
- } while (count > 0);
+ free_memory(shrink_mmap(priority, gfp_mask));
+ free_memory(shm_swap(priority, gfp_mask));
+ free_memory(swap_out(priority, gfp_mask));
+ shrink_dcache_memory(priority, gfp_mask);
+ } while (--priority >= 0);
+ retval = 0;
+done:
current->flags &= ~PF_MEMALLOC;
}
unlock_kernel();
+
return retval;
}
return -EINVAL;
/*
- * The write queue this time is holding sockets ready to use
+ * The read queue this time is holding sockets ready to use
* hooked into the SABM we saved
*/
do {
- cli();
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
- if (flags & O_NONBLOCK) {
- sti();
+ if (flags & O_NONBLOCK)
return -EWOULDBLOCK;
- }
+
interruptible_sleep_on(sk->sleep);
- if (signal_pending(current)) {
- sti();
+ if (signal_pending(current))
return -ERESTARTSYS;
- }
}
} while (skb == NULL);
newsk->pair = NULL;
newsk->socket = newsock;
newsk->sleep = &newsock->wait;
- sti();
/* Now attach up the new socket */
skb->sk = NULL;
+ skb->destructor = NULL;
kfree_skb(skb);
sk->ack_backlog--;
newsock->sk = newsk;
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c
* Joerg(DL1BKE) Fixed it.
* AX.25 037 Jonathan(G4KLX) New timer architecture.
+ * Joerg(DL1BKE) ax25->n2count never got reset
*/
#include <linux/config.h>
/*
* State machine for state 1, Awaiting Connection State.
- * The handling of the timer(s) is in file ax25_timer.c.
+ * The handling of the timer(s) is in file ax25_ds_timer.c.
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
break;
+
+ case AX25_SABME:
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ break;
case AX25_DISC:
ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
/* according to DK4EG´s spec we are required to
* send a RR RESPONSE FINAL NR=0. Please mail
- * <jr@lykos.oche.de> if this causes problems
+ * <jreuter@poboxes.com> if this causes problems
* with the TheNetNode DAMA Master implementation.
*/
/*
* State machine for state 2, Awaiting Release State.
- * The handling of the timer(s) is in file ax25_timer.c
+ * The handling of the timer(s) is in file ax25_ds_timer.c
* Handling of state 0 and connection release is in ax25.c.
*/
static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
{
switch (frametype) {
case AX25_SABM:
+ case AX25_SABME:
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_dama_off(ax25);
break;
switch (frametype) {
case AX25_SABM:
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ case AX25_SABME:
+ if (frametype == AX25_SABM) {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ } else {
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ }
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
else
ax25->condition |= AX25_COND_PEER_RX_BUSY;
+
if (ax25_validate_nr(ax25, nr)) {
- ax25_check_iframes_acked(ax25, nr);
+ if (ax25_check_iframes_acked(ax25, nr))
+ ax25->n2count=0;
if (type == AX25_COMMAND && pf)
ax25_ds_enquiry_response(ax25);
} else {
case AX25_REJ:
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+
if (ax25_validate_nr(ax25, nr)) {
+ if (ax25->va != nr)
+ ax25->n2count=0;
+
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
- ax25->n2count = 0;
ax25_requeue_frames(ax25);
+
if (type == AX25_COMMAND && pf)
ax25_ds_enquiry_response(ax25);
} else {
ax25_frames_acked(ax25, nr);
ax25->n2count = 0;
} else {
- ax25_check_iframes_acked(ax25, nr);
+ if (ax25_check_iframes_acked(ax25, nr))
+ ax25->n2count = 0;
}
if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
if (pf) ax25_ds_enquiry_response(ax25);
break;
}
if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % AX25_MODULUS;
+ ax25->vr = (ax25->vr + 1) % ax25->modulus;
queued = ax25_rx_iframe(ax25, skb);
if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25->vr = ns; /* ax25->vr - 1 */
* Check the state of the receive buffer.
*/
if (ax25->sk != NULL) {
- if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
+ if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) &&
+ (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
break;
}
}
ax25_clear_queues(ax25);
ax25->n2count = 0;
-
- /* state 1 or 2 should not happen, but... */
-
- if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
- ax25->state = AX25_STATE_0;
- else
- ax25->state = AX25_STATE_2;
+ ax25->state = AX25_STATE_2;
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
- ax25_start_t3timer(ax25);
+ ax25_stop_t3timer(ax25);
if (ax25->sk != NULL) {
ax25->sk->state = TCP_CLOSE;
}
ax25 = make->protinfo.ax25;
-
+ skb_set_owner_r(skb, make);
skb_queue_head(&sk->receive_queue, skb);
- skb->sk = make;
make->state = TCP_ESTABLISHED;
make->pair = sk;
int ax25_kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
{
skb->sk = NULL; /* Initially we don't know who it's for */
+ skb->destructor = NULL; /* Who initializes this, dammit?! */
if ((*skb->data & 0x0F) != 0) {
kfree_skb(skb); /* Not a KISS data frame */
* Joerg(DL1BKE) Fixed DAMA Slave mode: will work
* on non-DAMA interfaces like AX25L2V2
* again (this behaviour is _required_).
+ * Joerg(DL1BKE) ax25_check_iframes_acked() returns a
+ * value now (for DAMA n2count handling)
*/
#include <linux/config.h>
dev_queue_xmit(skb);
}
-void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
+int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
{
if (ax25->vs == nr) {
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
ax25_stop_t1timer(ax25);
ax25_start_t3timer(ax25);
+ return 1;
} else {
if (ax25->va != nr) {
ax25_frames_acked(ax25, nr);
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
+ return 1;
}
}
+ return 0;
}
#endif
* Some devices want to be initialized early..
*/
-#if defined(CONFIG_LANCE)
- lance_init();
-#endif
#if defined(CONFIG_SCC)
scc_init();
#endif
* SLHC if present needs attaching so other people see it
* even if not opened.
*/
+
+#ifdef CONFIG_INET
#if (defined(CONFIG_SLIP) && defined(CONFIG_SLIP_COMPRESSED)) \
|| defined(CONFIG_PPP) \
|| (defined(CONFIG_ISDN) && defined(CONFIG_ISDN_PPP))
slhc_install();
#endif
+#endif
#ifdef CONFIG_NET_PROFILE
net_profile_init();
* Yu Tianli : Fixed two ugly bugs in icmp_send
* - IP option length was accounted wrongly
* - ICMP header length was not accounted at all.
+ * Tristan Greaves : Added sysctl option to ignore bogus broadcast
+ * responses from broken routers.
*
* To Fix:
*
int sysctl_icmp_echo_ignore_all = 0;
int sysctl_icmp_echo_ignore_broadcasts = 0;
+/* Control parameter - ignore bogus broadcast responses? */
+int sysctl_icmp_ignore_bogus_error_responses =0;
+
/*
* ICMP control array. This specifies what to do with each ICMP.
*/
* first check your netmask matches at both ends, if it does then
* get the other vendor to fix their kit.
*/
-
- if (inet_addr_type(iph->daddr) == RTN_BROADCAST)
+
+ if (!sysctl_icmp_ignore_bogus_error_responses)
{
- if (net_ratelimit())
- printk(KERN_WARNING "%s sent an invalid ICMP error to a broadcast.\n",
- in_ntoa(skb->nh.iph->saddr));
- return;
+
+ if (inet_addr_type(iph->daddr) == RTN_BROADCAST)
+ {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s sent an invalid ICMP error to a broadcast.\n",
+ in_ntoa(skb->nh.iph->saddr));
+ return;
+ }
}
-
/*
* Deliver ICMP message to raw sockets. Pretty useless feature?
*/
static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, int len)
{
+#if 0
if (net_ratelimit())
printk(KERN_DEBUG "a guy asks for address mask. Who is it?\n");
+#endif
}
/*
/* From icmp.c */
extern int sysctl_icmp_echo_ignore_all;
extern int sysctl_icmp_echo_ignore_broadcasts;
+extern int sysctl_icmp_ignore_bogus_error_responses;
/* From ip_fragment.c */
extern int sysctl_ipfrag_low_thresh;
{NET_IPV4_ICMP_ECHO_IGNORE_BROADCASTS, "icmp_echo_ignore_broadcasts",
&sysctl_icmp_echo_ignore_broadcasts, sizeof(int), 0644, NULL,
&proc_dointvec},
+ {NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES, "icmp_ignore_bogus_error_responses",
+ &sysctl_icmp_ignore_bogus_error_responses, sizeof(int), 0644, NULL,
+ &proc_dointvec},
{NET_IPV4_ICMP_DESTUNREACH_RATE, "icmp_destunreach_rate",
&sysctl_icmp_destunreach_time, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_ICMP_TIMEEXCEED_RATE, "icmp_timeexceed_rate",
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/types.h>
/* #include <linux/module.h> */
-#include <linux/config.h>
#include <linux/init.h>
#include <net/irda/irda.h>
*
********************************************************************/
-#include <linux/config.h>
-
#include <net/irda/irda.h>
#include <net/irda/irqueue.h>
#include <net/irda/irlap.h>
*/
#define MAX2(a,b) ((a)>(b)?(a):(b))
#define MIN2(a,b) ((a)<(b)?(a):(b))
-#define MAX5(a,b,c,d,e) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,e)))))
-#define MIN5(a,b,c,d,e) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,e)))))
+#define MAX6(a,b,c,d,e,f) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,MAX2(e,f))))))
+#define MIN6(a,b,c,d,e,f) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,MIN2(e,f))))))
* m|#\s*include\s*<(.*?>"|
* m|#\s*(?define|undef)\s*CONFIG_(\w*)|
* m|(?!\w)CONFIG_|
+ * m|__SMP__|
*
* About 98% of the CPU time is spent here, and most of that is in
* the 'start' paragraph. Because the current characters are
* in a register, the start loop usually eats 4 or 8 characters
- * per memory read. The MAX5 and MIN5 tests dispose of most
+ * per memory read. The MAX6 and MIN6 tests dispose of most
* input characters with 1 or 2 comparisons.
*/
void state_machine(const char * map)
start:
GETNEXT
__start:
- if (current > MAX5('/','\'','"','#','C')) goto start;
- if (current < MIN5('/','\'','"','#','C')) goto start;
+ if (current > MAX6('/','\'','"','#','C','_')) goto start;
+ if (current < MIN6('/','\'','"','#','C','_')) goto start;
CASE('/', slash);
CASE('\'', squote);
CASE('"', dquote);
CASE('#', pound);
CASE('C', cee);
+ CASE('_', underscore);
goto start;
/* / */
goto cee_CONFIG_word;
use_config(map_dot, next - map_dot - 1);
goto __start;
+
+/* __SMP__ */
+underscore:
+ GETNEXT NOTCASE('_', __start);
+ GETNEXT NOTCASE('S', __start);
+ GETNEXT NOTCASE('M', __start);
+ GETNEXT NOTCASE('P', __start);
+ GETNEXT NOTCASE('_', __start);
+ GETNEXT NOTCASE('_', __start);
+ use_config("SMP", 3);
+ goto __start;
+
}
}